pythonpyqtqtoolbutton

QToolButton clicked signal is only called once


I have attached a QMenu to the QToolButton in which the Menu is not reflecting correctly.

I am populating my QMenu from reading a .txt file. The menu is showing the correct items on the first run, but if I made changes in the .txt file and re-clicked on the button, the QMenu is still showing the item on the first run.

Adding on, it seems that the clicked signal is only being called once?

class MyWin(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(MyWin, self).__init__()
        central_widget = QtGui.QWidget()
        self.setCentralWidget(central_widget)
        vlay = QtGui.QVBoxLayout(central_widget)
        hlay = QtGui.QHBoxLayout()
        vlay.addLayout(hlay)
        vlay.addStretch()

        self.add_button = QtGui.QToolButton()
        self.tab_bar = QtGui.QTabBar(self)
        self.add_button.setIcon(QtGui.QIcon('add.png'))
        self.add_button.clicked.connect(self.set_menu)

        #self.add_button.setMenu(self.set_menu())
        #self.add_button.setPopupMode(QtGui.QToolButton.InstantPopup)

        self.tab_bar.setTabButton(
            0,
            QtGui.QTabBar.ButtonPosition.RightSide,
            self.add_button
        )
        hlay.addWidget(self.add_button)
        hlay.addWidget(self.tab_bar)

    def set_menu(self):
        with open('/Desktop/item_file.txt') as f:
            menu_options = f.read().splitlines()
            print menu_options

        qmenu = QtGui.QMenu(self.add_button)
        for opt in menu_options:
            qmenu.addAction(opt, partial(self.set_new_tab, opt))

        self.add_button.setMenu(qmenu)
        self.add_button.setPopupMode(QtGui.QToolButton.InstantPopup)

    def set_new_tab(self, opt):
        self.tab_bar.addTab(opt)

Solution

  • When a Menu is established in the QPushButton, the mousePressEvent event no longer reaches the QPushButton but is intercepted by the QMenu so the clicked signal is not emited.

    One solution is to set a QMenu by default and use the aboutToShow signal to call set_menu that will add the QActions.

    On the other hand they are the same because clicked is not issued and therefore set_menu is not called, and even if you call set_menu as I have established it is better to reuse than create so in this case I eliminate the previous QActions with clear method.

    from functools import partial
    from PyQt4 import QtCore, QtGui
    
    class MyWin(QtGui.QMainWindow):
        def __init__(self, parent=None):
            super(MyWin, self).__init__()
            central_widget = QtGui.QWidget()
            self.setCentralWidget(central_widget)
            vlay = QtGui.QVBoxLayout(central_widget)
            hlay = QtGui.QHBoxLayout()
            vlay.addLayout(hlay)
            vlay.addStretch()
    
            self.add_button = QtGui.QToolButton()
            self.tab_bar = QtGui.QTabBar(self)
            self.add_button.setIcon(QtGui.QIcon('add.png'))
    
            self.qmenu = QtGui.QMenu(self.add_button)
            self.add_button.setMenu(self.qmenu)
            self.add_button.setPopupMode(QtGui.QToolButton.InstantPopup)
    
            self.qmenu.aboutToShow.connect(self.set_menu)
    
            self.tab_bar.setTabButton(
                0,
                QtGui.QTabBar.ButtonPosition.RightSide,
                self.add_button
            )
            hlay.addWidget(self.add_button)
            hlay.addWidget(self.tab_bar)
    
        @QtCore.pyqtSlot()
        def set_menu(self):
            with open('/Desktop/item_file.txt') as f:
                menu_options = f.read().splitlines()
                self.qmenu.clear()
                for opt in menu_options:
                    self.qmenu.addAction(opt, partial(self.set_new_tab, opt))
    
        def set_new_tab(self, opt):
            self.tab_bar.addTab(opt)
    
    if __name__ == '__main__':
        import sys
        app = QtGui.QApplication(sys.argv)
        w = MyWin()
        w.show()
        sys.exit(app.exec_())