Mailing List Archive

Threaded Tkinter
Hi all.

I have two questions and I'd really appreciate it if someone could
help me sort it out.

My first one -- I'm starting out with Tkinter. I wrote the code below,
which will supposedly create a button with an image, that image
changing every second. If I set the button to call swapIcons() when
pressed, the image changes fine, but using the SpinThread, the icons
don't change. Is this because Tkinter is not thread-safe and I cannot
safely change the button's image property from within a thread other
than the main one?

import Tkinter

class App(Tkinter.Frame):

def __init__(self):
Tkinter.Frame.__init__(self)

self.im1 = Tkinter.Image("photo", "tux", {"file":"oldtux.gif"})
self.im2 = Tkinter.Image("photo", "smiley", {"file":"smiley.gif"})
self.curImage = "tux"
self.b = Tkinter.Button(self)
self.b["image"] = self.curImage
self.b["command"] = self.swapIcons
self.b.pack({"side":"left"})
self.pack()

def swapIcons(self):
if (self.curImage == "tux"):
self.curImage = "smiley"
elif (self.curImage == "smiley"):
self.curImage = "tux"

self.b["image"] = self.curImage

import threading
import time
class SpinThread(threading.Thread):

def __init__(self, app):
threading.Thread.__init__(self)
self.app = app
self.setDaemon(1)

def run(self):
while (1):
self.app.swapIcons()
time.sleep(1)

app = App()
st = SpinThread(app)
st.start()
app.mainloop()


My second question -- I'd like to use Tkinter an Fnorb (CORBA)
together as the UI and the network agent, respectively. Incoming CORBA
calls will change appliation state and cause a change in the UI.

Now, since both Tkinter and Fnorb have their own event loops, I
probably have to run them each in a separate thread. This is fine with
me. The problem is that, if my first problem above is caused by the
fact that I cannot use Tkinter in mutiple threads, I will need to send
UI change requests for processing from within the main thread.

One way I know to do this is to have some sort of queue to which I can
send messages. Tkinter's event loop will have to process the
queue. How do I go about accomplishing this?

If this is not the correct way, what is?

Thanks for any help.

Please forward replies to gutier@intergate.bc.ca.
Threaded Tkinter [ In reply to ]
In article <m3btex4vxu.fsf@tiana.kli.prv>, Gerald Gutierrez
<gutier@intergate.bc.ca> writes
>
>Hi all.
>
>I have two questions and I'd really appreciate it if someone could
>help me sort it out.
>
>My first one -- I'm starting out with Tkinter. I wrote the code below,
>which will supposedly create a button with an image, that image
>changing every second. If I set the button to call swapIcons() when
>pressed, the image changes fine, but using the SpinThread, the icons
>don't change. Is this because Tkinter is not thread-safe and I cannot
>safely change the button's image property from within a thread other
>than the main one?
>
... code & Q2 elided
...
I tried this with 1.5.2 + win95 + local gifs and it worked fine for me.
Should I leave it running for a while?
--
Robin Becker
Threaded Tkinter [ In reply to ]
Gerald Gutierrez wrote:
[snip]
> My first one -- I'm starting out with Tkinter. I wrote the code below,
> which will supposedly create a button with an image, that image
> changing every second. If I set the button to call swapIcons() when
> pressed, the image changes fine, but using the SpinThread, the icons
> don't change. Is this because Tkinter is not thread-safe and I cannot
> safely change the button's image property from within a thread other
> than the main one?

Tkinter is thread safe. Tk is not. To deal with this, the tkinter code
uses a global lock, serializing access to the Tk API.

Because of this, your background thread will block on Tkinter calls
while
the foreground thread is sitting in the mainloop.

You can deal with this by using a queue to communicate from your
background
thread and polling (arghhh!) the queue from the Tk thread like so:

from Tkinter import Tk
tk = Tk()

# ...

def poll():
# clean out the queue
while not myqueue.empty():
processMessage(myqueue.get())

