Gui-Anderung einer Eigenschaft in einem anderen Modul

Fragen zu Tkinter.
BlackJack

@Alfons Mittelmeyer: Hundert Zeilen Code für etwas was andere GUI-Rahmenwerke schon mitbringen ist viel. Insbesondere in einer ausdrucksstarken Programmiersprache.

Das was Du da machst ist Stümperei denn da fehlt der Nachweis das `event_generate()` threadsicher ist. Von den Tcl-Wikiseiten zum Thema „Tcl and threads“ und den Regeln für das einbetten von Tcl in eine Anwendung mit Threads: „You can NEVER use the same interpreter from more than one thread“. Die Hervorhebung ist *nicht* von mir, die steht da in gross und fett im Original, scheint also ernst gemeint und wichtig zu sein. Solange also `Tkinter` keine Garantien über das was Tk/Tcl nicht garantiert hinaus gibt, dürfte das erklären warum man immer nur so ”stümperhafte” Polling-Beispiele mit Queue und `after()` findet: Das ist halt das einzige was tatsächlich threadsicher funktioniert. Die einzige Möglichkeit ist also schlecht. Okay… :-)
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben:Von den Tcl-Wikiseiten zum Thema „Tcl and threads“ und den Regeln für das einbetten von Tcl in eine Anwendung mit Threads: „You can NEVER use the same interpreter from more than one thread“.
Genau darum geht es, dass eben durch das Event die Funktion nicht von der andern Task ausgeführt wird, sondern dann erst von der GUI, wenn sie dran ist. Hier steht etwas über event_generate: Tkinter: is event_generate safe from background thread?

Und ein Eric Brunel hatte genau dasselbe getan, wie ich hier tue. Dann geht es um die Frage, ob event_generate threadsicher ist.

event_generate so zu benutzen ist falsch: app.event_generate("<<myevent1>>")
Hier wird nämlich das Event unmittelbar ausgeführt, ohne in die GUI Task zu wechseln.

Aber so ist es richtig: app.event_generate("<<myevent1>>", when='tail')
Hier wird das Event in die tkinter Event Queue geschrieben. Und in dieser Form ist event_generate threadsicher.

Eric Brunel hatte das dazu geschrieben:
This is where the problem is: if you do just a event_generate without
specifying the 'when' option, the binding is fired immediately in the
current thread. To be sure that an event is created and that the thread
switch actually happens, do:

app.event_generate("<<myevent1>>", when='tail')

and things should work fine.
Quelle: http://bytes.com/topic/python/answers/5 ... er-threads

Und wenn Du es genau wissen willst, musst Du Dir the Tcl/Tk Dokumentation durchlesen: http://www.tcl.tk/man/

Also hier ist die Doku:
event generate window event ?option value option value ...?
Generates a window event and arranges for it to be processed just as if it had come from the window system. Window gives the path name of the window for which the event will be generated; it may also be an identifier (such as returned by winfo id) as long as it is for a window in the current application. Event provides a basic description of the event, such as <Shift-Button-2> or <<Paste>>. If Window is empty the whole screen is meant, and coordinates are relative to the screen. Event may have any of the forms allowed for the sequence argument of the bind command except that it must consist of a single event pattern, not a sequence. Option-value pairs may be used to specify additional attributes of the event, such as the x and y mouse position; see EVENT FIELDS below. If the -when option is not specified, the event is processed immediately: all of the handlers for the event will complete before the event generate command returns. If the -when option is specified then it determines when the event is processed. Certain events, such as key events, require that the window has focus to receive the event properly.

-when When determines when the event will be processed; it must have one of the following values:

now
Process the event immediately, before the command returns. This also happens if the -when option is omitted.

tail
Place the event on Tcl's event queue behind any events already queued for this application.

head
Place the event at the front of Tcl's event queue, so that it will be handled before any other events already queued.

