The given code below is derived from another question on SO.
I have changed the way the mouse movements are used to calculate values for the progress bars and added a reset button. I noticed that mouse wheel rotations with approximately the same speed and angle are not always evaluated in the same way. Sometimes the progress bar "jumps" to almost 100% when the mouse wheel is moved quickly, and in other cases at almost the same speed and angle it does not even reach 50%.
What is the reason for this different behaviour (and how to fix it)?
main.py
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()
mainwindow.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1003</width>
<height>703</height>
</rect>
</property>
<property name="windowTitle">
<string>Mouse Pointer</string>
</property>
<property name="locale">
<locale language="English" country="UnitedKingdom"/>
</property>
<widget class="QWidget" name="centralwidget">
<layout class="QGridLayout" name="gridLayout_2">
<item row="3" column="0">
<layout class="QVBoxLayout" name="verticalLayout_bottom"/>
</item>
<item row="0" column="0">
<layout class="QVBoxLayout" name="verticalLayout_top"/>
</item>
<item row="1" column="0">
<layout class="QGridLayout" name="gridLayout">
<item row="2" column="1">
<widget class="QProgressBar" name="progressBar_y_plus">
<property name="maximum">
<number>1000</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="1" column="1">
<widget class="QProgressBar" name="progressBar_x_minus">
<property name="maximum">
<number>1000</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="0" column="1">
<widget class="QProgressBar" name="progressBar_x_plus">
<property name="maximum">
<number>1000</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="3" column="0">
<widget class="QLabel" name="label_y_minus">
<property name="text">
<string>Y-</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="label_x_plus">
<property name="text">
<string>X+</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QProgressBar" name="progressBar_y_minus">
<property name="maximum">
<number>1000</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="label_y_plus">
<property name="text">
<string>Y+</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="label_x_minus">
<property name="text">
<string>X-</string>
</property>
<property name="alignment">
<set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QProgressBar" name="progressBar_w_plus">
<property name="maximum">
<number>10000</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="5" column="1">
<widget class="QProgressBar" name="progressBar_w_minus">
<property name="maximum">
<number>10000</number>
</property>
<property name="value">
<number>0</number>
</property>
</widget>
</item>
<item row="4" column="0">
<widget class="QLabel" name="label_w_plus">
<property name="text">
<string>W+</string>
</property>
</widget>
</item>
<item row="5" column="0">
<widget class="QLabel" name="label_w_minus">
<property name="text">
<string>W-</string>
</property>
</widget>
</item>
</layout>
</item>
<item row="2" column="0">
<widget class="QPushButton" name="reset_button">
<property name="text">
<string>Reset bars</string>
</property>
</widget>
</item>
</layout>
</widget>
<widget class="QMenuBar" name="menubar">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>1003</width>
<height>24</height>
</rect>
</property>
</widget>
<widget class="QStatusBar" name="statusbar"/>
</widget>
<resources/>
<connections/>
</ui>