📜 ⬆️ ⬇️

Qt Graphics View Framework - the dark side. Part 2

Having started our journey, we do not stop and continue to explore the dark side of the documentation. Somewhere they may be characteristic of the whole Qt, and somewhere only the Graphics View is inherent. But one way or another, meeting them is not always painless.

Case number 3


We want to write our item, as an example we write the following code
MainWindow.h
class MainWindow : public QWidget { Q_OBJECT public: explicit MainWindow(QWidget *parent = 0); ~MainWindow(); private: QGraphicsScene *m_scene; QGraphicsRectItem *m_rect; QGraphicsItem *m_cross; QGraphicsView * graphicsView; }; 

MainWindow.cpp
 class CrossItem: public QGraphicsItem { public: QRectF boundingRect() const { return QRectF(0, 0, 30*scale(), 30*scale()); } void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(option) Q_UNUSED(widget); painter->setPen(QColor(Qt::red)); painter->drawLine(10, 0, 20, 0); painter->drawLine(20*scale(), 0*scale(), 20*scale(), 10*scale()); painter->drawLine(20, 10, 30, 10); painter->drawLine(30, 10, 30, 20); painter->drawLine(30, 20, 20, 20); painter->drawLine(20, 20, 20, 30); painter->drawLine(20, 30, 10, 30); painter->drawLine(10, 30, 10, 20); painter->drawLine(10, 20, 0, 20); painter->drawLine( 0, 20, 0, 10); painter->drawLine( 0, 10, 10, 10); painter->drawLine(10, 10, 10, 0); } }; MainWindow::MainWindow(QWidget *parent) : QWidget(parent) { setLayout(new QGridLayout()); graphicsView = new QGraphicsView(); layout()->addWidget(graphicsView); graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_scene = new QGraphicsScene(); m_cross = new CrossItem(); m_scene->addItem(m_cross); // m_cross->setScale(2); m_rect = new QGraphicsRectItem(m_cross->boundingRect(), m_cross); m_scene->addItem(m_rect); m_scene->setSceneRect(m_rect->boundingRect()); graphicsView->setScene(m_scene); } MainWindow::~MainWindow() { } 

Compile, run, see the square inscribed in the cross. Uncomment the line and rebuild the project to see all our errors after the launch.
The fact is that:
 painter->drawLine(20*scale(), 0*scale(), 20*scale(), 10*scale()); 

and
 return QRectF(0, 0, 30*scale(), 30*scale()); 

- woe from the mind.

Let's take a look at how QGraphicsItem stores transformation data:

 struct QGraphicsItemPrivate::TransformData { QTransform transform; qreal scale; qreal rotation; qreal xOrigin; qreal yOrigin; QList<QGraphicsTransform *> graphicsTransforms; bool onlyTransform; TransformData() : scale(1.0), rotation(0.0), xOrigin(0.0), yOrigin(0.0), onlyTransform(true) { } QTransform computedFullTransform(QTransform *postmultiplyTransform = 0) const { if (onlyTransform) { if (!postmultiplyTransform || postmultiplyTransform->isIdentity()) return transform; if (transform.isIdentity()) return *postmultiplyTransform; return transform * *postmultiplyTransform; } QTransform x(transform); if (!graphicsTransforms.isEmpty()) { QMatrix4x4 m; for (int i = 0; i < graphicsTransforms.size(); ++i) graphicsTransforms.at(i)->applyTo(&m); x *= m.toTransform(); } x.translate(xOrigin, yOrigin); x.rotate(rotation); x.scale(scale, scale); x.translate(-xOrigin, -yOrigin); if (postmultiplyTransform) x *= *postmultiplyTransform; return x; } }; 

Everything is very simple and unsophisticated, like the mooing of a cow in a meadow. And actually, before drawing a scene, the computedFullTransform method tugs and, after performing a couple of shamanic actions, slips it into QPainter. Here we can note that if we want to do something tricky with the shape of our item-a, then we should use the methods setScale , setPos , setRotation and setTransforms and more we don’t need anything.
')

Case number 4


We want to track the movement of the mouse on the item. Immediately I warn you - the rake here is laid out literally at every turn. And they beat me very painfully and very hard. But I will not describe the full list of ordeals. I’ll dwell only on key points.
We rewrite Mainwindow.cpp as follows:
 #include "MainWindow.h" #include <QGridLayout> #include <QGraphicsSceneHoverEvent> #include <QDebug> class MyRect: public QGraphicsRectItem { public: MyRect(const QRectF &rect, QGraphicsItem *parent=0):QGraphicsRectItem(rect, parent){} protected: void hoverMoveEvent(QGraphicsSceneHoverEvent *event) { qDebug()<<"rect"<<event->pos(); QGraphicsRectItem::hoverMoveEvent(event); } }; class CrossItem: public QGraphicsItem { public: QRectF boundingRect() const { return QRectF(0, 0, 30, 30); } void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(option) Q_UNUSED(widget); painter->setPen(QColor(Qt::red)); painter->drawLine(10, 0, 20, 0); painter->drawLine(20, 0, 20, 10); painter->drawLine(20, 10, 30, 10); painter->drawLine(30, 10, 30, 20); painter->drawLine(30, 20, 20, 20); painter->drawLine(20, 20, 20, 30); painter->drawLine(20, 30, 10, 30); painter->drawLine(10, 30, 10, 20); painter->drawLine(10, 20, 0, 20); painter->drawLine( 0, 20, 0, 10); painter->drawLine( 0, 10, 10, 10); painter->drawLine(10, 10, 10, 0); } protected: void hoverMoveEvent(QGraphicsSceneHoverEvent *event) { qDebug()<<"cross"<<event->pos(); QGraphicsItem::hoverMoveEvent(event); } }; MainWindow::MainWindow(QWidget *parent) : QWidget(parent) { setLayout(new QGridLayout()); graphicsView = new QGraphicsView(); layout()->addWidget(graphicsView); graphicsView->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); graphicsView->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); m_scene = new QGraphicsScene(); m_cross = new CrossItem(); m_rect = new MyRect(m_cross->boundingRect(), m_cross); m_cross->setAcceptHoverEvents(true); m_rect->setAcceptHoverEvents(true); m_scene->addItem(m_cross); m_scene->setSceneRect(m_rect->boundingRect()); graphicsView->setScene(m_scene); } MainWindow::~MainWindow() { } 

We follow the general logic that scene events are complete analogs of ordinary events. What do we happily tell the dock on QGraphicsSceneHoverEvent .

Compile, run and see that when you move the mouse, the event only reaches the rectangle. We climbed into the dock, and somewhere in the backside, when describing the setAcceptHoverEvents method , we read:

Leave your children to receive them. The parent doesn’t receive a case, however; the parent stays "hovered" until the cursor leaves its area, including its children's areas.

It’s not up to you to go for a child. children.

A QGraphicsWidget with window decorations will accept events regardless of the value of acceptHoverEvents ().

So, we need to track the events of the child. Realizing this and taking into account the other, we are looking for a method that will help us. And this is found, this is setFiltersChildEvents (not to be confused with similar setHandlesChildEvents , we will not find fame on that path). Add it to the constructor:
 m_cross->setFiltersChildEvents(true); 

And also add a filter to the CrossItem class:
  bool sceneEventFilter(QGraphicsItem *watched, QEvent *event) { if(event->type() == QEvent::GraphicsSceneHoverMove) qDebug()<<"cross"<<event; return false; } 

Compile, run. Hooray! Earned !!!

Total


Unfortunately, to sum up the constructive result, as in the last article, now I will fail. Rather, this is a bewilderment - having spent considerable efforts on the independence of the item from any geometric transformations, the Qt developers offered us a very strange system for delivering events to item-s. Although the example above is rather an exceptional case, one glance at the flags in QGraphicsItem is enough to think hard.

Source: https://habr.com/ru/post/183432/


All Articles