pythonrecursionpython-multithreadingcustomtkinterrecursionerror

i dont know what caused this Recursion Error


I am making a customtkinter game that is basically Cookie Clicker but themed around beans. and by suggestions from another thread I added Threading, which I'm having a hard time implementing, and multiple classes, which I don't understand how to make the data pass into my main class. regardless these attempts has gave me a recursion error

This is the entire program so far

import customtkinter as ctk
from customtkinter import CTkImage,CTkButton,CTkLabel
from PIL import Image
import time as t
from threading import Thread


class Bank():
    def __init__(self):
        self.money=0
    def deposit(self,amount,handler=None):
        self.money += amount
        if handler:
            handler(amount)
    def withdraw(self, amount, handler=None):
        if self.money >= amount:
           self.money -= amount
        if handler:
            handler(amount)
        else:
            print('error: not enough money available')
class BeanCounter():
    def __init__(self, amount=0):
        self.amount = amount

    def increment(self, handler=None):
        ''' accepts optional function called after one bean added '''
        self.amount += 1
        if handler:
            handler()
class BeanDoubler(Thread,BeanCounter):
    def __init__(self,update_func=None):
        self.bean_counter = BeanCounter()
        self.update_func = update_func
    def run(self):
        while True:
        # use a for loop to increment over the amount (thereby doubling)
            for _ in range(self.bean_counter.amount):
                self.bean_counter.increment(self.update_func)

class BeanApp(ctk.CTk,Bank,BeanCounter):
  def __init__(self):
        # sets window and frames
        self.title("Bean Tycoon")


        #click modes
        multiplier_lbl=CTkLabel(self,text= "Multipliers")
        multiplier_lbl.place(x=250,y=1,anchor="n",)
        

        one_click_mode_btn= CTkButton(self,text="x1",width=20,height=10,command=None,state="disabled")
        one_click_mode_btn.place(x=145,y=25,anchor="nw")
        two_click_mode_btn=CTkButton(self, text="x2",width=20,height=10,command=None,state="disabled")
        two_click_mode_btn.place(x=173,y=25,anchor="nw")
        click_multiplyer_lbl=CTkLabel(self,text=f"  Beans/click: x{None}   ")
        click_multiplyer_lbl.place(x=3,y=45,anchor="nw",)
        # Bean generator
        beanbtn = CTkImage(Image.open("None"),size=(200,200))
        def on_click():
            BeanCounter.increment(bean_label_updater())
   
        bean_amt_lbl = CTkLabel(self,text= f"  Beans: {None}  ",)
        bean_amt_lbl.place(x=3,y=5,anchor="nw")
        def bean_label_updater():
           bean_amt_lbl.configure(text= f"  Beans: {BeanCounter.amount}  ")
        sell_beans_btn = CTkButton(self,text= "",image=beanbtn, command= on_click,width=180,height=180) 
        sell_beans_btn.place(x=250,y=330, anchor="s")
        # Sell Beans
        money_amt_lbl = CTkLabel(self,text=f"  Money: ${None}  ", )
        money_amt_lbl.place(x=3,y=25,anchor='nw')
        def on_click_sell(self):
            Bank.deposit(BeanCounter.amount)  # times amount per bean ; todo: add bank label updater function
            BeanCounter.amount = 0
            
                
        sell_bean_btn = CTkButton(self,text="Sell Beans",image=None, command=on_click_sell)
        sell_bean_btn.place(x=250,y=360,anchor='s')
        #2 times multiplier
        
        
        #Shop
        shop_lbl= CTkLabel(self,text="Shop")
        shop_lbl.place(x=425,y=5,anchor="nw")
        double_bean_upgrade_btn = CTkButton(self,text="Bean Doubler\n$100",command=None,width=20,corner_radius=20)
        double_bean_upgrade_btn.place(x=390,y=30,anchor="nw")
        auto_collect_bean_btn = CTkButton(self,text="Auto Collect 1\n$200",command=None,width=20,corner_radius=20)
        auto_collect_bean_btn.place(x=390,y=70,anchor="nw")


if __name__ == "__main__":
  bank = Bank()
  bean_counter = BeanCounter()
  beandoubler = BeanDoubler()
  app = BeanApp()
  app.mainloop()

this is the error that pulled up recursion error

can anyone tell me where I went wrong and help me fix it?

what should come up is a customtkinter window that looks similar to this first window iteration the code that popped up that window kept freezing the application when i ran it, which is how I got to the current problem that I'm facing.

Edit 1: I have figured I should share the full callback log


Solution

  • Read the traceback, and you'll find that your error is only coming from self.title("Bean Tycoon") line, so has nothing to do with the remainder of the code...


    In any case, your classes don't need multi-inheritance

    Replace

    class BeanDoubler(Thread,BeanCounter):
    

    with

    class BeanDoubler(Thread):
    

    A BeanCounter instance is passed into the __init__ constructor of that class, as shown in my previous answer

    Similar for BeanApp. It only needs to be a ctk.CTk class, and the __init__ is different for that. Plus, you've removed super().__init__() for some reason


    Then, it is not BeanCounter.increment(bean_label_updater()), it is self.bean_counter.increment(bean_label_updater)...

    1. You use an instance to call a function, not a class

    2. You do not pass the return value of a function call into increment, only the function name / handle. It's the responsibility of increment to actually call the function via this section

       if handler:
           handler()
      

    Similarly, BeanCounter.amount is not a valid accessor. You should use self.bean_counter.amount since it is an instance field, as was also written in my last answer.


    You have defined functions within your __init__ of the BeanApp, which is generally not recommended. Define all functions at the class level, rather than within other functions. Then self will reference the BeanApp instance, where you can access self.bank and self.bean_counter, after updating your __init__ parameters and __main__ section.