Liste von Widgets, wie zu einem Slot connecten?

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Hallo mal wieder!

Angenommen ist habe eine Reihe von Eingabeelementen gleichen Typs (in meinem Falle SpinBoxen). Bei diesen möchte ich ein bestimmtes Signal mit einem Slot verbinden, dass bei einer Änderung auftritt und den aktuellen Wert beinhaltet.

Im einfachsten Falle würde ich eben x-Widgets erstellten und dazu x-Slots, die ich dann mit x-connects verbinden. Das ist natürlich unschön - auch wenn es bei 8 Spinboxes bei mir theoretisch noch machbar wäre.

Also muss das doch auch anders gehen! Das Erzeugen der Widgets ist mittels Schleife ja kein Problem. Aber ich möchte natürlich auch nur einen Slot erstellen, der entsprechend mit den Änderungs-Signalen verbunden ist und dann das Model enstprechend anpasst.

Aber wie kann ich nun das Signal so verändern, dass ich diesem einen neuen Parameter mitgeben kann, der einen Index oder einen anderen Parameter enthält, so daß ich in dem Slot weiß, welches Element verändert wurde? Es gibt ja die QButtonGroup, bei der das letztlich exakt so ist, wie ich mir das denke. Dort wird einfach der Index des gedrückten Buttons mit übergeben. Bei mir wäre es eben "neuer Wert" und "Index" o.ä.

Als dirty Workaround fiele mir noch ein, den / die Datenparameter des Signals des Widgets im Slot einfach zu ignorieren und per Hand alle Werte aus den in Frage kommenden Widgets auszulesen. Bei einer kleinen Anzahl wäre das ja durchaus ok, aber sauberer ist es ja schon, den Wert zu erhalten und eben dazu die Info, welches Element diesen Wert nun hat.

Ich bilde mir ein, dass es mal in der Qt-Doku dazu ein Beispiel gab, aber ich finde es einfach nicht wieder.

Ich hoffe auch hier kann mir jemand nen Denkanstoß oder Tipp geben.
franzf
User
Beiträge: 78
Registriert: Samstag 29. August 2009, 10:21

Die Lösung die Qt hier für dich bereit hält, ist der QSignalMapper. Schau dir in der Doku dazu einfach das Beispiel an.

Grüße
Franz
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Danke für den Hinweis. Allerdings habe ich da noch ein Problem. Eine SpinBox emitted ja das Signal "valueChanged(int i)". Der Mapper hat aber nur die beiden Slots map() und map(QObject). Wie kann ich denn das verbinden?

Ich habe mir mal ein Minimalbeispiel geschrieben:
http://paste.pocoo.org/show/144362/
Das funzt so halt nicht.

Vielleicht hilft das weiter, mein Problem zu verstehen.
franzf
User
Beiträge: 78
Registriert: Samstag 29. August 2009, 10:21

Das mit den connects ist manchmal schön verfuchst. Dazu kommen dann gleichnamige SIGNALS (oder SLOTS) mit unterschiedlichen Typen in C++.

Um dein Beispiel zum Arbeiten zu bringen, mussten beide geändert werden.
Zusätzlich dazu hab ich nicht auf den QString gemapped, sondern direkt auf das spin, dann hast du im SLOT gleich direkten Zugriff auf das value. Außerdem hab ich dem Objekt seinen objectName gesetzt, damit auch daran erkannt werden kann, welches Objekt das jetzt ist.

So schauts bei mir jetzt aus:

Code: Alles auswählen

import sys
from PyQt4 import QtGui, uic, QtCore

EIGS = ["MU", "KL", "IN"]

class Demo(QtGui.QWidget):
    
    def __init__(self):
        QtGui.QWidget.__init__(self)
        self.signal_map = QtCore.QSignalMapper(self)
        form = QtGui.QFormLayout(self)
        for tag in EIGS:
            spin = QtGui.QSpinBox(self)
            spin.setObjectName(tag)
            form.addRow(tag, spin)
            self.signal_map.setMapping(spin, spin)
            self.connect(spin, QtCore.SIGNAL('valueChanged(int)'), self.signal_map, QtCore.SLOT('map()'))
        self.connect(self.signal_map, QtCore.SIGNAL('mapped(QWidget*)'), self.slot_show)

    def slot_show(self, w):
        print w.objectName(), w.value()


def main():
    app = QtGui.QApplication(sys.argv)
    widget = Demo()
    widget.show()
    sys.exit(app.exec_())


if __name__ == "__main__":
    main()
lunar

franzf hat geschrieben:Die Lösung die Qt hier für dich bereit hält, ist der QSignalMapper. Schau dir in der Doku dazu einfach das Beispiel an.
Die Lösung, die Python dafür bereit hält, ist "functools.partial()":