mark
Place the event at the front of Tcl's event queue but behind any other events already queued with -when mark. This option is useful when generating a series of events that should be processed in order but at the front of the queue.
Quelle: https://www.tcl.tk/man/tcl/TkCmd/event.htm
Zuletzt geändert von Alfons Mittelmeyer am Donnerstag 3. September 2015, 12:22, insgesamt 1-mal geändert.
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@Alfons Mittelmeyer: Ob der Threadkontext gewechselt wurde, kannst Du mit `threading.currentThread()` überprüfen. In callbacks, die die GUI manipulieren, sollte dort der GUI-Thread (idR der MainThread) auftauchen.
Bei Qt z.B. muss man höllisch aufpassen, was Signal- und Eventauslieferung über mehrere Threads angeht. So muss man dort einen Eventloop in Subthreads explizit starten, damit überhaupt die Auslieferung von Mainthread nach Subthread funktioniert. KA, inwiefern Tkinter sowas unterstützt.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

Und was professionelle Softwareedntwickler überprüft haben: after und after_idle nicht threadsicher. Nur event_generate threadsicher!
Hi,

afaik the after() / after_idle() calls are not thread safe!

On my research leading to the code Andreas provide I found the
event_generate() method the only threadsafe way to invoke the tk
thread without letting it poll something. (As mentioned by Guido and
seen on other pages everyone else seems to poll... why?)

brgds,

-- Jan
Quelle: https://mail.python.org/pipermail/tkint ... 03522.html

Und Jan Uebernickel wundert sich auch, warum anscheinend fast alle after nehmen und pollen. Muss wohl an 'sogenannten Experten' liegen, die Nonsens verbreiten.

Was hat Guido van Rossum geschrieben?
> I've tried other things:
>
> - createfilehandler: This says it cannot work when Tcl/Tk is threaded and it
> will be removed in Python 3 anyway.
>
> - The bg thread can use after_idle() or after() to run a command on the Tk
> object; this works, but it doesn't seem to wake up the Tk main loop -- the
> command usually only runs when I cause a UI event to be generated (e.g.
> selecting the window).
>
> What other solutions could I try? I really don't like the "after(100,
> <repeat>)" solution.
>
> --
> --Guido van Rossum (python.org/~guido)
Selbe Quelle wie vorher.

Also Probleme mit after und after_idle
Zuletzt geändert von Alfons Mittelmeyer am Donnerstag 3. September 2015, 12:40, insgesamt 2-mal geändert.
BlackJack

@Alfons Mittelmeyer: Durch ``when='tail'`` wird das Ereignis an das Ende der Warteschlange gesetzt, das garantiert aber nicht das diese Operation threadsicher ist, denn diese Warteschlange wird in dem Augenblick von zwei Threads aus manipuliert. Und Tcl ist nicht threadsicher. Was garantiert dann also in diesem Fall Deiner Meinung nach die Threadsicherheit? Das das Eric Brunel 2006 scheinbar unter drei Betriebssystemen nicht offensichtlich auf die Nase gefallen ist, macht das ganze nicht threadsicher. Die Aussage „[…] and things should work fine.” auch nicht.

Da Du ja auf die offizielle Tcl/Tk-Dokumentation verwiesen hast, daraus ein Auszug: „An important constraint of the Tcl threads implementation is that only the thread that created a Tcl interpreter can use that interpreter. In other words, multiple threads can not access the same Tcl interpreter.“

Auch hier ist die Hervorhebung wieder aus dem Original übernommen und nicht von mir.

Wenn Du also `event_generate()` aus einem anderen Thread heraus verwendest musst Du auch stichhaltig begründen können warum das sicher sein soll obwohl die Dokumentation ausdrücklich sagt dass das nicht vorgesehen ist. Ein „aber bis jetzt ist nichts schlimmes passiert“ reicht da nicht. Insbesondere nicht mit einer Python-Implementierung mit GIL, denn sollte *das* der Grund sein warum es ”funktioniert” dann baust Du auf Sand.

Deine Implementierung ist falsch, da fehlerhaft, da sie auf falschen Annahmen aufbaut.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@BlackJack Komisch dass Du meinst, Du wüsstest es besser als Guido van Rossum, sodass Du sagen kannst, was der schreibt, ist falsch.

