如何在 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?