5

I want to detect middle mouse clicks on a QTabWidget. I was expecting there to be a mouse event related signal on QWidget, but all I am seeing are methods.

Do I need to subclass the QTabWidget and then override said methods in order to do what I want, or am I missing something?

Daniel Hedberg
  • 5,677
  • 4
  • 36
  • 61
Mathieson
  • 1,194
  • 2
  • 13
  • 28

1 Answers1

9

You can either install an event filter on the QTabBar (returned by QTabWidget.tabBar()) to receive and handle press and release events, or subclass QTabBar to redefine mousePressEvent and mouseReleaseEvent and replace the QTabBar of the QTabWidget with QTabWidget.setTabBar().

  1. Example using the event filter:

    class MainWindow(QMainWindow):
        def __init__(self):
            super(QMainWindow,self).__init__()
            self.tabWidget = QTabWidget(self)
            self.setCentralWidget(self.tabWidget)
            self.tabWidget.tabBar().installEventFilter(self)
            self.tabWidget.tabBar().previousMiddleIndex = -1           
    
        def eventFilter(self, object, event):
            if object == self.tabWidget.tabBar() and \
                event.type() in [QEvent.MouseButtonPress, 
                                 QEvent.MouseButtonRelease] and \
                event.button() == Qt.MidButton: 
                tabIndex = object.tabAt(event.pos())
                if event.type() == QEvent.MouseButtonPress:
                    object.previousMiddleIndex = tabIndex
                else:   
                    if tabIndex != -1 and tabIndex == object.previousMiddleIndex:
                        self.onTabMiddleClick(tabIndex)                    
                    object.previousMiddleIndex = -1                        
                return True               
            return False
    
        # function called with the index of the clicked Tab
        def onTabMiddleClick(self, index):
            pass
    
  2. Example using a QTabBar subclass:

    class TabBar(QTabBar):
        middleClicked = pyqtSignal(int)
    
        def __init__(self):
            super(QTabBar, self).__init__()
            self.previousMiddleIndex = -1
    
        def mousePressEvent(self, mouseEvent):
            if mouseEvent.button() == Qt.MidButton:
                self.previousIndex = self.tabAt(mouseEvent.pos())
            QTabBar.mousePressEvent(self, mouseEvent)
    
        def mouseReleaseEvent(self, mouseEvent):
            if mouseEvent.button() == Qt.MidButton and \
                self.previousIndex == self.tabAt(mouseEvent.pos()):
                self.middleClicked.emit(self.previousIndex)
            self.previousIndex = -1
            QTabBar.mouseReleaseEvent(self, mouseEvent)
    
    
    class MainWindow(QMainWindow):
        def __init__(self):
            super(QMainWindow,self).__init__()
            self.tabWidget = QTabWidget(self)
            self.setCentralWidget(self.tabWidget)
    
            self.tabBar = TabBar()
            self.tabWidget.setTabBar(self.tabBar)
            self.tabBar.middleClicked.connect(self.onTabMiddleClick)
    
        # function called with the index of the clicked Tab
        def onTabMiddleClick(self, index):
            pass
    

(In case you wonder why there is so much code for such a simple task, a click is defined as a press event followed by a release event at roughly the same spot, so the index of the pressed tab has to be the same as the released tab).

alexisdm
  • 29,448
  • 6
  • 64
  • 99
  • +1 for showing how to actually replicate a click signal with the press and release. If you weren't using the old and new tab index to determine if the click is valid, the general approach is also to use the manhatten length of the old and new point to determine if its within an acceptable range to call it a click: http://www.riverbankcomputing.co.uk/static/Docs/PyQt4/html/qpoint.html#manhattanLength – jdi Feb 25 '12 at 17:36
  • Exactly the kind of answer I was hoping for. Wasn't aware of the event filter. Thanks for giving such a detailed and well explained response. – Mathieson Feb 25 '12 at 22:00
  • @jdi: Thanks for the tip! Still figuring out how things work around here. Wouldn't have known to do that otherwise. – Mathieson Feb 29 '12 at 22:54
  • `if object == self.tabWidget.tabBar() ` ... should that really have parens on it? – Daniel Jun 24 '12 at 14:40
  • @dmd yes, it should. `tabBar()` is a function call. – alexisdm Jun 24 '12 at 20:43