@Alfons Mittelmeyer: Danke! Hat jetzt geklappt.
Gruss wuf
Spazieren in der GUI
- Hyperion
- Moderator
- Beiträge: 7478
- Registriert: Freitag 4. August 2006, 14:56
- Wohnort: Hamburg
- Kontaktdaten:
Er ist halt der Rebell des Forums, quasi ein Python Punk, der aus Prinzip alles anders macht, alle Regeln bricht und Ratschläge konsequent ignoriert...jens hat geschrieben:Du bist ein echter fan von 'eval',was?
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
assert encoding_kapiert
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Sorry hatte nicht gewußt, dass bei Eurer Normalschriftart groß i genauso wie klein L aussieht. Wenn man eintippt, hat man eine andere Schriftart bei der der Unterschied deutlich ist. Aber was Ihr dann ausgebt unterscheidet sich.Sirius3 hat geschrieben:@Alfons Mittelmeyer: noch ein Grund, warum man sich an die Namenskonvention halten sollte, bei "go_in" gibt es keine Mißverständnisse.
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Kann sein, dass das mit dem Thread unter Windows nicht klappt. Bei mir unter Ubuntu tut es. Daher habe ich es anderes implementiert. Allerdings hatte ich gemerkt, dass ich bei spazieren.py den Toplevel vergessen hatte: Bitte noch hinzufügen zu spazieren.py:Sirius3 hat geschrieben:@Alfons Mittelmeyer: leider tut Dein Spaziergang bei mir nicht:Ich gehe mal davon aus, dass es daran liegt, dass Du versuchst, von einem Thread aus auf die GUI zuzugreifen.Code: Alles auswählen
> ls() => \. Frame > Tcl_WaitForEvent: Notifier not initialized Abort trap: 6
Code: Alles auswählen
class Toplevel(GuiElement,StatTkInter.Toplevel):
def __init__(self,myname="Toplevel",**kwargs):
_initGuiElement(kwargs,StatTkInter.Toplevel,self,myname,"Toplevel",True)
Code: Alles auswählen
from spazieren import *
class App:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.button = Button(frame,
text="QUIT", fg="red",
command=frame.quit)
self.button.pack(side=LEFT)
self.slogan = Button(frame,
text="Hello",
command=self.write_slogan)
self.slogan.pack(side=LEFT)
def write_slogan(self):
print("Tkinter is easy to use!")
root = Tk()
app = App(root)
import GuiWalk
root.mainloop()
Code: Alles auswählen
from spazieren import *
goApp()
Toplevel()
goIn()
this().title('GuiWalk')
Entry('Entry')
Label('Label',text="""Your Input:""")
widget('Label').pack(side='left')
widget('Entry').pack(side='left')
### Code ======================================
def function(entry):
try: eval(compile(entry.get(),'<string>','exec'))
except:pass
entry.delete(0,END)
ls()
widget('Entry').bind('<Return>',lambda event, entry=widget('Entry'), func=function: func(entry))
del function
### ===========================================
goApp()
ls()
PS: natürlich kann man solche Befehle wie hier auch eingeben, während das Programm läuft - bei entsprechender Eingabemöglichkeit oder Dazuimport.
- cofi
- Python-Forum Veteran
- Beiträge: 4432
- Registriert: Sonntag 30. März 2008, 04:16
- Wohnort: RGFybXN0YWR0
Das hat nur begrenzt etwas mit den Betriebssystemen zu tun. Kein (verbreitetes) GUI Framework ist threadsafe, greifst du aus einem anderen Thread aus dem Hauptthread auf die GUI-Elemente zu, ist das Verhalten nicht definiert.Alfons Mittelmeyer hat geschrieben:Kann sein, dass das mit dem Thread unter Windows nicht klappt. Bei mir unter Ubuntu tut es.Sirius3 hat geschrieben:Ich gehe mal davon aus, dass es daran liegt, dass Du versuchst, von einem Thread aus auf die GUI zuzugreifen.
Soll heissen: Wenn es wie gewuenscht funktioniert, ist das ein gluecklicher Zufall.
Michael Markert ❖ PEP 8 Übersetzung ❖ Tutorial Übersetzung (3.x) ⇒ Online-Version (Python 3.3) ❖ Deutscher Python-Insider ❖ Projekte
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Naja hatte bei mir unter Linux Ubuntu einwandfrei funktioniert und beim User wuf hatte es unter Linux Ubuntu auch einwandfrei funktioniert. Dafür ist ja so ein Forum gut, damit man sieht, ob es woanders auch funktioniert. Für Windows .Net gibt es einen Backgroundworker, mit dem man dann von einer Task in die GUI einen event melden kann. Bei Python hatte ich so etwas nicht gefunden. Aber da ist dann eine in die GUI integrierte Eingabe in einem Toplevel Window die richtige Lösung. So etwas kann man überall machen, etwa auch in Java.cofi hat geschrieben:Das hat nur begrenzt etwas mit den Betriebssystemen zu tun. Kein (verbreitetes) GUI Framework ist threadsafe, greifst du aus einem anderen Thread aus dem Hauptthread auf die GUI-Elemente zu, ist das Verhalten nicht definiert.Alfons Mittelmeyer hat geschrieben:Kann sein, dass das mit dem Thread unter Windows nicht klappt. Bei mir unter Ubuntu tut es.Sirius3 hat geschrieben:Ich gehe mal davon aus, dass es daran liegt, dass Du versuchst, von einem Thread aus auf die GUI zuzugreifen.
Soll heissen: Wenn es wie gewuenscht funktioniert, ist das ein gluecklicher Zufall.
Allerdings bei .Net Windows GUI Applikationen muß man ein wenig tricksen. Die haben nämlich keine Konsoleneingabe oder eine über input. Aber wenn man Konsoleneingaben auf ein Programm umleitet, das nichts anderes tut, als die über Standard Input empfangenen Zeichen wieder über Standard Output auszugeben, dann kann man die Standardausgabe dieses Programmes über eine Pipe mit der Standardeingabe der Windows GUI Applikation verbinden: Und schon hat man eine Konsoleneingabe für Windows GUI Applikationen.
@Alfons Mittelmeyer: Ein Forum ist aber auch dazu gut das einem gesagt wird das Tk(inter) nicht thread-safe ist und man deshalb nur aus dem Thread in dem die `mainloop()` läuft Veränderungen machen darf und aus anderen nicht. Auch wenn das scheinbar beim eigenen Rechner noch keine Probleme gemacht hat die man bemerkt hat, das ist dann halt Zufall.
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Hatte eben funktioniert. Aber für einen anderen Thread gibt es selbstverständlich auch Lösungen, etwa Eingabe und Compilierung im Thread. Abholen und Ausführen über after von der GUI. Machbar ist alles. Aber von der GUI aus ohne Thread, finde ich am Besten, sofern man nicht Scripte über Standardinput für die GUI reinlesen will. Für händisch eingeben würde die Timer Lösung reichen, aber für reinrasselnde Scripte über Standardinput müßte man sich eine bessere Lösung ausdenken, bzw. man macht es nicht über Standardinput. Dass man in der GUI von einem Thread aus einen event auslösen könnte, darüber habe ich für tkinter noch nichts gefunden. Aber da fällt mir ein, dass es gar nicht für tkinter sein müßte. Es müßte reichen von einem Thread in die Haupttask zu kommen. Aber dieses Thema möchte ich nicht weiter vertiefen. Ich fang jetzt dann lieber an, meinen GuiDesigner umzuschreiben. Und doch da gäbe es auch eine Lösung - meine Message Queue.BlackJack hat geschrieben:@Alfons Mittelmeyer: Ein Forum ist aber auch dazu gut das einem gesagt wird das Tk(inter) nicht thread-safe ist und man deshalb nur aus dem Thread in dem die `mainloop()` läuft Veränderungen machen darf und aus anderen nicht. Auch wenn das scheinbar beim eigenen Rechner noch keine Probleme gemacht hat die man bemerkt hat, das ist dann halt Zufall.
Zuletzt geändert von Alfons Mittelmeyer am Sonntag 16. August 2015, 23:36, insgesamt 1-mal geändert.
- cofi
- Python-Forum Veteran
- Beiträge: 4432
- Registriert: Sonntag 30. März 2008, 04:16
- Wohnort: RGFybXN0YWR0
Nur der Main-Thread darf Aenderungen an der GUI machen, versucht ein anderer Thread das fallen alle Garantien weg.
Der funktionierende Weg den du nicht gefunden hast ist die `after` bzw `after_idle` Methode.
Der funktionierende Weg den du nicht gefunden hast ist die `after` bzw `after_idle` Methode.
Michael Markert ❖ PEP 8 Übersetzung ❖ Tutorial Übersetzung (3.x) ⇒ Online-Version (Python 3.3) ❖ Deutscher Python-Insider ❖ Projekte
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Die hatte ich schon gefunden, ich hatte es nur nicht so schnell geschrieben. Ach was sehe ich, ich war doch schneller und noch vor Dir dran: 'Abholen und Ausführen über after von der GUI.' Aber after_idle kannte ich noch nicht. Das wäre dann anscheinend die Lösung für Scripts.cofi hat geschrieben:Nur der Main-Thread darf Aenderungen an der GUI machen, versucht ein anderer Thread das fallen alle Garantien weg.
Der funktionierende Weg den du nicht gefunden hast ist die `after` bzw `after_idle` Methode.
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Fallen bei jeder GUI Änderung alle Garantien weg, oder gibt es einige Kommandos, bei denen die Garantien nicht wegfallen? Dass die GUI zyklisch über ein after nachschaut, müßte nicht sein, wenn man von einem Thread aus ein after triggern dürfte. Wie sieht das mit after triggern aus? Weißt Du etwas darüber, oder dafür auch keine Garantien?cofi hat geschrieben:Nur der Main-Thread darf Aenderungen an der GUI machen, versucht ein anderer Thread das fallen alle Garantien weg.
@Alfons Mittelmeyer: Events sind auch thread-safe, allerdings benutzt das kaum jemand weil die Implementierung in der `Tkinter`-Anbindung nicht vollständig ist. Man kann nur Ereignisse ohne zusätzlich Daten auslösen, also wirklich nur ein Event mit einem eigenen Namen ohne das man da weitere Informationen mitgeben kann. Wenn einem das reicht, kann man das natürlich so verwenden. In der Praxis habe ich das aber nur *sehr* selten gesehen, in der Regel wird einem immer eine `Queue.Queue` und `after()`/`after_idle()` nahegelegt.
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Man könnte natürlich Mausklicks, Buttonklicks in einen Eventbuffer schreiben. Aber das ginge mir zu sehr ins Eingemachte. Könnte auch Betriebssystemabhängig sein, sofern tkinter das nicht nochmal in ein anderes Format bringt. Und Python events bringen auch nicht viel. Mit einem Lock würde ich ja die GUI sperren. Eine Endlosschleife in der GUI geht auch nicht. Und wenn ich das triggern will, lande ich wieder bei after. Und eine Queue habe ich selber. Wenn die nicht idle ist und gerade Messages verarbeitet, ist es kein Problem von einem anderen Thread aus zusätzliche Messages in der Queue abzulegen. Wenn sie aber alles abgearbeitet hat, arbeitet sie nicht mehr. Und wenn dann eine Message kommt, führt sie sie gleich aus. Das aber darf sie nicht, wenn die Message von einem anderen Thread kommt. Wenn sie von einem anderen Thread kommt, dann kann man das so machen, dass man dann die Messages nur in der Queue ablegt. Aber weil das Message System nicht läuft, muß man es aufrufen und dannn sind wir wieder beim after.BlackJack hat geschrieben:@Alfons Mittelmeyer: Events sind auch thread-safe, allerdings benutzt das kaum jemand weil die Implementierung in der `Tkinter`-Anbindung nicht vollständig ist. Man kann nur Ereignisse ohne zusätzlich Daten auslösen, also wirklich nur ein Event mit einem eigenen Namen ohne das man da weitere Informationen mitgeben kann. Wenn einem das reicht, kann man das natürlich so verwenden. In der Praxis habe ich das aber nur *sehr* selten gesehen, in der Regel wird einem immer eine `Queue.Queue` und `after()`/`after_idle()` nahegelegt.
Allerdings ein event auslösen ohne zusätzliche Daten, das klingt jetzt sehr interessant. Denn damit könnte man die Abarbeitung der Messagequeue anstoßen. Darüber würde ich gerne mehr erfahren. Heißt das ich kann einfach so etwas machen:
self.bind("irgendetwas", mycallback)
Und kann es dann von außen her triggern?
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Ach hab es doch gefunden:Quelle: http://stackoverflow.com/questions/2706 ... -main-loop
So etwas ist natürlich toll. Dann braucht doFoo in meinem Fall nur aufzurufen: send("voelligwurscht_aber_nicht_bereits_sonst_definiert") und die Abarbeitung läuft.
Noch eine Frage: wie ist das mit Python Threads. Kommt es zu einem Threadwechsel nur nach Ausführung vollständiger Python Befehle oder auch mittendrin?
Wenn ich etwa von einer anderen Task aus einen append mache, bevor in der Maintask ein pop kommt, ist kein Problem.
Wenn ich von einer anderen Task aus einen append mache, nachdem in der Maintask ein pop ausgeführt wurde, ist auch kein Problem.
Aber wenn ich einen append mache, während eines pops, das dürfte ein Problem sein, sofern man da nicht lockt. Also ist da ein lock erforderlich?
Oder anders ausgedrückt: laufen die Threadwechsel Pythongesteuert ab oder auf Prozessorebene?
Code: Alles auswählen
from tkinter import *
def doFoo(*args):
print("Hello, world")
root = Tk()
root.bind("<<Foo>>", doFoo)
# some time later, inject the "<<Foo>>" virtual event at the
# tail of the event queue
root.event_generate("<<Foo>>", when="tail")
So etwas ist natürlich toll. Dann braucht doFoo in meinem Fall nur aufzurufen: send("voelligwurscht_aber_nicht_bereits_sonst_definiert") und die Abarbeitung läuft.
Noch eine Frage: wie ist das mit Python Threads. Kommt es zu einem Threadwechsel nur nach Ausführung vollständiger Python Befehle oder auch mittendrin?
Wenn ich etwa von einer anderen Task aus einen append mache, bevor in der Maintask ein pop kommt, ist kein Problem.
Wenn ich von einer anderen Task aus einen append mache, nachdem in der Maintask ein pop ausgeführt wurde, ist auch kein Problem.
Aber wenn ich einen append mache, während eines pops, das dürfte ein Problem sein, sofern man da nicht lockt. Also ist da ein lock erforderlich?
Oder anders ausgedrückt: laufen die Threadwechsel Pythongesteuert ab oder auf Prozessorebene?
@Alfons Mittelmeyer: Das mit dem Kontextwechsel bei Threads hatte ich gerade im dedizierten Thema dazu beantwortet, darum hier nur kurz: Es werden native Threads verwendet soweit auf dem System vorhanden, man muss also allgemein davon ausgehen das der Wechsel auf Prozessorebene stattfindet wenn man threadsicher programmieren will. `Queue.Queue` aus der Standardbibliothek ist aber beispielsweise von der Dokumentation her garantiert threadsicher und `collections.deque` auch. So etwas muss man sich also nicht aus einer Liste und Sperrobjekten selber basteln.
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Hatte im anderen Thread jetzt auch dazu geschrieben warum es so gemacht wird. Denn durch ein atomares Objekt, wie Fileschreiben, das länger dauert, käme der andere Thread ansonsten in der Zwischenzeit nicht dran.BlackJack hat geschrieben:@Alfons Mittelmeyer: Das mit dem Kontextwechsel bei Threads hatte ich gerade im dedizierten Thema dazu beantwortet, darum hier nur kurz: Es werden native Threads verwendet soweit auf dem System vorhanden, man muss also allgemein davon ausgehen das der Wechsel auf Prozessorebene stattfindet wenn man threadsicher programmieren will. `Queue.Queue` aus der Standardbibliothek ist aber beispielsweise von der Dokumentation her garantiert threadsicher und `collections.deque` auch. So etwas muss man sich also nicht aus einer Liste und Sperrobjekten selber basteln.
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Also hab es mal mit queue (Python3) gemacht. Hier das Modul execute_gui.py:BlackJack hat geschrieben:@Alfons Mittelmeyer: Das mit dem Kontextwechsel bei Threads hatte ich gerade im dedizierten Thema dazu beantwortet, darum hier nur kurz: Es werden native Threads verwendet soweit auf dem System vorhanden, man muss also allgemein davon ausgehen das der Wechsel auf Prozessorebene stattfindet wenn man threadsicher programmieren will. `Queue.Queue` aus der Standardbibliothek ist aber beispielsweise von der Dokumentation her garantiert threadsicher und `collections.deque` auch. So etwas muss man sich also nicht aus einer Liste und Sperrobjekten selber basteln.
Code: Alles auswählen
from spazieren import *
import queue
_queue = queue.Queue()
_root = None
def _execute(*args):
try: _queue.get()()
except: print("execute error")
def init(root):
global _root
_root = root
root.bind("<<EXEC>>",_execute)
def execute(command):
_queue.put(command)
_root.event_generate("<<EXEC>>", when="tail")
Code: Alles auswählen
from spazieren import *
import execute_gui
import threading
class App:
def __init__(self, master):
frame = Frame(master)
frame.pack()
self.button = Button(frame,
text="QUIT", fg="red",
command=frame.quit)
self.button.pack(side=LEFT)
self.slogan = Button(frame,
text="Hello",
command=self.write_slogan)
self.slogan.pack(side=LEFT)
def write_slogan(self):
print("Tkinter is easy to use!")
root = Tk()
app = App(root)
ls()
execute_gui.init(root)
class MyThread(threading.Thread):
def run(self):
while True:
a = input("> ")
try:
command = lambda cmd=compile(a+"\nls()",'<string>', 'exec'): eval(cmd)
execute_gui.execute(command)
except: print("Error:",a)
mythread = MyThread()
mythread.daemon = True
mythread.start()
root.mainloop()
Außerdem hattest Du geschrieben:
Stimmt natürlich nicht ganz, denn den command kann man auch mit Parametern machen. Sozusagen Funktionsaufruf von außen in Gui Callback verwandelt.BlackJack hat geschrieben:@Alfons Mittelmeyer: Events sind auch thread-safe, allerdings benutzt das kaum jemand weil die Implementierung in der `Tkinter`-Anbindung nicht vollständig ist. Man kann nur Ereignisse ohne zusätzlich Daten auslösen, also wirklich nur ein Event mit einem eigenen Namen ohne das man da weitere Informationen mitgeben kann. Wenn einem das reicht, kann man das natürlich so verwenden. In der Praxis habe ich das aber nur *sehr* selten gesehen, in der Regel wird einem immer eine `Queue.Queue` und `after()`/`after_idle()` nahegelegt.
-
- User
- Beiträge: 1715
- Registriert: Freitag 31. Juli 2015, 13:34
Tut mir leid BlackJack, wenn man jetzt die gängige Praxis und die Empfehlungen wohl ändern müssen wird.BlackJack hat geschrieben:In der Praxis habe ich das aber nur *sehr* selten gesehen, in der Regel wird einem immer eine `Queue.Queue` und `after()`/`after_idle()` nahegelegt.