Read Programming Python Online

Authors: Mark Lutz

Tags: #COMPUTERS / Programming Languages / Python

Programming Python (87 page)

BOOK: Programming Python
2.88Mb size Format: txt, pdf, ePub
ads
Sockets and pipes: Compare and contrast

Let’s see how we’ve
done. This script is similar in spirit to what we did in
Example 10-28
. Because of
the way its code is structured, though,
Example 10-29
has a major
advantage: because input calls are spawned off in a
thread
this time, the GUI is completely
responsive. Window moves, resizes, and so forth, happen immediately
because the GUI is not blocked while waiting for the next output from
the non-GUI program. The combination of a pipe, thread, and queue
works wonders here—the GUI need not wait for the spawned program, and
the spawned thread need not update the GUI itself.

Although it is more complex and requires thread support,
Example 10-29
’s lack of blocking
makes this
redirectedGuiShellCmd
much more generally useful than the original pipe version we coded.
Compared to the
sockets
of the prior section,
though, this solution is a bit of a mixed bag:

  • Because this GUI reads the spawned program’s standard
    output, no changes are required in the non-GUI program. Unlike the
    socket-based example in the prior section, the non-GUI program
    here needs no knowledge of the GUI that will display its
    results—it need not connect to a socket and need not flush its
    input stream, as required for the earlier socket-based
    option.

  • Although it requires no changes to the programs whose output
    is displayed, the GUI code’s complexity begins to approach that of
    the socket-based alternative, especially if you strip away the
    boilerplate code required for all socket programs.

  • It does not directly support running the GUI and non-GUI
    programs separately, or on remote machines. As we’ll see in
    Chapter 12
    , sockets allow data to be passed
    between programs running on the same machine or across
    networks.

  • Sockets apply to more use cases than displaying a program’s
    output stream. If the GUI must do more than display another
    program’s output, sockets become a more general solution—as we’ll
    also learn later, because sockets are bidirectional data streams,
    they allow data to be passed back and forth between two programs
    in more arbitrary ways.

Other uses for threaded pipe GUIs

Despite its tradeoffs, the thread/queue/pipe-based approach for
GUIs has fairly wide applicability. To illustrate, here’s another
quick usage example. The following runs a simple script normally from
a shell/terminal window; it prints one successively longer output line
every two seconds:

C:\...\PP4E\Gui\Tools>
type spams.py
import time
for i in range(1, 10, 2):
time.sleep(2) # print to standard output
print('spam' * i) # nothing GUI about this, eh?
C:\...\PP4E\Gui\Tools>
python spams.py
spam
spamspamspam
spamspamspamspamspam
spamspamspamspamspamspamspam
spamspamspamspamspamspamspamspamspam

Let’s wrap this up in a GUI, with code typed at the interactive
prompt for variety. The following imports the new GUI redirection
function as a library component and uses it to create a window that
displays the script’s five lines, appearing every two seconds just as
in the terminal window, followed by a final line containing

reflecting the spawned program’s
exit. The resulting output window is captured in
Figure 10-16
:

C:\...\PP4E\Gui\Tools>
python
>>>
from tkinter import Tk
>>>
from pipe_gui3 import redirectedGuiShellCmd
>>>
root = Tk()
>>>
redirectedGuiShellCmd('python -u spams.py', root)

Figure 10-16. Command pipe GUI displaying another program’s output

If the spawned program exits,
Example 10-29
’s producer thread
detects end-of-file on the pipe and puts a final empty line in the
queue; in response the consumer loop displays an

line in the GUI by default when
it detects this condition. In this case, program exit is normal and
silent; in other cases, we may need to add shutdown logic to suppress
error messages. Note that here again, the
sleep
call in the spawned program simulates
a long-running task, and we really need the
-u
unbuffered streams flag—without it, no
output appears in the GUI for eight seconds, until the spawned program
is completely finished. With it, the GUI receives and displays each
line as it is printed, one every two seconds.

