Question

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()
Was it helpful?

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()
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top