Arc in Qgraphicsscene

QT QGraphicsScene Drawing Arc

You can use a QGraphicsEllipseItem to add ellipses, circles, and segments/arcs to a QGraphicsScene.

Try

QGraphicsEllipseItem* item = new QGraphicsEllipseItem(x, y, width, height);
item->setStartAngle(startAngle);
item->setSpanAngle(endAngle - startAngle);
scene->addItem(item);

Unfortunately, QGraphicsEllipseItem only supports QPainter::drawEllipse() and QPainter::drawPie() - the latter can be used to draw arcs, but has the side effect that there is always a line drawn from the start and the end of the arc to the center.

If you require a true arc, you can e.g. subclass QGraphicsEllipseItem and override the paint() method:

class QGraphicsArcItem : public QGraphicsEllipseItem {
public:
QGraphicsArcItem ( qreal x, qreal y, qreal width, qreal height, QGraphicsItem * parent = 0 ) :
QGraphicsEllipseItem(x, y, width, height, parent) {
}

protected:
void paint ( QPainter * painter, const QStyleOptionGraphicsItem * option, QWidget * widget) {
painter->setPen(pen());
painter->setBrush(brush());
painter->drawArc(rect(), startAngle(), spanAngle());

// if (option->state & QStyle::State_Selected)
// qt_graphicsItem_highlightSelected(this, painter, option);
}
};

You then still need to handle the item highlighting, unfortunately qt_graphicsItem_highlightSelected is a static function defined inside the Qt library.

Arc in QGraphicsScene

A Subclass of QGraphicsItem, that takes 3 points, and intersects the three with an arc of a circle. The second point is always in the middle. (Selectablity and other properties haven't been fully implemented or tested).

Note: Qt Creator includes more advanced examples of subclassed QGraphicsItems such as Colliding Mice, and 40,000 chips examples.

http://qt-project.org/doc/qt-5/examples-graphicsview.html

Also to enable QObject signals and slots and properties from a QGraphicsItem, you should use QGraphicsObject.

Note: added onto github here.

arcgraphicsitem.h

#ifndef ARCGRAPHICSITEM_H
#define ARCGRAPHICSITEM_H

#include <QGraphicsItem>
#include <QLineF>
#include <QPointF>
#include <QPainter>
#include <QStyleOptionGraphicsItem>
#include <QWidget>

class ArcGraphicsItem : public QGraphicsItem
{

public:
ArcGraphicsItem();
ArcGraphicsItem(int i, QPointF point0, QPointF point1, QPointF point2);
~ArcGraphicsItem();

QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);

int type() const { return Type;}
int id() {return m_id;}

QPainterPath shape() const;
protected:

private:
void init();

enum { Type = UserType + 6 };
int m_id;

QPointF startP, midP, endP, p1, p2, p3, center;
QLineF lineBC;
QLineF lineAC;
QLineF lineBA;
QLineF lineOA;
QLineF lineOB;
QLineF lineOC;
QLineF bisectorBC;
QLineF bisectorBA;
qreal startAngle;
qreal spanAngle;

QRectF circle;
QRectF boundingRectTemp;
qreal rad;
};

#endif // ARCGRAPHICSITEM_H

arcgraphicsitem.cpp

#include "arcgraphicsitem.h"
#include "qmath.h"
#include <QPen>
#include <QDebug>
#include <QPainterPath>

ArcGraphicsItem::ArcGraphicsItem(int i,
QPointF point1,
QPointF point2,
QPointF point3)
: m_id(i), p1(point1), p2(point2), p3(point3)

{
init();
}

ArcGraphicsItem::ArcGraphicsItem()
{
p1 = QPointF(0,0);
p2 = QPointF(0,1);
p3 = QPointF(1,1);
m_id = -1;
init();
}

ArcGraphicsItem::~ArcGraphicsItem()
{

}

void ArcGraphicsItem::init()
{
lineBC = QLineF(p2, p3);
lineAC = QLineF(p1, p3);
lineBA = QLineF(p2, p1);

rad = qAbs(lineBC.length()/(2*qSin(qDegreesToRadians(lineAC.angleTo(lineBA)))));

bisectorBC = QLineF(lineBC.pointAt(0.5), lineBC.p2());
bisectorBC.setAngle(lineBC.normalVector().angle());

bisectorBA = QLineF(lineBA.pointAt(0.5), lineBA.p2());
bisectorBA.setAngle(lineBA.normalVector().angle());
bisectorBA.intersect(bisectorBC, ¢er);

circle = QRectF(center.x() - rad, center.y() - rad, rad*2, rad*2);

lineOA = QLineF(center, p1);
lineOB = QLineF(center, p2);
lineOC = QLineF(center, p3);

startAngle = lineOA.angle();
spanAngle = lineOA.angleTo(lineOC);
// Make sure that the span angle covers all three points with the second point in the middle
if(qAbs(spanAngle) < qAbs(lineOA.angleTo(lineOB)) || qAbs(spanAngle) < qAbs(lineOB.angleTo(lineOC)))
{
// swap the end point and invert the spanAngle
startAngle = lineOC.angle();
spanAngle = 360 - spanAngle;
}

int w = 10;
boundingRectTemp = circle.adjusted(-w, -w, w, w);
}

QRectF ArcGraphicsItem::boundingRect() const
{
// outer most edges
return boundingRectTemp;
}

void ArcGraphicsItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QPen paintpen;
painter->setRenderHint(QPainter::Antialiasing);
paintpen.setWidth(1);
// Draw arc

if(isSelected())
{
painter->setBrush(Qt::SolidPattern);
paintpen.setColor(Qt::black);
painter->setPen(paintpen);
}
else
{
paintpen.setStyle(Qt::DashLine);
paintpen.setColor(Qt::black);
painter->setPen(paintpen);
}

