Can't show QGraphicsItem's tooltip - qt

I subclass a QGraphicsItem to Node, but when I set it's tooltip, it can't show tooltip when mouse touch it.
There is some of my code:
class Node : public QGraphicsItem {
public:
Node(int id);
~Node() {}
int id;
private:
QRectF boundingRect() const;
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
};
void Node::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget) {
QPen pen(Qt::red);
painter->setPen(pen);
painter->setBrush(Qt::red);
painter->drawRoundRect(-10, -10, 10, 10);
}
QRectF Node::boundingRect() const {
QRectF rect;
rect.translate(-rect.center());
return rect;
}
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent), ui(new Ui::MainWindow) {
ui->setupUi(this);
view = new QGraphicsView(this);
scene = new QGraphicsScene(0, 0, 2000, 2000);
view->setScene(scene);
setCentralWidget(view);
generateMap();
ui->actionQuery_path->setToolTip(
"query the shortest path between to loactions");
view->show();
}
void MainWindow::generateMap() {
// waterhome
Node *waterhome = new Node(1);
waterhome->setToolTip("开水房");
waterhome->moveBy(100, 470);
scene->addItem(waterhome);
}
Now I can see my node, but can't see it's tooltip even the action's tooltip, I try to increase the Node's Z value, but it doesn't help, what's wrong?

The problem is with your boudingRect() function. Have a look at it and try to guess what is returns back.
If you copy-paste following code then the tooltip should appear:
QRectF Node::boundingRect() const
{
qreal penWidth = 1;
return QRectF(-10 - penWidth / 2, -10 - penWidth / 2,
20 + penWidth, 20 + penWidth);
}
void Node::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QPen pen(Qt::red);
painter->setPen(pen);
painter->setBrush(Qt::red);
painter->drawRoundedRect(-10, -10, 20, 20, 2, 2);
}

Related

How to change QPainterPath color after clicking on QPushButton