This is also, finally, the sort of code you could use to display
the output of a non-GUI program in a GUI, without sockets, changes in
the original program, or blocking the GUI. Of course, in many cases,
if you have to work this hard to add a GUI anyhow, you might as well
just make your script a traditional GUI program with a main window and
event loop. Furthermore, the GUIs we’ve coded in this section are
limited to displaying another program’s output; sometimes the GUI may
have to do more. For many programs, though, the general separation of
display and program logic provided by the spawned GUI model can be an
advantage—it’s easier to understand both parts if they are not mixed
together.

We’ll learn more about sockets in the next part of the book, so
you should consider parts of this discussion something of a preview.
As we’ll see, things start to become more and more interesting when we
start combining GUIs, threads, and network
sockets
.

Before we do, though, the next chapter rounds out the purely GUI
part of this book by applying the widgets and techniques we’ve learned
in more realistically scaled programs. And before that, the next
section wraps up here with a preview of some of the larger GUI
examples coming up, with a quick look at scripts that launch them
automatically, and allow you to sample some of what is possible with
Python and
tkinter.

The PyDemos and PyGadgets Launchers

To close out this chapter, let’s explore the implementations of the
two GUIs used to run major book examples. The following GUIs, PyDemos and
PyGadgets, are simply GUIs for launching other GUI programs. In fact,
we’ve now come to the end of the demo launcher story—both of the new
programs here interact with modules that we met earlier in
Part II
:

launchmodes.py

Starts independent Python programs portably.

Launcher.py

Finds programs, and ultimately runs both PyDemos and PyGadgets
when used by the self-configuring top-level launcher scripts.

LaunchBrowser.pyw

Spawns web browsers portably to open local or remote
pages.

See
Part II
(especially the ends of
Chapter 5
and
Chapter 6
) for links to the code for these
modules. The programs introduced here add the GUI components to the
program
-
launching
system—they simply provide
easy-to-use pushbuttons that spawn most of the larger examples in this
text when pressed.

Both of these scripts also assume that they will be run with the
current working directory set to their directory (they hardcode paths to
other programs relative to that). Either click on their names in a file
explorer or run them from a command-line shell after a
cd
to the top-level
PP4E
examples root directory. These scripts could allow invocations from other
directories by prepending an environment variable’s value to program
script paths, but they were really designed to be run only out of the
PP4E
root.

Because these demo launchers are long programs, in the interest of
space and time only their crucial and representative parts are listed in
this book; as usual, see the examples package distribution for the
portions omitted here.

PyDemos Launcher Bar (Mostly External)

The PyDemos
script constructs a bar of buttons that run programs in
demonstration mode, not for day-to-day use. I use PyDemos to show off
Python programs—it’s much easier to press its buttons than to run
command lines or fish through a file explorer GUI to find
scripts.

You can use PyDemos (and PyGadgets) to start and interact with
examples presented in this book—all of the buttons on this GUI represent
examples we will meet in later chapters. Unlike when using the
Launch_PyDemos
and
Launch_PyGadgets_bar
scripts at the top of
the examples package, though, make sure your
PYTHONPATH
system variable is set to include
the directory containing the
PP4E
examples root directory if you wish to run the scripts here directly;
they don’t attempt to automatically configure your system or module
import search paths.

To make this launcher bar even easier to run, drag it out to your
desktop to generate a clickable Windows shortcut (do something similar
on other systems). Since this script hardcodes command lines for running
programs elsewhere in the examples tree, it is also useful as an index
to major book examples.
Figure 10-17
shows what PyDemos
looks like when run on Windows, along with some of the demos it
launches—PyDemos is the vertical button bar on the right; it looks
slightly different but works the same on Linux.

Figure 10-17. PyDemos with its pop ups and a few demos

The source code that constructs this scene is listed in
Example 10-30
(its first page
may differ slightly from that shown being edited in
Figure 10-17
due to last minute
tweaks which engineers can’t seem to avoid). Because PyDemos doesn’t
present much that’s new in terms of GUI interface programming, though,
much of it has been removed here; again, see the examples package for
the remainder.

