Beispiele für komplexere Tk GUIs ?!?

Fragen zu Tkinter.
__deets__
User
Beiträge: 14480
Registriert: Mittwoch 14. Oktober 2015, 14:29

Alfons Mittelmeyer hat geschrieben: Es ist nur so, dass der GuiDesigner diesen globalen ID Murks a la pygubu kaum unterstützt.
Du behauptest das jetzt zum wiederholten male - es ist nur schlicht gelogen. pygubu erzeugt *KEINE* globalen IDs. Es gibt IDs innerhalb einer Widget-Hierachie. Das war's. Das ist nicht "global", jedenfalls unter der im Rest der Welt anerkannten Definition von "global".

Und das kann man auch beweisen. Die vom folgenden Programm erzeugten Widget-Hierachien sind getrennt von einander und haben unterschiedliche Top-Level Fenster Objekte. Nix mit "globalem Murks" - das ist allerdings eine treffende Beschreibung fuer das was du so verzapfst...

Code: Alles auswählen

GUI = """<?xml version='1.0' encoding='utf-8'?>
<interface>
  <object class="tk.Toplevel" id="mainwindow">
    <property name="height">200</property>
    <property name="resizable">both</property>
    <property name="title" translatable="yes">Minimal toplevel example</property>
    <property name="width">200</property>
    <child>
      <object class="ttk.Frame" id="container1">
        <property name="height">200</property>
        <property name="width">200</property>
        <layout>
          <property name="column">0</property>
          <property name="propagate">True</property>
          <property name="row">0</property>
          <property name="sticky">nsew</property>
          <columns>
            <column id="0">
              <property name="weight">1</property>
            </column>
          </columns>
          <rows>
            <row id="0">
              <property name="weight">1</property>
            </row>
          </rows>
        </layout>
        <child>
          <object class="ttk.Label" id="Label_1">
            <property name="anchor">center</property>
            <property name="font">{Helvetica} 36 {bold}</property>
            <property name="foreground">#000081</property>
            <property name="padding">40 10</property>
            <property name="text" translatable="yes">Hello World!</property>
            <layout>
              <property name="column">0</property>
              <property name="propagate">True</property>
              <property name="row">0</property>
              <property name="sticky">nsew</property>
            </layout>
          </object>
        </child>
      </object>
    </child>
  </object>
</interface>
"""

import os
try:
    import tkinter as tk
except:
    import Tkinter as tk
import pygubu

if __name__ == '__main__':
    builder_a = pygubu.Builder()
    builder_b = pygubu.Builder()

    mainwindows = []
    for b in (builder_a, builder_b):
        b.add_from_string(GUI)
        mainwindows.append(b.get_object('mainwindow'))

    print(list(id(mw) for mw in mainwindows))
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

__deets__ hat geschrieben:
Alfons Mittelmeyer hat geschrieben: Es ist nur so, dass der GuiDesigner diesen globalen ID Murks a la pygubu kaum unterstützt.
Du behauptest das jetzt zum wiederholten male - es ist nur schlicht gelogen. pygubu erzeugt *KEINE* globalen IDs. Es gibt IDs innerhalb einer Widget-Hierachie. Das war's. Das ist nicht "global", jedenfalls unter der im Rest der Welt anerkannten Definition von "global".

Und das kann man auch beweisen. Die vom folgenden Programm erzeugten Widget-Hierachien sind getrennt von einander und haben unterschiedliche Top-Level Fenster Objekte. Nix mit "globalem Murks" - das ist allerdings eine treffende Beschreibung fuer das was du so verzapfst...
Irgendwie scheinst Du nicht zu verstehen, worum es geht. Mein GuiDesigner hat so 200 bis 300 Widgets, dynamisch erzeugte und während der Laufzeit temporär angelegte Toplevels nicht mitgezählt.

Und wenn man da kreuz und quer auf die 200 bis 300 Widgets zugreift, dann entsteht ein Verhau, bei dem sich so schnell keiner mehr auskennt.

