'command' zwei Argumente übergeben?

Fragen zu Tkinter.
BlackJack

@Alfons Mittelmeyer: Ja globale Zustände sind etwas schlechtes in einem Programm da sie fehleranfällig sind und die Flexibilität einschränken. Persistenz auf der Festplatte ist etwas anderes, zumal dort durch Verzeichnisse auch Namensräume bestehen.

Gegen Code in Zeichenketten ist einzuwenden dass das nur ganz schlecht skaliert. Denn schon in der nächsten Ebene, wenn man in diesem Code in einer Zeichenkette wieder eine Zeichenkette mit Code haben möchte, wird das unhandlich und bei einer dritten Ebene unbenutzbar. Es geht hier nicht um Geschwindigkeit, sondern dass man bei zur Laufzeit kompiliertem Code in Zeichenketten offenbar mit der Ausdruckstärke der Sprache nicht zufrieden ist, oder damit nicht vernünftig umgehen kann. Und gerade Python ist dynamisch genug das man nur sehr selten auf solche Krücken zurückgreifen muss. Code-Fragmente aus Zeichenketten zu compilieren die über das ganze Programm verteilt sind, ist auch etwas anderes als ``import`` zu verwenden.

Den Vergleich mit dem Webbrowser habe ich nicht verstanden. Was hat Code in Zeichenketten mit URLs zu tun? Das sind zwei unterschiedliche Dinge. Das mit den immer mehr werdenen Klassen und Funktionen ist mir auch unverständlich, denn natürlich muss man bei vielen GUI-Elementen auch entsprechend Code schreiben der beschreibt wie sich diese Elemente verhalten sollen. Bei Dir wird das dann magischer, undurchsichtiger Code ohne Klassen und Funktionen. Na toll.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben: Den Vergleich mit dem Webbrowser habe ich nicht verstanden. Was hat Code in Zeichenketten mit URLs zu tun? Das sind zwei unterschiedliche Dinge. Das mit den immer mehr werdenen Klassen und Funktionen ist mir auch unverständlich, denn natürlich muss man bei vielen GUI-Elementen auch entsprechend Code schreiben der beschreibt wie sich diese Elemente verhalten sollen. Bei Dir wird das dann magischer, undurchsichtiger Code ohne Klassen und Funktionen. Na toll.
Ja gerade darum geht es, dass man wie bei einem Webbrowser Seiten lädt aber nicht HTML Seiten, sondern beliebige TkInter GUI Module. Diese kann man, da kein Parent angegeben werden muss in beliebige Frames plazieren. Und wenn man tausend mal verschiedene solche Module lädt und jedes Modul seine eigenen Variablen, Funktions- und Klassendefinitionen hat, dann werden diese Funktions- und Klassendefinitionen immer mehr und mehr und man kann sie nicht mit einem unimport wieder herauswerfen.

Wenn man einen Frameinhalt löscht, dann würde ich erwarten, dass dann damit auch die damit verbundenen Funktionen und Klassen wieder gelöscht werden. Aber das geht wohl nicht. Wenn man aber nur widgets hat und nicht mehr referenzierte Objekte, die aus Zeichenketten compiliert wurden, sollte wohl alles in den Müll wandern. Das ist der Grund, warum ich das so tue.
BlackJack

