- Mode Demo
# Mode Demo
from tkinter import *
####################################
# init
####################################
def init(data):
    # There is only one init, not one-per-mode
    data.mode = "splashScreen"
    data.score = 0
####################################
# mode dispatcher
####################################
def mousePressed(event, data):
    if (data.mode == "splashScreen"): splashScreenMousePressed(event, data)
    elif (data.mode == "playGame"):   playGameMousePressed(event, data)
    elif (data.mode == "help"):       helpMousePressed(event, data)
def keyPressed(event, data):
    if (data.mode == "splashScreen"): splashScreenKeyPressed(event, data)
    elif (data.mode == "playGame"):   playGameKeyPressed(event, data)
    elif (data.mode == "help"):       helpKeyPressed(event, data)
def timerFired(data):
    if (data.mode == "splashScreen"): splashScreenTimerFired(data)
    elif (data.mode == "playGame"):   playGameTimerFired(data)
    elif (data.mode == "help"):       helpTimerFired(data)
def redrawAll(canvas, data):
    if (data.mode == "splashScreen"): splashScreenRedrawAll(canvas, data)
    elif (data.mode == "playGame"):   playGameRedrawAll(canvas, data)
    elif (data.mode == "help"):       helpRedrawAll(canvas, data)
####################################
# splashScreen mode
####################################
def splashScreenMousePressed(event, data):
    pass
def splashScreenKeyPressed(event, data):
    data.mode = "playGame"
def splashScreenTimerFired(data):
    pass
def splashScreenRedrawAll(canvas, data):
    canvas.create_text(data.width/2, data.height/2-20,
                       text="This is a splash screen!", font="Arial 26 bold")
    canvas.create_text(data.width/2, data.height/2+20,
                       text="Press any key to play!", font="Arial 20")
####################################
# help mode
####################################
def helpMousePressed(event, data):
    pass
def helpKeyPressed(event, data):
    data.mode = "playGame"
def helpTimerFired(data):
    pass
def helpRedrawAll(canvas, data):
    canvas.create_text(data.width/2, data.height/2-40,
                       text="This is help mode!", font="Arial 26 bold")
    canvas.create_text(data.width/2, data.height/2-10,
                       text="How to play:", font="Arial 20")
    canvas.create_text(data.width/2, data.height/2+15,
                       text="Do nothing and score points!", font="Arial 20")
    canvas.create_text(data.width/2, data.height/2+40,
                       text="Press any key to keep playing!", font="Arial 20")
####################################
# playGame mode
####################################
def playGameMousePressed(event, data):
    data.score = 0
def playGameKeyPressed(event, data):
    if (event.keysym == 'h'):
        data.mode = "help"
def playGameTimerFired(data):
    data.score += 1
def playGameRedrawAll(canvas, data):
    canvas.create_text(data.width/2, data.height/2-40,
                       text="This is a fun game!", font="Arial 26 bold")
    canvas.create_text(data.width/2, data.height/2-10,
                       text="Score = " + str(data.score), font="Arial 20")
    canvas.create_text(data.width/2, data.height/2+15,
                       text="Click anywhere to reset score", font="Arial 20")
    canvas.create_text(data.width/2, data.height/2+40,
                       text="Press 'h' for help!", font="Arial 20")
####################################
# use the run function as-is
####################################
def run(width=300, height=300):
    def redrawAllWrapper(canvas, data):
        canvas.delete(ALL)
        canvas.create_rectangle(0, 0, data.width, data.height,
                                fill='white', width=0)
        redrawAll(canvas, data)
        canvas.update()
    def mousePressedWrapper(event, canvas, data):
        mousePressed(event, data)
        redrawAllWrapper(canvas, data)
    def keyPressedWrapper(event, canvas, data):
        keyPressed(event, data)
        redrawAllWrapper(canvas, data)
    def timerFiredWrapper(canvas, data):
        timerFired(data)
        redrawAllWrapper(canvas, data)
        # pause, then call timerFired again
        canvas.after(data.timerDelay, timerFiredWrapper, canvas, data)
    # Set up data and call init
    class Struct(object): pass
    data = Struct()
    data.width = width
    data.height = height
    data.timerDelay = 100 # milliseconds
    root = Tk()
    init(data)
    # create the root and the canvas
    canvas = Canvas(root, width=data.width, height=data.height)
    canvas.pack()
    # set up events
    root.bind("<Button-1>", lambda event:
                            mousePressedWrapper(event, canvas, data))
    root.bind("<Key>", lambda event:
                            keyPressedWrapper(event, canvas, data))
    timerFiredWrapper(canvas, data)
    # and launch the app
    root.mainloop()  # blocks until window is closed
    print("bye!")
