We might also use tkinter’s createfilehandler to register a callback to be run when
input shows up on the input pipe:
def callback(file, mask):
...read from file here...
import _tkinter, tkinter
_tkinter.createfilehandler(file, tkinter.READABLE, callback)
The file handler creation call is also available within tkinter and as a method of a Tk
instance object. Unfortunately again, as noted near the end of Chapter 9, this call is
not available on Windows and is a Unix-only alternative.
Avoiding blocking input calls with non-GUI threads
As a far more general solution to the blocking input delays of the prior section, the GUI
process might instead spawn a thread that reads the socket or pipe and places the data
on a queue. In fact, the thread techniques we met earlier in this chapter could be used
directly in such a role. This way, the GUI is not blocked while the thread waits for data
to show up, and the thread does not attempt to update the GUI itself. Moreover, more
than one data stream or long-running activity can overlap in time.
Example 10-29 shows how. The main trick this script employs is to split up the input
and output parts of the original redirectedGuiShellCmd of the guiStreams module we
met earlier in Example 10-12. By so doing, the input portion can be spawned off in a
parallel thread and not block the GUI. The main GUI thread uses an after timer loop
as usual, to watch for data to be added by the reader thread to a shared queue. Because
the main thread doesn’t read program output itself, it does not get stuck in wait states.
Example 10-29. PP4E\Gui\Tools\pipe_gui3.py
"""
read command pipe in a thread and place output on a queue checked in timer loop;
allows script to display program's output without being blocked between its outputs;
spawned programs need not connect or flush, but this approaches complexity of sockets
"""
import _thread as thread, queue, os
from tkinter import Tk
from PP4E.Gui.Tools.guiStreams import GuiOutput
stdoutQueue = queue.Queue() # infinite size
def producer(input):
while True:
line = input.readline() # OK to block: child thread
stdoutQueue.put(line) # empty at end-of-file
if not line: break
def consumer(output, root, term='<end>'):
try:
line = stdoutQueue.get(block=False) # main thread: check queue
except queue.Empty: # 4 times/sec, OK if empty
658 | Chapter 10: GUI Coding Techniques
Do
wnload from Wow! eBook <www.wowebook.com>