Und kapieren tust Du anscheinend auch nichts, weshalb Du das ständig wiederholst:
Da Du ja auf die offizielle Tcl/Tk-Dokumentation verwiesen hast, daraus ein Auszug: „An important constraint of the Tcl threads implementation is that only the thread that created a Tcl interpreter can use that interpreter. In other words, multiple threads can not access the same Tcl interpreter.“
Deshalb benutzt man ja event_generate, damit nicht die andere Task die Tcl Kommandos ausführt, sondern nur ein Event in die Tcl Event Queue geschrieben wird. Wenn dann der Tcl Interpreter wieder dran ist, dann führt dieser die damit verbundene Funktion aus. Begreifen fällt Dir anscheinend schwer.
Zuletzt geändert von Alfons Mittelmeyer am Donnerstag 3. September 2015, 13:01, insgesamt 1-mal geändert.
BlackJack

@Alfons Mittelmeyer: Wo kommt denn jetzt van Rossum ins Spiel? Komisch da Du mal wieder meinst es besser zu wissen als die Dokumentation. Und hier kommt es nicht auf Meinungen an: Zeig einfach technisch nachvollziehbar auf warum das threadsicher sein soll unter der Annahme das Tcl/Tk das nicht ist. Das wäre dann ja problemlos möglich. Was genau sichert von der Python-Anbindung aus den Tcl/Tk-internen Zugriff auf dessen Ereigniswarteschlange ab?
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben:@Alfons Mittelmeyer: Wo kommt denn jetzt van Rossum ins Spiel?
Lesen kannst Du anscheinend auch nicht, denn ich hatte einen Auszug gepostet, gezeichnet mit "--Guido van Rossum (python.org/~guido)". Also hier nochmals: https://mail.python.org/pipermail/tkint ... 03522.html
Benutzeravatar
sparrow
User
Beiträge: 4187
Registriert: Freitag 17. April 2009, 10:28

Alfons Mittelmeyer hat geschrieben:
BlackJack hat geschrieben:@Alfons Mittelmeyer: Wo kommt denn jetzt van Rossum ins Spiel?
Lesen kannst Du anscheinend auch nicht, denn ich hatte einen Auszug gepostet, gezeichnet mit "--Guido van Rossum (python.org/~guido)". Also hier nochmals: https://mail.python.org/pipermail/tkint ... 03522.html
Oh mein Gott. 100 Jahre Programmiererfahrung und nicht in der Lage auf einer Mailingliste zu sehen wer derjenige ist, der fragt?
jerch
User
Beiträge: 1669
Registriert: Mittwoch 4. März 2009, 14:19

@Alfons:
Wie alt bist Du eigentlich? Die Angriffe auf Personen stärken nicht Deine Argumente sondern machen DICH lächerlich. Dachte eigentlich, Du hättest meine Anspielung weiter oben verstanden, aber das Sandkastenniveau musste wohl trotzdem her.

1+ fürs Sperren.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

jerch hat geschrieben:@Alfons:
Wie alt bist Du eigentlich? Die Angriffe auf Personen stärken nicht Deine Argumente sondern machen DICH lächerlich. Dachte eigentlich, Du hättest meine Anspielung weiter oben verstanden, aber das Sandkastenniveau musste wohl trotzdem her.

1+ fürs Sperren.
Angriffe? BlackJack ist es, der laufend angreift und nicht auf die Antworten eingeht, sondern ständig wieder dasselbe bringt. Und schreibt, dass ich es beweisen müsste. Und jetzt beginnt wohl wieder der allgemeine Angriff von Dir und von anderen auf mich? Und dann wohl wieder sperren?
Benutzeravatar
jens
Python-Forum Veteran
Beiträge: 8502
Registriert: Dienstag 10. August 2004, 09:40
Wohnort: duisburg
Kontaktdaten:

Alfons Mittelmeyer hat geschrieben:Angriffe? BlackJack ist es, der laufend angreift und nicht auf die Antworten eingeht, sondern ständig wieder dasselbe bringt. Und schreibt, dass ich es beweisen müsste. Und jetzt beginnt wohl wieder der allgemeine Angriff von Dir und von anderen auf mich? Und dann wohl wieder sperren?
Bisher war es im Prinzip nie notwendig hier Thread zu sperren. Eigentlich hatten wir auch immer einen recht Freundlichen Tonfall... Doch seid du da bist, ist das in deinen Threads irgendwie nicht mehr so. Das sollte dir zu denken geben.

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

