Tkinter: asynchrone GUI-Aufrufe

03/02/2010 - 18:29 von Thomas Rachel | Report spam
Hallo,


folgendes Problem: ich habe eine GUI-Anwendung, die ihre Arbeit in einem
Thread erledigt.
Da ja bekanntlich GUI-Aufrufe nur aus dem eienn GUI-Thread zulàssig
sind, will/muß ich anstehende GUI-Jobs (Aktualiserung etc.) entsprechend
queuen, so daß der eigentliche Aufruf innerhalb des GUI-Threads erledigt
wird.

Nun dachte ich eigentlich, das dafür geeignete Werkzeug sei
after_idle(). Allerdings scheint dem nicht so zu sein; wenn ich dieses
verwende, hagelt es "pythonw.exe hat ein Problem festgestellt und muss
beendet werden." (muß leider unter Windows arbeiten). Irgendwas in
tcl85.dll.

Offenbar ist auch der Aufruf von after_idle() aus einem Fremdthread
verboten, da es sich ja ebenfalls um eine Tk-Funktion handelt. Eine
Alternativlösung, wo die GUI alle x ms mittels after() immer wieder
dieselbe Funktion aufruft, die den nàchsten GUI-Task aus einer Queue
zieht, funktioniert hingegen. (Aktivierung im untigen Programm durch
Setzen von POLLING auf 1).

Gibt es eine elegante Lösung, die ohne Polling auskommt? Sowas wie
after_idle(), nur eben in funktionierend? Sowas wie asyncExec() in Java?


Zusatzfrage: Auch wenn ich POLLING=1 habe, erscheint am Ende des Programms




invalid command name "12598008callit"






while executing
"12598008callit"
("after" script)

WOher kommt das? Kann ich das vermeiden?


TIA,

Thomas



Es geht um folgendes Programm als Minimalbeispiel:
#!/usr/bin/env python
# coding: latin1

from Tkinter import *

from threading import *

POLLING=0

import time

class CallLoop(object):
"""after_idle in funktionierend. period ist in ms.
"""
def __init__(self,master,period=5):
import Queue
self.period=period
self.queue=Queue.Queue()
self.master=master
master.after(0,self.poll)
def put(self,task,*a,**k):
"""Job in Queue legen."""
if a or k:
self.queue.put(lambda: task(*a,**k))
else:
self.queue.put(task)
def poll(self):
"""Endlosschleife."""
import Queue
try:
task=self.queue.get_nowait()
try:
task()
period=0
except: # swallow exceptions
pass
except Queue.Empty:
period=self.period
self.master.after(period,self.poll)
__call__=poll


class Application(Tk):
def __init__(self,use_call_loop):
Tk.__init__(self)
text1=StringVar()
text2=StringVar()
Entry(self,textvariable=text1).pack()
Entry(self,textvariable=text2).pack()
self.text=(text1,text2)

if use_call_loop:
cl=CallLoop(self)
self.my_after_idle=cl.put
else:
self.my_after_idle=self.after_idle

def settext(self,n,text):
self.my_after_idle(self.text[n].set,text)

def displayloop(self,n,it,t=.04):
for s in it:
self.settext(n,s)
time.sleep(t)

def mkcounter():
import itertools
return ("%06x"%i for i in itertools.count())

# Hauptprogramm:

app = Application(use_call_loop=POLLING)

Thread(target=app.displayloop,args=(0,mkcounter(),.03)).start()
Thread(target=app.displayloop,args=(1,mkcounter(),.05)).start()

app.mainloop()
 

Lesen sie die antworten

#1 Thomas Lenarz
07/02/2010 - 09:42 | Warnen spam
Thomas Rachel schrieb:
Da ja bekanntlich GUI-Aufrufe nur aus dem eienn GUI-Thread zulàssig
sind, will/muß ich anstehende GUI-Jobs (Aktualiserung etc.) entsprechend
queuen, so daß der eigentliche Aufruf innerhalb des GUI-Threads erledigt
wird.
[...]
Gibt es eine elegante Lösung, die ohne Polling auskommt? Sowas wie
after_idle(), nur eben in funktionierend? Sowas wie asyncExec() in Java?


Hallo Thomas,
ja, es sollte definitiv eine Möglichkeit geben, aus einem Worker-Thread
eine Nachricht an den Event-Dispatch-Thread abzusetzen, um ein Ereignis
zu visualisieren. Ich kenne sie allerdings auch nicht.

Nur vielleicht ein Tipp, der evtl. helfen könnte: Manchmal ist
kooperatives Multitasking ("Polling") angenehmer, weil deterministischer
und besser zu debuggen. In Python kann man hierzu geschickt
Generator-Funktionen (yield) nutzen. Dies hat den Vorteil, dass die
Funktion, die man unterbricht, um die Rechenzeit einer anderen zur
Verfügung zu stellen, sich automatisch ihren eigenen Zustand merkt.
Siehe http://www.python.org/doc/2.3.5/ref/yield.html.

Dies ist meiner Erinnerung nach auch sehr schön in folgendem Buch
beschrieben:

Exploring Python von Markus Nix, Torsten Marek, Martin Grimme, und
Michael Weigend

Viele Grüße
Thomas

Ähnliche fragen