This recipe shows the easiest way of handling access to sockets, serial ports
and other asynchronous I/O ports while running a Tkinter based GUI.
It allows for a worker thread to block in a select(). Whenever something arrives
it will received and inserted in a queue. The main (GUI) thread then polls
the queue 10 times per second (often enough so the user will not notice any
significant delay), and processes all messages that have arrived.
Python, 106 lines
This recipe describes how to handle asynchronous I/O in an environment where
you are running Tkinter as the graphical user interface. Tkinter is safe
to use as long as all the graphics commands are handled in a single thread.
Since it is more efficient to make I/O channels to block and wait for something
to happen rather than poll at regular intervals, we want I/O to be handled
in separate threads. These can communicate in a threasafe way with the main,
GUI-oriented process through one or several queues. In this solution the GUI
still has to make a poll at a reasonable interval, to check if there is
something in the queue that needs processing. Other solutions are possible,
but they add a lot of complexity to the application.
Created by Jacob Hallén, AB Strakt, Sweden. 2001-10-17
def __init__(self, master, queue, endCommand):
self.queue = queue
# Set up the GUI
console = Tkinter.Button(master, text='Done', command=endCommand)
# Add more GUI stuff here
Handle all the messages currently in the queue (if any).
msg = self.queue.get(0)
# Check contents of message and do what it says
# As a test, we simply print it
Launch the main part of the GUI and the worker thread. periodicCall and
endApplication could reside in the GUI part, but putting them here
means that you have all the thread controls in a single place.
def __init__(self, master):
Start the GUI and the asynchronous threads. We are in the main
(original) thread of the application, which will later be used by
the GUI. We spawn a new thread for the worker.
self.master = master
# Create the queue
self.queue = Queue.Queue()
# Set up the GUI part
self.gui = GuiPart(master, self.queue, self.endApplication)
# Set up the thread to do asynchronous I/O
# More can be made if necessary
self.running = 1
self.thread1 = threading.Thread(target=self.workerThread1)
# Start the periodic call in the GUI to check if the queue contains
Check every 100 ms if there is something new in the queue.
if not self.running:
# This is the brutal stop of the system. You may want to do
# some cleanup before actually shutting it down.
This is where we handle the asynchronous I/O. For example, it may be
One important thing to remember is that the thread has to yield
# To simulate asynchronous I/O, we create a random number at
# random intervals. Replace the following 2 lines with the real
time.sleep(rand.random() * 0.3)
msg = rand.random()
self.running = 0
rand = random.Random()
root = Tkinter.Tk()
client = ThreadedClient(root)
This seems to be a common problem, since there is a question about how to do it
a few times a month in comp.lang.pyhton. There are other solutions, involving
synchronisation between threads that will allow you to handle the problem
without the polling (the root.after()), but this is rather un-neat, since you
tend to get raising and waiting for semaphores all over your code.
In any case, a GUI already has a bunch of polling mechanisms built into it
(the main event loop), so adding one won't make much difference - especially
since it runs fairly seldom.
The code has only been tested under Linux, but it should work on any platform
with working threads.