问题描述
我想跟踪鼠标移动以进行计算(进度值条)而鼠标用于绘制其中之一两个QGraphicsView
.使用下面的代码,您可以在 QGraphicsView
或 中绘制 gridLayout
上的鼠标移动坐标,但不能同时进行.这怎么办?
main.py
导入系统从 PyQt5.QtCore 导入 Qt从 PyQt5.QtGui 导入 QCursor、QPainterPath、QPen从 PyQt5.QtWidgets 导入 QApplication、QMainWindow、QGraphicsView、QGraphicsScene、QGraphicsPathItem从 PyQt5.uic 导入 loadUi应用程序 = 无类 MainWindow(QMainWindow):def __init__(self, parent=None):super().__init__(parent)loadUi("mainwindow.ui", self)self.showMaximized()self.setMouseTracking(True)self.centralWidget().setAttribute(Qt.WA_MouseTracking)self._old_x = QCursor.pos().x()self._old_y = QCursor.pos().y()self.verticalLayout_top.addWidget(GraphicsView())self.verticalLayout_bottom.addWidget(GraphicsView())@静态方法def _update_bar(progress_bar, delta):current_value = progress_bar.value()new_value = current_value + deltaprogress_bar.setValue(new_value)def mouseMoveEvent(self, event):new_x = event.x()new_y = event.y()如果 new_x >self._old_x:self._update_bar(self.progressBar_x_plus, new_x - self._old_x)如果 new_x self._old_y:self._update_bar(self.progressBar_y_plus, new_y - self._old_y)如果 new_y
mainwindow.ui
更新 1:尝试在整个窗口中跟踪鼠标移动:
更新 1:尝试在整个窗口中跟踪鼠标移动:
class MainWindow(QMainWindow):def __init__(self, parent=None):super().__init__(parent)loadUi("mainwindow.ui", self)self.showMaximized()self.global_pos = QCursor.pos()用于放置(self.verticalLayout_top,self.verticalLayout_bottom):视图 = GraphicsView()listener = MouseListener(view.viewport())listener.posChanged.connect(self.on_pos_changed)Lay.addWidget(查看)window_listener = MouseListener(self)window_listener.posChanged.connect(self.on_pos_changed)
更新 2
更新 2
导入系统从 PyQt5.QtCore 导入 Qt、QObject、pyqtSignal、QPoint、QEvent从 PyQt5.QtGui 导入 QCursor、QPainterPath、QPen从 PyQt5.QtWidgets 导入 QApplication、QMainWindow、QGraphicsView、QGraphicsScene、QGraphicsPathItem、QWidget从 PyQt5.uic 导入 loadUi应用程序 = 无类鼠标监听器(QObject):posChanged = pyqtSignal(QPoint)def __init__(self, widget):super().__init__(小部件)self._widget = 小部件self._childrens = []self._setup_widget(self._widget)对于 self._widget.findChildren(QWidget) 中的 w:self._setup_widget(w)self._childrens.append(w)def _setup_widget(self, w):w.installEventFilter(self)w.setMouseTracking(True)def eventFilter(self, obj, event):# if obj in [self._widget] + self._childrens and event.type() == QEvent.MouseMove:如果 event.type() == QEvent.MouseMove:self.posChanged.emit(event.globalPos())如果 event.type() == QEvent.ChildAdded:obj = event.child()如果 obj.isWidgetType():self._setup_widget(obj)self._childrens.append(obj)如果 event.type() == QEvent.ChildRemoved:c = event.child()如果 c 在 self._childrens 中:c.removeEventFilter(self)self._childrens.remove(c)返回 super().eventFilter(obj, event)类 MainWindow(QMainWindow):def __init__(self, parent=None):super().__init__(parent)loadUi("mainwindow.ui", self)self.showMaximized()self.global_pos = QCursor.pos()用于放置(self.verticalLayout_top,self.verticalLayout_bottom):视图 = GraphicsView()listener = MouseListener(view.viewport())listener.posChanged.connect(self.on_pos_changed)Lay.addWidget(查看)# window_listener = MouseListener(self)# window_listener.posChanged.connect(self.on_pos_changed)@静态方法def _update_bar(progress_bar, delta):current_value = progress_bar.value()new_value = current_value + deltaprogress_bar.setValue(new_value)def on_pos_changed(self, pos):new_x = pos.x()new_y = pos.y()old_x = self.global_pos.x()old_y = self.global_pos.y()如果 new_x >旧_x:self._update_bar(self.progressBar_x_plus, new_x - old_x)如果 new_x old_y:self._update_bar(self.progressBar_y_plus, new_y - old_y)如果 new_y
解决方案
问题在于 mousePressEvent 不一定从父小部件传播到子小部件(该行为取决于每种类型的小部件,例如 QLabel 如果它传播鼠标事件),此外,如果您想侦听其他小部件的事件,则覆盖 mouseMoveEvent 方法的策略是有限的.
考虑到上述情况,一个可能的解决方案是使用 eventFilter 来监听任何小部件的事件,另一个改进是使用全局位置而不是本地位置,这样当鼠标从 QGraphicsView 变化时不受影响由本地坐标系.
class MouseListener(QObject):posChanged = pyqtSignal(QPoint)def __init__(self, widget):super().__init__(小部件)self._widget = 小部件self._widget.setMouseTracking(True)self._widget.installEventFilter(self)def eventFilter(self, obj, event):如果 obj 是 self._widget 和 event.type() == QEvent.MouseMove:self.posChanged.emit(event.globalPos())返回 super().eventFilter(obj, event)类 MainWindow(QMainWindow):def __init__(self, parent=None):super().__init__(parent)loadUi("mainwindow.ui", self)self.showMaximized()self.global_pos = QCursor.pos()用于放置(self.verticalLayout_top,self.verticalLayout_bottom):视图 = GraphicsView()listener = MouseListener(view.viewport())listener.posChanged.connect(self.on_pos_changed)Lay.addWidget(查看)@静态方法def _update_bar(progress_bar, delta):current_value = progress_bar.value()new_value = current_value + deltaprogress_bar.setValue(new_value)def on_pos_changed(self, pos):new_x = pos.x()new_y = pos.y()old_x = self.global_pos.x()old_y = self.global_pos.y()如果 new_x >旧_x:self._update_bar(self.progressBar_x_plus, new_x - old_x)如果 new_x old_y:self._update_bar(self.progressBar_y_plus, new_y - old_y)如果 new_y
更新:
class GraphicsView(QGraphicsView):def __init__(self):super().__init__()# ...def mousePressEvent(self, event):if event.buttons() &Qt.LeftButton:self.start = self.mapToScene(event.pos())self.path.moveTo(self.start)super().mousePressEvent(事件)def mouseMoveEvent(self, event):if event.buttons() &Qt.LeftButton:self.end = self.mapToScene(event.pos())self.path.lineTo(self.end)self.start = self.endself.item.setPath(self.path)super().mouseMoveEvent(事件)
更新:
在这种情况下,您不仅必须将 eventFilter 应用于 QGraphicsView 的视口,还必须应用于窗口的所有子项.
class MouseListener(QObject):posChanged = pyqtSignal(QPoint)def __init__(self, widget):super().__init__(小部件)self._widget = 小部件self._childrens = []self._setup_widget(self._widget)对于 self._widget.findChildren(QWidget) 中的 w:self._setup_widget(w)self._childrens.append(w)def _setup_widget(self, w):w.installEventFilter(self)w.setMouseTracking(True)def eventFilter(self, obj, event):如果 obj 在 [self._widget] + self._childrens 和 event.type() == QEvent.MouseMove:self.posChanged.emit(event.globalPos())如果 event.type() == QEvent.ChildAdded:obj = event.child()如果 obj.isWidgetType():self._setup_widget(obj)self._childrens.append(obj)如果 event.type() == QEvent.ChildRemoved:c = event.child()如果 c 在 self._childrens 中:c.removeEventFilter(self)self._childrens.remove(c)返回 super().eventFilter(obj, event)类 MainWindow(QMainWindow):def __init__(self, parent=None):super().__init__(parent)loadUi("mainwindow.ui", self)self.showMaximized()self.global_pos = QCursor.pos()侦听器 = 鼠标侦听器(自我)listener.posChanged.connect(self.on_pos_changed)用于放置(self.verticalLayout_top,self.verticalLayout_bottom):视图 = GraphicsView()Lay.addWidget(查看)# ...
I would like to track the mouse movement for calculation (of values for progress bars) while the mouse is used to draw in one of the two
QGraphicsView
. With the code below you can draw either in a QGraphicsView
or get the coordinates of mouse movements over the gridLayout
, but not both at once. How can this be done?
main.py
main.py
import sys
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QCursor, QPainterPath, QPen
from PyQt5.QtWidgets import QApplication, QMainWindow, QGraphicsView, QGraphicsScene, QGraphicsPathItem
from PyQt5.uic import loadUi
app = None
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
loadUi("mainwindow.ui", self)
self.showMaximized()
self.setMouseTracking(True)
self.centralWidget().setAttribute(Qt.WA_MouseTracking)
self._old_x = QCursor.pos().x()
self._old_y = QCursor.pos().y()
self.verticalLayout_top.addWidget(GraphicsView())
self.verticalLayout_bottom.addWidget(GraphicsView())
@staticmethod
def _update_bar(progress_bar, delta):
current_value = progress_bar.value()
new_value = current_value + delta
progress_bar.setValue(new_value)
def mouseMoveEvent(self, event):
new_x = event.x()
new_y = event.y()
if new_x > self._old_x:
self._update_bar(self.progressBar_x_plus, new_x - self._old_x)
if new_x < self._old_x:
self._update_bar(self.progressBar_x_minus, self._old_x - new_x)
if new_y > self._old_y:
self._update_bar(self.progressBar_y_plus, new_y - self._old_y)
if new_y < self._old_y:
self._update_bar(self.progressBar_y_minus, self._old_y - new_y)
self._old_x = new_x
self._old_y = new_y
class GraphicsView(QGraphicsView):
def __init__(self):
super().__init__()
self.start = None
self.end = None
self.setScene(QGraphicsScene())
self.path = QPainterPath()
self.item = GraphicsPathItem()
self.scene().addItem(self.item)
self.contents_rect = self.contentsRect()
self.setSceneRect(0, 0, self.contents_rect.width(), self.contents_rect.height())
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
def mousePressEvent(self, event):
self.start = self.mapToScene(event.pos())
self.path.moveTo(self.start)
def mouseMoveEvent(self, event):
self.end = self.mapToScene(event.pos())
self.path.lineTo(self.end)
self.start = self.end
self.item.setPath(self.path)
class GraphicsPathItem(QGraphicsPathItem):
def __init__(self):
super().__init__()
pen = QPen()
pen.setColor(Qt.black)
pen.setWidth(5)
self.setPen(pen)
def main():
global app
app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
mainwindow.ui
mainwindow.ui
<?
Update 1: Attempt to track mouse movements over the entire window:
Update 1: Attempt to track mouse movements over the entire window:
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
loadUi("mainwindow.ui", self)
self.showMaximized()
self.global_pos = QCursor.pos()
for lay in (self.verticalLayout_top, self.verticalLayout_bottom):
view = GraphicsView()
listener = MouseListener(view.viewport())
listener.posChanged.connect(self.on_pos_changed)
lay.addWidget(view)
window_listener = MouseListener(self)
window_listener.posChanged.connect(self.on_pos_changed)
Update 2
Update 2
import sys
from PyQt5.QtCore import Qt, QObject, pyqtSignal, QPoint, QEvent
from PyQt5.QtGui import QCursor, QPainterPath, QPen
from PyQt5.QtWidgets import QApplication, QMainWindow, QGraphicsView, QGraphicsScene, QGraphicsPathItem, QWidget
from PyQt5.uic import loadUi
app = None
class MouseListener(QObject):
posChanged = pyqtSignal(QPoint)
def __init__(self, widget):
super().__init__(widget)
self._widget = widget
self._childrens = []
self._setup_widget(self._widget)
for w in self._widget.findChildren(QWidget):
self._setup_widget(w)
self._childrens.append(w)
def _setup_widget(self, w):
w.installEventFilter(self)
w.setMouseTracking(True)
def eventFilter(self, obj, event):
# if obj in [self._widget] + self._childrens and event.type() == QEvent.MouseMove:
if event.type() == QEvent.MouseMove:
self.posChanged.emit(event.globalPos())
if event.type() == QEvent.ChildAdded:
obj = event.child()
if obj.isWidgetType():
self._setup_widget(obj)
self._childrens.append(obj)
if event.type() == QEvent.ChildRemoved:
c = event.child()
if c in self._childrens:
c.removeEventFilter(self)
self._childrens.remove(c)
return super().eventFilter(obj, event)
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
loadUi("mainwindow.ui", self)
self.showMaximized()
self.global_pos = QCursor.pos()
for lay in (self.verticalLayout_top, self.verticalLayout_bottom):
view = GraphicsView()
listener = MouseListener(view.viewport())
listener.posChanged.connect(self.on_pos_changed)
lay.addWidget(view)
# window_listener = MouseListener(self)
# window_listener.posChanged.connect(self.on_pos_changed)
@staticmethod
def _update_bar(progress_bar, delta):
current_value = progress_bar.value()
new_value = current_value + delta
progress_bar.setValue(new_value)
def on_pos_changed(self, pos):
new_x = pos.x()
new_y = pos.y()
old_x = self.global_pos.x()
old_y = self.global_pos.y()
if new_x > old_x:
self._update_bar(self.progressBar_x_plus, new_x - old_x)
if new_x < old_x:
self._update_bar(self.progressBar_x_minus, old_x - new_x)
if new_y > old_y:
self._update_bar(self.progressBar_y_plus, new_y - old_y)
if new_y < old_y:
self._update_bar(self.progressBar_y_minus, old_y - new_y)
self.global_pos = pos
class GraphicsView(QGraphicsView):
def __init__(self):
super().__init__()
self.start = None
self.end = None
self.setScene(QGraphicsScene())
self.path = QPainterPath()
self.item = GraphicsPathItem()
self.scene().addItem(self.item)
self.contents_rect = self.contentsRect()
self.setSceneRect(0, 0, self.contents_rect.width(), self.contents_rect.height())
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
def mousePressEvent(self, event):
if event.buttons() & Qt.LeftButton:
self.start = self.mapToScene(event.pos())
self.path.moveTo(self.start)
# super().mousePressEvent(event)
def mouseMoveEvent(self, event):
if event.buttons() & Qt.LeftButton:
self.end = self.mapToScene(event.pos())
self.path.lineTo(self.end)
self.start = self.end
self.item.setPath(self.path)
# super().mouseMoveEvent(event)
class GraphicsPathItem(QGraphicsPathItem):
def __init__(self):
super().__init__()
pen = QPen()
pen.setColor(Qt.black)
pen.setWidth(5)
self.setPen(pen)
def main():
global app
app = QApplication(sys.argv)
main_window = MainWindow()
main_window.show()
sys.exit(app.exec_())
if __name__ == "__main__":
main()
解决方案
The problem is that the mousePressEvent does not necessarily propagate from the parent widget to the child widget (that behavior depends on each type of widget, for example QLabel if it propagates the mouse events), in addition to your strategy of overriding the mouseMoveEvent method is limited if you want Listen to the events of other widgets.
Considering the above, a possible solution is to use an eventFilter to listen to the events of any widget, and another improvement is to use the global position instead of the local one so that when the mouse changes from QGraphicsView it is not affected by the local coordinate system.
class MouseListener(QObject):
posChanged = pyqtSignal(QPoint)
def __init__(self, widget):
super().__init__(widget)
self._widget = widget
self._widget.setMouseTracking(True)
self._widget.installEventFilter(self)
def eventFilter(self, obj, event):
if obj is self._widget and event.type() == QEvent.MouseMove:
self.posChanged.emit(event.globalPos())
return super().eventFilter(obj, event)
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
loadUi("mainwindow.ui", self)
self.showMaximized()
self.global_pos = QCursor.pos()
for lay in (self.verticalLayout_top, self.verticalLayout_bottom):
view = GraphicsView()
listener = MouseListener(view.viewport())
listener.posChanged.connect(self.on_pos_changed)
lay.addWidget(view)
@staticmethod
def _update_bar(progress_bar, delta):
current_value = progress_bar.value()
new_value = current_value + delta
progress_bar.setValue(new_value)
def on_pos_changed(self, pos):
new_x = pos.x()
new_y = pos.y()
old_x = self.global_pos.x()
old_y = self.global_pos.y()
if new_x > old_x:
self._update_bar(self.progressBar_x_plus, new_x - old_x)
if new_x < old_x:
self._update_bar(self.progressBar_x_minus, old_x - new_x)
if new_y > old_y:
self._update_bar(self.progressBar_y_plus, new_y - old_y)
if new_y < old_y:
self._update_bar(self.progressBar_y_minus, old_y - new_y)
self.global_pos = pos
Update:
class GraphicsView(QGraphicsView):
def __init__(self):
super().__init__()
# ...
def mousePressEvent(self, event):
if event.buttons() & Qt.LeftButton:
self.start = self.mapToScene(event.pos())
self.path.moveTo(self.start)
super().mousePressEvent(event)
def mouseMoveEvent(self, event):
if event.buttons() & Qt.LeftButton:
self.end = self.mapToScene(event.pos())
self.path.lineTo(self.end)
self.start = self.end
self.item.setPath(self.path)
super().mouseMoveEvent(event)
Update:
In this case you must apply the eventFilter not only to the viewport of the QGraphicsView but to all the children of the window.
class MouseListener(QObject):
posChanged = pyqtSignal(QPoint)
def __init__(self, widget):
super().__init__(widget)
self._widget = widget
self._childrens = []
self._setup_widget(self._widget)
for w in self._widget.findChildren(QWidget):
self._setup_widget(w)
self._childrens.append(w)
def _setup_widget(self, w):
w.installEventFilter(self)
w.setMouseTracking(True)
def eventFilter(self, obj, event):
if obj in [self._widget] + self._childrens and event.type() == QEvent.MouseMove:
self.posChanged.emit(event.globalPos())
if event.type() == QEvent.ChildAdded:
obj = event.child()
if obj.isWidgetType():
self._setup_widget(obj)
self._childrens.append(obj)
if event.type() == QEvent.ChildRemoved:
c = event.child()
if c in self._childrens:
c.removeEventFilter(self)
self._childrens.remove(c)
return super().eventFilter(obj, event)
class MainWindow(QMainWindow):
def __init__(self, parent=None):
super().__init__(parent)
loadUi("mainwindow.ui", self)
self.showMaximized()
self.global_pos = QCursor.pos()
listener = MouseListener(self)
listener.posChanged.connect(self.on_pos_changed)
for lay in (self.verticalLayout_top, self.verticalLayout_bottom):
view = GraphicsView()
lay.addWidget(view)
# ...
这篇关于同时在多个小部件中跟踪鼠标移动的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!