I subclassed a QPushButton so that I was able to re-implement the paintEvent(QPaintEvent *paint) method also advised by the official documentation.
Below is the sequence of operations:
a) After I launch the application the button is below:
b) This is after I hover on it:
c) then I click the button:
d) and finally I release the mouse
e) go away from the button
However the problem is that the QPainterPath with which I designed the green box it should be red after I release the button. And, of course, it should become green again after I click again the button.
Below the code:
custombutton.h
class CustomButton : public QPushButton
{
Q_OBJECT
public:
CustomButton(QWidget *parent = nullptr);
~CustomButton();
QString FirstName = "MACHINE";
QString LastName = "CONTROL";
protected:
void paintEvent(QPaintEvent *);
};
custombutton.cpp
CustomButton::CustomButton(QWidget *parent) : QPushButton(parent)
{
setGeometry(150, 150, 110, 110);
setAttribute(Qt::WA_TranslucentBackground);
setStyleSheet(
"QPushButton{background-color: lightGray;border: 1px solid black; border-radius: 5px;}"
"QPushButton:hover{background-color: gray;}"
"QPushButton:pressed{background-color: lightGray;}");
}
CustomButton::~CustomButton()
{
}
void CustomButton::paintEvent(QPaintEvent *paint)
{
QPushButton::paintEvent(paint);
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
QPainterPath path;
path.addRoundedRect(QRectF(15, 15, 80, 5), 2, 2);
QPen pen(Qt::black, 0.3);
p.setPen(pen);
p.fillPath(path, Qt::darkGreen);
p.drawPath(path);
p.save();
p.drawText(QPoint(20, 60), FirstName);
p.drawText(QPoint(20, 80), LastName);
p.setFont(QFont("Arial", 10));
p.restore();
}
mainwindow.h
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
public slots:
void onClickedButton();
private:
Ui::MainWindow *ui;
CustomButton *newBtn;
};
mainwindow.cpp
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
newBtn = new CustomButton(this);
newBtn->show();
connect(newBtn, &QPushButton::clicked, this, &MainWindow::onClickedButton);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::onClickedButton()
{
QPushButton* target = qobject_cast<QPushButton*>(sender());
if (target != nullptr)
{
QPainter p;
p.setRenderHint(QPainter::Antialiasing);
QPainterPath path;
path.addRoundedRect(QRectF(15, 15, 80, 5), 2, 2);
QPen pen(Qt::black, 0.3);
p.setPen(pen);
p.fillPath(path, Qt::darkRed);
p.drawPath(path);
p.save();
p.restore();
}
}
As you see in the MainWindow I created a function onClickedButton() which I thought would have triggered the QPainterPath into red after clicked. And in order to do that I casted it into an object (qobject_cast). But unfortunately it didn't work as expexted.
The solution is to create a flag that indicates the color and call update() for repainting:
#ifndef CUSTOMBUTTON_H
#define CUSTOMBUTTON_H
#include <QPushButton>
class CustomButton : public QPushButton
{
Q_OBJECT
public:
CustomButton(QWidget *parent = nullptr);
~CustomButton();
QString FirstName = "MACHINE";
QString LastName = "CONTROL";
private Q_SLOTS:
void handleClicked();
protected:
void paintEvent(QPaintEvent *);
private:
bool state;
};
#endif // CUSTOMBUTTON_H
#include "custombutton.h"
#include <QPainter>
#include <QPainterPath>
CustomButton::CustomButton(QWidget *parent) : QPushButton(parent), state(true)
{
setGeometry(150, 150, 110, 110);
setAttribute(Qt::WA_TranslucentBackground);
setStyleSheet(
"QPushButton{background-color: lightGray;border: 1px solid black; border-radius: 5px;}"
"QPushButton:hover{background-color: gray;}"
"QPushButton:pressed{background-color: lightGray;}");
connect(this, &QPushButton::clicked, this, &CustomButton::handleClicked);
}
CustomButton::~CustomButton()
{
}
void CustomButton::handleClicked()
{
state = !state;
update();
}
void CustomButton::paintEvent(QPaintEvent *paint)
{
QPushButton::paintEvent(paint);
QPainter p(this);
p.setRenderHint(QPainter::Antialiasing);
QPainterPath path;
path.addRoundedRect(QRectF(15, 15, 80, 5), 2, 2);
QPen pen(Qt::black, 0.3);
p.setPen(pen);
p.fillPath(path, state ? Qt::darkGreen: Qt::darkRed);
p.drawPath(path);
p.drawText(QPoint(20, 60), FirstName);
p.drawText(QPoint(20, 80), LastName);
p.setFont(QFont("Arial", 10));
}

Using both MouseMoveEvent for QGraphicsScene and HoverEnterEvent for QGraphicsItem