run(300, 300)
Result:

 
- Undo/Redo Demo
# Undo/Redo Demo
from tkinter import *
def init(data):
    data.points = [ ]
    data.undoList = [ ]
def mousePressed(event, data):
    data.points.append((event.x, event.y))
    data.undoList = [ ]
def keyPressed(event, data):
    if (event.keysym == "u"):
        if (len(data.points) > 0):
            data.undoList.append(data.points.pop())
    elif (event.keysym == "r"):
        if (len(data.undoList) > 0):
            data.points.append(data.undoList.pop())
    elif (event.keysym == "c"):
        data.points = [ ]
        data.undoList = [ ]
def timerFired(data):
    pass
def redrawAll(canvas, data):
    if (data.points != []):
        canvas.create_polygon(data.points, fill="gold", outline="black")
    canvas.create_text(data.width/2, 20,
                       text="click to add point. u=undo. r=redo. c=clear.")
    canvas.create_text(data.width/2, 40,
                       text=str(len(data.points)) + " point(s) in polygon")
    canvas.create_text(data.width/2, 60,
                       text=str(len(data.undoList)) + " point(s) on undoList")
####################################
# use the run function as-is
####################################
def run(width=300, height=300):
    def redrawAllWrapper(canvas, data):
        canvas.delete(ALL)
        canvas.create_rectangle(0, 0, data.width, data.height,
                                fill='white', width=0)
        redrawAll(canvas, data)
        canvas.update()
    def mousePressedWrapper(event, canvas, data):
        mousePressed(event, data)
        redrawAllWrapper(canvas, data)
    def keyPressedWrapper(event, canvas, data):
        keyPressed(event, data)
        redrawAllWrapper(canvas, data)
    def timerFiredWrapper(canvas, data):
        timerFired(data)
        redrawAllWrapper(canvas, data)
        # pause, then call timerFired again
        canvas.after(data.timerDelay, timerFiredWrapper, canvas, data)
    # Set up data and call init
    class Struct(object): pass
    data = Struct()
    data.width = width
    data.height = height
    data.timerDelay = 100 # milliseconds
    root = Tk()
    init(data)
    # create the root and the canvas
    canvas = Canvas(root, width=data.width, height=data.height)
    canvas.pack()
    # set up events
    root.bind("<Button-1>", lambda event:
                            mousePressedWrapper(event, canvas, data))
    root.bind("<Key>", lambda event:
                            keyPressedWrapper(event, canvas, data))
    timerFiredWrapper(canvas, data)
    # and launch the app
    root.mainloop()  # blocks until window is closed
    print("bye!")
run(300, 300)
Result:

 
- Images Demo
To run this demo, first download
playing-card-gifs.zip and unzip
that file, so the folder playing-card-gifs is at the same level as this code.
# Images Demo
from tkinter import *
def init(data):
    data.step = 0
    loadPlayingCardImages(data) # always load images in init!
