Tkinter动态创建的重量按钮无法正确增减数值(仅增减1而非预期的5)
嘿,我看了你的代码,问题其实出在plusminus函数里的判断逻辑上!
你现在是用if entry == weight_entry:来区分是不是重量输入框,但这里的weight_entry是你在全局作用域里定义的第一行的那个输入框对象。而你在add_row()函数里创建的新的重量输入框,是每个行里的局部变量weight_entry,它们和全局的那个根本不是同一个对象,所以这个判断条件永远不成立,自然就走了else分支,每次只加减1啦。
那怎么改呢?给你两个简单的解决思路,选哪个都行:
方法一:给重量输入框加个自定义标识
我们可以给所有重量输入框(包括第一行的和动态创建的)都加一个自定义属性,比如is_weight = True,然后在plusminus里判断这个属性,而不是比较对象是否相等。
步骤如下:
修改
plusminus函数的判断条件:def plusminus(increment, entry, row_counter): if entry.get() == '': new_value = 0 else: current_value = int(entry.get()) # 改成判断是否有is_weight属性且为True if hasattr(entry, 'is_weight') and entry.is_weight: # 重量字段,加减5 new_value = current_value + 5 if increment else current_value -5 else: # 其他字段(比如组数),加减1 new_value = current_value +1 if increment else current_value -1 if new_value < 0: new_value = 0 entry.delete(0, tk.END) entry.insert(0, str(new_value))给第一行的重量输入框加上标识:
# 原来的第一行weight_entry创建代码后面加一行 weight_entry = ttk.Entry(root, text='Weight') weight_entry.is_weight = True # 加这个标识在
add_row()函数里创建新的重量输入框时,也加上这个标识:weight_entry = ttk.Entry(root) weight_entry.is_weight = True # 给动态创建的重量输入框加标识 weight_entry.grid(row=row_counter, column=5, padx=5, pady=5)
方法二:在调用plusminus时直接传递是否为重量字段
另一种思路是,在创建按钮的时候,直接告诉plusminus这个操作的是重量还是组数,不用靠输入框对象判断。
修改
plusminus函数,新增一个is_weight参数:def plusminus(increment, entry, row_counter, is_weight=False): if entry.get() == '': new_value = 0 else: current_value = int(entry.get()) if is_weight: new_value = current_value +5 if increment else current_value -5 else: new_value = current_value +1 if increment else current_value -1 if new_value <0: new_value=0 entry.delete(0, tk.END) entry.insert(0, str(new_value))修改第一行的重量按钮的command:
btn_weightpos = tk.Button(root, text='+', command=lambda: plusminus(True, weight_entry, 0, is_weight=True)) btn_weightneg = tk.Button(root, text='-', command=lambda: plusminus(False, weight_entry, 0, is_weight=True))修改
add_row()里的重量按钮command:btn_weightpos = tk.Button(root, text='+', command=lambda: plusminus(True, weight_entry, row_counter, is_weight=True)) btn_weightneg = tk.Button(root, text='-', command=lambda: plusminus(False, weight_entry, row_counter, is_weight=True))
这样不管是哪一行的重量按钮,都会正确触发加减5的逻辑啦!
给你贴一份修改后的完整代码(用方法一的版本)
import tkinter as tk from tkinter import ttk from tkinter import * from PIL import ImageTk, Image import os weight_entry='0' root=tk.Tk() movement_chest = ["Flat Bench", 'Incline Bench', 'Decline Bench', 'Flat DB Press', "Incline DB Press", 'High-Low Cable Raise', "Low-High Cable Raise",] files_movement_chest = ["flat_bench.gif", 'incline_bench.gif', 'decline_bench.gif', 'db_flat.gif', "db_incline.gif", 'high_low_cable.gif', "low_high_cable.gif",] row_counter=1 class Toolbar(Frame): def __init__(self): super().__init__() self.initUI() def initUI(self): self.master.title("Workout Tracker") menubar = Menu(self.master) self.master.config(menu=menubar) fileMenu = Menu(menubar) fileMenu.add_command(label="Exit", command=self.onExit) menubar.add_cascade(label="File", menu=fileMenu) def onExit(self): self.quit() # Create a variable to hold the image reference image_label = None img = None # Set button function def plusminus(increment, entry, row_counter): if entry.get() == '': new_value = 0 else: current_value = int(entry.get()) # Get the current value from the entry widget # 修改判断逻辑:检查输入框是否有is_weight标识 if hasattr(entry, 'is_weight') and entry.is_weight: # If the entry widget is the weight field, change the value by 5 if increment: new_value = current_value + 5 else: new_value = current_value - 5 else: # For other fields like sets, change by 1 if increment: new_value = current_value + 1 print(entry) else: new_value = current_value - 1 if new_value < 0: new_value = 0 entry.delete(0, tk.END) # Clear the entry entry.insert(0, str(new_value)) # Insert the new value def on_combobox_select(event=None, row_counter=0): # Allow for manual function call (no event) global image_label, img # Use global variables # 修复动态行下拉框选值的问题:获取当前触发事件的下拉框的选中值 motion = Dropdown.get() if event is None else event.widget.get() # Remove the old image if it exists if image_label: image_label.destroy() # Find the index of the selected motion in movement_chest if motion in movement_chest: index = movement_chest.index(motion) # Get the index of the selected motion # Get the corresponding filename for the selected motion img_path = files_movement_chest[index] # Check if the image file exists and load it if os.path.exists(img_path): img = ImageTk.PhotoImage(Image.open(img_path)) else: print(f"Image file '{img_path}' not found.") img = None # Only add the label if the image is loaded correctly if img: image_label = Label(root, image=img) image_label.grid(row=row_counter, column=0, columnspan=3, sticky="nsew") # Use grid here with spanning else: img_path = "workout.gif" if os.path.exists(img_path): img = ImageTk.PhotoImage(Image.open(img_path)) else: print("Image file 'workout.gif' not found.") img = None if img: # Only add the label if the image is loaded correctly image_label = Label(root, image=img) image_label.grid(row=row_counter, column=0, columnspan=3, sticky="nsew") # Use grid here with spanning # Create a function that adds a new row of widgets def add_row(): global row_counter # Add row-specific widgets like motion dropdown, weight entry, set entry, etc. label_dropdown = tk.Label(root, text='Motion') label_dropdown.grid(row=row_counter, column=4, pady=(15, 0), sticky='n') # Create a new row of widgets for motion motion_dropdown = ttk.Combobox( root, values=movement_chest, state="readonly", ) motion_dropdown.grid(row=row_counter, column=4, padx=5, pady=5) motion_dropdown.bind("<<ComboboxSelected>>", lambda event: on_combobox_select(event, row_counter)) # Create weight entry for this row label_weight = tk.Label(root, text='Weight') label_weight.grid(row=row_counter, column=5, pady=(15, 0), sticky='n') weight_entry = ttk.Entry(root) weight_entry.is_weight = True # 给动态创建的重量输入框加标识 weight_entry.grid(row=row_counter, column=5, padx=5, pady=5) # Buttons for incrementing/decrementing weight btn_weightpos = tk.Button(root, text='+', command=lambda: plusminus(True, weight_entry, row_counter)) btn_weightpos.grid(row=row_counter, column=5, padx=(0, 19), pady=(0, 8), sticky='s') btn_weightneg = tk.Button(root, text='-', command=lambda: plusminus(False, weight_entry, row_counter)) btn_weightneg.grid(row=row_counter, column=5, padx=(19, 0), pady=(0, 8), sticky='s') # Create set entry for this row label_set = tk.Label(root, text='Sets') label_set.grid(row=row_counter, column=6, pady=(15, 58), sticky='n') set_entry = ttk.Entry(root) set_entry.grid(row=row_counter, column=6, padx=5, pady=5) # Buttons for incrementing/decrementing sets btn_setpos = tk.Button(root, text='+', command=lambda: plusminus(True, set_entry, row_counter)) btn_setpos.grid(row=row_counter, column=6, padx=(0, 19), pady=(0, 8), sticky='s') btn_setneg = tk.Button(root, text='-', command=lambda: plusminus(False, set_entry, row_counter)) btn_setneg.grid(row=row_counter, column=6, padx=(19, 0), pady=(0, 8), sticky='s') # Increment row_counter for the next row row_counter += 1 # Dropdown (Combobox) Dropdown = ttk.Combobox( root, values=movement_chest, state="readonly", ) label_dropdown = tk.Label(root, text='Motion') label_dropdown.grid(row=0, column=4, pady=(70, 0), sticky='n') Dropdown.grid(row=0, column=4, padx=5, pady=5) Dropdown.bind("<<ComboboxSelected>>", on_combobox_select) # Weight field label_weight = tk.Label(root, text='Weight') weight_entry = ttk.Entry(root, text='Weight') weight_entry.is_weight = True # 给第一行的重量输入框加标识 label_weight.grid(row=0, column=5, pady=(70, 0), sticky='n') weight_entry.grid(row=0, column=5, padx=5, pady=5) # button to increment the weight value btn_weightpos = tk.Button(root, text='+', command=lambda: plusminus(True, weight_entry, 0)) # Using lambda to pass argument btn_weightpos.grid(row=0, column=5, padx=(0, 19), pady=(0, 65), sticky='s') # button to decrement the weight value btn_weightneg = tk.Button(root, text='-', command=lambda: plusminus(False, weight_entry, 0)) btn_weightneg.grid(row=0, column=5, padx=(19, 0), pady=(0, 65), sticky='s') # Set field label_set = tk.Label(root, text='Sets') set_entry = ttk.Entry(root, text='Sets') label_set.grid(row=0, column=6, pady=(70, 0), sticky='n') set_entry.grid(row=0, column=6, padx=5, pady=5) # button to increment the set value btn_setpos = tk.Button(root, text='+', command=lambda: plusminus(True, set_entry, 0)) # Using lambda to pass argument btn_setpos.grid(row=0, column=6, padx=(0, 19), pady=(0, 65), sticky='s') # button to decrement the set value btn_setneg = tk.Button(root, text='-', command=lambda: plusminus(False, set_entry, 0)) btn_setneg.grid(row=0, column=6, padx=(19, 0), pady=(0, 65), sticky='s') # Manually trigger the combobox selection to load the default image (workout.gif) on_combobox_select() # Add "Add Row" button add_row_button = tk.Button(root, text="Add Row", command=add_row) add_row_button.grid(row=0, column=7, padx=5, pady=5) # Initial row setup (first row) root.mainloop()
(另外我还顺手改了on_combobox_select里的motion获取逻辑,原来动态行的下拉框选的时候,会错误拿到第一行下拉框的值,现在改成获取当前触发事件的下拉框的选中值了,这个小问题也顺便修复了~)
备注:内容来源于stack exchange,提问作者anon

