MIME-Version: 1.0 Content-Location: file:///C:/90861A52/Week12.htm Content-Transfer-Encoding: quoted-printable Content-Type: text/html; charset="us-ascii" Week 12

Week = 12
GUI Programming

Indiana University

Computer Science A202 / A598

and Informatics I211

 

Basic= GUI Elements

<= ![if !supportLists]>n   There are three basic Graphic User Interface (GUI) elements<= o:p>

<= ![if !supportLists]>q   <= /span>widgets that = are visible on the screen

<= ![if !supportLists]>n   &nb= sp; examples: buttons, labels, check boxes, text fields, and scroll bars=

<= ![if !supportLists]>q   a layout manager that determines where the widgets are positioned on the screen

<= ![if !supportLists]>q   <= /span>event handler funct= ions that are invoked when GUI events occur

<= ![if !supportLists]>n   &nb= sp; examples:

<= ![if !supportLists]>q    left mouse button click on a GUI button

<= ![if !supportLists]>q    <enter> key pressed with focus on a text field

<= ![if !supportLists]>q    scroll bar moved

Pytho= n's Tkinter module

<= ![if !supportLists]>n    Most programming languages have several libraries = for GUI programming

<= ![if !supportLists]>q   for example in Java the= two most popular libraries are in the packages java.awt and javax.swi= ng

<= ![if !supportLists]>q   <= /span>generally you must use just one GUI library in a program<= /span>

<= ![if !supportLists]>q   <= /span>the widget, layout manager, and handler concepts are similar regardl= ess of the GUI library

<= ![if !supportLists]>n    The module Tkinter contains Python's most widely used and most portable GUI library

<= ![if !supportLists]>q   <= /span>"Tkinter" stands for "Tk interface"

<= ![if !supportLists]>q   <= /span>note the capital T

<= ![if !supportLists]>q   <= /span>Tk is a popular GUI Tool kit (library) developed in Unix systems and ported to most other systems

<= ![if !supportLists]>q   the IDLE interface uses= Tkinter

