JustToThePoint English Website Version
JustToThePoint en español
JustToThePoint in Thai

Python - GUI Programming (Tkinter)

Tkinter is Python’s standard GUI (Graphical User Interface) package. It has the advantage of being included with the Python standard library. It is easy-to-use, lightweight, and cross-platform.

# A countdown timer using Python. t is the length of the countdown in seconds. After that, a countdown will begin on the screen of the format ‘minutes:seconds’. 
# Use: python alarm.py t.
import sys, time, beepy 

beepy is a Python language module that allows users to easily play notification sounds on Linux, OSX and Windows. Installation: pip install beepy.

from tkinter import * # We need to import the Python GUI Tkinter module
import os

def countdown(t):    
    mins, secs = divmod(t, 60) # It calculates the number of minutes and seconds.
    timer = '{:02d}:{:02d}'.format(mins, secs) # It shows the minutes and seconds using the variable timer.
    clock['text'] = timer # It changes the Label _text_ property to change/update the Label text.
    
    if t>0: # Is there any time left?
    	clock.after(1000, countdown, t-1) # All widgets have the after() method with the following syntax: after(delay, callback). This method calls the callback function (e.g., countdown) once after a delay in milliseconds (1000 ms = 1 s) has taken place.
    else:
    	beepy.beep(sound="ping") # It plays a beep sound.
    	close()
    	
def close(): # Quit the application.
    root.destroy() # It terminates the mainloop process and destroys all the widgets inside the window.

if __name__ == '__main__':               
    root=Tk() # It sets up the main application window and assign it to the variable root.
    root.title("Countdown") # It gives our main application window the title "Countdown".

    labelfont = ('times', 20, 'bold') # It defines the font's family, size, and style.
    clock = Label(root, text='Hello world') # After creating a window, we can add a widget. Widgets are objects, instances of classes that represent buttons, labels, frames, and so on. We create a Label widget with the text 'Hello world'.
    clock.config(font=labelfont) # It allows us to specify the font used to display the text.
    clock.config(bg='black', fg='yellow') # The attribute fg can be used to have the text in another color (e.g., black) and the attribute bg can be used to change the background color of the label.
    clock.config(height=3, width=20) # It defines the Label's width and height.
    clock.pack(expand=YES, fill=BOTH) # It adds the Label widget to the windows. Tkinter has three Layout Managers to position widgets in a window: pack, place, and grid. The pack() fill option is used to make a widget fill (BOTH, horizontally and vertically) the entire frame. the pack() expand option is used for assigning additional space to the widget container.

    countdown(int(sys.argv[1])) # sys.argv is a list in Python, which contains the command-line arguments passed to the script. Use: _python alarm.py t_. Therefore, sys.argv[0] = "alarm.py" and sys.argv[1] = t.

    root.mainloop() # It tells Tk to enter its event loop, which is necessary for everything to appear on the screen and allow users to interact with our program.

Countdown with Tkinter

Object Oriented Programming with Tkinter

Let’s build upon the last example and improve it using the power of Object Oriented Programming.

import sys, time, beepy
from tkinter import *
from tkinter import ttk
import os

class App(Tk): # It defines an App class that inherits from the Tk class
    def __init__(self):
        super().__init__()

        self.title("Countdown to Launch") # It gives our main application window the title "Countdown to Launch".

        mainframe = ttk.Frame(self, padding="3 3 12 12") # It creates a frame widget, which will hold the contents of our user interface.
        mainframe.grid(column=0, row=0, sticky=(N, W, E, S)) # It places it directly inside our main window.
        self.columnconfigure(0, weight=1) # It tells Tk that the frame should expand to fill any extra space if the window is resized.
        self.rowconfigure(0, weight=1)

        Label(mainframe, text="Coundown!").grid(column=0, row=0, sticky=E) # It creates a label inside the frame widget (parent = mainframe) and places it in the appropriate column (0) and row (0)

        self.countdownTime = StringVar()

