Python Tkinter countdown app with a freezing GUI So I want to create a very basic constantly changing text bvariable with a counter function. I created it, and it is working well, but once I press the "countdown" button, the GUI freezes, and the only way to close the window is from PyCharm.

Any suggestons about what causes the freeze and how to fix it?

The program unfreezes when the countdown is finished, so it's just a hanging interface, not a crash. Written for Python 3.8.

from tkinter import *


class MyFirstGUI:
    def __init__(self, master):
        self.master = master
        master.title("A simple GUI")

        self.label = Label(master, textvariable=time)
        self.label.pack()

        self.greet_button = Button(master, text="Greet", command=self.greet)
        self.greet_button.pack()

        self.close_button = Button(master, text="Countdown", command=self.countdown)
        self.close_button.pack()

    def greet(self):
        print("Greetings!")

    def countdown(self):
        remaining = 100
        for i in range(100):
            time.set(remaining)
            remaining -= 1
            self.master.after(1000)
            self.master.update_idletasks()

root = Tk()
time = StringVar()

my_gui = MyFirstGUI(root)

root.mainloop()
Tkinter is already in a loop. The window handler gets all messed up because of your own loop, which is called synchronously. Effectively you're running a loop in a loop and wonder why the outer loop takes so long to get to the next step. That's because of the inner loop taking so much time.

Long story short, your next window update will only commence once the countdown loop is finished.

There's mainly 2 ways of fixing that.


Don't loop.
Use asynchronous (or threaded) loops instead.


Basically, you'll need a function start_countdown which starts a threaded version of countdown. That would probably look something like this:

import threading


class MyFirstGUI:
    # Init and rest of methods snipped for brevity
    def refresh(self):
        self.root.update()
        self.root.after(1000,self.refresh)

    def start_countdown(self):
        self.refresh()
        threading.Thread(target=countdown).start()


Now, the countdown will be calculated in a thread that doesn't halt the current loop (the Tkinter main window). With that as basis, see if you can rebuild your countdown method. You'll see it becomes more focussed on the actual calculations and less on the screen.

Please see this answer on Stack Overflow for the complete story on how to work with Tkinter and calls that would ordinarily block.