Question

I need to be able to know what item I've clicked in a dynamically generated menu system. I only want to know what I've clicked on, even if it's simply a string representation.

def populateShotInfoMenus(self):
    self.menuFilms = QMenu()
    films = self.getList()

    for film in films:
        menuItem_Film = self.menuFilms.addAction(film)
        self.connect(menuItem_Film, SIGNAL('triggered()'), self.onFilmSet)
        self.menuFilms.addAction(menuItem_Film)

def onFilmRightClick(self, value):
    self.menuFilms.exec_(self.group1_inputFilm.mapToGlobal(value))

def onFilmSet(self, value):
    print 'Menu Clicked ', value
Was it helpful?

Solution

Instead of using onFilmSet directly as the receiver of your connection, use a lambda function so you can pass additional parameters:

receiver = lambda film=film: self.onFilmSet(self, film)
self.connect(menuItem_Film, SIGNAL('triggered()'), receiver)

OTHER TIPS

Take a look at the Qt's property system. You can dynamically add a property containing a string or anything you desire, which defines the action. Then you can use sender() method in the slot to obtain the QObject calling the slot. Then, query the property you set and do whatever you want accordingly.

But, this is not the best method to do this. Using sender() is not advised because it violates the object oriented principle of modularity.

The best method would be using QSignalMapper class. This class maps signals from different objects to the same slot with different arguments.

I haven't used PyQt therefore i cannot give you exact syntax or an example, but it shouldn't be hard to find with a little research.

I was trying to figure out a similar issue and after looking at the code above this is what worked for me. Thought it would be good to show it all together. =)

self.taskMenu = QtGui.QMenu("Task")
self.tasks = self.getTasks() #FETCHES A LIST OF LIST
self.menuTasks = QtGui.QMenu()
for item in self.tasks:
     menuItem_Task = self.taskMenu.addAction(item[1]) 
     receiver = lambda taskType=item[0]: self.setTask(taskType)
     self.connect(menuItem_Task, QtCore.SIGNAL('triggered()'), receiver)
     self.taskMenu.addAction(menuItem_Task)

def setTask(self,taskType):
     print taskType

Just some additional information, I don't know why, but lambda function doesn't work with new pyqt syntax for connections :

Example of code not working :

    self.contextTreeMenuAssignTo = QtGui.QMenu(self)
    actionAssign = contextMenu.addMenu( self.contextTreeMenuAssignTo )
    actionAssign.setText("Assign to : ")
    for user in self.whoCanBeAssignated() :
        actionAssignTo = QtGui.QAction( user[0]  ,self)
        self.contextTreeMenuAssignTo.addAction( actionAssignTo )
        actionAssignTo.triggered.connect(  lambda userID = user[1] :  self.assignAllTo( userID )  )

But if you subsitute the last line with the old style connection syntax :

self.connect(actionAssignTo, QtCore.SIGNAL('triggered()'),  lambda userID = user[1] :  self.assignAllTo( userID )  )    

Everything is fine. With the new connection syntax, you only get the last element of the loop :(

I found this answer here for dealing with this issue in PyQt5, python3. I don't like it, the bVal variable to be precise, as I don't fully understand it but it took a long time to find so I thought I'd share it here. The bVal picks up the boolean value from triggered and allows the taskType to be passed.

self.taskMenu = QtGui.QMenu("Task")
self.tasks = self.getTasks() #FETCHES A LIST OF LIST
self.menuTasks = QtGui.QMenu()

for item in self.tasks:
   menuItem_Task = self.taskMenu.addAction(item[1]) 
   receiver = lambda: bVal, taskType=item: self.setTask(bVal, taskType)
   menuItem_Task.triggered.connect(receiver)
   self.taskMenu.addAction(menuItem_Task)

def setTask(self, ignore_bVal, taskType):
   print taskType
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top