python while循环is making制造my customtkinter自定义kinter应用freeze冻结.我怎么能做一个更好的功能,不会冻结应用程序?

wwtsj6pe  于 2023-05-16  发布在  Python
关注(0)|答案(2)|浏览(67)

我正在使用customtkinter库制作一个游戏,这个游戏是一个涉及豆子的点击器大亨游戏。与cookie clicker非常相似。下面是整个代码

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

# stores money
money = 0
# variables for bean generation
beans = 0
click = 1
#toggles bean collection
toggle=False
#Application
class BeanApp(ctk.CTk):
    def __init__(self):
        super().__init__()
        # sets window and frames
        self.title("Bean Tycoon")
        self.geometry("500x500+700+200")

        #click modes
        multiplier_lbl=CTkLabel(self,text= "Multipliers")
        multiplier_lbl.place(x=250,y=1,anchor="n",)
        def xOne():#x1 multiplier toggle
            global click
            click=1
            one_click_mode_btn.configure(state="disabled")
            two_click_mode_btn.configure(state="enable")
            click_multiplyer_lbl.configure(text=f"  Beans/click: x{click}   ")
        def xTwo():#x2 multiplier toggle
            global click
            click=2
            one_click_mode_btn.configure(state="enable")
            two_click_mode_btn.configure(state="disabled")
            click_multiplyer_lbl.configure(text=f"  Beans/click: x{click}   ")

        one_click_mode_btn= CTkButton(self,text="x1",width=20,height=10,command=xOne,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=xTwo,state="disabled")
        two_click_mode_btn.place(x=173,y=25,anchor="nw")
        click_multiplyer_lbl=CTkLabel(self,text=f"  Beans/click: x{click}   ")
        click_multiplyer_lbl.place(x=3,y=45,anchor="nw",)
        # Bean generator
        #beanbtn = CTkImage(Image.open("Pictures\TheBean.png"),size=(200,200))
        bean_amt_lbl = CTkLabel(self,text= f"  Beans: {beans}  ",)
        bean_amt_lbl.place(x=3,y=5,anchor="nw")
        def clicked():
            global beans
            global click
            beans += click
            bean_amt_lbl.configure(text= f"  Beans: {beans}  ")

        sell_beans_btn = CTkButton(self,text= "",image=None, command= clicked,width=180,height=180) 
        sell_beans_btn.place(x=250,y=330, anchor="s")
        # Sell Beans
        money_amt_lbl = CTkLabel(self,text=f"  Money: ${money}  ", )
        money_amt_lbl.place(x=3,y=25,anchor='nw')
        def sell():
            global money
            global beans
            while beans >0:
                money = money+beans
                beans = 0
                bean_amt_lbl.configure(text=f"  Beans: {beans}  ")
                money_amt_lbl.configure(text=f"  Money: ${money  }")
                
        sell_bean_btn = CTkButton(self,text="Sell Beans",image=None, command=sell)
        sell_bean_btn.place(x=250,y=360,anchor='s')
        #2 times multiplier
        def d_b_u():#double_bean_upgrade
            global click,money
            if money>=100:
                click=2
                money=money-100
                money_amt_lbl.configure(text=f"  Money: ${money  }")
                double_bean_upgrade_btn.configure(state="disabled")
                two_click_mode_btn.configure(state="disabled")
                one_click_mode_btn.configure(state="enable")
                click_multiplyer_lbl.configure(text=f"  Beans/click: x{click}   ")
        #automatic bean collector
        def a_b_c_u():#automatic_bean_collector_upgrade
            global beans,money,toggle
            if money>=20:
                money=money-20
                money_amt_lbl.configure(text=f"  Money: ${money  }")
                auto_collect_bean_btn.configure(state="disabled")
                while beans >=0 :
                    beans=beans+1
                    bean_amt_lbl.configure(text=f"  Beans: {beans}  " )
                    t.sleep(3)
                    print(beans) 
                               
        #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=d_b_u,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=a_b_c_u,width=20,corner_radius=20)
        auto_collect_bean_btn.place(x=390,y=70,anchor="nw")