This is the textvariable for the Entry countdownTime_entry, which means that anytime the entry changes, Tk will automatically update the variable countdownTime

        self.initialValue = IntVar() # This is the countdown's initial value
        self.active = BooleanVar(False) # This is a status variable. It indicates whether the countdown is active or not (it has been paused or it has not started yet).
        self.t = IntVar() # This is the countdown's remaining time
        countdownTime_entry = ttk.Entry(mainframe, width=7, textvariable=self.countdownTime) # The purpose of an Entry widget is to allow the user to enter or edit a single line of text. It is inside the frame widget.
        countdownTime_entry.grid(column=1, row=0, sticky=(W, E)) # It places it in the appropriate column (1) and row (0)

        labelfont = ('times', 20, 'bold') # It defines the font's family, size, and style.
        self.clock = Label(self, text='') # It creates a Label widget.
        self.clock.config(font=labelfont) # It allows us to specify the font used to display the text.
        self.clock.config(bg='black', fg='yellow') # The attribute fg can be used to have the text in another color (e.g., black) and the attribute bg can be used to change the background color of the label.
        self.clock.grid(column=0, row=2, sticky=(W, E))

        style = ttk.Style() # We are going to create a new style to change the appearance of our buttons.
        style.theme_use("clam") # It sets the current theme to clam.
        style.map("C.TButton", foreground=[('disabled', 'grey'),('pressed', 'red'), ('active', 'blue')],
              background=[('pressed', '!disabled', 'black'), ('active', 'white')]) # The style.map() method is used to dynamically change the appearance of a widget based on its specific state.

        self.stopButton = ttk.Button(mainframe, text="Pause", command=self.stop, style="C.TButton")

It creates a ttk.Button widget inside the frame widget (parent = mainframe). command=self.stop, self.stop is the function to be called when the button is pressed. style=“C.TButton” is the style to be used in rendering this button.

        self.stopButton.grid(column=0, row=2, sticky=W, padx=3, pady=3)
        self.stopButton["state"] = "disabled" # Initially, the stopButton is disabled. 
        ttk.Button(mainframe, text="Start", command=self.start, style="C.TButton").grid(column=2, row=2, sticky=W, padx=3, pady=3)
        ttk.Button(mainframe, text="Reset", command=self.reset, style="C.TButton").grid(column=1, row=2, sticky=W, padx=3, pady=3)

Countdown with Tkinter

    def stop(self):
        if self.active.get(): # If the user has pressed the stop button (pause) and the countdown is active
            self.active.set(False) # The countdown is now inactive
            self.stopButton['text'] = "Resume" 
        else:  # If the user has pressed the stop button (resume) 
            self.active.set(True) # The countdown is now active
            self.countdown() # It resumes the countdown
            self.stopButton['text'] = "Pause"
            
    def reset(self): # If the user has pressed the reset button, the countdown's remaining time is set to the countdown's initial value
        self.t.set(self.initialValue.get())

    def start(self): # If the user has pressed the start button
        if not self.active.get(): # and the countdown is inactive
            self.active.set(True)
            try:
                self.t.set(int(self.countdownTime.get())) # It initializes the countdown's remaining time
            except ValueError:
                self.t.set(10) # If the user has not entered any number, it initializes the countdown's remaining time to 10 

            self.initialValue.set(self.t.get())
            self.stopButton["state"] = "enable" # It enables the stop button
            self.countdown() # It starts the countdown  

    def countdown(self):
        if self.active.get(): # If the countdown is still active...
            mins, secs = divmod(self.t.get(), 60) # It calculates the number of minutes and seconds.
            timer = '{:02d}:{:02d}'.format(mins, secs) # It shows the minutes and seconds using the variable timer.
            self.clock['text'] = timer # It changes the Label _text_ property to change/update the Label text.

            if self.t.get()>0: # If there's still some remaining time ...
                self.t.set(self.t.get()-1)
                self.clock.after(1000, self.countdown) # All widgets have the after() method with the following syntax: after(delay, callback). This method calls the callback function (e.g., countdown) once after a delay in milliseconds (1000 ms = 1 s) has taken place.
            else: # The countdown is over
                beepy.beep(sound="ping") # It plays a beep sound
                self.close()

    def close(self):
        self.destroy()

