Pythonischere API für PyQt

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
Antworten
flying sheep
User
Beiträge: 48
Registriert: Donnerstag 17. September 2009, 16:44
Kontaktdaten:

hi, ich wollte fragen, ob es irgend ein interesse gibt, eine art PyQt4Py (Pythonic Qt4 for Python) zu erstellen.

ich spreche davon, mehr mit __getitem__ usw. zuarbeiten, properties statt gettern und settern zu verwenden usw.:

Code: Alles auswählen

l = QGridLayout(self)
self.layout = l

w = QLabel(self)
w.text = "hello"
w.size = 600, 400

l[0,0] = w
print(l[0,0]) #gibt aus: QLabel("hello")
gäbe es eine möglichkeit ein modul zu erstellen, das auf PyQt4 aufsetzt und quasi dessen getter und setter maskiert und mit builtins ersetzt? damit dir(QWidget) auch nicht u.a. setParent oder sowas zurückliefert
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Braucht man da nicht einfach nur ein

Code: Alles auswählen

QLabel.text = property(QLabel.text, QLabel.setText)
Das scheint mir jetzt eher Monkey-Patch-Fleißarbeit zu sein und ich (sich nicht mit Qt und anderen GUI-Rahmenwerken unter Python auskennend) finde komisch, dass das nicht Teil von pyside (oder wie die Qt-Bindings gerade heißen) ist.

Möglicherweise kann man einfach nach "foo" und "setFoo" in allen Klassen suchen und das dann automatisiert ergänzen. Das sollte doch in 10 Zeilen oder so machbar sein. Hier -> http://www.pyside.org/home-binding/api-extractor/ schreiben die allerdings etwas von einem API-Extractor und vielleicht muss man den einfach schlau genug machen.

Stefan
lunar

Theoretisch ist es möglich, ein solches Modul zu entwickeln, da man über QMetaObject an alle Eigenschaften im Sinne von Qt herankommt, und daraus anschließend Eigenschaften im Sinne von Python machen könnte.

Praktisch ist das keine ganz so gute Idee, denn das führt zwangsläufig zu Namenskonflikten. In Qt haben Eigenschaften und Methoden unterschiedliche Namensräume. Eine von QObject abgeleitete Klasse kann also eine Qt-Eigenschaft namens "foo" und eine Methode namens "foo" haben.

In Python dagegen müssen sich Eigenschaften und Methoden einen Namensraum teilen, es kann nur ein einziges "foo" in einer Klasse geben, und das muss sich entscheiden, ob es Eigenschaft sein will oder Methode.

Das wäre nun alles kein Problem, wenn per definitionem eine Beziehung zwischen Getter und Eigenschaftsname bestehen würde. Die aber gibt es nicht, Getter können benannt werden, wie man lustig ist. Mehr noch, Getter können überladen werden.

In Qt kann es also eine Eigenschaft "foo", eine Methode "foo()" und eine Methode "foo(mit_argument)" geben. Würde man nun die Qt-Eigenschaft "foo" in eine Python-Eigenschaft "foo" umsetzen, fiele die zweite "foo"-Methode über Bord.

Aus diesem Grund verzichten sowohl PyQt als auch pyside auf diese Möglichkeit. Da zumindest PyQt4 aber eine "pyqtConfigure"-Methode bietet, mit der man viele Eigenschaften auf einmal setzen kann, und zudem die Konstruktoren der Objekte ebenfalls so überladen sind, dass man direkt Eigenschaften setzen kann, fällt das eigentlich nicht so ins Gewicht. Da gibt es wichtigeres (z.B. überflüssige Typen wie QString und QVariant transient zu machen).
flying sheep
User
Beiträge: 48
Registriert: Donnerstag 17. September 2009, 16:44
Kontaktdaten:

gemäß des kapselungsgeraffels von c++ und so sind doch die properties c++-versteckt, oder? d.h. hat man über python/sip eh keinen zugriff drauf. wenn man z.b. qwidget.width eingibt, ist das eindeutig die funtion width und nicht evtl. eine property.