@Alfons Mittelmeyer: Man hat ja nicht tausende von Klassen die jeweils GUI-Elemente beschreiben, und wenn man einen Frame aus der GUI entfernt, also keine Referenzen mehr darauf hält, dann bleiben auch nur die Datentypen im Speicher belegt aber nicht die Daten die benötigt wurden um das jeweilige Exemplar zum Leben zu erwecken. Du solltest vielleicht mal nachmessen was Du da tatsächlich an Speicher sparst (oder ob das überhaupt Speicher spart). Ich wäre sehr überrascht wenn das tatsächlich irgendeinen praktischen Unterschied macht. Die Anzahl der Module bei einem Programm ist ja in der Regel fix, und zumindest bei CPython ist die Codegrösse eines Moduls auch beschränkt, 64KiB wenn ich mich nicht irre, also ist die dadurch belegte Gesamtgrösse ja auch halbwegs absehbar. Wenn ein Python-Programm so gross wird das man die Module nicht mehr alle im Speicher halten kann ohne Probleme zu bekommen, dann muss man heutzutage schon einen Rechner mit sehr wenig Speicher verwenden.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben: Gegen Code in Zeichenketten ist einzuwenden dass das nur ganz schlecht skaliert. Denn schon in der nächsten Ebene, wenn man in diesem Code in einer Zeichenkette wieder eine Zeichenkette mit Code haben möchte, wird das unhandlich und bei einer dritten Ebene unbenutzbar. Es geht hier nicht um Geschwindigkeit, sondern dass man bei zur Laufzeit kompiliertem Code in Zeichenketten offenbar mit der Ausdruckstärke der Sprache nicht zufrieden ist, oder damit nicht vernünftig umgehen kann. Und gerade Python ist dynamisch genug das man nur sehr selten auf solche Krücken zurückgreifen muss. Code-Fragmente aus Zeichenketten zu compilieren die über das ganze Programm verteilt sind, ist auch etwas anderes als ``import`` zu verwenden.
Da hast Du natürlich recht. Wenn man einen String beginnt mit """
dann geht in der nächsten Ebene noch \"\"\"

Wenn man allerdings darin ein \n braucht, das geht dann nicht. man kann natürlich sich dann daraus noch mit einer Funtion behelfen, etwa + nl() +
die dann ein newline zurückgibt.

Und ein über 200 Zeilen langer Programmstring schaut auch nicht gut aus. Statt so langer Strings sollte ich lieber von einer Quelldatei importieren.

Danke, für den Tip. Das betrifft drei Module meines GuiCreators, nämlich die Änderung der config options, der layout options pack_info, grid_info oder place_info und der Navigation in der GUI

Andere Module sind aber sehr übersichtlich, denn man hat eine klare Trennung von GUI und Code. Der GUI Teil umfasst die Kreation des Widgets und das Layout, wie etwa in meinem Pack Modul:

Code: Alles auswählen

Button('RIGHT',text="""RIGHT""",pady='2',padx='1',bg='green')
Button('BOTTOM',text="""BOTTOM""",pady='2',padx='1',bg='green')
Button('TOP',text="""TOP""",pady='2',padx='1',bg='green')
Label('Label',width='1')
Label('PackTitle',text="""pack""",fg='blue')
Button('LEFT',text="""LEFT""",pady='2',padx='1',bg='green')

widget('PackTitle').pack(side='left')
widget('Label').pack(side='left')
widget('TOP').pack(side='left')
widget('LEFT').pack(side='left')
widget('RIGHT').pack(side='left')
widget('BOTTOM').pack(side='left')
Das ist vom GuiCreator generierter Code. Die Reihenfolge der Label und Button Commands ist zufällig, da sie aus dem Verzeichnis Dictionary kommt.
Allerdings spielt bei einem Pack Layout die Reihenfolge eine wichtige Rolle. Deshalb wird aus einer Packing Liste am Ende die richtige Pack Reihenfolge generiert.

Übersichlich ist das aber deshalb weil hier GUI und Code unvermischt sind. Der Code Teil schließt sich dann an:

Code: Alles auswählen

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

# the buttons do a pack with parameter side = top, left, bottom or right
# and send a 'BASE_LAYOUT_CHANGED', which contains the current packed widget reference and the layout before

push(EvCmd("""
send('BASE_LAYOUT_PLACE_MOUSEOFF')
push(this().Layout)
pack(side=Par())
send('BASE_LAYOUT_CHANGED',(this(),pop()))
"""))

widget("TOP").evcommand(top(),TOP)
widget("LEFT").evcommand(top(),LEFT)
widget("RIGHT").evcommand(top(),RIGHT)
widget("BOTTOM").evcommand(pop(),BOTTOM)

# ---- Receivers for message 'BASE_LAYOUT_REFRESH' ------------------------------------------------

# if the current user widget has a PACKLAYOUT, the background of Label 'PackTitle' shall be shown yellow, otherwise normal
registerReceiver('BASE_LAYOUT_REFRESH',"""
if Msg().Layout == PACKLAYOUT: Par()[0].configure(bg="yellow")
else: Par()[0].configure(bg=Par()[1])
""",(widget("PackTitle"),widget("PackTitle").getconfig("bg")))


# if there is already a GRIDLAYOUT in the container of the current user widget, the container LabelFrame 'PackFrame' will be hidden (unlayout),
# and otherwise shown
registerReceiver('BASE_LAYOUT_REFRESH',"send('HIDELAYOUT_PackOrGrid',(Msg(),GRIDLAYOUT,Par()))",container())

### ========================================================
Wichtig ist dabei der Beginn des CODE Teils mit ### CODE und das Ende mit ###

Daran erkennt der GuiCreator, dass es der Code Teil ist und speichert ihn beim Hereinladen als String im Container ab. Nach Modifizierund der GUI kann dann das ganze Programm inklusive Code wieder gespreichert werden. Oder der Code kann auch zur Laufzeit modifiziert und getestet werden.

Hier in diesem Beispiel wird der String compilert und dann zur Variablenvermeidung auf einen Stack gepusht. Denn er wird für alle 4 Buttons gebraucht. Den Button Kommandos kann man nämlich entweder Strings oder compilierten Code übergeben.

Was der Code macht:

send('BASE_LAYOUT_PLACE_MOUSEOFF')

Das Widget, das gepackt werden soll, könnte zuerst ein Placelayout gehabt haben. Beim Placelayout könnten Mouseevents mit dem Widget verbuden sein, sodass man es mit der Maus bewegen kann.
Wenn es aber gepackt wird und dann bei mouse move seine yx Koordinaten verändern will, gäbe es einen Crash. Daher die Message, solche Mouse events bitteschön wieder herauszunehmem. Darum kümmert sich dann das Place Layout Modul.

push(this().Layout)

Hier wird die Kennzeichnung des alten Layouts gesichert.

pack(side=Par())

Hier wird der Pack vorgenommen. Die Seite kommt dabei aus dem Parameter, der bei der Definition der Buttoncommands übergeben wird.

send('BASE_LAYOUT_CHANGED',(this(),pop()))

Hier wird gesendet, dass sich das Layout geändert hat. Als Parameter wird übergeben, das widget und die Kennung des vorherigen Layouts.
Je nachdem, wie das zuvor war, müssen nämlich andere Module benachrichtigt werden. War es vorher auch ein Packlayout, muss nichts geschehen, war es ein anderes Layout, mussen die anderen Layout Basis Module benachrichtigt werden. Durch gelben Hintergrund des Titels zeigen die nämlich an, welches Layout das selektierte widget hat. Zu benachrichtigen ist dann auch das Layout Options Modul, dass es jetzt ein pack_info zeigen muss.
Hatte das Widget vorher kein Layout, dann ist auch das Navigationsmodul zu verständigen, denn dieses zeigt die Namen von widgets, die kein Layout haben in kursiv.

Darauf folgen dann die Buttonkommandos mit Übergabe des Parameters, welche Seite. Denn man kann auch einen Parameter mit übergeben und wenn es mehrere sein sollen, nimmt man ein tuple.

Als nächstes wird ein Receiver für die Message BASE_LAYOUT_REFRESH installiert.

Wenn ein selektiertes widget ein Packlayout hat, soll der Titel des Pack Moduls gelben Hintergrund haben, ansonsten nicht.

Und dann kommt noch ein zweiter Receiver für dieselbe message. Hier sendet dann das Packmodul, dass es versteckt werden soll, wenn bereits ein GRIDLAYOUT im container des widgets vorliegt.
Bei pack und grid gäbe es sonst einen Systemabsturz

War das jetzt etwa sehr unübersichtlich? Sicher ist dieser Code äußerst ungewohnt. Aber unübersichtlich würde ich nicht unbedingt denken.
BlackJack

@Alfons Mittelmeyer: Also ich finde ihn unübersichtlich unter anderem wahrscheinlich weil er ungewohnt ist. Und solche Stapel-Geschichten verlangen dem Leser in der Regel mehr ab als imperativer Code weil man sich immer klar machen muss was der Stack-Effekt gerade ist und das immer alle Operationen ”balanciert” sind, man also nichts auf dem Stapel vergisst, oder in irgendwelchen Fällen zu viel herunter nimmt. Selbst bei Sprachen wie Forth bieten moderne Implementierungen lokale Variablen, damit man sich diese Gehirnakrobatik nicht zu sehr antun muss und sich mehr auf das eigentlich zu lösende Problem konzentrieren kann.

Ich verstehe nicht wie Du diese `widget()`-Aufrufe als ”Variablenvermeidung” bezeichnen kannst. Beim erstellen gibst Du den Widgets Namen die als erstes Argument als Zeichenkette übergeben werden. Und dann greifst Du immer mit ``widget('name')`` darauf zu. Das ist doch im Grunde nur ein ”globaler” Widgetnamensraum den Du da aufmachst in dem Du so etwas wie Variablen ”selber bastelst”. Das hätte man auch einfacher direkt schreiben können.

Von einem modernen GUI-Creator erwarte ich auch das er *gar keinen* Quelltext generiert, sondern Datendateien ablegt und ein Modul zur Verfügung stellt mit dem man zur Laufzeit aus diesen Dateien GUI-Objekte erstellen kann. So wie das bei Qt oder Gtk gemacht wird. In Python schreibt man dann nur noch den Code um Rückruffunktionen zu registrieren die dann auf die Ereignisse aus der GUI entsprechend reagieren.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben:@Alfons Mittelmeyer: Also ich finde ihn unübersichtlich unter anderem wahrscheinlich weil er ungewohnt ist. Und solche Stapel-Geschichten verlangen dem Leser in der Regel mehr ab als imperativer Code weil man sich immer klar machen muss was der Stack-Effekt gerade ist und das immer alle Operationen ”balanciert” sind, man also nichts auf dem Stapel vergisst, oder in irgendwelchen Fällen zu viel herunter nimmt. Selbst bei Sprachen wie Forth bieten moderne Implementierungen lokale Variablen, damit man sich diese Gehirnakrobatik nicht zu sehr antun muss und sich mehr auf das eigentlich zu lösende Problem konzentrieren kann.

Ich verstehe nicht wie Du diese `widget()`-Aufrufe als ”Variablenvermeidung” bezeichnen kannst. Beim erstellen gibst Du den Widgets Namen die als erstes Argument als Zeichenkette übergeben werden. Und dann greifst Du immer mit ``widget('name')`` darauf zu. Das ist doch im Grunde nur ein ”globaler” Widgetnamensraum den Du da aufmachst in dem Du so etwas wie Variablen ”selber bastelst”. Das hätte man auch einfacher direkt schreiben können.

Von einem modernen GUI-Creator erwarte ich auch das er *gar keinen* Quelltext generiert, sondern Datendateien ablegt und ein Modul zur Verfügung stellt mit dem man zur Laufzeit aus diesen Dateien GUI-Objekte erstellen kann. So wie das bei Qt oder Gtk gemacht wird. In Python schreibt man dann nur noch den Code um Rückruffunktionen zu registrieren die dann auf die Ereignisse aus der GUI entsprechend reagieren.
Ja mit dem Stapel hast Du recht, da sollte ich mir etwas zusätzliches überlegen. Es gibt da noch ein globales Dictionary für temporären Gebrauch, das ich manchmal so benütze:

Code: Alles auswählen


VAR["Buttons"] = EvCmd("""
send('BASE_LAYOUT_PLACE_MOUSEOFF')
push(this().Layout)
pack(side=Par())
send('BASE_LAYOUT_CHANGED',(this(),pop()))
""")

widget("TOP").evcommand(VAR["Buttons"],TOP)
...

VAR.pop("Buttons",None)
Aber in den Rückruffunktionen benütze ich es nicht. Ob man dann ein lokales Dictionary nur gültig für die jeweilige Callbackfunktion anbieten sollte. Wäre wohl sinnvoll, denn so etwas sieht nicht gut aus:

Code: Alles auswählen

push(None)
push(None)
for Stack[-2],Stack[-1] in Par()[10].items()
Aber widget("Name") ist kein globaler Namensraum sondern ein lokaler. Der Name bezieht sich auf den lokalen Namensraum, nämlich in dem Frame oder sonstigem Container in dem gerade die widgets definiert wurden.

Deshalb muss man widgets oder widget Eigenschaften für die Callbacks auch als Parameter angeben:

Code: Alles auswählen

registerReceiver('BASE_LAYOUT_REFRESH',"""
if this().Layout == PACKLAYOUT: Par()[0]["bg"] ="yellow"
else: Par()[0]["bg"]=Par()[1]
""",(widget("PackTitle"),widget("PackTitle")["bg"]))
Zur Ausführungszeit befindet man sich hier im Namensraum des (zur Bearbeitung) ausgewählten Widgets und da ist widget("PackTitle") unbekannt. Zur Definitionszeit dieses Empfänger Callbacks allerdings befindet man sich im Namensraum des Pack Layout Moduls (Widgetverzeichnis des containers (Labelframe)) und kann dann die Referenz auf den eventuell gelb zu hinterlegenden Titel und die original Hintergrundfarbe übergeben. Hier erfolgt zur Ausführungszeit des Callbacks also auch kein Zugriff auf das Dictionary des Pack Layout Widget Verzeichnisses.

Und einfacher schreiben? Ich gebe ja zu, dass man statische GUIs anders schreiben kann. Oder kann man zur Laufzeit bei Klassen noch zusätzliche Membervariablen für neu hinzukommende Widgets definieren?
Das wäre für mich ungewohnt, aber vielleicht geht es ja bei Python. Trotzdem Klassen kann ich nicht brauchen, da sie nicht zu löschen sind und nicht beim Löschen eines Widgets mit verschwinden.

Das mit einer Struktur wie bei Qt ist allerdings eine gute Idee. Eine Abspeicherung der GUI wie bei Qt oder bei Gtk sollte ich auch mit anbieten. Aber ist nicht schlimm, wenn man die GUI auch von bereits vorhandenen Programmen zur Laufzeit abspeichern kann, oder?

Und dass man dafür Namen braucht sollte verständlich sein. Wenn man viele Entry widgets in einem Container hat und die dann alle Entry heißen, die man zwar noch mit einem Index unterscheiden kann, wäre etwas unübersichtlich. Meinst Du nicht auch?
Benutzeravatar
kbr
User
Beiträge: 1508
Registriert: Mittwoch 15. Oktober 2008, 09:27

Alfons Mittelmeyer hat geschrieben:Trotzdem Klassen kann ich nicht brauchen, da sie nicht zu löschen sind und nicht beim Löschen eines Widgets mit verschwinden.
Klassen sind kein Problem - die Instanzen sind es, die zusätzlichen Speicher belegen. Deren Speicherplatz wird aber auch wieder freigegeben, sobald die Instanzen aus dem Scope geraten (bzw. der Referenzzähler wieder auf Null geht).
BlackJack

@Alfons Mittelmeyer: Das man Klassen ”nicht löschen kann” ist in der Praxis kein Problem. Welches Problem hast Du damit? Zumal man sie natürlich auch löschen könnte, was spricht Deiner Meinung nach denn technisch dagegen? Den Wunsch scheint ausser Dir aber niemand zu verspüren, weil das keinen Sinn macht.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben:@Alfons Mittelmeyer: Das man Klassen ”nicht löschen kann” ist in der Praxis kein Problem. Welches Problem hast Du damit? Zumal man sie natürlich auch löschen könnte, was spricht Deiner Meinung nach denn technisch dagegen? Den Wunsch scheint ausser Dir aber niemand zu verspüren, weil das keinen Sinn macht.
Ich hatte mir Sorgen gemacht, dass bei Nachladen einer Menge unterschiedlicher Module, die man aber nur temporär braucht, der Speicherplatz wegen Anwachsen von Klassendefinition auch sehr anwachsen könnte. In der Praxis kommt aber ein solcher Fall kaum vor, dass man wie bei einem Webbrowser eine Menge von Python GUI nachlädt und sozusagen browst. Also kann man wohl getrost Klassendefinitionen verwenden. Immerhin ein nettes Experiment, ob man lange Funktionen mit über 200 Zeilen Quellcode statt mit lokalen Variablen auch mit einem Stack beherrschen kann.

Ja, kann man, aber das erfordert viel Übung, die man sich vorher etwa durch FORTH Programmierung angeeignet hatte. Und ist sicherlich nicht für jeden zu empfehlen. Also würde ich sagen, für begrenze Anwendungen, in denen verschiedenste Module nachladen nicht gegen unendlich geht, wie gewohnt Klassen verwenden.
Benutzeravatar
kbr
User
Beiträge: 1508
Registriert: Mittwoch 15. Oktober 2008, 09:27

Alfons Mittelmeyer hat geschrieben:Ich hatte mir Sorgen gemacht, dass bei Nachladen einer Menge unterschiedlicher Module, die man aber nur temporär braucht, der Speicherplatz wegen Anwachsen von Klassendefinition auch sehr anwachsen könnte ... Immerhin ein nettes Experiment, ob man lange Funktionen mit über 200 Zeilen Quellcode statt mit lokalen Variablen auch mit einem Stack beherrschen kann.
Der Speicherplatzbedarf von Python-Programmen ist bei heutiger Hardware üblicherweise kein Problem, bei einer VAX mit 32 kB Hauptspeicher von vor 30 Jahren sähe das natürlich anders aus - die kannte allerdings auch noch kein Python.
Bei Funktionen mit 200 Zeilen Quellcode würde ich mir jedoch Gedanken über ein Refactoring machen; eigentlich auch schon ab etwa 20 Zeilen. Das reduziert oft auch die Zahl der lokalen Variablen pro Funktion. Und sobald globale Variable erforderlich werden, wird es Zeit Klassen anzulegen.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

kbr hat geschrieben: Bei Funktionen mit 200 Zeilen Quellcode würde ich mir jedoch Gedanken über ein Refactoring machen; eigentlich auch schon ab etwa 20 Zeilen. Das reduziert oft auch die Zahl der lokalen Variablen pro Funktion. Und sobald globale Variable erforderlich werden, wird es Zeit Klassen anzulegen.
Sorry lokale Variablen?

Code: Alles auswählen

push(None)
push(None)
for Stack[-2],Stack[-1] in Par()[10].items()
Oder mein Code zur Umsortierung der zufälligen Reigenfolge aus dem config dictionary?

Code: Alles auswählen

		push("title")
		Par()[2].execute()
		push("geometry")
		Par()[2].execute()
		push("from")
		Par()[2].execute()
		push("to")
		Par()[2].execute()
		push("increment")
		Par()[2].execute()
		push("resolution")
		Par()[2].execute()
		push("bigincrement")
		Par()[2].execute()
		push("showvalue")
		Par()[2].execute()
		push("tickinterval")
		Par()[2].execute()
		push("digits")
		Par()[2].execute()
		push("orient")
		Par()[2].execute()
		push("label")
		Par()[2].execute()
		push("text")
		Par()[2].execute()
		push("link")
		Par()[2].execute()
		push("state")
		Par()[2].execute()
		push("underline")
		Par()[2].execute()
		push("default")
		Par()[2].execute()
		push("relief")
		Par()[2].execute()
		push("sliderrelief")
		Par()[2].execute()
		push("overrelief")
		Par()[2].execute()
		push("buttondownrelief")
		Par()[2].execute()
		push("width")
		Par()[2].execute()
		push("length")
		Par()[2].execute()
		push("sliderlength")
		Par()[2].execute()
		push("height")
		Par()[2].execute()
		push("wraplength")
		Par()[2].execute()
		push("padx")
		Par()[2].execute()
		push("pady")
		Par()[2].execute()
		push("bd")
		Par()[2].execute()
		push("anchor")
		Par()[2].execute()
		push("justify")
		Par()[2].execute()
		push("font")
		Par()[2].execute()
		push("fg")
		Par()[2].execute()
		push("bg")
		Par()[2].execute()
		push("troughcolor")
		Par()[2].execute()
		push("selectforeground")
		Par()[2].execute()
		push("selectbackground")
		Par()[2].execute()
		push("insertwidth")
		Par()[2].execute()
		push("insertwidth")
		Par()[2].execute()
		push("insertborderwidth")
		Par()[2].execute()
		push("insertontime")
		Par()[2].execute()
		push("selectborderwidth")
		Par()[2].execute()
		push("activeforeground")
		Par()[2].execute()
		push("activebackground")
		Par()[2].execute()
		push("disabledforeground")
		Par()[2].execute()
		push("disabledbackground")
		Par()[2].execute()
		push("highlightcolor")
		Par()[2].execute()
		push("highlightbackground")
		Par()[2].execute()
		push("highlightthickness")
		Par()[2].execute()
Also der Stack mit push ist global, Par()[2] ist lokal.
Allerdings hast Du recht!

Mit Einführung einer Klasse, der man auch einen Parameter übergeben kann, könnte man den Code auf die Hälfte reduzieren. Und noch weniger Code wird es, wenn man daraus ein tuple macht und in einer Schleife abarbeitet. Allerdings zur besseren Übersicht sollte man das tuple auf einzelne Zeilen aufteilen, was dann wieder der Zeilenanzahl von Alternative eins entspricht, aber weniger Code benötigt und damit erübrigt sich wieder die Klasse, der man auch einen Parameter übergeben kann. Aber sicher wäre Parameterübergabe sinnvoll und würde wohl auch etwas bringen. Hätte ich aber auch selber draufkommen sollen.

Das grundlegende Problem, dass aus einem String kompilierter Code keine Funktion ist, sofern man keine Funktion verwenden will und daher keine lokalen Variablen haben kann, wird dadurch natürlich nicht gelöst. Übersichtlicher wird es aber schon, wenn man gleich auf mehrere Werte zugreifen kann und weniger Inanspruchnahme des Stacks benötigt.

Ja gegen Ende der Programmierung ist sicherlich ein Refaktoring angebracht.
BlackJack

@Alfons Mittelmeyer: Ja lokale Variablen. Die sind *gut* weil sie Dinge *Namen* geben und damit den Code *verständlicher* machen, statt mit irgendwelchen anonymen Werten auf einem Stapel zu jonglieren. Das ist *schlecht*, weil *unverständlich*, *kompliziert*, und damit *fehleranfällig*.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@kbr Danke für den Rat, schaut jetzt schon viel besser aus:

Code: Alles auswählen

		push(None)
		for Stack[-1] in (
"title",
"geometry",
"from",
"to"
"increment",
"resolution",
"bigincrement",
"showvalue",
"tickinterval",
"digits",
"orient",
"label",
"text",
"link",
"state",
"underline",
"default",
"relief",
"sliderrelief",
"overrelief",
"buttondownrelief",
"width",
"length",
"sliderlength",
"height",
"wraplength",
"padx",
"pady",
"bd",
"anchor",
"justify",
"font",
"fg",
"bg",
"troughcolor",
"selectforeground",
"selectbackground",
"insertwidth",
"insertwidth",
"insertborderwidth",
"insertontime",
"selectborderwidth",
"activeforeground",
"activebackground",
"disabledforeground",
"disabledbackground",
"highlightcolor",
"highlightbackground",
"highlightthickness"): Par()[2].execute()
		pop()
Brauchte dazu nur im dazugehörigen ausführbaren Objekt das abschließende pop() entfernen. Darin wird aus dem Config Dictionary das was in der obigen Liste steht, sortiert in eine neue Liste übernommen, sofern es vorhanden ist.

Der dazugehörige Code ist zwar um ein pop() kürzer geworden. Sehr verständlicher freilich ist er immer noch nicht, wenn man nicht bis drei zählen kann:

Code: Alles auswählen

EvCmd("if top() in third(): Stack[-2].append((top(),third().pop(top())))")
Ach schreiben wir wohl besser so, weil das top() verwirrt. Man kann auch stattdessen first() schreiben:

Code: Alles auswählen

EvCmd("if first() in third(): second().append((first(),third().pop(first())))")
Sirius3
User
Beiträge: 18294
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: das was Du da schreibst, erinnert mehr an Postscript als an Python, nur das man dort nicht immer explizit den Stack mitgeben muß. Das zeigt doch auch, daß Python dafür nicht gemacht ist. Was versuchst Du eigentlich mit der Liste von 50 Keys zu erreichen? Ich sehe da nirgends, daß denen irgendwelche Werte zugewiesen werden.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Sirius3 hat geschrieben:@Alfons Mittelmeyer: das was Du da schreibst, erinnert mehr an Postscript als an Python, nur das man dort nicht immer explizit den Stack mitgeben muß. Das zeigt doch auch, daß Python dafür nicht gemacht ist. Was versuchst Du eigentlich mit der Liste von 50 Keys zu erreichen? Ich sehe da nirgends, daß denen irgendwelche Werte zugewiesen werden.
Das verstehe ich jetzt nicht, warum Du da keine Wertzuweisungen siehst. Ich hatte ja geschrieben worum es geht.
Und das ist reine Konzentration auf nur drei Dinge, ohne störende Ablenkungen:

Code: Alles auswählen

if first() in third(): second().append((first(),third().pop(first())))
Also die einfache Welt des eins, zwei, drei statt verwirrender Vielfalt
Und sprich mal, wie das klingt und wie der Rythmus ist. Das ist geradezu ein Mantra reiner Programmierung.
Vielleicht die Augen schleßen, den Rythmus hören und dann kommt eventuell die Erleuchtung

Und vielleicht noch ein Hinweis. Der Listenübergabe (in einer Schleife) vorausgegangen war:

Code: Alles auswählen

push(this().getconfdict()) # third
push([])  # second
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Auch jetzt ist mir noch etwas eingefallen. fist, second, third versteht man anscheinend nicht so besonders gut.

Also bei eins,zei,drei ist es schwierig, aber a,b,c wäre man gewohnt. Da sollte ich mal darüber nachdenken.

Man müßte allerdings das dann DynThInter.a nennen oder eine andere Klasse. Und statt Stack[-1] = 5 könnte man dann schreiben DynTkInter.a.set(5)
Und dann könnte man sich das abc als lokale Variablen hernehmen, sofern man dann auf dem Stack nichts mehr pusht und am Ende haut man dann das verwendete abc vom Stack wieder herunter.
So nämlich funktionieren lokale Variablen.

Schön wäre aber, wenn man die lokalen 'Variablen' beliebig benennen könnte. Hat jemand eine Idee?

So etwas wie DynTkInter.var("xyz") würde mir aber nicht gefallen.
Zuletzt geändert von Alfons Mittelmeyer am Montag 3. August 2015, 15:34, insgesamt 1-mal geändert.
BlackJack

@Alfons Mittelmeyer: Meine Idee wäre ja einfach in Python auch in Python zu programmieren, und wenn man etwas anderes mit Stapel haben möchte, Forth oder Factor zu nehmen. ;-)
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben:@Alfons Mittelmeyer: Meine Idee wäre ja einfach in Python auch in Python zu programmieren, und wenn man etwas anderes mit Stapel haben möchte, Forth oder Factor zu nehmen. ;-)
Nein ich möchte ja gerade keinen Stapel, sondern eine temporäre Funktion, etwa in dem Stil:

Code: Alles auswählen

mybutton.config(command = pseudofunction(mybutton,parfg='white',parbg='yellow'):
	mybutton.config(fg=parfg,bg=parbg)
)
Also kompilieren läßt sich das schon einmal nicht.

Einfach zum Verzweifeln

Eigentlich wäre das ja schon da. Und das da geht noch:

Code: Alles auswählen

mybutton.config(command = lambda(mybutton,parfg='white',parbg='yellow':
	mybutton.config(fg=parfg,bg=parbg)
))
Aber das geht nicht mehr:

Code: Alles auswählen

mybutton.config(command = lambda(mybutton,parfg='white',parbg='yellow':
	mybutton.config(fg=parfg,bg=parbg)
	print("Test")
))
Man müßte das lambda auf mehrzeilig erweitern, dann wären alle Probleme gelöst.
BlackJack

@Alfons Mittelmeyer: Da stellt sich dann die Frage nach dem warum. Es gibt ``def``. Mehrzeilige anonyme Funktionen wird es Python nicht geben. Da haben schon so viele Leute versucht Syntax für zu erfinden und alle wurden von Guido abgelehnt, der ja am liebsten das bisherige ``lambda`` schon rausgeworfen hätte.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben:@Alfons Mittelmeyer: Da stellt sich dann die Frage nach dem warum. Es gibt ``def``. Mehrzeilige anonyme Funktionen wird es Python nicht geben. Da haben schon so viele Leute versucht Syntax für zu erfinden und alle wurden von Guido abgelehnt, der ja am liebsten das bisherige ``lambda`` schon rausgeworfen hätte.
Das sollte doch klar sein, warum man ein erweitertes lambda bräuchte.

Wenn man für ein GUI Objekt einn Rückruf anlegen möchte, dann kann man das ohne weiteres tun ohne Funktion, wenn der Ausdruck einzeilig ist.

Wenn er aber zweizeilig ist, dann muß man extra eine Funktion dafür hernehmen, obwohl niemand anderes diese Funktion benützen will, als nur dieser Button. Funktionsdefinitionen sind eigentlich dafür da, dass andere diese Funktion benützen.

Damit Funktionen nicht miteinander ins Gehege kommen, muss man dann für diesen Button gleich eine Klasse, bezw. für den Container dann eine Klasse definieren.
Da bleiben dann Funktionsdefinitionen im Speicher übrig, wenn man den Button wieder löscht. Un außerdem wird die Programmierung umständlich. Ja wenn man es gewohnt, dann denkt man eben, das muß so sein.

Und dass alles viel Einfacher würde, wenn man keine solche Funktion und gar noch Klasse bräuchte, das will man einfach nicht einsehen. Wenn lambda eine Funktion wäre, würde alles viel einfachr. Übrigens bei Squirrel geht so etwas.

Übrigens habe ich teilweise die Lösung gefunden. Für Verzeigungen und Schleifen allerdings noch nicht:

Code: Alles auswählen

def pseudofunction(a=None,b=None,c=None,d=None,e=None,f=None,g=None):pass

a=(lambda a=5, b=6,c=7: pseudofunction(
print(a),
print(b),
print("noch etwas"),
print(c)
))

a()

print("\nEinmal gemacht\n")

a()

print("\nZweimal gemacht")
Nein besser ist es wohl so:

Code: Alles auswählen

def pseudofunction(par=None):pass

a=(lambda a=5, b=6,c=7: pseudofunction((
print(a),
print(b),
print("noch etwas"),
print(c)
)))

a()

print("\nEinmal gemacht\n")

a()

print("\nZweimal gemacht")
Also, das Problem beliebig viele Parameter und auch Funktionsaufrufe dürfte damit gelöst sein. Das mit der Logik noch nicht.
Gesperrt