Ich will nicht sagen, dass ein sehr erfahrener und sehr disziplinierter Programmierer damit nicht umgehen kann.

Auf die einzelnen Komponenten soll man nur über klar definierte Interfaces zugreifen. Aber auch Methodeninterfaces genügen nicht. Wenn man etwa im GuiDesigner in der Selektion ein anderes Widget auswählt, sind davon vielleicht zehn GUI Komponenten davon betroffen.

Aber dass die Sektion dann von zehn - weiß es nicht genau - GUI Komponenten bestimmte Methoden aufrufen soll, das ist ebenfalls nicht der richtige Weg. Die Selektion braucht überhaupt nichts über andere GUI Komponenten zu wissen. Sie sendet einfach:

send('SELECTION_CHANGED')

Wenn ich diese Widgdet IDs nicht anbiete - ich könnte das tun, denn für DynTkInter generiere ich so etwas, könnte ich auch für tkinter generieren - dann gibt es nur Closures, die man von außen nicht aufrufen kann, oder Objekte mit Methoden aber ohne Referenz.

Natürlich gibt es schon eine Referenz, aber ich glaube nicht, dass jemand eine Methode aus einem command oder event oder dem Eventbroker herauspicken möchte. Und eventuell findet er dort auch nur ein partial oder ein lambda.

Ein globaler Verhau wäre dann zwar über diesem Weg möglich: root.labelframe1.notebook.panedwindow.labelframe1.entry2

Aber ich glaube nicht, dass jemand das im Ernst tun möchte. So wie es jetzt ist, kann auch der unerfahrene Programmierer eine komplexe GUI erstellen ohne dass er einen Verhau produziert, den er bald nicht mehr durchblickt.

Ich habe ja nichts gegen solche IDs bei einer kleinen GUI und wenn der Programmierer damit gut umgehen kann. Viele Beispiele hier im Forum belegen aber, dass auch bei kleinen GUIs ein Verhau produziert wird, bei dem kaum durchzublicken ist.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Bei recht großen GUIs soll man auch keine aussagekräftigen Namen mehr für Widgets verwenden, weil man dann nicht weiß, ob man die Namen schon verwendet hat. Die nummeriert man am Besten einfach durch. Bei professionellen Programmen mit jeder Menge von Widgets heißt dann so ein Widget, z.B.:

'F12574'
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Will jemand ein Modul, das automatisch IDs generiert?

Aus den exportierten Sourcen (with names) lassen sich die IDs automatisch generieren.
Auch aus sonstigen Sourcen, bei denen man tk Namen eingetragen hat, lassen sich die IDs generieren.
Bei anderen auch, ist dann aber sinnlos.
Das Modul hat gerade einmal 28 Zeilen.
__deets__
User
Beiträge: 14480
Registriert: Mittwoch 14. Oktober 2015, 14:29

Alfons Mittelmeyer hat geschrieben:Bei recht großen GUIs soll man auch keine aussagekräftigen Namen mehr für Widgets verwenden, weil man dann nicht weiß, ob man die Namen schon verwendet hat. Die nummeriert man am Besten einfach durch. Bei professionellen Programmen mit jeder Menge von Widgets heißt dann so ein Widget, z.B.:

'F12574'
:lol: was du immer für Behauptungen aufstellst. Nur weil du nicht begreifst wie der Rest der Welt mit GUIs arbeitet :roll:

Natürlich benutzt man benamste Widgets. Nur baut man nicht - so wie du das hier darlegst - monströse widgethierachien. Da ist die Unprofessionalität begraben.

Sondern man baut überschaubare Teilbäume, die man dann an geeigneter Stelle in eine bestehende Hierarchie einhängt. Macht man in Qt, macht man in HTML & seinen diversen Frameworks. Wenn möglich noch mit Data-binding, um sich ich mehr Arbeit zu sparen.

Genauso wie man sein Programm nicht in eine riesige Klasse schreibt, sondern in funktional sinnvolle Untereinheiten gliedert.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