@Alfons Mittelmeyer: Van Rossum schreibt da das er keine Ahnung vom Thema hat und das ihm das Pollen, was seiner Beobachtung nach anscheinend *alle* machen nicht gefällt. Dem widerspreche ich jetzt wo genau?

Ich habe nicht behauptet das Pollen mit `after()` eine schöne Lösung ist. Nur das es die gängige Praxis ist. Und offensichtlich aus dem Grund das es das einzige ist was threadsicher ist, weil Tcl/Tk laut dessen Dokumentation *grundsätzlich* nur aus einem Thread heraus benutzt werden darf, also insgesamt *nicht* threadsicher ist. Da bleibt ja nur das Pollen aus dem einen Thread heraus den man verwenden darf.

Ich greife eine technisch kaputte ”Lösung” an. Du wirst persönlich und pampig weil es Dir anscheinend nicht gefällt das Du hundert Zeilen ach so genialen Code produziert hast, der nicht threadsicher ist, aber eben genau für den Einsatz von mehreren Threads gedacht ist.
Benutzeravatar
snafu
User
Beiträge: 6738
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

jens hat geschrieben:Bisher war es im Prinzip nie notwendig hier Thread zu sperren. Eigentlich hatten wir auch immer einen recht Freundlichen Tonfall... Doch seid du da bist, ist das in deinen Threads irgendwie nicht mehr so. Das sollte dir zu denken geben.
Das stimmt jetzt aber auch nicht so ganz. Hier sind über die Jahre immer wieder mal irgendwelche skurrilen Typen aufgetaucht und ab und zu waren auch Threadsperrungen nötig.

Ich stimme der Kritik am Code von Alfons und zum Teil auch der Kritik an ihm als Person durchaus zu. Aber man sollte IMHO aufpassen, dass man nicht ins pure Bashing abgleitet.

Meiner Meinung nach dürften wir alle vernünftig genug sein, um Personen, die uns möglicherweise missfallen, *nicht* unbegründet zu schikanieren. Wie gesagt: Kritik an Codeschnipseln halte ich für berechtigt (wie die Person das aufnimmt, steht auf einem anderen Blatt). Kritik an bestimmten - konkret benannten - Äußerungen geht ebenfalls in Ordnung. Aber Alfons nun als das größte Übel von allen hinzustellen, finde ich doch arg übertrieben.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@Alfons Mittelmeyer: etwas erwiesenermaßen threadsicher zu programmieren ist schwierig. Quasi unmöglich wird es, wenn externe Bibliotheken zum Einsatz kommen, die nachweislich nicht threadsicher sind. Dann bleibt eigentlich nur, zu garantieren, dass nur EIN EINZIGER Thread auf diese Bibliothek zugreift.

event_generate hat zwei Modi:
1. direkt ausführen (ohne when=): da sieht auch jemand ohne Erfahrung im Thread-Programmieren, dass das nicht gut geht.
2. verzögert ausführen (mit when=): da denkt jemand, der keine Erfahrung im Thread-Programmieren hat, dass sich das doch gut anhört.

Dazu muß aber das Event in eine Queue eingetragen werden, also in Pseudo-Code ungefähr das:

Code: Alles auswählen

QUEUE[INDEX] = EVENT
INDEX += 1
Wenn das jetzt zwei Threads A und B gleichzeitig machen wollen, kann das passieren:

Code: Alles auswählen

A: QUEUE[INDEX] = EVENT_A
B: QUEUE[INDEX] = EVENT_B
B: INDEX += 1
A: INDEX += 1
Event B überschreibt Event A und wir haben ein uninitialisiertes Element in der Queue. Im besten Fall stürzt das Programm jetzt ab. Das heißt nicht threadsicher und soetwas kann so oder so ähnlich bei Tk immer passieren, wenn man mit Threads nicht aufpasst.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Alfons Mittelmeyer hat geschrieben:Und schreibt, dass ich es beweisen müsste.
Nun ja, das ist allgemein so üblich, dass derjenige in der Beweispflicht steht, der von der Norm abweichende Thesen aufstellt ;-)
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
BlackJack

Zumal es hier darum geht das behauptet wird dort würde etwas existieren. Also kann man das ja auch zeigen. Umgekehrt kann ich nicht beweisen das es nicht existiert weil da ja eben nichts ist auf das ich zeigen könnte.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

