PyQt: Layout in Layout produziert Fehler

Python und das Qt-Toolkit, erstellen von GUIs mittels des Qt-Designers.
hurz
User
Beiträge: 12
Registriert: Mittwoch 11. März 2009, 23:47

Donnerstag 12. März 2009, 00:16

Hallo zusammen!

Folgendes Problem tritt bei mir auf (Python 2.54, passendes PyQT-Paket):

Ich habe dieses Eingabeformular (für eine Filmdatenbank) im Qt-Designer gestaltet:

Bild

Dem Galileo-Openbook zu Python 2.5 folgend habe ich die gespeicherte *.ui-Datei in eine *.py-Datei umgewandelt und nach Vorschrift eingebunden.

Der folgende Fehler erscheint beim Ausführen des Python-Scripts:

Code: Alles auswählen

  File "C:\Daten\programmieren\python25\filme\eingabe.py", line 127, in setupUi
    self.formLayout.addLayout(self.horizontalLayout, 7, 1, 1, 1)
AttributeError: addLayout
Klingt für mich, als könne man Layouts nicht ineinander verschachteln. :shock: :?
Das kann ich mir eigentlich nicht vorstellen...

Jedenfalls habe ich mit dem Qt-Designer dieses Formular erstellt und am resultierenden Python-Code nichts geändert. Da sollte das doch dann eigentlich fehlerfrei laufen...?

Ich hoffe, ihr könnt mir helfen!
Benutzeravatar
cofi
Moderator
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Donnerstag 12. März 2009, 07:15

addLayout gibt es nur bei BoxLayouts -> QHBoxLayout, QVBoxLayout

Dazu kommt: Schmeiss das Buch weg oder vergiss ganz schnell den objektorientierten Teil.

Der Designer ist für Qt geschrieben, PyQt implementiert Qt aber nicht komplett, so hat beispielsweise das QGridLayout keine addLayout Methode. ;)
Ich persönlich kann dem Grid-Layout aber sowieso nicht besonders viel abgewinnen, schau dir mal QFormLayout an, das ist genau für eine Zwecke gemacht und kommt mit Unterlayouts klar.

http://www.riverbankcomputing.co.uk/sta ... asses.html
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Donnerstag 12. März 2009, 10:02

Und außerdem generiert man aus UI-Dateien eigentlich keine PY-Dateien mehr, sondern bindet sie direkt im Code ein, ohne Codegenerierung.
My god, it's full of CARs! | Leonidasvoice vs Modvoice
hurz
User
Beiträge: 12
Registriert: Mittwoch 11. März 2009, 23:47

Donnerstag 12. März 2009, 10:10

cofi hat geschrieben:Dazu kommt: Schmeiss das Buch weg oder vergiss ganz schnell den objektorientierten Teil.
Ich kenne Objektorientierung aus Java und C++, da hats mir auch erstmal den Kiefer runtergeklappt, als ich das im Galileobuch gelesen habe. Kennst du denn andere gute Quellen, um mir einen vernünftigen Python-Stil anzueignen? Ich hab auch hier im Forum schon bisschen durchgeschaut, irgendwie scheint es nichts zu geben, was wirklich empfohlen werden kann...?
Der Designer ist für Qt geschrieben, PyQt implementiert Qt aber nicht komplett, so hat beispielsweise das QGridLayout keine addLayout Methode. ;)

Ich persönlich kann dem Grid-Layout aber sowieso nicht besonders viel abgewinnen, schau dir mal QFormLayout an, das ist genau für eine Zwecke gemacht und kommt mit Unterlayouts klar.
Vielleicht sollte man das dem Konvertierprogramm mal mitteilen... :)
Ich hatte schon das QFormLayout verwendet, aber auch das hat keine addLayout-Methode, sondern eine setLayout-Methode. (geistreich... ;) )

Ist es dann empfehlenswert, den QtDesigner in die Tonne zu kloppen und brav den Python-Quellcode zusammenzuklauben? Ich möchte ja nicht jedesmal erst alle Layout-Fehler beheben, bevor das Programm funktioniert...

Oder ist vielleicht ein anderes Grafik-Toolkit besser geeignet für mein Programm? Neben dieser Eingabe-Maske soll es natürlich eine Ausgabe der Filmliste geben, die optimalerweise jeweils mit Vorschaubild des Filmposters ausgestattet ist, und die man zügig durchsuchen kann. Bin ich da bei PyQt überhaupt richtig? Das (hier scheinbar eher verschmähte) Galileo-Buch lobt immerhin:
Da Qt einerseits sehr mächtig ist und andererseits eine wirklich konsequente und gut strukturierte objektorientierte API bereitstellt
hurz
User
Beiträge: 12
Registriert: Mittwoch 11. März 2009, 23:47