I'm trying to create a program in which you can connect points together with lines. I instantiate QGraphicsEllipseItem into a QGraphicsScene and I use HoverEnterEvent and HoverLeaveEvent to change the color and the size of the ellipses when the mouse is over them. To draw a temporary line between the point clicked and the mouse cursor I have to use MouseMoveEvent into the scene. However, when I do that, the HoverEvents of the items don't work anymore ! How can I use both MouseMoveEvent of the scene and HoverEvents of the items ?
void GraphicsScene::mousePressEvent(QGraphicsSceneMouseEvent *mouseEvent)
{
for (int i = 0; i < pointList.size(); ++i) {
if (pointList[i]->over == 1){
pointList[i]->press();
lineActivated=true;
tempLine.setLine(pointList[i]->x(),pointList[i]->y(),pointList[i]->x(),pointList[i]->y());
}
}
}
void GraphicsScene::mouseMoveEvent(QGraphicsSceneMouseEvent *event)
{
if(lineActivated){
const QPointF pos = event->scenePos();
tempLine.setP2(pos);
}
}
void Point::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
pen.setColor(Qt::green);
pen.setWidth(2);
this->setPen(pen);
over=true;
qDebug("enter");
update();
}
void Point::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
if(isClicked==false){
pen.setColor(Qt::lightGray);
pen.setWidth(1);
over=false;
this->setPen(pen);
qDebug("leave");
update();
}
}
By default QGraphicsScene::mouseMoveEvent sends the necessary information to handle the hover event of the items but override that method you eliminate that behavior. The solution is to call the parent method.
#include <QtWidgets>
class GraphicsScene: public QGraphicsScene{
public:
using QGraphicsScene::QGraphicsScene;
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event){
if(!m_lineitem){
m_lineitem = new QGraphicsLineItem;
addItem(m_lineitem);
}
QLineF l(event->scenePos(), event->scenePos());
m_lineitem->setLine(l);
QGraphicsScene::mousePressEvent(event);
}
void mouseMoveEvent(QGraphicsSceneMouseEvent *event){
if(m_lineitem){
QLineF l(m_lineitem->line().p1(), event->scenePos());
m_lineitem->setLine(l);
}
QGraphicsScene::mouseMoveEvent(event);
}
private:
QGraphicsLineItem *m_lineitem = nullptr;
};
class Point: public QGraphicsEllipseItem{
public:
Point(QGraphicsItem *parent=nullptr): QGraphicsEllipseItem(parent){
setRect(QRectF(-5, -5, 10, 10));
QPen pen;
pen.setColor(Qt::lightGray);
pen.setWidth(1);
setPen(pen);
setAcceptHoverEvents(true);
}
protected:
void hoverEnterEvent(QGraphicsSceneHoverEvent *event){
QPen pen;
pen.setColor(Qt::green);
pen.setWidth(2);
setPen(pen);
QGraphicsEllipseItem::hoverEnterEvent(event);
}
void hoverLeaveEvent(QGraphicsSceneHoverEvent *event){
QPen pen;
pen.setColor(Qt::lightGray);
pen.setWidth(1);
setPen(pen);
QGraphicsEllipseItem::hoverLeaveEvent(event);
}
};
int main(int argc, char *argv[]){
QApplication a(argc, argv);
GraphicsScene *scene = new GraphicsScene;
QGraphicsView w(scene);
w.setRenderHint(QPainter::Antialiasing, true);
w.fitInView(QRectF(0, 0, 100, 100), Qt::KeepAspectRatio);
for(const QPointF & p: {QPointF(10.0, 10.0), QPointF(90.0, 20.0), QPointF(30.0, 40.0)}){
Point *it = new Point();
scene->addItem(it);
it->setPos(p);
}
w.resize(640, 480);
w.show();
return a.exec();
}

Why doesn't child QGraphicsObject repaint its background correctly

The QGraphicsObject in green rectangle, it is the parent of the QGraphicsObject in red rectangle.
childInRed->setParentItem(this);
When I drag the parent object in green rect and move it fast, the background of the child object in red rect is not repainted correctly.
I know I can use update in the parent's mouseMoveEvent force the child to repaint. But this is not good, because I don't need to repaint the parent at all.
#include "asdf.h"
#include <QtWidgets/QGraphicsScene>
#include <QtWidgets/QGraphicsView>
#include <QtWidgets>
class CTestGraphicsObject : public QGraphicsObject
{
public:
QColor m_c;
CTestGraphicsObject(QColor c)
: QGraphicsObject(NULL)
, m_c(c)
{
setFlag(QGraphicsItem::ItemIsMovable, true);
setFlag(QGraphicsItem::ItemIsFocusable, true);
setFlag(QGraphicsItem::ItemIsSelectable, true);
auto effect = new QGraphicsDropShadowEffect;
effect->setOffset(4, 4);
effect->setBlurRadius(20);
setGraphicsEffect(effect);
}
virtual QRectF boundingRect() const override
{
auto rc = QRectF(0, 0, 100, 100);
return rc;
}
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
{
painter->setPen(QPen(m_c));
painter->drawRect(this->boundingRect());
}
};
asdf::asdf(QWidget *parent)
: QMainWindow(parent)
{
ui.setupUi(this);
auto s = new QGraphicsScene(this);
auto v = new QGraphicsView;
v->setScene(s);
CTestGraphicsObject* pParent = new CTestGraphicsObject(Qt::green);
CTestGraphicsObject* pChild = new CTestGraphicsObject(Qt::red);
pChild->setParentItem(pParent);
pChild->setPos(0, 100);
s->addItem(pParent);
s->addItem(pChild);
QVBoxLayout* l = new QVBoxLayout(this->centralWidget());
l->addWidget(v);
}
asdf::~asdf()
{
}
The QGraphicsDropShadowEffect causes this problem, It seems I'm not using it in right way.
According to the Qt documentation, the scene uses the bounding rect and region to define the area to repainted when an item is updated (moved in your case).
If you child is outside its parent, the scene will miss some part when repainting...
Extend the bouding rect/region to cover its children.
If you do something like that, it will work:
virtual QRectF boundingRect() const override
{
if (this->childrenBoundingRect().isEmpty()) // No children
return QRectF(0, 0, 100, 100);
return QRectF(0, 0, 100, 100).united(this->childrenBoundingRect());
}
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) override
{
painter->setPen(QPen(m_c));
painter->drawRect(QRectF(0, 0, 100, 100));
}

