eric7/E5Gui/E5ToolButton.py

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

eric ide

mercurial