In short, its
demoButton
function simply attaches a new button to the main window, spring-loaded
to spawn a Python program when pressed. To start programs, PyDemos calls
an instance of the
launchmodes.PortableLauncher
object we met at
the end of
Chapter 5
—its role as a
tkinter callback handler here is why a function-call operation is used
to kick off the launched program.

As pictured in
Figure 10-17
, PyDemos also
constructs two pop-up windows when buttons at the bottom of the main
window are pressed—an Info pop up giving a short description of the last
demo spawned, and a Links pop up containing radio buttons that open a
local web browser on book-related sites when pressed:

  • The
    Info
    pop up displays a simple message
    line and changes its font every second to draw attention to itself;
    since this can be a bit distracting, the pop up starts out iconified
    (click the Info button to see or hide it).

  • The
    Links
    pop up’s radio buttons are much
    like hyperlinks in a web page, but this GUI isn’t a browser: when
    the Links pop up is pressed, the portable
    LaunchBrowser
    script mentioned in
    Part II
    is used to find and start a web
    browser used to connect to the relevant site, assuming you have an
    Internet connection. This in turn uses Python’s
    webbrowser
    modules today.

  • The
    windows
    module we coded
    earlier in this chapter (
    Example 10-16
    ) is used to give
    this GUI’s windows a blue “PY” icon, instead of the standard red
    “Tk.”

The PyDemos GUI also comes with “code” buttons to the right of
each demo’s button, which open the source files that implement the
associated example. These files open in pop-up versions of the PyEdit
text editor that we’ll meet in
Chapter 11
.
Figure 10-18
captures
some of these code viewer windows in action, resized slightly for
display here.

For the web-based examples opened by the last two demo buttons in
the launcher, this GUI also attempts to spawn a locally running web
server for web-based demos not shown running here (we’ll meet the server
in
Chapter 15
). For this edition, the web
servers are spawned only when the corresponding web demo button is first
selected (not on PyDemos startup), and the web servers generate a pop-up
command prompt window on Windows to monitor server status.

Figure 10-18. PyDemos with its “code” source code viewer pop-ups

PyDemos runs on Windows, Macs, and Linux, but that’s largely due
to the inherent portability of both Python and tkinter. For more
details, consult the source, which is shown in
part in
Example 10-30
.

Example 10-30. PP4E\PyDemos.pyw (external)

