I want to implement arc in QGraphicsScene. I want that on clicking of three points my arc should be drawn such that on clicking of three points arc is drawn where first point will be starting of arc, second will be any point on arc and third will be end point of arc. I have tried studing drawArc function but got confused with startangle and spanangle. I was unable to set them dynamically. Please suggest me some way to proceed.
I tried the solution to embend it in my project but got the following error:
error: cannot allocate an object of abstract type 'arc'
arcItem = new arc(++id, startP, midP, endP);
Can you please help me out to solve the problem. I am giving a part of code to my project.
In mousepress event of cadgraphicsscene I have done following thing.
cadgraphicsscene.cpp
void CadGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
// mousePressEvent in the graphicsScene
if(mouseEvent->button() == Qt::LeftButton)
{
switch (entityMode)
{
case ArcMode:
if (mFirstClick)
{
startP = mouseEvent->scenePos();
mFirstClick = false;
mSecondClick = true;
}
else if (!mFirstClick && mSecondClick)
{
midP = mouseEvent->scenePos();
mFirstClick = false;
mSecondClick = false;
mThirdClick = true;
}
else if (!mSecondClick && mThirdClick)
{
endP = mouseEvent->scenePos();
mThirdClick = false;
mPaintFlag = true;
}
if (mPaintFlag)
{
arcItem = new arc(++id, startP, midP, endP);
itemList.append(arcItem);
mUndoStack->push(new CadCommandAdd(this, arcItem));
setFlags();
}
}
}
}
arc.cpp
#include "arc.h"
arc::arc(int i, QPointF point1, QPointF point2, QPointF point3)
{
// assigns id
id = i;
p1 = point1;
p2 = point2;
p3 = point3;
lineBC(point2, point3);
lineAC(point1, point3);
lineBA(point2, point1);
rad = qAbs(lineBC.length()/(2*qSin(qDegreesToRadians(lineAC.angleTo(lineBA)))));
bisectorBC(lineBC.pointAt(0.5), lineBC.p2());
bisectorBC.setAngle(lineBC.normalVector().angle());
bisectorBA(lineBA.pointAt(0.5), lineBA.p2());
bisectorBA.setAngle(lineBA.normalVector().angle());
bisectorBA.intersect(bisectorBC, ¢er);
ellipse = new QGraphicsEllipseItem(center.x() - rad, center.y() - rad, rad*2, rad*2);
lineOA(center, point1);
lineOC(center, point3);
}
arc::arc(int i, QLineF start, QLineF end)
{
// assigns id
id = i;
lineOA.angle() = start;
lineOC.angle() - lineOA.angle() = end;
}
int arc::type() const
{
// Enable the use of qgraphicsitem_cast with arc item.
return Type;
}
void arc::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
QPen paintpen;
painter->setRenderHint(QPainter::Antialiasing);
paintpen.setWidth(1);
if (isSelected())
{
// sets brush for end points
painter->setBrush(Qt::SolidPattern);
paintpen.setColor(Qt::red);
painter->setPen(paintpen);
paintpen.setStyle(Qt::DashLine);
paintpen.setColor(Qt::black);
painter->setPen(paintpen);
painter->drawArc(ellipse->boundingRect(),lineOA.angle(),lineOC.angle() - lineOA.angle());
}
else
{
painter->setBrush(Qt::SolidPattern);
paintpen.setColor(Qt::black);
painter->setPen(paintpen);
painter->drawArc(ellipse->boundingRect(),lineOA.angle(),lineOC.angle() - lineOA.angle());
}
}
arc.h
include <QGraphicsItem>
#include "qmath.h"
class arc : public QObject, public QGraphicsItem
{
Q_OBJECT
public:
arc(int, QPointF, QPointF, QPointF);
arc(int, QLineF, QLineF);
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget);
enum { Type = UserType + 6 };
int type() const;
int id;
QPointF startP, midP, endP, p1, p2, p3,center;
QLineF lineBC;
QLineF lineAC;
QLineF lineBA;
QLineF lineOA;
QLineF lineOC;
QLineF bisectorBC;
QLineF bisectorBA;
QGraphicsEllipseItem *ellipse;
qreal rad;
private:
QVector<QPointF> stuff;
};
#endif // ARC_H
Please help me out to solve the error.
This sounds like it could be solved with some relatively simple math:
https://www.google.com/search?q=define%20circle%20three%20points
https://math.stackexchange.com/a/213678
https://www.khanacademy.org/math/geometry/triangle-properties/perpendicular_bisectors/v/three-points-defining-a-circle
Here is my translation of the math into Qt goodness
// m_points is a QList<QPointF>
// use math to define the circle
QLineF lineBC(m_points.at(1), m_points.at(2));
QLineF lineAC(m_points.at(0), m_points.at(2));
QLineF lineBA(m_points.at(1), m_points.at(0));
qreal rad = qAbs(lineBC.length()/(2*qSin(qDegreesToRadians(lineAC.angleTo(lineBA)))));
QLineF bisectorBC(lineBC.pointAt(0.5), lineBC.p2());
bisectorBC.setAngle(lineBC.normalVector().angle());
QLineF bisectorBA(lineBA.pointAt(0.5), lineBA.p2());
bisectorBA.setAngle(lineBA.normalVector().angle());
QPointF center;
bisectorBA.intersect(bisectorBC, ¢er);
qDebug() << rad << center;
QT QGraphicsScene Drawing Arc
QPainterPath* path = new QPainterPath();
path->arcMoveTo(0,0,50,50,20);
path->arcTo(0,0,50,50,20, 90);
scene.addPath(*path);
Putting all of this together into a nice little project turns into this:
https://github.com/peteristhegreat/ThreePointsCircle
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include <QGraphicsView>
#include <QGraphicsScene>
#include "graphicsscene.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QGraphicsView * view = new QGraphicsView;
GraphicsScene * scene = new GraphicsScene();
view->setScene(scene);
view->setSceneRect(-300,-300, 300, 300);
this->resize(600, 600);
this->setCentralWidget(view);
}
MainWindow::~MainWindow()
{
}
graphicsscene.h
#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QPointF>
#include <QList>
class GraphicsScene : public QGraphicsScene
{
Q_OBJECT
public:
explicit GraphicsScene(QObject *parent = 0);
virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent * mouseEvent);
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent * mouseEvent);
virtual void mousePressEvent(QGraphicsSceneMouseEvent * mouseEvent);
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent * mouseEvent);
signals:
public slots:
private:
QList <QPointF> m_points;
};
#endif // GRAPHICSSCENE_H
graphicsscene.cpp
#include "graphicsscene.h"
#include <QDebug>
#include <QGraphicsEllipseItem>
#include <QGraphicsPathItem>
#include <QPainterPath>
#include "qmath.h"
GraphicsScene::GraphicsScene(QObject *parent) :
QGraphicsScene(parent)
{
this->setBackgroundBrush(Qt::gray);
}
void GraphicsScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent * mouseEvent)
{
qDebug() << Q_FUNC_INFO << mouseEvent->scenePos();
QGraphicsScene::mouseDoubleClickEvent(mouseEvent);
}
void GraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent * mouseEvent)
{
qDebug() << Q_FUNC_INFO << mouseEvent->scenePos();
QGraphicsScene::mouseMoveEvent(mouseEvent);
}
void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent * mouseEvent)
{
qDebug() << Q_FUNC_INFO << mouseEvent->scenePos();
QGraphicsScene::mousePressEvent(mouseEvent);
}
void GraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent * me)
{
qDebug() << Q_FUNC_INFO << me->scenePos();
int radius = 20;
QGraphicsEllipseItem * ellipse = this->addEllipse(me->scenePos().x() - radius, me->scenePos().y() - radius, radius*2, radius*2);
ellipse->setBrush(Qt::white);
m_points.append(me->scenePos());
if(m_points.size() == 3)
{
// use math to define the circle
QLineF lineBC(m_points.at(1), m_points.at(2));
QLineF lineAC(m_points.at(0), m_points.at(2));
QLineF lineBA(m_points.at(1), m_points.at(0));
qreal rad = qAbs(lineBC.length()/(2*qSin(qDegreesToRadians(lineAC.angleTo(lineBA)))));
QLineF bisectorBC(lineBC.pointAt(0.5), lineBC.p2());
bisectorBC.setAngle(lineBC.normalVector().angle());
QLineF bisectorBA(lineBA.pointAt(0.5), lineBA.p2());
bisectorBA.setAngle(lineBA.normalVector().angle());
QPointF center;
bisectorBA.intersect(bisectorBC, ¢er);
qDebug() << rad << center;
bool drawCircle = true;
QGraphicsEllipseItem * ellipse = new QGraphicsEllipseItem(center.x() - rad, center.y() - rad, rad*2, rad*2);
if(drawCircle)
this->addItem(ellipse);
// add arc
// this->addItem(path);
QPainterPath path;
QLineF lineOA(center, m_points.at(0));
QLineF lineOC(center, m_points.at(2));
path.arcMoveTo(ellipse->boundingRect(),lineOA.angle());
path.arcTo(ellipse->boundingRect(), lineOA.angle(), lineOC.angle() - lineOA.angle());
QGraphicsPathItem * pathItem = new QGraphicsPathItem(path);
pathItem->setPen(QPen(Qt::red,10));
this->addItem(pathItem);
if(!drawCircle)
delete ellipse;
m_points.clear();
}
QGraphicsScene::mouseReleaseEvent(me);
}
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
Related
I have this program where i'm drawing a rectangular polygon which can be rotated, scaled and moved using a transform.
I've however bumped into a problem which i don't understand and although i would like to figure it out myself, I've come to realize that it's time to seek some help.
The problem:
when the polygon is rotated to certain angles above 45 degrees, dragging the top left anchor makes it go out of control, ie. it moves further away from the anchor until it goes completely haywire...
Without providing a direct answer to the problem, what kind of knowledge am I missing to solve this? I know the basics of matrices and how they work although I have a hard to visualizing it...
To me the calculations itself seem correct and scaling either X or Y independently works as expected but the combination does not.
To reproduce:
Hold Ctrl to rotate the polygon
Drag the top left corner
Here's a minimal example:
// project
QT += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
CONFIG += c++11
SOURCES += \
main.cpp
HEADERS += mainwindow.h
# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target
// mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QMouseEvent>
#include <QTimer>
#include <QSlider>
#include <QtMath>
enum class MoveMode {
MIDDLE,
TOPLEFT,
TOPRIGHT,
BOTTOMLEFT,
BOTTOMRIGHT,
ROTATION,
SYMMETRY,
NONE
};
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow() override { };
void paintMe();
void updateMatrix();
void updateSelectionMatrix();
void updateTransformedRect();
void handleDragging();
void paintGrid(QPainter& painter, QTransform transform);
protected:
void paintEvent(QPaintEvent* event) override;
void mousePressEvent(QMouseEvent* event) override;
void mouseMoveEvent(QMouseEvent* event) override;
void mouseReleaseEvent(QMouseEvent* event) override;
private:
QPointF getSelectionAnchorPoint(QVector<QPointF> selectionPolygon, MoveMode moveMode) const;
QRect mapRect(QRect rect, QTransform transform);
QRectF mSelection;
QPointF currentPoint;
QPointF anchorPoint;
QTransform viewTransform;
QTransform viewInverse;
bool isRotating = false;
bool mouseDragging = false;
bool isTranslating = false;
bool somethingDragged = false;
qreal mRotAngle = 0;
qreal mSelectionRot = 0;
qreal mPreviousAngle = 0;
qreal mScaleX;
qreal mScaleY;
QPointF mOffset;
MoveMode mMoveMode = MoveMode::NONE;
};
#endif // MAINWINDOW_H
// main.cpp
#include "mainwindow.h"
#include <QApplication>
#include <QMainWindow>
#include <QDebug>
#include <QtMath>
#include <QPainter>
#include <QHBoxLayout>
#include <QLine>
class SelectionManager : public QObject
{
public:
SelectionManager() {};
~SelectionManager() {};
const QPolygonF& mySelectionPolygon() { return mSelectionPolygon; }
MoveMode getMoveMode() const { return mMoveMode; }
void setMoveMode(MoveMode moveMode) { mMoveMode = moveMode; }
QPointF currentTransformAnchor() const { return mAnchorPoint; }
QTransform selectionTransform() const { return mSelectionTransform; }
QPointF mapToLocalSpace(QPointF point) const { return mSelectionTransform.map(point); };
QPointF mapFromLocalSpace(QPointF point) const { return mSelectionTransform.inverted().map(point); }
QPolygonF mapToLocalSpace(QPolygonF polygon) const { return mSelectionTransform.map(polygon); }
QPolygonF mapFromLocalSpace(QPolygonF polygon) const { return mSelectionTransform.inverted().map(polygon); }
void setMoveModeForAnchorInRange(QPointF point)
{
QPolygonF transformPoly = mapToLocalSpace(mSelectionPolygon);
const double calculatedSelectionTol = mSelectionTolerance;
MoveMode mode;
if (QLineF(point, transformPoly[0]).length() < calculatedSelectionTol)
{
mode = MoveMode::TOPLEFT;
}
else if (QLineF(point, transformPoly[1]).length() < calculatedSelectionTol)
{
mode = MoveMode::TOPRIGHT;
}
else if (QLineF(point, transformPoly[2]).length() < calculatedSelectionTol)
{
mode = MoveMode::BOTTOMRIGHT;
}
else if (QLineF(point, transformPoly[3]).length() < calculatedSelectionTol)
{
mode = MoveMode::BOTTOMLEFT;
}
else if (transformPoly.containsPoint(point, Qt::WindingFill))
{
mode = MoveMode::MIDDLE;
}
else {
mode = MoveMode::NONE;
}
mMoveMode = mode;
}
void adjustSelection(const QPointF& currentPoint, qreal offsetX, qreal offsetY, qreal rotationOffset)
{
QPointF offset(offsetX, offsetY);
MoveMode moveMode = mMoveMode;
switch (moveMode)
{
case MoveMode::MIDDLE: {
translate(currentPoint - offset);
break;
}
case MoveMode::TOPLEFT:
case MoveMode::TOPRIGHT:
case MoveMode::BOTTOMRIGHT:
case MoveMode::BOTTOMLEFT: {
QPolygonF fixedScalePolygon = anchorTransform().map(mSelectionPolygon);
qreal scaleX = 1;
qreal scaleY = 1;
if (moveMode == MoveMode::TOPLEFT) {
QPolygonF worldSelectionPolygon = mapToLocalSpace(mSelectionPolygon);
QLineF lineYCurrentPointFromBottomLeftAnchor(worldSelectionPolygon[3], currentPoint);
QLineF lineXCurrentPointFromTopRightAnchor(worldSelectionPolygon[1], currentPoint);
QLineF lineY(fixedScalePolygon[3], fixedScalePolygon[0]);
QLineF lineX(fixedScalePolygon[1], fixedScalePolygon[0]);
scaleY = lineYCurrentPointFromBottomLeftAnchor.dy() / lineY.dy();
scaleX = lineXCurrentPointFromTopRightAnchor.dx() / lineX.dx();
}
scale(scaleX, scaleY);
break;
}
case MoveMode::ROTATION: {
rotate(rotationOffset);
break;
}
default:
break;
}
calculateSelectionTransformation();
}
void translate(QPointF newPos)
{
mTranslation += newPos;
}
void rotate(qreal angle)
{
mRotatedAngle += angle;
}
void scale(qreal sX, qreal sY)
{
mScaleX = sX;
mScaleY = sY;
}
qreal angleFromPoint(QPointF point, QPointF anchorPoint) const
{
return qRadiansToDegrees(getDifferenceAngle(mSelectionTransform.map(anchorPoint), point));
}
void setSelection(QRectF rect)
{
mSelectionPolygon = rect;
mSomethingSelected = (rect.isValid() ? true : false);
mScaleX = 1;
mScaleY = 1;
calculateSelectionTransformation();
mClickTransform = mSelectionTransform;
}
void setTransformAnchor(QPointF point)
{
QPointF newPos = mapToLocalSpace(point);
QPointF oldPos = mapToLocalSpace(mAnchorPoint);
mTranslation = mTranslation - oldPos + newPos;
mAnchorPoint = point;
}
QTransform anchorTransform()
{
QPointF anchorPoint = mAnchorPoint;
QTransform t;
t.translate(-anchorPoint.x(), -anchorPoint.y());
QTransform t2;
t2.translate(mTranslation.x(), mTranslation.y());
QTransform r;
r.rotate(mRotatedAngle);
QTransform s;
s.scale(1, 1);
return (t * s * r * t2);
}
void calculateSelectionTransformation()
{
QPointF anchorPoint = mAnchorPoint;
QTransform t;
t.translate(-anchorPoint.x(), -anchorPoint.y());
QTransform t2;
t2.translate(mTranslation.x(), mTranslation.y());
QTransform r;
r.rotate(mRotatedAngle);
QTransform s;
s.scale(mScaleX, mScaleY);
mSelectionTransform = (t * s * r * t2);
}
qreal getDifferenceAngle(const QPointF a, const QPointF b) const
{
return qAtan2(b.y() - a.y(), b.x() - a.x());
}
private:
bool mSomethingSelected = false;
QPolygonF mSelectionPolygon;
QPointF mOffset;
qreal mScaleX;
qreal mScaleY;
QPointF mTranslation;
qreal mRotatedAngle = 0.0;
MoveMode mMoveMode = MoveMode::NONE;
QTransform mSelectionTransform;
QTransform mClickTransform;
const qreal mSelectionTolerance = 8.0;
QPointF mAnchorPoint;
};
SelectionManager selectMan;
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
selectMan.setSelection(QRectF(100,100, 300, 300));
mScaleX = 1;
mScaleY = 1;
updateMatrix();
setMouseTracking(true);
}
void MainWindow::mousePressEvent(QMouseEvent* event)
{
QPoint pos = event->pos();
QPolygonF selectionPoly = selectMan.mySelectionPolygon();
QPointF curPoint = pos;
selectMan.setMoveModeForAnchorInRange(pos);
QPointF anchor = getSelectionAnchorPoint(selectionPoly, selectMan.getMoveMode());
selectMan.setTransformAnchor(anchor);
QPointF anchorPoint = selectMan.currentTransformAnchor();
mRotAngle = selectMan.angleFromPoint(curPoint, anchorPoint) - mPreviousAngle;
currentPoint = pos;
mOffset = currentPoint;
update();
}
void MainWindow::mouseMoveEvent(QMouseEvent* event)
{
QTransform t = viewInverse;
if (event->buttons() & Qt::LeftButton) {
mouseDragging = true;
}
if (mouseDragging) {
if (event->modifiers() == Qt::CTRL) {
isRotating = true;
} else {
isRotating = false;
}
}
currentPoint = event->pos();
if (mouseDragging) {
handleDragging();
}
}
void MainWindow::handleDragging()
{
qreal newAngle = 0;
if (isRotating) {
selectMan.setMoveMode(MoveMode::ROTATION);
QPolygonF mSelectionPolygon = selectMan.mySelectionPolygon();
QPointF anchorPoint = selectMan.currentTransformAnchor();
newAngle = selectMan.angleFromPoint(currentPoint, anchorPoint) - mRotAngle;
}
selectMan.adjustSelection(currentPoint, mOffset.x(), mOffset.y(), newAngle - mPreviousAngle);
mPreviousAngle = newAngle;
mOffset = currentPoint;
update();
}
QPointF MainWindow::getSelectionAnchorPoint(QVector<QPointF> selectionPolygon, MoveMode moveMode) const
{
QPointF anchorPoint;
if (moveMode == MoveMode::BOTTOMRIGHT)
{
anchorPoint = selectionPolygon[0];
}
else if (moveMode == MoveMode::BOTTOMLEFT)
{
anchorPoint = selectionPolygon[1];
}
else if (moveMode == MoveMode::TOPLEFT)
{
anchorPoint = selectionPolygon[2];
}
else if (moveMode == MoveMode::TOPRIGHT)
{
anchorPoint = selectionPolygon[3];
} else {
anchorPoint = QLineF(selectionPolygon[0], selectionPolygon[2]).pointAt(0.5);
}
return anchorPoint;
}
void MainWindow::mouseReleaseEvent(QMouseEvent* event)
{
handleDragging();
currentPoint = event->pos();
mOffset = currentPoint;
isRotating = false;
mouseDragging = false;
somethingDragged = false;
}
void MainWindow::updateMatrix()
{
QTransform transform;
transform.translate(-this->width()/2,-this->height()/2);
viewTransform = transform;
viewInverse = viewTransform.inverted();
}
void MainWindow::paintEvent(QPaintEvent* )
{
QPainter painter(this);
painter.save();
QPen pen(QColor(180, 220, 255));
painter.setPen(pen);
QTransform selectionT = selectMan.selectionTransform();
painter.setPen(Qt::yellow);
QPolygonF mappedPol = selectionT.map(selectMan.mySelectionPolygon());
painter.drawEllipse(QRectF(currentPoint,QSize(10,10)));
painter.setPen(Qt::blue);
painter.drawPolygon((selectMan.mySelectionPolygon()));
painter.setPen(Qt::red);
painter.drawPolygon(mappedPol);
painter.setPen(QColor(255,100,255));
for (int corner = 0; corner < mappedPol.count()-1; corner++) {
QRectF cornerRect(QPointF(mappedPol[corner]-QPointF(10,10)),QSizeF(20,20));
if (corner == 0) {
painter.setBrush(Qt::yellow);
} else if (corner == 1) {
painter.setBrush(Qt::green);
} else if (corner == 2) {
painter.setBrush(Qt::blue);
} else if (corner == 3) {
painter.setBrush(Qt::red);
}
painter.drawRect(cornerRect);
}
QPolygonF localPolygon = selectMan.mySelectionPolygon();
painter.save();
painter.setBrush(Qt::NoBrush);
for (int corner = 0; corner < localPolygon.count()-1; corner++) {
QRectF cornerRect(QPointF(localPolygon[corner]-QPointF(10,10)),QSizeF(20,20));
if (corner == 0) {
painter.setPen(Qt::yellow);
} else if (corner == 1) {
painter.setPen(Qt::green);
} else if (corner == 2) {
painter.setPen(Qt::blue);
} else if (corner == 3) {
painter.setPen(Qt::red);
}
painter.drawRect(cornerRect);
}
painter.restore();
paintGrid(painter, selectionT);
paintGrid(painter, QTransform());
painter.restore();
}
void MainWindow::paintGrid(QPainter& painter, QTransform transform) {
int left = this->rect().left();
int right = this->rect().right();
int top = this->rect().top();
int bottom = this->rect().bottom();
QPen pen(Qt::lightGray);
pen.setCosmetic(true);
painter.setPen(pen);
painter.setOpacity(0.5);
painter.setBrush(Qt::NoBrush);
painter.setRenderHint(QPainter::Antialiasing, false);
painter.setTransform(transform);
for (int x = left; x < right; x += 20) {
painter.drawLine(x, top, x, bottom);
}
for (int y = top; y < bottom; y += 20) {
painter.drawLine(left, y, right, y);
}
}
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.resize(500, 500);
w.show();
return a.exec();
}
Solved the problem!
I was missing some fundamental knowledge about calculating the distance between two points. Found a nice recap of how to do that by using the dot product.
Normally calculating the distance between two points will always result in a positive value but i also needed to be able to scale into negative coordinates, thus what i was looking for was the signed distance.
Here's how i solved it:
Given we define the following
a = currentPos
b = bottomRightAnchor
c = topLeftAnchor
First get the direction the vector of the current position and the bottomRightAnchor, we subtract the two vectors
abDir = (a - b)
Next we need to get the normalized direction between the topLeftAnchor and the bottomRightAnchor, which will give us the direction ranging from -1 to 1
cbDir = (c - b)
normCbDir = sqrt((cbDir.x * cbDir.x) + (cbDir.y * cbDir.y))
We can now take the dot product of those vectors which will result in the signed distance.
distance = dot(abDir, normCbDir)
Thus we now have the signed distance from the current position to the bottom right corner.
The code is mostly the same, I only had to rework the logic inside the adjustSelection function.
...
QPolygonF projectedPolygon = mapToTransform(mSelectionPolygon);
QVector2D pointVec = QVector2D(currentPoint);
QVector2D movingAnchor = QVector2D(projectedPolygon[0]);
QVector2D staticAnchor = QVector2D(projectedPolygon[1]);
QVector2D directionVecX = staticAnchor - pointVec;
// Calculates the signed distance
qreal distanceX = QVector2D::dotProduct(directionVecX, (staticAnchor - movingAnchor).normalized());
staticAnchor = QVector2D(projectedPolygon[3]);
QVector2D directionVecY = staticAnchor - pointVec;
qreal distanceY = QVector2D::dotProduct(directionVecY, (staticAnchor - movingAnchor).normalized());
qreal originWidth = mSelectionPolygon[1].x() - mSelectionPolygon[0].x();
qreal originHeight = mSelectionPolygon[3].y() - mSelectionPolygon[0].y();
scaleX = distanceX / originWidth;
scaleY = distanceY / originHeight;
...
I want to implement arc in QGraphicsScene. I want that on clicking of three points my arc should be drawn such that on clicking of three points arc is drawn where first point will be starting of arc, second will be any point on arc and third will be end point of arc. I have tried studing drawArc function but got confused with startangle and spanangle. I was unable to set them dynamically. Please suggest me some way to proceed.
I tried the solution to embend it in my project but got the following error:
error: cannot allocate an object of abstract type 'arc'
arcItem = new arc(++id, startP, midP, endP);
Can you please help me out to solve the problem. I am giving a part of code to my project.
In mousepress event of cadgraphicsscene I have done following thing.
cadgraphicsscene.cpp
void CadGraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
// mousePressEvent in the graphicsScene
if(mouseEvent->button() == Qt::LeftButton)
{
switch (entityMode)
{
case ArcMode:
if (mFirstClick)
{
startP = mouseEvent->scenePos();
mFirstClick = false;
mSecondClick = true;
}
else if (!mFirstClick && mSecondClick)
{
midP = mouseEvent->scenePos();
mFirstClick = false;
mSecondClick = false;
mThirdClick = true;
}
else if (!mSecondClick && mThirdClick)
{
endP = mouseEvent->scenePos();
mThirdClick = false;
mPaintFlag = true;
}
if (mPaintFlag)
{
arcItem = new arc(++id, startP, midP, endP);
itemList.append(arcItem);
mUndoStack->push(new CadCommandAdd(this, arcItem));
setFlags();
}
}
}
}
arc.cpp
#include "arc.h"
arc::arc(int i, QPointF point1, QPointF point2, QPointF point3)
{
// assigns id
id = i;
p1 = point1;
p2 = point2;
p3 = point3;
lineBC(point2, point3);
lineAC(point1, point3);
lineBA(point2, point1);
rad = qAbs(lineBC.length()/(2*qSin(qDegreesToRadians(lineAC.angleTo(lineBA)))));
bisectorBC(lineBC.pointAt(0.5), lineBC.p2());
bisectorBC.setAngle(lineBC.normalVector().angle());
bisectorBA(lineBA.pointAt(0.5), lineBA.p2());
bisectorBA.setAngle(lineBA.normalVector().angle());
bisectorBA.intersect(bisectorBC, ¢er);
ellipse = new QGraphicsEllipseItem(center.x() - rad, center.y() - rad, rad*2, rad*2);
lineOA(center, point1);
lineOC(center, point3);
}
arc::arc(int i, QLineF start, QLineF end)
{
// assigns id
id = i;
lineOA.angle() = start;
lineOC.angle() - lineOA.angle() = end;
}
int arc::type() const
{
// Enable the use of qgraphicsitem_cast with arc item.
return Type;
}
void arc::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
QPen paintpen;
painter->setRenderHint(QPainter::Antialiasing);
paintpen.setWidth(1);
if (isSelected())
{
// sets brush for end points
painter->setBrush(Qt::SolidPattern);
paintpen.setColor(Qt::red);
painter->setPen(paintpen);
paintpen.setStyle(Qt::DashLine);
paintpen.setColor(Qt::black);
painter->setPen(paintpen);
painter->drawArc(ellipse->boundingRect(),lineOA.angle(),lineOC.angle() - lineOA.angle());
}
else
{
painter->setBrush(Qt::SolidPattern);
paintpen.setColor(Qt::black);
painter->setPen(paintpen);
painter->drawArc(ellipse->boundingRect(),lineOA.angle(),lineOC.angle() - lineOA.angle());
}
}
arc.h
include <QGraphicsItem>
#include "qmath.h"
class arc : public QObject, public QGraphicsItem
{
Q_OBJECT
public:
arc(int, QPointF, QPointF, QPointF);
arc(int, QLineF, QLineF);
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget);
enum { Type = UserType + 6 };
int type() const;
int id;
QPointF startP, midP, endP, p1, p2, p3,center;
QLineF lineBC;
QLineF lineAC;
QLineF lineBA;
QLineF lineOA;
QLineF lineOC;
QLineF bisectorBC;
QLineF bisectorBA;
QGraphicsEllipseItem *ellipse;
qreal rad;
private:
QVector<QPointF> stuff;
};
#endif // ARC_H
Please help me out to solve the error.
This sounds like it could be solved with some relatively simple math:
https://www.google.com/search?q=define%20circle%20three%20points
https://math.stackexchange.com/a/213678
https://www.khanacademy.org/math/geometry/triangle-properties/perpendicular_bisectors/v/three-points-defining-a-circle
Here is my translation of the math into Qt goodness
// m_points is a QList<QPointF>
// use math to define the circle
QLineF lineBC(m_points.at(1), m_points.at(2));
QLineF lineAC(m_points.at(0), m_points.at(2));
QLineF lineBA(m_points.at(1), m_points.at(0));
qreal rad = qAbs(lineBC.length()/(2*qSin(qDegreesToRadians(lineAC.angleTo(lineBA)))));
QLineF bisectorBC(lineBC.pointAt(0.5), lineBC.p2());
bisectorBC.setAngle(lineBC.normalVector().angle());
QLineF bisectorBA(lineBA.pointAt(0.5), lineBA.p2());
bisectorBA.setAngle(lineBA.normalVector().angle());
QPointF center;
bisectorBA.intersect(bisectorBC, ¢er);
qDebug() << rad << center;
QT QGraphicsScene Drawing Arc
QPainterPath* path = new QPainterPath();
path->arcMoveTo(0,0,50,50,20);
path->arcTo(0,0,50,50,20, 90);
scene.addPath(*path);
Putting all of this together into a nice little project turns into this:
https://github.com/peteristhegreat/ThreePointsCircle
main.cpp
#include "mainwindow.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
return a.exec();
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = 0);
~MainWindow();
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include <QGraphicsView>
#include <QGraphicsScene>
#include "graphicsscene.h"
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
QGraphicsView * view = new QGraphicsView;
GraphicsScene * scene = new GraphicsScene();
view->setScene(scene);
view->setSceneRect(-300,-300, 300, 300);
this->resize(600, 600);
this->setCentralWidget(view);
}
MainWindow::~MainWindow()
{
}
graphicsscene.h
#ifndef GRAPHICSSCENE_H
#define GRAPHICSSCENE_H
#include <QGraphicsScene>
#include <QGraphicsSceneMouseEvent>
#include <QPointF>
#include <QList>
class GraphicsScene : public QGraphicsScene
{
Q_OBJECT
public:
explicit GraphicsScene(QObject *parent = 0);
virtual void mouseDoubleClickEvent(QGraphicsSceneMouseEvent * mouseEvent);
virtual void mouseMoveEvent(QGraphicsSceneMouseEvent * mouseEvent);
virtual void mousePressEvent(QGraphicsSceneMouseEvent * mouseEvent);
virtual void mouseReleaseEvent(QGraphicsSceneMouseEvent * mouseEvent);
signals:
public slots:
private:
QList <QPointF> m_points;
};
#endif // GRAPHICSSCENE_H
graphicsscene.cpp
#include "graphicsscene.h"
#include <QDebug>
#include <QGraphicsEllipseItem>
#include <QGraphicsPathItem>
#include <QPainterPath>
#include "qmath.h"
GraphicsScene::GraphicsScene(QObject *parent) :
QGraphicsScene(parent)
{
this->setBackgroundBrush(Qt::gray);
}
void GraphicsScene::mouseDoubleClickEvent(QGraphicsSceneMouseEvent * mouseEvent)
{
qDebug() << Q_FUNC_INFO << mouseEvent->scenePos();
QGraphicsScene::mouseDoubleClickEvent(mouseEvent);
}
void GraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent * mouseEvent)
{
qDebug() << Q_FUNC_INFO << mouseEvent->scenePos();
QGraphicsScene::mouseMoveEvent(mouseEvent);
}
void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent * mouseEvent)
{
qDebug() << Q_FUNC_INFO << mouseEvent->scenePos();
QGraphicsScene::mousePressEvent(mouseEvent);
}
void GraphicsScene::mouseReleaseEvent(QGraphicsSceneMouseEvent * me)
{
qDebug() << Q_FUNC_INFO << me->scenePos();
int radius = 20;
QGraphicsEllipseItem * ellipse = this->addEllipse(me->scenePos().x() - radius, me->scenePos().y() - radius, radius*2, radius*2);
ellipse->setBrush(Qt::white);
m_points.append(me->scenePos());
if(m_points.size() == 3)
{
// use math to define the circle
QLineF lineBC(m_points.at(1), m_points.at(2));
QLineF lineAC(m_points.at(0), m_points.at(2));
QLineF lineBA(m_points.at(1), m_points.at(0));
qreal rad = qAbs(lineBC.length()/(2*qSin(qDegreesToRadians(lineAC.angleTo(lineBA)))));
QLineF bisectorBC(lineBC.pointAt(0.5), lineBC.p2());
bisectorBC.setAngle(lineBC.normalVector().angle());
QLineF bisectorBA(lineBA.pointAt(0.5), lineBA.p2());
bisectorBA.setAngle(lineBA.normalVector().angle());
QPointF center;
bisectorBA.intersect(bisectorBC, ¢er);
qDebug() << rad << center;
bool drawCircle = true;
QGraphicsEllipseItem * ellipse = new QGraphicsEllipseItem(center.x() - rad, center.y() - rad, rad*2, rad*2);
if(drawCircle)
this->addItem(ellipse);
// add arc
// this->addItem(path);
QPainterPath path;
QLineF lineOA(center, m_points.at(0));
QLineF lineOC(center, m_points.at(2));
path.arcMoveTo(ellipse->boundingRect(),lineOA.angle());
path.arcTo(ellipse->boundingRect(), lineOA.angle(), lineOC.angle() - lineOA.angle());
QGraphicsPathItem * pathItem = new QGraphicsPathItem(path);
pathItem->setPen(QPen(Qt::red,10));
this->addItem(pathItem);
if(!drawCircle)
delete ellipse;
m_points.clear();
}
QGraphicsScene::mouseReleaseEvent(me);
}
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
I have a class, that inherits QGraphicsItem and this class needs to be inherited by another class that inherits QGraphicsLineItem and QGraphicsTextItem. When I do this it gives me the error class Line has no member named setLine
Following is the whole scenerio explained:
getEntity.h
#ifndef getEntity_H
#define getEntity_H
#include <QGraphicsItem>
class getEntity :public QObject , public QGraphicsItem
{
public:
getEntity(QObject* parent=0) : QObject(parent) {}
virtual ~getEntity() {}
virtual getEntity* my_clone() { return 0; }
};
#endif // getEntity_H
line.h
//This is my class that needs to inherits gEntity
#ifndef LINE_H
#define LINE_H
#include <QPainter>
#include <QGraphicsLineItem>
class Line : public QObject, public QGraphicsLineItem
{
Q_OBJECT
public:
Line(int, QPointF, QPointF);
QRectF boundingRect() const;
virtual void paint(QPainter *painter,
const QStyleOptionGraphicsItem *option,
QWidget *widget);
enum { Type = UserType + 2 };
int type() const;
int id;
QPointF start_p, end_p, move_p, check_p;
private:
QVector<QPointF> stuff;
};
#endif // LINE_H
line.cpp
#include "line.h"
Line::Line(int i, QPointF p1, QPointF p2)
{
// assigns id
id = i;
// set values of start point and end point of line
start_p = p1;
end_p = p2;
}
int Line::type() const
{
// Enable the use of qgraphicsitem_cast with line item.
return Type;
}
QRectF Line::boundingRect() const
{
qreal extra = 1.0;
// bounding rectangle for line
return QRectF(line().p1(), QSizeF(line().p2().x() - line().p1().x(),
line().p2().y() - line().p1().y()))
.normalized()
.adjusted(-extra, -extra, extra, extra);
}
void Line::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget)
{
// draws/paints the path of line
QPen paintpen;
painter->setRenderHint(QPainter::Antialiasing);
paintpen.setWidth(1);
if (isSelected())
{
// sets brush for end points
painter->setBrush(Qt::SolidPattern);
paintpen.setColor(Qt::red);
painter->setPen(paintpen);
painter->drawEllipse(start_p, 2, 2);
painter->drawEllipse(end_p, 2, 2);
// sets pen for line path
paintpen.setStyle(Qt::DashLine);
paintpen.setColor(Qt::black);
painter->setPen(paintpen);
painter->drawLine(start_p, end_p);
}
else
{ painter->save();
painter->setBrush(Qt::SolidPattern);
paintpen.setColor(Qt::black);
painter->setPen(paintpen);
painter->drawEllipse(start_p, 2, 2);
painter->drawEllipse(end_p, 2, 2);
painter->drawLine(start_p, end_p);
painter->restore();
}
}
cadgraphicscene.cpp
//Here it returns pointing to setLine method, when I inherit getEntity in Line class
if (mPaintFlag)
{
lineItem = new Line(++id, start_p, end_p);
lineItem->setLine(start_p.x(), start_p.y(), end_p.x(), end_p.y());
How the Line class can correctly inherit getEntity? Do help!
I'm using QGraphicView to show game map that consist QGraphicsPixmapItems. I need to show tooltip on mouse hover at QGraphicsPixmapItem.
For saving QGraphicsPixmapItem position I using MazeItem:
#ifndef MAZEITEM_H
#define MAZEITEM_H
#include <QPoint>
#include <QGraphicsItem>
class MazeItem
{
private:
QPoint myPosition;
QString myToolTip;
public:
MazeItem();
QPoint position() const;
QString toolTip() const;
void setToolTip(const QString &toolTip);
void setPosition(const QPoint &position);
QPoint getPosition();
QGraphicsPixmapItem * pixmap;
};
#endif // MAZEITEM_H
I have widget class to display game map:
#include <QWidget>
#include <QtGui>
#include <QGraphicsView>
#include <QToolTip>
#include "mazeitem.h"
class MazeGUI : public QWidget
{
Q_OBJECT
private:
QGraphicsView * graphicsView;
QGraphicsScene * graphicsScene;
QString sceneString;
int imageSize;
QList<MazeItem> mazeItems;
void addItem(int x, int y, QPixmap picture);
bool event(QEvent *event);
int itemAt(const QPoint &pos);
public:
explicit MazeGUI(QWidget *parent = 0);
void setScene(QString sceneString);
signals:
public slots:
void redraw();
};
#endif // MAZEGUI_H
In constructor I set mouse tracking.
MazeGUI::MazeGUI(QWidget *parent) :
QWidget(parent)
{
setMouseTracking(true);
...
}
This is how I add new maze item.
void MazeGUI::addItem(int x, int y, QPixmap picture)
{
MazeItem mazeItem;
mazeItem.setPosition(QPoint(x, y));
mazeItem.setToolTip("text");
mazeItem.pixmap = this->graphicsScene->addPixmap(picture);
mazeItem.pixmap->setPos(y, x);
mazeItems.append(mazeItem);
}
And this I have from Qt tutorials,
bool MazeGUI::event(QEvent *event)
{
if (event->type() == QEvent::ToolTip) {
// HERE - it never goes here!!
QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);
int index = itemAt(helpEvent->pos());
if (index != -1) {
QToolTip::showText(helpEvent->globalPos(), mazeItems[index].toolTip());
} else {
QToolTip::hideText();
event->ignore();
}
return true;
}
return QWidget::event(event);
}
int MazeGUI::itemAt(const QPoint &pos)
{
for (int i=0; i < mazeItems.size(); ++i)
{
if (mazeItems[i].getPosition() == pos)
return i;
}
return -1;
}
Was adding the tooltip on wrong object:
Instead of:
mazeItem.setToolTip("text");
It should be:
mazeItem.pixmap->setToolTip("text");
I'm creating a 2D game in QT and i'm trying to implement a drag & drop into my program.
For some reason the drop is not registered: qDebug should print a message on dropping but this doesn't happen.
#include "dialog.h"
#include "ui_dialog.h"
#include "world.h"
#include <vector>
Dialog::Dialog(QWidget *parent) : QDialog(parent), ui(new Ui::Dialog)
{
ui->setupUi(this);
scene = new QGraphicsScene(this);
ui->graphicsView->setScene(scene);
MySquare *item;
QGraphicsRectItem *enemyItem;
World *myWorld = new World();
std::vector<Tile*> tiles = myWorld->createWorld(":/texture.jpg");
int count = 0;
foreach (Tile *tile, tiles){
count++;
item = new MySquare(tile->getXPos()*4,tile->getYPos()*4,4,4);
item->setBrush(QColor(tile->getValue()*255,tile->getValue()*255,tile->getValue()*255));
item->setAcceptDrops(true);
scene->addItem(item);
}
player = new MySquare(10,20,10,10);
player->setAcceptDrops(true);
scene->addItem(player);
//drag & drop part
QPushButton *pushButton = new QPushButton("Click Me",this);
connect(pushButton,SIGNAL(pressed()),this,SLOT(makeDrag()));
setAcceptDrops(true);
}
void Dialog::makeDrag()
{
QDrag *dr = new QDrag(this);
// The data to be transferred by the drag and drop operation is contained in a QMimeData object
QMimeData *data = new QMimeData;
data->setText("This is a test");
// Assign ownership of the QMimeData object to the QDrag object.
dr->setMimeData(data);
// Start the drag and drop operation
dr->start();
}
mysquare.cpp
#include "mysquare.h"
MySquare::MySquare(int _x,int _y, int _w, int _h)
{
isPlayer=false;
Pressed=false;
setFlag(ItemIsMovable);
setFlag(ItemIsFocusable);
setAcceptDrops(true);
color=Qt::red;
color_pressed = Qt::green;
x = _x;
y = _y;
w = _w;
h = _h;
}
QRectF MySquare::boundingRect() const
{
return QRectF(x,y,w,h);
}
void MySquare::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QRectF rec = boundingRect();
QBrush brush(color);
if (Pressed){
brush.setColor(color);
} else {
brush.setColor(color_pressed);
}
painter->fillRect(rec,brush);
painter->drawRect(rec);
}
void MySquare::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
Pressed=true;
update();
QGraphicsItem::mousePressEvent(event);
qDebug() << "mouse Pressed";
}
void MySquare::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
{
Pressed=false;
update();
QGraphicsItem::mousePressEvent(event);
qDebug() << "mouse Released";
}
void MySquare::keyPressEvent(QKeyEvent *event){
int x = pos().x();
int y = pos().y();
//key handling
QGraphicsItem::keyPressEvent(event);
}
void MySquare::dropEvent(QDropEvent *event)
{
qDebug("dropEvent - square");
// Unpack dropped data and handle it the way you want
qDebug("Contents: %s", event->mimeData()->text().toLatin1().data());
}
void MySquare::dragMoveEvent(QDragMoveEvent *event){
qDebug("dragMoveEvent - square ");
event->accept();
}
void MySquare::dragEnterEvent(QDragEnterEvent *event){
event->setAccepted(true);
qDebug("dragEnterEvent - square");
event->acceptProposedAction();
}
void MySquare::setBrush(QColor _color){
color = _color;
color_pressed = _color;
update(); //repaint
}
edit; there is no problem with qDebug() i'm just using it to test them i'm inside the drag events..which i'm not
In your mouseReleaseEvent, you pass to QGraphicsItem::mousePressEvent instead of QGraphicsItem::mouseReleaseEvent
Edit: I don't know if this matters, but initialize the QGraphicsItem in your constructor
MySquare::MySquare(int _x,int _y, int _w, int _h) : QGraphicsItem()