Python Tkinter回调传参错误:切换书架时标签更新失败
问题
我尝试在下拉菜单触发计算后更新标签,但遇到无法理解的错误:
- 背景:此前方法运行正常,未修改核心逻辑,仅给所属的
ShelfDownloader类初始化时添加了controller,该类仅在其他模块调用一次。 - 现象:类初始化时
self.total_books_label显示正常,但切换书架时出错。调试显示切换后计算结果正确,但标签更新失败。
尝试两种回调写法均报错:
- 直接设置
command=self.shelf_option_callback(parent)时,出现AttributeError: 'str' object has no attribute 'tk'; - 使用
lambda: self.shelf_option_callback(parent)时,出现TypeError: ... takes 0 positional arguments but 1 was given。
相关代码
class ShelfDownloader(ctk.CTkFrame): def __init__(self, parent, controller): ctk.CTkFrame.__init__(self, parent) self.parent = parent self.controller = controller ... def shelf_option_callback(self, parent, *args): if not self.downloader: sys.exit() shelf_choice = [s for s in SHELF_METADATA if self.shelf_choice_var.get() in s['shelf_name']][0] for k, v in shelf_choice.items(): self.downloader_dict[k] = v updated_dict = self.downloader.update_shelf(**self.downloader_dict) for k, v in updated_dict.items(): self.downloader_dict[k] = v self.display_total_books_and_pages(parent) def draw_top_panel(self): parent = self.top_frame LL1 = ctk.CTkLabel(parent, text="Choose shelf to download:") # LL1.pack() shelf_opt_menu = ctk.CTkOptionMenu(master=parent, width=170, variable=self.shelf_choice_var, values=self.shelf_list, command= lambda: self.shelf_option_callback(parent) ## ^^^^^^^^^^^^^^^^^^^^^^^^^^^ ## This is my problem spot. Tried adding ## command=self.shelf_option_callback(parent) ## but different error. shelf_opt_menu.pack() def display_total_books_and_pages(self, parent): if hasattr(self, 'total_books_label'): self.total_books_label.destroy() ## GUI feedback info, partially a debugging tool _books, _pages = (self.downloader_dict['total_book_count'], self.downloader_dict['total_page_count']) self.display_text.set(f"Shelf has {_books} books, retrieving {_pages} pages.") logger.debug(f"{self.display_text.get()}") logger.debug(f"display_total_books_and_pages: {type(self.display_text)}") self.total_books_label = ctk.CTkLabel(master=parent, width=180, text=f"{self.display_text.get()}") self.total_books_label.pack()
调试输出
2022-12-12 12:05:22,612, gui_download, 183: Shelf has 1624 books, retrieving 17 pages. 2022-12-12 12:05:22,612, gui_download, 184: display_total_books_and_pages: <class 'tkinter.StringVar'>
错误日志
错误1(直接传参时)
Exception in Tkinter callback Traceback (most recent call last): File "C:\Users\megha\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 1948, in __call__ return self.func(*args) ^^^^^^^^^^^^^^^^ ... File "c:\MyProjects\gr_shelf_tools\src\gui_downloader.py", line 124, in shelf_option_callback self.display_total_books_and_pages(parent) File "c:\MyProjects\gr_shelf_tools\src\gui_downloader.py", line 184, in display_total_books_and_pages self.total_books_label = ctk.CTkLabel(master=parent, width=180, text=f"{self.display_text.get()}") ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ... File "C:\Users\megha\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 2591, in _setup self.tk = master.tk ^^^^^^^^^ AttributeError: 'str' object has no attribute 'tk'
错误2(使用lambda时)
Exception in Tkinter callback Traceback (most recent call last): File "C:\Users\megha\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 1948, in __call__ return self.func(*args) ^^^^^^^^^^^^^^^^ File "C:\Users\megha\AppData\Local\Programs\Python\Python311\Lib\site-packages\customtkinter\windows\widgets\core_widget_classes\dropdown_menu.py", line 101, in <lambda> command=lambda v=value: self._button_callback(v), ^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users\megha\AppData\Local\Programs\Python\Python311\Lib\site-packages\customtkinter\windows\widgets\core_widget_classes\dropdown_menu.py", line 106, in _button_callback self._command(value) File "C:\Users\megha\AppData\Local\Programs\Python\Python311\Lib\site-packages\customtkinter\windows\widgets\ctk_optionmenu.py", line 381, in _dropdown_callback self._command(self._current_value) TypeError: ShelfDownloader.draw_top_panel.<locals>.<lambda>() takes 0 positional arguments but 1 was given
解决思路与方案
错误原因分析
- 直接传参
command=self.shelf_option_callback(parent):这里是立即执行了函数,而非传递函数对象作为回调。函数执行后的返回值被当成了回调,且CTkOptionMenu触发时会自动传递选中的字符串作为参数,导致原本的parent参数被替换成了字符串,创建Label时master是字符串,因此报错'str' object has no attribute 'tk'。 - 使用
lambda: self.shelf_option_callback(parent):CTkOptionMenu的command回调会自动传入选中的选项值(字符串),但该lambda未定义参数接收这个值,触发时就会报参数不匹配的错误。
修复方案
方案1:修正lambda的参数接收(保留parent传递)
修改draw_top_panel中的command,让lambda接收CTkOptionMenu传递的参数(即使不用),同时保留parent参数:
shelf_opt_menu = ctk.CTkOptionMenu( master=parent, width=170, variable=self.shelf_choice_var, values=self.shelf_list, command=lambda selected_val: self.shelf_option_callback(parent) )
方案2:优化代码,移除多余的parent参数(更推荐)
因为parent就是类属性self.top_frame,可以直接在回调中使用,无需传递,彻底避免参数混乱:
- 修改
shelf_option_callback,移除parent参数,直接调用无参的display_total_books_and_pages:
def shelf_option_callback(self, selected_val=None): # 接收CTkOptionMenu传递的选中值,可选忽略 if not self.downloader: sys.exit() shelf_choice = [s for s in SHELF_METADATA if self.shelf_choice_var.get() in s['shelf_name']][0] for k, v in shelf_choice.items(): self.downloader_dict[k] = v updated_dict = self.downloader.update_shelf(**self.downloader_dict) for k, v in updated_dict.items(): self.downloader_dict[k] = v self.display_total_books_and_pages() # 不再传递parent
- 修改
display_total_books_and_pages,移除parent参数,直接使用self.top_frame作为master:
def display_total_books_and_pages(self): if hasattr(self, 'total_books_label'): self.total_books_label.destroy() _books, _pages = (self.downloader_dict['total_book_count'], self.downloader_dict['total_page_count']) self.display_text.set(f"Shelf has {_books} books, retrieving {_pages} pages.") logger.debug(f"{self.display_text.get()}") logger.debug(f"display_total_books_and_pages: {type(self.display_text)}") self.total_books_label = ctk.CTkLabel( master=self.top_frame, # 直接使用类属性 width=180, text=f"{self.display_text.get()}" ) self.total_books_label.pack()
- 修改
draw_top_panel中的command,直接传递函数对象:
shelf_opt_menu = ctk.CTkOptionMenu( master=parent, width=170, variable=self.shelf_choice_var, values=self.shelf_list, command=self.shelf_option_callback # 直接传函数,自动接收选中值 )
这样修改后,既解决了参数传递的错误,也让代码结构更清晰,避免了不必要的参数传递。
内容的提问来源于stack exchange,提问作者Meghan M.
相关产品推荐
相关产品推荐