"""
################################################################################
PyDemos.pyw
Programming Python, 2nd, 3rd, and 4th Editions (PP4E), 2001--2006--2010
Version 2.1 (4E), April '10: updated to run under Python 3.X, and spawn
local web servers for web demos only on first demo button selection.
Version 2.0 (3E), March '06: add source-code file viewer buttons; add new
Demos (PyPhoto, PyMailGUI); spawn locally running web servers for the
browser-based Demos; add window icons; and probably more I've forgotten.
Launch major Python+Tk GUI examples from the book, in a platform-neutral way.
This file also serves as an index to major program examples, though many book
examples aren't GUI-based, and so aren't listed here. Also see:
- PyGadgets.py, a simpler script for starting programs in non-demo mode
that you wish to use on a regular basis
- PyGadgets_bar.pyw, which creates a button bar for starting all PyGadgets
programs on demand, not all at once
- Launcher.py for starting programs without environment settings--finds
Python, sets PYTHONPATH, etc.
- Launch_*.pyw for starting PyDemos and PyGadgets with Launcher.py--run these
for a quick look
- LaunchBrowser.pyw for running example web pages with an automatically
located web browser
- README-PP4E.txt, for general examples information
Caveat: this program tries to start a locally running web server and web
Browser automatically, for web-based demos, but does not kill the server.
################################################################################
"""
...code omitted: see examples package...
################################################################################
# start building main GUI windows
################################################################################
from PP4E.Gui.Tools.windows import MainWindow # a Tk with icon, title, quit
from PP4E.Gui.Tools.windows import PopupWindow # same but Toplevel, diff quit
Root = MainWindow('PP4E Demos 2.1')
# build message window
Stat = PopupWindow('PP4E demo info')
Stat.protocol('WM_DELETE_WINDOW', lambda:0) # ignore wm delete
Info = Label(Stat, text = 'Select demo',
font=('courier', 20, 'italic'), padx=12, pady=12, bg='lightblue')
Info.pack(expand=YES, fill=BOTH)
################################################################################
# add launcher buttons with callback objects
################################################################################
from PP4E.Gui.TextEditor.textEditor import TextEditorMainPopup
# demo launcher class
class Launcher(launchmodes.PortableLauncher): # use wrapped launcher class
def announce(self, text): # customize to set GUI label
Info.config(text=text)
def viewer(sources):
for filename in sources:
TextEditorMainPopup(Root, filename, # as pop up in this process
loadEncode='utf-8') # else PyEdit may ask each!
def demoButton(name, what, doit, code):
"""
add buttons that runs doit command-line, and open all files in code;
doit button retains state in an object, code in an enclosing scope;
"""
rowfrm = Frame(Root)
rowfrm.pack(side=TOP, expand=YES, fill=BOTH)
b = Button(rowfrm, bg='navy', fg='white', relief=RIDGE, border=4)
b.config(text=name, width=20, command=Launcher(what, doit))
b.pack(side=LEFT, expand=YES, fill=BOTH)
b = Button(rowfrm, bg='beige', fg='navy')
b.config(text='code', command=(lambda: viewer(code)))
b.pack(side=LEFT, fill=BOTH)
################################################################################
# tkinter GUI demos - some use network connections
################################################################################
demoButton(name='PyEdit',
what='Text file editor', # edit myself
doit='Gui/TextEditor/textEditor.py PyDemos.pyw', # assume in cwd
code=['launchmodes.py',
'Tools/find.py',
'Gui/Tour/scrolledlist.py', # show in PyEdit viewer
'Gui/ShellGui/formrows.py', # last = top of stacking
'Gui/Tools/guimaker.py',
'Gui/TextEditor/textConfig.py',
'Gui/TextEditor/textEditor.py'])
demoButton(name='PyView',
what='Image slideshow, plus note editor',
doit='Gui/SlideShow/slideShowPlus.py Gui/gifs',
code=['Gui/Texteditor/textEditor.py',
'Gui/SlideShow/slideShow.py',
'Gui/SlideShow/slideShowPlus.py'])
...code omitted: see examples package...
################################################################################
# toggle info message box font once a second
################################################################################
def refreshMe(info, ncall):
slant = ['normal', 'italic', 'bold', 'bold italic'][ncall % 4]
info.config(font=('courier', 20, slant))
Root.after(1000, (lambda: refreshMe(info, ncall+1)) )
################################################################################
# unhide/hide status box on info clicks
################################################################################
Stat.iconify()
def onInfo():
if Stat.state() == 'iconic':
Stat.deiconify()
else:
Stat.iconify() # was 'normal'
################################################################################
# finish building main GUI, start event loop
################################################################################
def onLinks():
...code omitted: see examples package...
Button(Root, text='Info', command=onInfo).pack(side=TOP, fill=X)
Button(Root, text='Links', command=onLinks).pack(side=TOP, fill=X)
Button(Root, text='Quit', command=Root.quit).pack(side=BOTTOM, fill=X)
refreshMe(Info, 0) # start toggling
Root.mainloop()
BOOK: Programming Python
2.88Mb size Format: txt, pdf, ePub
ads

Other books

China Dog by Judy Fong Bates
The Guide to Getting It On by Paul Joannides
The Accidental Boyfriend by Maggie Dallen
Zahrah the Windseeker by Nnedi Okorafor-Mbachu
A Billionaire BWWM Romance 2: Jealousy and Trust by J A Fielding, Bwwm Romance Dot Com
The Bells of El Diablo by Frank Leslie