Section 12.6. PyClock: An AnalogDigital Clock Widget


12.6. PyClock: An Analog/Digital Clock Widget

One of the first things I always look for when exploring a new computer interface is a clock. Because I spend so much time glued to computers, it's essentially impossible for me to keep track of the time unless it is right there on the screen in front of me (and even then, it's iffy). The next program, PyClock, implements such a clock widget in Python. It's not substantially different from the clock programs that you may be used to seeing on the X Window System. Because it is coded in Python, though, this one is both easily customized and fully portable among Windows, the X Window System, and Macs, like all the code in this chapter. In addition to advanced GUI techniques, this example demonstrates Python math and time module tools.

12.6.1. A Quick Geometry Lesson

Before I show you PyClock, though, let me provide a little background and a confession. Quickhow do you plot points on a circle? This, along with time formats and events, turns out to be a core concept in clock widget programs. To draw an analog clock face on a canvas widget, you essentially need to be able to sketch a circlethe clock face itself is composed of points on a circle, and the second, minute, and hour hands of the clock are really just lines from a circle's center out to a point on the circle. Digital clocks are simpler to draw, but not much to look at.

Now the confession: when I started writing PyClock, I couldn't answer the last paragraph's opening question. I had utterly forgotten the math needed to sketch out points on a circle (as had most of the professional software developers I queried about this magic formula). It happens. After going unused for a few decades, such knowledge tends to be garbage collected. I finally was able to dust off a few neurons long enough to code the plotting math needed, but it wasn't my finest intellectual hour.[*]

[*] Lest that make software engineers seem too doltish, I should also note that I have been called on repeatedly to teach Python programming to physicists, all of whom had mathematical training well in advance of my own, and many of whom were still happily abusing FORTRAN common blocks and go-tos. Specialization in modern society can make fools of us all.

If you are in the same boat, I don't have space to teach geometry in depth here, but I can show you one way to code the point-plotting formulas in Python in simple terms. Before tackling the more complex task of implementing a clock, I wrote the plotterGui script shown in Example 12-11 to focus on just the circle-plotting logic.

Its point function is where the circle logic livesit plots the (X,Y) coordinates of a point on the circle, given the relative point number, the total number of points to be placed on the circle, and the circle's radius (the distance from the circle's center to the points drawn upon it). It first calculates the point's angle from the top by dividing 360 by the number of points to be plotted, and then multiplying by the point number; in case you've forgotten too, it's 360 degrees around the whole circle (e.g., if you plot 4 points on a circle, each is 90 degrees from the last, or 360/4). Python's standard math module gives all the required constants and functions from that point forwardpi, sine, and cosine. The math is really not too obscure if you study this long enough (in conjunction with your old geometry text if necessary). See the book's examples distribution for alternative ways to code the number crunching.

Even if you don't care about the math, though, check out this script's circle function. Given the (X,Y) coordinates of a point on the circle returned by point, it draws a line from the circle's center out to the point and a small rectangle around the point itselfnot unlike the hands and points of an analog clock. Canvas tags are used to associate drawn objects for deletion before each plot.

Example 12-11. PP3E\Gui\Clock\plotterGui.py

 # plot circles (like I did in high school) import math, sys from Tkinter import * def point(tick, range, radius):     angle = tick * (360.0 / range)     radiansPerDegree = math.pi / 180     pointX = int( round( radius * math.sin(angle * radiansPerDegree) ))     pointY = int( round( radius * math.cos(angle * radiansPerDegree) ))     return (pointX, pointY) def circle(points, radius, centerX, centerY, slow=0):     canvas.delete('lines')     canvas.delete('points')     for i in range(points):         x, y = point(i+1, points, radius-4)         scaledX, scaledY = (x + centerX), (centerY - y)         canvas.create_line(centerX, centerY, scaledX, scaledY, tag='lines')         canvas.create_rectangle(scaledX-2, scaledY-2,                                 scaledX+2, scaledY+2,                                            fill='red', tag='points')         if slow: canvas.update( ) def plotter( ):     circle(scaleVar.get(), (Width / 2), originX, originY, checkVar.get( )) def makewidgets( ):     global canvas, scaleVar, checkVar     canvas = Canvas(width=Width, height=Width)     canvas.pack(side=TOP)     scaleVar = IntVar( )     checkVar = IntVar( )     scale = Scale(label='Points on circle', variable=scaleVar, from_=1, to=360)     scale.pack(side=LEFT)     Checkbutton(text='Slow mode', variable=checkVar).pack(side=LEFT)     Button(text='Plot', command=plotter).pack(side=LEFT, padx=50) if _ _name_ _ == '_ _main_ _':     Width = 500                                       # default width, height     if len(sys.argv) == 2: Width = int(sys.argv[1])   # width cmdline arg?     originX = originY = Width / 2                     # same as circle radius     makewidgets( )                                         # on default Tk root     mainloop( ) 

