tkinter multithreaded benutzen

Code-Stücke können hier veröffentlicht werden.
Antworten
Costi
User
Beiträge: 545
Registriert: Donnerstag 17. August 2006, 14:21

Code: Alles auswählen

class TkThreadAccess:

	def __init__(self, root):
		self.jobs = []
		self.root = root
	
	def blocking_do(self, func):
		lock = threading.Event()
		val = {}
		def tmp1(*args, **kw):
			def tmp2():
				val[None] = func(*args, **kw)
				lock.set()
			self.do(tmp2)
			lock.wait()
			lock.clear()
			return val[None]
			
		return tmp1
		
	
	def do(self, func):
		self.jobs.append(func)
	
	def update(self):
		self.root.update()
		while self.jobs:
			self.jobs.pop(0)()
	
	def mainloop(self):
		while True:
			self.update()
			sleep(0.1)
cp != mv
BlackJack

@Costi: Muss ich das jetzt verstehen!?
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Ich versteh es auch nicht, vorallem da Tkinter als relativ Thread-safe gilt.

Siehe hier und hier der Script auf den sich van Rossum bezieht.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Costi
User
Beiträge: 545
Registriert: Donnerstag 17. August 2006, 14:21

genau, relativ thread safe.
was man zum beispiel nicht machen kann ist es einen Toplevel fenster in einen thread zu erstellen und sein .mainloop im main thread aufzurufen (wann das sinnvoll ist oder nicht spielt keine rolle).


hier ein beispiel:

Code: Alles auswählen

from Tkinter import *
import threading
from time import sleep


SAFE = True


root = Tk()
e = []

if SAFE:
	ta = TkThreadAccess(root)

def in_thread():
	if SAFE:
		top = ta.blocking_do(Toplevel)()
	else:
		top = Toplevel()
	e.append(top)


threading.Thread(target=in_thread).start()


if SAFE:
	for i in range(10):
		ta.update()

e.pop().mainloop()

TkThreadAccess und dieses beispiel sind nicht optimal implementiert, es geht eher um das prinzip:
statt direkt mit einer funktion an der gui rumzufummeln wird die funktion dem main thread uebergeben, der sie ausfuert und gegebenenfalls das ergebnis dem thread zurueckgeliefert.

in diesen fall haette man bei SAFE = False folgenden fehler

Code: Alles auswählen

Exception in thread Thread-1:
Traceback (most recent call last):
  File "/usr/lib/python2.6/threading.py", line 532, in __bootstrap_inner
    self.run()
  File "/usr/lib/python2.6/threading.py", line 484, in run
    self.__target(*self.__args, **self.__kwargs)
  File "test.py", line 88, in in_thread
    top = Toplevel()
  File "/usr/lib/python2.6/lib-tk/Tkinter.py", line 1978, in __init__
    BaseWidget.__init__(self, master, 'toplevel', cnf, {}, extra)
  File "/usr/lib/python2.6/lib-tk/Tkinter.py", line 1935, in __init__
    (widgetName, self._w) + extra + self._options(cnf))
RuntimeError: main thread is not in main loop

(blocking_do sollte besser wrap heissen)
cp != mv
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Es gibt dennoch bei weitem bessere Implementierungen, als das was du dort hingeschrieben hast.
Vorallem dein tmp-Funktionspart ist nun nicht besonders schön.
Eigentlich ist alles was du brauchst eine Queue, das reicht eigentlich schon völlig, um Tkinter vollständig thread-safe zu bekommen.

btw, nutz bitte 4 Leerzeichen, das ist ja kaum Lesbar.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Costi
User
Beiträge: 545
Registriert: Donnerstag 17. August 2006, 14:21

hier eine ueberarbeitet version die eigentlich alles pseudo thread safe machen kann:

Code: Alles auswählen

class EvalInMainloopCaller:

	def __init__(self):
		self.jobs = []
	
	def wrap(self, func):
		lock = threading.Event()
		val = {}
		def tmp1(*args, **kw):
			def tmp2():
				val[None] = func(*args, **kw)
				lock.set()
			self.do(tmp2)
			lock.wait()
			lock.clear()
			return val[None]
			
		return tmp1
		
	
	def do(self, func):
		self.jobs.append(func)
	
	def update(self):
		while self.jobs:
			self.jobs.pop(0)()
	
	def mainloop_with_root(self, root):
		while True:
			root.update()
			self.update()
			sleep(0.1)

welche vorteile hat die Queue eigentlich ueber einer liste? ist es schneller?

EDIT:
ok, stimmt, die queue ist schneller....
cp != mv
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

http://docs.python.org/library/queue.html
Les dir den kleinen Abschnitt mal durch.

