Read Programming Python Online

Authors: Mark Lutz

Tags: #COMPUTERS / Programming Languages / Python

Programming Python (79 page)

BOOK: Programming Python
4.96Mb size Format: txt, pdf, ePub
ads
GuiMaker: Automating Menus and Toolbars

The last section’s
mixin class makes common tasks simpler, but it still doesn’t
address the complexity of linking up widgets such as menus and toolbars.
Of course, if we had access to a GUI layout tool that generates Python
code, this would not be an issue, at least for some of the more static
interfaces we may require. We’d design our widgets interactively, press a
button, and fill in the callback handler blanks.

Especially for a relatively simple toolkit like tkinter, though, a
programming-based approach can often work just as well. We’d like to be
able to inherit something that does all the grunt work of construction for
us, given a template for the menus and toolbars in a window. Here’s one
way it can be done—using trees of simple objects. The class in
Example 10-3
interprets data
structure representations of menus and toolbars and builds all the widgets
automatically.

Example 10-3. PP4E\Gui\Tools\guimaker.py

"""
###############################################################################
An extended Frame that makes window menus and toolbars automatically.
Use GuiMakerFrameMenu for embedded components (makes frame-based menus).
Use GuiMakerWindowMenu for top-level windows (makes Tk8.0 window menus).
See the self-test code (and PyEdit) for an example layout tree format.
###############################################################################
"""
import sys
from tkinter import * # widget classes
from tkinter.messagebox import showinfo
class GuiMaker(Frame):
menuBar = [] # class defaults
toolBar = [] # change per instance in subclasses
helpButton = True # set these in start() if need self
def __init__(self, parent=None):
Frame.__init__(self, parent)
self.pack(expand=YES, fill=BOTH) # make frame stretchable
self.start() # for subclass: set menu/toolBar
self.makeMenuBar() # done here: build menu bar
self.makeToolBar() # done here: build toolbar
self.makeWidgets() # for subclass: add middle part
def makeMenuBar(self):
"""
make menu bar at the top (Tk8.0 menus below)
expand=no, fill=x so same width on resize
"""
menubar = Frame(self, relief=RAISED, bd=2)
menubar.pack(side=TOP, fill=X)
for (name, key, items) in self.menuBar:
mbutton = Menubutton(menubar, text=name, underline=key)
mbutton.pack(side=LEFT)
pulldown = Menu(mbutton)
self.addMenuItems(pulldown, items)
mbutton.config(menu=pulldown)
if self.helpButton:
Button(menubar, text = 'Help',
cursor = 'gumby',
relief = FLAT,
command = self.help).pack(side=RIGHT)
def addMenuItems(self, menu, items):
for item in items: # scan nested items list
if item == 'separator': # string: add separator
menu.add_separator({})
elif type(item) == list: # list: disabled item list
for num in item:
menu.entryconfig(num, state=DISABLED)
elif type(item[2]) != list:
menu.add_command(label = item[0], # command:
underline = item[1], # add command
command = item[2]) # cmd=callable
else:
pullover = Menu(menu)
self.addMenuItems(pullover, item[2]) # sublist:
menu.add_cascade(label = item[0], # make submenu
underline = item[1], # add cascade
menu = pullover)
def makeToolBar(self):
"""
make button bar at bottom, if any
expand=no, fill=x so same width on resize
this could support images too: see Chapter 9,
would need prebuilt gifs or PIL for thumbnails
"""
if self.toolBar:
toolbar = Frame(self, cursor='hand2', relief=SUNKEN, bd=2)
toolbar.pack(side=BOTTOM, fill=X)
for (name, action, where) in self.toolBar:
Button(toolbar, text=name, command=action).pack(where)
def makeWidgets(self):
"""
make 'middle' part last, so menu/toolbar
is always on top/bottom and clipped last;
override this default, pack middle any side;
for grid: grid middle part in a packed frame
"""
name = Label(self,
width=40, height=10,
relief=SUNKEN, bg='white',
text = self.__class__.__name__,
cursor = 'crosshair')
name.pack(expand=YES, fill=BOTH, side=TOP)
def help(self):
"override me in subclass"
showinfo('Help', 'Sorry, no help for ' + self.__class__.__name__)
def start(self):
"override me in subclass: set menu/toolbar with self"
pass
###############################################################################
# Customize for Tk 8.0 main window menu bar, instead of a frame
###############################################################################
GuiMakerFrameMenu = GuiMaker # use this for embedded component menus
class GuiMakerWindowMenu(GuiMaker): # use this for top-level window menus
def makeMenuBar(self):
menubar = Menu(self.master)
self.master.config(menu=menubar)
for (name, key, items) in self.menuBar:
pulldown = Menu(menubar)
self.addMenuItems(pulldown, items)
menubar.add_cascade(label=name, underline=key, menu=pulldown)
if self.helpButton:
if sys.platform[:3] == 'win':
menubar.add_command(label='Help', command=self.help)
else:
pulldown = Menu(menubar) # Linux needs real pull down
pulldown.add_command(label='About', command=self.help)
menubar.add_cascade(label='Help', menu=pulldown)
###############################################################################
# Self-test when file run standalone: 'python guimaker.py'
###############################################################################
if __name__ == '__main__':
from guimixin import GuiMixin # mix in a help method
menuBar = [
('File', 0,
[('Open', 0, lambda:0), # lambda:0 is a no-op
('Quit', 0, sys.exit)]), # use sys, no self here
('Edit', 0,
[('Cut', 0, lambda:0),
('Paste', 0, lambda:0)]) ]
toolBar = [('Quit', sys.exit, {'side': LEFT})]
class TestAppFrameMenu(GuiMixin, GuiMakerFrameMenu):
def start(self):
self.menuBar = menuBar
self.toolBar = toolBar
class TestAppWindowMenu(GuiMixin, GuiMakerWindowMenu):
def start(self):
self.menuBar = menuBar
self.toolBar = toolBar
class TestAppWindowMenuBasic(GuiMakerWindowMenu):
def start(self):
self.menuBar = menuBar
self.toolBar = toolBar # guimaker help, not guimixin
root = Tk()
TestAppFrameMenu(Toplevel())
TestAppWindowMenu(Toplevel())
TestAppWindowMenuBasic(root)
root.mainloop()