The circle defaults to 500 pixels wide unless you pass a width on the command line. Given a number of points on a circle, this script marks out the circle in clockwise order every time you press Plot, by drawing lines out from the center to small rectangles at points on the circle's shape. Move the slider to plot a different number of points and click the checkbox to make the drawing happen slow enough to notice the clockwise order in which lines and points are drawn (this forces the script to update the display after each line is drawn). Figure 12-15 shows the result for plotting 120 points with the circle width set to 400 on the command line; if you ask for 60 and 12 points on the circle, the relationship to clock faces and hands starts becoming clearer.

Figure 12-15. plotterGui in action


For more help, this book's examples distribution also includes text-based versions of this plotting script that print circle point coordinates to the stdout stream for review, instead of rendering them in a GUI. See the plotterText scripts in the clock's directory. Here is the sort of output they produce when plotting 4 and 12 points on a circle that is 400 points wide and high; the output format is simply:

 pointnumber : angle = (Xcoordinate, Ycoordinate) 

and assumes that the circle is centered at coordinate (0,0):

 ---------- 1 : 90.0 = (200, 0) 2 : 180.0 = (0, -200) 3 : 270.0 = (-200, 0) 4 : 360.0 = (0, 200) ---------- 1 : 30.0 = (100, 173) 2 : 60.0 = (173, 100) 3 : 90.0 = (200, 0) 4 : 120.0 = (173, -100) 5 : 150.0 = (100, -173) 6 : 180.0 = (0, -200) 7 : 210.0 = (-100, -173) 8 : 240.0 = (-173, -100) 9 : 270.0 = (-200, 0) 10 : 300.0 = (-173, 100) 11 : 330.0 = (-100, 173) 12 : 360.0 = (0, 200) ---------- 

To understand how these points are mapped to a canvas, you first need to know that the width and height of a circle are always the samethe radius x 2. Because Tkinter canvas (X,Y) coordinates start at (0,0) in the upper-left corner, the plotter GUI must offset the circle's center point to coordinates (width/2, width/2)the origin point from which lines are drawn. For instance, in a 400 x 400 circle, the canvas center is (200,200). A line to the 90-degree angle point on the right side of the circle runs from (200,200) to (400,200)the result of adding the (200,0) point coordinates plotted for the radius and angle. A line to the bottom at 180 degrees runs from (200,200) to (200,400) after factoring in the (0,-200) point plotted.

This point-plotting algorithm used by plotterGui, along with a few scaling constants, is at the heart of the PyClock analog display. If this still seems a bit much, I suggest you focus on the PyClock script's digital display implementation first; the analog geometry plots are really just extensions of underlying timing mechanisms used for both display modes. In fact, the clock itself is structured as a generic Frame object that embeds digital and analog display objects and dispatches time change and resize events to both in the same way. The analog display is an attached Canvas that knows how to draw circles, but the digital object is simply an attached Frame with labels to show time components.

Numeric Python Tools

If you do enough number crunching to have followed this section's abbreviated geometry lesson, you will probably also be interested in exploring the NumPy numeric programming extension for Python. It adds things such as vector objects and advanced mathematical operations, and effectively turns Python into a scientific/numeric programming tool that supports efficient numerical array computations, and it has been compared to MatLab. NumPy has been used effectively by many organizations, including Lawrence Livermore and Los Alamos National Labsin many cases, allowing Python with NumPy to replace legacy FORTRAN code.

NumPy must be fetched and installed separately; see Python's web site for links. On the Vaults of Parnassus and similar sites, you'll also find related numeric tools (e.g., SciPy), as well as visualization and 3-D animation tools (e.g., PyOpenGL, Blender, vtk, and VPython). Besides the math module, Python itself also has a built-in complex number type for engineering work, and a fixed-precision decimal type in release 2.4; see the library manual for details.


12.6.2. Running PyClock