__deets__ hat geschrieben: :lol: was du immer für Behauptungen aufstellst. Nur weil du nicht begreifst wie der Rest der Welt mit GUIs arbeitet :roll:
Natürlich benutzt man benamste Widgets.
Bei BMW HMI sind die Widgets in einer Datenbank und es sind mehr als zehntausend. Und man hat auch nicht nur einen Screen sondern hunderte.
__deets__ hat geschrieben: Nur baut man nicht - so wie du das hier darlegst - monströse widgethierachien. Da ist die Unprofessionalität begraben.

Sondern man baut überschaubare Teilbäume, die man dann an geeigneter Stelle in eine bestehende Hierarchie einhängt. Macht man in Qt, macht man in HTML & seinen diversen Frameworks. Wenn möglich noch mit Data-binding, um sich ich mehr Arbeit zu sparen.

Genauso wie man sein Programm nicht in eine riesige Klasse schreibt, sondern in funktional sinnvolle Untereinheiten gliedert.
Ja macht man auch bestimmt so, nur dann läßt man sein Programm laufen und dann hat man wieder die vereinte GUI.

Wenn jetzt jemand die zerteilte GUI als Gesamtapplikation startet, dann ist sie vereint. Wenn er nun darin ein Container Widget, das so eine Untereinheit ist, woanders hineinsetzt. Ja, dann muß er das auch im Code an der alten Stelle raushängen und an der neuen Stelle reinhängen. Dann war die ganze Verschieberei im GUI Builder nur zur Ansicht, also ziemlich für die Katz.

Im GuiDesigner, kann man auch Teile abspeichern. Allerdings beim Export nicht realisiert, aber macht ja nichts, weil man ja etwa über das Clipboard sich die Teile wieder herausholen kann. Im GUI Designer gibt es jetzt zwei Möglichkeiten, nämlich auch das wieder zu exportieren, wo herausgelöscht wurde und das wo hineingefügt wurde. Oder man exportiert einfach die ganze GUI. Wenn man es so macht wie Du, paßt dann der Code nicht mehr dazu, weil Du da teilweise GUI Aufbau im Code hast.

Kann man ja so machen. Aber Cut, Copy und Paste im GuiDesigner geht dann halt nicht mehr, ohne dass man auch den Code ändern muss. Sollte natürlich auch kein Problem sein. Wenn man den GUI Aufbau im GuiDesigner hat, statt im Code, braucht man im Code halt nichts nachtragen.

Es geht nicht um monströse Widget HIerarchien, denn ob man die GUI zerteilt hat oder nicht, es ist dann immer noch dieselbe tkinter GUI und diese hat nun einmal soviele Ebenen wie sie eben hat.

Aber wenn man den Code in der Fortführung der __init__ von Container Widgets aufruft und über Messages mit anderen Komponenten kommuniziert, spielt der GUI Aufbau Null Rolle für das Funktionieren des Programmes. Man kann das so zerteilen oder wieder vereinen, wie immer es gefällt, ohne eine Zeile Code deswegen ändern zu müssen. Es ist schnurzpiepegal aus welchen unterschiedlichen Sourcen die GUI aufgebaut wird. Hauptsache, sie wird aufgebaut. Und es ist schnurzpiepegal wo ein Container Widget ist. Dessen __init__ ist immer noch dessen __init__ und nicht verschoben.

PS: dasselbe gilt natürlich auch für die 'globalen' IDs: die ID ist immer noch die ID. Es ist nur die Frage, wie dann der Code aussieht.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

__deets__ hat geschrieben: Natürlich benutzt man benamste Widgets. Nur baut man nicht - so wie du das hier darlegst - monströse widgethierachien. Da ist die Unprofessionalität begraben.
Ich weiß nicht was Du damit meinst. Soll ich die GUI meines GuiDesigner Moduls noch weiter splitten. Aber ich weiß jetzt nicht wirklich nicht, wie ich das tun kann.

