Read Programming Python Online

Authors: Mark Lutz

Tags: #COMPUTERS / Programming Languages / Python

Programming Python (78 page)

BOOK: Programming Python
5.75Mb size Format: txt, pdf, ePub
ads

[
37
]
Perhaps most prominently today, the popular
Eve
Online
game uses Python for scripting and much of the
functionality—both on the server and the client. It uses the
Stackless Python implementation to boost massively parallel
multitasking responsiveness. Other notable game companies using
Python include the makers of Civilization IV, and the now-defunct
Origin Systems (at last report, its game Ultima Online II was to
use Python for scripting its animation).

The End of the Tour

And that’s a wrap for our tour of the tkinter library. You've now
seen all the core widgets and tools previewed in
Chapter 7
(flip back for a summary of
territory covered on this tour). For more details, watch for all of the
tools introduced here to appear again in the advanced GUI techniques in
Chapter 10
, the larger GUI examples in
Chapter 11
, and the remainder of the book at
large. To some extent, the last few chapters have laid the groundwork
needed to step up to the larger programs that
follow
.

Other Widgets and Options

I should point
out, though, that this story is still not quite complete.
Although we’ve covered the entire basic tkinter widget arsenal and
mastered GUI fundamentals along the way, we’ve skipped a handful of
newer and more advanced widgets introduced to tkinter recently:

Spinbox

An
Entry
used to
select among a set or range of values

LabelFrame

A
Frame
with a
border and title around a group of items

PanedWindow

A geometry manager
widget containing multiple widgets that can be
resized by moving separator lines with the mouse

Moreover, we haven’t even mentioned any of the higher-level
widgets available in the popular Pmw, Tix, or ttk extension packages for
tkinter
(described in
Chapter 7
) or any other third-party
packages in general. For instance:

  • Tix and ttk both provide additional widget options outlined in
    Chapter 7
    , which are now part of
    Python’s standard library.

  • The third-party domain tends to change over time, but has
    hosted tree widgets, HTML viewers, font selection dialogs, tables,
    and much more for tkinter, and includes the Pmw megawidget
    set.

  • Many tkinter programs such as Python’s standard IDLE
    development GUI include font dialogs, tree widgets, and more that
    you may be able to use in your own applications.

Because such extensions are too complex for us to cover in a
useful fashion here, though, we’ll defer to other resources in the
interest of space. To sample richer options for your GUI scripts, be
sure to consult tkinter, Tk, Tix, ttk, and Pmw documentation for more
details on additional widgets, and visit the PyPI website at
http://python.org/
or search the Web for other
third-party tkinter extensions.

I should also mention that there are more widget configuration
options than we have met on this tour. Consult Tk and tkinter resources
for options not listed explicitly here. Although other tkinter tools are
analogous to those presented here, the space I have for illustrating
additional widgets and options in this book is limited by both my
publisher and the finite nature of trees.

Chapter 10. GUI Coding Techniques
“Building a Better Mousetrap”

This chapter continues our look at building GUIs with Python and the
tkinter library by presenting a collection of more advanced GUI
programming patterns and techniques. In the preceding three chapters, we
explored all the fundamentals of tkinter itself. Here, our goal is to put
them to work to add higher-level structures that will be useful in larger
programs. That is, our focus shifts here to writing code of our own which
implements utility above and beyond the basic tkinter toolkit—utility that
we’ll actually find useful in more complete examples later in the
book.

Some of the techniques we will be studying in this chapter are as
follows:

  • Providing common GUI operations in “mixin” classes

  • Building menus and toolbars from data structure templates

  • Adding GUI interfaces to command-line tools

  • Redirecting input and output streams to GUI widgets

  • Reloading GUI callback handlers on the fly

  • Wrapping up and automating top-level window interfaces

  • Using threads and queues to avoiding blocking in GUIs

  • Popping up GUI windows on demand from non-GUI programs

  • Adding GUIs as separate programs with sockets and pipes

As with other chapters in this book, this chapter has a dual
agenda—not only will we be studying GUI programming, but we’ll also be
learning more about general Python development concepts such as
object-oriented programming (OOP) and code reuse. As we’ll see, by coding
GUI tools in Python, it’s easy to apply them in a wide variety of contexts
and programs.

As a segue to the next chapter, this one also closes with a look at
the PyDemos and PyGadgets launcher toolbars—GUIs used to start larger GUI
examples. Although most of their code is external to this book, we’ll
explore enough of their structure to help you study them in the examples
distribution package.

Two notes before we begin: first, be sure to read the code listings
in this chapter for details we won’t present in the narrative. Second,
although small examples that apply in this chapter’s techniques will show
up along the way, more realistic application will have to await more
realistic programs. We’ll put these techniques to use in the larger
examples in the next chapter and throughout the rest of the book. In fact,
we’ll be reusing the modules we develop here often, as tools in other
programs in this book; reusable software wants to be reused. First,
though, let’s do what our species does best and build some tools.

GuiMixin: Common Tool Mixin Classes