Apply QGraphicsColorizeEffect to a part of a QGraphicsSvgItem (only to the svg itself)

I have a QGraphicsSvgItem subclass where I would like to modify the svg color. I want to use for that QGraphicsColorizeEffect, and it works great.
My items also have a custom selection rectangle, highlighted - similar to other item types.
When I apply the colorize effect, the highlight also turns to the same color...
I have tried to setEnabled(false); in paint but it seems to have no effect.
sample code:
file mysvg.h
#ifndef MYSVG_H
#define MYSVG_H
#include <QGraphicsSvgItem>
#include <QGraphicsColorizeEffect>
class MySvg : public QGraphicsSvgItem
{
public:
MySvg();
~MySvg();
virtual void paint(QPainter* painter,
const QStyleOptionGraphicsItem* option,
QWidget* widget = NULL);
private:
QGraphicsColorizeEffect* m_effect;
void drawSelectionRectangle(QPainter* painter, const QStyleOptionGraphicsItem* option, const QRectF& rectangle);
};
#endif // MYSVG_H
file mysvg.cpp
#include <QStyleOptionGraphicsItem>
#include <QStyle>
#include <QPainterPath>
#include <QPainter>
#include <QFileDialog>
#include <QSvgRenderer>
MySvg::MySvg()
{
m_effect = new QGraphicsColorizeEffect();
m_effect->setColor(Qt::red);
setGraphicsEffect(m_effect);
setFlags(QGraphicsItem::ItemIsMovable |
QGraphicsItem::ItemIsFocusable |
QGraphicsItem::ItemIsSelectable);
QString filename = QFileDialog::getOpenFileName(0, tr("Open Svg File"),
QString(), tr("Svg files (*.svg *.svgz)"));
setSharedRenderer(new QSvgRenderer(filename));
}
MySvg::~MySvg()
{
delete renderer();
delete m_effect;
}
void MySvg::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QStyleOptionGraphicsItem opt(*option);
opt.state = QStyle::State_None;
QGraphicsSvgItem::paint(painter, &opt, widget);
//m_effect->setEnabled(false); // no effect though seemed logical
QRectF rectangle = boundingRect();
if (option->state & (QStyle::State_Selected))
drawSelectionRectangle(painter, option, rectangle);
//m_effect->setEnabled(true);
}
void MySvg::drawSelectionRectangle(QPainter *painter, const QStyleOptionGraphicsItem *option, const QRectF &rectangle)
{
painter->setPen(QPen(option->palette.windowText(), 0, Qt::DotLine));
painter->setBrush(QColor(255, 188, 0, 50));
painter->drawRect(rectangle);
}
file main.cpp
#include <QApplication>
#include <QGraphicsView>
#include "mysvg.h"
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
QGraphicsScene s;
QGraphicsView view;
view.setScene(&s);
s.setSceneRect(-50, -50, 500, 650);
view.show();
MySvg* svg = new MySvg();
s.addItem(svg);
return app.exec();
}
file mysvg.pro
QT += core gui svg
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = mysvg
TEMPLATE = app
SOURCES += main.cpp \
mysvg.cpp
HEADERS += mysvg.h
I have considered making a QGraphicsSvgItem a private member of the MySvg item - but the MySvg item has to do a lot of other things, and I don't know what to do with the renderer (who would own it...) - if I can figure out how to make a QGraphicsSvgItem subclass a member of the MySvg class, I can apply the colorize to the member and perform all other operations on the MySvg item...
Please help me figure a way to apply color to the svg, but not other drawing portion of the item.
Edit - I have tried to add a member item to the class and apply the colorize effect to the member - but it doesn't apply the colorize effect at all... The svg loads with all original colors.
Here is the code containing a member item:
new mysvg.h
class SvgMember : public QGraphicsSvgItem
{
public:
SvgMember (const QByteArray &content, const QColor& c);
~SvgMember ();
private:
QGraphicsColorizeEffect* m_effect;
};
class MySvg : public QGraphicsItem
{
public:
MySvg();
~MySvg();
virtual void paint(QPainter* painter,
const QStyleOptionGraphicsItem* option,
QWidget* widget = NULL);
virtual QRectF boundingRect() const;
virtual QPainterPath shape() const;
private:
void drawSelectionRectangle(QPainter* painter, const QStyleOptionGraphicsItem* option, const QRectF& rectangle);
SvgMember * m_member;
};
new mysvg.cpp
MySvg::MySvg()
{
setFlags(QGraphicsItem::ItemIsMovable |
QGraphicsItem::ItemIsFocusable |
QGraphicsItem::ItemIsSelectable);
QString filename = QFileDialog::getOpenFileName(0, QObject::tr("Open Svg File"),
QString(), QObject::tr("Svg files (*.svg *.svgz)"));
QFile f(filename);
f.open(QFile::ReadOnly | QFile::Text);
QByteArray svgContents = f.readAll();
f.close();
m_member = new SvgMember (svgContents, Qt::red);
}
MySvg::~MySvg()
{
delete m_member;
}
void MySvg::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
QStyleOptionGraphicsItem opt(*option);
opt.state = QStyle::State_None;
m_member->paint(painter, &opt, widget);
QRectF rectangle = boundingRect();
if (option->state & (QStyle::State_Selected))
drawSelectionRectangle(painter, option, rectangle);
}
/*! \brief reimplemented to use member rectangle */
QRectF MySvg::boundingRect() const
{
return m_member->boundingRect();
}
/*! \brief reimplemented to use member shape */
QPainterPath MySvg::shape() const
{
return m_member->shape();
}
void MySvg::drawSelectionRectangle(QPainter *painter, const QStyleOptionGraphicsItem *option, const QRectF &rectangle)
{
painter->setPen(QPen(option->palette.windowText(), 0, Qt::DotLine));
painter->setBrush(QColor(255, 188, 0, 50));
painter->drawRect(rectangle);
}
SvgMember ::SvgMember (const QByteArray &content, const QColor &c)
{
m_effect = new QGraphicsColorizeEffect();
setGraphicsEffect(m_effect);
m_effect->setColor(c);
setSharedRenderer(new QSvgRenderer(content));
}
SvgMember ::~SvgMember ()
{
delete renderer();
delete m_effect;
}
What can I do to apply the colorize effect to the svg - but not to the selection rectangle ?
The effect is a feature of the base class QGraphicsItem. It is applied to the entire graphics item and all its children. So, everything that is painted inside the item is affected by its effect.
The selection rectangle should be painted outside of the SVG item object.
It can be achieved by enclosing QGraphicsSvgItem by the composite class QGraphicsItemGroup.
When a QGraphicsItem is added to QGraphicsItemGroup it becomes reparented. Thus the item is destroyed when the group object is destroyed. So, it is not needed to delete it manually. QGraphicsItem takes ownership of effect, so it is not needed to delete the effect object.
The following class MyGraphicsItemGroup works as you expect.
Implementation "mygraphicsitemgroup.cpp"
#include "mygraphicsitemgroup.h"
#include <QGraphicsColorizeEffect>
#include <QGraphicsSvgItem>
#include <QStyleOptionGraphicsItem>
#include <QPainter>
#include <QFileDialog>
MyGraphicsItemGroup::MyGraphicsItemGroup()
{
setFlags(QGraphicsItem::ItemIsMovable |
QGraphicsItem::ItemIsFocusable |
QGraphicsItem::ItemIsSelectable);
QString filename = QFileDialog::getOpenFileName(0,
QObject::tr("Open Svg File"), QString(),
QObject::tr("Svg files (*.svg *.svgz)"));
QGraphicsColorizeEffect *effect = new QGraphicsColorizeEffect();
effect->setColor(Qt::red);
QGraphicsSvgItem *svg = new QGraphicsSvgItem(filename);
svg->setGraphicsEffect(effect);
addToGroup(svg);
}
void MyGraphicsItemGroup::paint(QPainter* painter,
const QStyleOptionGraphicsItem* option, QWidget* widget)
{
QStyleOptionGraphicsItem opt(*option);
opt.state = QStyle::State_None;
QGraphicsItemGroup::paint(painter, &opt, widget);
QRectF rectangle = boundingRect();
if (option->state & QStyle::State_Selected)
drawSelectionRectangle(painter, option, rectangle);
}
void MyGraphicsItemGroup::drawSelectionRectangle(QPainter *painter,
const QStyleOptionGraphicsItem *option, const QRectF &rectangle)
{
painter->setPen(QPen(option->palette.windowText(), 0, Qt::DotLine));
painter->setBrush(QColor(255, 188, 0, 50));
painter->drawRect(rectangle);
}
Header "mygraphicsitemgroup.h"
#ifndef MYGRAPHICSITEMGROUP_H
#define MYGRAPHICSITEMGROUP_H
#include <QGraphicsItemGroup>
class MyGraphicsItemGroup : public QGraphicsItemGroup
{
public:
MyGraphicsItemGroup();
virtual void paint(QPainter* painter,
const QStyleOptionGraphicsItem* option, QWidget* widget);
void drawSelectionRectangle(QPainter *painter,
const QStyleOptionGraphicsItem *option, const QRectF &rectangle);
};
#endif // MYGRAPHICSITEMGROUP_H

