0

I want to make a simple clicker game and one of my upgrades needs to be constantly running in background. I tried while True: loop but it just doesn't work and python crushes. What's wrong?

from time import sleep
import tkinter as tk


window=tk.Tk()
window.columnconfigure([0,4], minsize=60,)

def click():
    points["text"]=1+int(points["text"])

def up3():
    if int(points["text"])>=int(cost3["text"]):
        lvl3["text"]=int(lvl3["text"])+1
        points["text"]=int(points["text"])-int(cost3["text"])
        cost3["text"]=int(int(cost3["text"])*2)
        while True:
            points["text"]=int(points["text"])+100*int(lvl3["text"])
            sleep(1)

# points and click button
points=tk.Label(text="10000000")
points.grid(row=0,column=0,columnspan=5,sticky="WE")
tk.Button(text="click!",command=click).grid(row=1,column=0,columnspan=5,sticky="WE")

# upgrade 3
tk.Button(text="passive points",command=up3).grid(row=5,sticky="WE",column=0)
tk.Label(text="+100/s").grid(row=5,column=1)
cost3=tk.Label(text="10000")
cost3.grid(row=5,column=3)
lvl3=tk.Label(text="1")
lvl3.grid(row=5,column=4)

window.mainloop()
martineau
  • 119,623
  • 25
  • 170
  • 301
  • 2
    You can't put an infinite loop in a single-threaded program; the code execution will never be able to switch back to handling buttons and such. You should look up [how to run a function in the background of tkinter](https://stackoverflow.com/questions/5048082/how-to-run-a-function-in-the-background-of-tkinter). – Random Davis Apr 27 '22 at 18:17
  • Welcome to Stackoveflow, please add the error code your getting. – Faraaz Kurawle Apr 27 '22 at 18:18
  • put `root.update()` inside the while loop. – Faraaz Kurawle Apr 27 '22 at 18:18
  • also its not recommed to use `time.sleep` in GUIs as it frezzes the python interpreter. use `root.after` instead.\ – Faraaz Kurawle Apr 27 '22 at 18:21
  • 1
    @FaraazKurawle: It's much better if you make the body of the loop a function that ends with a `root.after` call to schedule the function again, so you're not effectively *replacing* the event loop, rather than working with it. – ShadowRanger Apr 27 '22 at 18:22

1 Answers1

3

Here's how to use the universal widget method after() to effectively perform a while True: infinite loop that won't interfere with the execution of the rest of your tkinter app.

I've added a function called updater() which performs the actions you want and then reschedules another call to itself after a 1000 milliseconds (i.e. 1 second) delay.

This is what some of the answers to the linked question in @Random Davis's comment suggest doing, as do I.

import tkinter as tk


window=tk.Tk()
window.columnconfigure([0,4], minsize=60,)

def click():
    points["text"] = int(points["text"]) + 1

def up3():
    if int(points["text"]) >= int(cost3["text"]):
        lvl3["text"] = int(lvl3["text"]) + 1
        points["text"] = int(points["text"]) - int(cost3["text"])
        cost3["text"] = int(int(cost3["text"]) * 2)
        updater()  # Start update loop.

def updater():
    points["text"] = int(points["text"]) + 100*int(lvl3["text"])
    window.after(1000, updater)  # Call again in 1 sec.

# points and click button
points = tk.Label(text="10000000")
points.grid(row=0, column=0, columnspan=5, sticky="WE")
tk.Button(text="click!",command=click).grid(row=1, column=0, columnspan=5, sticky="WE")

# upgrade 3
tk.Button(text="passive points", command=up3).grid(row=5, sticky="WE", column=0)
tk.Label(text="+100/s").grid(row=5, column=1)
cost3 = tk.Label(text="10000")
cost3.grid(row=5, column=3)
lvl3 = tk.Label(text="1")
lvl3.grid(row=5, column=4)

window.mainloop()
martineau
  • 119,623
  • 25
  • 170
  • 301