def loadPlayingCardImages(data):
    cards = 55 # cards 1-52, back, joker1, joker2
    data.cardImages = [ ]
    for card in range(cards):
        rank = (card%13)+1
        suit = "cdhsx"[card//13]
        filename = "playing-card-gifs/%s%d.gif" % (suit, rank)
        data.cardImages.append(PhotoImage(file=filename))
def getPlayingCardImage(data, rank, suitName):
    suitName = suitName[0].lower() # only car about first letter
    suitNames = "cdhsx"
    assert(1 <= rank <= 13)
    assert(suitName in suitNames)
    suit = suitNames.index(suitName)
    return data.cardImages[13*suit + rank - 1]
def getSpecialPlayingCardImage(data, name):
    specialNames = ["back", "joker1", "joker2"]
    return getPlayingCardImage(data, specialNames.index(name)+1, "x")
def mousePressed(event, data):
    pass
def keyPressed(event, data):
    pass
def timerFired(data):
    data.step += 1
def redrawAll(canvas, data):
    suitNames = ["Clubs", "Diamonds", "Hearts", "Spades", "Xtras"]
    suit = (data.step//10) % len(suitNames)
    suitName = suitNames[suit]
    cards = 3 if (suitName == "Xtras") else 13
    margin = 10
    (left, top) = (margin, 40)
    for rank in range(1,cards+1):
        image = getPlayingCardImage(data, rank, suitName)
        if (left + image.width() > data.width):
            (left, top) = (margin, top + image.height() + margin)
        canvas.create_image(left, top, anchor=NW, image=image)
        left += image.width() + margin
    canvas.create_text(data.width/2, 20, text=suitName, font="Arial 28 bold")
####################################
# use the run function as-is
####################################
def run(width=300, height=300):
    def redrawAllWrapper(canvas, data):
        canvas.delete(ALL)
        canvas.create_rectangle(0, 0, data.width, data.height,
                                fill='white', width=0)
        redrawAll(canvas, data)
        canvas.update()
    def mousePressedWrapper(event, canvas, data):
        mousePressed(event, data)
        redrawAllWrapper(canvas, data)
    def keyPressedWrapper(event, canvas, data):
        keyPressed(event, data)
        redrawAllWrapper(canvas, data)
    def timerFiredWrapper(canvas, data):
        timerFired(data)
        redrawAllWrapper(canvas, data)
        # pause, then call timerFired again
        canvas.after(data.timerDelay, timerFiredWrapper, canvas, data)
    # Set up data and call init
    class Struct(object): pass
    data = Struct()
    data.width = width
    data.height = height
    data.timerDelay = 250 # milliseconds
    # Create root before calling init (so we can create images in init)
    root = Tk()
    init(data)
    # create the root and the canvas
    canvas = Canvas(root, width=data.width, height=data.height)
    canvas.pack()
    # set up events
    root.bind("<Button-1>", lambda event:
                            mousePressedWrapper(event, canvas, data))
    root.bind("<Key>", lambda event:
                            keyPressedWrapper(event, canvas, data))
    timerFiredWrapper(canvas, data)
    # and launch the app
    root.mainloop()  # blocks until window is closed
    print("bye!")
run(420, 360)
Result:

 
- Side Scroller Demo
# Side Scroller Demo
from tkinter import *
def init(data):
    data.scrollX = 0  # amount view is scrolled to the right
    data.scrollMargin = 50 # closest player may come to either canvas edge
    data.playerX = data.scrollMargin # player's left edge
    data.playerY = 0  # player's bottom edge (distance above the base line)
    data.playerWidth = 10
    data.playerHeight = 20
    data.walls = 5
    data.wallPoints = [0]*data.walls
    data.wallWidth = 20
    data.wallHeight = 40
    data.wallSpacing = 90 # wall left edges are at 90, 180, 270,...
    data.currentWallHit = -1 # start out not hitting a wall
def getPlayerBounds(data):
    # returns absolute bounds, not taking scrollX into account
    (x0, y1) = (data.playerX, data.height/2 - data.playerY)
    (x1, y0) = (x0 + data.playerWidth, y1 - data.playerHeight)
    return (x0, y0, x1, y1)
def getWallBounds(wall, data):
    # returns absolute bounds, not taking scrollX into account
    (x0, y1) = ((1+wall) * data.wallSpacing, data.height/2)
    (x1, y0) = (x0 + data.wallWidth, y1 - data.wallHeight)
    return (x0, y0, x1, y1)
def getWallHit(data):
    # return wall that player is currently hitting
    # note: this should be optimized to only check the walls that are visible
    # or even just directly compute the wall without a loop
    playerBounds = getPlayerBounds(data)
    for wall in range(data.walls):
        wallBounds = getWallBounds(wall, data)
        if (boundsIntersect(playerBounds, wallBounds) == True):
            return wall
    return -1
def boundsIntersect(boundsA, boundsB):
    # return l2<=r1 and t2<=b1 and l1<=r2 and t1<=b2
    (ax0, ay0, ax1, ay1) = boundsA
    (bx0, by0, bx1, by1) = boundsB
    return ((ax1 >= bx0) and (bx1 >= ax0) and
            (ay1 >= by0) and (by1 >= ay0))
def movePlayer(dx, dy, data):
    data.playerX += dx
    data.playerY += dy
    # scroll to make player visible as needed
    if (data.playerX < data.scrollX + data.scrollMargin):
        data.scrollX = data.playerX - data.scrollMargin
    if (data.playerX > data.scrollX + data.width - data.scrollMargin):
        data.scrollX = data.playerX - data.width + data.scrollMargin
    # and check for a new wall hit
    wall = getWallHit(data)
    if (wall != data.currentWallHit):
        data.currentWallHit = wall
        if (wall >= 0):
            data.wallPoints[wall] += 1
def mousePressed(event, data):
    pass
def keyPressed(event, data):
    if (event.keysym == "Left"):    movePlayer(-5, 0, data)
    elif (event.keysym == "Right"): movePlayer(+5, 0, data)
    elif (event.keysym == "Up"):    movePlayer(0, +5, data)
    elif (event.keysym == "Down"):  movePlayer(0, -5, data)
def timerFired(data):
    pass
def redrawAll(canvas, data):
    # draw the base line
    lineY = data.height/2
    lineHeight = 5
    canvas.create_rectangle(0, lineY, data.width, lineY+lineHeight,fill="black")
    # draw the walls
    # (Note: should optimize to only consider walls that can be visible now!)
    sx = data.scrollX
    for wall in range(data.walls):
        (x0, y0, x1, y1) = getWallBounds(wall, data)
        fill = "orange" if (wall == data.currentWallHit) else "pink"
        canvas.create_rectangle(x0-sx, y0, x1-sx, y1, fill=fill)
        (cx, cy) = ((x0+x1)/2 - sx, (y0 + y1)/2)
        canvas.create_text(cx, cy, text=str(data.wallPoints[wall]))
        cy = lineY + 5
        canvas.create_text(cx, cy, text=str(wall), anchor=N)
    # draw the player
    (x0, y0, x1, y1) = getPlayerBounds(data)
    canvas.create_oval(x0 - sx, y0, x1 - sx, y1, fill="cyan")
    # draw the instructions
    msg = "Use arrows to move, hit walls to score"
    canvas.create_text(data.width/2, 20, text=msg)
####################################
# use the run function as-is
####################################
def run(width=300, height=300):
    def redrawAllWrapper(canvas, data):
        canvas.delete(ALL)
        canvas.create_rectangle(0, 0, data.width, data.height,
                                fill='white', width=0)
        redrawAll(canvas, data)
        canvas.update()
    def mousePressedWrapper(event, canvas, data):
        mousePressed(event, data)
        redrawAllWrapper(canvas, data)
    def keyPressedWrapper(event, canvas, data):
        keyPressed(event, data)
        redrawAllWrapper(canvas, data)
    def timerFiredWrapper(canvas, data):
        timerFired(data)
        redrawAllWrapper(canvas, data)
        # pause, then call timerFired again
        canvas.after(data.timerDelay, timerFiredWrapper, canvas, data)
    # Set up data and call init
    class Struct(object): pass
    data = Struct()
    data.width = width
    data.height = height
    data.timerDelay = 100 # milliseconds
    root = Tk()
    init(data)
    # create the root and the canvas
    canvas = Canvas(root, width=data.width, height=data.height)
    canvas.pack()
    # set up events
    root.bind("<Button-1>", lambda event:
                            mousePressedWrapper(event, canvas, data))
    root.bind("<Key>", lambda event:
                            keyPressedWrapper(event, canvas, data))
    timerFiredWrapper(canvas, data)
    # and launch the app
    root.mainloop()  # blocks until window is closed
    print("bye!")
run(300, 300)
Result:

 
- Snake Demo
# Snake
from tkinter import *
import random
def init(data):
    data.rows = 10
    data.cols = 10
    data.margin = 5 # margin around grid
    data.snake = [(data.rows/2, data.cols/2)]
    data.direction = (0, +1) # (drow, dcol)
    placeFood(data)
    data.timerDelay = 250
    data.gameOver = False
    data.paused = True
# getCellBounds from grid-demo.py
def getCellBounds(row, col, data):
    # aka "modelToView"
    # returns (x0, y0, x1, y1) corners/bounding box of given cell in grid
    gridWidth  = data.width - 2*data.margin
    gridHeight = data.height - 2*data.margin
    x0 = data.margin + gridWidth * col / data.cols
    x1 = data.margin + gridWidth * (col+1) / data.cols
    y0 = data.margin + gridHeight * row / data.rows
    y1 = data.margin + gridHeight * (row+1) / data.rows
    return (x0, y0, x1, y1)
def mousePressed(event, data):
    data.paused = False
def keyPressed(event, data):
    if (event.keysym == "p"): data.paused = True; return
    elif (event.keysym == "r"): init(data); return
    if (data.paused or data.gameOver): return
    if (event.keysym == "Up"):      data.direction = (-1, 0)
    elif (event.keysym == "Down"):  data.direction = (+1, 0)
    elif (event.keysym == "Left"):  data.direction = (0, -1)
    elif (event.keysym == "Right"): data.direction = (0, +1)
    # for debugging, take one step on any keypress
    takeStep(data)
def timerFired(data):
    if (data.paused or data.gameOver): return
    takeStep(data)
def takeStep(data):
    (drow, dcol) = data.direction
    (headRow, headCol) = data.snake[0]
    (newRow, newCol) = (headRow+drow, headCol+dcol)
    if ((newRow < 0) or (newRow >= data.rows) or
        (newCol < 0) or (newCol >= data.cols) or
        ((newRow, newCol) in data.snake)):
        data.gameOver = True
    else:
        data.snake.insert(0, (newRow, newCol))
        if (data.foodPosition == (newRow, newCol)):
            placeFood(data)
        else:
            # didn't eat, so remove old tail (slither forward)
            data.snake.pop()
def placeFood(data):
    data.foodPosition = None
    row0 = random.randint(0, data.rows-1)
    col0 = random.randint(0, data.cols-1)
    for drow in range(data.rows):
        for dcol in range(data.cols):
            row = (row0 + drow) % data.rows
            col = (col0 + dcol) % data.cols
            if (row,col) not in data.snake:
                data.foodPosition = (row, col)
                return
def drawBoard(canvas, data):
    for row in range(data.rows):
        for col in range(data.cols):
            (x0, y0, x1, y1) = getCellBounds(row, col, data)
            canvas.create_rectangle(x0, y0, x1, y1, fill="white")
def drawSnake(canvas, data):
    for (row, col) in data.snake:
        (x0, y0, x1, y1) = getCellBounds(row, col, data)
        canvas.create_oval(x0, y0, x1, y1, fill="blue")
def drawFood(canvas, data):
    if (data.foodPosition != None):
        (row, col) = data.foodPosition
        (x0, y0, x1, y1) = getCellBounds(row, col, data)
        canvas.create_oval(x0, y0, x1, y1, fill="green")
def drawGameOver(canvas, data):
    if (data.gameOver):
        canvas.create_text(data.width/2, data.height/2, text="Game over!",
                           font="Arial 26 bold")
def redrawAll(canvas, data):
    drawBoard(canvas, data)
    drawSnake(canvas, data)
    drawFood(canvas, data)
    drawGameOver(canvas, data)
####################################
# use the run function as-is
####################################
def run(width=300, height=300):
    def redrawAllWrapper(canvas, data):
        canvas.delete(ALL)
        canvas.create_rectangle(0, 0, data.width, data.height,
                                fill='white', width=0)
        redrawAll(canvas, data)
        canvas.update()
    def mousePressedWrapper(event, canvas, data):
        mousePressed(event, data)
        redrawAllWrapper(canvas, data)
    def keyPressedWrapper(event, canvas, data):
        keyPressed(event, data)
        redrawAllWrapper(canvas, data)
    def timerFiredWrapper(canvas, data):
        timerFired(data)
        redrawAllWrapper(canvas, data)
        # pause, then call timerFired again
        canvas.after(data.timerDelay, timerFiredWrapper, canvas, data)
    # Set up data and call init
    class Struct(object): pass
    data = Struct()
    data.width = width
    data.height = height
    data.timerDelay = 100 # milliseconds
    root = Tk()
    init(data)
    # create the root and the canvas
    canvas = Canvas(root, width=data.width, height=data.height)
    canvas.pack()
    # set up events
    root.bind("<Button-1>", lambda event:
                            mousePressedWrapper(event, canvas, data))
    root.bind("<Key>", lambda event:
                            keyPressedWrapper(event, canvas, data))
    timerFiredWrapper(canvas, data)
    # and launch the app
    root.mainloop()  # blocks until window is closed
    print("bye!")
run(300, 300)
Result:
