Seite 1 von 1

Lambda-Funktion als Slot

Verfasst: Freitag 11. September 2009, 10:56
von vorlautboy
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_())

Verfasst: Freitag 11. September 2009, 12:13
von EyDu
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.

Verfasst: Freitag 11. September 2009, 13:15
von vorlautboy
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))

Verfasst: Freitag 11. September 2009, 14:16
von EyDu
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?

Verfasst: Freitag 11. September 2009, 14:27
von vorlautboy
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

Verfasst: Freitag 11. September 2009, 14:39
von EyDu
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 ^^