if __name__ == '__main__':
    app = App() # It creates a new instance of the App class
    app.mainloop() # It tells Tk to enter its event loop, which is necessary for everything to appear on the screen and allow users to interact with our program. 

A random generator app

import json, random
import tkinter as tk
from tkinter.constants import E, N, W, EW
import requests
from PIL import Image, ImageTk
from colorutils import random_web # A library which provides utilities for working with colors in Python.
# A standard 52-card deck comprises 13 ranks in each of the four French suits: clubs (♣), diamonds (♦), hearts (♥) and spades (♠).
suits = ['\u2666', '\u2665', '\u2663', '\u2660']
ranks = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"]

class My_Random:
    def __init__(self):
        self.window = tk.Tk() 
        self.window.title("Random Generator")
        self.window.geometry('650x300')
     
        self.text1 = tk.Text(self.window, height=7)
        self.text1.grid(row=0, columnspan=4, rowspan=2, padx=25, pady=5)

The Tkinter Grid geometry manager puts the widgets in a 2-dimensional table. The master widget is split into a number of rows and columns. The intersection of a row and a column is called a cell.

A cell is an area where we will place one and only one widget. To place multiple widgets in a cell, we can use a Frame. We use padx and pady for padding between cells. The first row of the grid is our Text widget. columnspan/rowspan indicate how many columns/rows are occupied by the cell or the cell “spans”. The text widget will take up two rows and four columns.

        self.text1.tag_configure('color',
                    foreground='red',
                    font=('Tempus Sans ITC', 12, 'bold'))
        # We create a tag to change the font and color of the text. Later on, we will apply the tag: self.text1.insert('1.0', random_fact, 'color').
        load = Image.open("head.jpg")
        load = load.resize((100, 100))
        self.heads = ImageTk.PhotoImage(load)
        # ImageTk.PhotoImage returns a Tkinter-compatible photo image. 
        load = Image.open("tail.jpg")
        load = load.resize((100, 100))
        self.tails = ImageTk.PhotoImage(load)
    
        self.lb1 = tk.Label(self.window, image=self.heads)
        # A label is a Tkinter widget which is used to display text or an image (image=self.heads). Previously, we have created a Tkinter-compatible photo image.
        self.lb1.grid(row=2, column=0, sticky=W, padx=7, pady=7)
        # A Tkinter label widget lb1 is placed in the third row and first column, and it will contain the coin after being tossed.
        self.var = tk.StringVar()
        # StringVar is a Tkinter constructor used to create a Tkinter string variable.
        self.var2 = tk.StringVar()
        self.var3 = tk.StringVar()
        self.lb2 = tk.Label(self.window, textvariable=self.var, relief=tk.RAISED, font=("Arial", 20))
        # We associate the StringVar variable (self.var) to the Tkinter label (self.lb2). After that, Tkinter will update this label whenever the variable is modified.
        self.lb2.grid(row=2, column=1, sticky=W, padx=7, pady=7)
        # A Tkinter label widget lb2 is placed in the third row and second column, and it will contain the dice after being rolled.
        self.lb3 = tk.Label(self.window, textvariable=self.var2, relief=tk.RAISED, font=("Arial", 50))
        self.lb3.grid(row=2, column=2, sticky=W, padx=7, pady=7)
        # A Tkinter label widget lb3 is placed in the third row and third column, and it will contain the random card.
        self.lb4 = tk.Label(self.window, textvariable=self.var3, relief=tk.RAISED, font=("Arial", 30))
        self.lb4.grid(row=2, column=3, sticky=W, padx=7, pady=7)
        # A Tkinter label widget lb3 is placed in the third row and fourth column, and it will contain the random color.
        self.btn = tk.Button(self.window, text='Generate Random', command=self.my_random)
        # command indicates the method to be called when the button is clicked.
        self.btn.grid(row=3, column=1, sticky=EW, columnspan=2)
        # A Tkinter button widget btn is placed in the fourth row and second column, and it will take up two columns.
        self.window.mainloop()

    def my_random(self):
        self.get_fun_fact()
        self.head_tails()
        self.random_card()
        self.roll_dice()
        self.random_color()

    # Generates a random color.
    def random_color(self):
        # It generates a random color.
        myColor = random_web() 
        # We call the set method of the StringVar "var3" to set its value. Tkinter will automatically update the label widget lb4. 
        self.var3.set(myColor)
        # We change the label's foreground color.
        self.lb4.config(foreground=myColor)

    # Generates a random card.
    def random_card(self):
        # We call the set method of the StringVar "var2" to set its value. Tkinter will automatically update the label widget lb3. 
        self.var2.set(random.choice(ranks) + random.choice(suits))

    # Toss or flip a coin.
    def head_tails(self):
        coin_flip = ['heads','tails'] 
        if random.choice(coin_flip)=='heads':
            # To dynamically update the Label widget, we can use the config method.
            self.lb1.config(image=self.heads)
        else:
            self.lb1.config(image=self.tails)
 
    # Get a random fact.
    def get_fun_fact(self):
        url ="https://uselessfacts.jsph.pl/random.json?language=en"
        response = requests.request("GET", url)
        # We send a GET request to url. response.text is a JSON object.
        data = json.loads(response.text)
        # json.loads() method is used to parse a valid JSON string and convert it into a Python Dictionary.
{
    "id": "a3c47eef-d46b-4807-b055-bf740fe0f39d", 
    "text": "There's a systematic lull in conversation every 7 minutes.", 
    "source": "djtech.net", 
    "source_url": "http://www.djtech.net/humor/useless_facts.htm", 
    "language": "en", 
    "permalink": "https://uselessfacts.jsph.pl/
a3c47eef-d46b-4807-b055-bf740fe0f39d"
}
        random_fact = data['text'] # We are only interested in "text".
        self.text1.insert('0.0', random_fact, 'color')
        # It inserts the string "random_fact" in the position 'row.column' and applies the tag 'color'.
    
    # Roll a dice.
    def roll_dice(self):
        number = random.randint(1, 6)
        if number == 1:
            myNumber = """[‐‐‐‐‐‐] 
            [‐‐0‐‐] 
            [‐‐‐‐‐‐]"""
        elif number == 2:
            myNumber = """[-0‐‐-] 
            [‐‐‐‐‐‐] 
            [‐‐-0-]"""
        elif number == 3:
            myNumber = """[‐‐‐‐‐‐] 
            [0-0-0] 
            [‐‐‐‐‐‐]"""
        elif number == 4:
            myNumber = """[0‐‐-0] 
            [‐‐‐‐‐‐] 
            [0‐‐-0]"""
        elif number == 5:
            myNumber = """[0‐‐-0] 
            [‐‐0‐‐] 
            [0‐‐-0]"""
        else:
            myNumber = """[0-0-0] 
            [‐‐‐‐‐‐] 
            [0-0-0]"""

        self.var.set(myNumber)
def main():
    mr = My_Random()

if __name__ == "__main__":
    main()


Bitcoin donation

JustToThePoint Copyright © 2011 - 2022 PhD. Máximo Núñez Alarcón, Anawim. ALL RIGHTS RESERVED. Bilingual e-books, articles, and videos to help your child and your entire family succeed, develop a healthy lifestyle, and have a lot of fun.

This website uses cookies to improve your navigation experience.
By continuing, you are consenting to our use of cookies, in accordance with our Cookies Policy and Website Terms and Conditions of use.