Apart from the circle geometry bit, the rest of PyClock is straightforward. It simply draws a clock face to represent the current time and uses widget after methods to wake itself up 10 times per second to check whether the system time has rolled over to the next second. On second rollovers, the analog second, minute, and hour hands are redrawn to reflect the new time (or the text of the digital display's labels is changed). In terms of GUI construction, the analog display is etched out on a canvas, redrawn whenever the window is resized, and changes to a digital format upon request.

PyClock also puts Python's standard time module into service to fetch and convert system time information as needed for a clock. In brief, the onTimer method gets system time with time.time, a built-in tool that returns a floating-point number giving seconds since the epochthe point from which your computer counts time. The time.localtime call is then used to convert epoch time into a tuple that contains hour, minute, and second values; see the script and Python library manual for additional time-related call details.

Checking the system time 10 times per second may seem intense, but it guarantees that the second hand ticks when it should, without jerks or skips (after events aren't precisely timed). It is not a significant CPU drain on systems I use.[*] On Linux and Windows, PyClock uses negligible processor resourcewhat it does use is spent largely on screen updates in analog display mode, not on after events. To minimize screen updates, PyClock redraws only clock hands on second rollovers; points on the clock's circle are redrawn only at startup and on window resizes. Figure 12-16 shows the default initial PyClock display format you get when the file clock.py is run directly.

[*] Speaking of performance, when I first wrote this example, I ran multiple clocks on all my test machinesfrom a 650 MHz Pentium III to an "old" 200 MHz Pentium Iwithout seeing any degraded performance in any running clocks. The PyDemos script, for instance, launches six clocks that run in the same process, and all update smoothly. They probably do on older machines too, but mine have collected too much dust to yield useful metrics.

Figure 12-16. PyClock default analog display


The clock hand lines are given arrows at their endpoints with the canvas line object's arrow and arrowshape options. The arrow option can be first, last, none, or both; the arrowshape option takes a tuple giving the length of the arrow touching the line, its overall length, and its width.

Like PyView, PyClock also uses the widget pack_forget and pack methods to dynamically erase and redraw portions of the display on demand (i.e., in response to bound events). Clicking on the clock with a left mouse button changes its display to digital by erasing the analog widgets and drawing the digital interface; you get the simpler display captured in Figure 12-17.

Figure 12-17. PyClock goes digital


This digital display form is useful if you want to conserve real estate on your computer screen and minimize PyClock CPU utilization (it incurs very little screen update overhead). Left-clicking on the clock again changes back to the analog display. The analog and digital displays are both constructed when the script starts, but only one is ever packed at any given time.

A right mouse click on the clock in either display mode shows or hides an attached label that gives the current date in simple text form. Figure 12-18 shows a PyClock running with a digital display, a clicked-on date label, and a centered photo image object.

Figure 12-18. PyClock extended display with an image


The image in the middle of Figure 12-18 is added by passing in a configuration object with appropriate settings to the PyClock object constructor. In fact, almost everything about this display can be customized with attributes in PyClock configuration objectshand colors, clock tick colors, center photos, and initial size.

Because PyClock's analog display is based upon a manually sketched figure on a canvas, it has to process window resize events itself: whenever the window shrinks or expands, the clock face has to be redrawn and scaled for the new window size. To catch screen resizes, the script registers for the <Configure> event with bind; surprisingly, this isn't a top-level window manager event like the Close button. As you expand a PyClock, the clock face gets bigger with the windowtry expanding, shrinking, and maximizing the clock window on your computer. Because the clock face is plotted in a square coordinate system, PyClock always expands in equal horizontal and vertical proportions, though; if you simply make the window only wider or taller, the clock is unchanged.

New in this edition and version is a countdown timer feature: press the "s" or "m" key to pop up a simple dialog for entering the number of seconds or minutes for the countdown, respectively. Once the countdown expires, the pop up in Figure 12-19 appears and fills the entire screen on Windows. I sometimes use this in classes I teach to remind myself and my students when it's time to move on (the effect is more striking when this pop up is projected onto an entire wall!).

Figure 12-19. PyClock countdown timer expired


Finally, like PyEdit, PyClock can be run either standalone or attached to and embedded in other GUIs that need to display the current time. When standalone, the windows module from the last chapter (Example 11-13) is reused here to get a window icon, title, and quit pop up for free. To make it easy to start preconfigured clocks, a utility module called clockStyles provides a set of clock configuration objects you can import, subclass to extend, and pass to the clock constructor; Figure 12-20 shows a few of the preconfigured clock styles and sizes in action, ticking away in sync.

Figure 12-20. A few canned clock styles (Guido's photo reprinted with permission from Dr. Dobb's Journal)


Each of these clocks uses after events to check for system-time rollover 10 times per second. When run as top-level windows in the same process, all receive a timer event from the same event loop. When started as independent programs, each has an event loop of its own. Either way, their second hands sweep in unison each second.

12.6.3. PyClock Source Code

All of the PyClock source code lives in one file, except for the precoded configuration style objects. If you study the code at the bottom of the file shown in Example 12-12, you'll notice that you can either make a clock object with a configuration object passed in or specify configuration options by command-line arguments (in which case, the script simply builds a configuration object for you). More generally, you can run this file directly to start a clock, import and make its objects with configuration objects to get a more custom display, or import and attach its objects to other GUIs. For instance, PyGadgets in Chapter 10 runs this file with command-line options to tailor the display.

Example 12-12. PP3E\Gui\Clock\clock.py

 ############################################################################### # PyClock 2.0: a clock GUI, with both analog and digital display modes, # a pop-up date label, clock face images, resizing, etc.  May be run both # standalone, or embedded (attached) in other GUIs that need a clock. # New in 2.0: s/m keys set seconds/minutes timer for pop-up msg; window icon. ############################################################################### from Tkinter import * from tkSimpleDialog import askinteger import math, time, sys ############################################################################### # Option configuration classes ############################################################################### class ClockConfig:     # defaults--override in instance or subclass     size = 200                                        # width=height     bg, fg = 'beige', 'brown'                         # face, tick colors     hh, mh, sh, cog = 'black', 'navy', 'blue', 'red'  # clock hands, center     picture = None                                    # face photo file class PhotoClockConfig(ClockConfig):     # sample configuration     size    = 320     picture = '../gifs/ora-pp.gif'     bg, hh, mh = 'white', 'blue', 'orange' ############################################################################### # Digital display object ############################################################################### class DigitalDisplay(Frame):     def _ _init_ _(self, parent, cfg):         Frame._ _init_ _(self, parent)         self.hour = Label(self)         self.mins = Label(self)         self.secs = Label(self)         self.ampm = Label(self)         for label in self.hour, self.mins, self.secs, self.ampm:             label.config(bd=4, relief=SUNKEN, bg=cfg.bg, fg=cfg.fg)             label.pack(side=LEFT)     def onUpdate(self, hour, mins, secs, ampm, cfg):         mins = str(mins).zfill(2)                          # or '%02d' % x         self.hour.config(text=str(hour), width=4)         self.mins.config(text=str(mins), width=4)         self.secs.config(text=str(secs), width=4)         self.ampm.config(text=str(ampm), width=4)     def onResize(self, newWidth, newHeight, cfg):         pass  # nothing to redraw here ############################################################################### # Analog display object ############################################################################### class AnalogDisplay(Canvas):     def _ _init_ _(self, parent, cfg):         Canvas._ _init_ _(self, parent,                         width=cfg.size, height=cfg.size, bg=cfg.bg)         self.drawClockface(cfg)         self.hourHand = self.minsHand = self.secsHand = self.cog = None     def drawClockface(self, cfg):                         # on start and resize         if cfg.picture:                                   # draw ovals, picture             try:                 self.image = PhotoImage(file=cfg.picture)           # bkground             except:                 self.image = BitmapImage(file=cfg.picture)           # save ref             imgx = (cfg.size - self.image.width( ))  / 2            # center it             imgy = (cfg.size - self.image.height( )) / 2             self.create_image(imgx+1, imgy+1,  anchor=NW, image=self.image)         originX =  originY = radius = cfg.size/2         for i in range(60):             x, y = self.point(i, 60, radius-6, originX, originY)             self.create_rectangle(x-1, y-1, x+1, y+1, fill=cfg.fg)  # mins         for i in range(12):             x, y = self.point(i, 12, radius-6, originX, originY)             self.create_rectangle(x-3, y-3, x+3, y+3, fill=cfg.fg)  # hours         self.ampm = self.create_text(3, 3, anchor=NW, fill=cfg.fg)     def point(self, tick, units, radius, originX, originY):         angle = tick * (360.0 / units)         radiansPerDegree = math.pi / 180         pointX = int( round( radius * math.sin(angle * radiansPerDegree) ))         pointY = int( round( radius * math.cos(angle * radiansPerDegree) ))         return (pointX + originX+1), (originY+1 - pointY)     def onUpdate(self, hour, mins, secs, ampm, cfg):        # on timer callback         if self.cog:                                        # redraw hands, cog             self.delete(self.cog)             self.delete(self.hourHand)             self.delete(self.minsHand)             self.delete(self.secsHand)         originX = originY = radius = cfg.size/2         hour = hour + (mins / 60.0)         hx, hy = self.point(hour, 12, (radius * .80), originX, originY)         mx, my = self.point(mins, 60, (radius * .90), originX, originY)         sx, sy = self.point(secs, 60, (radius * .95), originX, originY)         self.hourHand = self.create_line(originX, originY, hx, hy,                              width=(cfg.size * .04),                              arrow='last', arrowshape=(25,25,15), fill=cfg.hh)         self.minsHand = self.create_line(originX, originY, mx, my,                              width=(cfg.size * .03),                              arrow='last', arrowshape=(20,20,10), fill=cfg.mh)         self.secsHand = self.create_line(originX, originY, sx, sy,                              width=1,                              arrow='last', arrowshape=(5,10,5), fill=cfg.sh)         cogsz = cfg.size * .01         self.cog = self.create_oval(originX-cogsz, originY+cogsz,                                     originX+cogsz, originY-cogsz, fill=cfg.cog)         self.dchars(self.ampm, 0, END)         self.insert(self.ampm, END, ampm)     def onResize(self, newWidth, newHeight, cfg):         newSize = min(newWidth, newHeight)         #print 'analog onResize', cfg.size+4, newSize         if newSize != cfg.size+4:             cfg.size = newSize-4             self.delete('all')             self.drawClockface(cfg)  # onUpdate called next ############################################################################### # Clock composite object ############################################################################### ChecksPerSec = 10  # second change timer class Clock(Frame):     def _ _init_ _(self, config=ClockConfig, parent=None):         Frame._ _init_ _(self, parent)         self.cfg = config         self.makeWidgets(parent)                     # children are packed but         self.labelOn = 0                             # clients pack or grid me         self.display = self.digitalDisplay         self.lastSec = self.lastMin = -1         self.countdownSeconds = 0         self.onSwitchMode(None)         self.onTimer( )     def makeWidgets(self, parent):         self.digitalDisplay = DigitalDisplay(self, self.cfg)         self.analogDisplay  = AnalogDisplay(self,  self.cfg)         self.dateLabel      = Label(self, bd=3, bg='red', fg='blue')         parent.bind('<ButtonPress-1>', self.onSwitchMode)         parent.bind('<ButtonPress-3>', self.onToggleLabel)         parent.bind('<Configure>',     self.onResize)         parent.bind('<KeyPress-s>',    self.onCountdownSec)         parent.bind('<KeyPress-m>',    self.onCountdownMin)     def onSwitchMode(self, event):         self.display.pack_forget( )         if self.display == self.analogDisplay:             self.display = self.digitalDisplay         else:             self.display = self.analogDisplay         self.display.pack(side=TOP, expand=YES, fill=BOTH)     def onToggleLabel(self, event):         self.labelOn += 1         if self.labelOn % 2:             self.dateLabel.pack(side=BOTTOM, fill=X)         else:             self.dateLabel.pack_forget( )         self.update( )     def onResize(self, event):         if event.widget == self.display:             self.display.onResize(event.width, event.height, self.cfg)     def onTimer(self):         secsSinceEpoch = time.time( )         timeTuple      = time.localtime(secsSinceEpoch)         hour, min, sec = timeTuple[3:6]         if sec != self.lastSec:             self.lastSec = sec             ampm = ((hour >= 12) and 'PM') or 'AM'               # 0...23             hour = (hour % 12) or 12                             # 12..11             self.display.onUpdate(hour, min, sec, ampm, self.cfg)             self.dateLabel.config(text=time.ctime(secsSinceEpoch))             self.countdownSeconds -= 1             if self.countdownSeconds == 0:                 self.onCountdownExpire( )                # countdown timer         self.after(1000 / ChecksPerSec, self.onTimer)    # run N times per second     def onCountdownSec(self, event):         secs = askinteger('Countdown', 'Seconds?')         if secs: self.countdownSeconds = secs     def onCountdownMin(self, event):         secs = askinteger('Countdown', 'Minutes')         if secs: self.countdownSeconds = secs * 60     def onCountdownExpire(self):         # caveat: only one active, no progress indicator         win = Toplevel( )         msg = Button(win, text='Timer Expired!', command=win.destroy)         msg.config(font=('courier', 80, 'normal'), fg='white', bg='navy')         msg.config(padx=10, pady=10)         msg.pack(expand=YES, fill=BOTH)         win.lift( )                             # raise above siblings         if sys.platform[:3] == 'win':            # full screen on Windows             win.state('zoomed') ############################################################################### # Standalone clocks ############################################################################### # b/w compat: manual window borders, passed-in parent appname = 'PyClock 2.0' class ClockWindow(Clock):     def _ _init_ _(self, config=ClockConfig, parent=None, name=''):         Clock._ _init_ _(self, config, parent)         self.pack(expand=YES, fill=BOTH)         title = appname         if name: title = appname + ' - ' + name         self.master.title(title)                # master=parent or default         self.master.protocol('WM_DELETE_WINDOW', self.quit) # use new custom Tk, Toplevel for icons, etc. from PP3E.Gui.Tools.windows import PopupWindow, MainWindow class ClockPopup(PopupWindow):     def _ _init_ _(self, config=ClockConfig, name=''):         PopupWindow._ _init_ _(self, appname, name)         clock = Clock(config, self)         clock.pack(expand=YES, fill=BOTH) class ClockMain(MainWindow):     def _ _init_ _(self, config=ClockConfig, name=''):         MainWindow._ _init_ _(self, appname, name)         clock = Clock(config, self)         clock.pack(expand=YES, fill=BOTH) ############################################################################### # Program run ############################################################################### if _ _name_ _ == '_ _main_ _':     def getOptions(config, argv):         for attr in dir(ClockConfig):              # fill default config obj,             try:                                   # from "-attr val" cmd args                 ix = argv.index('-' + attr)             except:                 continue             else:                 if ix in range(1, len(argv)-1):                     if type(getattr(ClockConfig, attr)) == type(0):                         setattr(config, attr, int(argv[ix+1]))                     else:                         setattr(config, attr, argv[ix+1])    #config = PhotoClockConfig( )     config = ClockConfig( )     if len(sys.argv) >= 2:         getOptions(config, sys.argv)           # clock.py -size n -bg 'blue'...    #myclock = ClockWindow(config, Tk( ))      # parent is Tk root if standalone    #myclock = ClockPopup(ClockConfig( ), 'popup')     myclock = ClockMain(config)     myclock.mainloop( ) 

And finally, Example 12-13 shows the module that is actually run from the PyDemos launcher scriptit predefines a handful of clock styles and runs six of them at once, attached to new top-level windows for a demo effect (though one clock per screen is usually enough in practice, even for me).[*]

[*] Note that some images named in this script may be missing in the book examples distribution due to copyright concerns. Insert lawyer joke here.

Example 12-13. PP3E\Gui\Clock\clockStyles.py

 from clock import * from Tkinter import mainloop gifdir = '../gifs/' if _ _name_ _ == '_ _main_ _':     from sys import argv     if len(argv) > 1:         gifdir = argv[1] + '/' class PPClockBig(PhotoClockConfig):     picture, bg, fg = gifdir + 'ora-pp.gif', 'navy', 'green' class PPClockSmall(ClockConfig):     size    = 175     picture = gifdir + 'ora-pp.gif'     bg, fg, hh, mh = 'white', 'red', 'blue', 'orange' class GilliganClock(ClockConfig):     size    = 550     picture = gifdir + 'gilligan.gif'     bg, fg, hh, mh = 'black', 'white', 'green', 'yellow' class GuidoClock(GilliganClock):     size = 400     picture = gifdir + 'guido_ddj.gif'     bg = 'navy' class GuidoClockSmall(GuidoClock):     size, fg = 278, 'black' class OusterhoutClock(ClockConfig):     size, picture = 200, gifdir + 'ousterhout-new.gif'     bg, fg, hh    = 'black', 'gold', 'brown' class GreyClock(ClockConfig):     bg, fg, hh, mh, sh = 'grey', 'black', 'black', 'black', 'white' class PinkClock(ClockConfig):     bg, fg, hh, mh, sh = 'pink', 'yellow', 'purple', 'orange', 'yellow' class PythonPoweredClock(ClockConfig):     bg, size, picture = 'white', 175, gifdir + 'pythonPowered.gif' if _ _name_ _ == '_ _main_ _':     for configClass in [         ClockConfig,         PPClockBig,         #PPClockSmall,         GuidoClockSmall,         #GilliganClock,         OusterhoutClock,         #GreyClock,         PinkClock,         PythonPoweredClock     ]:         ClockPopup(configClass, configClass._ _name_ _)     Button(text='Quit Clocks', command='exit').pack( )     mainloop( ) 




Programming Python
Programming Python
ISBN: 0596009259
EAN: 2147483647
Year: 2004
Pages: 270
Authors: Mark Lutz

Similar book on Amazon

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net