在Qt GraphicsView中创建长行(或十字线)光标的最佳方法

时间:2011-01-15 04:38:33

标签: qt qgraphicsview

创建长十字线光标(只要视口)的简单方法是创建一条十字线graphicsItem,当鼠标移动时,设置项目的pos属性。 但是当场景很复杂时,这种方式会很慢,因为它应该更新整个视口以更新光标的pos

另一个简单的方法是setCursor(QCursor(..)),使用QPixmap来定义长十字线,这种方式会很快,但光标会超过视口矩形。

还有另一种快速显示长十字线光标的方法吗?

非常感谢!

3 个答案:

答案 0 :(得分:10)

如果我理解正确,你想绘制一条水平线和一条垂直线,在光标位置交叉,并且与视口一样大。

一个可行的解决方案是重新实现QGraphicsScene::drawForeground()以画画画两条线。

问题是场景不知道鼠标位置。这意味着视图必须跟踪它并在鼠标位置发生变化时通知场景。

要做到这一点,您必须创建自己的GraphicsScene(继承QGraphicsScene)和您自己的GraphicsView(继承QGraphicsView)。

GraphicsView构造函数上,您必须开始鼠标跟踪。每当鼠标在视图中移动时,这将使您收到mouseMoveEvent

GraphicsViewTrack::GraphicsViewTrack(QWidget* parent) : QGraphicsView(parent) {
    setMouseTracking(true);
}

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

正如您在上面的代码段中看到的那样,视图会发出一个信号(mousePosChanged),场景将连接到该信号。此信号包含鼠标位置,转换为场景的坐标。

现在,在场景方面,您必须添加一个将在鼠标位置更改时调用的插槽,将新鼠标位置存储在成员变量中并重新实现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
}

最后要做的是将GraphicsView的信号连接到GraphicsScene插槽。

我会让你检查这个解决方案是否可以接受。

答案 1 :(得分:1)

基于Jerome回答并使用python我在我的QGraphicsScene子类中创建了这段代码:

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()

编写适当的C ++代码应该是直截了当的。请注意,我利用了Qt Api框架传递的rect参数,并将画家剪辑到那个 区域,因为它是可见区域。

我也缓存了笔对象,因为我在其他实验中意识到,在绘画时创建对象会影响性能,并且这样做也可以让用户有机会在程序选项中设置自定义笔。

答案 2 :(得分:0)

我找到了办法! 我在Windows系统下开发,所以可以使用较低的GDI api跳出Qt的绘画系统。 细节是获得QGraphicsView的viewPort的HDC。然后在QGraphicsView的QMouseEvent中使用“MoveToEx”和“LineTo”在视口上绘制两条线,然后我应该擦除“旧”光标,使用“setROP2(HDC dc,R2_NOT)”很容易做到这一点,然后再次绘制旧的Cursor。 此方法不进入QPainter系统,因此光标下的GraphicsItems不会重新绘制。

为了解决鼠标快速移动时的填充问题,我不使用“双缓冲”。我使用QTimer在CPU空闲时更新光标。细节在QMouseEvent中,不要在时间更新光标,而是将位置存储到列表中,当CPU空闲时,将光标绘制在位置列表中

我希望这能帮助那些遇到同样问题的人。 谢谢Jérôme,他给了我QGraphicsScene的有用提示。