下面给出的代码是从SO上的another question派生的。
我更改了鼠标移动用于计算进度条值的方式,并添加了一个重置按钮。我注意到,鼠标滚轮以大约相同的速度和角度旋转时,并不总是以相同的方式进行评估。有时,当鼠标滚轮快速移动时,进度条“跳”到几乎100%,而在其他情况下,以几乎相同的速度和角度,它甚至达不到50%。
这种不同行为的原因是什么(以及如何解决)?
using System;
using UnityEditor;
using UnityEditor.PackageManager.Requests;
using UnityEditor.PackageManager;
using UnityEngine;
namespace Unity.Editor.Example {
static class ListPackageExample
{
static ListRequest Request;
[MenuItem("Window/List Package Example")]
static void List()
{
Request = Client.List(); // List packages installed for the Project
EditorApplication.update += Progress;
}
static void Progress()
{
if (Request.IsCompleted)
{
if (Request.Status == StatusCode.Success)
foreach (var package in Request.Result)
Debug.Log("Package name: " + package.name);
else if (Request.Status >= StatusCode.Failure)
Debug.Log(Request.Error.message);
EditorApplication.update -= Progress;
}
}
}
}
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)
wheelChanged = 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.Wheel:
self.wheelChanged.emit(event.angleDelta())
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()
self.reset_button.clicked.connect(self._reset_progress_bars)
for lay in (self.verticalLayout_top, self.verticalLayout_bottom):
view = GraphicsView()
lay.addWidget(view)
window_listener = MouseListener(self)
window_listener.posChanged.connect(self.on_pos_changed)
window_listener.wheelChanged.connect(self.on_wheel_changed)
@staticmethod
def _update_bar(progress_bar, first, second):
delta = abs(first - second)
current_value = progress_bar.value()
new_value = current_value + delta
progress_bar.setValue(new_value)
def _reset_progress_bars(self):
self.progressBar_x_plus.setValue(0)
self.progressBar_x_minus.setValue(0)
self.progressBar_y_plus.setValue(0)
self.progressBar_y_minus.setValue(0)
self.progressBar_w_plus.setValue(0)
self.progressBar_w_minus.setValue(0)
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, old_x, new_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, old_y, new_y)
if new_y < old_y:
self._update_bar(self.progressBar_y_minus, old_y, new_y)
self.global_pos = pos
def on_wheel_changed(self, pos):
new_w = pos.y()
if new_w > 0:
self._update_bar(self.progressBar_w_plus, 0, new_w)
print("W+", new_w)
if new_w < 0:
self._update_bar(self.progressBar_w_minus, 0, new_w)
print("W-", new_w)
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.horizontalScrollBar().blockSignals(True)
self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
self.verticalScrollBar().blockSignals(True)
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()
答案 0 :(得分:1)
对象收到事件后,其event()
函数将对其进行处理。
如果对象以某种方式支持该事件,则它可以返回False,这意味着该事件将由其祖先(如果有的话)处理(对于小部件,表示其直接父代),它将继续直到顶级对象,直到它们中的任何一个都返回True。
考虑到某些事件(例如QWheelEvent)可以由小部件处理,并且仍然可以“返回”给其父母(通过返回False),这与将事件设置为接受或忽略并不一定有关。
因此,当您看到“加速”值时得到的实际上是与您添加到_childrens
列表中的所有对象正在处理的同一转轮事件。
此外,您使用的是QGraphicsView(内部创建QGraphicsWheelEvent并根据场景内容和rect表现不同),它是一个滚动区域,一个复杂的窗口小部件,至少具有4个子窗口小部件:视口,滚动内容和2个滚动条。
即使您隐藏了滚动条并阻止了它们的信号,它们仍然存在并且滚动区域会与它们交互,Qt也会通过向其发送事件来进行交互。
尝试使滚动条处于活动状态和可见状态,您会发现在滚动条达到其最大值(向下滚动)或最小值(向上)之后,车轮事件的数量变大:这是因为它们无法超过当前限制,因此该事件将被其父项“忽略”(其event()
返回True)并进行处理。
在这种情况下,将按以下顺序或多或少地接收并过滤转轮事件:
通过您的实现,很难区分已经过滤或未过滤的事件,并且显然不能使用与鼠标移动相同的概念,因为没有引用以前的事件数据。
我能想到的唯一解决方案是子类化QApplication并重写其notify()
方法:
class WheelNotifyApp(QApplication):
wheelChanged = pyqtSignal(QPoint)
def notify(self, obj, event):
if event.type() == QEvent.Wheel and isinstance(obj, QWindow):
self.wheelChanged.emit(event.angleDelta())
return super().notify(obj, event)
class MainWindow(QMainWindow):
def __init__(self, parent=None):
# ...
QApplication.instance().wheelChanged.connect(self.on_wheel_changed)