eric6/E5Gui/E5TabWidget.py

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

eric ide

mercurial