butterfly_viewer.aux_trackers

Trackers and signalers for SplitView.

Not intended as a script. Used in Butterfly Viewer and Registrator.

Creates widgets to track and signal events in SplitView, primarily for mouse movement.

  1#!/usr/bin/env python3
  2
  3"""Trackers and signalers for SplitView.
  4
  5Not intended as a script. Used in Butterfly Viewer and Registrator.
  6
  7Creates widgets to track and signal events in SplitView, primarily for mouse movement.
  8"""
  9# SPDX-License-Identifier: GPL-3.0-or-later
 10
 11
 12
 13from PyQt5 import QtCore, QtGui
 14
 15
 16
 17class EventTracker(QtCore.QObject):
 18    """Track and signal mouse movement and clicks.
 19    
 20    Args:
 21        widget (QWidget or child class thereof): Widget of which to track and signal mouse events.
 22    """
 23
 24    mouse_position_changed = QtCore.pyqtSignal(QtCore.QPoint)
 25    mouse_leaved = QtCore.pyqtSignal()
 26    mouse_entered = QtCore.pyqtSignal()
 27
 28    def __init__(self, widget):
 29        super().__init__(widget)
 30        self._widget = widget
 31        self.widget.setMouseTracking(True)
 32        self.widget.installEventFilter(self)
 33
 34    @property
 35    def widget(self):
 36        return self._widget
 37
 38    def eventFilter(self, source, event):
 39        if source is self.widget and event.type() == QtCore.QEvent.MouseMove:
 40            self.mouse_position_changed.emit(event.pos())
 41        if source is self.widget and event.type() == QtCore.QEvent.Leave:
 42            self.mouse_leaved.emit()
 43        if source is self.widget and event.type() == QtCore.QEvent.Enter:
 44            self.mouse_entered.emit()
 45        return super().eventFilter(source, event)
 46
 47
 48
 49class EventTrackerSplitBypassDeadzone(QtCore.QObject):
 50    """Limit the reported global position of the mouse to within a widget's bounds.
 51    
 52    This class is intended to help track mouse movement in the deadzones along the SplitView's borders.
 53    These deadzones fix the issue of resize handles appearing in QMdiArea; however, 
 54    these deadzones hide the mouse from the view, so the mouse must be separately tracked 
 55    to ensure the split is updated.
 56    The mouse position is bounded to include only positions within the widget to fix issues with positions 
 57    reported as outside the bounds.
 58
 59    Args:
 60        widget (QWidget or child class thereof): Widget of which to track and signal mouse events (intended to be the resize_deadzone in SplitView).
 61    """
 62
 63    def __init__(self, widget):
 64        super().__init__(widget)
 65        self._widget = widget
 66        self.widget.setMouseTracking(True)
 67        self.widget.installEventFilter(self)
 68
 69    mouse_position_changed_global = QtCore.pyqtSignal(QtCore.QPoint)
 70
 71    @property
 72    def widget(self):
 73        return self._widget
 74
 75    def width(self):
 76        return self.widget.width()
 77
 78    def height(self):
 79        return self.widget.height()
 80
 81    def eventFilter(self, source, event):
 82        """Override eventFilter to limit the reported position of mouse movement.
 83        
 84        Args:
 85            source (PyQt source)
 86            event (PyQt event)
 87
 88        Returns:
 89            The base eventFilter using source and event (passes it along to PyQt).
 90        """
 91        pos = QtGui.QCursor.pos()
 92        if event.type() == QtCore.QEvent.MouseMove:
 93            pos = self.limit_mouse_position_to_within_widget_bounds(pos) # Prevent erroneous mouse tracking outside the widget
 94            self.mouse_position_changed_global.emit(pos)
 95
 96        return super().eventFilter(source, event)
 97
 98
 99    def limit_mouse_position_to_within_widget_bounds(self, pos):