das heißt, man müsste sip händisch so konfigurieren, dass sip nicht die methode width in den namespace packt, sondern daraus (und aus setWidth) eine property baut. das bedeutet zwar, dass man kein modul auf pyqt draufbauen kann, aber es bedeutet, dass man die devs von pyside / pyqt zu ner alternativen api überreden könnte (oder zur not forken :()
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

flying sheep hat geschrieben:(oder zur not forken :()
Meine Glaskugel sagt mir, dass es wohl tatsächlich auf Arbeit für dich hinauslaufen könnte, wenn das Vorhaben umgesetzt werden soll. :)
lunar

@flying sheep: Qt-Eigenschaften sind nicht „versteckt“, sondern über "QObject.property()" und "QObject.setProperty()" erreichbar. Mit „Kapselungsgeraffel“ hat das auch so ziemlich gar nichts zu tun …

Was den Rest angeht, so habe ich das Gefühl, dass Du meinen Beitrag überhaupt nicht gelesen hast. "QWidget.width" ist zwar eine Methode, aber eben nicht eindeutig. Es kann mehrere überladene Versionen dieser Methode geben. Diese überladenen Methoden kann man nicht mehr erreichen, wenn man pauschal jeden Getter in eine entsprechende Eigenschaft verwandelt.

Falls Du ein konstruiertes Beispiel sehen möchtest, so verweise ich auf meinen Beitrag in einem anderen Forum (dort ist die Syntaxhervorhebung schöner).
lunar

@mkesper: Das hat jetzt aber wenig mit dem Vorschlag des OP zu tun.
flying sheep
User
Beiträge: 48
Registriert: Donnerstag 17. September 2009, 16:44
Kontaktdaten:

außerdem kannte ich das schon. trotzdem danke, denn ich hab beim letzten lesen das hier übersehen und das ist mal echt eine erleichterung!

@lunar: du führst die diskussion öfter, oder? ^^
wegen der namensraumproblematik sei angemerkt, dass eine umsetzung in meinem sinne (mit python-properties) den spaß sowieso inkompatibel zum c++-qt4 macht, man also entsprechende methoden umbenennen könnte. soweit ich weiß ist mir eine solche aber auch noch nie untergekommen. könntest du ein beispiel nennen?

PS: im verinkten beitrag ist auch von qtruby die rede: wie löst das denn die namensraum-problematik? soweit ich weiß sind funktionen in ruby auch objekte.
lunar

@flying sheep: So einfach ist das auch nicht. Dieses „Umbenennen“ müsste schließlich automatisiert vonstatten gehen, ohne seinerseits neue Konflikte zu erzeugen. Davon abgesehen, müsste sowas wieder extra dokumentiert werden, da die C++-Dokumentation dann ja nicht mehr greift.

Ein Beispiel für Namenskonflikte zwischen Methoden und Eigenschaften kann ich Dir nicht nennen. Ich habe nicht danach gesucht. Die theoretische Möglichkeit reicht aber schon, damit man das berücksichtigen muss.

Möglich wäre das prinzipiell alles schon, nur eben doch mit verhältnismäßig viel Aufwand. Ob es das jetzt wert ist … ich persönlich finde das jetzt nicht so schlimm, aber ich bin möglicherweise auch zu stark von C++ beeinflusst.

Was Ruby angeht, so habe ich keine Ahnung. Ich kenne weder Ruby noch QtRuby.
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

lunar hat geschrieben:Was Ruby angeht, so habe ich keine Ahnung. Ich kenne weder Ruby noch QtRuby.
Die Doku zu QtRuby sagt, dass fooBar(), foo_bar(), setFooBar(), set_foo_bar() und foo_bar=() funktionieren. Statt CamelCase geht auch under_score. Statt C-style setter-Methode geht auch der Ruby-typische Setter. Da man in Ruby ja Argument-Klammern weglassen kann, hat man damit die typische Property-Notation fooBar und fooBar=.

Offenbar wird das Ruby-Binding genau wie PHP, C# und Perl aus einer Metabeschreibung generiert. Warum es für Python anders ist, habe ich nicht recherchiert. Wirklich zufriedenstellend finde ich das jedoch alles nicht, denn insbesondere wenn immer auf die C++-Dokumentation verwiesen wird, ist das kein UI-Rahmenwerk für die jeweilige Sprache sondern eines, wo man C++ mit etwas anderer Syntax benutzt.

Stefan
lunar

sma hat geschrieben:Die Doku zu QtRuby sagt, dass fooBar(), foo_bar(), setFooBar(), set_foo_bar() und foo_bar=() funktionieren. Statt CamelCase geht auch under_score. Statt C-style setter-Methode geht auch der Ruby-typische Setter. Da man in Ruby ja Argument-Klammern weglassen kann, hat man damit die typische Property-Notation fooBar und fooBar=.
Tja, "fooBar=()" gibt es nun eben nicht in Python. Da sind Eigenschaften und Methoden unterschiedliche Dinge, und können nicht denselben Namen tragen, was die an sich gute Idee des OP in der Implementierung recht kompliziert macht.
Wirklich zufriedenstellend finde ich das jedoch alles nicht, denn insbesondere wenn immer auf die C++-Dokumentation verwiesen wird, ist das kein UI-Rahmenwerk für die jeweilige Sprache sondern eines, wo man C++ mit etwas anderer Syntax benutzt.
Genau das tut man ja auch, ein natives Python-Rahmenwerk gibt es ja nicht. Es wäre auch schwer, ein in Größe und Qualität ebenbürtiges Rahmenwerk zu implementieren … es wäre auch ziemlich viel Aufwand, nur weil die Namensgebung und die Semantik von Qt-Eigenschaften nicht pythonisch sind :)
flying sheep
User
Beiträge: 48
Registriert: Donnerstag 17. September 2009, 16:44
Kontaktdaten:

uncool. man müsste halt recherchieren, ob es in qt4 getter mit optionalen argumenten gibt, was ich stark bezweifle und dann für jede funktion "qklasse.prop" , für die es eine setProp-funktion gibt eine python-property anfertigen.

was haltet ihr vom rest meines originalposts?
lunar

@flying sheep: Du meinst hinsichtlich der Verwendung eines Tupels als Ersatz für QSize und des Zugriffs auf "QGridLayout"?

Nun, ich würde sagen, auch wieder nicht weit genug gedacht. QSize verhält sich anders als ein Tupel (vgl. "QSize(100, 200) * 10" und "(100, 200) * 10"). QGridLayout.addWidget hat neben Zeile und Spalte noch ein Argument für die Ausrichtung, dass bei Deinem Ansatz über "__getitem__()" verloren geht. Man müsste eher "label.size = (100, 200, Qt.AlignCenter)" oder ähnlich verwenden.

Ein vollständiger Ersatz ist das also nicht …
flying sheep
User
Beiträge: 48
Registriert: Donnerstag 17. September 2009, 16:44
Kontaktdaten:

aber ein einfacher. wenn man nur zwei argumente verwendet, funktioniert es auch (und ich benutze meist nur die beiden) ansonsten halt mehr tupelelemente, wie du vorgeschlagen hast

qsize sollte auch __getitem__ unterstützen, um auf x und y zuzugreifen. multiassignment, unpacking und so.
flying sheep
User
Beiträge: 48
Registriert: Donnerstag 17. September 2009, 16:44
Kontaktdaten:

na, wenn ich sogar dich überzeugt hab, ist es ja ein klacks, das dem haupt-pyqt-entwickler schmackhaft zu machen -.-
flying sheep
User
Beiträge: 48
Registriert: Donnerstag 17. September 2009, 16:44
Kontaktdaten:

ich muss das mal nochmal aufwärmen:
qyoto (qt-bindings für .net) wird automatisch erstellt. dort ist es möglich, properties rubymäßig per qwidget.property=5 zu setzen. d.h. muss es eine methode geben, properties eindeutig zu bestimmen. nun müsste man nur noch das tun, die getter umbenennen und mit einer python-property verknüpfen, fertig ist die laube.
Antworten