Die Vorteile sollten eigentlich sofort ersichtlich sein, denn eine Queue ist Tread-Safe, was eine Liste, wie du sicher bermerkt hast, nicht ist.
Du verenkst dich da in etwas, was es im Prinzp schon gibt.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Costi
User
Beiträge: 545
Registriert: Donnerstag 17. August 2006, 14:21

was gibt es den schon, dann muss ich es nicht selber machen...
cp != mv
BlackJack

Also ich hab's immer noch nicht verstanden was mir das bringt. Kann ich mal ein Beispiel sehen wo mehrere Threads auf die GUI zugreifen? Denn nur dass ist ja problematisch.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

@Costi
Ich meine damit, das Tkinter schon Thread-Safe ist und bei einigen Stellen, wie zB input in ein TextWidget, ganz einfach eine Queue genommen werden kann.

Und BlackJack hat recht, an welcher Stelle brauchst du das überhaupt ?
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Costi
User
Beiträge: 545
Registriert: Donnerstag 17. August 2006, 14:21

ich habe ein gui, und ein thread der arbeitet.
der thread aendert manchmal die gui und startet einamal selber einen dialog um werte zu bekommen. dieser dialog ist mit Toplevel implementiert. und da habe wir den salat.
beziungsweise an einer anderen stelle gibts auch probleme, da wird einfach der state eines buttons ueber den thread auf "active" gestellt, das sollte doch funktionieren?

EDIT:
beziungsweise das mit dem toplevel sollte glaube ich eigentlich auch funktionieren, tut es aber nicht...
cp != mv
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

:shock: wie habe ich mir das Vorzustellen ?

Das Programm läuft und ich sitz davor bis sich irgendwann mal was tut und dann irgendwas aufpoppt ?

Das scheint mir keine gute Idee für eine GUI, also was versuchst du da eigentlich ?
Wenn irgendwas in einem bestimmten Intervall starten willst gibt es zB auch die Methode "after(...)".

Ansonsten, kannst du mal eine betreffende Strucktur als Minimal-Beispiel posten ?
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Costi
User
Beiträge: 545
Registriert: Donnerstag 17. August 2006, 14:21

die gui laeuft, und der thread popt eine andere gui auf.

und naja, das ganze laeuft jetzt, etwas hacky aber was solls.
ich mach noch die bugs die kommen werden weg aber wirklich wissen warum das ganze irgendwie funktioniert will ich nicht :D

es gibt software die kann man einfach nicht sauber schreiben...
cp != mv
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

Nein, man kann jede Software sauber schreiben, es kommt nur ob es dem Kosten-Nutzen Faktor an. :)
Dennoch sollte man verstanden haben, was man dort eigentlich tut, sonst bringt das meist recht wenig egal ob sauber oder nicht.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
BlackJack

@Costi: Wenn Du tatsächlich aus mehreren Threads auf `Tk` zugreifst, dann ist das schlicht und einfach falsch! Das ist nicht sicher. Selbst wenn es bei allen Deinen Tests läuft, ist das kein Grund zu sagen es funktioniert. Du hast dann einfach nur Glück.

Und so sauber, dass man nicht absichtlich Sachen macht, von denen man *weiss*, dass sie nur mit Glück nicht *hart* Abstürzen, sollte man schon programmieren.
Costi
User
Beiträge: 545
Registriert: Donnerstag 17. August 2006, 14:21

@BlackJack
ich weiss, deswegen benutze ich ja auch das gepostete TkThreadAccess ... der thread uebergibt den main thread den code der ausgefuert werden soll stat es selber auszufueren.
und hey, schonmal windows gesehen.... wenn das kein (mehr oder weniger) funktionierendes gefrickel ist!*

*im gegensatz zu linux mit klaren unterteilungen, packetmanagement, mehr sicherheit, etc


@Xynon1
ich weiss nicht, wie waers mit einen Perl interpreter geschrieben in Malbolge[1] :lol:


http://de.wikipedia.org/wiki/Malbolge
cp != mv
BlackJack

@Costi: Ich sehe aber nicht wie Dein `TkThreadAccess` da helfen soll. Du zeigst ja kein Beispiel wie man das überhaupt verwenden soll. Ich versteh's nicht.
Xynon1
User
Beiträge: 1267
Registriert: Mittwoch 15. September 2010, 14:22

@Costi
Malbolge, würde ich zwar nicht als Programmier-, sondern als Scherzsprache ansehen.
Aber mal abgesehen davon, kann man damit auch "sauber" programmieren, in den man einen Übersetzer schreibt, was ja auch alle Programmierer, die sich damit beschäftigt haben getan haben.
Wie in dem von dir erwähnten Artikel steht, wurde das erste Programm mit Lisp generiert.
Traue keinem Computer, den du nicht aus dem Fenster werfen kannst.
Xynon auf GitHub
Antworten