2017-01-26 2 views
1

Я работаю над приложением Python, которое отображает данные из большого файла, содержащего записи из лотов и множества источников. Один из вариантов, которые я пытаюсь дать пользователю, - это возможность только строить график для подмножества этих источников, если это необходимо. Я делаю это, сначала прочитав файлы, узнав, сколько уникальных вещей есть, а затем создаю QCheckBox() для каждого, названного в честь его источника (каждый источник имеет уникальное имя). В этом конкретном случае файл данных анализируется в гигантский словарь, где ключи являются уникальным источником. Я хочу подключиться к событию stateChange() для каждого флажка, а затем отключить построение графика для этого источника, когда поле не отмечено. Который в этом случае будет добавлять/удалять источник из списка источников, когда флажок установлен или не установлен. Проблема, с которой я сталкиваюсь, заключается в том, что все мои флажки заканчиваются подключением к конечному источнику в моем списке.подключение к событиям stateChange для динамически созданных QCheckBoxes

Первоначально созданное окно выглядит правильно, каждая кнопка называется соответствующим образом. Каждый раз, когда нажимается кнопка, btnstate() должен просто печатать текст, связанный с этой кнопкой. Этот метод работает, если вы можете явно определить каждую кнопку, как показано на кнопках радиоприемника в примере. Если вы нажмете либо, вы получите правильное имя кнопки, напечатанной, но при снятии/перепроверке ЛЮБОЙ из флажков, btnstate печатает «test4».

Что я делаю неправильно?

Вот код (источники изменены фиктивных значения):

import sys 
from PyQt4.QtGui import * 

def btnstate(b): 
    print b.text() 

def main(): 
    app = QApplication([]) 
    widget = QWidget() 
    layout = QVBoxLayout() 
    widget.setLayout(layout) 
    radio_layout = QHBoxLayout() 
    checkbox_layout = QHBoxLayout() 

    #setup radio buttons for config pop-up window 
    r1 = QRadioButton("Page Count") 
    r2 = QRadioButton("Date") 
    r1.toggled.connect(lambda:btnstate(r1)) 
    r2.toggled.connect(lambda:btnstate(r2)) 
    radio_layout.addWidget(r1) 
    radio_layout.addWidget(r2) 

    cbs = [] 
    for idx, serial in enumerate(["test1", "test2", "test3", "test4"]): 
     temp = QCheckBox(serial) 
     temp.setText(serial) 
     temp.setChecked(True) 
     checkbox_layout.addWidget(temp) 
     temp.stateChanged.connect(lambda:btnstate(temp)) 
     cbs.append(temp) 

    layout.addLayout(radio_layout) 
    layout.addLayout(checkbox_layout) 
    widget.show() 
    sys.exit(app.exec_()) 


if __name__ == '__main__': 
    main() 
+0

Слишком много текста, попробуйте упростить свое объяснение – eyllanesc

ответ

0

Я думаю, что причина, по которой это происходит, связаны с тем, как Python связывает и развязывает ссылки внутри цикла. Поскольку temp переопределяется на каждой итерации, слот также обновляется, так что он эффективно вызывает одну и ту же лямбду для каждой кнопки. Это своего рода ручная волна, потому что мое понимание деталей ссылки на Python не очень глубокое. Но я знаю, что привязки Python к Qt имеют множество проблем с набором ссылок Python и сборкой мусора, например. при удалении виджетов, поскольку иерархия объектов Qt не полностью работает с Python.

В любом случае, более практично, есть довольно легкое исправление. Вы можете использовать метод functools.partial для определения частичной функции как слота, а не лямбда. Привяжите кнопку в качестве первого объекта, оставив состояние кнопки (излучаемое как аргумент сигнала) несвязанным. Как так:

import functools 

def btnstate(button, state): 
    print button.text() 

Затем в цикле:

for idx, serial in enumerate(['test1', 'test2', 'test3', 'test4']): 
    temp = QCheckBox(serial) 
    checkbox_layout.addWidget(temp) 
    temp.stateChanged.connect(functools.partial(btnstate, serial)) 

Запуск этого, я теперь получить правильные этикетки напечатаны, когда каждая коробка чек/снят.

Edit:

См this post другой пример подсчета ссылок Пайтона взаимодействующего со странными способами с иерархии объектов Qt.

+0

Каждая «лямба» отличается, но все они видят одну и ту же переменную temp, которая просто привязана к последнему флажку, созданному в цикле. Эквивалентное решение 'partial' заключается в кэшировании текущей переменной в качестве аргумента по умолчанию:' lambda state, temp = temp: btnstate (temp) '. Вопрос в том, что касается областей, а не сбор мусора, и это связано исключительно с Python. – ekhumoro

+0

@ekhumoro Ах, право, обзор - вопрос не GC. Спасибо, что указали это, прошло некоторое время с тех пор, как я подумал об этой детали. – bnaecker

Смежные вопросы