4

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

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

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

非常感谢!

4

3 回答 3

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 插槽。

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

于 2011-01-17T07:56:41.420 回答
2

基于 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++ 代码对您来说应该是直截了当的。请注意,我利用rectQt Api 框架传递的参数并将画家剪辑到该区域,因为它是要绘制的可见区域。

I also cache the pen object, since I realized in other experiments that creating objects while painting will penalty performace and doing it so you also give the user the chance to set a custom pen in your program options.

于 2011-09-16T11:52:44.513 回答
0

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

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

我希望这将帮助其他遇到与我相同问题的人。感谢 Jérôme,他给了我有用的 QGraphicsScene 提示。

于 2011-01-21T13:13:51.873 回答