100        """Return a given global mouse position which is limited to within the widget's borders.
101        
102        Args:
103            pos (QPoint): The position of the mouse in global coordinates.
104
105        Returns:
106            pos_global_bounded (QPoint): The position of the mouse in global coordinates limited ("floored") to within the widget borders.
107        """
108        pos_global = pos
109        pos_widget = self.widget.mapFromGlobal(pos_global)
110        x_bounded = max(min(pos_widget.x(), self.width()), 0)
111        y_bounded = max(min(pos_widget.y(), self.height()), 0)
112        pos_widget.setX(x_bounded)
113        pos_widget.setY(y_bounded)
114        pos_global_bounded = self.widget.mapToGlobal(pos_widget)
115        return pos_global_bounded
116
117
118
119class EventTrackerSplitBypassInterface(QtCore.QObject):
120    """Track mouse events while over widgets (for example, interface elements).
121
122    Needed to track the split of the sliding overlay while mouse is hovering over interface widgets.
123    Prevents the split from skipping and stopping when entering and exiting interface elements.
124    
125    Args:
126        widget (QWidget or child class thereof): The widget over which to track mouse movement.
127    """
128
129    mouse_position_changed = QtCore.pyqtSignal()
130    propagate_mouse_press_event = QtCore.pyqtSignal(QtCore.QEvent)
131
132    def __init__(self, widget):
133        super().__init__(widget)
134        self._widget = widget
135        self.widget.setMouseTracking(True)
136        self.widget.installEventFilter(self)
137
138    @property
139    def widget(self):
140        return self._widget
141
142    def eventFilter(self, source, event):
143        """"Override event filter to emit mouse movement and button press events.
144                
145        See parent method for full documentation.
146        
147        Args:
148            source (PyQt source)
149            event (PyQt event)
150
151        Returns:
152            The base eventFilter using source and event (passes it along to PyQt).
153        """
154        if event.type() == QtCore.QEvent.MouseMove:
155            self.mouse_position_changed.emit() # Emits position when mouse moves
156        if event.type() == QtCore.QEvent.MouseButtonPress:
157            self.propagate_mouse_press_event.emit(event)
158        return super().eventFilter(source, event)
class EventTracker(PyQt5.QtCore.QObject):
18class EventTracker(QtCore.QObject):
19    """Track and signal mouse movement and clicks.
20    
21    Args:
22        widget (QWidget or child class thereof): Widget of which to track and signal mouse events.
23    """
24
25    mouse_position_changed = QtCore.pyqtSignal(QtCore.QPoint)
26    mouse_leaved = QtCore.pyqtSignal()
27    mouse_entered = QtCore.pyqtSignal()
28
29    def __init__(self, widget):
30        super().__init__(widget)
31        self._widget = widget
32        self.widget.setMouseTracking(True)
33        self.widget.installEventFilter(self)
34
35    @property
36    def widget(self):
37        return self._widget
38
39    def eventFilter(self, source, event):
40        if source is self.widget and event.type() == QtCore.QEvent.MouseMove:
41            self.mouse_position_changed.emit(event.pos())
42        if source is self.widget and event.type() == QtCore.QEvent.Leave:
43            self.mouse_leaved.emit()
44        if source is self.widget and event.type() == QtCore.QEvent.Enter:
45            self.mouse_entered.emit()
46        return super().eventFilter(source, event)

Track and signal mouse movement and clicks.

Arguments:
  • widget (QWidget or child class thereof): Widget of which to track and signal mouse events.
def eventFilter(self, source, event):
39    def eventFilter(self, source, event):
40        if source is self.widget and event.type() == QtCore.QEvent.MouseMove:
41            self.mouse_position_changed.emit(event.pos())
42        if source is self.widget and event.type() == QtCore.QEvent.Leave:
43            self.mouse_leaved.emit()
44        if source is self.widget and event.type() == QtCore.QEvent.Enter:
45            self.mouse_entered.emit()
46        return super().eventFilter(source, event)

eventFilter(self, QObject, QEvent) -> bool

