9 |
9 |
10 import enum |
10 import enum |
11 |
11 |
12 from PyQt6.QtCore import pyqtSlot, pyqtSignal, Qt, QTimer, QSize |
12 from PyQt6.QtCore import pyqtSlot, pyqtSignal, Qt, QTimer, QSize |
13 from PyQt6.QtWidgets import ( |
13 from PyQt6.QtWidgets import ( |
14 QToolButton, QStyle, QStyleOptionToolButton, QStyleOption, QApplication, |
14 QToolButton, |
15 QLabel |
15 QStyle, |
|
16 QStyleOptionToolButton, |
|
17 QStyleOption, |
|
18 QApplication, |
|
19 QLabel, |
16 ) |
20 ) |
17 |
21 |
18 |
22 |
19 class EricToolButtonOptions(enum.IntEnum): |
23 class EricToolButtonOptions(enum.IntEnum): |
20 """ |
24 """ |
21 Class defining the tool button options. |
25 Class defining the tool button options. |
22 """ |
26 """ |
|
27 |
23 DEFAULT = 0 |
28 DEFAULT = 0 |
24 SHOW_MENU_INSIDE = 1 |
29 SHOW_MENU_INSIDE = 1 |
25 TOOLBAR_LOOKUP = 2 |
30 TOOLBAR_LOOKUP = 2 |
26 |
31 |
27 |
32 |
28 class EricToolButton(QToolButton): |
33 class EricToolButton(QToolButton): |
29 """ |
34 """ |
30 Class implementing a specialized tool button subclass. |
35 Class implementing a specialized tool button subclass. |
31 |
36 |
32 @signal aboutToShowMenu() emitted before the tool button menu is shown |
37 @signal aboutToShowMenu() emitted before the tool button menu is shown |
33 @signal aboutToHideMenu() emitted before the tool button menu is hidden |
38 @signal aboutToHideMenu() emitted before the tool button menu is hidden |
34 @signal middleClicked() emitted when the middle mouse button was clicked |
39 @signal middleClicked() emitted when the middle mouse button was clicked |
35 @signal controlClicked() emitted when the left mouse button was |
40 @signal controlClicked() emitted when the left mouse button was |
36 clicked while pressing the Ctrl key |
41 clicked while pressing the Ctrl key |
37 @signal doubleClicked() emitted when the left mouse button was |
42 @signal doubleClicked() emitted when the left mouse button was |
38 double clicked |
43 double clicked |
39 """ |
44 """ |
|
45 |
40 aboutToShowMenu = pyqtSignal() |
46 aboutToShowMenu = pyqtSignal() |
41 aboutToHideMenu = pyqtSignal() |
47 aboutToHideMenu = pyqtSignal() |
42 middleClicked = pyqtSignal() |
48 middleClicked = pyqtSignal() |
43 controlClicked = pyqtSignal() |
49 controlClicked = pyqtSignal() |
44 doubleClicked = pyqtSignal() |
50 doubleClicked = pyqtSignal() |
45 |
51 |
46 def __init__(self, parent=None): |
52 def __init__(self, parent=None): |
47 """ |
53 """ |
48 Constructor |
54 Constructor |
49 |
55 |
50 @param parent reference to the parent widget |
56 @param parent reference to the parent widget |
51 @type QWidget |
57 @type QWidget |
52 """ |
58 """ |
53 super().__init__(parent) |
59 super().__init__(parent) |
54 |
60 |
55 self.setMinimumWidth(16) |
61 self.setMinimumWidth(16) |
56 |
62 |
57 self.__menu = None |
63 self.__menu = None |
58 self.__options = EricToolButtonOptions.DEFAULT |
64 self.__options = EricToolButtonOptions.DEFAULT |
59 |
65 |
60 self.__badgeLabel = QLabel(self) |
66 self.__badgeLabel = QLabel(self) |
61 font = self.__badgeLabel.font() |
67 font = self.__badgeLabel.font() |
62 font.setPixelSize(int(self.__badgeLabel.height() / 2.5)) |
68 font.setPixelSize(int(self.__badgeLabel.height() / 2.5)) |
63 self.__badgeLabel.setFont(font) |
69 self.__badgeLabel.setFont(font) |
64 self.__badgeLabel.hide() |
70 self.__badgeLabel.hide() |
65 |
71 |
66 opt = QStyleOptionToolButton() |
72 opt = QStyleOptionToolButton() |
67 self.initStyleOption(opt) |
73 self.initStyleOption(opt) |
68 |
74 |
69 self.__pressTimer = QTimer() |
75 self.__pressTimer = QTimer() |
70 self.__pressTimer.setSingleShot(True) |
76 self.__pressTimer.setSingleShot(True) |
71 self.__pressTimer.setInterval( |
77 self.__pressTimer.setInterval( |
72 QApplication.style().styleHint( |
78 QApplication.style().styleHint( |
73 QStyle.StyleHint.SH_ToolButton_PopupDelay, opt, self)) |
79 QStyle.StyleHint.SH_ToolButton_PopupDelay, opt, self |
|
80 ) |
|
81 ) |
74 self.__pressTimer.timeout.connect(self.__showMenu) |
82 self.__pressTimer.timeout.connect(self.__showMenu) |
75 |
83 |
76 ################################################################## |
84 ################################################################## |
77 ## Menu handling methods below. |
85 ## Menu handling methods below. |
78 ## |
86 ## |
79 ## The menu is handled in EricToolButton and is not passed to |
87 ## The menu is handled in EricToolButton and is not passed to |
80 ## QToolButton. No menu indicator will be shown in the button. |
88 ## QToolButton. No menu indicator will be shown in the button. |
81 ################################################################## |
89 ################################################################## |
82 |
90 |
83 def menu(self): |
91 def menu(self): |
84 """ |
92 """ |
85 Public method to get a reference to the tool button menu. |
93 Public method to get a reference to the tool button menu. |
86 |
94 |
87 @return reference to the tool button menu |
95 @return reference to the tool button menu |
88 @rtype QMenu |
96 @rtype QMenu |
89 """ |
97 """ |
90 return self.__menu |
98 return self.__menu |
91 |
99 |
92 def setMenu(self, menu): |
100 def setMenu(self, menu): |
93 """ |
101 """ |
94 Public method to set the tool button menu. |
102 Public method to set the tool button menu. |
95 |
103 |
96 @param menu reference to the tool button menu |
104 @param menu reference to the tool button menu |
97 @type QMenu |
105 @type QMenu |
98 """ |
106 """ |
99 if menu is not None: |
107 if menu is not None: |
100 if self.__menu: |
108 if self.__menu: |
101 self.__menu.aboutToHide.disconnect(self.__menuAboutToHide) |
109 self.__menu.aboutToHide.disconnect(self.__menuAboutToHide) |
102 |
110 |
103 self.__menu = menu |
111 self.__menu = menu |
104 self.__menu.aboutToHide.connect(self.__menuAboutToHide) |
112 self.__menu.aboutToHide.connect(self.__menuAboutToHide) |
105 |
113 |
106 def showMenuInside(self): |
114 def showMenuInside(self): |
107 """ |
115 """ |
108 Public method to check, if the menu edge shall be aligned with |
116 Public method to check, if the menu edge shall be aligned with |
109 the button. |
117 the button. |
110 |
118 |
111 @return flag indicating that the menu edge shall be aligned |
119 @return flag indicating that the menu edge shall be aligned |
112 @rtype bool |
120 @rtype bool |
113 """ |
121 """ |
114 return bool(self.__options & EricToolButtonOptions.SHOW_MENU_INSIDE) |
122 return bool(self.__options & EricToolButtonOptions.SHOW_MENU_INSIDE) |
115 |
123 |
116 def setShowMenuInside(self, enable): |
124 def setShowMenuInside(self, enable): |
117 """ |
125 """ |
118 Public method to set a flag to show the menu edge aligned with |
126 Public method to set a flag to show the menu edge aligned with |
119 the button. |
127 the button. |
120 |
128 |
121 @param enable flag indicating to align the menu edge to the button |
129 @param enable flag indicating to align the menu edge to the button |
122 @type bool |
130 @type bool |
123 """ |
131 """ |
124 if enable: |
132 if enable: |
125 self.__options |= EricToolButtonOptions.SHOW_MENU_INSIDE |
133 self.__options |= EricToolButtonOptions.SHOW_MENU_INSIDE |
126 else: |
134 else: |
127 self.__options &= ~EricToolButtonOptions.SHOW_MENU_INSIDE |
135 self.__options &= ~EricToolButtonOptions.SHOW_MENU_INSIDE |
128 |
136 |
129 @pyqtSlot() |
137 @pyqtSlot() |
130 def __showMenu(self): |
138 def __showMenu(self): |
131 """ |
139 """ |
132 Private slot to show the tool button menu. |
140 Private slot to show the tool button menu. |
133 """ |
141 """ |
134 if self.__menu is None or self.__menu.isVisible(): |
142 if self.__menu is None or self.__menu.isVisible(): |
135 return |
143 return |
136 |
144 |
137 self.aboutToShowMenu.emit() |
145 self.aboutToShowMenu.emit() |
138 |
146 |
139 if self.__options & EricToolButtonOptions.SHOW_MENU_INSIDE: |
147 if self.__options & EricToolButtonOptions.SHOW_MENU_INSIDE: |
140 pos = self.mapToGlobal(self.rect().bottomRight()) |
148 pos = self.mapToGlobal(self.rect().bottomRight()) |
141 if ( |
149 if QApplication.layoutDirection() == Qt.LayoutDirection.RightToLeft: |
142 QApplication.layoutDirection() == |
|
143 Qt.LayoutDirection.RightToLeft |
|
144 ): |
|
145 pos.setX(pos.x() - self.rect().width()) |
150 pos.setX(pos.x() - self.rect().width()) |
146 else: |
151 else: |
147 pos.setX(pos.x() - self.__menu.sizeHint().width()) |
152 pos.setX(pos.x() - self.__menu.sizeHint().width()) |
148 else: |
153 else: |
149 pos = self.mapToGlobal(self.rect().bottomLeft()) |
154 pos = self.mapToGlobal(self.rect().bottomLeft()) |
150 |
155 |
151 self.__menu.popup(pos) |
156 self.__menu.popup(pos) |
152 |
157 |
153 @pyqtSlot() |
158 @pyqtSlot() |
154 def __menuAboutToHide(self): |
159 def __menuAboutToHide(self): |
155 """ |
160 """ |
156 Private slot to handle the tool button menu about to be hidden. |
161 Private slot to handle the tool button menu about to be hidden. |
157 """ |
162 """ |
158 self.setDown(False) |
163 self.setDown(False) |
159 self.aboutToHideMenu.emit() |
164 self.aboutToHideMenu.emit() |
160 |
165 |
161 ################################################################## |
166 ################################################################## |
162 ## Methods to handle the tool button look |
167 ## Methods to handle the tool button look |
163 ################################################################## |
168 ################################################################## |
164 |
169 |
165 def toolbarButtonLook(self): |
170 def toolbarButtonLook(self): |
166 """ |
171 """ |
167 Public method to check, if the button has the toolbar look. |
172 Public method to check, if the button has the toolbar look. |
168 |
173 |
169 @return flag indicating toolbar look |
174 @return flag indicating toolbar look |
170 @rtype bool |
175 @rtype bool |
171 """ |
176 """ |
172 return bool(self.__options & EricToolButtonOptions.TOOLBAR_LOOKUP) |
177 return bool(self.__options & EricToolButtonOptions.TOOLBAR_LOOKUP) |
173 |
178 |
174 def setToolbarButtonLook(self, enable): |
179 def setToolbarButtonLook(self, enable): |
175 """ |
180 """ |
176 Public method to set the toolbar look state. |
181 Public method to set the toolbar look state. |
177 |
182 |
178 @param enable flag indicating toolbar look |
183 @param enable flag indicating toolbar look |
179 @type bool |
184 @type bool |
180 """ |
185 """ |
181 if enable: |
186 if enable: |
182 self.__options |= EricToolButtonOptions.TOOLBAR_LOOKUP |
187 self.__options |= EricToolButtonOptions.TOOLBAR_LOOKUP |
183 |
188 |
184 opt = QStyleOption() |
189 opt = QStyleOption() |
185 opt.initFrom(self) |
190 opt.initFrom(self) |
186 size = self.style().pixelMetric( |
191 size = self.style().pixelMetric( |
187 QStyle.PixelMetric.PM_ToolBarIconSize, opt, self) |
192 QStyle.PixelMetric.PM_ToolBarIconSize, opt, self |
|
193 ) |
188 self.setIconSize(QSize(size, size)) |
194 self.setIconSize(QSize(size, size)) |
189 else: |
195 else: |
190 self.__options &= ~EricToolButtonOptions.TOOLBAR_LOOKUP |
196 self.__options &= ~EricToolButtonOptions.TOOLBAR_LOOKUP |
191 |
197 |
192 self.setProperty("toolbar-look", enable) |
198 self.setProperty("toolbar-look", enable) |
193 self.style().unpolish(self) |
199 self.style().unpolish(self) |
194 self.style().polish(self) |
200 self.style().polish(self) |
195 |
201 |
196 ################################################################## |
202 ################################################################## |
197 ## Methods to handle some event types |
203 ## Methods to handle some event types |
198 ################################################################## |
204 ################################################################## |
199 |
205 |
200 def mousePressEvent(self, evt): |
206 def mousePressEvent(self, evt): |
201 """ |
207 """ |
202 Protected method to handle mouse press events. |
208 Protected method to handle mouse press events. |
203 |
209 |
204 @param evt reference to the mouse event |
210 @param evt reference to the mouse event |
205 @type QMouseEvent |
211 @type QMouseEvent |
206 """ |
212 """ |
207 if self.popupMode() == QToolButton.ToolButtonPopupMode.DelayedPopup: |
213 if self.popupMode() == QToolButton.ToolButtonPopupMode.DelayedPopup: |
208 self.__pressTimer.start() |
214 self.__pressTimer.start() |
209 |
215 |
210 if ( |
216 if ( |
211 (evt.buttons() == Qt.MouseButton.LeftButton and |
217 evt.buttons() == Qt.MouseButton.LeftButton |
212 self.__menu is not None and |
218 and self.__menu is not None |
213 (self.popupMode() == |
219 and (self.popupMode() == QToolButton.ToolButtonPopupMode.InstantPopup) |
214 QToolButton.ToolButtonPopupMode.InstantPopup)) or |
220 ) or (evt.buttons() == Qt.MouseButton.RightButton and self.__menu is not None): |
215 (evt.buttons() == Qt.MouseButton.RightButton and |
|
216 self.__menu is not None) |
|
217 ): |
|
218 self.setDown(True) |
221 self.setDown(True) |
219 self.__showMenu() |
222 self.__showMenu() |
220 else: |
223 else: |
221 super().mousePressEvent(evt) |
224 super().mousePressEvent(evt) |
222 |
225 |
223 def mouseReleaseEvent(self, evt): |
226 def mouseReleaseEvent(self, evt): |
224 """ |
227 """ |
225 Protected method to handle mouse release events. |
228 Protected method to handle mouse release events. |
226 |
229 |
227 @param evt reference to the mouse event |
230 @param evt reference to the mouse event |
228 @type QMouseEvent |
231 @type QMouseEvent |
229 """ |
232 """ |
230 self.__pressTimer.stop() |
233 self.__pressTimer.stop() |
231 |
234 |
232 if ( |
235 if evt.button() == Qt.MouseButton.MiddleButton and self.rect().contains( |
233 evt.button() == Qt.MouseButton.MiddleButton and |
236 evt.position().toPoint() |
234 self.rect().contains(evt.position().toPoint()) |
|
235 ): |
237 ): |
236 self.middleClicked.emit() |
238 self.middleClicked.emit() |
237 self.setDown(False) |
239 self.setDown(False) |
238 elif ( |
240 elif ( |
239 evt.button() == Qt.MouseButton.LeftButton and |
241 evt.button() == Qt.MouseButton.LeftButton |
240 self.rect().contains(evt.position().toPoint()) and |
242 and self.rect().contains(evt.position().toPoint()) |
241 evt.modifiers() == Qt.KeyboardModifier.ControlModifier |
243 and evt.modifiers() == Qt.KeyboardModifier.ControlModifier |
242 ): |
244 ): |
243 self.controlClicked.emit() |
245 self.controlClicked.emit() |
244 self.setDown(False) |
246 self.setDown(False) |
245 else: |
247 else: |
246 super().mouseReleaseEvent(evt) |
248 super().mouseReleaseEvent(evt) |
247 |
249 |
248 def mouseDoubleClickEvent(self, evt): |
250 def mouseDoubleClickEvent(self, evt): |
249 """ |
251 """ |
250 Protected method to handle mouse double click events. |
252 Protected method to handle mouse double click events. |
251 |
253 |
252 @param evt reference to the mouse event |
254 @param evt reference to the mouse event |
253 @type QMouseEvent |
255 @type QMouseEvent |
254 """ |
256 """ |
255 super().mouseDoubleClickEvent(evt) |
257 super().mouseDoubleClickEvent(evt) |
256 |
258 |
257 self.__pressTimer.stop() |
259 self.__pressTimer.stop() |
258 |
260 |
259 if evt.buttons() == Qt.MouseButton.LeftButton: |
261 if evt.buttons() == Qt.MouseButton.LeftButton: |
260 self.doubleClicked.emit() |
262 self.doubleClicked.emit() |
261 |
263 |
262 def contextMenuEvent(self, evt): |
264 def contextMenuEvent(self, evt): |
263 """ |
265 """ |
264 Protected method to handle context menu events. |
266 Protected method to handle context menu events. |
265 |
267 |
266 @param evt reference to the context menu event |
268 @param evt reference to the context menu event |
267 @type QContextMenuEvent |
269 @type QContextMenuEvent |
268 """ |
270 """ |
269 # block to prevent showing the context menu and the tool button menu |
271 # block to prevent showing the context menu and the tool button menu |
270 if self.__menu is not None: |
272 if self.__menu is not None: |
271 return |
273 return |
272 |
274 |
273 super().contextMenuEvent(evt) |
275 super().contextMenuEvent(evt) |
274 |
276 |
275 ################################################################## |
277 ################################################################## |
276 ## Methods to handle the tool button badge |
278 ## Methods to handle the tool button badge |
277 ################################################################## |
279 ################################################################## |
278 |
280 |
279 def setBadgeText(self, text): |
281 def setBadgeText(self, text): |
280 """ |
282 """ |
281 Public method to set the badge text. |
283 Public method to set the badge text. |
282 |
284 |
283 @param text badge text to be set |
285 @param text badge text to be set |
284 @type str |
286 @type str |
285 """ |
287 """ |
286 if text: |
288 if text: |
287 self.__badgeLabel.setText(text) |
289 self.__badgeLabel.setText(text) |