Das ist die GUI meines GuiDesigner Moduls. Bzw. was Du hier siehst habe ich bereits in zwei Module gesplittet, nämlich Guidesigner,py und Modules.py. In Guidesigner.py steht drin, dass es ein Toplevel ist und ein Menü hat. Und in Modules.py steht das mit den zwei Frames und dem Labelframe drin. Die beiden Module zusammengefügt und exportiert ergibt folgende GUI:

Code: Alles auswählen

# -*- coding: utf-8 -*-

try:
    import tkinter as tk
except ImportError:
    import Tkinter as tk

class DynTkInterGuiDesigner(tk.Toplevel):

    def __init__(self,master,**kwargs):
        tk.Toplevel.__init__(self,master,**kwargs)
        # widget definitions ===================================
        self.GuiFrame = tk.Frame(self)
        self.Toolbar = tk.Frame(self)
        self.WidgetPath = tk.LabelFrame(self,text=' Path: ', labelanchor='w', bd=0)
        self.TopMenu = tk.Menu(self,activebackground='#ececac')
        self['menu'] = self.TopMenu
        self.Toolbar.pack(fill='x')
        self.WidgetPath.pack(fill='x', anchor='w')
        self.GuiFrame.pack(anchor='nw')

if __name__ == '__main__':
    DynTkInterGuiDesigner(tk.Tk()).mainloop()
Es ist klar, wenn ich ein UI File hätte, dass ich dafür ein UI File nehmen würde. Aber kannst Du mir verraten, was Du daran monströs findest und wie ich das weiter splitten könnte? Ich denke dass ein UI File pro Container Widget die Grenze ist, wo man nicht weiter splitten sollte, oder? Und wenn ich daraus zwei Module gemacht habe, dürfte wohl genügen, oder?

Man sieht an diesem Export aber nicht alles, weil es noch einen Parameter gibt, der nicht nach tkinter exportiert werden kann. Im Parameter 'link' steht der Name des Files drin, der den Inhalt der Container Widgets beschreibt, vergleichbar mit der Ableitung von einer Basis Klasse.
__deets__
User
Beiträge: 14480
Registriert: Mittwoch 14. Oktober 2015, 14:29

Du bist doch derjenige der hier von riesigen Hierarchien redet, die dann unwartbar werden. Der gezeigte Code hier faellt offensichtlich nicht unter diese Kategorie.

Deine sonstigen Ausfuehrungen zu Selektion und generell deine Abstraktionen, auch im Code des DynTKinter, sind fuer mich (und ich behaupte auch mal die meisten anderen hier) voellig undurchschaubar. Insofern kann ich da auch keine Hinweise geben, wie das besser zu machen waere, oder sich auch nur in Relation setzen laesst zu allgemein bekannten Vorgehensweisen.

Meine Antworten wenden sich meistens gegen von dir getaetigte Aussagen, Tools wie pygubu oder die Verwendung von nicht-code basierten Formaten wie XML und JSON zur Erstellung von GUIs waeren unbrauchbar weil nicht profesionell. Das entspricht nicht den Erfahrungen die ich persoenlich mit einer grossen, von Millionen Usern benutzen Anwendung, und viele andere hier und da draussen in der Welt gemacht haben.

Wenn du ein Beispiel fuer eine grosse Qt-Anwendung sehen willst, die recht offensichtlich ihre hochkomplexe UI in viele, viele, viele kleinere , handhabbare Stuecke zergliedert, kann ich nur empfehlen einen Blick auf LibreCAD/QTCAD zu werfen:

https://github.com/LibreCAD/LibreCAD/tr ... c/ui/forms

Die groesste UI-Datei darin ist qg_dlgtext.ui mit ~60KB ist nur *ein* Dialog - zum einstellen der Fonteigenschaften. Mit vielleicht 50 oder so Widgets, Struktur/Layout-Elemente inbegriffen.

