2009-09-23 19 views
6

Necesito poder saber en qué elemento he hecho clic en un sistema de menú generado dinámicamente. Solo quiero saber en qué he hecho clic, incluso si es simplemente una representación de cadena.Pyqt - QMenu dinámicamente poblado y hecho clic

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 

Respuesta

11

En lugar de utilizar onFilmSet directamente como el receptor de la conexión, utilice una función lambda para que pueda pasar parámetros adicionales:

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

exactamente lo que estaba buscando, ahhh sweet lambda! – crackerbunny

1

Echa un vistazo a la propiedad del sistema de Qt. Puede agregar dinámicamente una propiedad que contenga una cadena o cualquier cosa que desee, lo que define la acción. Luego puede usar el método sender() en la ranura para obtener el QObject llamando al slot. Luego, consulta la propiedad que establezcas y haz lo que quieras en consecuencia.

Pero este no es el mejor método para hacerlo. No se recomienda usar remitente() porque viola el principio de modularidad orientado a objetos.

El mejor método sería utilizar la clase QSignalMapper. Esta clase asigna señales de diferentes objetos a la misma ranura con diferentes argumentos.

No he usado PyQt por lo tanto no puedo darle la sintaxis exacta o un ejemplo, pero no debería ser difícil de encontrar con un poco de investigación.

+0

Estoy de acuerdo en que este parece ser el lugar para usar un mapeador de señales. –

1

Estaba tratando de resolver un problema similar y después de mirar el código anterior, esto es lo que funcionó para mí. Pensé que sería bueno mostrarlo todo junto. =)

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 
1

Sólo un poco de información adicional, no sé por qué, pero la función lambda no funciona con la nueva sintaxis pyqt para las conexiones:

Ejemplo de código no funciona:

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) ) 

Pero si subsitute la última línea con la sintaxis de conexión viejo estilo:

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

Todo está bien. Con la nueva sintaxis de conexión, que sólo recibe el último elemento del bucle :(

+0

Para mí (Qt4) también funciona con la nueva sintaxis de conexión, mira mi comentario a esta respuesta: http://stackoverflow.com/a/1102089/3224008 – Leistungsabfall

1

encontré this respuesta aquí para hacer frente a esta cuestión en PyQt5, python3. No me gusta ella, la variable bVal para ser precisos , ya que no entiendo completamente pero tardó mucho tiempo para encontrar, así que pensé que me gustaría compartir aquí. el bVal recoge el valor booleano del desencadenada y permite que el TaskType que se pasa.

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 
+0

No necesita pasar 'bVal' a' setTask', por lo que puede deshacerse de ese feo 'ignore_bVal'. El único requisito es que inserte un argumento inicial en 'lambda' para que el argumento' taskType' no se sobrescriba con el valor booleano. Una solución un poco más robusta (y más general) sería reemplazar 'bVal' con' * args', que ignoraría * todos los valores * posibles enviados por la señal, y dejaría intactos los argumentos de la palabra clave. – ekhumoro

+0

Pude resolver un problema similar con Python3 y PyQt5 gracias a esta respuesta. Esta idea aplicada también se puede ver aquí: https: // github.com/muammar/mkchromecast/commit/719fde94e271f97a2047e84b56d7603a5d8cde09 – muammar