butterfly_viewer.aux_mdi
QMdiArea with drag-and-drop functions, vertical/horizontal window tiling, and keyboard shortcuts.
Not intended as a script.
Creates the multi document interface (MDI) widget for the Butterfly Viewer.
1#!/usr/bin/env python3 2 3"""QMdiArea with drag-and-drop functions, vertical/horizontal window tiling, and keyboard shortcuts. 4 5Not intended as a script. 6 7Creates the multi document interface (MDI) widget for the Butterfly Viewer. 8""" 9# SPDX-License-Identifier: GPL-3.0-or-later 10 11 12 13from PyQt5 import QtWidgets, QtCore, QtGui 14 15 16 17class QMdiAreaWithCustomSignals(QtWidgets.QMdiArea): 18 """Extend QMdiArea with drag-and-drop functions, vertical/horizontal window tiling, and keyboard shortcuts. 19 20 Instantiate without input. 21 22 Features: 23 Signals for drag-and-drop, subwindow events, shortcut keys. 24 Methods for arranging the subwindows vertically and horizontally, and to track the history of the arrangement. 25 """ 26 27 file_path_dragged_and_dropped = QtCore.pyqtSignal(str) 28 file_path_dragged = QtCore.pyqtSignal(bool) 29 shortcut_escape_was_activated = QtCore.pyqtSignal() 30 shortcut_f_was_activated = QtCore.pyqtSignal() 31 shortcut_h_was_activated = QtCore.pyqtSignal() 32 shortcut_ctrl_c_was_activated = QtCore.pyqtSignal() 33 34 first_subwindow_was_opened = QtCore.pyqtSignal() 35 last_remaining_subwindow_was_closed = QtCore.pyqtSignal() 36 37 def __init__(self): 38 super().__init__() 39 40 self.setAcceptDrops(True) 41 self.subWindowActivated.connect(self.subwindow_was_activated) 42 self.last_tile_method = None 43 self.are_there_any_subwindows_open = False 44 self.most_recently_activated_subwindow = None 45 46 self.escape_shortcut = QtWidgets.QShortcut(QtGui.QKeySequence("Escape"), self) 47 self.escape_shortcut.activated.connect(self.shortcut_escape_was_activated) 48 49 self.f_shortcut = QtWidgets.QShortcut(QtGui.QKeySequence("f"), self) 50 self.f_shortcut.activated.connect(self.shortcut_f_was_activated) 51 52 self.h_shortcut = QtWidgets.QShortcut(QtGui.QKeySequence("h"), self) 53 self.h_shortcut.activated.connect(self.shortcut_h_was_activated) 54 55 self.ctrl_c_shortcut = QtWidgets.QShortcut(QtGui.QKeySequence("Ctrl+c"), self) 56 self.ctrl_c_shortcut.activated.connect(self.shortcut_ctrl_c_was_activated) 57 58 def tile_subwindows_vertically(self, button_input=None): 59 """Arrange subwindows vertically as a single column. 60 61 Arranges subwindows top to bottom in order of when they were added (oldest to newest). 62 63 Args: 64 button_input: Always set as None (kept for legacy purposes). 65 """ 66 windows = self.subWindowList() 67 position = QtCore.QPoint() 68 for window in windows: 69 rect = QtCore.QRect(0, 0, self.width(), self.height()/len(windows)) 70 window.setGeometry(rect) 71 window.move(position) 72 position.setY(position.y() + window.height()) 73 self.last_tile_method = "vertically" 74 75 def tile_subwindows_horizontally(self, button_input=None): 76 """Arrange subwindows horizontally as a single row. 77 78 Arranges subwindows left to right in order of when they were added (oldest to newest). 79 80 Args: 81 button_input: Always set as None (kept for legacy purposes). 82 """ 83 windows = self.subWindowList() 84 position = QtCore.QPoint() 85 for window in windows: 86 rect = QtCore.QRect(0, 0, self.width()/len(windows), self.height()) 87 window.setGeometry(rect) 88 window.move(position) 89 position.setX(position.x() + window.width()) 90 self.last_tile_method = "horizontally" 91 92 def tileSubWindows(self, button_input=None): 93 """Arrange subwindows as tiles (override). 94 95 Args: 96 button_input: Always set as None (kept for legacy purposes). 97 """ 98 super().tileSubWindows() 99 self.last_tile_method = "grid" 100 101 def tile_what_was_done_last_time(self): 102 """Arrange subwindows based on previous arrangement. 103 104 Needed to arrange windows in the last arranged method during events like resizing. 105 """ 106 if self.last_tile_method == "horizontally": 107 self.tile_subwindows_horizontally() 108 elif self.last_tile_method == "vertically": 109 self.tile_subwindows_vertically() 110 else: 111 self.tileSubWindows() 112 113 def dragEnterEvent(self, event): 114 """event: Signal that one or more files have been dragged into the area.""" 115 self.file_path_dragged.emit(True) 116 event.accept() 117 118 def dragMoveEvent(self, event): 119 """event: Signal that one or more files are being dragged in the area.""" 120 event.accept() 121 122 def dragLeaveEvent(self, event): 123 """event: Signal that one or more files have been dragged out of the area.""" 124 self.file_path_dragged.emit(False) 125 event.accept() 126 127 def dropEvent(self, event): 128 """event: Signal that one or more files have been dropped into the area.""" 129 event.setDropAction(QtCore.Qt.CopyAction) 130 131 self.file_path_dragged.emit(False) 132 133 urls = event.mimeData().urls() 134 135 if urls: 136 for url in urls: 137 file_path = url.toLocalFile() 138 self.file_path_dragged_and_dropped.emit(file_path) 139 event.accept() 140 else: 141 event.ignore() 142 143 def subwindow_was_activated(self, window): 144 """Signal if first subwindow has been activated or if last remaining subwindow has been closed. 145 146 Triggered when subwindow activated signal of area is emitted. 147 Fixes issues with improper subwindow activation behavior. 148 149 Args: 150 window (QMdiSubWindow) 151 """ 152 153 if not window: # When the last remaining subwindow is closed, subWindowActivated throws Null window 154 self.are_there_any_subwindows_open = False 155 self.last_remaining_subwindow_was_closed.emit() 156 elif not self.are_there_any_subwindows_open: # If there is indeed a window but the boolean still shows there are none open, then change the boolean 157 self.are_there_any_subwindows_open = True 158 self.first_subwindow_was_opened.emit() 159 self.most_recently_activated_subwindow = window 160 return 161 162 def resizeEvent(self, event): 163 """Override resizeEvent() to maintain horizontal and vertical arrangement of subwindows during resizing. 164 165 Fixes shuffling of subwindows when area is resized in vertical and horizontal arrangements. 166 """ 167 super().resizeEvent(event) 168 169 if self.last_tile_method == "horizontally": 170 self.tile_subwindows_horizontally() 171 elif self.last_tile_method == "vertically": 172 self.tile_subwindows_vertically() 173 else: 174 return
18class QMdiAreaWithCustomSignals(QtWidgets.QMdiArea): 19 """Extend QMdiArea with drag-and-drop functions, vertical/horizontal window tiling, and keyboard shortcuts. 20 21 Instantiate without input. 22 23 Features: 24 Signals for drag-and-drop, subwindow events, shortcut keys. 25 Methods for arranging the subwindows vertically and horizontally, and to track the history of the arrangement. 26 """ 27 28 file_path_dragged_and_dropped = QtCore.pyqtSignal(str) 29 file_path_dragged = QtCore.pyqtSignal(bool) 30 shortcut_escape_was_activated = QtCore.pyqtSignal() 31 shortcut_f_was_activated = QtCore.pyqtSignal() 32 shortcut_h_was_activated = QtCore.pyqtSignal() 33 shortcut_ctrl_c_was_activated = QtCore.pyqtSignal() 34 35 first_subwindow_was_opened = QtCore.pyqtSignal() 36 last_remaining_subwindow_was_closed = QtCore.pyqtSignal() 37 38 def __init__(self): 39 super().__init__() 40 41 self.setAcceptDrops(True) 42 self.subWindowActivated.connect(self.subwindow_was_activated) 43 self.last_tile_method = None 44 self.are_there_any_subwindows_open = False 45 self.most_recently_activated_subwindow = None 46 47 self.escape_shortcut = QtWidgets.QShortcut(QtGui.QKeySequence("Escape"), self) 48 self.escape_shortcut.activated.connect(self.shortcut_escape_was_activated) 49 50 self.f_shortcut = QtWidgets.QShortcut(QtGui.QKeySequence("f"), self) 51 self.f_shortcut.activated.connect(self.shortcut_f_was_activated) 52 53 self.h_shortcut = QtWidgets.QShortcut(QtGui.QKeySequence("h"), self) 54 self.h_shortcut.activated.connect(self.shortcut_h_was_activated) 55 56 self.ctrl_c_shortcut = QtWidgets.QShortcut(QtGui.QKeySequence("Ctrl+c"), self) 57 self.ctrl_c_shortcut.activated.connect(self.shortcut_ctrl_c_was_activated) 58 59 def tile_subwindows_vertically(self, button_input=None): 60 """Arrange subwindows vertically as a single column. 61 62 Arranges subwindows top to bottom in order of when they were added (oldest to newest). 63 64 Args: 65 button_input: Always set as None (kept for legacy purposes). 66 """ 67 windows = self.subWindowList() 68 position = QtCore.QPoint() 69 for window in windows: 70 rect = QtCore.QRect(0, 0, self.width(), self.height()/len(windows)) 71 window.setGeometry(rect) 72 window.move(position) 73 position.setY(position.y() + window.height()) 74 self.last_tile_method = "vertically" 75 76 def tile_subwindows_horizontally(self, button_input=None): 77 """Arrange subwindows horizontally as a single row. 78 79 Arranges subwindows left to right in order of when they were added (oldest to newest). 80 81 Args: 82 button_input: Always set as None (kept for legacy purposes). 83 """ 84 windows = self.subWindowList() 85 position = QtCore.QPoint() 86 for window in windows: 87 rect = QtCore.QRect(0, 0, self.width()/len(windows), self.height()) 88 window.setGeometry(rect) 89 window.move(position) 90 position.setX(position.x() + window.width()) 91 self.last_tile_method = "horizontally" 92 93 def tileSubWindows(self, button_input=None): 94 """Arrange subwindows as tiles (override). 95 96 Args: 97 button_input: Always set as None (kept for legacy purposes). 98 """ 99 super().tileSubWindows() 100 self.last_tile_method = "grid" 101 102 def tile_what_was_done_last_time(self): 103 """Arrange subwindows based on previous arrangement. 104 105 Needed to arrange windows in the last arranged method during events like resizing. 106 """ 107 if self.last_tile_method == "horizontally": 108 self.tile_subwindows_horizontally() 109 elif self.last_tile_method == "vertically": 110 self.tile_subwindows_vertically() 111 else: 112 self.tileSubWindows() 113 114 def dragEnterEvent(self, event): 115 """event: Signal that one or more files have been dragged into the area.""" 116 self.file_path_dragged.emit(True) 117 event.accept() 118 119 def dragMoveEvent(self, event): 120 """event: Signal that one or more files are being dragged in the area.""" 121 event.accept() 122 123 def dragLeaveEvent(self, event): 124 """event: Signal that one or more files have been dragged out of the area.""" 125 self.file_path_dragged.emit(False) 126 event.accept() 127 128 def dropEvent(self, event): 129 """event: Signal that one or more files have been dropped into the area.""" 130 event.setDropAction(QtCore.Qt.CopyAction) 131 132 self.file_path_dragged.emit(False) 133 134 urls = event.mimeData().urls() 135 136 if urls: 137 for url in urls: 138 file_path = url.toLocalFile() 139 self.file_path_dragged_and_dropped.emit(file_path) 140 event.accept() 141 else: 142 event.ignore() 143 144 def subwindow_was_activated(self, window): 145 """Signal if first subwindow has been activated or if last remaining subwindow has been closed. 146 147 Triggered when subwindow activated signal of area is emitted. 148 Fixes issues with improper subwindow activation behavior. 149 150 Args: 151 window (QMdiSubWindow) 152 """ 153 154 if not window: # When the last remaining subwindow is closed, subWindowActivated throws Null window 155 self.are_there_any_subwindows_open = False 156 self.last_remaining_subwindow_was_closed.emit() 157 elif not self.are_there_any_subwindows_open: # If there is indeed a window but the boolean still shows there are none open, then change the boolean 158 self.are_there_any_subwindows_open = True 159 self.first_subwindow_was_opened.emit() 160 self.most_recently_activated_subwindow = window 161 return 162 163 def resizeEvent(self, event): 164 """Override resizeEvent() to maintain horizontal and vertical arrangement of subwindows during resizing. 165 166 Fixes shuffling of subwindows when area is resized in vertical and horizontal arrangements. 167 """ 168 super().resizeEvent(event) 169 170 if self.last_tile_method == "horizontally": 171 self.tile_subwindows_horizontally() 172 elif self.last_tile_method == "vertically": 173 self.tile_subwindows_vertically() 174 else: 175 return
Extend QMdiArea with drag-and-drop functions, vertical/horizontal window tiling, and keyboard shortcuts.
Instantiate without input.
Features:
Signals for drag-and-drop, subwindow events, shortcut keys. Methods for arranging the subwindows vertically and horizontally, and to track the history of the arrangement.
59 def tile_subwindows_vertically(self, button_input=None): 60 """Arrange subwindows vertically as a single column. 61 62 Arranges subwindows top to bottom in order of when they were added (oldest to newest). 63 64 Args: 65 button_input: Always set as None (kept for legacy purposes). 66 """ 67 windows = self.subWindowList() 68 position = QtCore.QPoint() 69 for window in windows: 70 rect = QtCore.QRect(0, 0, self.width(), self.height()/len(windows)) 71 window.setGeometry(rect) 72 window.move(position) 73 position.setY(position.y() + window.height()) 74 self.last_tile_method = "vertically"
Arrange subwindows vertically as a single column.
Arranges subwindows top to bottom in order of when they were added (oldest to newest).
Arguments:
- button_input: Always set as None (kept for legacy purposes).
76 def tile_subwindows_horizontally(self, button_input=None): 77 """Arrange subwindows horizontally as a single row. 78 79 Arranges subwindows left to right in order of when they were added (oldest to newest). 80 81 Args: 82 button_input: Always set as None (kept for legacy purposes). 83 """ 84 windows = self.subWindowList() 85 position = QtCore.QPoint() 86 for window in windows: 87 rect = QtCore.QRect(0, 0, self.width()/len(windows), self.height()) 88 window.setGeometry(rect) 89 window.move(position) 90 position.setX(position.x() + window.width()) 91 self.last_tile_method = "horizontally"
Arrange subwindows horizontally as a single row.
Arranges subwindows left to right in order of when they were added (oldest to newest).
Arguments:
- button_input: Always set as None (kept for legacy purposes).
93 def tileSubWindows(self, button_input=None): 94 """Arrange subwindows as tiles (override). 95 96 Args: 97 button_input: Always set as None (kept for legacy purposes). 98 """ 99 super().tileSubWindows() 100 self.last_tile_method = "grid"
Arrange subwindows as tiles (override).
Arguments:
- button_input: Always set as None (kept for legacy purposes).
102 def tile_what_was_done_last_time(self): 103 """Arrange subwindows based on previous arrangement. 104 105 Needed to arrange windows in the last arranged method during events like resizing. 106 """ 107 if self.last_tile_method == "horizontally": 108 self.tile_subwindows_horizontally() 109 elif self.last_tile_method == "vertically": 110 self.tile_subwindows_vertically() 111 else: 112 self.tileSubWindows()
Arrange subwindows based on previous arrangement.
Needed to arrange windows in the last arranged method during events like resizing.
114 def dragEnterEvent(self, event): 115 """event: Signal that one or more files have been dragged into the area.""" 116 self.file_path_dragged.emit(True) 117 event.accept()
event: Signal that one or more files have been dragged into the area.
119 def dragMoveEvent(self, event): 120 """event: Signal that one or more files are being dragged in the area.""" 121 event.accept()
event: Signal that one or more files are being dragged in the area.
123 def dragLeaveEvent(self, event): 124 """event: Signal that one or more files have been dragged out of the area.""" 125 self.file_path_dragged.emit(False) 126 event.accept()
event: Signal that one or more files have been dragged out of the area.
128 def dropEvent(self, event): 129 """event: Signal that one or more files have been dropped into the area.""" 130 event.setDropAction(QtCore.Qt.CopyAction) 131 132 self.file_path_dragged.emit(False) 133 134 urls = event.mimeData().urls() 135 136 if urls: 137 for url in urls: 138 file_path = url.toLocalFile() 139 self.file_path_dragged_and_dropped.emit(file_path) 140 event.accept() 141 else: 142 event.ignore()
event: Signal that one or more files have been dropped into the area.
144 def subwindow_was_activated(self, window): 145 """Signal if first subwindow has been activated or if last remaining subwindow has been closed. 146 147 Triggered when subwindow activated signal of area is emitted. 148 Fixes issues with improper subwindow activation behavior. 149 150 Args: 151 window (QMdiSubWindow) 152 """ 153 154 if not window: # When the last remaining subwindow is closed, subWindowActivated throws Null window 155 self.are_there_any_subwindows_open = False 156 self.last_remaining_subwindow_was_closed.emit() 157 elif not self.are_there_any_subwindows_open: # If there is indeed a window but the boolean still shows there are none open, then change the boolean 158 self.are_there_any_subwindows_open = True 159 self.first_subwindow_was_opened.emit() 160 self.most_recently_activated_subwindow = window 161 return
Signal if first subwindow has been activated or if last remaining subwindow has been closed.
Triggered when subwindow activated signal of area is emitted. Fixes issues with improper subwindow activation behavior.
Arguments:
- window (QMdiSubWindow)
163 def resizeEvent(self, event): 164 """Override resizeEvent() to maintain horizontal and vertical arrangement of subwindows during resizing. 165 166 Fixes shuffling of subwindows when area is resized in vertical and horizontal arrangements. 167 """ 168 super().resizeEvent(event) 169 170 if self.last_tile_method == "horizontally": 171 self.tile_subwindows_horizontally() 172 elif self.last_tile_method == "vertically": 173 self.tile_subwindows_vertically() 174 else: 175 return
Override resizeEvent() to maintain horizontal and vertical arrangement of subwindows during resizing.
Fixes shuffling of subwindows when area is resized in vertical and horizontal arrangements.