Insofern bleibe ich dabei: man dekompositioniert Anwendungen in kleine, ueberschaubare Eigenheiten. Das die Gesamthierarchie dann gross wird - geschenkt.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

__deets__ hat geschrieben:Du bist doch derjenige der hier von riesigen Hierarchien redet, die dann unwartbar werden. Der gezeigte Code hier faellt offensichtlich nicht unter diese Kategorie.
Bei diesem Code habe ich noch eine Kleinigkeit vergessen, mämlich dass da noch eine Message im Labelframe definiert war. Ja macht ja nichts. Im Original DynTkInter Script sieht er so aus, aber nur der von Modules.py also ohne Toplevel und Menu:

Code: Alles auswählen

Frame('Toolbar',**{'link': 'guidesigner/Toolbar.py'})
Frame('GuiFrame',**{'link': 'guidesigner/GuiFrame.py'})
LabelFrame('WidgetPath',**{'text': ' Path: ','labelanchor' : 'w','bd' : 0})
goIn()

Message('message_path',**{'bg': '#ffffa0','anchor': 'nw'})
pack(anchor='w', fill='x', expand=1)

### CODE ===================================================

# ...

### ========================================================

goOut()

widget('Toolbar').pack(fill='x')
widget('WidgetPath').pack(anchor='w', fill='x')
widget('GuiFrame').pack(anchor='nw')
Den Code habe ich hier mal nicht mit gepostet, war eh nur die Ausgabe des Widget Pfades in dem gelben Streifen oben.

Programmierung innerhalb solchen Scripts benutzt wahrscheinlich außer mir niemand und will wahrscheinlich auch niemand. Aber XML habe ich gehört, hätte man gerne. Ich habe auch schon darüber nachgedacht, ob auf Pygubu Art oder ob man qt Kompatibilität erreichen könnte. Wahrscheinlich nicht ganz. Man könnte ja mal Pygubu XML versuchen und danach mit den Erfahrungen daraus an qt Kompatibilität denken.

Im Prinzip ist das von der Struktur so aufgebaut wie ein XML File, nur dass es ausführbarer Python Code ist. Und dann gibt es noch zwei spezielle Properties: 'link' heißt, auch das folgende GUI Script wird nachladen. Und ### CODE heißt, dass das ein nicht generierter Code ist, den der Programmierer dazu geschrieben hat, sozusagen eine property Code. Dieser Code wird bei normalem Start ausgeführt. Wenn das Modul im GUI Designer geladen wird, wird der Code je nach Art des Ladens nicht ausgeführt oder ausgeführt. Beim Laden im GUI Designer wird dieser Code aber als Property mitgeladen und beim Abspeichern auch wieder mit abgespeichert und kann in einem Text Widget editiert und gar zur Laufzeit ausgeführt werden. Naja, ich schreibe nur manchmal einen Kommentar rein, wenn ich im Menü etwas ändern will. Das habe ich nämlich nicht unterteilt und kann dann im Programmeditor die Stelle des betreffenden Codes leicht finden, indem ich dann nach dem Kommentar suche.

Wenn man dieses Modul ausführt, erhält man die statische GUI des GUI Designers mit Ausnahme einiger Widgets, die kein Layout haben:

Bild

Und wenn man das exportiert und mit dem Modul builder sich die IDs der Widgets ausgeben läßt, sind es 212 Widgets. Exportierter reiner GUI Code ergibt das ein GUI File von 966 Zeilen. Das Menü ist da nicht dabei.