Donnerstag 12. März 2009, 10:25

Leonidas hat geschrieben:Und außerdem generiert man aus UI-Dateien eigentlich keine PY-Dateien mehr, sondern bindet sie direkt im Code ein, ohne Codegenerierung.
Nach deinem Tip hab ich mich in die Dokumentation von PyQt4 gestürzt. Folgender Code bringt mir einen Absturz von pythonw.exe:

Code: Alles auswählen

from PyQt4 import uic
uic.loadUi("eingabe.ui")
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Donnerstag 12. März 2009, 11:36

hurz hat geschrieben:Bin ich da bei PyQt überhaupt richtig?
Also Qt an sich ist schon nicht übel. Aber du scheinst da entweder fehlerhafte UI-Dateien verfüttert zu haben oder einen Fehler im UI-Loader gefunden zu haben :?

Generell ist es aber durchaus empfehlenswert GUIs in GUI-Designer zu bauen, unabhängig ob das nun der Qt Designer für Qt ist, Visual Studio für Windows-Ressourcen-Dateien oder Glade für GTK+.

Oder wie schaut bei dir der "Absturz" aus? Messagebox mit Fehler oder schließt sich das Programm einfach?
My god, it's full of CARs! | Leonidasvoice vs Modvoice
hurz
User
Beiträge: 12
Registriert: Mittwoch 11. März 2009, 23:47

Donnerstag 12. März 2009, 12:21

Leonidas hat geschrieben:Oder wie schaut bei dir der "Absturz" aus? Messagebox mit Fehler oder schließt sich das Programm einfach?
Messagebox: Runtime error von Visual C++...

Folgenden Inhalt hat meine Pythondatei:

Code: Alles auswählen

from PyQt4 import uic
uic.loadUi("eingabe.ui")
Ich habe aber die Pythondatei versehentlich eben direkt ausgeführt, also nicht über IDLE, sondern unter Windows mit nem Doppelklick, und siehe da: gleicher Runtime-Fehler, aber dahinter in der Dos-Box lugt hervor:

Code: Alles auswählen

QWidget: Must construct a QApplication before a QPaintDevice
Klingt eher nach einem Anfängerfehler...? :?
Leonidas
Administrator
Beiträge: 16024
Registriert: Freitag 20. Juni 2003, 16:30
Kontaktdaten:

Donnerstag 12. März 2009, 12:37

Dann solltest du das wohl noch eine QApplication machen, bevor du die GUI lädst (allerdings denke ich, dass ein Interpreter-Crash dennoch ein Bug der Library ist und nicht passieren sollte).
My god, it's full of CARs! | Leonidasvoice vs Modvoice
hurz
User
Beiträge: 12
Registriert: Mittwoch 11. März 2009, 23:47

Donnerstag 12. März 2009, 13:12

Folgendes habe ich mir mal hergedichtet:

Code: Alles auswählen

import sys
from PyQt4 import QtGui
from PyQt4 import uic

app = QtGui.QApplication(sys.argv)
window = QtGui.QDialog()
uic.loadUi("eingabe.ui")
uic.setupUi(window)

window.show()
sys.exit(app.exec_())
Ich erhalte dann eine lange Liste, von der aber wohl wieder nur die letzte Zeile interessant ist:

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\Daten\programmieren\python25\filme\gui2.py", line 7, in <module>
    uic.loadUi("eingabe.ui")
  File "C:\Python25\lib\site-packages\PyQt4\uic\__init__.py", line 106, in loadUi
    return loader.DynamicUILoader().loadUi(uifile, baseinstance)
  File "C:\Python25\lib\site-packages\PyQt4\uic\Loader\loader.py", line 22, in loadUi
    return self.parse(filename)
  File "C:\Python25\lib\site-packages\PyQt4\uic\uiparser.py", line 667, in parse
    actor(elem)
  File "C:\Python25\lib\site-packages\PyQt4\uic\uiparser.py", line 526, in createUserInterface
    self.traverseWidgetTree(elem)
  File "C:\Python25\lib\site-packages\PyQt4\uic\uiparser.py", line 504, in traverseWidgetTree
    handler(self, child)
  File "C:\Python25\lib\site-packages\PyQt4\uic\uiparser.py", line 163, in createWidget
    self.traverseWidgetTree(elem)
  File "C:\Python25\lib\site-packages\PyQt4\uic\uiparser.py", line 504, in traverseWidgetTree
    handler(self, child)
  File "C:\Python25\lib\site-packages\PyQt4\uic\uiparser.py", line 163, in createWidget
    self.traverseWidgetTree(elem)
  File "C:\Python25\lib\site-packages\PyQt4\uic\uiparser.py", line 504, in traverseWidgetTree
    handler(self, child)
  File "C:\Python25\lib\site-packages\PyQt4\uic\uiparser.py", line 320, in createLayout
    self.traverseWidgetTree(elem)
  File "C:\Python25\lib\site-packages\PyQt4\uic\uiparser.py", line 504, in traverseWidgetTree
    handler(self, child)
  File "C:\Python25\lib\site-packages\PyQt4\uic\uiparser.py", line 328, in handleItem
    self.traverseWidgetTree(elem)
  File "C:\Python25\lib\site-packages\PyQt4\uic\uiparser.py", line 504, in traverseWidgetTree
    handler(self, child)
  File "C:\Python25\lib\site-packages\PyQt4\uic\uiparser.py", line 323, in createLayout
    self.stack.peek().addLayout(layout, *elem.attrib["grid-position"])