# poll again in 1 second
tk.after(1000, poll)

poll()

Does anyone know of a better way to do this?

Alternately, in the case of your example, you could just put the image
swapping code in the poll() function.

[snip]
> My second question -- I'd like to use Tkinter an Fnorb (CORBA)
> together as the UI and the network agent, respectively. Incoming CORBA
> calls will change appliation state and cause a change in the UI.
>
> Now, since both Tkinter and Fnorb have their own event loops, I
> probably have to run them each in a separate thread. This is fine with
> me. The problem is that, if my first problem above is caused by the
> fact that I cannot use Tkinter in mutiple threads, I will need to send
> UI change requests for processing from within the main thread.
>
> One way I know to do this is to have some sort of queue to which I can
> send messages. Tkinter's event loop will have to process the
> queue. How do I go about accomplishing this?
>
> If this is not the correct way, what is?
>

Fnorb is Tkinter aware: see examples/tkinter. I think it deals with Tk
events and ORB requests in the same thread using select(). In my brief
experiments with this, I've had mixed results.

If the native mechanisms fail you, you can probably use the queue
approach
as identified above.

> Thanks for any help.
>
> Please forward replies to gutier@intergate.bc.ca.


=============================================================================
michaelMuller = proteus@cloud9.net | http://www.cloud9.net/~proteus
-----------------------------------------------------------------------------
Mantra for the 60's: Tune in, turn on, drop out
Mantra for the 90's: Turn on, jack in, jerk off
=============================================================================
Threaded Tkinter [ In reply to ]
On Thu, 3 Jun 1999 gutier@intergate.bc.ca wrote:

>
> Hi Michael.
>
> I just went through your code and tried it out. I think I understand
> what it does now. Thanks for posting it.
>
> For what I would really like to eventually do (processing network
> activity on a separate thread and updating it on a UI), I still need
> to signal a change in UI from another thread. The poll works but, as
> you obviously know, it is suboptimal. What would be best is if one
> could inject a message into Tk's event queue to cause it to
> immediately call a function, which would read a queue and do
> something.
>
> I understand there is an "event" command in Tk, but I know nothing
> about it. Do you think it would be helpful?
[snip]

You can generate custom events under Tkinter, but I think you're going to run
into the same problem with the Tkinter lock: the function that sends the
event is going to block on the Tkinter lock for as long as the main thread
is in the message queue.

From what I've seen of the Tkinter main loop, there are facilities for
obtaining events from sources other than the window event queue,
unfortunately I don't know how to use them.

=============================================================================
michaelMuller = proteus@cloud9.net | http://www.cloud9.net/~proteus
-----------------------------------------------------------------------------
If you are not willing to control your own mind, there are plenty of other
people who are willing to do it for you.
=============================================================================
Threaded Tkinter [ In reply to ]
On Thu, 3 Jun 1999 gutier@intergate.bc.ca wrote:

>
> Hi Michael.
>
> Thanks for the suggestion.
>
> I had been mulling over the idea of somehow getting the Tk event loop
> to periodically call a method of mine which would read a queue and do
> what's needed. Would the lock you describe interfere with this in any
> way? If not, how do I go about injecting messages into the Tk event
> loop?
>
> Also, as you can guess I know little about Tk. Where is the event loop
> in the code you provided below? Does the tk.after() call look at Tk's
> event loop and perform necessary actions?
[snip]

I omitted the event loop call in this example. Add the line:

tk.mainloop()

This is the standard event loop, and it will continue to process events
until the application is terminated.

tk.after() creates a "timer"; the event handler subsequently reacts to the
timer and calls the function at the appropriate time.

=============================================================================
michaelMuller = proteus@cloud9.net | http://www.cloud9.net/~proteus
-----------------------------------------------------------------------------
Mantra for the 60's: Tune in, turn on, drop out
Mantra for the 90's: Turn on, jack in, jerk off
=============================================================================