Show a Dice Roll (Python and Tk)

vegaseat 0 Tallied Votes 4K Views Share

This snippet shows you how to animate a dice roll with the Tkinter GUI, useful for many games that require a dice to give a random number from 1 to 6. An interesting use of Tkinter's grid(), grid_forget() and after() functions.

# select a dice image at random using Tkinter
# tested with Python24     vegaseat      23dec2006

import Tkinter as tk  # use tk namespace for Tkinter items
import random

def create_dice():
    """
    create the dice canvas list as dice[0] to dice[6]
    """
    dice = []
    dice.append(draw_dice('dot0'))   # empty
    dice.append(draw_dice('dot5'))   # center dot --> 1
    dice.append(draw_dice('dot4', 'dot6'))
    dice.append(draw_dice('dot3', 'dot5', 'dot7'))
    dice.append(draw_dice('dot1', 'dot3', 'dot7', 'dot9'))
    dice.append(draw_dice('dot1', 'dot3', 'dot5', 'dot7', 'dot9'))
    dice.append(draw_dice('dot1', 'dot3', 'dot4', 'dot6', 'dot7', 'dot9'))
    return dice

def draw_dice(*arg):
    """
    draws the 7 different dice dots on the canvas
    """
    w = 20
    h = 20
    c = tk.Canvas(root, width=w+3, height=h+3, bg='yellow')
    # set the dot specs
    x = 2
    y = 2
    r = 5
    if 'dot1' in arg:
        dot1 = c.create_oval(x, y, x+r, y+r, fill='black')
    x = w/2
    x = 18
    if 'dot3' in arg:
        dot3 = c.create_oval(x, y, x+r, y+r, fill='black')
    x = 2
    y = h/2
    if 'dot4' in arg:
        dot4 = c.create_oval(x, y, x+r, y+r, fill='black')
    x = w/2
    if 'dot5' in arg:
        dot5 = c.create_oval(x, y, x+r, y+r, fill='black')
    x = 18
    if 'dot6' in arg:
        dot6 = c.create_oval(x, y, x+r, y+r, fill='black')
    x = 2
    y = 18
    if 'dot7' in arg:
        dot7 = c.create_oval(x, y, x+r, y+r, fill='black')
    x = w/2
    x = 18
    if 'dot9' in arg:
        dot9 = c.create_oval(x, y, x+r, y+r, fill='black')
    if 'dot0' in arg:
        pass
    return c

def click():
    """
    display a randomly selected dice value
    """
    # start with a time delay of 100 ms and increase it as the dice rolls
    t = 100
    stop = random.randint(13, 18)
    for x in range(stop):
        dice_index = x%6 + 1
        root.title(str(dice_index))  # test
        dice_list[dice_index].grid(row=1, column=0, pady=5)
        root.update()
        if x == stop-1:
            # final result available via var1.get()
            var1.set(str(x%6 + 1))
            break
        root.after(t, dice_list[dice_index].grid_forget())
        t += 25
    

# create the window form
root = tk.Tk()

# StringVar() updates result label automatically
var1 = tk.StringVar()
# set initial value
var1.set("")
# create the result label
result = tk.Label(root, textvariable=var1, fg='red')
result.grid(row=3, column=0, columnspan=3)

dice_list = create_dice()
# start with an empty canvas
dice_list[0].grid(row=1, column=0, pady=5)

button1 = tk.Button(root, text="Press me", command=click)
button1.grid(row=2, column=0, pady=3)

# start of program event loop
root.mainloop()

Dani AI

Generated

Building on vegaseat’s use of after() for animation, you can draw the die directly on a Canvas at any (x, y) and then move counters by the rolled amount. This avoids widget layout concerns and keeps the UI responsive. Note: in modern Python 3 the module is tkinter (not Tkinter), and you should never block the event loop with time.sleep() during animations.

import tkinter as tk, random

root = tk.Tk()
c = tk.Canvas(root, width=420, height=220, bg="white"); c.pack()

# Draw a die at (x,y) with size 's'; return the tag used so it can be updated.
def draw_die(x, y, val, s=64, tag="die"):
    c.delete(tag)
    h = s/2
    c.create_rectangle(x-h, y-h, x+h, y+h, fill="white", outline="black", width=2, tags=tag)
    spots = {
        1:[(0,0)], 2:[(-.3,-.3),(.3,.3)], 3:[(-.3,-.3),(0,0),(.3,.3)],
        4:[(-.3,-.3),(.3,-.3),(-.3,.3),(.3,.3)],
        5:[(-.3,-.3),(.3,-.3),(0,0),(-.3,.3),(.3,.3)],
        6:[(-.3,-.3),(.3,-.3),(-.3,0),(.3,0),(-.3,.3),(.3,.3)]
    }
    r = s*0.09
    for ox, oy in spots[val]:
        cx, cy = x + ox*s, y + oy*s
        c.create_oval(cx-r, cy-r, cx+r, cy+r, fill="black", outline="", tags=tag)
    return tag

# Board path and a counter
path = [(20 + i*20, 180) for i in range(18)] + [(380, 180 - i*20) for i in range(8)]
counter = c.create_oval(0,0,0,0, fill="tomato")
pos = 0

def place_counter():
    x,y = path[pos]
    c.coords(counter, x-8, y-8, x+8, y+8)

def move_counter(steps):
    def step(k=steps):
        nonlocal steps
        if k == 0: return
        globals()["pos"] = (globals()["pos"] + 1) % len(path)
        place_counter()
        c.after(120, step, k-1)
    step()

def roll():
    # Quick roll animation, then move
    def spin(n=10):
        v = random.randint(1,6)
        draw_die(80, 70, v)
        if n: c.after(60, spin, n-1)
        else: move_counter(v)
    spin()

place_counter()
tk.Button(root, text="Roll", command=roll).pack()
root.mainloop()

: save as dice_tk.py and run with Python 3. : to position the die elsewhere, change the (80, 70) in draw_die(...). If you want to reuse vegaseat’s grid-based die widget instead of drawing on the canvas, embed it at any coordinate with Canvas.create_window(x, y, window=your_die_frame), and use after() for the roll animation so the UI does not freeze.

WoBinator 0 Newbie Poster

how do i run this? i have python...im not noob:rolleyes:

vegaseat 1,735 DaniWeb's Hypocrite Team Colleague

You copy and paste the code into your editor and save it as a Python file, for instance

If you have Windows, double click on the file name. If your editor allows it, run the file from within the editor. Editors like IDLE, DrPython or PyPE allow you to do this.

Jake_6 0 Newbie Poster

how would i place this in a different spot on a canvas? and use it to move counters by the amount set? is this possible?

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.