tkinter GUI Designer

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

spaghetticode hat geschrieben:[OT]Bekommt außer mir eigentlich niemand Kopfschmerzen?[/OT]
Man muss dabei noch bedenken, was geschieht, wenn das erste load_Script ein Script lädt, das weitere load_Scripts enthält. Im Nu sind da 24 Scripts baumartig geladen und ausgeführt.

Aber diese Funktion benütze ich nicht beim DynTkInter Format, denn da ist das eine Config Option (natürlich beim GUI Designer editierbar):

Code: Alles auswählen

	Frame('CreateFrame',link="guidesigner/CreateFrame.py")
	grid(sticky='n',row='0')
Wenn man die GUI speichert, dann muss man sie nicht ganz von der root aus speichern, sondern kann einen Frame auswählen. Dann wird nur ab dem Inhalt dieses Frames gespeichert und dieser Link automatisch gesetzt. Damit hat man dann getrennte Scripts und kann so ein großes Programm in Einzelteile zerlegen. Wenn man allerdings so eine GUI im GUI Designer geladen hat, kann man so einen Link wieder auf leer setzen und hat dann die Scripte wieder vereint.

Die Scripte enthalten aber nicht nur die GUI (das sind die Widgets, die auch vom GUI Designer wieder generiert werden), sondern auch CODE. Die Codeteile werden im GUI Designer beim Reinladen ausgeschnitten und in einer Queue gespeichert. Anstatt der Code Teile wird dann eine Funktion eingefügt. Wenn das Script dann gestartet wird, werden die ausgeschnittenen Code Teile durch die eingefügte Funktion im zugehörigen parent (container widgets haben eine CODE Variable) gespeichert. Wenn man die GUI geändert hat und speichert, speichert man nicht nur die neu generierte GUI sondern auch den Code dazu wieder ab, Also kann man das ganze Programm beliebig stückeln und zusammensetzen.

Allerdings für normales tkinter habe ich noch keine Lösung für solche Umverteilungen. Denn da wären bei mehreren Containern Unterfunktionen erforderlich und dann würde der Code immer weiter einrutschen. Und das passt dann leider nicht mehr zusammen, wenn man das anderes einteilt. Müßte man händisch die Einrückung berichtigen.
Zuletzt geändert von Alfons Mittelmeyer am Freitag 28. August 2015, 17:30, insgesamt 1-mal geändert.
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

So viel Aufwand nur um grundlos etwas nicht robustes zu bauen, dessen Funktionalität durch import abgebildet wird. Hut ab.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

sparrow hat geschrieben:So viel Aufwand nur um grundlos etwas nicht robustes zu bauen, dessen Funktionalität durch import abgebildet wird. Hut ab.
Sorry, was soll import bringen. Ich lade Code, schneide Teile davon ab, füge etwas ein, generiere etwas dazu. Und das soll ich dann in ein File abspeichern, damit ich es dann importiere? Und wie verhält sich imp.reload, wenn ich dann das eine Script durch andere ersetze. Geht das auch so schön rekursiv? Ich kann es ja irgendwann mal probieren und compile, exec ersetzen durch open, write, close, imp.reload

Und viel Aufwand? Das waren drei Zeilen: compile, exec, main
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Alfons Mittelmeyer hat geschrieben:Das Modul kann ich hier nicht hinzufügen, denn etwa 2000 Zeilen wären etwas zuviel.
Dies sind jetzt nicht so übermässig viele wie du meinst. Mit diesen wenigen Zeilen bringst du unseren 'Pastepin' noch lange nicht zum überlaufen.

Gruss wuf :wink:
Take it easy Mates!
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: Python-Code zu generieren und diesen wieder versuchen zu parsen, um ihn weiter zu verarbeiten, ist nicht robust, vor allem nicht, wenn da noch jemand drin rumeditiert. Willst Du raten, was jetzt zur GUI gehört und was nicht? Und was meinst Du mit Queue? Das sicherste, beste und einfachste ist die GUI in einem geeigneten Dateiformat zu speichern, zu bearbeiten und im Programmcode zu laden.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi Alfons

Das folgende beschriebene:
Alfons Mittelmeyer hat geschrieben:
sparrow hat geschrieben:Ich würde sagen: zeig erst einmal das Programm, dann können wir uns über Vorstellungen bei der Bedienung unterhalten. Und damit meine ich keine Screenshots.
Sorry, wie soll ich ein Programm zeigen, das noch nicht existiert? Das war ein manuelles Gridlayout mit dem GUI Designer. Aber wenn Du das unbedingt sehen willst:

Code: Alles auswählen

Frame(bg='#a0a0a0',height='30',width='2').grid(row='1')
Frame(bg='#a0a0a0',height='2',width='60').grid(column='1',row='0')
Frame(bg='#a0a0a0',height='30',width='2').grid(column='2',row='1')
Frame(bg='#a0a0a0',height='2',width='60').grid(column='3',row='0')
Frame(bg='#a0a0a0',height='30',width='2').grid(column='4',row='1')
Frame(bg='#a0a0a0',height='2',width='60').grid(column='5',row='0')
Frame(bg='#a0a0a0',height='30',width='2').grid(column='6',row='1')
Frame(bg='#a0a0a0',height='2',width='60').grid(column='1',row='2')
Frame(bg='#a0a0a0',height='2',width='60').grid(column='3',row='2')
Frame(bg='#a0a0a0',height='2',width='60').grid(column='5',row='2')
Frame(bg='#a0a0a0',height='30',width='2').grid(row='3')
Frame(bg='#a0a0a0',height='30',width='2').grid(column='2',row='3')
Frame(bg='#a0a0a0',height='30',width='2').grid(column='4',row='3')
Frame(bg='#a0a0a0',height='30',width='2').grid(column='6',row='3')
Frame(bg='#a0a0a0',height='2',width='60').grid(column='1',row='4')
Frame(bg='#a0a0a0',height='2',width='60').grid(column='3',row='4')
Frame(bg='#a0a0a0',height='2',width='60').grid(column='5',row='4')
um die Augen zu schonen hier noch in grafischerForm:
Bild

Gruss wuf :wink:
Take it easy Mates!
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:@Alfons Mittelmeyer: Python-Code zu generieren und diesen wieder versuchen zu parsen, um ihn weiter zu verarbeiten, ist nicht robust, vor allem nicht, wenn da noch jemand drin rumeditiert. Willst Du raten, was jetzt zur GUI gehört und was nicht? Und was meinst Du mit Queue? Das sicherste, beste und einfachste ist die GUI in einem geeigneten Dateiformat zu speichern, zu bearbeiten und im Programmcode zu laden.
Also Code parsen, tue ich nicht. Das sollt man wirklich vermeiden. Aber spezielle Markierungen am Code kann man bearbeiten, etwa:

Code: Alles auswählen

### NAME Cancel
Cancel = Button(parent,text="""Quit""")
Cancel.grid(column='2',sticky='e',row='3')
Das wäre reiner tkinter code. Gut für eigene kleine Programme oder Programmbeispiele in einem Script. Ein Zugriff von außen, nachdem die GUI aufgebaut ist, ist aber nicht möglich. Diese Kommentarzeile '### NAME Cancel' kann ich jederzeit finden, ohne zu parsen und kann sie ersetzten etwa durch: gui.preset_name('Cancel'). Wenn ich dabei eine spezielle library verwende mit modifiziertem Button Befehl, kann dann dieser Buttonbefehl den zuvor gesetzten Namen übernehmen, sodass dann über diesen Namen der Zugriff auf das Widget auch später noch möglich ist. Wenn ich gleich 'gui.preset_name('Cancel')' im Source Code hätte, würde manchem nicht gefallen, weil er dazu noch eine spezielle Library braucht, und er nur normales tkinter ohne späteren Zugriff will. Durch solche speziellen Kommentare braucht man keine verschiedenen Source Versionen für unterschiedliche Zwecke oder Umgebungen. Dieser Kommentar würde auch erlauben, dass der GUI Designer den richtigen Namen für das Widget bekommt, wenn man mit ihm etwa die GUI editiert und dabei diese Source verwendet anstelle einer getrennten gespeicherten Source in einem anderen Format.

Also parsen ist so etwas wohl nicht. Und warum soll nicht jemand darin rumeditieren? Warum soll man nicht Widgets auch ohne den GUI Designer hinzufügen können. Ich finde es praktisch, wenn man darin rumeditieren kann, wie man will. Für DynTkInter gibt es auch die Möglichkeit, zusätzlichen Code mit hineinzuschreiben und ihn entsprechend zu markieren, sodass er etwa im GUI Designer nicht ausgeführt wird oder wieder mit abgespeichert wird. Das ist auch im tkinter Format möglich, nur wenn man dann mit dem GUI Designer in Module splittet, würden die Einrückungen nicht mehr passen. Hier ein Beispiel:

Code: Alles auswählen

def main(parent):

    ### NAME myframe
    myframe = Frame(parent)
    
    def main(parent):
    
        ### NAME mybutton
        mybutton = Button(parent)
        mybutton.pack()
        
        ### CODE ===============
        
        # hier etwa Callback definieren 
        
        ### ======================
    
    main(myframe)
    myframe.pack()
    
    # hier etwa weitere Widgets
    
    ### CODE ==============
    
    # hier Code zum ersten Parent
    
    ### ====================
Hier sucht man nach '### CODE #' und schneidet dann die Zeilen einschließlich '### =' aus und speichert diese Abschnitte in einer Queue. Außerdem ersetzt man dann diese Code Blöcke, etwa durch 'gui.remember_code()', etwa so:

Code: Alles auswählen

def main(parent):

    gui.preset_name('myframe')
    myframe = Frame(parent)
    
    def main(parent):
    
        gui.preset_name('mybutton')
        mybutton = Button(parent)
        mybutton.pack()
        
        gui.remember_code(parent)
		
    main(myframe)
    myframe.pack()
    
    # hier etwa weitere Widgets
    
    gui.remember_code(parent)
Das 'gui.remember_code' holt sich an der betreffenden Stelle den Code Abschnitt aus der Queue und übergibt ihn zum Merken an den jeweiligen Parent. Später kann dann die modifizierte GUI (Widget Definitionen) mitsamt dem gemerkten Code wieder so abgespeichert werden. Normalerweise könnte man auch die Einrückungen beim Splitten richtig behandeln, aber es gibt da auch diese Strings mit """. Und wenn man das dann richtig berücksichtigen wollte, dann müßte man doch ein klein wenig parsen. Soll ich, oder soll ich nicht, ist die Frage?
Benutzeravatar
sparrow
User
Beiträge: 4193
Registriert: Freitag 17. April 2009, 10:28

Natürlich parst du Code. Oder du lässt es eval oder compile für dich machen.
So wie ich dich hier verstehe, änderst du den Code im Programm auch noch, bevor du ihn compilierst. Das ist, vorsichtig ausgedrückt, seltsam.

Wie bereits mehrmals gesagt, zeig das lauffähige Programm. Die Codeschnipsel die zu hier zeigst sind eher missglückt und du versuchst sie in einem Kontext zu erklären (deine dynTkinter-Bibliothek und die Funktionen denes Gui-Builders), den niemand außer dir kennt.
Am veröffentlichten Produkt können wir dann sehen, was du wirklich meinst.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

sparrow hat geschrieben:Natürlich parst du Code.
Ich ersetze Markierungen im Code und parse keinen Code. Code parsen wäre Python Syntax analysieren.
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: um es exakt auszudrücken: Du parst einen MetaCode der per Kommentare über normalen Python-Code drübergestülpt wurde, erzeugst daraus zwei Versionen, eine GUI-Designer-Version und eine Produktivversion. Neben dem unglaublich komplizierten und fehleranfälligen Handling erreichst Du zudem noch, dass jeder, der sich nicht an Deine Konventionen hält mit unvorhersehbarem Verhalten Deines Designers belohnt wird.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:@Alfons Mittelmeyer: um es exakt auszudrücken: Du parst einen MetaCode der per Kommentare über normalen Python-Code drübergestülpt wurde, erzeugst daraus zwei Versionen, eine GUI-Designer-Version und eine Produktivversion. Neben dem unglaublich komplizierten und fehleranfälligen Handling erreichst Du zudem noch, dass jeder, der sich nicht an Deine Konventionen hält mit unvorhersehbarem Verhalten Deines Designers belohnt wird.
Niemand wird gezwungen, dieses extra tkinter Format zu nehmen, mit dem ich glaubte, Euch einen Gefallen zu tun. Dann lasse ich eben einfach dieses Format und habe dann keine Arbeit damit. Wenn etwa jemand zu einer Generierten GUI noch ergänzen möchte:

Code: Alles auswählen

ConfigOptions.grid_remove()
DetailedLayout.grid_remove()
Und das sollte man nicht erst in der Geschäftslogik tun, nachdem die GUI steht. Dann soll er es eben in einem extra Source File tun, mit Hilfe einer Interface Funktion

Code: Alles auswählen

def main(parent):
    gui.widget(parent,"ConfigOptions").grid_remove()
    gui.widget(parent,"DetailedLayout").grid_remove()
Zufrieden?

Bzw. statt Sourcefile-Angabe sollte ich auch Funktionsangabe ermöglichen, wie etwa 'yourmodule.yourfunction'.
Sirius3
User
Beiträge: 17750
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: Du willst hier niemanden verstehen :evil: .
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:@Alfons Mittelmeyer: Du willst hier niemanden verstehen :evil: .
Also was wollt Ihr nun? Entweder Code ist mit dabei, dann muss ich ihn so sichern. Oder er ist extra, dann brauche ich ihn nicht sichern.
Benutzeravatar
snafu
User
Beiträge: 6740
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

Das Forum befiehlt - Alfons folgt! :twisted:
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:Neben dem unglaublich komplizierten und fehleranfälligen Handling erreichst Du zudem noch, dass jeder, der sich nicht an Deine Konventionen hält mit unvorhersehbarem Verhalten Deines Designers belohnt wird.
Das Verhalten ist nicht unvorhersehbar sondern genau festgelegt. Wenn jemand den Kommentar ### NAME ButtonOK wegmacht, dann heißt dieser Button nicht mehr 'ButtonOK' sondern 'button'.

Wenn jemand den Kommentar ### CODE == nicht macht, dann wird dieser Code ausgeführt und kann nicht vom GUI Designer gespeichert werden. Wenn dieser Code etwa ein grid_remove() für einen Widget macht, dann hat er das eben gemacht und man muss das widget durch grid() wieder setzen. Wenn der Code ein destroy() macht, dann ist das Widget weg. Aber selber schuld, wenn man auf einen Button seiner Anwendung drückt, die ein Widget löscht. Wenn man den Kommentar ### CODE nicht drin hat aber Code hineingeschrieben hat und dann nach GUI Editierung so dumm ist, die vorige GUI Version, die den zusätzlichen Code enthält, zu überschreiben und wer dann auch nicht noch eine Sicherung davon hat, ja dem kann man dann nicht mehr helfen.

Es gibt schließlich auch die Möglichkeit, den veränderten Teil der GUI händisch in die bisherige GUI zu kopieren. Dann passiert dem Code auch nichts.
Benutzeravatar
wuf
User
Beiträge: 1529
Registriert: Sonntag 8. Juni 2003, 09:50

Hi Alfons

Ich glaube wir warten mal ab bis du dein abstraktes Wunderwerk vollbracht hast. Ich nehme an du wirst es irgendwann veröffentlichen. Weihnachten kommt ja bald. Oder es wird sich mit der Zeit aus deinem Speichermedium verflüchtigen. Wünsche dir noch viel Erfolg.

Gruss wuf :wink:
Take it easy Mates!
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

snafu hat geschrieben:Das Forum befiehlt - Alfons folgt! :twisted:
Nö, folgen tue ich nicht. Für mich mache ich schon das tkinter Format. Denn es ist für mich praktisch, wenn ich nicht händisch das DynTkInter Format umschreiben muss, um hier ein tkinter Bespiel zu posten. Es handelt sich hierbei um eine zusätzliche Lade und Speicheroption. Die Frage ist, soll ich die in die offizielle Version mit hineinnehmen, gar nicht hineinnehmen oder hineinnehmen aber disablen. Wenn es doch jemand haben will, kann ich ihm ja sagen, wie er es enablen kann.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

jens hat geschrieben:
Sirius3 hat geschrieben:"release early, release often".
+1


@Alfons Mittelmeyer: Ich will nicht sagen, das pygubu das non+ultra ist.
Ich frage mich nur, ob es nicht Sinnvoller ist, sich bei dem Projekt einzubringen, anstatt das Rad zum x-ten Mal neu zu erfinden ;)

Was pygubu auch fehlt ist eine schöne Möglichkeit grid layouts *bequem* zu entwerfen. Denn auch dort muß man mit Zahlen hantieren.
@jens Mich bei pygubu einbringen, macht keinen Sinn, da bei pygubu kein schönes Gridlayout möglich ist.

Ich hab es jetzt - allerdings noch nicht für die Zeilen einzeln anpassbar. Siehe: https://github.com/AlfonsMittelmeyer/py ... -messaging
Antworten