class EventTrackerSplitBypassDeadzone(PyQt5.QtCore.QObject):
 50class EventTrackerSplitBypassDeadzone(QtCore.QObject):
 51    """Limit the reported global position of the mouse to within a widget's bounds.
 52    
 53    This class is intended to help track mouse movement in the deadzones along the SplitView's borders.
 54    These deadzones fix the issue of resize handles appearing in QMdiArea; however, 
 55    these deadzones hide the mouse from the view, so the mouse must be separately tracked 
 56    to ensure the split is updated.
 57    The mouse position is bounded to include only positions within the widget to fix issues with positions 
 58    reported as outside the bounds.
 59
 60    Args:
 61        widget (QWidget or child class thereof): Widget of which to track and signal mouse events (intended to be the resize_deadzone in SplitView).
 62    """
 63
 64    def __init__(self, widget):
 65        super().__init__(widget)
 66        self._widget = widget
 67        self.widget.setMouseTracking(True)
 68        self.widget.installEventFilter(self)
 69
 70    mouse_position_changed_global = QtCore.pyqtSignal(QtCore.QPoint)
 71
 72    @property
 73    def widget(self):
 74        return self._widget
 75
 76    def width(self):
 77        return self.widget.width()
 78
 79    def height(self):
 80        return self.widget.height()
 81
 82    def eventFilter(self, source, event):
 83        """Override eventFilter to limit the reported position of mouse movement.
 84        
 85        Args:
 86            source (PyQt source)
 87            event (PyQt event)
 88
 89        Returns:
 90            The base eventFilter using source and event (passes it along to PyQt).
 91        """
 92        pos = QtGui.QCursor.pos()
 93        if event.type() == QtCore.QEvent.MouseMove:
 94            pos = self.limit_mouse_position_to_within_widget_bounds(pos) # Prevent erroneous mouse tracking outside the widget
 95            self.mouse_position_changed_global.emit(pos)
 96
 97        return super().eventFilter(source, event)
 98
 99
100    def limit_mouse_position_to_within_widget_bounds(self, pos):
101        """Return a given global mouse position which is limited to within the widget's borders.
102        
103        Args:
104            pos (QPoint): The position of the mouse in global coordinates.
105
106        Returns:
107            pos_global_bounded (QPoint): The position of the mouse in global coordinates limited ("floored") to within the widget borders.
108        """
109        pos_global = pos
110        pos_widget = self.widget.mapFromGlobal(pos_global)
111        x_bounded = max(min(pos_widget.x(), self.width()), 0)
112        y_bounded = max(min(pos_widget.y(), self.height()), 0)
113        pos_widget.setX(x_bounded)
114        pos_widget.setY(y_bounded)
115        pos_global_bounded = self.widget.mapToGlobal(pos_widget)
116        return pos_global_bounded

Limit the reported global position of the mouse to within a widget's bounds.

This class is intended to help track mouse movement in the deadzones along the SplitView's borders. These deadzones fix the issue of resize handles appearing in QMdiArea; however, these deadzones hide the mouse from the view, so the mouse must be separately tracked to ensure the split is updated. The mouse position is bounded to include only positions within the widget to fix issues with positions reported as outside the bounds.

Arguments:
  • widget (QWidget or child class thereof): Widget of which to track and signal mouse events (intended to be the resize_deadzone in SplitView).
def eventFilter(self, source, event):
82    def eventFilter(self, source, event):
83        """Override eventFilter to limit the reported position of mouse movement.
84        
85        Args:
86            source (PyQt source)
87            event (PyQt event)
88
89        Returns:
90            The base eventFilter using source and event (passes it along to PyQt).
91        """
92        pos = QtGui.QCursor.pos()
93        if event.type() == QtCore.QEvent.MouseMove:
94            pos = self.limit_mouse_position_to_within_widget_bounds(pos) # Prevent erroneous mouse tracking outside the widget
95            self.mouse_position_changed_global.emit(pos)
96
97        return super().eventFilter(source, event)

Override eventFilter to limit the reported position of mouse movement.