@Sirius3 Finde ich gut, dass Du es sachlich angehst. Das wäre aber dann einen eigenen Thread hier wert, nämlich wie sicher ist die Tcl/Tk Event Queue. Es kann ja sein, dass jemand die Maus bewegt, während er sich nicht in der Tcl/Tk Task befindet. Der Maus Handler - in einer anderen Task, und das ist sogar normal - schreibt aber das Maus Event in diese Event Queue. Kann es dann passieren, dass bei Bewegung der Maus oder beim Klicken das System abstürzt, weil wir uns gerade nicht in der Tcl/Tk Task befinden?

Da müßte man sehr in die Implementierungsdetails gehen und diese Event Queue und den Befehl event_generate, den es dann auch in C etwa für den Maus Handler gibt, genauer untersuchen.

Die andere Frage wäre dann auch, was ist mit after? After soll ja nicht die mainloop aufwecken. Was ist, wenn wir etwas während after etwas machen, das etwa <Configure> betrifft. Hat das dann einen Effekt oder nicht?

Also was meinst Du, sollte man besser die Maus nicht klicken, weil das eventuell nicht threadsicher wäre und das System dabei abstürzen könnte? Denn genau darum geht es, ein Event in diese Queue zu schreiben.
Also nach Meinung von einigen sollte man es wohl besser unterlassen, die Maus zu klicken, solange es nicht bewiesen ist, dass das threadsicher ist.
BlackJack

@Alfons Mittelmeyer: Man muss da eben nicht in die Implementierungsdetails gehen denn die sind nicht die öffentliche Programmierschnittstelle. Das ist ja gerade die wichtige Eigenschaft von Implementierungsdetails, das man sich nicht auf sie verlassen kann und darf. Öffentlich ist die Informationen das man den Tcl-Interpreter nur von einem Thread aus benutzen darf. Damit ist alles gesagt was man wissen muss. Wer es doch tut schreibt Code der funktionieren kann, aber nicht muss, und schlicht und einfach falsch ist.

Edit:Die Ereignisse für Mausklicks werden Tk-intern in diese Queue geschrieben, also innerhalb des Threads in dem die Hauptschleife läuft, womit das sicher ist weil daran nur dieser eine Thread beteiligt ist.
Alfons Mittelmeyer
User
Beiträge: 1715
Registriert: Freitag 31. Juli 2015, 13:34

BlackJack hat geschrieben:Öffentlich ist die Informationen das man den Tcl-Interpreter nur von einem Thread aus benutzen darf. Damit ist alles gesagt was man wissen muss. Wer es doch tut schreibt Code der funktionieren kann, aber nicht muss, und schlicht und einfach falsch ist.
Bitte erkennen, begreifen, verstehen. Damit man den Tcl-Interpreter in dem richtigen Thread verwendet, schreibt man nur etwas in die Queue von ihm und weckt ihn dadurch auf. Und daher habe ich das mit dem Event genau richtig implementiert.
BlackJack hat geschrieben:Edit:Die Ereignisse für Mausklicks werden Tk-intern in diese Queue geschrieben, also innerhalb des Threads in dem die Hauptschleife läuft, womit das sicher ist weil daran nur dieser eine Thread beteiligt ist.
Das ist ebenfalls nicht wahr. Das hieße ja, solange Du Dich nicht in dem Thread der Hauptschleife befindest, kannst Du klicken, was Du willst und es passiert nichts. Also kann man tkinter vergessen, weil Du klicken kannst was Du willst, ohne jeden Effekt. Richtig ist, dass man erst etwas in die Queue schreiben muß, um die mainloop aufzuwecken.

Und was macht event_generate? Genau dasselbe, was ich bei den anderen Threads getan habe:

Code: Alles auswählen

    def send(self,msgid,msgdata=None):
        self.Queue.put((msgid,msgdata))
        self.event.set()
Schreibt etwas in eine threadsichere Queue und setzt das event des Threads, damit der aus dem lock von wait() in der Event loop aufwacht.
Statt send, musst Du Dir eben event_generate vorstellen, wenn Du dazu in der Lage bist.
Antworten