if __name__ == "__main__":
    app = BeanApp()
    app.mainloop()

游戏包括在后台运行的递增计数器,循环通过按钮切换。然而,当循环激活时,它冻结应用程序。如何解决这个问题?
编辑:我给图像库和图像函数加了标签,因为它现在对代码没有任何影响。
编辑2:如何正确实现线程?我不认为我做得对…

#automatic bean collector
def b_c_():
        global beans
        while beans >=0 :
                beans=beans+1
                bean_amt_lbl.configure(text=f"  Beans: {beans}  " )
                t.sleep(3)
                print(beans) 
    bean_collector=Thread(target= b_c_)
    def a_b_c_u():#automatic_bean_collector_upgrade
        global beans,money,toggle
        if money>=20:
            money=money-20
            money_amt_lbl.configure(text=f"  Money: ${money  }")
            auto_collect_bean_btn.configure(state="disabled")
            bean_collector.run()

编辑3:在这个项目上休息了一会儿,现在我又回来了。无论如何,在实现OneCrickiteer提供的代码后,我一边编辑一边摆脱错误并适应我的代码,我最终遇到了一个递归错误

#entire program
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")
        self.bank = Bank()
        self.bean_counter = BeanCounter()

        #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("Pictures\TheBean.png"),size=(200,200))
        def on_click():
            self.bean_counter.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: {self.bean_counter.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(self.bean_counter.amount)  # times amount per bean ; todo: add bank label updater function
            self.bean_counter.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()

RecursionError:超过最大递归深度
self在BeanApp下的所有函数中被删除为self.bean_counter,而self.bank没有注册为Class Bank和Class BeanCounter的一部分。我也把类放在类函数class BeanDoubler(Thread,BeanCounter)中,让它们下面的函数工作并注册对象。这是我的解决方案,以摆脱提示的错误,但现在我所拥有的是一个递归错误。

fwzugrvs

fwzugrvs1#

您需要将循环放入另一个线程中,以便用户界面不会冻结。您可以使用thread库。

import thread

def foo(args):
  # loop logic

thread.start_new_thread(foo, (args))
3duebb1j

3duebb1j2#

不阻塞UI的一般解决方案是在UI实际上需要更新时传递函数,根本不使用循环,或者非常少。
您还拥有全局状态,这是不推荐的。要解决这两个问题,请添加更多的类。

class Bank:
  def __init__(self):
    self.money = 0

  def deposit(self, amount, handler=None)
    ''' accepts optional function with the amount deposited '''
    self.money += amount
    if handler:
      handler(amount)

  def withdraw(self, amount, handler=None)
    ''' accepts optional function with the amount withdrawn '''
    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()

然后更新app构造函数,用上面的函数定义一些功能

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

      self.bank = bank
      self.bean_counter = bean_counter

    def bean_label_updater(self):
      ''' updates the bean label with the current amount '''  
      bean_amt_lbl.configure(text= f"  Beans: {self.bean_counter.amount}  ")

    def on_click(self):
      self.bean_counter.increment(bean_label_updater)
    # TODO: button target=on_click

    def on_click_sell(self):
      self.bank.deposit(self.bean_counter.amount)  # times amount per bean ; todo: add bank label updater function
      self.bean_counter.amount = 0
      self.bean_label_updater()

然后呢

if __name__ == "__main__":
  bank = Bank()
  bean_counter = BeanCounter()

  app = BeanApp(bank, bean_counter)
  app.mainloop()

这并不能完全解决“自动收集器”阻塞UI的问题,但它至少为您指出了一个将“状态/模型”与“视图”分离的一般起点
一个解决方案是像这样围绕beancounter类创建一个线程 Package 器,然后在关联的操作上启动它。

class BeanDoubler(Thread):
  def __init__(self, bean_counter, update_func=None):
    self.bean_counter = bean_counter
    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)

(code尚未测试,抱歉,如果有错别字)

相关问题