tkinter GUI Designer

Fragen zu Tkinter.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Ich habe einen GUI Designer für tkinter geschrieben. Veröffentlicht ist er noch nicht. Er beruht auf eine tkinter Erweiterung meinerseits, die ich DynTkInter genannt habe. Um keine Definitionen zu haben, hatte ich zuerst mit Stack gearbeitet und aus Strings kompilierten Code verwendet. Jetzt habe ich den GUI Designer umgeschrieben. Da jetzt jedes Modul aus einer Funktion main besteht, benütze ich jetzt lokale Definitionen, brauche keinen Stack und kompiliere nicht mehr aus Strings. Außerdem habe ich das Programm in 24 Module zerlegt, was die Übersicht über den Quellcode fördert.

Aus einigen Posts hier hatte ich entnommen, wie man sich den Zugrifff auf die GUI wünscht. Ich soll bewährte Zugriffsmethoden verwenden, hatte man hier geschrieben. Allerdings, diese 'bewährten' Zugriffsmethoden sind nur Behelfszugriffsmethoden. Wenn ein GUI Designer nicht in Python sondern in C oder etwas anderem geschrieben ist, dann existieren keine Python Module, die man direkt verwenden kann. Daher muss es dann solche Behelfszugriffsmethoden geben.

Eigentlich ist es ja etwas umständlich eine bereits aufgebaute GUI für Widgets zu haben und dann noch einen weiteren GUI Teil mit den Callbacks für die Widgets zu programmieren. Besser ist es vielleicht in den Modulen oder Programmteilen, in denen sich die Widgets befinden, auch die Callbacks zu programmieren.

Wie auch immer, ich stelle beide Zugriffsarten zur Verfügung. Die erste ist mit GUI wie auch immer und einer Zugriffsmethode. Die könnte etwa so aussehen:

Code: Alles auswählen

myX_Spinbox = gui.get_widget("//CreateAndLayout/LayoutShort/LayoutShortShowHide/PlaceLayout/X")
OK so? Außerdem werden auch weitere Informationen oder Attribute gewünscht. An welche denkt Ihr da?
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Ich würde erstmal generelle Informationen haben wollen. Am wichtigsten erscheint mit doch: Wie klickt man sich denn nun seine GUI zusammen?!?

Und generell: Ich würde einen GUI Designer erst mal nur dazu verwenden mit ein Grundgerüst zu generieren. Also etwas, was dann vollkommen unabhängig vom GUI Designer verwendbar ist. Mit einfach hilft eine GUi visuell zusammen zu stellen, statt code zu schreiben und nachsehen...

Denn: Die Frage ist halt (egal. welches Tool man nimmt): Wie lange wird wohl das jeweilige Tool weiter entwickelt?!?
Es gibt nämlich zig GUI Designer und gefühlt sind 95% davon veraltete, nicht mehr gewartete Projekte... Hätte ich nun so einen genommen, der das laden "per XML/JSON oder was auch immer" erlaubt, dann hätte ich irgendwann ein Problem.


Also erstmal zeigen, wie man sich denn die Sachen zusammen bauen kann. Dann kann man über speichern/laden nachdenken.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Und super wäre es, wenn ein Tool wirklich guten, sauber Aufgeteilen GUI-Code generieren würde.

Also:

* keine Sternchen-Importe
* Grid-Manager nutzten
* nicht alles in einer Klasse packen -> siehe: http://www.python-forum.de/viewtopic.php?f=18&t=36886
* Py 2 und 3 kompatibler Code erzeugen

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

...und noch superiöser wäre es, wenn man bestehenden Code einlesen könnte. Aber das wird schwierig... Es gab mal Tools, die haben Code Bereiche mit Kommentaren versehen. In diesen darf dann eigener Code rein. Ist aber nicht das Gelbe vom Ei ;)

Toll wäre es, wenn GUI-Code und eigentliches Programm so getrennt wird, das es zu keiner Mischung kommen kann. Wie das genau aussehen könnte, weiß ich nicht genau. Vielleicht: GUI-Klasse hat vor gefertigte NotImplemented() methoden. Dann von dieser erben und die eigentlichen Programm aufrufe schaffen?!?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
sparrow
User
Beiträge: 4164
Registriert: Freitag 17. April 2009, 10:28

Ich persönlich würde auch niemals im GUI-Designer sagen, welche Aktion mit einem Widget verbunden ist.
Aus verschiedenen Gründen baue ich meine Qt-GUIs mit deren (wie ich finde außerordentlich guten) Designer, aber die Kopplung der Signale an die Bedienelemente nehme ich ausschließlich in meinem Code vor. Im Designer benenne ich den Kram nur.
BlackJack

