Wie separate ui-Files als Widgets in QMainWindow einbinden?

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 zusammen,

was ist eine gute und komfortable Methode, einzelne, kleinere Widgets, die mit dem QtDesigner erstellt wurden, in einem "Haupt"-Widget zusammenzuführen?

Letzteres soll natürlich ebenfalls per Designer erstellt werden.

Also prinzipiell verdeutlicht folgendes Mockup meine Idee, welches das "Haupt"-Widget darstellen soll:
Bild

Ich hatte bisher die Idee, in meinem QMainWindow mit dem Designer einfach QWidgets / QGroupBoxes als Container anzulegen und dann später im Code an dieser Stelle einfach die anderen GUI-Elemente als Kindelemente einzusetzen. Dann würden in obigen Beispiel innerhalb der beiden Boxen meine anderen beiden erstellten Widgets platziert werden. Alles wäre leicht veränderbar per Designer und damit leicht zu ändern bei späteren Umstrukturierungen oder Änderungen.

Das klappt aber nur bedingt, da ich bei leeren Elementen im Designer kein Layout setzen kann. Das jedoch ist ja entscheident, damit auch die Kindelemente später sich gut in das Gesamtlayout einpassen.

Leider habe ich im Netz nichts gefunden, was mir da geholfen hätte. In den Tutorials basteln die immer nur ein GUI-Element und nutzen das, nicht mehrere kleine, wie ich. Ich kann mir jedoch gar nicht vorstellen, dass andere nicht vo vorgehen wollen.

Edit: Habe das leider im falschen Forum gepostet. Es soll natürlich ins Qt / KDE-Subforum. Ich hoffe ein Admin kann das verschieben :-)
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Ich denke die einfachste Moeglichkeit ist die UI-Dateien in einer selbstgeschriebenen Datei zusammenzufuehren und Abstand von dem UI-Mainwindow zunehmen, wenn es sowieso nichts anderes macht als die anderen Widgets zu managen.

Allerdings verstehe ich nicht was du mit "spaeter einsetzen" meinst. Was genau hast du denn vor?
Hyperion hat geschrieben:Edit: Habe das leider im falschen Forum gepostet. Es soll natürlich ins Qt / KDE-Subforum. Ich hoffe ein Admin kann das verschieben :-)
So neu ist das Layout nun auch nicht, dass du da noch im alten Trott sein kannst :P
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

cofi hat geschrieben:Ich denke die einfachste Moeglichkeit ist die UI-Dateien in einer selbstgeschriebenen Datei zusammenzufuehren und Abstand von dem UI-Mainwindow zunehmen, wenn es sowieso nichts anderes macht als die anderen Widgets zu managen.
Hm... damit verliere ich aber Flexibilität. Ich kann so ja einfach kleinere "Einheiten" designen und testen. Zudem kann es ja auch mal sein, dass man durchaus dynamische Elemente hat, die man eben nicht per Designer, sondern im Code erstellen möchte. Auch für die muss ich ja irgend wie die Chance haben, einen Container so anzulegen, dass ich ihn nachträglich befüllen kann, das ganze sich aber eben doch sauber in das Gesamtlayout einfügt.
Allerdings verstehe ich nicht was du mit "spaeter einsetzen" meinst. Was genau hast du denn vor?
Naja, ich erstelle ja per uic.loadUi("main.ui") in meiner Hauptklasse im Script das Haupt-GUI Element. Dieses will ich dann ja mit den weiteren Sub-Elementen befüllen, die in anderen ui-Files definiert sind. Das passiert ja sequentiell, daher das "später".
cofi hat geschrieben:
Hyperion hat geschrieben:Edit: Habe das leider im falschen Forum gepostet. Es soll natürlich ins Qt / KDE-Subforum. Ich hoffe ein Admin kann das verschieben :-)
So neu ist das Layout nun auch nicht, dass du da noch im alten Trott sein kannst :P
*g* Nee, ich hatte mich einfach stumpf verklickt. Ist wohl schon zu spät ;-)
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

Nein, die Flexibilitaet verlierst du damit ueberhaupt nicht. Deine Widgets koennen genauso modular aufgebaut werden und genauso eingebunden werden, einzig dass die Stelle, die das zusammenfuehrt, in Code statt in UI beschrieben wird.

Weil mein erster Post etwas undeutlich war: Ich meine nicht, dass du deine Widgets umschreibst, sondern dass deine Buendelungsstelle nicht im Designer erstellt wird und stattdessen eigenen Code benutzt, um die Widgets zusammenzufuehren.

Hm ich glaube das wird schon wieder undeutlich, ist wohl doch schon zu spaet.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

