在Qt GraphicsView中创建长线(或交叉线)光标的最佳方法
创建长十字线光标(与视口一样长)的简单方法是创建十字线在Qt GraphicsView中创建长线(或交叉线)光标的最佳方法,qt,qgraphicsview,Qt,Qgraphicsview,创建长十字线光标(与视口一样长)的简单方法是创建十字线graphicsItem,当鼠标移动时,设置项目的pos属性。 但是当场景复杂时,这种方法会非常慢,因为它应该更新整个视口以更新光标的pos 另一种简单的方法是setCursor(QCursor(..),使用QPixmap定义长十字线,这种方法速度非常快,但光标将超过视口矩形 有没有其他快速显示长十字光标的方法 非常感谢 如果我理解正确,您希望绘制一条水平线和一条垂直线,在光标位置交叉,并与视口一样大 一个可行的解决方案是重新实现,与画家画两
graphicsItem
,当鼠标移动时,设置项目的pos
属性。
但是当场景复杂时,这种方法会非常慢,因为它应该更新整个视口以更新光标的pos
另一种简单的方法是setCursor(QCursor(..)
,使用QPixmap
定义长十字线,这种方法速度非常快,但光标将超过视口矩形
有没有其他快速显示长十字光标的方法
非常感谢 如果我理解正确,您希望绘制一条水平线和一条垂直线,在光标位置交叉,并与视口一样大 一个可行的解决方案是重新实现,与画家画两条线 问题是场景不知道鼠标的位置。这意味着视图必须跟踪它,并在鼠标位置更改时通知场景 为此,您必须创建自己的
graphicscene
(继承qgraphicscene
)和自己的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
),场景将连接到该信号。此信号包含转换为场景坐标的鼠标位置
现在,在场景端,您必须添加一个在鼠标位置更改时调用的插槽,将新鼠标位置存储在成员变量中,然后重新实现:
最后要做的事情是将GraphicsView的信号连接到Graphicscene插槽
我将让您检查此解决方案在性能方面是否可以接受。我找到了一种方法! 我在Windows系统下开发,所以可以使用较低的GDIAPI跳出Qt的绘图系统。 详细信息是获取QGraphicsView视口的HDC。然后在QGraphicsView的QMouseEvent中使用“MoveToEx”和“LineTo”在视口上绘制两条线,然后我应该做的是擦除“旧”光标,使用“setROP2(HDC dc,R2_NOT)”很容易做到这一点,然后再次绘制存储的旧光标。 此方法不会进入QPaint系统,因此光标下的GraphicsSites不会被重新绘制 为了解决鼠标快速移动时的窃取问题,我没有使用“双缓冲区”。我使用QTimer在CPU空闲时更新光标。详细信息在QMouseEvent中,此时不更新光标,而是将位置存储到列表中,当CPU空闲时,在位置列表中绘制光标 我希望这能帮助其他遇到同样问题的人。
感谢Jérôme,他给了我关于QGraphicscene的有用提示。基于Jerome的回答并使用python,我在我的
QGraphicscene
子类中创建了这段代码:
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++代码。注意,我利用了qtapi框架传递的rect
参数,并将画师剪辑到该参数
面积,因为它是要绘制的可见区域
我还缓存笔对象,因为我在其他实验中意识到,在绘制时创建对象会影响性能,因此,用户也有机会在程序选项中设置自定义笔。再次设置光标有什么问题?您可以在QWidget上设置光标,这样您就可以在QGraphicsView::viewport()返回的小部件上设置光标。感谢您的建议,setCursor函数不会将光标剪切到小部件上,因此光标(长线)将超出视口,在桌面上绘制等。您的解决方案只比我的第一个方法快一点,因为drawforeground()不需要重新创建所有项目的BSPIndex,但问题是invalidate(),此方法将调用update(),因此视口中的所有项目都将被重新绘制。@Jérôme:不需要执行信号/插槽之类的操作
qgraphicscene
只需要实现每个鼠标事件相关的方法。唯一需要的是子类化qgraphicscene
并实现mouseMoveEvent
请参见API文档。@jnblue,您可以调用invalidateScene并传入qgraphicscene::ForegroundLayer。这只会更新前景层
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()