J'essaie de connecter des emplacements avec des fonctions lambda, mais cela ne fonctionne pas comme je m'y attendais. Dans le code ci-dessous, je réussis à connecter correctement les deux premiers boutons. Pour les deux autres, que je connecte en boucle, cela ne va pas. Quelqu'un avant moi avait la même question (Qt - Connecter un slot avec un argument en utilisant lambda), mais cette solution ne fonctionne pas pour moi. Je regarde mon écran depuis une demi-heure, mais je n'arrive pas à comprendre en quoi mon code est différent.

class MainWindow(QtGui.QWidget):
    def __init__(self):
        super(QtGui.QWidget, self).__init__()

        main_layout = QtGui.QVBoxLayout(self)

        # Works:
        self.button_1 = QtGui.QPushButton('Button 1 manual', self)
        self.button_2 = QtGui.QPushButton('Button 2 manual', self)
        main_layout.addWidget(self.button_1)
        main_layout.addWidget(self.button_2)

        self.button_1.clicked.connect(lambda x:self.button_pushed(1))
        self.button_2.clicked.connect(lambda x:self.button_pushed(2))

        # Doesn't work:
        self.buttons = []
        for idx in [3, 4]:
            button = QtGui.QPushButton('Button {} auto'.format(idx), self)
            button.clicked.connect(lambda x=idx: self.button_pushed(x))
            self.buttons.append(button)
            main_layout.addWidget(button)


    def button_pushed(self, num):
        print 'Pushed button {}'.format(num)

En appuyant sur les deux premiers boutons, on obtient «Bouton poussé 1» et «Bouton poussé 2», les deux autres donnent «Bouton poussé faux», même si je m'attendais à 3 et 4.

Je n'ai pas non plus complètement compris le mécanisme lambda. Qu'est-ce qui se connecte exactement? Un pointeur vers une fonction générée par lambda (avec le paramètre substitué en) ou la fonction lambda est-elle évaluée chaque fois que le signal se déclenche?

10
zeus300 5 mars 2016 à 23:32

3 réponses

Meilleure réponse

Le signal QPushButton.clicked émet un argument qui indique l'état du bouton. Lorsque vous vous connectez à votre emplacement lambda, l'argument facultatif auquel vous affectez idx est écrasé par l'état du bouton.

Au lieu de cela, établissez votre connexion comme

button.clicked.connect(lambda state, x=idx: self.button_pushed(x))

De cette façon, l'état du bouton est ignoré et la valeur correcte est transmise à votre méthode.

34
three_pineapples 5 mars 2016 à 23:15

Il faut se méfier! Dès que vous connectez votre signal à un slot lambda avec une référence à vous-même, votre widget ne sera pas récupéré! C'est parce que lambda crée une fermeture avec encore une autre référence irrécupérable au widget.

Ainsi, self.someUIwidget.someSignal.connect(lambda p: self.someMethod(p)) est très mauvais :)

9
eyllanesc 18 févr. 2020 à 00:39

Je ne suis pas vraiment sûr de ce qui ne va pas avec votre utilisation de lambda ici non plus. Je pense que c'est parce que idx (votre index de boucle lorsque vous configurez les boutons automatiques) est hors de portée et ne contient plus la valeur appropriée.

Mais je ne pense pas que vous ayez besoin de le faire de cette façon. Il semble que la seule raison pour laquelle vous utilisez lambda est que vous puissiez passer un argument à button_pushed (), identifiant de quel bouton il s'agissait. Il y a une fonction sender() qui peut être appelée dans le slot button_pushed () qui identifie le bouton à l'origine du signal.

Voici un exemple qui, je pense, fait plus ou moins ce que vous visiez:

from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *

import sys

class MainWindow(QWidget):
    def __init__(self):
        super(QWidget, self).__init__()

        main_layout = QVBoxLayout(self)

        self.buttons = []

        # Works:
        self.button_1 = QPushButton('Button 1 manual', self)
        main_layout.addWidget(self.button_1)
        self.buttons.append(self.button_1)
        self.button_1.clicked.connect(self.button_pushed)

        self.button_2 = QPushButton('Button 2 manual', self)
        main_layout.addWidget(self.button_2)
        self.buttons.append(self.button_2)
        self.button_2.clicked.connect(self.button_pushed)

        # Doesn't work:
        for idx in [3, 4]:
            button = QPushButton('Button {} auto'.format(idx), self)
            button.clicked.connect(self.button_pushed)
            self.buttons.append(button)
            main_layout.addWidget(button)


    def button_pushed(self):
        print('Pushed button {}'.format(self.buttons.index(self.sender())+1))


app = QApplication(sys.argv)
w = MainWindow()
w.show()
sys.exit(app.exec_())
0
jfsturtz 5 mars 2016 à 21:58