Lambda-Funktion als Slot

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
vorlautboy
User
Beiträge: 38
Registriert: Sonntag 7. Dezember 2008, 18:43

Hallo Leute!

Ich habe ein QMainWindow, darin ein QTableWidget mit mehreren Spalten. Der Benutzer soll nun über einen Dialog auswählen können welche Spalten angezeigt werden sollen. Dazu wird dem Dialog das QTableWidget übergeben, dass zu jeder Spalte eine QCheckBox erstellt. Der Dialog soll nun ein Live-Dialog sein, d.h. wenn sich der Status einer Checkbox ändert, soll die Spalte direkt angezeigt oder versteckt werden. Dazu verbinde ich QCheckBox.toggeled mit einem Slot und genau hier liegt das Problem. QTableWidget.horizontalHeader.setSectionHidden erwartet den entgegengesetzten Wahrheitswert, wie den, der von QCheckBox.toggled kommt. Deshalb wollte ich eine eine lambda-Funktion übegeben, die den Status invertiert, komischerweise werden jetzt alle Änderungen nur auf die letzte Spalte angewandt...
Wo liegt der Fehler?

Hier der Dialog:

Code: Alles auswählen

from PyQt4.QtGui import (QDialog, QVBoxLayout, QCheckBox,
                         QMainWindow, QTableWidget, QApplication)
from PyQt4.QtCore import Qt

class ColumnsDlg(QDialog):
    def __init__(self, tableWidget, parent=None):
        super(ColumnsDlg, self).__init__(parent)

        model = tableWidget.model()
        header = tableWidget.horizontalHeader()
        
        layout = QVBoxLayout()
        
        for i in range(model.columnCount()):            
            label = model.headerData(i, Qt.Horizontal).toString()
            shown = not header.isSectionHidden(i)
            cb = QCheckBox(label)
            cb.setChecked(shown)
            cb.stateChanged.connect(lambda shown: header.setSectionHidden(i, not shown))
            layout.addWidget(cb)

        self.setLayout(layout)
Und hier ein MainWindow, zum Ausprobieren:

Code: Alles auswählen

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

        self.table = QTableWidget()
        self.table.setColumnCount(3)
        self.table.setHorizontalHeaderLabels(["Name", "Adresse", "Telefon"])
        self.setCentralWidget(self.table)

        tableMenu = self.menuBar().addMenu("&Tabelle")
        columnsAction = tableMenu.addAction("S&palten")
        columnsAction.triggered.connect(self.showColumns)

        self.resize(400, 300)

    def showColumns(self):
        dlg = ColumnsDlg(self.table, self)
        dlg.show()


if __name__ == "__main__":
    import sys
    
    app = QApplication(sys.argv)
    mw = MainWindow()
    mw.show()
    sys.exit(app.exec_())
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Hallo.

Ja, das Verhalten von "lambda" ist etwas unerwartet. Am einfachst umgehst du es mit einer extra Methode, welche dir die entsprechende Funktion generiert:

Code: Alles auswählen

def generator_method(header, i):
    def function(shown):
        header.setSectionHidden(i, not shown)
    return function
Und dann entsprechend die Zeile 19:

Code: Alles auswählen

cb.stateChanged.connect(self.generator_method(header, i))
Alternativ zu der extra Methode kannst du den Parametern der lambda-Funktion auch einen Default-Wert geben. Dann sollte es auch funktionieren.

Interessant ist für dich vielleicht auf die "partial"-Funktion im [mod]functools[/mod]-Modul.
Das Leben ist wie ein Tennisball.
vorlautboy
User
Beiträge: 38
Registriert: Sonntag 7. Dezember 2008, 18:43

EyDu hat geschrieben:Interessant ist für dich vielleicht auf die "partial"-Funktion im [mod]functools[/mod]-Modul.
daran hatte ich auch gedacht, nur leider sind die Argumente von QCheckBox.toggled und QHeaderView.setSectionHidden entgegengesetzt verschieden. Kann ich die Argumente nicht an den Rückgabewert der lambda-Funktion binden, in etwa so?

Code: Alles auswählen

cb.stateChanged.connect(lambda shown: functools.partial(header.setSectionHidden, i, shown))
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Ich verstehe jetzt nicht wirklich worauf du mit deinem letzten Posting hinaus willst. Liegt ab vielleicht an den 2 Stunde Schlaf in den letzten 32 Stunden :roll: Wäre gut, wenn du das noch etwas präzisieren könntest. Hast du meinen ersten Vorschlag (den ohne functools.partial) mal ausprobiert? Und was genau funktioniert daran ggf. nicht?
Das Leben ist wie ein Tennisball.
vorlautboy
User
Beiträge: 38
Registriert: Sonntag 7. Dezember 2008, 18:43

EyDu hat geschrieben:Ich verstehe jetzt nicht wirklich worauf du mit deinem letzten Posting hinaus willst. Liegt ab vielleicht an den 2 Stunde Schlaf in den letzten 32 Stunden :roll: Wäre gut, wenn du das noch etwas präzisieren könntest. Hast du meinen ersten Vorschlag (den ohne functools.partial) mal ausprobiert? Und was genau funktioniert daran ggf. nicht?
klappt schon (sofern man der generator-methode als erstes argument self übergibt); hielt es nur für etwas umständlich dafür extra eine methode zu verweden, ich dachte eigentlich, dass lambdas genau dafür gedacht wären...

Und wie kommst du auf die 2 Studen Schlaf? :D
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Stimmt, das self hatte ich vergessen. Dann ist es eben eine Funktion ^^
vorlautboy hat geschrieben:Und wie kommst du auf die 2 Studen Schlaf? :D
Zwei Wörter: Abschlussarbeit, Montag ^^
Das Leben ist wie ein Tennisball.
Antworten