Code: Alles auswählen

class Demo(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        form = QFormLayout(self)
        for tag in EIGS:
            spin = SpinBox(self)
            form.addRow(tag, spin)
            spin.valueChanged[int].connect(partial(self.slot_show, spin, tag)))

    def slot_show(self, spin, tag, value):
          print('Spinbox {0!r} with {1} changed to {2}'.format(spin, tag, value))
Das ist doch viel hübscher. Man kann nicht nur beliebig viele Argumente an den Slot weiterreichen, sondern auch die Argumente des Signals noch nutzen.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

@franzf: Argh... ich hatte das "map" tatsächlich so übersehen... dabei hat's mein Editor sogar per Syntax Highlughting sichtbar gemacht. An manchen tagen weiß man es einfach nicht .... :-D

@Lunar: Jo, das ist hübsch und kompakt! Vielen Dank dafür :-)
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Jetzt habe ich doch noch mal eine Verständnisfrage zu folgender Zeile:
lunar hat geschrieben:

Code: Alles auswählen

class Demo(QWidget):
            spin.valueChanged[int].connect(partial(self.slot_show, spin, tag))
was ist "valueChanegd[int]" für ein Ausdruck? mit [] greife ich doch normalerweise auf einen Index oder einen Key in einem Dict zu. Aber int ist doch eine built-in-Funktion? Ich kapiere dieses Konstrukt einfach nicht. Oder ist der Slot von spin in Wirklichkeit ein dict und nutzt als Key die Signatur der int() Funktion?
Benutzeravatar
str1442
User
Beiträge: 520
Registriert: Samstag 31. Mai 2008, 21:13

(Ich habe von Qt keine Ahnung, aber) [Nn]ormalerweise, und insbesondere, sollte das ein Dictlike sein, ist das ein Zugriff auf __setitem__ mit dem Typ / der Klasse "int" (ist eine Klasse, keine Funktion) - was auch kein Problem darstellt:

Code: Alles auswählen

>>> a = {}
>>> a[int] = 42
>>> print a
{<type 'int'>: 42}
>>> print list(a.keys())[0]()
0
>>> print list(a.keys())[0]() + 30
30
>>> class A(object):
...  pass
... 
>>> a[A] = A()
>>> print a
{<type 'int'>: 42, <class '__main__.A'>: <__main__.A object at 0xb7dc342c>}
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Hyperion hat geschrieben:was ist "valueChanegd[int]" für ein Ausdruck? mit [] greife ich doch normalerweise auf einen Index oder einen Key in einem Dict zu. Aber int ist doch eine built-in-Funktion? Ich kapiere dieses Konstrukt einfach nicht.
Ja und?

Code: Alles auswählen

d = {
    str : "foo"
    int : 3
    unicode : u'bar'
    float : 42.0
}

print d[int]
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
derdon
User
Beiträge: 1316
Registriert: Freitag 24. Oktober 2008, 14:32

Code: Alles auswählen

>>> hash(list())
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: unhashable type: 'list'
>>> hash(dict())
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: unhashable type: 'dict'
>>> hash(set())
Traceback (most recent call last):
  File "<input>", line 1, in <module>
TypeError: unhashable type: 'set'
>>> hash(int()), hash(float()), hash(str()), hash(unicode()), hash(frozenset())
(0, 0, 0, 0, -32682612)
Siehe hashable
Leonidas
Python-Forum Veteran
Beiträge: 16025
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Und was hättest du erwartet? Es sind nur die Hashwerte von immutablen Objekten definiert, der Rest kann alles mögliche zurückgeben.
My god, it's full of CARs! | Leonidasvoice vs (former) Modvoice
lunar

@Hyperion: Dokumentation lesen!

Dahinter steckt auch keine tiefe Magie. Der Index-Operator [] lässt sich mit __setitem__ und __getitem__ überladen. Bei Wörterbüchern ist der Operator eben so implementiert, dass er einen Wert zu einem Schlüssel liefert. Bei "bound signals" in PyQt4 dagegen ist er so implementiert, dass er das passende Signal für die übergebene Signatur auswählt.

@derdon und Rest: Mit Hashing hat das nun gar nichts zu tun. __setitem__ kann man beliebig implementieren, auch ohne sich überhaupt um den Hash zu kümmern.
franzf
User
Beiträge: 78
Registriert: Samstag 29. August 2009, 10:21

lunar hat geschrieben:Das ist doch viel hübscher. Man kann nicht nur beliebig viele Argumente an den Slot weiterreichen, sondern auch die Argumente des Signals noch nutzen.
Ui, sehr doll! Python hat ja richtig charme :)
Thx!
Antworten