Best way to create a long (or cross) cursor in a Qt GraphicsView

An easy way to create a long cross cursor (while the viewport is) is to create a graphicsItem cross line when the mouse moves, set the item pos property. But this method will be very slow if the scene is complicated because it needs to refresh the entire viewport in order to refresh the pos cursor.

Another simple way is setCursor(QCursor(..)) , using QPixmap to define a long transverse line, this method will be very fast, but the cursor will exceed the viewport rectangle.

Is there any other way to quickly display a long cross cursor?

Thank you very much!

+4
source share
3 answers

If I understand correctly, you want to draw a horizontal line and a vertical line intersecting at the cursor position and the size of the viewport.

A clear solution would be to override QGraphicsScene :: drawForeground () to draw two lines using the painter.

The problem is that the scene is not aware of the position of the mouse. This means that the view will have to track it and report the scene when the mouse position has changed.

To do this, you need to create your own GraphicsScene (inheriting from QGraphicsScene ) and your own GraphicsView (inheriting from QGraphicsView ).

On your GraphicsView constructor, you will need to start mouse tracking. This will force you to receive mouseMoveEvent each time the mouse moves inside the view:

 GraphicsViewTrack::GraphicsViewTrack(QWidget* parent) : QGraphicsView(parent) { setMouseTracking(true); } void GraphicsViewTrack::mouseMoveEvent(QMouseEvent* pEvent) { QPointF MousePos = this->mapToScene(pEvent->pos()); emit mousePosChanged(MousePos.toPoint()); } 

As you can see in the above code snippet, the view emits a signal ( mousePosChanged ) to which the scene will connect. This signal contains the mouse position converted to the scene coordinates.

Now, on the scene side, you need to add a slot that will be called when the mouse position changes, save the new mouse position in the member variable and override QGraphicsScene :: drawForeground () :

 void GraphicsSceneCross::drawForeground(QPainter* painter, const QRectF& rect) { QRectF SceneRect = this->sceneRect(); painter->setPen(QPen(Qt::black, 1)); painter->drawLine(SceneRect.left(), m_MousePos.y(), SceneRect.right(), m_MousePos.y()); painter->drawLine(m_MousePos.x(), SceneRect.top(), m_MousePos.x(), SceneRect.bottom()); } void GraphicsSceneCross::onMouseChanged(QPoint NewMousePos) { m_MousePos = NewMousePos; // Store the mouse position in a member variable invalidate(); // Tells the scene it should be redrawn } 

The last thing to do is connect the GraphicsView signal to the GraphicsScene slot.

I will let you check if this solution is acceptable.

+10
source

Based on Jerome's answer and using python, I created this code in my QGraphicsScene subclass:

 def drawForeground(self, painter, rect): if self.guidesEnabled: painter.setClipRect(rect) painter.setPen(self.guidePen) painter.drawLine(self.coords.x(), rect.top(), self.coords.x(), rect.bottom()) painter.drawLine(rect.left(), self.coords.y(), rect.right(), self.coords.y()) def mouseMoveEvent(self, event): self.coords = event.scenePos() self.invalidate() 

The appropriate C ++ code should be written for you. Note that I am using the rect argument passed by the Qt Api map, and I am pinning the artist to this area, since this is the visible area to be drawn.

I also cache the pen object, because in other experiments, I realized that creating objects while drawing will execute a penalty and do this so that you also provide the user with the ability to customize a custom pen in your program’s settings.

+1
source

I found a way to do this! I am developing a system for Windows, so I can use a lower GDI api jumping from the Qt paint system. Detail - get HDC QGraphicsView viewPort. Then in QMouseEvent from QGraphicsView use "MoveToEx" and "LineTo", drawing two lines in the viewport, then I will need to erase the "old" cursor. This is easy to do using "setROP2 (HDC dc, R2_NOT)", then re-write the old cursor. This method is not part of the QPainter system, so GraphicsItems will not be redrawn under the cursor.

To solve the filter problem, when the mouse moves fast, I do not use a “double buffer”. I used QTimer to update the cursor when the processor was idle. The part is in QMouseEvent, do not update the cursor in time, but save the position in the list. When the CPU is idle, draw a cursor in the list of positions

I would like this to help other people who are facing the same problem with me. Thanks to Jerome, who gave me some helpful QGraphicsScene advice.

0
source

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


All Articles