Snapping to grid doesn't work

I am trying to do snapping on grid such that whatever I will draw it should take only gridpoints and no other points. I have made grid in cadgraphicsscene.cpp and made different class for snapping.
My grid is made as follows:
cadgraphicscene.cpp
void CadGraphicsScene::drawBackground(QPainter *painter, const QRectF &rect)
{
const int gridSize = 50;
const int realLeft = static_cast<int>(std::floor(rect.left()));
const int realRight = static_cast<int>(std::ceil(rect.right()));
const int realTop = static_cast<int>(std::floor(rect.top()));
const int realBottom = static_cast<int>(std::ceil(rect.bottom()));
// Draw grid.
const int firstLeftGridLine = realLeft - (realLeft % gridSize);
const int firstTopGridLine = realTop - (realTop % gridSize);
QVarLengthArray<QLine, 100> lines;
for (qreal x = firstLeftGridLine; x <= realRight; x += gridSize)
lines.append(QLine(x, realTop, x, realBottom));
for (qreal y = firstTopGridLine; y <= realBottom; y += gridSize)
lines.append(QLine(realLeft, y, realRight, y));
painter->setPen(QPen(QColor(220, 220, 220), 0.0));
painter->drawLines(lines.data(), lines.size());
// Draw axes.
painter->setPen(QPen(Qt::lightGray, 0.0));
painter->drawLine(0, realTop, 0, realBottom);
painter->drawLine(realLeft, 0, realRight, 0);
}
My snap class looks as follows:
snap.cpp
#include "snap.h"
#include <QApplication>
Snap::Snap(const QRect& rect, QGraphicsItem* parent,
QGraphicsScene* scene):
QGraphicsRectItem(QRectF())
{
setFlags(QGraphicsItem::ItemIsSelectable |
QGraphicsItem::ItemIsMovable |
QGraphicsItem::ItemSendsGeometryChanges);
}
void Snap::mousePressEvent(QGraphicsSceneMouseEvent *event){
offset = pos() - computeTopLeftGridPoint(pos());
QGraphicsRectItem::mousePressEvent(event);
}
QVariant Snap::itemChange(GraphicsItemChange change,
const QVariant &value)
{
if (change == ItemPositionChange && scene()) {
QPointF newPos = value.toPointF();
if(QApplication::mouseButtons() == Qt::LeftButton &&
qobject_cast<CadGraphicsScene*> (scene())){
QPointF closestPoint = computeTopLeftGridPoint(newPos);
return closestPoint+=offset;
}
else
return newPos;
}
else
return QGraphicsItem::itemChange(change, value);
}
QPointF Snap::computeTopLeftGridPoint(const QPointF& pointP){
CadGraphicsScene* customScene = qobject_cast<CadGraphicsScene*> (scene());
int gridSize = customScene->getGridSize();
qreal xV = floor(pointP.x()/gridSize)*gridSize;
qreal yV = floor(pointP.y()/gridSize)*gridSize;
return QPointF(xV, yV);
}
snap.h
#ifndef SNAP_H
#define SNAP_H
#include <QGraphicsRectItem>
#include "cadgraphicsscene.h"
class Snap : public QGraphicsRectItem
{
public:
Snap(const QRect& rect, QGraphicsItem* parent,
QGraphicsScene* scene);
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event);
QVariant itemChange(GraphicsItemChange change,
const QVariant &value);
private:
QPointF offset;
QPointF computeTopLeftGridPoint(const QPointF &pointP);
};
#endif // SNAP_H
But nothing happened, no snapping is done. Can you please help me in the above?
One of the problems is that you pass QRect() to the QGraphicsRectItem constructor in the initialization list of Snap class. This means it will have 0 width and height. Instead pass the same QRect object that you pass to your snap constructor:
Snap::Snap(const QRect& rect, QGraphicsItem* parent, QGraphicsScene* scene) :
QGraphicsRectItem(rect)
You also don't seem to use the parent and the scene arguments so you might as well leave them out:
Snap::Snap(const QRect& rect) :
QGraphicsRectItem(rect)
Or if you plan to use the parent for something then you can set a default value to 0 in the declaration:
Snap(const QRect& rect, QGraphicsItem* parent = 0);
Then pass them both to the base class constructor:
Snap::Snap(const QRect& rect, QGraphicsItem* parent) :
QGraphicsRectItem(rect, parent)
snap.h
#ifndef SNAP_H
#define SNAP_H
#include <QGraphicsRectItem>
class Snap : public QGraphicsRectItem
{
public:
Snap(const QRect &rect, QGraphicsItem *parent = 0);
protected:
void mousePressEvent(QGraphicsSceneMouseEvent *event);
QVariant itemChange(GraphicsItemChange change,
const QVariant &value);
private:
QPointF offset;
QPointF computeTopLeftGridPoint(const QPointF &pointP);
};
#endif // SNAP_H
snap.cpp
#include "snap.h"
#include <QDebug>
#include <qmath.h>
Snap::Snap(const QRect& rect, QGraphicsItem* parent) :
QGraphicsRectItem(rect, parent)
{
setFlags(QGraphicsItem::ItemIsSelectable |
QGraphicsItem::ItemIsMovable |
QGraphicsItem::ItemSendsGeometryChanges);
}
void Snap::mousePressEvent(QGraphicsSceneMouseEvent *event){
offset = pos() - computeTopLeftGridPoint(pos());
QGraphicsRectItem::mousePressEvent(event);
}
QVariant Snap::itemChange(GraphicsItemChange change,
const QVariant &value)
{
qDebug()<<"inside itemChange";
if (change == ItemPositionChange && scene())
{
QPointF newPos = value.toPointF();
QPointF closestPoint = computeTopLeftGridPoint(newPos);
return closestPoint+=offset;
}
else
return QGraphicsItem::itemChange(change, value);
}
QPointF Snap::computeTopLeftGridPoint(const QPointF& pointP){
int gridSize = 100;
qreal xV = qFloor(pointP.x()/gridSize)*gridSize;
qreal yV = qFloor(pointP.y()/gridSize)*gridSize;
return QPointF(xV, yV);
}
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QGraphicsView>
#include <QLayout>
#include "snap.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
centralWidget()->setLayout(new QVBoxLayout);
QGraphicsView *view = new QGraphicsView(this);
centralWidget()->layout()->addWidget(view);
Snap *snap = new Snap(QRect(0,0,100,100));
view->setScene(new QGraphicsScene);
view->scene()->addItem(snap);
}
MainWindow::~MainWindow()
{
delete ui;
}

Resources