2017-01-26 4 views
1

Ich arbeite an einer Python-Anwendung, die Daten aus einer großen Datei mit Datensätzen aus sehr vielen Quellen plottet. Eine der Optionen, die ich dem Benutzer geben möchte, ist die Option, nur für eine Teilmenge dieser Quellen zu plotten, wenn dies gewünscht wird. Ich bearbeite dies, indem ich zuerst die Dateien lese, herausfinde, wie viele eindeutige Dinge es gibt, und dann für jedes ein QCheckBox() erzeuge, das nach seiner Quelle benannt ist (jede Quelle hat einen eindeutigen Namen). In diesem speziellen Fall wird die Datendatei in ein riesiges Wörterbuch zerlegt, in dem die Schlüssel die eindeutige Quelle sind. Ich möchte für jedes Kontrollkästchen eine Verbindung mit dem stateChange() - Ereignis herstellen und dann das Plotten für diese Quelle deaktivieren, wenn das Kontrollkästchen deaktiviert ist. Das würde in diesem Fall die Quelle aus einer Liste von Quellen hinzufügen/entfernen, wenn die Box aktiviert/deaktiviert ist. Das Problem, auf das ich stoße, ist, dass alle meine Checkboxen sich mit der letzten Quelle in meiner Liste verbinden.Verbinden mit StateChange-Ereignissen für dynamisch erstellte QCheckBoxes

Anfangs sieht das Fenster, das erstellt wird, korrekt aus, jede Schaltfläche wird entsprechend benannt. Jedes Mal, wenn eine Schaltfläche gedrückt wird, soll btnstate() einfach den mit dieser Schaltfläche verknüpften Text drucken. Die Methode funktioniert, wenn Sie jede Schaltfläche explizit definieren können, wie die Optionsfelder im Beispiel zeigen. Wenn Sie auf eine der beiden Schaltflächen klicken, wird der korrekte Name der Schaltfläche angezeigt. Wenn Sie jedoch eines der Kontrollkästchen deaktivieren, wird btnnstate "test4" ausgeben.

Was mache ich falsch? Hier

ist der Code (Quellen geändert Dummy-Werte):

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

Zu viel Text, bitte versuchen Sie, Ihre Erklärung zu vereinfachen – eyllanesc

Antwort

0

Ich denke, der Grund, dass dies geschieht mit dem zu tun hat, wie Python bindet und entbindet Referenzen innerhalb der Schleife. Da temp bei jeder Iteration neu definiert wird, wird auch der Slot aktualisiert, so dass er für jede Schaltfläche effektiv das gleiche Lambda aufruft. Das ist eine Art Hand-wellig, weil mein Verständnis der Details der Python-Referenzierung nicht sehr tief ist. Aber ich weiß, dass die Python-Bindungen zu Qt viele Probleme mit Pythons Referenzierung und Speicherbereinigung haben, z. beim Löschen von Widgets, weil die Qt-Objekthierarchie nicht vollständig mit Python arbeitet.

Wie auch immer, praktisch, es gibt eine ziemlich einfache Lösung. Sie können die Methode functools.partial verwenden, um eine Teilfunktion als Slot und nicht als Lambda zu definieren. Binden Sie den Button als erstes Objekt und lassen Sie den Button-Status (als Signalargument ausgegeben) ungebunden. Wie so:

import functools 

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

Dann in der Schleife:

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

des Lauf, bekomme ich jetzt die richtigen Etiketten gedruckt, wenn jedes Feld Scheck/deaktiviert ist.

Edit:

Siehe this post für ein weiteres Beispiel Referenzzählung Python mit seltsamer Weise mit Qt-Objekthierarchie zu interagieren.

+0

Jedes 'Lambda' ist anders, aber alle sehen die gleiche 'Temp'-Variable, die einfach an das letzte in der Schleife erzeugte Kontrollkästchen gebunden ist. Eine äquivalente Lösung zu "partiell" ist das Zwischenspeichern der aktuellen Variablen als Standardargument: 'lambda state, temp = temp: btnstate (temp)'. Die Frage betrifft eigentlich Bereiche, nicht Müllsammlung, und es ist rein Python-bezogen. – ekhumoro

+0

@ekhumoro Ah, richtig, Scoping ist das Problem nicht GC. Danke, dass Sie darauf hingewiesen haben, es ist eine Weile her, seit ich über diese Art von Details nachgedacht habe. – bnaecker

Verwandte Themen