To make sense of this module, you have to be familiar with the menu
fundamentals introduced in
Chapter 9
.
If you are, though, it’s straightforward—the
GuiMaker
class simply traverses the menu and
toolbar structures and builds menu and toolbar widgets along the way. This
module’s self-test code includes a simple example of the data structures
used to lay out menus and toolbars:

Menu bar templates

Lists and nested sublists of
(
label
,
underline
,
handler
)
triples. If a
handler
is a sublist rather than a
function or method, it is assumed to be a cascading submenu.

Toolbar templates

List of
(
label
,
handler
,
pack-options
)
triples.
pack-options
is coded as a dictionary of
options passed on to the widget
pack
method; we can code these as
{'k':v}
literals, or with the
dict(k=v)
call’s keyword syntax.
pack
accepts a dictionary argument, but we
could also transform the dictionary into individual keyword
arguments
by using Python’s
func(**kargs)
call syntax. As is, labels
are assumed to be text, but images could be supported too (see the
note under
BigGui: A Client Demo Program
).

For variety, the mouse cursor changes based upon its location: a
hand in the toolbar, crosshairs in the default middle part, and something
else over Help buttons of frame-based menus (customize
as desired).

Subclass Protocols

In addition to menu
and toolbar layouts, clients of this class can also tap
into and customize the method and geometry protocols the class
implements:

Template attributes

Clients of this class are expected to set
menuBar
and
toolBar
attributes somewhere in the
inheritance chain by the time the
start
method has finished.

Initialization

The
start
method can be
overridden to construct menu and toolbar templates dynamically,
since
self
is available when it
is called;
start
is also where
general initializations should be performed—
GuiMixin
’s
__init__
constructor must be run, not
overridden.

Adding widgets

The
makeWidgets
method
can be redefined to construct the middle part of the window—the
application portion between the menu bar and the toolbar. By
default,
makeWidgets
adds a
label in the middle with the name of the most specific class, but
this method is expected to be specialized.

