0

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

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
  • 2
    Just an aside, you seem to want to treat your classes and their members as instances as well as singletons. You might want to verify that when you do `BeanCounter.increment(bean_label_updater())` that is actually does what you hope – JonSG May 16 '23 at 14:17
  • 1
    hm could explain why self did not work in Class BeanApp for BeanCounter and Bank classes. which was somthing i noticed while debugging – T.E.G Studios May 16 '23 at 14:32
  • also keep in mind im pretty new to programing, so if structuring them as instances and singletons is not a good way id appreciate guidance to making it better – T.E.G Studios May 16 '23 at 14:57

2 Answers2

0

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.

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
  • the one problem i had with the previous answer is `self.bean_counter.amount` and related fuctions in both `class Bank`and `class BeanDoubler` had no proper inheritance within `class BeanApp` which in turn none of the functions worked. so i tried fixing it as to get no errors in the code. clearly i just made it worse. ill revert my code back to your answer and try again with a different approach with these explanations in mind – T.E.G Studios May 16 '23 at 15:38
  • I'm not sure what you mean. `self` attribute access doesn't need inheritance, but you do need to define the variables, through `__init__` constructor, which you missed copying from my previous post https://docs.python.org/3/tutorial/classes.html?highlight=__init__#class-objects – OneCricketeer May 16 '23 at 19:14
  • my class structure is on point with the tutorial you sent me. and i copied and pasted your code a couple of times just to make sure it was all correct. still, the objects are not connected with the previous classes. i use Pylance on VS Code and the objects appear white with a tool tip that says `(function)bean_counter: Any` which from my understanding shows that the program recognizes that the function exists but has no attributes. which is what ive been trying to pass into `class BeanApp` – T.E.G Studios May 16 '23 at 21:45
  • imma take this back to [my first question](https://stackoverflow.com/a/76267371/21627934) as this is getting off topic. i do understand where i went wrong originally and i hope i can get these errors resolved – T.E.G Studios May 16 '23 at 22:12
0

so i found i was missing the super().__init__ constructor so now the UI runs but im now left with the object inheritance errors. i will be closing this thread and going back to my original problem

i guess while debugging the original answer i have accidently removed the super().__init__