AttributeError: addLayout
Anscheinend hängt es daran, dass der Übersetzer für alle Layouts die Methode addLayout() verwendet, obwohl selbige beim QGridLayout und QFormLayout setLayout() heißt.

Was soll ich da tun? Von Python 2.5 auf 2.6 wechseln? Oder dann gleich auf Version 3? Vielleicht gibt es den Bug dort nicht...

Oder kann ich sonst etwas falsch machen, was diesen Fehler erzeugen kann?
Benutzeravatar
cofi
Moderator
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Donnerstag 12. März 2009, 14:52

hurz mach mal langsam ;) Am besten ist es immer die Dokumentation von PyQt anzuschaun, besser gesagt die Klassenübersicht um zu schaun was geht, was nicht - s.o.

setLayout != addLayout: Mit ersterem legst du ein Layout für ein Widget fest mit letzterem fügst du eins hinzu. Ein großer Unterschied ;)

Zum Formlayout: Das baut auf Zeilen auf schau dir mal die `addRow' Methoden an, da gibts auch welche mit Layout.

Zu deinen Fehlermeldungen: Die gibts, weil es eben kein addLayout gibt, das sagt ja auch der Error. Und nein die Pythonversion zu wechseln hilft nichts, du musst einfach deine .ui datei so umschreiben, dass du kein GridLayout nutzt, bspw Formlayout nutzen ;)

Zum uic Modul ... ich Werd damit nicht so recht warm, da man damit nur Instanzen des Widgets in der .ui erstellen kann und da der Designer in Bezug zu PyQt nicht so recht zu gebrauchen ist, muss man dann auch wieder MonkeyPatchen -> Der Vorteil dynamischer zu sein, fällt dann auch weg. Aber ich hab den Designer erst vor kurzem für mich entdeckt, vllt find ich da noch was passendes :)
hurz
User
Beiträge: 12
Registriert: Mittwoch 11. März 2009, 23:47

Donnerstag 12. März 2009, 15:20

cofi hat geschrieben:hurz mach mal langsam ;)
Ich bin in der pythonischen Sturm-und-Drang-Phase und kaum zu bremsen! :D
Am besten ist es immer die Dokumentation von PyQt anzuschaun, besser gesagt die Klassenübersicht um zu schaun was geht, was nicht - s.o.

setLayout != addLayout: Mit ersterem legst du ein Layout für ein Widget fest mit letzterem fügst du eins hinzu. Ein großer Unterschied ;)

Zum Formlayout: Das baut auf Zeilen auf schau dir mal die `addRow' Methoden an, da gibts auch welche mit Layout.
Ich möchte ja gerade vermeiden, dass ich in dem erzeugten Pythoncode noch rumwurschteln muss... Der Designer soll mir eine korrekte ui-Datei erstellen. Warum tut er das nicht?
Zu deinen Fehlermeldungen: Die gibts, weil es eben kein addLayout gibt, das sagt ja auch der Error. Und nein die Pythonversion zu wechseln hilft nichts, du musst einfach deine .ui datei so umschreiben, dass du kein GridLayout nutzt, bspw Formlayout nutzen ;)
Ich benutze ja das FormLayout schon seit Beginn.
da der Designer in Bezug zu PyQt nicht so recht zu gebrauchen ist, muss man dann auch wieder MonkeyPatchen
Was ist denn das? :?

