如何在 QPainter 上实现 mouseMoveEvent?

Posted

技术标签:

【中文标题】如何在 QPainter 上实现 mouseMoveEvent?【英文标题】:How to implement mouseMoveEvent on QPainter? 【发布时间】:2021-12-27 13:29:27 【问题描述】:

我对这个question 有同样的问题,我发现这个answer 对这个问题很有帮助。

我已尝试运行答案中提供的代码并且它有效。

提供的answer中包含的代码:

import random
import sys

from PyQt5 import QtCore, QtGui, QtWidgets


class Window(QtWidgets.QMainWindow):
    def __init__(self):
        super(Window, self).__init__()

        self.rect = QtCore.QRect()
        self.drag_position = QtCore.QPoint()

        button = QtWidgets.QPushButton("Add", self)
        button.clicked.connect(self.on_clicked)

        self.resize(640, 480)

    @QtCore.pyqtSlot()
    def on_clicked(self):
        if True:
            self.rect = QtCore.QRect(
                QtCore.QPoint(*random.sample(range(200), 2)), QtCore.QSize(100, 100)
            )
            self.update()

    def paintEvent(self, event):
        super().paintEvent(event)
        if not self.rect.isNull():
            painter = QtGui.QPainter(self)
            painter.setRenderHint(QtGui.QPainter.Antialiasing)
            painter.setPen(QtGui.QPen(QtCore.Qt.black, 5, QtCore.Qt.SolidLine))
            painter.drawEllipse(self.rect)

    def mousePressEvent(self, event):
        print(1)
        if (
            2 * QtGui.QVector2D(event.pos() - self.rect.center()).length()
            < self.rect.width()
        ):
            self.drag_position = event.pos() - self.rect.topLeft()
        super().mousePressEvent(event)

    def mouseMoveEvent(self, event):
        print(2)
        if not self.drag_position.isNull():
            self.rect.moveTopLeft(event.pos() - self.drag_position)
            self.update()
        super().mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):
        print(3)
        self.drag_position = QtCore.QPoint()
        super().mouseReleaseEvent(event)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    Rect = Window()
    Rect.show()
    sys.exit(app.exec_())

我的问题:

拖动时如何移动这些“点”。我的第一个想法是将QPainter 子类化并在该类中调整事件,但是当我阅读文档时,我发现没有可以移动“点”的方法。我现在真的被困在这里,我找不到正确的实现。

【问题讨论】:

我相信你误解了什么是QPainter:它是一个只执行绘图操作的类。它不“存储”任何东西,也不能像鼠标事件那样与用户输入交互:它就像一个“素描艺术家”,你给​​他们一个画布(小部件)并告诉他们要画什么。如果你想“移动点”,你必须以某种方式存储它们的数据,以便你可以访问它并修改它,并最终绘制它(通常在调用update()之后发生)。 @musicamante - Ahhh,在阅读了一些笔记和文档之后,我终于明白了 QPainter 是什么。正如您所说,我需要将这些“点”放在QRect 上,然后将其位置添加到列表中。我不清楚的一件事是我怎么知道QRect 是否被点击? 如果您要在任何鼠标事件处理程序中检查它,请使用if event.pos() in someRect:。请注意,如果您使用 PySide(更严格一点)它不会实现 __contains__ 魔术方法if x in y 对应于 y.__contains__(x)),因此您需要使用 @ 987654324@:if someRect.contains(event.pos()):。对于不涉及直接鼠标事件的任何其他情况,您需要将 当前 光标位置映射到本地坐标:pos = self.mapFromGlobal(QCursor.pos()) 澄清我在第一条评论中写的内容:在绘画完成后重用 QPainter 是一种很好的做法,如 docs also suggests:“记得销毁 QPainter绘制后的对象”,所以无论如何你都不应该在其中“存储”任何东西(并且使用类属性来做这件事不是一个好主意,除非你真的知道什么你正在做的)。可以“保留”并最终重用 QPainter 的特定情况很少(但根据我的经验,我并不直接需要),但对于一般用途,这是一个基本的“不要”。 @musicamante - 感谢您的指导!我会努力追随并记住这一点。我将尝试在上面的示例中实现这一点,看看它是否有效。谢谢! 【参考方案1】:

我已经尝试在问题中包含的代码中实现@musicamante 在 cmets 中所说的关于使用 if event.pos() in someRect: 的内容,我最终得到了下面的代码。

用法:当您单击小部件中的任意位置时会出现一个点。您可以通过单击停止并拖动点来移动点。

实施:

import sys
from PyQt5 import QtWidgets, QtGui, QtCore


class GUI(QtWidgets.QMainWindow):
    def __init__(self):
        super().__init__()
        self.setFixedSize(self.size())
        self.stop = False
        self.button = QtWidgets.QPushButton("Stop", self)
        self.button.clicked.connect(self.stop_)
        self.selected_item_index = None
        self.there_is_selected = False
        self.list_ = [] # list of points
        self.show()

    def stop_(self):
        if self.stop:
            self.stop = False
        if not self.stop:
            self.stop = True

    def mousePressEvent(self, event):
        if not self.stop: 
            self.rect = QtCore.QRect(QtCore.QPoint(event.x() + 5, event.y() + 5), QtCore.QPoint(event.x() - 5, event.y() - 5))
            self.list_.append(self.rect)
            print(self.list_)
            #self.points << e.pos()
            self.update()
        elif self.stop:
            index = -1 # I set -1 so the first iteration in the for loop will be equal to 0
            for item in self.list_:
                index += 1
                if event.pos() in item:
                    print("there is selected")
                    self.selected_item_index = index
                    self.there_is_selected = True
                    break
                else:
                    self.selected_item_index = None
                    self.there_is_selected = False
        print(f"---- There is selected : self.there_is_selected in the index : self.selected_item_index")

    def mouseMoveEvent(self, event):
        if self.there_is_selected:
            self.list_[self.selected_item_index] = QtCore.QRect(QtCore.QPoint(event.x() + 5, event.y() + 5), QtCore.QPoint(event.x() - 5, event.y() - 5))
            self.update()

    def paintEvent(self, ev):
        qp = QtGui.QPainter(self)
        qp.setRenderHint(QtGui.QPainter.Antialiasing)
        pen = QtGui.QPen(QtCore.Qt.red, 3)
        brush = QtGui.QBrush(QtCore.Qt.red)
        qp.setPen(pen)
        qp.setBrush(brush)
        for i in self.list_:
            qp.drawEllipse(i)
        # or 
        # qp.drawPoints(self.points)


if __name__ == '__main__':
    app = QtWidgets.QApplication(sys.argv)
    window = GUI()
    sys.exit(app.exec_())

编辑:修复了启动时的崩溃

上面的实现工作得很好,但我仍然不确定并且很好奇它是否是正确的实现。老实说,我也不确定这是否适用于体面的计算机,因为我不确定它是否是正确的实现方式。

【讨论】:

我刚刚意识到它在启动时立即崩溃。刚刚修好了。

以上是关于如何在 QPainter 上实现 mouseMoveEvent?的主要内容,如果未能解决你的问题,请参考以下文章

如何在半视图上实现滑动手势和在另一半视图上实现平移手势?

如何在 BaseAdapter 上实现 getFilter?

如何在 React 中处理父级的 mouseMove 事件?

如何在 Windows 上实现 RPC 客户端

如何在约束布局上实现重叠/负边距?

如何在 Android 1.5 上实现推送?