@jens: Ich würde ja gar nicht erst Code generieren sondern eine Datendatei speichern und ein Modul zur Verfügung stellen um die dann live in Widget-Objekte umzusetzen. Also wie das `uic`-Modul von PyQt.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

BlackJack hat geschrieben:@jens: Ich würde ja gar nicht erst Code generieren sondern eine Datendatei speichern und ein Modul zur Verfügung stellen um die dann live in Widget-Objekte umzusetzen. Also wie das `uic`-Modul von PyQt.
Das theoretisch schön, aber praktisch nicht so ganz:

+ man sieht schlechter, was man denn so an kram hat
+ der editor sieht keine Objektnamen
+ wenn das Projekt tot ist, hat man ein Problem

Wenn sich das wirklich breit etabliert, dann sieht zumindest der letzte Punkt anders aus. Aber wie gesagt, wieviele tote Ansätze gibt es wohl schon?!?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Und mit code generieren, meine ich: statischen code generieren. Der unabhängig funktioniert und wartbar ist,selbst wenn das Designer Projekt schon längst Vergangenheit ist...

Oder soll das Projekt für sowas gar nicht da sein?!? Dann finde ich den Namen missverständlich...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

@jens: Warum sieht man schlechter was man so hat? Bei einfachen GUIs lassen sich zum Beispiel Qt's *.ui-Dateien (XML) eigentlich noch ganz gut lesen, und bei komplexeren Sachen will man weder die noch Quelltext wirklich lesen. Objektnamen sieht der Editor bei *.ui-Dateien auch nicht, trozdem würde ich deswegen keinen Quelltext daraus generieren wollen. Und welches Problem hat man wenn das Projekt tot ist? Das Modul zum laden der Datendatei muss doch nur weiterhin funktionieren. Da sehe ich als einziges Problem das Python sich inkompatibel ändert, was mal abgesehen von 2 vs. 3 nicht wirklich passiert ist. Und Tk/Tkinter ändert sich seit Jahrzehnten nicht bzw. im Schneckentempo. So ein Datenformat hätte zusätzlich den Vorteil das man Laderoutinen für andere Programmiersprachen schreiben kann, so wie man Qt's *.ui-Dateien ja auch in verschiedenen Programmiersprachen nutzen kann. Format und Lademodul dürften bei Tkinter auch nicht sooo kompliziert werden. Ich könnte mir vorstellen dass das gar nicht so umfangreich wäre.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

BlackJack hat geschrieben:@jens: Warum sieht man schlechter was man so hat? Bei einfachen GUIs lassen sich zum Beispiel Qt's *.ui-Dateien (XML) eigentlich noch ganz gut lesen, und bei komplexeren Sachen will man weder die noch Quelltext wirklich lesen. Objektnamen sieht der Editor bei *.ui-Dateien auch nicht, trozdem würde ich deswegen keinen Quelltext daraus generieren wollen.
Für kleine GUIs wird man so oder so wenig Probleme haben... Bei Komplexeren Sachen, denke ich aber schon, das der Quellentext hilfreich ist. Halt gerade, wegen den Objektnamen.

Aktuell haben wie ja eh nix in Richtung .ui Dateien (außer halt, von toten Projekten)... Also hat man aktuell eh nur die Wahl zwischen Quellentext oder ein ganz anderes Framework nehmen...
BlackJack hat geschrieben:Und welches Problem hat man wenn das Projekt tot ist? Das Modul zum laden der Datendatei muss doch nur weiterhin funktionieren.
Und was ist, wenn man die GUI ändern will? die .ui Dateien per Hand anpassen oder doch alles neu machen?
BlackJack hat geschrieben:Und Tk/Tkinter ändert sich seit Jahrzehnten nicht bzw. im Schneckentempo.
Naja, das hat Vor- und Nachteile :wink:
BlackJack hat geschrieben:So ein Datenformat hätte zusätzlich den Vorteil das man Laderoutinen für andere Programmiersprachen schreiben kann, so wie man Qt's *.ui-Dateien ja auch in verschiedenen Programmiersprachen nutzen kann.
Das wäre ein Vorteil. Aber für mich persönlich, nicht relevant :P
BlackJack hat geschrieben:Format und Lademodul dürften bei Tkinter auch nicht sooo kompliziert werden. Ich könnte mir vorstellen dass das gar nicht so umfangreich wäre.
Vermutlich steckt der Teufel im Detail :?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
BlackJack

@jens: Ob man nun eine *.ui-Datei von Hand ändert oder generierten Quelltext sollte eigentlich nicht so viel unterschied sein. Mit dem Vorteil das man die *.ui-Datei auch einfach mit eigenen Programmen sicher und einfach automatisiert bearbeiten kann wenn die zum Beispiel in XML oder JSON ist.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