<= ![if !supportLists]>n    See the course web resources page for links to = Tkinter documentation (which isn't as good as most other Python docs)

GUI example

# click.py by chaynes@indiana.edu

 

from Tkinter import * # import everything from Tkinter module

 

root =3D Tk() # create a Tk interpreter with frame=

 

label =3D Label(root, text=3D'Hello, Tk user!') # create a label wid= get

label.grid() # display it in the root frame with the grid layout man= ager

 

def clickHandler(): # installed to handle button clicks

    label.con= fig(text=3D'You clicked') # change the label text

 

# create a button with click handler and display it

Button(root, text=3D'Click me', command=3DclickHandler).grid()<= /o:p>

 

mainloop() # listen for events, don't end application

Overa= ll form for a Tkinter program application

from Tkinter import *

<= ![if !supportLists]>q  = import everything from the Tkinter module

<= ![if !supportLists]>q  = note the capital T

root =3D Tk()

<= ![if !supportLists]>q  = create an instance of the class Tk, which create a Tk interpr= eter and a new frame (window) on the display

<= ![if !supportLists]>q  = root is the parent of all G= UI widgets

<= ![if !supportLists]>n  = unless nested containers are used, as explained later

<= ![if !supportLists]>q  = only do this once in an application: if you need other frames, use a different class

<= ![if !supportLists]>n   &nb= sp;   Application-specific co= de here …

<= ![if !supportLists]>q  = for example defining event handlers, creating widgets, informing the layout manager of the widgets, and installing the event handlers=

mainloop()

<= ![if !supportLists]>q  = tells the Tk interpreter to listen for GUI events<= /p>

<= ![if !supportLists]>q  = this call does not return until the GUI is terminated, so it must be executed after all other GUI setup code

<= ![if !supportLists]>q  = without this statement the application will terminate immediately

Steps= for building a basic GUI, part 1

<= ![if !supportLists]>n   These steps may be done in any order or interleaved, so long as values are defined before they are used

<= ![if !supportLists]>q   <= /span>of course try to organize the code in way that aids understanding

<= ![if !supportLists]>n   Define event handlers

<= ![if !supportLists]>q   <= /span>unlike other code you write, event handlers are ca= lled automatically by the system, not by your program code

<= ![if !supportLists]>q   <= /span>event handlers are also called listeners, because they are called when some event is "heard"

<= ![if !supportLists]>q   <= /span>for example, the button handler in the click example

   def clickHandle= r():

       label.config(text=3D'You clicked')=

 =

Steps= for building a basic GUI, part 2

<= ![if !supportLists]>n    Create widgets

<= ![if !supportLists]>q   <= /span>each is an instance of a class

<= ![if !supportLists]>q   <= /span>each has a parent contains it (for now always root)

<= ![if !supportLists]>q   <= /span>in the click example

   label =3D Label= (root, text=3D'Hello, Tk user!')

   Button(root, text=3D'Click me', command=3DclickHandler).grid()

<= ![if !supportLists]>n    Inform the layout manager of each widget

<= ![if !supportLists]>q   <= /span>often information on how to position a widget is provided at this ti= me

<= ![if !supportLists]>q   <= /span>in the click example, which uses the grid layout manag= er, there is the grid() method call to the anonymous Button widget above and the call label.grid()

<= ![if !supportLists]>n    Install a handler in each widget that has an event= of interest

<= ![if !supportLists]>q   <= /span>as above with the command parameter of the Button widg= et

A sec= ond example: upCounter

# upCounter.py by chaynes@indiana.edu

 

from Tkinter import *

 

root =3D Tk()

 

count =3D 0

 

label =3D Label(root, text=3Dstr(count))

label.grid()

 

def clickHandler():

    global co= unt # required for local assignment

    count += =3D 1

    label.con= fig(text=3Dstr(count))

 

Button(root, text=3D'Increment', command=3DclickHandler).grid()=

 

mainloop()

Widget attributes

<= ![if !supportLists]>n   Widgets have configuration attributes that may be set when the widgets are created

<= ![if !supportLists]>q   <= /span>they all have default values if not specified=

<= ![if !supportLists]>q   <= /span>in the upCounter example text and command attributes are set when the label and button widgets are created

label =3D Label(root, text=3Dstr(count))

Button(root, text=3D'Increment', command=3DclickHandler).grid()=

<= ![if !supportLists]>n   A widget's attribute values can also be changed at any time using its .con= fig method

<= ![if !supportLists]>q   <= /span>event handlers frequently do this

<= ![if !supportLists]>q   <= /span>for example, in the clickHandler function of the upCounter example

 label.config(text=3Dstr(count))

description

<= ![if !supportLists]>n      We create a downCounter GUI frame uses three widgets<= /p>

<= ![if !supportLists]>q a label displaying the count

<= ![if !supportLists]>q a button that decrements the count

<= ![if !supportLists]>q an entry widget in which a new count value = can be entered

=  

 =

implementation

from Tkinter import *

root =3D Tk()

count =3D 0

def clickHandler():

    global co= unt

    count -= =3D 1

    label.config(text=3Dstr(count))    =

def entryHandler(_):

    global co= unt

    count =3D int(entry.get())

    label.config(text=3Dstr(count))    =

label =3D Label(root, text=3Dstr(count))

label.grid()

Button(root, text=3D'Decrement', command=3DclickHandler).grid()=

 

entry =3D Entry(root, width=3D6)

entry.bind('<Return>', entryHandler) # install event hander for keyboard return

entry.grid()

 

mainloop()

widgets

<= ![if !supportLists]>n   An Entry widget is a one-line box for entering text

<= ![if !supportLists]>q   <= /span>in other GUI tool kits such widgets are often call= ed "text fields"

<= ![if !supportLists]>n   A width attribute specifies the box width in characters

<= ![if !supportLists]>n   An entry has the keyboard focus if there is a vertical cursor in it

<= ![if !supportLists]>q   <= /span>the user can move the focus to an entry by clickin= g in it

<= ![if !supportLists]>q   <= /span>a widget only responds to keyboard events if it has the focus

<= ![if !supportLists]>n   A call of the form entry.bind('<Return>', handler<= b>) installs the handler function to handle the event of a keyboard return if = entry has the focus

<= ![if !supportLists]>q   <= /span>handlers installed by the .bind method take= one argument, which in this use can be ignored

<= ![if !supportLists]>q   <= /span>we have seen that handlers installed as command= attributes take no arguments

Layout managers

<= ![if !supportLists]>n   A widget cannot be displayed until the layout manager is informed of its existence and given any needed information on how it is to be positioned

<= ![if !supportLists]>n   Tkinter has three layout managers

<= ![if !supportLists]>q   <= /span>grid layou= t is not complicated and is fairly powerful

<= ![if !supportLists]>n   &nb= sp; it is the only one we will use

<= ![if !supportLists]>q   <= /span>pack layou= t is very powerful, but harder to understand

<= ![if !supportLists]>q   place<= span style=3D'font-size:10.0pt;font-family:Arial;mso-bidi-font-family:Arial; mso-bidi-language:#AC45'> layout allows the program to control just where widgets go

<= ![if !supportLists]>q   <= /span>never use = more than one layout manager (in the same container)

<= ![if !supportLists]>q   <= /span>most GUI libraries have grid and place layout mana= gers of some kind, though they differ widely in the details of their operation

<= ![if !supportLists]>n&nb= sp;  Tkinter layout managers redo t= he layout whenever a widget is added or modified

Grid layout

<= ![if !supportLists]>n   Grid layout arranges widgets in a table-like format with rows and columns

<= ![if !supportLists]>q   <= /span>it is similar to the table layout in HTML, spreadsheets , and text editors (such as Word)

<= ![if !supportLists]>n   The width of each column is determined by the width of the widest widget in the column

<= ![if !supportLists]>q   <= /span>similarly row height is based on the maximum eleme= nt height

<= ![if !supportLists]>n   Normally the grid cell in which a widget is placed is specified in its .grid method call by keyword arguments

<= ![if !supportLists]>q   <= /span>widget.grid= (row=3DrowIndex, col= umn=3DcolumnIndex)

<= ![if !supportLists]>q   <= /span>zero-based indexing

<= ![if !supportLists]>q   <= /span>if neither is specified, as in the .grid() calls in our examples so far, the widget is placed in column 0 of a new row= at the bottom

Advan= ced grid layout, part 1

<= ![if !supportLists]>n    In your project you are not expected to use this a= nd other "advanced" features in these notes, but you are encouraged = to try them if you like

<= ![if !supportLists]>n    The sticky option (keyword argument) gives control over where the widget is positioned in a grid cell if it does not f= ill the whole cell

<= ![if !supportLists]>q   <= /span>values for this option are the Tkinter constants N, S, E, = W, NW, NE, SW, and SE, making the widget be as close as possible to= the edge or corner of the grid cell in the indicated direction

<= ![if !supportLists]>n   &nb= sp; example: label.grid(row=3D0, column=3D0, sticky=3DE) will rig= ht-justify label in the upper-left cell

<= ![if !supportLists]>q   <= /span>these constants may be added to stretch the widget=

<= ![if !supportLists]>n   &nb= sp; example: E+W stretches the widget to fill the cell horizontal= ly

<= ![if !supportLists]>q   <= /span>the default position is centered in the cell

<= ![if !supportLists]>n    The columnspan option indicates that the wi= dget is to be layed out in an area that spans the indicated number of columns

<= ![if !supportLists]>q   <= /span>example: label.grid(row=3D0, column=3D1, columnspan=3D2) will= position label in the area of the 2nd and 3rd columns of row 1<= /o:p>

<= ![if !supportLists]>q   <= /span>similar to columnspan specifications in HTML tables and spreadsheets=

Advan= ced grid layout, part 2

<= ![if !supportLists]>n   The .columnconfigure (or .rowconfigure) method of a parent (e.g. = root) may be used to options that apply to an entire row or column

<= ![if !supportLists]>q   <= /span>the first argument is the column (or row) index

<= ![if !supportLists]>q   <= /span>the minsize options specifies the minimum w= idth (or height) of the column (row) in pixels

<= ![if !supportLists]>q   <= /span>the pad option specifies extra space to be added to the width (or height) in pixels

<= ![if !supportLists]>q   <= /span>example: root.columnconfigure(1, pad=3D5) a= dds 5 pixels of width to the second column

<= ![if !supportLists]>n   There are many more GUI features, including more grid layout possibilities, that you can explore in the Tkinter documentation. GUIs can be complicated!

description

<= ![if !supportLists]>n      The resetCounter GUI frame has three rows and two columns

<= ![if !supportLists]>n      The first row has a Decrement counter descriptive label that does not ch= ange and spans both columns

<= ![if !supportLists]>n      The second row has a Reset button in the first column and an entry for a reset value in the second column

<= ![if !supportLists]>n      The third row has a Decrement button in the first column and a label displaying the count value in the second column

<= ![if !supportLists]>q no decrement occurs if the counter has reached zer= o

<= ![if !supportLists]>q the initial counter and reset values are zero

implementation, part 1

from Tkinter import *

root =3D Tk()

count =3D 0

resetValue =3D 0

 

def decrementHandler():

    global co= unt # required for local assignment of global variables

    if count = > 0:

        count -=3D 1

        showCount()

def showCount(): label.config(text=3Dstr(count))

def resetHandler():

    global co= unt; count =3D resetValue

    showCount= ()
   

def entryHandler(_):

    global re= setValue

    resetValu= e =3D int(entry.get())

    entry.del= ete(0, END)

    resetHand= ler()

implementation, part 2

Label(root, text=3D'Decrement counter').grid(row=3D0, column=3D0, columnspan=3D2)

 

root.columnconfigure(0, pad=3D10)  

Button(root, text=3D'Reset', command=3DresetHandler).grid(row=3D1, c= olumn=3D0,
        =             &nb= sp;            =             &nb= sp;     sticky=3DW)

 

entry =3D Entry(root, width=3D4)

entry.bind('<Return>', entryHandler) # event hander for keyboa= rd return

entry.grid(row=3D1, column=3D1)

 

Button(root, text=3D'Decrement', command=3DdecrementHandler).grid(ro= w=3D2,
        =             &nb= sp;            =             &nb= sp;             column=3D0,

        =             &nb= sp;            =             &nb= sp;            =     sticky=3DW)

label =3D Label(root, text=3Dstr(count))

label.grid(row=3D2, column=3D1, sticky=3DW)

 

mainloop()

 

description

<= ![if !supportLists]>n   The timer application plays a sound when a given number of seconds has elapsed and is easily reset to time the same interval again

<= ![if !supportLists]>n   Its GUI has

<= ![if !supportLists]>q   <= /span>a text entry box for entering a number of seconds<= o:p>

<= ![if !supportLists]>q   <= /span>a Start button for starting the timer<= /o:p>

<= ![if !supportLists]>q   <= /span>a label indicating how many seconds are left<= /o:p>

 =

 =

implementation

from Tkinter import *

import winsound, threading

root =3D Tk()

tickInterval =3D 1 # seconds

      = timer =3D None

 

<= ![if !supportLists]>q     function definiti= ons here

   

label =3D Label(root, text=3D'')

label.grid()

 

entry =3D Entry(root, width=3D6)

entry.bind('<Return>', entryHandler) # install event hander for keyboard return

entry.grid()

entry.focus() # start with focus on text entry=

 

button =3D Button(root, text=3D'Start', command=3DstartHandler)=

button.grid()

button.bind('<Return>', entryHandler)

 

mainloop()

function definitions, part 1

def setSeconds(secs):

    global se= conds

    seconds = =3D secs

    label.config(text=3Dstr(seconds))

 

def startTimer(secs):

    global ti= mer

    setSecond= s(secs)

    # invoke timerCallback after tickInterval seconds

    timer =3D threading.Timer(tickInterval, timerCallback)

    timer.sta= rt()

   

def timerCallback():

    if second= s > 1:

        startTimer(seconds - tickInterval)

    else:

        done()

 

=  

function definitions, part 2

def timerCallback():

    if second= s > 1:

        startTimer(seconds - tickInterval)

    else:

        done()

 

def done():

    label.config(text=3D'Done')

    winsound.PlaySound('play default beep', winsound.SND_FILENAME)<= /o:p>

    button.fo= cus() # with focus on button, <return> will restart timer

   

def entryHandler(ignored):

    startHand= ler()

 

def startHandler():

    global se= conds

    try:=

        startTimer(int(entry.get()))

    except: #= clear the entry text if it is bogus

        entry.delete(0, len(entry.get()))

Advan= ced layout: containers

<= ![if !supportLists]>n    In our examples so far, the layout has consisted entirely of widgets

<= ![if !supportLists]>n    More generally, layout managers position compon= ents that may be either widgets or containers containing other components=

<= ![if !supportLists]>n    Each container is drawn on in a rectangular area w= ith width, height, and position (like a widget), and also has its own layout manager for its components

<= ![if !supportLists]>n    The screen may thus be viewed as a collection on non-overlapping, but possibly nested, rectangles

<= ![if !supportLists]>q   <= /span>those rectangles containing others are containers<= /p>

<= ![if !supportLists]>q   <= /span>this nesting ability allows much greater layout freedom

<= ![if !supportLists]>n    This use of components, containers, and widgets is= the same in most GUI libraries

Advan= ced: frames are Tkinter containers

<= ![if !supportLists]>n   In Tkinter, instances of the Frame class are containers

<= ![if !supportLists]>n   Instances of the Tk class are frames

<= ![if !supportLists]>q   <= /span>for example root in our code

<= ![if !supportLists]>n   The first argument of every widget constructor is the container in which it is nested (layed out)

<= ![if !supportLists]>q   <= /span>this has been root in our code so far<= /o:p>

<= ![if !supportLists]>n   Create all the components within a container before making the container's layout manager call

<= ![if !supportLists]>n   Frames have specified width and height attributes, allowing empty frames (containi= ng no components) to be used to add blank space in the layout of their parent container

Advan= ced frame example

<= ![if !supportLists]>n    frameCounter.py is derived from <= b>resetCounter.py

<= ![if !supportLists]>q   <= /span>difference: the entry box is located 10 pixels to the right of the r= eset button using a containing frame with groove relief and an empty space frame=

<= ![if !supportLists]>q   <= /span>in resetCounter.py, replace the reset button and entry lines = with the following

frame =3D Frame(root, borderwidth=3D2, relief=3DGROOVE)

Button(frame, text=3D'Reset', command=3DresetHandler).grid(row=3D0, =

        =             &nb= sp;            =             &nb= sp;         column=3D0)

Frame(frame, width=3D10).grid(row=3D0, column=3D1) # add 10 pixels o= f space

 

entry =3D Entry(frame, width=3D4)

entry.bind('<Return>', entryHandler)

entry.grid(row=3D0, column=3D2)

entry.focus() # start with focus on text entry=

 

# must be after creating reset button, space frame, and entry widget= s

frame.grid(row=3D1, column=3D0, columnspan=3D2, sticky=3DW)

Advan= ced: timer and dialogTimer examples

<= ![if !supportLists]>n   timer.py is a handly application that beeps and says "Done" after a given number of seconds

<= ![if !supportLists]>q   <= /span>this illustrates the use of the threading.Timer= class with a callback function invoked when the time has elapsed=

<= ![if !supportLists]>n   dialogTimer.py is a = fancy version of timer.py

<= ![if !supportLists]>q   <= /span>among other new functionality, it pops up a dialog frame when the time has elapsed

<= ![if !supportLists]>q   <= /span>among other new programming tricks, it uses

<= ![if !supportLists]>n   &nb= sp; .geometry to control the positio= n of a window on the screen

<= ![if !supportLists]>n   &nb= sp; tkFont.Font  to create a large font

<= ![if !supportLists]>n   &nb= sp; the background attribute to set color

<= ![if !supportLists]>n   &nb= sp; a handy function to manage optional command arguments

<= ![if !supportLists]>n   &nb= sp; the <Escape> binding to allow closing of a dialog with = the escape key

 

click.py

# click.py by chaynes@indiana.edu

from Tkinter import * # import everything, note capital T

root =3D Tk() # create a Tk interpreter with frame

label =3D Label(root, text=3D'Hello, Tk user!') # create a label widget
label.grid() # display it in the root frame with the grid layout manager

def clickHandler(): # installed to handle button clicks
    label.config(text=3D'You clicked') # change the label text

# create a button with click handler and display it
Button(root, text=3D'Click me', command=3DclickHandler).grid()

mainloop() # listen for events, don't end application

dialogTimer.py

'''dialogTimer.py by chaynes@indiana.edu

Usage: python dialogTimer.py [TIME [TICK_INTERVAL [TICK_DECREMENT]]]

Display a window with an entry box, start button, and time label.  Entering a number
or pressing start begins a countdown from TIME by TICK_DECREMENT every TICK_INTERVAL
seconds (defaults 10, 6, and .1, resp.).  The time is self-starting if the time is
not 0. The timer may be started or restarted by entering a time in the box, which
becomes the new time, or pressing start, which starts the count from the time in the
box.

When the time reaches zero (or very close to it), the displayed time changes to
'Done', beeps, and displays a dialog saying "Time's up!" and presenting a button with
the focus that restarts the timer from its last entry value. Restarting the timer,
either by this button or the main timer window entry box or button dismisses the
dialog.
'''

from Tkinter import *
import winsound, threading, sys, tkFont

root =3D Tk()
root.title('Timer')

def nextArg(default):
    if len(sys.argv) > 1:
        return sys.argv.pop(1)
    else:
        return default

time =3D float(nextArg(10)) # initial time value
tickInterval =3D float(nextArg(6)) # seconds
tickDecrement =3D float(nextArg(.1))

timer =3D None
dialog =3D None # time over dialog frame

def setTime(t):
    global time
    time =3D t
    label.config(text=3D'%.1f'%time)

def startTimer(time):
    global timer
    setTime(time)
    # invoke timerCallback after tickInterval seconds
    timer =3D threading.Timer(tickInterval, timerCallback)
    timer.start()

def timerCallback():
    if time > tickDecrement + .0001:
        startTimer(time - tickDecrement)
    else:
        done()

def done():
    label.config(text=3D'Done')
    winsound.PlaySound('play default beep', winsound.SND_FILENAME)
    button.focus() # with focus on button, <return> will start timer again

    global dialog
    dialog =3D Toplevel(root)
    labelFont =3D tkFont.Font(family=3D'Ariel', size=3D18)
    Label(dialog, text=3D"Time's up!", font=3DlabelFont, background=3D'red').grid()
    restart =3D Button(dialog, text=3D'Restart', command=3DstartHandler)
    restart.grid(row=3D0, column=3D1, padx=3D5)
    restart.bind('<Return>', startHandler)
    restart.focus()
    restart.bind_all('<Escape>', closeHandler)
    dialog.geometry('170x30+400+300') # format WIDTHxHEIGHT+X+Y

def closeHandler(*args): # take zero or one argument
    global dialog
    dialog.destroy()
    dialog =3D None

def startHandler(*args):
    if dialog: # if there is a dialog, kill it
        closeHandler()
    try:
        startTimer(float(entry.get()))
    except: # clear the entry text if it is bogus
        entry.delete(0, len(entry.get()))

entry =3D Entry(root, width=3D4)
entry.bind('<Return>', startHandler) # install event hander for keyboard return
entry.grid(row=3D0, column=3D0)
entry.insert(0, str(time)) # instert time in entry box
entry.select_range(0, END) # select all text in entry box
entry.focus() # start with focus on text entry

button =3D Button(root, text=3D'Start', command=3DstartHandler)
button.grid(row=3D0, column=3D1)
button.bind('<Return>', startHandler)

root.columnconfigure(2, minsize=3D50)
label =3D Label(root, padx=3D3)
label.grid(row=3D0, column=3D2)

root.geometry('1x25+10+10')

if time !=3D 0:
    startHandler()

mainloop()

downCounter.py

# downCounter.py by chaynes@indiana.edu

from Tkinter import *

root =3D Tk()

count =3D 0

label =3D Label(root, text=3Dstr(count))
label.grid()

def clickHandler():
    global count # required for local assignment of global variables
    count -=3D 1
    showCount()

def showCount():
    label.config(text=3Dstr(count))

def entryHandler(_):
    global count
    count =3D int(entry.get())
    showCount()

Button(root, text=3D'Decrement', command=3DclickHandler).grid()

entry =3D Entry(root, width=3D6)
entry.bind('<Return>', entryHandler) # install event hander for keyboard return
entry.grid()

mainloop()

frameCounter.py

# frameCounter.py by chaynes@indiana.edu

from Tkinter import *

root =3D Tk()

count =3D 0
resetValue =3D 0

def decrementHandler():
    global count # required for local assignment of global variables
    if count > 0:
        count -=3D 1
        showCount()

def showCount():
    label.config(text=3Dstr(count))

def resetHandler():
    global count; count =3D resetValue
    showCount()

def entryHandler(_):
    global resetValue
    resetValue =3D int(entry.get())
    entry.delete(0, END)
    resetHandler()

Label(root, text=3D'Decrement counter').grid(row=3D0, column=3D0, columnspan=3D2)

root.columnconfigure(0, pad=3D10)

frame =3D Frame(root, borderwidth=3D2, relief=3DGROOVE)
Button(frame, text=3D'Reset', command=3DresetHandler).grid(row=3D0, column=3D0)

Frame(frame, width=3D10, borderwidth=3D2, relief=3DGROOVE).grid(row=3D0, column=3D1) # add 10 pixels of space

entry =3D Entry(frame, width=3D4)
entry.bind('<Return>', entryHandler) # install event hander for keyboard return
entry.grid(row=3D0, column=3D2)

# must be after creating the reset button, space frame, and entry
frame.grid(row=3D1, column=3D0, columnspan=3D2, sticky=3DW)

Button(root, text=3D'Decrement', command=3DdecrementHandler).grid(row=3D2, column=3D0,
                                                              sticky=3DW)
label =3D Label(root, text=3Dstr(count))
label.grid(row=3D2, column=3D1, sticky=3DW)

mainloop()

resetCounter.py

# resetCounter.py by chaynes@indiana.edu

from Tkinter import *

root =3D Tk()

count =3D 0
resetValue =3D 0

def decrementHandler():
    global count # required for local assignment of global variables
    if count > 0:
        count -=3D 1
        showCount()

def showCount():
    label.config(text=3Dstr(count))

def resetHandler():
    global count; count =3D resetValue
    showCount()

def entryHandler(_):
    global resetValue
    resetValue =3D int(entry.get())
    entry.delete(0, END)
    resetHandler()

Label(root, text=3D'Decrement counter').grid(row=3D0, column=3D0, columnspan=3D2)

root.columnconfigure(0, pad=3D10)
Button(root, text=3D'Reset', command=3DresetHandler).grid(row=3D1, column=3D0, sticky=3DW)

entry =3D Entry(root, width=3D4)
entry.bind('<Return>', entryHandler) # install event hander for keyboard return
entry.grid(row=3D1, column=3D1)

Button(root, text=3D'Decrement', command=3DdecrementHandler).grid(row=3D2, column=3D0,
                                                              sticky=3DW)
label =3D Label(root, text=3Dstr(count))
label.grid(row=3D2, column=3D1, sticky=3DW)

mainloop()

timer.py

# timer.py by chaynes@indiana.edu

from Tkinter import *
import winsound, threading

root =3D Tk()

tickInterval =3D 1 # seconds
time =3D 0
timer =3D None

def setTime(t):
    global time
    time =3D t
    label.config(text=3Dstr(time))

def startTimer(secs):
    global timer
    setTime(secs)
    # invoke timerCallback after tickInterval seconds
    timer =3D threading.Timer(tickInterval, timerCallback)
    timer.start()

def timerCallback():
    if time > 1:
        startTimer(time - tickInterval)
    else:
        done()

def done():
    label.config(text=3D'Done')
    winsound.PlaySound('play default beep', winsound.SND_FILENAME)
    button.focus() # with focus on button, <Return> will start timer again

def startHandler(*args): # take zero or one argument
    global time
    try:
        startTimer(int(entry.get()))
    except: # clear the entry text if it is bogus
        entry.delete(0, len(entry.get()))

label =3D Label(root)
label.grid()

entry =3D Entry(root, width=3D6)
entry.bind('<Return>', startHandler) # install event hander for keyboard return
entry.grid()
entry.focus() # start with focus on text entry

button =3D Button(root, text=3D'Start', command=3DstartHandler)
button.grid()
button.bind('<Return>', startHandler)

mainloop()

upCounter.py

# upCounter.py by chaynes@indiana.edu

from Tkinter import *

root =3D Tk()

count =3D 0

label =3D Label(root, text=3Dstr(count))
label.grid()

def clickHandler():
    global count # required for local assignment of global variables
    count +=3D 1
    label.config(text=3Dstr(count))

Button(root, text=3D'Increment', command=3DclickHandler).grid()

mainloop()