Also mein Problem scheint zu sein, dass mir der Designer keine korrekte ui-Datei erzeugt, die damit auch nicht eingebunden werden kann. (Programmabsturz)
Bleibt mir wohl nur die Lösung, via pyuic umzuwandeln und dann den erzeugten Pythoncode zu korrigieren...
Benutzeravatar
cofi
Moderator
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Donnerstag 12. März 2009, 15:51

Also für mich schaut

Code: Alles auswählen

self.formLayout.addLayout(self.horizontalLayout, 7, 1, 1, 1)
nicht nach einem QFormLayout aus.

Nein ich meinte mit meinen ganzen Kommentaren zu Code nicht, dass du in dem generierten Code rumfummeln sollst, sondern, dass du anschauen sollst was geht und dementsprechend deine .ui-Dateien aufbaust. ;)

http://en.wikipedia.org/wiki/Monkey_patch gibt leider keine deutsche Seite dazu ;)

Ich spiel derweil mal mit meinem Designer, ob ich hinbekomm, was du willst ;)
Benutzeravatar
cofi
Moderator
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Donnerstag 12. März 2009, 16:05

UI: http://paste.pocoo.org/show/107579/

Test-Script: http://paste.pocoo.org/show/107580/

Passt wunderbar ;) Ist aber natürlich nur optisch benutzbar ...
hurz
User
Beiträge: 12
Registriert: Mittwoch 11. März 2009, 23:47

Donnerstag 12. März 2009, 16:14

Vielen Dank für die Mühe!

Wie erklärst du mir (und der Weltöffentlichkeit) dann das Folgende?

Code: Alles auswählen

Traceback (most recent call last):
  File "C:\Daten\programmieren\python25\filme\test.py", line 6, in <module>
    window = uic.loadUi("test.ui")
  File "C:\Python25\lib\site-packages\PyQt4\uic\__init__.py", line 106, in loadUi
    return loader.DynamicUILoader().loadUi(uifile, baseinstance)
  File "C:\Python25\lib\site-packages\PyQt4\uic\Loader\loader.py", line 22, in loadUi
    return self.parse(filename)
  File "C:\Python25\lib\site-packages\PyQt4\uic\uiparser.py", line 667, in parse
    actor(elem)
  File "C:\Python25\lib\site-packages\PyQt4\uic\uiparser.py", line 526, in createUserInterface
    self.traverseWidgetTree(elem)
  File "C:\Python25\lib\site-packages\PyQt4\uic\uiparser.py", line 504, in traverseWidgetTree
    handler(self, child)
  File "C:\Python25\lib\site-packages\PyQt4\uic\uiparser.py", line 320, in createLayout
    self.traverseWidgetTree(elem)
  File "C:\Python25\lib\site-packages\PyQt4\uic\uiparser.py", line 504, in traverseWidgetTree
    handler(self, child)
  File "C:\Python25\lib\site-packages\PyQt4\uic\uiparser.py", line 328, in handleItem
    self.traverseWidgetTree(elem)
  File "C:\Python25\lib\site-packages\PyQt4\uic\uiparser.py", line 504, in traverseWidgetTree
    handler(self, child)
  File "C:\Python25\lib\site-packages\PyQt4\uic\uiparser.py", line 320, in createLayout
    self.traverseWidgetTree(elem)
  File "C:\Python25\lib\site-packages\PyQt4\uic\uiparser.py", line 504, in traverseWidgetTree
    handler(self, child)
  File "C:\Python25\lib\site-packages\PyQt4\uic\uiparser.py", line 328, in handleItem
    self.traverseWidgetTree(elem)
  File "C:\Python25\lib\site-packages\PyQt4\uic\uiparser.py", line 504, in traverseWidgetTree
    handler(self, child)
  File "C:\Python25\lib\site-packages\PyQt4\uic\uiparser.py", line 323, in createLayout
    self.stack.peek().addLayout(layout, *elem.attrib["grid-position"])
AttributeError: addLayout
Ich stelle mal meinen Quellcode rein, vielleicht kannst du den dann mal bei dir testen?

/Edith: da: http://paste.pocoo.org/show/107587/
Benutzeravatar
cofi
Moderator
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Donnerstag 12. März 2009, 16:39

Gute Frage. Bei mir tut das jedenfalls :(

Code: Alles auswählen

In [1]: import sys, PyQt4.QtCore
In [2]: sys.version
Out[2]: '2.6.1 (r261:67515, Feb 23 2009, 00:54:49) \n[GCC 4.3.3]'
In [3]: PyQt4.QtCore.QT_VERSION_STR
Out[3]: '4.4.2'
In [4]: PyQt4.QtCore.PYQT_VERSION_STR
Out[4]: '4.4.4'
Antworten