cofi hat geschrieben: Weil mein erster Post etwas undeutlich war: Ich meine nicht, dass du deine Widgets umschreibst, sondern dass deine Buendelungsstelle nicht im Designer erstellt wird und stattdessen eigenen Code benutzt, um die Widgets zusammenzufuehren.
Also wenn ich Dich da richtig verstehe, ist das ja genau mein Plan. Nur ist die Frage, wie kann ich das erreichen?
lunar

Ich verstehe Dein Problem nicht ganz. Du hast doch bereits ein Layout mit Containern. Wo ist jetzt das Problem, die einzelnen Widgets in diese Container einzufügen? Innerhalb eines Containers reicht ja ein einfaches QVBoxLayout, um das eigene Widget auf die gesamte Größe des Containers zu bringen.
franzf
User
Beiträge: 78
Registriert: Samstag 29. August 2009, 10:21

Das Zauberwort heißt "Promoten".
Du machst dir im Designer das Layout, wie du dir es wünschst. die "Container" sind bei dir ja schon die am meisten speizialisierten QWidgets (z.B. ein QFrame).
Angenommen du willst ein eigenes Widget, welches von QFrame erbt, ohne Umwege in deinem Layout per Designer unterbringen. Weiter angenommen die Klasse heißt "MyFrame" und liegt in der Datei "myframe.py".
Leg im Designer einen QFrame an die gewünschte Stelle, clicke rechts drauf -> "Promote to", trage als Klassenname "MyFrame" ein, als Header "myframe".

Das könnte dann so ausschauen:

gui.ui

Code: Alles auswählen

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>521</width>
    <height>602</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QHBoxLayout" name="horizontalLayout">
    <item>
     <widget class="MyFrame" name="frame">
      <property name="frameShape">
       <enum>QFrame::StyledPanel</enum>
      </property>
      <property name="frameShadow">
       <enum>QFrame::Raised</enum>
      </property>
     </widget>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>521</width>
     <height>25</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <customwidgets>
  <customwidget>
   <class>MyFrame</class>
   <extends>QFrame</extends>
   <header>myframe</header>
   <container>1</container>
  </customwidget>
 </customwidgets>
 <resources/>
 <connections/>
</ui>
myframe.py

Code: Alles auswählen

from PyQt4.Qt import *

class MyFrame(QFrame):
    def __init__(self, parent=None):
        QFrame.__init__(self, parent)
        layout = QVBoxLayout(self)
        layout.addWidget(QPushButton("Hello", self))
        layout.addWidget(QTextEdit(self))
main.py

Code: Alles auswählen

from PyQt4.Qt import *
from gui import Ui_MainWindow
import sys

class main(QMainWindow):
    def __init__(self, parent=None):
        QMainWindow.__init__(self, parent)
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

app = QApplication(sys.argv)
w = main()
w.show()
app.exec_()
Bevor du das ganze starten kannst, braucht es noch ein kurzes

Code: Alles auswählen

pyuic4 gui.ui >gui.py
Ich find es im übrigen angenehmer für den User, wenn er nicht erst ewig warten muss, bis der uic zur Laufzeit die ui erstellt hat, wenn der Programmierer (am besten per Makefile) die ui schon vorher mit dem pyuic4 in eine (zügig ladbare) Klasse packen kann ;)
BlackJack

@franzf: Codegenerierung ist IMHO hässlich. Hast Du belastbare Zahlen wie viel schneller das ist?
lunar

@franzf: Interessant, ich wusste nicht, dass das auch in Python funktioniert. Im Deutschen spricht der Designer übrigens von "Benutzerdefinierten Klassen", der Menüpunkt heißt "Als Platzhalter für benutzerdefinierte Klasse festlegen …".

Für die Behauptung am Schluss Deines Postings hätte ich auch gerne belastbare Zahlen gesehen. "uic.loadUi()" erzeugt die Objekte direkt aus der XML-Beschreibung, die unter Python >= 2.5 mit cElementTree gelesen wird. Das kann so viel langsamer nicht sein, dass der Nutzer "ewig" warten müsste … insofern würde ich gerne wissen, ob Du überhaupt schon mal GUIs so entwickelt hast, dass die GUI dynamisch zur Laufzeit geladen wird. Ich habe das getan und keinesfalls ewig auf das Laden der GUI warten müssen.

Ein Nachteil an der Code-Generierung ist übrigens, dass es Probleme mit Paketen gibt. Sobald man Ressourcen verwendet, steht nämlich ein relativer Import des Ressourcen-Moduls im Quelltext.
franzf
User
Beiträge: 78
Registriert: Samstag 29. August 2009, 10:21