QPainterPath path;
path.arcMoveTo(circle,startAngle);
path.arcTo(circle, startAngle, spanAngle);

// Draw points

if (isSelected())
{
// sets brush for end points
painter->setBrush(Qt::SolidPattern);
paintpen.setColor(Qt::red);
painter->setPen(paintpen);

qreal ptRad = 10;
painter->drawEllipse(p1, ptRad, ptRad);
painter->drawEllipse(p2, ptRad, ptRad);
painter->drawEllipse(p3, ptRad, ptRad);
}

painter->setBrush(Qt::NoBrush);
painter->drawPath(path);
}

QPainterPath ArcGraphicsItem::shape() const
{
QPainterPath path;
path.arcMoveTo(circle,startAngle);
path.arcTo(circle, startAngle, spanAngle);
return path;
}

Hope that helps

How to draw a point (on mouseclick) on a QGraphicsScene?

UPDATE: There is a new class called QGraphicsSceneMouseEvent that makes this a little easier.
I just finished an example using it here:

https://stackoverflow.com/a/26903599/999943

It differs with the answer below in that it subclasses QGraphicsScene, not QGraphicsView, and it uses mouseEvent->scenePos() so there isn't a need to manually map coordinates.


You are on the right track, but you still have a little more to go.

You need to subclass QGraphicsView to be able to do something with mouse presses or with mouse releases using QMouseEvent.

    #include <QGraphicsView>
#include <QGraphicsScene>
#include <QGraphicsEllipseItem>
#include <QMouseEvent>

class MyQGraphicsView : public QGraphicsView
{
Q_OBJECT
public:
explicit MyQGraphicsView(QWidget *parent = 0);

signals:

public slots:
void mousePressEvent(QMouseEvent * e);
// void mouseReleaseEvent(QMouseEvent * e);
// void mouseDoubleClickEvent(QMouseEvent * e);
// void mouseMoveEvent(QMouseEvent * e);
private:
QGraphicsScene * scene;
};

QGraphicsView doesn't natively have dimension-less points. You will probably want to use QGraphicsEllipse item or simply, scene->addEllipseItem() with a very small radius.

    #include "myqgraphicsview.h"
#include <QPointF>

MyQGraphicsView::MyQGraphicsView(QWidget *parent) :
QGraphicsView(parent)
{
scene = new QGraphicsScene();
this->setSceneRect(50, 50, 350, 350);
this->setScene(scene);
}

void MyQGraphicsView::mousePressEvent(QMouseEvent * e)
{
double rad = 1;
QPointF pt = mapToScene(e->pos());
scene->addEllipse(pt.x()-rad, pt.y()-rad, rad*2.0, rad*2.0,
QPen(), QBrush(Qt::SolidPattern));
}

Note the usage of mapToScene() to make the pos() of the event map correctly to where the mouse is clicked on the scene.

You need to add an instance of your subclassed QGraphicsView to the centralWidget's layout of your ui if you are going to use a form.

    QGridLayout * gridLayout = new QGridLayout(ui->centralWidget);
gridLayout->addWidget( new MyQGraphicsView() );

or if your ui has a layout already it will look like this:

    ui->centralWidget->layout()->addWidget( new MyGraphicsView() );

If you don't use a QMainWindow and a form, you can add it to a QWidget if you set a layout for it and then add your QGraphicsView to that layout in a similar manner. If you don't want a margin around your QGraphicsView, just call show on it and don't put it inside a different layout.

    #include <QtGui/QApplication>
#include "myqgraphicsview.h"

int main(int argc, char *argv[])
{
QApplication a(argc, argv);

MyQGraphicsView view;
view.show();

return a.exec();
}

And that's it. Now you are dangerous with QGraphicsView's and their interaction with the mouse.

Be sure to read and study about Qt's Graphics View Framework and the related examples to be effective when using QGraphicsView and QGraphicsScene. They are very powerful tools for 2D graphics and can have a bit of a learning curve but they are worth it.

QT is fuzzing up my arc

By "relatively smooth" you are probably referring to the parts where the arc is a perfect line or staircase, where the result of anti-aliasing is almost not visible:

Sample Image

At other angles it's more difficult to get a result what would look fine on a low DPI screen, what's why you get these jagged smoothing:

Sample Image

There is not much you can do, besides applying some sharpening effect. But at the end you will get almost the same result as with no antialiasing:

Sample Image

However you can still improve the black line by snapping it to the nearest grid. You need to use floating-point versions of QPoint and QRect:

QPointF centrePt(this->width() / 2 + 0.5, this->height() / 2 + 0.5);
...
QRectF bounds(centrePt.x()-radius + 0.5, centrePt.y()-radius + 0.5, (2*radius), (2*radius));

Original:

Sample Image Sample Image

With snapping:

Sample Image Sample Image

How to draw an arc between two known points in Qt?

I think you misunderstood the parameters for arcTo, especially the bounding rectangle.

Given your image, you should move path to (106, 80) (center of the bounding rectangle)

path.moveTo(106, 80);

The bounding rectangle of the arc should look like this:

  • x: 76
  • y: 30
  • width: 60
  • height: 100

The arc itsel should have a start angle at 90° and should span 180° in negative direction.

This results in:

path.arcTo(76.0, 30.0, 60.0, 100.0, 90.0, -180.0);

Update

arcTo

path.moveTo(106, 30);
path.cubicTo(QPointF(156.0, 30.0), QPointF(156.0, 130.0), QPointF(106.0, 130.0));


Related Topics



Leave a reply



Submit