Arguments:
  • source (PyQt source)
  • event (PyQt event)
Returns:
  • The base eventFilter using source and event (passes it along to PyQt).
def limit_mouse_position_to_within_widget_bounds(self, pos):
100    def limit_mouse_position_to_within_widget_bounds(self, pos):
101        """Return a given global mouse position which is limited to within the widget's borders.
102        
103        Args:
104            pos (QPoint): The position of the mouse in global coordinates.
105
106        Returns:
107            pos_global_bounded (QPoint): The position of the mouse in global coordinates limited ("floored") to within the widget borders.
108        """
109        pos_global = pos
110        pos_widget = self.widget.mapFromGlobal(pos_global)
111        x_bounded = max(min(pos_widget.x(), self.width()), 0)
112        y_bounded = max(min(pos_widget.y(), self.height()), 0)
113        pos_widget.setX(x_bounded)
114        pos_widget.setY(y_bounded)
115        pos_global_bounded = self.widget.mapToGlobal(pos_widget)
116        return pos_global_bounded

Return a given global mouse position which is limited to within the widget's borders.

Arguments:
  • pos (QPoint): The position of the mouse in global coordinates.
Returns:
  • pos_global_bounded (QPoint): The position of the mouse in global coordinates limited ("floored") to within the widget borders.
class EventTrackerSplitBypassInterface(PyQt5.QtCore.QObject):
120class EventTrackerSplitBypassInterface(QtCore.QObject):
121    """Track mouse events while over widgets (for example, interface elements).
122
123    Needed to track the split of the sliding overlay while mouse is hovering over interface widgets.
124    Prevents the split from skipping and stopping when entering and exiting interface elements.
125    
126    Args:
127        widget (QWidget or child class thereof): The widget over which to track mouse movement.
128    """
129
130    mouse_position_changed = QtCore.pyqtSignal()
131    propagate_mouse_press_event = QtCore.pyqtSignal(QtCore.QEvent)
132
133    def __init__(self, widget):
134        super().__init__(widget)
135        self._widget = widget
136        self.widget.setMouseTracking(True)
137        self.widget.installEventFilter(self)
138
139    @property
140    def widget(self):
141        return self._widget
142
143    def eventFilter(self, source, event):
144        """"Override event filter to emit mouse movement and button press events.
145                
146        See parent method for full documentation.
147        
148        Args:
149            source (PyQt source)
150            event (PyQt event)
151
152        Returns:
153            The base eventFilter using source and event (passes it along to PyQt).
154        """
155        if event.type() == QtCore.QEvent.MouseMove:
156            self.mouse_position_changed.emit() # Emits position when mouse moves
157        if event.type() == QtCore.QEvent.MouseButtonPress:
158            self.propagate_mouse_press_event.emit(event)
159        return super().eventFilter(source, event)

Track mouse events while over widgets (for example, interface elements).

Needed to track the split of the sliding overlay while mouse is hovering over interface widgets. Prevents the split from skipping and stopping when entering and exiting interface elements.

Arguments:
  • widget (QWidget or child class thereof): The widget over which to track mouse movement.
def eventFilter(self, source, event):
143    def eventFilter(self, source, event):
144        """"Override event filter to emit mouse movement and button press events.
145                
146        See parent method for full documentation.
147        
148        Args:
149            source (PyQt source)
150            event (PyQt event)
151
152        Returns:
153            The base eventFilter using source and event (passes it along to PyQt).
154        """
155        if event.type() == QtCore.QEvent.MouseMove:
156            self.mouse_position_changed.emit() # Emits position when mouse moves
157        if event.type() == QtCore.QEvent.MouseButtonPress:
158            self.propagate_mouse_press_event.emit(event)
159        return super().eventFilter(source, event)

"Override event filter to emit mouse movement and button press events.

See parent method for full documentation.

Arguments:
  • source (PyQt source)
  • event (PyQt event)
Returns:
  • The base eventFilter using source and event (passes it along to PyQt).