Jetzt verstehe ich nicht, warum das jetzt wieder nichts Komplexes sein sollte. Und es macht überhaupt keinen Unterschied, ob eine GUI von der Source her in ganz kleine überschaubare Zeile zerteilt ist. Das ist gut, damit man sich gut zurechtfindet. Trotzdem erhält man mit builder.py die IDs der kompletten GUI. Denn trotz Zerteilung in der Source wie auch immer, es gibt nur eine GUI und das ist die tkinter GUI.
__deets__ hat geschrieben: Meine Antworten wenden sich meistens gegen von dir getaetigte Aussagen, Tools wie pygubu oder die Verwendung von nicht-code basierten Formaten wie XML und JSON zur Erstellung von GUIs waeren unbrauchbar weil nicht profesionell. Das entspricht nicht den Erfahrungen die ich persoenlich mit einer grossen, von Millionen Usern benutzen Anwendung, und viele andere hier und da draussen in der Welt gemacht haben.
Habe ich etwas von unprofessionell geschrieben? Kann ich mich nicht daran erinnern. IDs sind nichts Schlechtes. Es kommt aber darauf an, wie sie eingesetzt werden. Für einen guten Programmierer sind solche IDs kein Problem, denn er weiß, dass man nicht kreuz und quer auf alles zugreifen soll. Er implementiert wohl definierte Interfaces. Er benutzt auch Message Interfaces.

Ganz schlecht: über Ebenen hangeln. Von einem Submenü über die Root bis irgendwo in eine tiefe GUI Ebene
Besser: IDs benutzen
Noch besser: dabei wohl definierte Interfaces benutzen
Am Besten: Message Interfaces benutzen

Und ich weiß aus kürzlich gemachter Erfahrung, dass jemand der angefangen hat sich in Python und GUI einzuarbeiten und eine kompexe GUI implementieren möchte, am Besten fährt, wenn man ihm keine IDs zur Verfügung stellt, mit denen er kreuz und quer auf alles zugreifen kann. Wenn man die Anfänge gesehen hat, denkt man Null Chance, dass der eine komplexe GUI fertig bringt. Man überzeugt ihn, dass es keine gute Idee ist, sich von Menüs irgendwo in die GUI zu hangeln und schlägt ihm den Eventbroker vor. Der dabei benützte Eventbroker gibt auch Meldungen aus, nämlich, dass der Empfänger noch nicht existiert und dazu auch die Message. Und dann kann man sich freuen, weil man sieht, dass eine Komponente schon tut und fertig ist. Lediglich der Empfänger ist noch nicht implementiert. Bei Methodenaufruf, müßten diese schon existieren und man müßte auch wissen wo. Bei Messages muß man sich darum nicht kümmern sondern nur die Message senden.
__deets__ hat geschrieben: Wenn du ein Beispiel fuer eine grosse Qt-Anwendung sehen willst, die recht offensichtlich ihre hochkomplexe UI in viele, viele, viele kleinere , handhabbare Stuecke zergliedert, kann ich nur empfehlen einen Blick auf LibreCAD/QTCAD zu werfen:

https://github.com/LibreCAD/LibreCAD/tr ... c/ui/forms

Die groesste UI-Datei darin ist qg_dlgtext.ui mit ~60KB ist nur *ein* Dialog - zum einstellen der Fonteigenschaften. Mit vielleicht 50 oder so Widgets, Struktur/Layout-Elemente inbegriffen.

Insofern bleibe ich dabei: man dekompositioniert Anwendungen in kleine, ueberschaubare Eigenheiten. Das die Gesamthierarchie dann gross wird - geschenkt.
Ich stimme mit Dir da völlig überein. Ich habe auch gesehen, dass auch unerfahrene Programmierer dann das gerne tun, wenn sie sehen, dass das auch ganz ohne Probleme geht. Nur in den Modulen den Eventbroker importieren und dann kann gesendet und empfangen werden, ohne dass etwas anders wäre. Und der Code ist dann in kleine überschaubare Einheiten zerteilt. Und man zerteilt auch die GUI weil man dann besser sieht, welcher GUI Teil zu welchem Code gehört.

Also IDs sind wirklich nichts Schlechtes, aber in den Händen eines unerfahrenen Programmierers führen sie leicht zu nicht mehr durchschaubarem Code, weil dieser dann nicht an wohl definierte Interfaces denkt, wenn er ganz einfach quer Beet auf alle Widgets zugreifen kann.
Antworten