src/eric7/EricWidgets/EricTabWidget.py

branch
eric7
changeset 9221
bf71ee032bb4
parent 9209
b99e7fd55fd3
child 9413
80c06d472826
equal deleted inserted replaced
9220:e9e7eca7efee 9221:bf71ee032bb4
19 class EricWheelTabBar(QTabBar): 19 class EricWheelTabBar(QTabBar):
20 """ 20 """
21 Class implementing a tab bar class substituting QTabBar to support wheel 21 Class implementing a tab bar class substituting QTabBar to support wheel
22 events. 22 events.
23 """ 23 """
24
24 def __init__(self, parent=None): 25 def __init__(self, parent=None):
25 """ 26 """
26 Constructor 27 Constructor
27 28
28 @param parent reference to the parent widget (QWidget) 29 @param parent reference to the parent widget (QWidget)
29 """ 30 """
30 super().__init__(parent) 31 super().__init__(parent)
31 self._tabWidget = parent 32 self._tabWidget = parent
32 33
33 def wheelEvent(self, event): 34 def wheelEvent(self, event):
34 """ 35 """
35 Protected slot to support wheel events. 36 Protected slot to support wheel events.
36 37
37 @param event reference to the wheel event (QWheelEvent) 38 @param event reference to the wheel event (QWheelEvent)
38 """ 39 """
39 with contextlib.suppress(AttributeError): 40 with contextlib.suppress(AttributeError):
40 delta = event.angleDelta().y() 41 delta = event.angleDelta().y()
41 if delta > 0: 42 if delta > 0:
42 self._tabWidget.prevTab() 43 self._tabWidget.prevTab()
43 elif delta < 0: 44 elif delta < 0:
44 self._tabWidget.nextTab() 45 self._tabWidget.nextTab()
45 46
46 event.accept() 47 event.accept()
47 48
48 49
49 class EricDnDTabBar(EricWheelTabBar): 50 class EricDnDTabBar(EricWheelTabBar):
50 """ 51 """
51 Class implementing a tab bar class substituting QTabBar. 52 Class implementing a tab bar class substituting QTabBar.
52 53
53 @signal tabMoveRequested(int, int) emitted to signal a tab move request 54 @signal tabMoveRequested(int, int) emitted to signal a tab move request
54 giving the old and new index position 55 giving the old and new index position
55 """ 56 """
57
56 tabMoveRequested = pyqtSignal(int, int) 58 tabMoveRequested = pyqtSignal(int, int)
57 59
58 def __init__(self, parent=None): 60 def __init__(self, parent=None):
59 """ 61 """
60 Constructor 62 Constructor
61 63
62 @param parent reference to the parent widget (QWidget) 64 @param parent reference to the parent widget (QWidget)
63 """ 65 """
64 EricWheelTabBar.__init__(self, parent) 66 EricWheelTabBar.__init__(self, parent)
65 self.setAcceptDrops(True) 67 self.setAcceptDrops(True)
66 68
67 self.__dragStartPos = QPoint() 69 self.__dragStartPos = QPoint()
68 70
69 def mousePressEvent(self, event): 71 def mousePressEvent(self, event):
70 """ 72 """
71 Protected method to handle mouse press events. 73 Protected method to handle mouse press events.
72 74
73 @param event reference to the mouse press event (QMouseEvent) 75 @param event reference to the mouse press event (QMouseEvent)
74 """ 76 """
75 if event.button() == Qt.MouseButton.LeftButton: 77 if event.button() == Qt.MouseButton.LeftButton:
76 self.__dragStartPos = QPoint(event.position().toPoint()) 78 self.__dragStartPos = QPoint(event.position().toPoint())
77 EricWheelTabBar.mousePressEvent(self, event) 79 EricWheelTabBar.mousePressEvent(self, event)
78 80
79 def mouseMoveEvent(self, event): 81 def mouseMoveEvent(self, event):
80 """ 82 """
81 Protected method to handle mouse move events. 83 Protected method to handle mouse move events.
82 84
83 @param event reference to the mouse move event (QMouseEvent) 85 @param event reference to the mouse move event (QMouseEvent)
84 """ 86 """
85 if ( 87 if (
86 event.buttons() == Qt.MouseButton.LeftButton and 88 event.buttons() == Qt.MouseButton.LeftButton
87 (event.position().toPoint() - self.__dragStartPos) 89 and (event.position().toPoint() - self.__dragStartPos).manhattanLength()
88 .manhattanLength() > QApplication.startDragDistance() 90 > QApplication.startDragDistance()
89 ): 91 ):
90 drag = QDrag(self) 92 drag = QDrag(self)
91 mimeData = QMimeData() 93 mimeData = QMimeData()
92 index = self.tabAt(event.position().toPoint()) 94 index = self.tabAt(event.position().toPoint())
93 mimeData.setText(self.tabText(index)) 95 mimeData.setText(self.tabText(index))
94 mimeData.setData("action", b"tab-reordering") 96 mimeData.setData("action", b"tab-reordering")
95 mimeData.setData("tabbar-id", str(id(self)).encode("utf-8")) 97 mimeData.setData("tabbar-id", str(id(self)).encode("utf-8"))
96 drag.setMimeData(mimeData) 98 drag.setMimeData(mimeData)
97 drag.exec() 99 drag.exec()
98 EricWheelTabBar.mouseMoveEvent(self, event) 100 EricWheelTabBar.mouseMoveEvent(self, event)
99 101
100 def dragEnterEvent(self, event): 102 def dragEnterEvent(self, event):
101 """ 103 """
102 Protected method to handle drag enter events. 104 Protected method to handle drag enter events.
103 105
104 @param event reference to the drag enter event (QDragEnterEvent) 106 @param event reference to the drag enter event (QDragEnterEvent)
105 """ 107 """
106 mimeData = event.mimeData() 108 mimeData = event.mimeData()
107 formats = mimeData.formats() 109 formats = mimeData.formats()
108 if ( 110 if (
109 "action" in formats and 111 "action" in formats
110 mimeData.data("action") == b"tab-reordering" and 112 and mimeData.data("action") == b"tab-reordering"
111 "tabbar-id" in formats and 113 and "tabbar-id" in formats
112 int(mimeData.data("tabbar-id")) == id(self) 114 and int(mimeData.data("tabbar-id")) == id(self)
113 ): 115 ):
114 event.acceptProposedAction() 116 event.acceptProposedAction()
115 EricWheelTabBar.dragEnterEvent(self, event) 117 EricWheelTabBar.dragEnterEvent(self, event)
116 118
117 def dropEvent(self, event): 119 def dropEvent(self, event):
118 """ 120 """
119 Protected method to handle drop events. 121 Protected method to handle drop events.
120 122
121 @param event reference to the drop event (QDropEvent) 123 @param event reference to the drop event (QDropEvent)
122 """ 124 """
123 fromIndex = self.tabAt(self.__dragStartPos) 125 fromIndex = self.tabAt(self.__dragStartPos)
124 toIndex = self.tabAt(event.position().toPoint()) 126 toIndex = self.tabAt(event.position().toPoint())
125 if fromIndex != toIndex: 127 if fromIndex != toIndex:
129 131
130 132
131 class EricTabWidget(QTabWidget): 133 class EricTabWidget(QTabWidget):
132 """ 134 """
133 Class implementing a tab widget class substituting QTabWidget. 135 Class implementing a tab widget class substituting QTabWidget.
134 136
135 It provides slots to show the previous and next tab and give 137 It provides slots to show the previous and next tab and give
136 them the input focus and it allows to have a context menu for the tabs. 138 them the input focus and it allows to have a context menu for the tabs.
137 139
138 @signal customTabContextMenuRequested(const QPoint & point, int index) 140 @signal customTabContextMenuRequested(const QPoint & point, int index)
139 emitted when a context menu for a tab is requested 141 emitted when a context menu for a tab is requested
140 """ 142 """
143
141 customTabContextMenuRequested = pyqtSignal(QPoint, int) 144 customTabContextMenuRequested = pyqtSignal(QPoint, int)
142 145
143 def __init__(self, parent=None, dnd=False): 146 def __init__(self, parent=None, dnd=False):
144 """ 147 """
145 Constructor 148 Constructor
146 149
147 @param parent reference to the parent widget (QWidget) 150 @param parent reference to the parent widget (QWidget)
148 @param dnd flag indicating the support for Drag & Drop (boolean) 151 @param dnd flag indicating the support for Drag & Drop (boolean)
149 """ 152 """
150 super().__init__(parent) 153 super().__init__(parent)
151 154
152 if dnd: 155 if dnd:
153 if not hasattr(self, 'setMovable'): 156 if not hasattr(self, "setMovable"):
154 self.__tabBar = EricDnDTabBar(self) 157 self.__tabBar = EricDnDTabBar(self)
155 self.__tabBar.tabMoveRequested.connect(self.moveTab) 158 self.__tabBar.tabMoveRequested.connect(self.moveTab)
156 self.setTabBar(self.__tabBar) 159 self.setTabBar(self.__tabBar)
157 else: 160 else:
158 self.__tabBar = EricWheelTabBar(self) 161 self.__tabBar = EricWheelTabBar(self)
159 self.setTabBar(self.__tabBar) 162 self.setTabBar(self.__tabBar)
160 self.setMovable(True) 163 self.setMovable(True)
161 else: 164 else:
162 self.__tabBar = EricWheelTabBar(self) 165 self.__tabBar = EricWheelTabBar(self)
163 self.setTabBar(self.__tabBar) 166 self.setTabBar(self.__tabBar)
164 167
165 self.__lastCurrentIndex = -1 168 self.__lastCurrentIndex = -1
166 self.__currentIndex = -1 169 self.__currentIndex = -1
167 self.currentChanged.connect(self.__currentChanged) 170 self.currentChanged.connect(self.__currentChanged)
168 171
169 def setCustomTabBar(self, dnd, tabBar): 172 def setCustomTabBar(self, dnd, tabBar):
170 """ 173 """
171 Public method to set a custom tab bar. 174 Public method to set a custom tab bar.
172 175
173 @param dnd flag indicating the support for Drag & Drop (boolean) 176 @param dnd flag indicating the support for Drag & Drop (boolean)
174 @param tabBar reference to the tab bar to set (QTabBar) 177 @param tabBar reference to the tab bar to set (QTabBar)
175 """ 178 """
176 self.__tabBar = tabBar 179 self.__tabBar = tabBar
177 self.setTabBar(self.__tabBar) 180 self.setTabBar(self.__tabBar)
178 if dnd: 181 if dnd:
179 if isinstance(tabBar, EricDnDTabBar): 182 if isinstance(tabBar, EricDnDTabBar):
180 self.__tabBar.tabMoveRequested.connect(self.moveTab) 183 self.__tabBar.tabMoveRequested.connect(self.moveTab)
181 else: 184 else:
182 self.setMovable(True) 185 self.setMovable(True)
183 186
184 def __currentChanged(self, index): 187 def __currentChanged(self, index):
185 """ 188 """
186 Private slot to handle the currentChanged signal. 189 Private slot to handle the currentChanged signal.
187 190
188 @param index index of the current tab 191 @param index index of the current tab
189 """ 192 """
190 if index == -1: 193 if index == -1:
191 self.__lastCurrentIndex = -1 194 self.__lastCurrentIndex = -1
192 else: 195 else:
193 self.__lastCurrentIndex = self.__currentIndex 196 self.__lastCurrentIndex = self.__currentIndex
194 self.__currentIndex = index 197 self.__currentIndex = index
195 198
196 def switchTab(self): 199 def switchTab(self):
197 """ 200 """
198 Public slot used to switch between the current and the previous 201 Public slot used to switch between the current and the previous
199 current tab. 202 current tab.
200 """ 203 """
201 if self.__lastCurrentIndex == -1 or self.__currentIndex == -1: 204 if self.__lastCurrentIndex == -1 or self.__currentIndex == -1:
202 return 205 return
203 206
204 self.setCurrentIndex(self.__lastCurrentIndex) 207 self.setCurrentIndex(self.__lastCurrentIndex)
205 self.currentWidget().setFocus() 208 self.currentWidget().setFocus()
206 209
207 def nextTab(self): 210 def nextTab(self):
208 """ 211 """
209 Public slot used to show the next tab. 212 Public slot used to show the next tab.
210 """ 213 """
211 ind = self.currentIndex() + 1 214 ind = self.currentIndex() + 1
212 if ind == self.count(): 215 if ind == self.count():
213 ind = 0 216 ind = 0
214 217
215 self.setCurrentIndex(ind) 218 self.setCurrentIndex(ind)
216 self.currentWidget().setFocus() 219 self.currentWidget().setFocus()
217 220
218 def prevTab(self): 221 def prevTab(self):
219 """ 222 """
220 Public slot used to show the previous tab. 223 Public slot used to show the previous tab.
221 """ 224 """
222 ind = self.currentIndex() - 1 225 ind = self.currentIndex() - 1
223 if ind == -1: 226 if ind == -1:
224 ind = self.count() - 1 227 ind = self.count() - 1
225 228
226 self.setCurrentIndex(ind) 229 self.setCurrentIndex(ind)
227 self.currentWidget().setFocus() 230 self.currentWidget().setFocus()
228 231
229 def setTabContextMenuPolicy(self, policy): 232 def setTabContextMenuPolicy(self, policy):
230 """ 233 """
231 Public method to set the context menu policy of the tab. 234 Public method to set the context menu policy of the tab.
232 235
233 @param policy context menu policy to set (Qt.ContextMenuPolicy) 236 @param policy context menu policy to set (Qt.ContextMenuPolicy)
234 """ 237 """
235 self.tabBar().setContextMenuPolicy(policy) 238 self.tabBar().setContextMenuPolicy(policy)
236 if policy == Qt.ContextMenuPolicy.CustomContextMenu: 239 if policy == Qt.ContextMenuPolicy.CustomContextMenu:
237 self.tabBar().customContextMenuRequested.connect( 240 self.tabBar().customContextMenuRequested.connect(
238 self.__handleTabCustomContextMenuRequested) 241 self.__handleTabCustomContextMenuRequested
242 )
239 else: 243 else:
240 self.tabBar().customContextMenuRequested.disconnect( 244 self.tabBar().customContextMenuRequested.disconnect(
241 self.__handleTabCustomContextMenuRequested) 245 self.__handleTabCustomContextMenuRequested
246 )
242 247
243 def __handleTabCustomContextMenuRequested(self, point): 248 def __handleTabCustomContextMenuRequested(self, point):
244 """ 249 """
245 Private slot to handle the context menu request for the tabbar. 250 Private slot to handle the context menu request for the tabbar.
246 251
247 @param point point the context menu was requested (QPoint) 252 @param point point the context menu was requested (QPoint)
248 """ 253 """
249 _tabbar = self.tabBar() 254 _tabbar = self.tabBar()
250 for index in range(_tabbar.count()): 255 for index in range(_tabbar.count()):
251 rect = _tabbar.tabRect(index) 256 rect = _tabbar.tabRect(index)
252 if rect.contains(point): 257 if rect.contains(point):
253 self.customTabContextMenuRequested.emit( 258 self.customTabContextMenuRequested.emit(
254 _tabbar.mapToParent(point), index) 259 _tabbar.mapToParent(point), index
260 )
255 return 261 return
256 262
257 self.customTabContextMenuRequested.emit(_tabbar.mapToParent(point), -1) 263 self.customTabContextMenuRequested.emit(_tabbar.mapToParent(point), -1)
258 264
259 def selectTab(self, pos): 265 def selectTab(self, pos):
260 """ 266 """
261 Public method to get the index of a tab given a position. 267 Public method to get the index of a tab given a position.
262 268
263 @param pos position determining the tab index (QPoint) 269 @param pos position determining the tab index (QPoint)
264 @return index of the tab (integer) 270 @return index of the tab (integer)
265 """ 271 """
266 _tabbar = self.tabBar() 272 _tabbar = self.tabBar()
267 for index in range(_tabbar.count()): 273 for index in range(_tabbar.count()):
268 rect = _tabbar.tabRect(index) 274 rect = _tabbar.tabRect(index)
269 if rect.contains(pos): 275 if rect.contains(pos):
270 return index 276 return index
271 277
272 return -1 278 return -1
273 279
274 def moveTab(self, curIndex, newIndex): 280 def moveTab(self, curIndex, newIndex):
275 """ 281 """
276 Public method to move a tab to a new index. 282 Public method to move a tab to a new index.
277 283
278 @param curIndex index of tab to be moved (integer) 284 @param curIndex index of tab to be moved (integer)
279 @param newIndex index the tab should be moved to (integer) 285 @param newIndex index the tab should be moved to (integer)
280 """ 286 """
281 # step 1: save the tab data of tab to be moved 287 # step 1: save the tab data of tab to be moved
282 toolTip = self.tabToolTip(curIndex) 288 toolTip = self.tabToolTip(curIndex)
283 text = self.tabText(curIndex) 289 text = self.tabText(curIndex)
284 icon = self.tabIcon(curIndex) 290 icon = self.tabIcon(curIndex)
285 whatsThis = self.tabWhatsThis(curIndex) 291 whatsThis = self.tabWhatsThis(curIndex)
286 widget = self.widget(curIndex) 292 widget = self.widget(curIndex)
287 curWidget = self.currentWidget() 293 curWidget = self.currentWidget()
288 294
289 # step 2: move the tab 295 # step 2: move the tab
290 self.removeTab(curIndex) 296 self.removeTab(curIndex)
291 self.insertTab(newIndex, widget, icon, text) 297 self.insertTab(newIndex, widget, icon, text)
292 298
293 # step 3: set the tab data again 299 # step 3: set the tab data again
294 self.setTabToolTip(newIndex, toolTip) 300 self.setTabToolTip(newIndex, toolTip)
295 self.setTabWhatsThis(newIndex, whatsThis) 301 self.setTabWhatsThis(newIndex, whatsThis)
296 302
297 # step 4: set current widget 303 # step 4: set current widget
298 self.setCurrentWidget(curWidget) 304 self.setCurrentWidget(curWidget)
299 305
300 def __freeSide(self): 306 def __freeSide(self):
301 """ 307 """
302 Private method to determine the free side of a tab. 308 Private method to determine the free side of a tab.
303 309
304 @return free side (QTabBar.ButtonPosition) 310 @return free side (QTabBar.ButtonPosition)
305 """ 311 """
306 side = self.__tabBar.style().styleHint( 312 side = self.__tabBar.style().styleHint(
307 QStyle.StyleHint.SH_TabBar_CloseButtonPosition, 313 QStyle.StyleHint.SH_TabBar_CloseButtonPosition, None, None, None
308 None, None, None) 314 )
309 side = ( 315 side = (
310 QTabBar.ButtonPosition.RightSide 316 QTabBar.ButtonPosition.RightSide
311 if side == QTabBar.ButtonPosition.LeftSide else 317 if side == QTabBar.ButtonPosition.LeftSide
312 QTabBar.ButtonPosition.LeftSide 318 else QTabBar.ButtonPosition.LeftSide
313 ) 319 )
314 return side 320 return side
315 321
316 def animationLabel(self, index, animationFile, interval=100): 322 def animationLabel(self, index, animationFile, interval=100):
317 """ 323 """
318 Public slot to set an animated icon. 324 Public slot to set an animated icon.
319 325
320 @param index tab index 326 @param index tab index
321 @type int 327 @type int
322 @param animationFile name of the file containing the animation 328 @param animationFile name of the file containing the animation
323 @type str 329 @type str
324 @param interval interval in milliseconds between animation frames 330 @param interval interval in milliseconds between animation frames
326 @return reference to the created label 332 @return reference to the created label
327 @rtype EricAnimatedLabel 333 @rtype EricAnimatedLabel
328 """ 334 """
329 if index == -1: 335 if index == -1:
330 return None 336 return None
331 337
332 if hasattr(self.__tabBar, 'setTabButton'): 338 if hasattr(self.__tabBar, "setTabButton"):
333 side = self.__freeSide() 339 side = self.__freeSide()
334 animation = EricAnimatedLabel( 340 animation = EricAnimatedLabel(
335 self, animationFile=animationFile, interval=interval) 341 self, animationFile=animationFile, interval=interval
342 )
336 self.__tabBar.setTabButton(index, side, None) 343 self.__tabBar.setTabButton(index, side, None)
337 self.__tabBar.setTabButton(index, side, animation) 344 self.__tabBar.setTabButton(index, side, animation)
338 animation.start() 345 animation.start()
339 return animation 346 return animation
340 else: 347 else:
341 return None 348 return None
342 349
343 def resetAnimation(self, index): 350 def resetAnimation(self, index):
344 """ 351 """
345 Public slot to reset an animated icon. 352 Public slot to reset an animated icon.
346 353
347 @param index tab index (integer) 354 @param index tab index (integer)
348 """ 355 """
349 if index == -1: 356 if index == -1:
350 return 357 return
351 358
352 if hasattr(self.__tabBar, 'tabButton'): 359 if hasattr(self.__tabBar, "tabButton"):
353 side = self.__freeSide() 360 side = self.__freeSide()
354 animation = self.__tabBar.tabButton(index, side) 361 animation = self.__tabBar.tabButton(index, side)
355 if animation is not None: 362 if animation is not None:
356 animation.stop() 363 animation.stop()
357 self.__tabBar.setTabButton(index, side, None) 364 self.__tabBar.setTabButton(index, side, None)

eric ide

mercurial