If you read
the last three chapters, you probably noticed that the code
used to construct nontrivial GUIs can become long if we make each widget
by hand. Not only do we have to link up all the widgets manually, but we
also need to remember and then set dozens of options. If we stick to this
strategy, GUI programming often becomes an exercise in typing, or at least
in cut-and-paste text editor operations.

Widget Builder Functions

Instead of
performing each step by hand, a better idea is to wrap or
automate as much of the GUI construction process as possible. One
approach is to code functions that provide typical widget
configurations, and automate the construction process for cases to which
they apply. For instance, we could define a button function to handle
configuration and packing details and support most of the buttons we
draw.
Example 10-1
provides
a handful of such widget builder calls.

Example 10-1. PP4E\Gui\Tools\widgets.py

"""
###############################################################################
wrap up widget construction in functions for easier use, based upon some
assumptions (e.g., expansion); use **extras fkw args for width, font/color,
etc., and repack result manually later to override defaults if needed;
###############################################################################
"""
from tkinter import *
def frame(root, side=TOP, **extras):
widget = Frame(root)
widget.pack(side=side, expand=YES, fill=BOTH)
if extras: widget.config(**extras)
return widget
def label(root, side, text, **extras):
widget = Label(root, text=text, relief=RIDGE) # default config
widget.pack(side=side, expand=YES, fill=BOTH) # pack automatically
if extras: widget.config(**extras) # apply any extras
return widget
def button(root, side, text, command, **extras):
widget = Button(root, text=text, command=command)
widget.pack(side=side, expand=YES, fill=BOTH)
if extras: widget.config(**extras)
return widget
def entry(root, side, linkvar, **extras):
widget = Entry(root, relief=SUNKEN, textvariable=linkvar)
widget.pack(side=side, expand=YES, fill=BOTH)
if extras: widget.config(**extras)
return widget
if __name__ == '__main__':
app = Tk()
frm = frame(app, TOP) # much less code required here!
label(frm, LEFT, 'SPAM')
button(frm, BOTTOM, 'Press', lambda: print('Pushed'))
mainloop()

This module makes some assumptions about its clients’ use cases,
which allows it to automate typical construction chores such as packing.
The net effect is to reduce the amount of code required of its
importers. When run as a script,
Example 10-1
creates a simple
window with a ridged label on the left and a button on the right that
prints a message when pressed, both of which expand along with the
window. Run this on your own for a look; its window isn’t really
anything new for us, and its code is meant more as library than
script—as we’ll see when we make use of it later in
Chapter 19
’s PyCalc.

This function-based approach can cut down on the amount of code
required. As functions, though, its tools don’t lend themselves to
customization in the broader OOP sense. Moreover, because they are not
methods, they do not have access to the state of an object representing
the GUI.

Mixin Utility Classes

Alternatively, we
can implement common methods in a class and inherit them
everywhere they are needed. Such classes are commonly called
mixin
classes because their methods are “mixed in”
with other classes. Mixins serve to package generally useful tools as
methods. The concept is almost like importing a module, but mixin
classes can access the subject instance,
self
, to utilize both per-instance state and
inherited methods. The script in
Example 10-2
shows how.

Example 10-2. PP4E\Gui\Tools\guimixin.py

"""
###############################################################################
a "mixin" class for other frames: common methods for canned dialogs,
spawning programs, simple text viewers, etc; this class must be mixed
with a Frame (or a subclass derived from Frame) for its quit method
###############################################################################
"""
from tkinter import *
from tkinter.messagebox import *
from tkinter.filedialog import *
from PP4E.Gui.Tour.scrolledtext import ScrolledText # or tkinter.scrolledtext
from PP4E.launchmodes import PortableLauncher, System # or use multiprocessing
class GuiMixin:
def infobox(self, title, text, *args): # use standard dialogs
return showinfo(title, text) # *args for bkwd compat
def errorbox(self, text):
showerror('Error!', text)
def question(self, title, text, *args):
return askyesno(title, text) # return True or False
def notdone(self):
showerror('Not implemented', 'Option not available')
def quit(self):
ans = self.question('Verify quit', 'Are you sure you want to quit?')
if ans:
Frame.quit(self) # quit not recursive!
def help(self):
self.infobox('RTFM', 'See figure 1...') # override this better
def selectOpenFile(self, file="", dir="."): # use standard dialogs
return askopenfilename(initialdir=dir, initialfile=file)
def selectSaveFile(self, file="", dir="."):
return asksaveasfilename(initialfile=file, initialdir=dir)
def clone(self, args=()): # optional constructor args
new = Toplevel() # make new in-process version of me
myclass = self.__class__ # instance's (lowest) class object
myclass(new, *args) # attach/run instance to new window
def spawn(self, pycmdline, wait=False):
if not wait: # start new process
PortableLauncher(pycmdline, pycmdline)() # run Python progam
else:
System(pycmdline, pycmdline)() # wait for it to exit
def browser(self, filename):
new = Toplevel() # make new window
view = ScrolledText(new, file=filename) # Text with Scrollbar
view.text.config(height=30, width=85) # config Text in Frame
view.text.config(font=('courier', 10, 'normal')) # use fixed-width font
new.title("Text Viewer") # set window mgr attrs
new.iconname("browser") # file text added auto
"""
def browser(self, filename): # if tkinter.scrolledtext
new = Toplevel() # included for reference
text = ScrolledText(new, height=30, width=85)
text.config(font=('courier', 10, 'normal'))
text.pack(expand=YES, fill=BOTH)
new.title("Text Viewer")
new.iconname("browser")
text.insert('0.0', open(filename, 'r').read() )
"""
if __name__ == '__main__':
class TestMixin(GuiMixin, Frame): # standalone test
def __init__(self, parent=None):
Frame.__init__(self, parent)
self.pack()
Button(self, text='quit', command=self.quit).pack(fill=X)
Button(self, text='help', command=self.help).pack(fill=X)
Button(self, text='clone', command=self.clone).pack(fill=X)
Button(self, text='spawn', command=self.other).pack(fill=X)
def other(self):
self.spawn('guimixin.py') # spawn self as separate process
TestMixin().mainloop()

