butterfly_registrator.aux_viewing
QGraphicsView with synchronized pan and zoom functionality.
Not intended as a script.
Creates the base (main) view of the SplitView for the Butterfly Viewer and Registrator.
Credits:
PyQt MDI Image Viewer by tpgit (http://tpgit.github.io/MDIImageViewer/) for sync pan and zoom.
1#!/usr/bin/env python3 2 3"""QGraphicsView with synchronized pan and zoom functionality. 4 5Not intended as a script. 6 7Creates the base (main) view of the SplitView for the Butterfly Viewer and Registrator. 8 9Credits: 10 PyQt MDI Image Viewer by tpgit (http://tpgit.github.io/MDIImageViewer/) for sync pan and zoom. 11""" 12# SPDX-License-Identifier: GPL-3.0-or-later 13 14 15 16from PyQt5 import QtCore, QtGui, QtWidgets 17 18 19 20class SynchableGraphicsView(QtWidgets.QGraphicsView): 21 """Extend QGraphicsView for synchronous panning and zooming between multiple instances. 22 23 Extends QGraphicsView: 24 Pan and zoom signals. 25 Scrolling operations and mouse wheel zooming. 26 Mouse tracking. 27 View shortcuts (for example, centering the view). 28 29 Args: 30 scene (QGraphicsScene) 31 parent (QWidget or child class thereof) 32 """ 33 34 35 def __init__(self, scene=None, parent=None): 36 if scene: 37 super(SynchableGraphicsView, self).__init__(scene, parent) 38 else: 39 super(SynchableGraphicsView, self).__init__(parent) 40 41 self.scroll_to_zoom_always_on = True 42 43 self._handDrag = False 44 45 self.clearTransformChanges() 46 self.connectSbarSignals(self.scrollChanged) 47 48 self.setMouseTracking(True) # Allows split to follow the mouse cursor without needing to click the mouse (expected split behavior) 49 self.right_clicked_is_pressed = False 50 51 self.installEventFilter(self) 52 53 54 # Signals 55 56 transformChanged = QtCore.pyqtSignal() 57 """Transformed Changed **Signal**. 58 59 Emitted whenever the |QGraphicsView| Transform matrix has been 60 changed.""" 61 62 scrollChanged = QtCore.pyqtSignal() 63 """Scroll Changed **Signal**. 64 65 Emitted whenever the scrollbar position or range has changed.""" 66 67 wheelNotches = QtCore.pyqtSignal(float) 68 """Wheel Notches **Signal** (*float*). 69 70 Emitted whenever the mouse wheel has been rolled. A wheelnotch is 71 equal to wheel delta / 240""" 72 73 def connectSbarSignals(self, slot): 74 """Connect to scrollbar changed signals to synchronize panning. 75 76 :param slot: slot to connect scrollbar signals to.""" 77 sbar = self.horizontalScrollBar() 78 sbar.valueChanged.connect(slot, type=QtCore.Qt.UniqueConnection) 79 #sbar.sliderMoved.connect(slot, type=QtCore.Qt.UniqueConnection) 80 sbar.rangeChanged.connect(slot, type=QtCore.Qt.UniqueConnection) 81 82 sbar = self.verticalScrollBar() 83 sbar.valueChanged.connect(slot, type=QtCore.Qt.UniqueConnection) 84 #sbar.sliderMoved.connect(slot, type=QtCore.Qt.UniqueConnection) 85 sbar.rangeChanged.connect(slot, type=QtCore.Qt.UniqueConnection) 86 87 #self.scrollChanged.connect(slot, type=QtCore.Qt.UniqueConnection) 88 89 def disconnectSbarSignals(self): 90 """Disconnect from scrollbar changed signals.""" 91 sbar = self.horizontalScrollBar() 92 sbar.valueChanged.disconnect() 93 #sbar.sliderMoved.disconnect() 94 sbar.rangeChanged.disconnect() 95 96 sbar = self.verticalScrollBar() 97 sbar.valueChanged.disconnect() 98 #sbar.sliderMoved.disconnect() 99 sbar.rangeChanged.disconnect() 100 101 right_mouse_button_was_clicked = QtCore.pyqtSignal(QtCore.QPoint) 102 103 # Properties 104 105 @property 106 def handDragging(self): 107 """Hand dragging state (*bool*)""" 108 return self._handDrag 109 110 @property 111 def scrollState(self): 112 """Tuple of percentage of scene extents 113 *(sceneWidthPercent, sceneHeightPercent)*""" 114 centerPoint = self.mapToScene(self.viewport().width()/2, 115 self.viewport().height()/2) 116 sceneRect = self.sceneRect() 117 centerWidth = centerPoint.x() - sceneRect.left() 118 centerHeight = centerPoint.y() - sceneRect.top() 119 sceneWidth = sceneRect.width() 120 sceneHeight = sceneRect.height() 121 122 sceneWidthPercent = centerWidth / sceneWidth if sceneWidth != 0 else 0 123 sceneHeightPercent = centerHeight / sceneHeight if sceneHeight != 0 else 0 124 return (sceneWidthPercent, sceneHeightPercent) 125 126 @scrollState.setter 127 def scrollState(self, state): 128 sceneWidthPercent, sceneHeightPercent = state 129 x = (sceneWidthPercent * self.sceneRect().width() + 130 self.sceneRect().left()) 131 y = (sceneHeightPercent * self.sceneRect().height() + 132 self.sceneRect().top()) 133 self.centerOn(x, y) 134 135 @property 136 def zoomFactor(self): 137 """Zoom scale factor (*float*).""" 138 return self.transform().m11() 139 140 @zoomFactor.setter 141 def zoomFactor(self, newZoomFactor): 142 newZoomFactor = newZoomFactor / self.zoomFactor 143 self.scale(newZoomFactor, newZoomFactor) 144 145 146 # Events 147 148 def mouseMoveEvent(self, event): 149 event.ignore() 150 return super().mouseMoveEvent(event) 151 152 def wheelEvent(self, wheelEvent): 153 """Overrides the wheelEvent to handle zooming. 154 155 :param QWheelEvent wheelEvent: instance of |QWheelEvent|""" 156 assert isinstance(wheelEvent, QtGui.QWheelEvent) 157 if (wheelEvent.modifiers() & QtCore.Qt.ControlModifier) or self.scroll_to_zoom_always_on: 158 self.wheelNotches.emit(wheelEvent.angleDelta().y() / 240.0) 159 wheelEvent.accept() 160 else: 161 super(SynchableGraphicsView, self).wheelEvent(wheelEvent) 162 163 def keyReleaseEvent(self, keyEvent): 164 """Overrides to make sure key release passed on to other classes. 165 166 :param QKeyEvent keyEvent: instance of |QKeyEvent|""" 167 assert isinstance(keyEvent, QtGui.QKeyEvent) 168 #print("graphicsView keyRelease count=%d, autoRepeat=%s" % 169 #(keyEvent.count(), keyEvent.isAutoRepeat())) 170 keyEvent.ignore() 171 #super(SynchableGraphicsView, self).keyReleaseEvent(keyEvent) 172 173 def mousePressEvent(self, event): 174 """Overrides to allow left-click for panning and limit repeat right-clicks. 175 176 Enables hand dragging (panning) of the view by clicking left mouse button. 177 178 Parameters: 179 180 - event: [PtQt event].""" 181 assert isinstance(event, QtGui.QMouseEvent) 182 if event.button() == QtCore.Qt.LeftButton: # Pan the image by clicking and dragging left mouse button 183 self.enableHandDrag(True) 184 event.accept() 185 if (not self.right_clicked_is_pressed) and (event.button() == QtCore.Qt.RightButton): # Indicate right button is pressed to prevent rapid repeating release signals 186 self.right_clicked_is_pressed = True 187 super().mousePressEvent(event) 188 189 def mouseReleaseEvent(self, event): 190 """Overrides to disable panning left-click for panning and limit repeat right-clicks. 191 192 Undoes the actions from mousePressEvent (release of mouse buttons). 193 194 Parameters: 195 196 - event: [PtQt event].""" 197 assert isinstance(event, QtGui.QMouseEvent) 198 if event.button() == QtCore.Qt.LeftButton: 199 self.enableHandDrag(False) 200 event.accept() 201 if (self.right_clicked_is_pressed) and (event.button() == QtCore.Qt.RightButton): 202 self.right_clicked_is_pressed = False 203 self.right_mouse_button_was_clicked.emit(event.pos()) 204 super().mouseReleaseEvent(event) 205 206 def dragEnterEvent(self, event): 207 """Ignore drag event, thus passing it along to the parent widget. 208 209 Allows for the drag-and-drop function in digitaltwin_imageviewer.py by 210 passing drag events to the underlying MDI area. 211 212 Parameters: 213 214 - event : [PyQt event]""" 215 event.ignore() # Give the drag event to the parent widget (e.g., to allow drag-and-drop in multi-instance viewers) 216 217 218 # Zoom and pan (scrolling) methods 219 220 def checkTransformChanged(self): 221 """Return True if view transform has changed. 222 223 :rtype: bool""" 224 delta = 0.001 225 result = False 226 227 def different(t, u): 228 if u == 0.0: 229 d = abs(t - u) 230 else: 231 d = abs((t - u) / u) 232 return d > delta 233 234 t = self.transform() 235 u = self._transform 236 237 if False: 238 print("t = ") 239 self.dumpTransform(t, " ") 240 print("u = ") 241 self.dumpTransform(u, " ") 242 print("") 243 244 if (different(t.m11(), u.m11()) or 245 different(t.m22(), u.m22()) or 246 different(t.m12(), u.m12()) or 247 different(t.m21(), u.m21()) or 248 different(t.m31(), u.m31()) or 249 different(t.m32(), u.m32())): 250 self._transform = t 251 self.transformChanged.emit() 252 result = True 253 return result 254 255 def clearTransformChanges(self): 256 """Reset view transform changed info.""" 257 self._transform = self.transform() 258 259 def scrollToTop(self): 260 """Scroll view to top.""" 261 sbar = self.verticalScrollBar() 262 sbar.setValue(sbar.minimum()) 263 264 def scrollToBottom(self): 265 """Scroll view to bottom.""" 266 sbar = self.verticalScrollBar() 267 sbar.setValue(sbar.maximum()) 268 269 def scrollToBegin(self): 270 """Scroll view to left edge.""" 271 sbar = self.horizontalScrollBar() 272 sbar.setValue(sbar.minimum()) 273 274 def scrollToEnd(self): 275 """Scroll view to right edge.""" 276 sbar = self.horizontalScrollBar() 277 sbar.setValue(sbar.maximum()) 278 279 def centerView(self): 280 """Center view.""" 281 sbar = self.verticalScrollBar() 282 sbar.setValue((sbar.maximum() + sbar.minimum())/2) 283 sbar = self.horizontalScrollBar() 284 sbar.setValue((sbar.maximum() + sbar.minimum())/2) 285 286 def enableScrollBars(self, enable): 287 """Set visibility of the view's scrollbars. 288 289 :param bool enable: True to enable the scrollbars """ 290 if enable: 291 self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) 292 self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) 293 else: 294 self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) 295 self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) 296 297 def enableHandDrag(self, enable): 298 """Set whether dragging the view with the hand cursor is allowed. 299 300 :param bool enable: True to enable hand dragging """ 301 if enable: 302 if not self._handDrag: 303 self._handDrag = True 304 self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag) 305 else: 306 if self._handDrag: 307 self._handDrag = False 308 self.setDragMode(QtWidgets.QGraphicsView.NoDrag) 309 310 311 # Printing methods 312 313 def dumpTransform(self, t, padding=""): 314 """Dump the transform t to stdout. 315 316 :param t: the transform to dump 317 :param str padding: each line is preceded by padding""" 318 print("%s%5.3f %5.3f %5.3f" % (padding, t.m11(), t.m12(), t.m13())) 319 print("%s%5.3f %5.3f %5.3f" % (padding, t.m21(), t.m22(), t.m23())) 320 print("%s%5.3f %5.3f %5.3f" % (padding, t.m31(), t.m32(), t.m33()))
21class SynchableGraphicsView(QtWidgets.QGraphicsView): 22 """Extend QGraphicsView for synchronous panning and zooming between multiple instances. 23 24 Extends QGraphicsView: 25 Pan and zoom signals. 26 Scrolling operations and mouse wheel zooming. 27 Mouse tracking. 28 View shortcuts (for example, centering the view). 29 30 Args: 31 scene (QGraphicsScene) 32 parent (QWidget or child class thereof) 33 """ 34 35 36 def __init__(self, scene=None, parent=None): 37 if scene: 38 super(SynchableGraphicsView, self).__init__(scene, parent) 39 else: 40 super(SynchableGraphicsView, self).__init__(parent) 41 42 self.scroll_to_zoom_always_on = True 43 44 self._handDrag = False 45 46 self.clearTransformChanges() 47 self.connectSbarSignals(self.scrollChanged) 48 49 self.setMouseTracking(True) # Allows split to follow the mouse cursor without needing to click the mouse (expected split behavior) 50 self.right_clicked_is_pressed = False 51 52 self.installEventFilter(self) 53 54 55 # Signals 56 57 transformChanged = QtCore.pyqtSignal() 58 """Transformed Changed **Signal**. 59 60 Emitted whenever the |QGraphicsView| Transform matrix has been 61 changed.""" 62 63 scrollChanged = QtCore.pyqtSignal() 64 """Scroll Changed **Signal**. 65 66 Emitted whenever the scrollbar position or range has changed.""" 67 68 wheelNotches = QtCore.pyqtSignal(float) 69 """Wheel Notches **Signal** (*float*). 70 71 Emitted whenever the mouse wheel has been rolled. A wheelnotch is 72 equal to wheel delta / 240""" 73 74 def connectSbarSignals(self, slot): 75 """Connect to scrollbar changed signals to synchronize panning. 76 77 :param slot: slot to connect scrollbar signals to.""" 78 sbar = self.horizontalScrollBar() 79 sbar.valueChanged.connect(slot, type=QtCore.Qt.UniqueConnection) 80 #sbar.sliderMoved.connect(slot, type=QtCore.Qt.UniqueConnection) 81 sbar.rangeChanged.connect(slot, type=QtCore.Qt.UniqueConnection) 82 83 sbar = self.verticalScrollBar() 84 sbar.valueChanged.connect(slot, type=QtCore.Qt.UniqueConnection) 85 #sbar.sliderMoved.connect(slot, type=QtCore.Qt.UniqueConnection) 86 sbar.rangeChanged.connect(slot, type=QtCore.Qt.UniqueConnection) 87 88 #self.scrollChanged.connect(slot, type=QtCore.Qt.UniqueConnection) 89 90 def disconnectSbarSignals(self): 91 """Disconnect from scrollbar changed signals.""" 92 sbar = self.horizontalScrollBar() 93 sbar.valueChanged.disconnect() 94 #sbar.sliderMoved.disconnect() 95 sbar.rangeChanged.disconnect() 96 97 sbar = self.verticalScrollBar() 98 sbar.valueChanged.disconnect() 99 #sbar.sliderMoved.disconnect() 100 sbar.rangeChanged.disconnect() 101 102 right_mouse_button_was_clicked = QtCore.pyqtSignal(QtCore.QPoint) 103 104 # Properties 105 106 @property 107 def handDragging(self): 108 """Hand dragging state (*bool*)""" 109 return self._handDrag 110 111 @property 112 def scrollState(self): 113 """Tuple of percentage of scene extents 114 *(sceneWidthPercent, sceneHeightPercent)*""" 115 centerPoint = self.mapToScene(self.viewport().width()/2, 116 self.viewport().height()/2) 117 sceneRect = self.sceneRect() 118 centerWidth = centerPoint.x() - sceneRect.left() 119 centerHeight = centerPoint.y() - sceneRect.top() 120 sceneWidth = sceneRect.width() 121 sceneHeight = sceneRect.height() 122 123 sceneWidthPercent = centerWidth / sceneWidth if sceneWidth != 0 else 0 124 sceneHeightPercent = centerHeight / sceneHeight if sceneHeight != 0 else 0 125 return (sceneWidthPercent, sceneHeightPercent) 126 127 @scrollState.setter 128 def scrollState(self, state): 129 sceneWidthPercent, sceneHeightPercent = state 130 x = (sceneWidthPercent * self.sceneRect().width() + 131 self.sceneRect().left()) 132 y = (sceneHeightPercent * self.sceneRect().height() + 133 self.sceneRect().top()) 134 self.centerOn(x, y) 135 136 @property 137 def zoomFactor(self): 138 """Zoom scale factor (*float*).""" 139 return self.transform().m11() 140 141 @zoomFactor.setter 142 def zoomFactor(self, newZoomFactor): 143 newZoomFactor = newZoomFactor / self.zoomFactor 144 self.scale(newZoomFactor, newZoomFactor) 145 146 147 # Events 148 149 def mouseMoveEvent(self, event): 150 event.ignore() 151 return super().mouseMoveEvent(event) 152 153 def wheelEvent(self, wheelEvent): 154 """Overrides the wheelEvent to handle zooming. 155 156 :param QWheelEvent wheelEvent: instance of |QWheelEvent|""" 157 assert isinstance(wheelEvent, QtGui.QWheelEvent) 158 if (wheelEvent.modifiers() & QtCore.Qt.ControlModifier) or self.scroll_to_zoom_always_on: 159 self.wheelNotches.emit(wheelEvent.angleDelta().y() / 240.0) 160 wheelEvent.accept() 161 else: 162 super(SynchableGraphicsView, self).wheelEvent(wheelEvent) 163 164 def keyReleaseEvent(self, keyEvent): 165 """Overrides to make sure key release passed on to other classes. 166 167 :param QKeyEvent keyEvent: instance of |QKeyEvent|""" 168 assert isinstance(keyEvent, QtGui.QKeyEvent) 169 #print("graphicsView keyRelease count=%d, autoRepeat=%s" % 170 #(keyEvent.count(), keyEvent.isAutoRepeat())) 171 keyEvent.ignore() 172 #super(SynchableGraphicsView, self).keyReleaseEvent(keyEvent) 173 174 def mousePressEvent(self, event): 175 """Overrides to allow left-click for panning and limit repeat right-clicks. 176 177 Enables hand dragging (panning) of the view by clicking left mouse button. 178 179 Parameters: 180 181 - event: [PtQt event].""" 182 assert isinstance(event, QtGui.QMouseEvent) 183 if event.button() == QtCore.Qt.LeftButton: # Pan the image by clicking and dragging left mouse button 184 self.enableHandDrag(True) 185 event.accept() 186 if (not self.right_clicked_is_pressed) and (event.button() == QtCore.Qt.RightButton): # Indicate right button is pressed to prevent rapid repeating release signals 187 self.right_clicked_is_pressed = True 188 super().mousePressEvent(event) 189 190 def mouseReleaseEvent(self, event): 191 """Overrides to disable panning left-click for panning and limit repeat right-clicks. 192 193 Undoes the actions from mousePressEvent (release of mouse buttons). 194 195 Parameters: 196 197 - event: [PtQt event].""" 198 assert isinstance(event, QtGui.QMouseEvent) 199 if event.button() == QtCore.Qt.LeftButton: 200 self.enableHandDrag(False) 201 event.accept() 202 if (self.right_clicked_is_pressed) and (event.button() == QtCore.Qt.RightButton): 203 self.right_clicked_is_pressed = False 204 self.right_mouse_button_was_clicked.emit(event.pos()) 205 super().mouseReleaseEvent(event) 206 207 def dragEnterEvent(self, event): 208 """Ignore drag event, thus passing it along to the parent widget. 209 210 Allows for the drag-and-drop function in digitaltwin_imageviewer.py by 211 passing drag events to the underlying MDI area. 212 213 Parameters: 214 215 - event : [PyQt event]""" 216 event.ignore() # Give the drag event to the parent widget (e.g., to allow drag-and-drop in multi-instance viewers) 217 218 219 # Zoom and pan (scrolling) methods 220 221 def checkTransformChanged(self): 222 """Return True if view transform has changed. 223 224 :rtype: bool""" 225 delta = 0.001 226 result = False 227 228 def different(t, u): 229 if u == 0.0: 230 d = abs(t - u) 231 else: 232 d = abs((t - u) / u) 233 return d > delta 234 235 t = self.transform() 236 u = self._transform 237 238 if False: 239 print("t = ") 240 self.dumpTransform(t, " ") 241 print("u = ") 242 self.dumpTransform(u, " ") 243 print("") 244 245 if (different(t.m11(), u.m11()) or 246 different(t.m22(), u.m22()) or 247 different(t.m12(), u.m12()) or 248 different(t.m21(), u.m21()) or 249 different(t.m31(), u.m31()) or 250 different(t.m32(), u.m32())): 251 self._transform = t 252 self.transformChanged.emit() 253 result = True 254 return result 255 256 def clearTransformChanges(self): 257 """Reset view transform changed info.""" 258 self._transform = self.transform() 259 260 def scrollToTop(self): 261 """Scroll view to top.""" 262 sbar = self.verticalScrollBar() 263 sbar.setValue(sbar.minimum()) 264 265 def scrollToBottom(self): 266 """Scroll view to bottom.""" 267 sbar = self.verticalScrollBar() 268 sbar.setValue(sbar.maximum()) 269 270 def scrollToBegin(self): 271 """Scroll view to left edge.""" 272 sbar = self.horizontalScrollBar() 273 sbar.setValue(sbar.minimum()) 274 275 def scrollToEnd(self): 276 """Scroll view to right edge.""" 277 sbar = self.horizontalScrollBar() 278 sbar.setValue(sbar.maximum()) 279 280 def centerView(self): 281 """Center view.""" 282 sbar = self.verticalScrollBar() 283 sbar.setValue((sbar.maximum() + sbar.minimum())/2) 284 sbar = self.horizontalScrollBar() 285 sbar.setValue((sbar.maximum() + sbar.minimum())/2) 286 287 def enableScrollBars(self, enable): 288 """Set visibility of the view's scrollbars. 289 290 :param bool enable: True to enable the scrollbars """ 291 if enable: 292 self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) 293 self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) 294 else: 295 self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) 296 self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) 297 298 def enableHandDrag(self, enable): 299 """Set whether dragging the view with the hand cursor is allowed. 300 301 :param bool enable: True to enable hand dragging """ 302 if enable: 303 if not self._handDrag: 304 self._handDrag = True 305 self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag) 306 else: 307 if self._handDrag: 308 self._handDrag = False 309 self.setDragMode(QtWidgets.QGraphicsView.NoDrag) 310 311 312 # Printing methods 313 314 def dumpTransform(self, t, padding=""): 315 """Dump the transform t to stdout. 316 317 :param t: the transform to dump 318 :param str padding: each line is preceded by padding""" 319 print("%s%5.3f %5.3f %5.3f" % (padding, t.m11(), t.m12(), t.m13())) 320 print("%s%5.3f %5.3f %5.3f" % (padding, t.m21(), t.m22(), t.m23())) 321 print("%s%5.3f %5.3f %5.3f" % (padding, t.m31(), t.m32(), t.m33()))
Extend QGraphicsView for synchronous panning and zooming between multiple instances.
Extends QGraphicsView:
Pan and zoom signals. Scrolling operations and mouse wheel zooming. Mouse tracking. View shortcuts (for example, centering the view).
Arguments:
- scene (QGraphicsScene)
- parent (QWidget or child class thereof)
Transformed Changed Signal.
Emitted whenever the |QGraphicsView| Transform matrix has been changed.
Scroll Changed Signal.
Emitted whenever the scrollbar position or range has changed.
Wheel Notches Signal (float).
Emitted whenever the mouse wheel has been rolled. A wheelnotch is equal to wheel delta / 240
74 def connectSbarSignals(self, slot): 75 """Connect to scrollbar changed signals to synchronize panning. 76 77 :param slot: slot to connect scrollbar signals to.""" 78 sbar = self.horizontalScrollBar() 79 sbar.valueChanged.connect(slot, type=QtCore.Qt.UniqueConnection) 80 #sbar.sliderMoved.connect(slot, type=QtCore.Qt.UniqueConnection) 81 sbar.rangeChanged.connect(slot, type=QtCore.Qt.UniqueConnection) 82 83 sbar = self.verticalScrollBar() 84 sbar.valueChanged.connect(slot, type=QtCore.Qt.UniqueConnection) 85 #sbar.sliderMoved.connect(slot, type=QtCore.Qt.UniqueConnection) 86 sbar.rangeChanged.connect(slot, type=QtCore.Qt.UniqueConnection)
Connect to scrollbar changed signals to synchronize panning.
Parameters
- slot: slot to connect scrollbar signals to.
90 def disconnectSbarSignals(self): 91 """Disconnect from scrollbar changed signals.""" 92 sbar = self.horizontalScrollBar() 93 sbar.valueChanged.disconnect() 94 #sbar.sliderMoved.disconnect() 95 sbar.rangeChanged.disconnect() 96 97 sbar = self.verticalScrollBar() 98 sbar.valueChanged.disconnect() 99 #sbar.sliderMoved.disconnect() 100 sbar.rangeChanged.disconnect()
Disconnect from scrollbar changed signals.
153 def wheelEvent(self, wheelEvent): 154 """Overrides the wheelEvent to handle zooming. 155 156 :param QWheelEvent wheelEvent: instance of |QWheelEvent|""" 157 assert isinstance(wheelEvent, QtGui.QWheelEvent) 158 if (wheelEvent.modifiers() & QtCore.Qt.ControlModifier) or self.scroll_to_zoom_always_on: 159 self.wheelNotches.emit(wheelEvent.angleDelta().y() / 240.0) 160 wheelEvent.accept() 161 else: 162 super(SynchableGraphicsView, self).wheelEvent(wheelEvent)
Overrides the wheelEvent to handle zooming.
Parameters
- QWheelEvent wheelEvent: instance of |QWheelEvent|
164 def keyReleaseEvent(self, keyEvent): 165 """Overrides to make sure key release passed on to other classes. 166 167 :param QKeyEvent keyEvent: instance of |QKeyEvent|""" 168 assert isinstance(keyEvent, QtGui.QKeyEvent) 169 #print("graphicsView keyRelease count=%d, autoRepeat=%s" % 170 #(keyEvent.count(), keyEvent.isAutoRepeat())) 171 keyEvent.ignore()
Overrides to make sure key release passed on to other classes.
Parameters
- QKeyEvent keyEvent: instance of |QKeyEvent|
174 def mousePressEvent(self, event): 175 """Overrides to allow left-click for panning and limit repeat right-clicks. 176 177 Enables hand dragging (panning) of the view by clicking left mouse button. 178 179 Parameters: 180 181 - event: [PtQt event].""" 182 assert isinstance(event, QtGui.QMouseEvent) 183 if event.button() == QtCore.Qt.LeftButton: # Pan the image by clicking and dragging left mouse button 184 self.enableHandDrag(True) 185 event.accept() 186 if (not self.right_clicked_is_pressed) and (event.button() == QtCore.Qt.RightButton): # Indicate right button is pressed to prevent rapid repeating release signals 187 self.right_clicked_is_pressed = True 188 super().mousePressEvent(event)
Overrides to allow left-click for panning and limit repeat right-clicks.
Enables hand dragging (panning) of the view by clicking left mouse button.
Parameters:
- event: [PtQt event].
190 def mouseReleaseEvent(self, event): 191 """Overrides to disable panning left-click for panning and limit repeat right-clicks. 192 193 Undoes the actions from mousePressEvent (release of mouse buttons). 194 195 Parameters: 196 197 - event: [PtQt event].""" 198 assert isinstance(event, QtGui.QMouseEvent) 199 if event.button() == QtCore.Qt.LeftButton: 200 self.enableHandDrag(False) 201 event.accept() 202 if (self.right_clicked_is_pressed) and (event.button() == QtCore.Qt.RightButton): 203 self.right_clicked_is_pressed = False 204 self.right_mouse_button_was_clicked.emit(event.pos()) 205 super().mouseReleaseEvent(event)
Overrides to disable panning left-click for panning and limit repeat right-clicks.
Undoes the actions from mousePressEvent (release of mouse buttons).
Parameters:
- event: [PtQt event].
207 def dragEnterEvent(self, event): 208 """Ignore drag event, thus passing it along to the parent widget. 209 210 Allows for the drag-and-drop function in digitaltwin_imageviewer.py by 211 passing drag events to the underlying MDI area. 212 213 Parameters: 214 215 - event : [PyQt event]""" 216 event.ignore() # Give the drag event to the parent widget (e.g., to allow drag-and-drop in multi-instance viewers)
Ignore drag event, thus passing it along to the parent widget.
Allows for the drag-and-drop function in digitaltwin_imageviewer.py by passing drag events to the underlying MDI area.
Parameters:
- event : [PyQt event]
221 def checkTransformChanged(self): 222 """Return True if view transform has changed. 223 224 :rtype: bool""" 225 delta = 0.001 226 result = False 227 228 def different(t, u): 229 if u == 0.0: 230 d = abs(t - u) 231 else: 232 d = abs((t - u) / u) 233 return d > delta 234 235 t = self.transform() 236 u = self._transform 237 238 if False: 239 print("t = ") 240 self.dumpTransform(t, " ") 241 print("u = ") 242 self.dumpTransform(u, " ") 243 print("") 244 245 if (different(t.m11(), u.m11()) or 246 different(t.m22(), u.m22()) or 247 different(t.m12(), u.m12()) or 248 different(t.m21(), u.m21()) or 249 different(t.m31(), u.m31()) or 250 different(t.m32(), u.m32())): 251 self._transform = t 252 self.transformChanged.emit() 253 result = True 254 return result
Return True if view transform has changed.
256 def clearTransformChanges(self): 257 """Reset view transform changed info.""" 258 self._transform = self.transform()
Reset view transform changed info.
260 def scrollToTop(self): 261 """Scroll view to top.""" 262 sbar = self.verticalScrollBar() 263 sbar.setValue(sbar.minimum())
Scroll view to top.
265 def scrollToBottom(self): 266 """Scroll view to bottom.""" 267 sbar = self.verticalScrollBar() 268 sbar.setValue(sbar.maximum())
Scroll view to bottom.
270 def scrollToBegin(self): 271 """Scroll view to left edge.""" 272 sbar = self.horizontalScrollBar() 273 sbar.setValue(sbar.minimum())
Scroll view to left edge.
275 def scrollToEnd(self): 276 """Scroll view to right edge.""" 277 sbar = self.horizontalScrollBar() 278 sbar.setValue(sbar.maximum())
Scroll view to right edge.
280 def centerView(self): 281 """Center view.""" 282 sbar = self.verticalScrollBar() 283 sbar.setValue((sbar.maximum() + sbar.minimum())/2) 284 sbar = self.horizontalScrollBar() 285 sbar.setValue((sbar.maximum() + sbar.minimum())/2)
Center view.
287 def enableScrollBars(self, enable): 288 """Set visibility of the view's scrollbars. 289 290 :param bool enable: True to enable the scrollbars """ 291 if enable: 292 self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) 293 self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded) 294 else: 295 self.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff) 296 self.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAlwaysOff)
Set visibility of the view's scrollbars.
Parameters
- bool enable: True to enable the scrollbars
298 def enableHandDrag(self, enable): 299 """Set whether dragging the view with the hand cursor is allowed. 300 301 :param bool enable: True to enable hand dragging """ 302 if enable: 303 if not self._handDrag: 304 self._handDrag = True 305 self.setDragMode(QtWidgets.QGraphicsView.ScrollHandDrag) 306 else: 307 if self._handDrag: 308 self._handDrag = False 309 self.setDragMode(QtWidgets.QGraphicsView.NoDrag)
Set whether dragging the view with the hand cursor is allowed.
Parameters
- bool enable: True to enable hand dragging
314 def dumpTransform(self, t, padding=""): 315 """Dump the transform t to stdout. 316 317 :param t: the transform to dump 318 :param str padding: each line is preceded by padding""" 319 print("%s%5.3f %5.3f %5.3f" % (padding, t.m11(), t.m12(), t.m13())) 320 print("%s%5.3f %5.3f %5.3f" % (padding, t.m21(), t.m22(), t.m23())) 321 print("%s%5.3f %5.3f %5.3f" % (padding, t.m31(), t.m32(), t.m33()))
Dump the transform t to stdout.
Parameters
- t: the transform to dump
- str padding: each line is preceded by padding