Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Tkinter: asynchrone GUI-Aufrufe

9 views
Skip to first unread message

Thomas Rachel

unread,
Feb 3, 2010, 12:29:52 PM2/3/10
to
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()
-----

Thomas Lenarz

unread,
Feb 7, 2010, 3:42:36 AM2/7/10
to
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

Thomas Rachel

unread,
Feb 9, 2010, 4:03:14 AM2/9/10
to
Am 07.02.2010 09:42, schrieb Thomas Lenarz:

> 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.

Schade. Trotzdem danke für die Antwort. Meine bisherigen Google-Versuche
führten leider auch zu keinem Ergebnis, es kam immer nur der Rat mit dem
Pollen. Unschön, aber scheint wirklich die einzige Möglichkeit zu sein.


> 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.

Das stimmt - im allgemeinen Fall. Wenn aber - wie in meinem Fall - die
aufgerufene Funktion (oder Generator) einmal mehrere hundert ms oder gar
Sekunden brauchen kann, würde das dazu führen, daß während der Zeit die
GUI eben nicht ansprechbar ist. Weiteres Aufsplitten ist im vorliegenden
Fall leider nicht trivial möglich.


Viele Grüße,
Thomas

0 new messages