QtCore.QObject.connect в цикле влияет только на последний экземпляр

У меня есть петля. Я создал QCheckBox и поместил его в ячейку QTableWidget, и все в порядке. На каждом шаге цикла я вызывал функцию connect для слота SLOT, но применяется только последний экземпляр QCheckBox. Я много гуглил и обнаружил, что у многих людей есть моя проблема. Я применил их решения, но моя проблема остается.

for row in xrange(len(uniqueFields)):
    instance = QtGui.QCheckBox(uniqueFields[row], findInstance.tableWidget)
    print QtCore.QObject.connect(instance,
        QtCore.SIGNAL(_fromUtf8("stateChanged (int)")),
        lambda: findInstance.projectsInstance.myslot(
                    "TWCH", findInstance, instance.text(),
                    instance.checkState(), instance))
    findInstance.tableWidget.setRowCount(findInstance.tableWidget.rowCount() + 1)
    findInstance.tableWidget.setCellWidget(row, 0, instance)

Примечание. моя функция connect возвращает True.

Как создать функцию connect в цикле, который перечисляет все instances?


person zoyak    schedule 22.10.2013    source источник


Ответы (3)


У меня такая же проблема, вы должны использовать functools.partial, например:

for key, val in a_DICT_THAT_YOU_STORED_YOUR_OBJECTS_AND_STRINGS:
    obj = partial(   findInstance.projectsInstance.myslot,arg1="TWCH",arg2=self,arg3=key,arg4=val.checkState() )
    QtCore.QObject.connect(val, QtCore.SIGNAL(_fromUtf8("stateChanged (int)")), obj)

Конечно, argX должен установить ваше реальное имя вашего аргумента имени вашей функции.

person PersianGulf    schedule 29.10.2013

Поместите переменную цикла в аргумент по умолчанию, например:

lambda state, instance=instance: findInstance.projectsInstance.myslot(
    "TWCH", findInstance, instance.text(), instance.checkState(), instance)

Это даст каждому lambda собственную локальную копию переменной instance.

ИЗМЕНИТЬ

Вот простой скрипт, который демонстрирует, как использовать лямбда-аргументы по умолчанию:

from PyQt4 import QtGui

class Window(QtGui.QWidget):
    def __init__(self):
        QtGui.QWidget.__init__(self)
        layout = QtGui.QVBoxLayout(self)
        for index in range(4):
            instance = QtGui.QCheckBox('Checkbox(%d)' % index, self)
            instance.stateChanged.connect(
                lambda state, instance=instance:
                    self.mySlot(instance.text()))
            layout.addWidget(instance)

    def mySlot(self, text):
        print('clicked: %s' % text)


if __name__ == '__main__':

    import sys
    app = QtGui.QApplication(sys.argv)
    window = Window()
    window.show()
    sys.exit(app.exec_())
person ekhumoro    schedule 22.10.2013
comment
Это не работает, потому что при испускании сигнала в слот не передаются никакие аргументы. Вам нужно создать лямбду без аргументов, но у которой есть собственная копия экземпляра. Отсюда мой ответ, где вы используете функцию для создания лямбда - person three_pineapples; 23.10.2013
comment
@three_pineapples. Он отлично работает: рабочий пример см. в моем обновленном ответе. Как я объяснил в своем первоначальном ответе, аргумент по умолчанию используется для кэширования переменной цикла, поэтому нет необходимости использовать замыкание (хотя это тоже сработает). - person ekhumoro; 23.10.2013
comment
Ах, извините, я пропустил часть instance=instance (я только что прочитал instance). Ваш метод намного чище, чем мое предложение, и я начну использовать его в своем собственном коде. - person three_pineapples; 23.10.2013

Проблема в том, что вы создаете функцию, используя lambda, где некоторые переменные внутри функции не передаются в качестве аргументов функции. Когда лямбда-функция выполняется, когда испускается сигнал, она использует значение этих переменных (например, instance) в этот момент времени. Чтобы было ясно, каждая создаваемая вами лямбда-функция использует значение instance во время выполнения, а не определяет время. Таким образом, instance содержит только ссылку на объект, использованный в последней итерации нашего цикла, что объясняет поведение, которое вы видите.

Некоторую полезную информацию можно найти здесь (также читайте комментарии) http://eli.thegreenplace.net/2011/04/25/passing-extra-arguments-to-pyqt-slot/

Из комментариев по ссылке выше:

Что вы можете сделать, так это заставить другую функцию генерировать лямбду, то есть что-то вроде:

def make_callback(param):   
        return lambda: self.on_button(param)

И в связи звоните make_callback(i). Затем для каждой итерации создается другая лямбда.

Таким образом, вы хотели бы обобщить это и передать такие вещи, как instance, в функцию make_callback, а затем поместить свое определение lambda в функцию make_callback. Я бы привел наглядный пример этого, но, как говорится в другом ответе, ваше форматирование, похоже, очень запуталось в вашем вопросе, и я, вероятно, ошибусь для вашего конкретного приложения. Если вы не понимаете, что я сказал, сделайте код в своем вопросе более понятным, и я попробую создать пример!

person three_pineapples    schedule 22.10.2013