Although
Example 10-2
is geared toward GUIs, it’s really about design concepts. The
GuiMixin
class implements common operations
with standard interfaces that are immune to changes in implementation.
In fact, the implementations of some of this class’s methods did
change—between the first and second editions of this book, old-style
Dialog
calls were replaced with the
new Tk standard dialog calls; in the fourth edition, the file browser
was updated to use a different scrolled text class. Because this class’s
interface hides such details, its clients did not have to be changed to
use the new
techniques
.

As is,
GuiMixin
provides
methods for common dialogs, window cloning, program spawning, text file
browsing, and so on. We can add more methods to such a mixin later if we
find ourselves coding the same methods repeatedly; they will all become
available immediately everywhere this class is imported and mixed.
Moreover,
GuiMixin
’s methods can be
inherited and used as is, or they can be redefined in subclasses. Such
are the natural advantages of classes over functions.

There are a few details worth highlighting here:

  • The
    quit
    method serves some
    of the same purpose as the reusable
    Quitter
    button we used in earlier
    chapters. Because mixin classes can define a large library of
    reusable methods, they can be a more powerful way to package
    reusable components than individual classes. If the mixin is
    packaged well, we can get a lot more from it than a single button’s
    callback.

  • The
    clone
    method makes a
    new in-process copy, in a new top-level window, of the most specific
    class that mixes in a
    GuiMixin
    (
    self
    .
    __class__
    is the class object that the
    instance was created from). Assuming that the class requires no
    constructor arguments other than a parent container, this opens a
    new independent copy of the window (pass in any extra constructor
    arguments required).

  • The
    browser
    method opens
    the
    ScrolledText
    object we wrote
    in
    Chapter 9
    in a new window
    and fills it with the text of a file to be viewed. As noted in the
    preceding chapter, there is also a
    ScrolledText
    widget in standard library
    module
    tkinter.scrolledtext
    , but
    its interface differs, it does not load a file automatically, and it
    is prone to becoming deprecated (though it hasn’t over many years).
    For reference, its alternative code is included.

  • The
    spawn
    method launches a
    Python program command line as a new independent process and waits
    for it to end or not (depending on the default False
    wait
    argument—
    GUIs usually shouldn’t wait).
    This method is simple, though, because we wrapped launching details
    in the
    launchmodes
    module
    presented at the end of
    Chapter 5
    .
    GuiMixin
    both fosters and
    practices good code reuse habits.

The
GuiMixin
class is meant to
be a library of reusable tool methods and is essentially useless by
itself. In fact, it must generally be mixed with a
Frame
-based class to be used:
quit
assumes it’s mixed with a
Frame
, and
clone
assumes it’s mixed with a widget class.
To satisfy such constraints, this module’s self-test code at the bottom
combines
GuiMixin
with a
Frame
widget.

Figure 10-1
shows the
scene created by the module’s self-test after pressing “clone” and
“spawn” once each, and then “help” in one of the three copies. Because
they are separate processes, windows started with “spawn” keep running
after other windows are closed and do not impact other windows when
closed themselves; a “clone” window is in-process instead—it is closed
with others, but its “X” destroys just itself. Make sure your PYTHONPATH
includes the
PP4E
directory’s
container for the cross-directory package imports in this example and
later examples which use it.

Figure 10-1. GuiMixin self-test code in action

We’ll see
GuiMixin
show up
again as a mixin in later examples; that’s the whole point of code
reuse, after all. Although functions are often useful, classes support
inheritance and access to instance state, and provide an extra
organizational structure—features that are especially useful given the
coding requirements of GUIs. For instance, many of
GuiMixin
’s methods could be replaced with
simple functions, but
clone
and
quit
could not. The next section
carries these talents of mixin classes even
further.

BOOK: Programming Python
5.75Mb size Format: txt, pdf, ePub
ads

Other books

Ham Bones by Carolyn Haines
Panic by Sharon M. Draper
A Brief History of the Celts by Peter Berresford Ellis
Entanglement by Gregg Braden
Live for You by Valentine, Marquita
The Keys to Jericho by Ren Alexander
One Night with a Quarterback by Jeanette Murray