Search code examples
pythoncontextmenupyqt4qpushbutton

python pyqt4 contextMenu on multiple pushButtons


I'm trying to build off this example that I've came across from here: Right click contextMenu on QPushButton

How would I get this to work when I'm creating the button dynamically? I can't figure out a way to dynamically create the method on_context_menu.

Here is the code that I have so far.

import sys
from PyQt4 import QtGui,QtCore
import sip

class LayoutTest(QtGui.QWidget):
    def __init__(self):
        super(LayoutTest, self).__init__()
        self.setGeometry(300, 300, 400, 200)
        VmasterLayout = QtGui.QVBoxLayout(self)
        self.Hbox = QtGui.QHBoxLayout()

        for i in range(1,4):
            self.butVal = 'buttonMenu_%s' % i
            self.button = QtGui.QPushButton(self.butVal)
            self.button.clicked.connect(self.allCheckButton)
            self.button.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
            self.connect(self.button, QtCore.SIGNAL('customContextMenuRequested(const QPoint&)'), self.on_context_menu)

            # create context menu
            self.popMenu = QtGui.QMenu(self)
            action = QtGui.QActionGroup(self, exclusive=True)
            listVer = ['image_v001','image_v003','image_v012','image_v120','image_v140', 'image_v013']
            for i, vDir in enumerate(sorted(listVer)):
                x = action.addAction(QtGui.QAction( vDir, self, checkable = True))
                x.triggered.connect(self.foo(x.text())) 
                self.popMenu.addAction(x)
                self.popMenu.addSeparator()

            self.Hbox.addWidget(self.button)

        VmasterLayout.addLayout(self.Hbox)

    def on_context_menu(self, point):
        # show context menu
        self.popMenu.exec_(self.button.mapToGlobal(point)) 

    def foo(self, name):
        def poo():
            print 'Image version is: %s' % name
        return poo

    def allCheckButton(self):
        point = QtGui.QCursor.pos()
        print point

def run():
    app = QtGui.QApplication(sys.argv)
    ex = LayoutTest()
    ex.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    run()

Solution

  • I've rewrite this code: python pyqt4 contextMenu on pushButton.

    First of all, you need to be able to identify different buttons if you want to create them dynamically, so I've created my own QPushButton class - MyPushButton.

    After that I've moved on_context_menu in that class.

    And third important thing is that I've connected MainForm class and MyPushButton class with buttonXclickedSignal signal , that gives MainForm class information which button is clicked so you can handle button clicked signal in MainForm and do whatever you want for different buttons.

    Here is code (run it and try to click buttons and right click on buttons):

    import sys
    from PyQt4 import QtGui, QtCore
    
    class MyPushButton(QtGui.QPushButton):
        def __init__(self, popMenu,elementID, mainForm):
            super(MyPushButton, self).__init__()
            self.__elementID = elementID
            self.__mainForm = mainForm
            self.__popMenu = popMenu
    
            self.connect(self, QtCore.SIGNAL('customContextMenuRequested(const QPoint&)'), self.on_context_menu)   
            self.connect(self, QtCore.SIGNAL('clicked()'),  self,        QtCore.SLOT("triggerOutput()"))    
    
        def on_context_menu(self, point):
            # show context menu
            self.__popMenu.exec_(self.mapToGlobal(point)) 
    
        @QtCore.pyqtSlot()
        def triggerOutput(self):
            self.__mainForm.emit(QtCore.SIGNAL("buttonXclickedSignal(PyQt_PyObject)"), self.__elementID) # send signal to MainForm class
    
    
    class MainForm(QtGui.QWidget):
        def __init__(self, parent=None):
            super(MainForm, self).__init__(parent)
            self.setGeometry(300, 300, 400, 200)
            VmasterLayout = QtGui.QVBoxLayout(self)
            self.Hbox = QtGui.QHBoxLayout()
    
            # Custom signal
            self.connect(self, QtCore.SIGNAL("buttonXclickedSignal(PyQt_PyObject)"),         self.buttonXclicked)
    
            for i in range(1,4):
                # create context menu as you like
                popMenu = QtGui.QMenu(self)
                popMenu.addAction(QtGui.QAction('button %s - test0'%(i), self))
                popMenu.addAction(QtGui.QAction('button %s - test1'%(i), self))
                popMenu.addSeparator()
                popMenu.addAction(QtGui.QAction('button %s - test2'%(i), self))
    
                # create button
                self.button = MyPushButton(popMenu, i, self)   
                self.button.setText("test button %s" %(i))    
                self.button.resize(100, 30)
    
                # set button context menu policy
                self.button.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)        
    
                self.Hbox.addWidget(self.button)
    
            VmasterLayout.addLayout(self.Hbox)
    
        def buttonXclicked(self, buttonID):
            if buttonID == 1: 
                #do something , call some method ..
                print "button with ID ", buttonID, " is clicked"
            if buttonID == 2: 
                #do something , call some method ..
                print "button with ID ", buttonID, " is clicked"
            if buttonID == 3: 
                #do something , call some method ..
                print "button with ID ", buttonID, " is clicked"
    
    def main():
        app = QtGui.QApplication(sys.argv)
        form = MainForm()
        form.show()
        app.exec_()
    
    if __name__ == '__main__':
        main()