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

发布于 2024-10-12 03:31:49 字数 291 浏览 7 评论 0原文

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

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

有没有其他方法可以快速显示长十字线光标?

非常感谢!

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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

旧梦荧光笔 2024-10-19 03:31:49

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

一个可能的解决方案是重新实现 ​​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 插槽。

我将让您检查该解决方案在性能方面是否可接受。

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.

二智少女猫性小仙女 2024-10-19 03:31:49

基于 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 参数,并将画家剪辑到该参数
区域,因为它是要绘制的可见区域。

我还缓存了笔对象,因为我在其他实验中意识到,在绘画时创建对象会降低性能,因此您还可以让用户有机会在程序选项中设置自定义笔。

Based on Jerome 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()

It should be straighforward for you to write its appropiate C++ code. Note that I take advantage of the rect argument passed by Qt Api framework and I clip the painter to that
area, since it's the visible area to be drawn.

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.

ぽ尐不点ル 2024-10-19 03:31:49

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

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

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

I have found a way to do this!
I develops under Windows system, so can use lower GDI api jumping out of Qt's painting system.
The detail is get the HDC of the QGraphicsView's viewPort. Then in the QMouseEvent of QGraphicsView use "MoveToEx" and "LineTo" drawing two lines on the viewport,then I should do is erase the "old" cursor, It's easy to do this using "setROP2(HDC dc,R2_NOT)",then draw the old Cursor stored again.
This method doesn't enter the QPainter system, so the GraphicsItems under the cursor will not be repaint.

To solve the filker problem when the mouse moves fast, I don't use "double buffer". I used QTimer to update the cursor at CPU's idle. The detail is in QMouseEvent ,don't update the cursor at time ,but store the position to a list, When CPU idle, draw the cursor at the list of positions

I wish this will help others who meets the same problem with me.
Thanks Jérôme, who gave me useful tip of QGraphicsScene.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文