War abens weg und konnte nicht eher antworten. Eines ist schon mal schon mal sicher, es kann nicht sein, dass das Projekt einmal nicht mehr gewartet wird. Denn im Unterschied zu anderen GUI Designern ist dieser in Python programmiert. Kann jeder verändern und anpassen, wenn er will. Trennung GUI und Code wird erreicht durch unabhängige GUI und Zugriff.

Den Zugriff wollen wir mal ein wenig verfeinern. Das alles anzugeben ist doch nicht so gut:

Code: Alles auswählen

myX_Spinbox = gui.get_widget("//CreateAndLayout/LayoutShort/LayoutShortShowHide/PlaceLayout/X")
Daher führen wir einen weiteren Parameter ein, nämlich den Parent. Ohne Angabe des Parents zählt die Angabe von der Root aus, das wäre dann so:

Code: Alles auswählen

myX_Spinbox = gui.get_widget("CreateAndLayout/LayoutShort/LayoutShortShowHide/PlaceLayout/X")
Wenn man aber bereits die GUI Callbacks für den Inhalt eines Container Widgets macht, dann hat man auch den Parent. In diesem Fall also für 'PlaceLayout'. Dann reduziert sich die Angabe folgendermaßen:

Code: Alles auswählen

myX_Spinbox = gui.get_widget("X",parent)
Desweiteren bieten viele GUI Designer nur die normalen Widgets an. Aber man möchte auch eigene abgeleitete Widget Klassen implementieren. Da habe ich mir gedacht, dass man im GUI Designer solche widgets als placeholder widgets markiert. Wenn man etwa eine abgeleitete Buttonklasse verwenden will, dann wird das Widget im GUI Designer als Button dargestellt, aber entsprechend markiert. Beim Aufbau der GUI später wird das widget nicht dargestellt. Das geschieht dann erst, wenn man ein Objekt seiner eigenen Klasse kreiert hat und ihm dann die Attribute (config und layout) des Placeholderwidgets zuweist, etwa:

Code: Alles auswählen

mybutton = MyButton(parent)
gui.set_attributes(mybutton,'X',parent)
Vielleicht fällt Euch ja ein besserer Funktionsname ein. Ein Problem dabei bereitet das Packlayout. Wenn nämlich die normalen tkinter Widgets vorher gepackt werden und das abgeleitete Widget erst später, dann würde es an der falschen Stelle stehen. Da muss ich noch etwas implementieren, dass das Packlayout für alle Widgets im betreffenden Container erst vorgenommen wird, wenn alle Placeholder Widgets - gar keine Widgets, sondern nur Objekte mit dazu gespeicherten Attributen - mit Packlayout durch die verwendeten abgeleiteten eigenen Widgets ersetzt wurden. Würde das reichen? Wenn Ihr weitere Informationen über die Widgets braucht - die kann man auch mit ganz normalen tkinter Funktionen gewinnen - etwa welches Layout, kann ich gerne noch Funktionen dafür bereitsstellen. Das Layout erhält man etwa durch try: pack_info(), try: grid_info(), try: place_info(). Aber normalerweise weiß man selber, was man genommen hatte.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Wenn ich dich richtig verstehe, willst du Teile der GUI ladbar machen. Über dieses gui.get_widget()

Das erinnert mich ein wenig an meinem Beispiel von http://www.python-forum.de/viewtopic.ph ... 67#p281867 mit den tk.LabelFrame Klassen.
Dort hab ich es ja auch geändert, das sich das "widget" selber Plaziert, siehe: http://www.python-forum.de/viewtopic.ph ... 22#p282122

Ich denke, das ist eine Sauberere Lösung.


Außerdem hab ich den Eindruck, das grid() immer vor zu ziehen ist.


Aber nochmal: Wie sieht denn der Teil aus, mit dem man die Daten für gui.get_widget() zusammen stellt?!? Das ist IMHO viel wesentlicher.

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

jens hat geschrieben:Wenn ich dich richtig verstehe, willst du Teile der GUI ladbar machen. Über dieses gui.get_widget()

Das erinnert mich ein wenig an meinem Beispiel von http://www.python-forum.de/viewtopic.ph ... 67#p281867 mit den tk.LabelFrame Klassen.
Dort hab ich es ja auch geändert, das sich das "widget" selber Plaziert, siehe: http://www.python-forum.de/viewtopic.ph ... 22#p282122

Ich denke, das ist eine Sauberere Lösung.


Außerdem hab ich den Eindruck, das grid() immer vor zu ziehen ist.


Aber nochmal: Wie sieht denn der Teil aus, mit dem man die Daten für gui.get_widget() zusammen stellt?!? Das ist IMHO viel wesentlicher.
Also gui.get_widget hast Du falsch verstanden. Hier wird nichts geladen, sondern das Widget ist da. Die Funktion liefert nur über den Namen eine Referenz auf das Widget.

