I have a qtabwidget with one or more tabs in it at any given time. I would like the user to be able to rearrange the second through last tab in any order, but the first tab to remain at index 0.
From everything I've found, there is no way to enable setMovable independently for each tab.
The best workaround I've come up with thus far is to simply move the first tab back to position 0 if the user moves it (or another tab in front of it). This obviously isn't ideal, but it would be acceptable if it worked correctly... It works for a short while, however it occasionally crashed the application (It appears to happen when user drags a tab before it and holds the mouse there, so its in a constant loop of trying to rearrange and something low level crashes)
Any other suggestions on a feasible workaround (either to this method, or a similar widget where this capability would be easier to implement)? I figure its probably possible to re-class the QTabWidget in a way that it would ignore mouse drags on the first tab, but I'm not sure how I could go about preventing another tab from being moved before it...
The only way I've found, yet, to "pin" the first tab of a QTabWidget is by using a subclass of QTabBar. The global strategy consists to install an eventFilter
on the subclass ofQTabBar
and to conditionally block the MouseMove
events in order to:
The code below present a simple application to show how this can be done.
import sys
from PySide import QtGui, QtCore
class myQTabBar(QtGui.QTabBar):
def __init__(self, *args, **kargs):
super(myQTabBar, self).__init__(*args, **kargs)
self.setMovable(True)
self.installEventFilter(self)
def eventFilter(self, source, event):
if event.type() == QtCore.QEvent.Type.MouseMove:
if source.currentIndex() == 0: # Block MouseMove for first tab.
return True
else: # For remaining tabs:
# block MouseMove if the left edge of the moving tab goes
# farther to the left than the right edge of first tab.
moving_leftEdge = event.pos().x() - self.edge_offset
fixed_rightEdge = self.tabRect(0).width()
if moving_leftEdge < fixed_rightEdge:
return True
elif event.type() == QtCore.QEvent.Type.MouseButtonPress:
# Get mouse click horizontal position.
xclick = event.pos().x()
# Get the left edge horizontal position of the targeted tab.
xleft = self.tabRect(self.tabAt(event.pos())).x()
# Compute and store offset between mouse click horizontal
# position and the left edge of the targeted tab
self.edge_offset = xclick - xleft
return QtGui.QWidget.eventFilter(self, source, event)
class myQTabWidget(QtGui.QTabWidget):
def __init__(self, *args, **kargs):
super(myQTabWidget, self).__init__(*args, **kargs)
tab_bar = myQTabBar()
self.setTabBar(tab_bar)
self.addTab(QtGui.QWidget(), 'Tab1')
self.addTab(QtGui.QWidget(), 'Tab2')
self.addTab(QtGui.QWidget(), 'Tab3')
if __name__ == '__main__':
app = QtGui.QApplication(sys.argv)
instance1 = myQTabWidget()
instance1.show()
sys.exit(app.exec_())
Which results in: