Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

The easy way to create the long cross line cursor (as long as viewport) is create a cross line graphicsItem, when mouse moved, set the item's pos property. But this way will be very slow when the scene is complex, because it should update the whole viewport to update the cursor's pos.

The another easy way is setCursor(QCursor(..)),use a QPixmap to define the long cross line, this way will very fast , but the cursor will exceed the viewport rect.

Is there another way to show a long cross line cursor fastly?

Thanks very much!

like image 341
jnblue Avatar asked Dec 17 '22 18:12

jnblue


1 Answers

If I understand correctly, you want to draw an horizontal line and a vertical line, crossing at the cursor position, and being as large as the viewport is.

A poosible solution would be to reimplement QGraphicsScene::drawForeground() to draw the two lines with the painter.

The problem is that the scene doesn't know about the mouse position. This means the view will have to track it and inform the scene when the mouse position has changed.

To do that, you'll have to create your own GraphicsScene (inheriting QGraphicsScene) and your own GraphicsView (inheriting QGraphicsView).

On your GraphicsView constructor, you'll have to start mouse tracking. This will make your you receive a 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 code snippet above, the view is emitting a signal (mousePosChanged) to which the scene will be connected. This signal contains the mouse position, converted to the scene's coordinates.

Now, on the scene side, you have to add a slot which will be called when the mouse position changed, store the new mouse position in a member variable and reimplement 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's signal to the GraphicsScene slot.

I'll let you check if this solution is acceptable performance wise.

like image 193
Jérôme Avatar answered Dec 28 '22 07:12

Jérôme