lunar hat geschrieben:Ein Nachteil an der Code-Generierung ist übrigens, dass es Probleme mit Paketen gibt. Sobald man Ressourcen verwendet, steht nämlich ein relativer Import des Ressourcen-Moduls im Quelltext.
OK, das wusste ich nicht. Python ist für mich eigentlich eher Fremdsprache ;) Ich nehms halt gern her zum Testen.
Und meine Erfahrung bezüglich Ladezeit mit loadUi() bezog sich auch eher auf den QUiLoader unter C++. Bei einfachen Guis ist es noch vertretbar (1-2 sec.), werden die Formulare aber größer, konnte ich auch schon mal >4 Sekunden warten. Und wenn ich nur schnell was schauen/einstallen will ist das nervig :P
Zum Glück wird auch fleißig gecached, so dass der nächste Start schneller geht. (Das Cachen zieht mir beim Testen jetzt auch nen ziemlichen Strich durch die Rechnung...)

Ansonsten ist die Codegenerierung unter Qt ziemlich sicher, es wird ein eigenes File erzeugt, welches der User einbinden muss. Oben steht ein fettes Warning "do not Edit". Drum versteh ich auch die prinizpielle Abneigung gegen Codegenerierung unter Qt nicht ;) Wenn das in Python natürlich noch andere Nebenwirkungen hat, ist mir das aber jetzt klar.
DasIch
User
Beiträge: 2718
Registriert: Montag 19. Mai 2008, 04:21
Wohnort: Berlin

franzf hat geschrieben:Ich find es im übrigen angenehmer für den User, wenn er nicht erst ewig warten muss, bis der uic zur Laufzeit die ui erstellt hat, wenn der Programmierer (am besten per Makefile) die ui schon vorher mit dem pyuic4 in eine (zügig ladbare) Klasse packen kann ;)
Ich bezweifle dass ein User überhaupt die Chance hat den Unterschied außer im direkten Vergleich überhaupt bemerken kann.
Benutzeravatar
cofi
Python-Forum Veteran
Beiträge: 4432
Registriert: Sonntag 30. März 2008, 04:16
Wohnort: RGFybXN0YWR0

franzf hat geschrieben:Wenn das in Python natürlich noch andere Nebenwirkungen hat, ist mir das aber jetzt klar.
In Python hat das vor allem auch noch die Nebenwirkung dem Geist von Python zuwiderzulaufen, da man nichts kompilieren muss und die Erzeugung auch nich an ein Makefile delegieren kann bzw den Aufruf kann man dann trotzdem vergessen.
D.H. man laeuft Gefahr alte Versionen zu Verwenden.

Gilt natuerlich fuer die Entwicklung und nicht fuer den angesprochenen End-User, aber da muss man dann doch an mehreren Ecken Aenderungen machen, wenn man den statisch generierten Code nutzen will.

P.S. Das ist keine Abneigung gegen generierten Code, denn der wird ja eh erzeugt, sondern eine gegen statisch generierten Code, der dann am besten noch im selben Verzeichnis rumfliegt.
lunar

@franzf: Code-Generierung steht einfach im Weg. Die Ausführung eines Compilers passt nicht in den Entwicklungszyklus eines Python-Programmierers. Man kann leicht nicht-existente Fehler jagen, weil man vergessen hat, pyuic nach Änderungen auszuführen. Dynamisches Laden dagegen funktioniert einfach, weil immer automatisch die aktuelle GUI verwendet wird.

Bei C++ dagegen verursacht generierter Code keinen Mehraufwand. Ein Buildsystem hats eh und Compilieren muss man so oder so. Außerdem macht generierter Code die Typprüfung des Compilers, so dass man auf Casts oder indirekte Eigenschaftszugriffe verzichten kann.

Die Problematik der Ladezeiten ist imho irrelevant. Die Ladezeit entsteht viel eher durch die hinter einer komplexen GUI stehende komplexe Logik, die allerlei initialisieren möchte. Die GUI fällt da kaum ins Gewicht.
franzf
User
Beiträge: 78
Registriert: Samstag 29. August 2009, 10:21

@lunar + cofi:
Danke für die Erklärungen, da war ich unter Python ja noch richtig in meinen C++-Denkschemata gefangen ;) Gelobe für die Zukunft Besserung.
Und wenn ich viele Daten für die Logik laden muss, braucht die Gui nicht länger zum Laden, denn das wird dann in (mindestens) einen eigenen Thread ausgelagert.

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

@franzf: Vielen Dank für diese Lösung. Ich werde das mal ausprobieren.

@Admins: Bitte verschiebt diesen Thread in das PyQt-Subforum - dürfte später für Suchende sinnvoll sein :-)

Edit (Leonidas): Verschoben.
Antworten