Und wie das Teil ausschaut, kommt darauf an, wie man es generiert. Das wäre dann der zweite Zeil, nämlich dass es sich um Python Code handelt mit normalen Widgets. Und so könnte aussehen, was ich für mich temporär verwende:

Code: Alles auswählen

gui.preset_name("buttonOK")
buttonOK = tk.Button(parent,text="OK")
buttonOK.grid(row=2,column=0)
Und so wird vermutlich der Source Code aussehen, aus dem ich das mit preset_name oder wie auch immer temporär generiere:

Code: Alles auswählen

### NAME buttonOK
buttonOK = tk.Button(parent,text="OK")
buttonOK.grid(row=2,column=0)
Und da buttonOK eine lokale Variable war, die nicht mehr existiert, wenn man seine Callbacks nicht gleich da einbaut, sondern erst nach Aufbau der GUI, gibt es mit gui.get_widget('buttonOK',parent) einen Zugriff auf die Referenz mittels Namens
Zuletzt geändert von Alfons Mittelmeyer am Donnerstag 27. August 2015, 08:43, insgesamt 4-mal geändert.
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Alfons Mittelmeyer hat geschrieben:Hier wird nichts geladen, sondern das Widget ist da.
Und wo bzw. wie wird ein Widget zusammen gesetzt?!?

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

jens hat geschrieben:
Alfons Mittelmeyer hat geschrieben:Hier wird nichts geladen, sondern das Widget ist da.
Und wo bzw. wie wird ein Widget zusammen gesetzt?!?
Du meinst, wie man das Widget im GUI Designer zusammenstellt?
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Ja, das ist doch der Kern eines GUI Designers...

GitHub | Open HUB | Xing | Linked in
Bitcoins to: 1JEgSQepxGjdprNedC9tXQWLpS424AL8cd
Sirius3
User
Beiträge: 17711
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: ich vermute, Du baust wieder einmal zu viel Magie um Dinge, die Python sowieso schon von sich aus kann. Aber ohne Code, kann ich nur vermuten:

Wenn Du ein verschachteltes Layout hast, würde ich das genau so in einem Baum abbilden:

Code: Alles auswählen

gui = load_widgets('mygui.json')
[...]
myX_Spinbox = gui.CreateAndLayout.LayoutShort.LayoutShortShowHide.PlaceLayout.X
Natürlich will das niemand so schreiben, aber irgendwo hat man ja schon den Parent:

Code: Alles auswählen

myX_Spinbox = parent.X
Das mit den abgeleiteten Klassen ist meiner Meinung nach auch zu kompliziert gedacht. Abgeleitete Klassen werden einfach vor dem Laden der GUI registriert:

Code: Alles auswählen

register_widget('MyButton', MyButton)
gui = load_widgets('mygui.json')
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

jens hat geschrieben:Ja, das ist doch der Kern eines GUI Designers...
Bräuchte einen Bilder Paste Dienst. Weiss aber keinen. Auf google Drive hatte ich unter Android ein gif geladen. Kann das aber unter Linux nicht ansehen: https://drive.google.com/file/d/0Bwsrqx ... sp=sharing
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:@Alfons Mittelmeyer: ich vermute, Du baust wieder einmal zu viel Magie um Dinge, die Python sowieso schon von sich aus kann. Aber ohne Code, kann ich nur vermuten:

Wenn Du ein verschachteltes Layout hast, würde ich das genau so in einem Baum abbilden:

Code: Alles auswählen

gui = load_widgets('mygui.json')
[...]
myX_Spinbox = gui.CreateAndLayout.LayoutShort.LayoutShortShowHide.PlaceLayout.X
Das kann man so nicht machen. Das ist ja so eine Art von Klassenbaum. Und wie löscht man da Verzeichnisse aus der Klasse wieder heraus? Etwa mit del? Das Prinzip ist ja Dynamik mit Hinzuladen von Teilen und Löschen von Teilen
Sirius3 hat geschrieben:Das mit den abgeleiteten Klassen ist meiner Meinung nach auch zu kompliziert gedacht. Abgeleitete Klassen werden einfach vor dem Laden der GUI registriert:

Code: Alles auswählen

register_widget('MyButton', MyButton)
gui = load_widgets('mygui.json')
Das muss ich mir noch überlegen wie das gemeint ist. Das sieht ja nach globalen Namen aus. Wäre natürlich auch machbar. Bei komplexer GUI mit vielen Elementen und Dazuladen und Löschen, kommt man aber mit globalen Namen bald ins Schleudern. Ach so, es sind Klassen und keine Widgets. Blick noch nicht recht durch.
Zuletzt geändert von Alfons Mittelmeyer am Donnerstag 27. August 2015, 11:33, insgesamt 1-mal geändert.
Antworten