Packing protocol

In a specialized
makeWidgets
method, clients may attach
their middle portion’s widgets to any side of
self
(a
Frame
) since the menu and toolbars have
already claimed the container’s top and bottom by the time
makeWidgets
is run. The middle
part does not need to be a nested frame if its parts are packed.
The menu and toolbars are also automatically packed first so that
they are clipped last if the window shrinks.

Gridding protocol

The middle part can contain a grid layout, as long as it is
gridded in a nested
Frame
that
is itself packed within the
self
parent. (Remember that each
container level may use
grid
or
pack
, not both, and that
self
is a
Frame
with already packed bars by the
time
makeWidgets
is called.)
Because the
GuiMaker Frame
packs itself within its parent, it is not directly embeddable in a
container with widgets arranged in a grid, for similar reasons;
add an intermediate gridded
Frame
to use it in this context.

GuiMaker Classes

In return for conforming to
GuiMaker
protocols and
templates, client subclasses get a
Frame
that knows how to automatically build up
its own menus and toolbars from template data structures. If you read
the preceding chapter’s menu examples, you probably know that this is a
big win in terms of reduced coding requirements.
GuiMaker
is also clever enough to export
interfaces for both menu styles that we met in
Chapter 9
:

GuiMakerWindowMenu

Implements Tk 8.0-style top-level window menus, useful for
menus associated with standalone programs and pop ups.

GuiMakerFrameMenu

Implements alternative
Frame/Menubutton
-based menus, useful for
menus on objects embedded as components of a larger GUI.

Both classes build toolbars, export the same protocols, and expect
to find the same template structures; they differ only in the way they
process menu templates. In fact, one is simply a subclass of the other
with a specialized menu maker method—only top-level menu processing
differs between the two styles (a
Menu
with
Menu
cascades rather than a
Frame
with
Menubuttons
).

GuiMaker Self-Test

Like
GuiMixin
, when
we run
Example 10-3
as a top-level
program, we trigger the self-test logic at the bottom of its file;
Figure 10-2
shows the windows we get. Three
windows come up, representing each of the self-test code’s
TestApp
classes. All three have a menu and
toolbar with the options specified in the template data structures
created in the self-test code: File and Edit menu pull downs, plus a
Quit toolbar button and a standard Help menu button. In the screenshot,
one window’s File menu has been torn off and the Edit menu of another is
being pulled down; the lower window was resized for effect.

Figure 10-2. GuiMaker self-test at work

GuiMaker
can be mixed in with
other superclasses, but it’s primarily intended to serve the same
extending and embedding roles as a tkinter
Frame
widget class (which makes sense, given
that it’s really just a customized
Frame
with extra construction protocols). In
fact, its self-test combines a
GuiMaker
frame with the prior section’s
GuiMixin
tools package class.

Because of the superclass relationships coded, two of the three
windows get their
help
callback
handler from
GuiMixin
;
TestAppWindowMenuBasic
gets
GuiMaker
’s instead. Notice that the order in
which these two classes are mixed can be important: because both
GuiMixin
and
Frame
define a
quit
method, we need to list the class from
which we want to get it first in the mixed class’s header line due to
the left-to-right search rule of multiple inheritance. To select
GuiMixin
’s methods, it should usually
be listed before a superclass derived from real widgets.

We’ll put
GuiMaker
to more
practical use in instances such as the PyEdit example in
Chapter 11
. The next section shows another way
to use
GuiMaker
’s templates to build
up a sophisticated interface, and serves as another test of its
functionality.

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

Other books

B00AFPTSI0 EBOK by Grant Ph.D., Adam M.
Swordfights & Lullabies by Debora Geary
Sir Finn of Glenrydlen by Rowan Blair Colver
Portrait of a Man by Georges Perec, David Bellos
An Unexpected Baby by Shadonna Richards
The Silent Wife by A S A Harrison
Dead Romantic by Simon Brett
A Trouble of Fools by Linda Barnes
Museums and Women by John Updike


readsbookonline.com Copyright 2016 - 2024