customtkinter点击按钮报错TypeError: 'str' object is not callable
问题描述
测试customtkinter模块过程中,已经写完测试GUI界面、按钮点击后触发的蛋糕价格与尺寸计算逻辑,点击按钮时程序抛出异常,错误信息如下:
C:\Users\Jacek\Desktop>python test.py Exception in Tkinter callback Traceback (most recent call last): File "C:\Python\Python38-32\lib\tkinter\__init__.py", line 1883, in __call__ return self.func(*args) File "C:\Python\Python38-32\lib\site-packages\customtkinter\widgets\ctk_button.py", line 390, in clicked self.function() TypeError: 'str' object is not callable
报错栈指向customtkinter库内部源码,不属于自行编写的250行业务代码范围,排查所有变量类型未发现异常,完整测试代码如下:
import tkinter import customtkinter customtkinter.set_appearance_mode("dark") # Modes: system (default), light, dark customtkinter.set_default_color_theme("blue") # Themes: blue (default), dark-blue, green gui = customtkinter.CTk() # create CTk window like you do with the Tk window gui.geometry("520x420") gui.title("Layer cake calculator") # FRAMES CONFIGURATION frame_left = customtkinter.CTkFrame(master=gui, width=180, corner_radius=0) frame_left.grid(row=0, column=0, sticky="nswe") frame_right = customtkinter.CTkFrame(master=gui, width=180,) frame_right.grid(row=0, column=1, sticky="nswe", padx=20, pady=20) # WELCOME label welcome_label = customtkinter.CTkLabel(master=frame_left, text="Welcome to bday cake calculator!", text_font=("Arial", 12)).grid(row=1, column=0, pady=10, padx=10) # HOW MANY PORTIONS entry portion_label = customtkinter.CTkLabel(master=frame_left, text="How many portions?").grid(row=2, column=0, sticky="w") portion_entry = customtkinter.CTkEntry(master=frame_left, width=100).grid(row=3, column=0, sticky="w", padx=20, pady=5) # PRICE entry price_label = customtkinter.CTkLabel(master=frame_left, text="Price per person?").grid(row=4, column=0, sticky="w") price_entry = customtkinter.CTkEntry(master=frame_left, width=100).grid(row=5, column=0, sticky="w", padx=20, pady=5) # WHICH FORM label form_label = customtkinter.CTkLabel(master=frame_left, text="Which form?").grid(row=6, column=0, sticky="w", pady=5) form_combobox = customtkinter.CTkComboBox(master=frame_left, width=100, values=["Round", "Square", "Rectangle"]).grid(row=7, column=0, sticky="w", pady=5, padx=20) # MORE INFO more_info_label = customtkinter.CTkLabel(master=frame_left, text="More info:").grid(row=8, column=0, sticky="w", pady=5) # GLUTENFREE checkbox gf = tkinter.BooleanVar() gf_check = customtkinter.CTkCheckBox(master=frame_left, variable=gf, text="Gluten free?") gf_check.deselect() gf_check.grid(row=9, column=0, sticky="w", padx=20, pady=5) # BOX INCLUDED checkbox box = tkinter.BooleanVar() box_check = customtkinter.CTkCheckBox(master=frame_left, variable=box, text="Box included?") box_check.deselect() box_check.grid(row=10, column=0, sticky="w", padx=20, pady=5) # FUNCTIONS def print_test(): # TEST FUNCTION print(portion_entry.get()) print(price_entry.get()) print(form_combobox.get()) ####ACTIVE print(gf.get()) print(box.get()) def price_calculator(): # Price for the cake portion = int(portion_entry.get()) price_per_person = int(price_entry.get()) price_calc = portion * price_per_person if gf.get(): price_calc += (portion * 2) if box.get(): box_price = 0 if portion <= 20: box_price = 7 # TODO sprawdzić ceny opakowań elif portion <= 30: box_price = 10 elif portion <= 40: box_price = 15 elif portion > 40: box_price = 20 price_calc += box_price print("Prize for cake: ", price_calc, " zł") if box.get(): print("That includes box price -", box_price, " zł") # if gf.get(): # print("And gf bonus: ", gf_bonus, " zł") def round_calc(): # Round cake size calculator portion = int(portion_entry.get()) pi = round(math.pi, 2) one_piece = 22.8 def circle_field_calc(portion): circle_field = portion * one_piece return circle_field def d_calc(portion, circle_field): d = 2 * math.sqrt(circle_field / pi) d = round(d) return [d, circle_field] if portion < 31: # cake with one layer print("Diameter size in cm = ", d_calc(portion, circle_field_calc(portion))[0]) elif 31 <= portion <= 50: # cake with two layers first_layer = d_calc(portion, circle_field_calc(portion))[1] * 0.65 # 65 % of cake field is first layer field second_layer = d_calc(portion, circle_field_calc(portion))[1] * 0.35 # 35 % of cake field is second layer field first_d = d_calc(portion, first_layer)[0] # calculating d in first layer second_d = d_calc(portion, second_layer)[0] # calculating d in second layer print("Diameter size of first layer = ", first_d, "cm,\n", "\t\t\t\tand second layer =", second_d, "cm.") elif 51 <= portion <= 75: # cake with three layers first_layer = d_calc(portion, circle_field_calc(portion))[1] * 0.465 second_layer = d_calc(portion, circle_field_calc(portion))[1] * 0.32 third_layer = d_calc(portion, circle_field_calc(portion))[1] * 0.24 first_d = d_calc(portion, first_layer)[0] second_d = d_calc(portion, second_layer)[0] third_d = d_calc(portion, third_layer)[0] print("Diameter size of first layer = ", first_d, "cm,\n", "\t\t\t\tsecond layer =", second_d, "cm,\n", "\t\t\t\tand third layer =", third_d, "cm.") def one_portion_field(side): one_portion_field = side ** 2 return one_portion_field def square_calc(): portion = int(portion_entry.get()) square_cake_size = one_portion_field(6) * portion if portion <= 30: cake_side = math.sqrt(square_cake_size) print("Side of square cake: ", round(cake_side), " cm") elif 31 < portion <= 50: square_1 = square_cake_size * 0.65 square_2 = square_cake_size * 0.35 cake_side_1 = round(math.sqrt(square_1)) cake_side_2 = round(math.sqrt(square_2)) print("Side of first layer: ", round(cake_side_1), "cm,\n", "\tand second layer: ", round(cake_side_2), "cm,\n") elif 51 <= portion <= 75: square_1 = square_cake_size * 0.46 square_2 = square_cake_size * 0.32 square_3 = square_cake_size * 0.24 cake_side_1 = round(math.sqrt(square_1)) cake_side_2 = round(math.sqrt(square_2)) cake_side_3 = round(math.sqrt(square_3)) print("Side of first layer: ", round(cake_side_1), "cm,\n", "\tsecond layer: ", round(cake_side_2), "cm,\n", "\tand third layer: ", round(cake_side_3), "cm,\n") def rectangle_calc(): portion = int(portion_entry.get()) one_portion_field(6) cake_size = one_portion_field(6) * portion cake_side_a = round(math.sqrt(cake_size / 0.7)) cake_side_b = round(cake_side_a - 0.3 * cake_side_a) if portion > 25: layer_1 = cake_size * 0.65 layer_2 = cake_size * 0.35 cake_side_a_1 = round(math.sqrt(layer_1 / 0.7)) cake_side_b_1 = round(cake_side_a_1 - 0.3 * cake_side_a_1) cake_side_a_2 = round(math.sqrt(layer_2 / 0.7)) cake_side_b_2 = round(cake_side_a_2 - 0.3 * cake_side_a_2) print("Sides of first layer: ", round(cake_side_a_1), " cm X ", round(cake_side_b_1), "cm,\n", "\t\tand second layer: ", round(cake_side_a_2), " cm X ", round(cake_side_b_2), "cm,\n") elif portion > 45: layer_1 = cake_size * 0.46 layer_2 = cake_size * 0.32 layer_3 = cake_size * 0.24 cake_side_a_1 = round(math.sqrt(layer_1 / 0.7)) cake_side_b_1 = round(cake_side_a_1 - 0.3 * cake_side_a_1) cake_side_a_2 = round(math.sqrt(layer_2 / 0.7)) cake_side_b_2 = round(cake_side_a_2 - 0.3 * cake_side_a_2) cake_side_a_3 = round(math.sqrt(layer_3 / 0.7)) cake_side_b_3 = round(cake_side_a_3 - 0.3 * cake_side_a_3) print("Sides of first layer: ", round(cake_side_a_1), " cm X ", round(cake_side_b_1), "cm,\n", "\t\tsecond layer: ", round(cake_side_a_2), " cm X ", round(cake_side_b_2), "cm,\n", "\t\tthird layer: ", round(cake_side_a_3), " cm X ", round(cake_side_b_3), "cm,\n") def button_func(): price_calculator() if form_combobox.get("active") == "Round": round_calc() elif form_combobox.get("active") == "Square": square_calc() elif form_combobox.get("active") == "Rectangle": rectangle_calc() # BUTTON button = customtkinter.CTkButton(master=frame_left, text="Enter", command="print_test").grid(row=11, column=0, padx=20, pady=15) gui.mainloop() #python test.py
解决方案
代码里存在多个会触发报错的问题,逐个修复即可:
- 当前直接触发TypeError的根因:按钮的
command参数传入了字符串"print_test",而customtkinter/tkinter要求这个参数传入可调用的函数对象,不能加引号把函数名变成字符串。另外已经写了整合所有计算逻辑的button_func,直接传这个函数即可,不要加括号、不要加引号。# 错误写法 button = customtkinter.CTkButton(master=frame_left, text="Enter", command="print_test").grid(row=11, column=0, padx=20, pady=15) # 修正后 button = customtkinter.CTkButton(master=frame_left, text="Enter", command=button_func) button.grid(row=11, column=0, padx=20, pady=15) - 控件赋值写法错误:所有Label、Entry、ComboBox控件都是直接在实例化后链式调用
.grid(),这种写法会让变量保存的是grid()的返回值None,后续调用.get()方法时会直接报NoneType has no attribute get。所有控件要先实例化赋值给变量,再单独调用布局方法,以输入框为例:# 错误写法 portion_entry = customtkinter.CTkEntry(master=frame_left, width=100).grid(row=3, column=0, sticky="w", padx=20, pady=5) # 修正后 portion_entry = customtkinter.CTkEntry(master=frame_left, width=100) portion_entry.grid(row=3, column=0, sticky="w", padx=20, pady=5)welcome_label、portion_label、price_label、price_entry、form_label、form_combobox、more_info_label全部要按这个规则修改。 - 缺少模块导入:计算逻辑里大量使用
math.pi、math.sqrt,代码开头没有导入math模块,运行计算会触发NameError,在导入部分加上import math即可。 - 下拉框取值方法错误:
button_func和print_test里写的form_combobox.get("active")是tkinter原生Listbox的写法,customtkinter的CTkComboBox直接调用无参数的.get()就能拿到当前选中值,把所有form_combobox.get("active")改成form_combobox.get()。 - 矩形蛋糕计算的判断顺序错误:
rectangle_calc()里先判断portion > 25,后面的portion >45分支永远不会触发(大于45的数必然满足大于25的条件),把判断顺序调换,先判断数值更大的区间:# 修正后的判断顺序 if portion > 45: # 三层蛋糕计算逻辑 elif portion > 25: # 两层蛋糕计算逻辑
所有问题修复后程序即可正常运行。
内容的提问来源于stack exchange,提问作者olenka
相关产品推荐
相关产品推荐

