下方存在CTkScrollableFrame时,滚动ttk.Combobox下拉列表触发异常
CTkScrollableFrame与ttk.Combobox滚动冲突引发AttributeError
当界面同时包含customtkinter.CTkScrollableFrame和tkinter.ttk.Combobox组件时,滚动Combobox的下拉列表会触发如下异常:
Exception in Tkinter callback Traceback (most recent call last): File "C:\Users[user]\AppData\Local\Programs\Python\Python311\Lib\tkinter\__init__.py", line 1948, in __call__ return self.func(*args) ^^^^^^^^^^^^^^^^ File "C:\Users[user]\AppData\Local\Programs\Python\Python311\Lib\site-packages\customtkinter\windows\widgets\ctk_scrollable_frame.py", line 248, in _mouse_wheel_all if self.check_if_master_is_canvas(event.widget): ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ File "C:\Users[user]\AppData\Local\Programs\Python\Python311\Lib\site-packages\customtkinter\windows\widgets\ctk_scrollable_frame.py", line 280, in check_if_master_is_canvas elif widget.master is not None: ^^^^^^^^^^^^^ AttributeError: 'str' object has no attribute 'master'
复现代码
import tkinter as tk import tkinter.ttk as ttk import customtkinter as ctk def display_combobox(root): sample_values = ['test' for _ in range(30)] combo_box = ttk.Combobox(root, values=sample_values) combo_box.place(anchor='center', relx=0.5, rely=0.3) def display_scrollable(root): scrollabe_frame = ctk.CTkScrollableFrame(root, label_text='text', width=150) scrollabe_frame.place(anchor='center', relx=0.5, rely=0.6) if __name__ == '__main__': root = tk.Tk() root.geometry('500x500') display_combobox(root) display_scrollable(root) root.mainloop()
问题根源
CTkScrollableFrame会给全局窗口绑定鼠标滚轮事件(通过_mouse_wheel_all方法),当滚动ttk.Combobox的下拉列表时,触发的事件中event.widget返回的是下拉列表的字符串标识符,而非标准Tkinter组件对象。而check_if_master_is_canvas方法直接尝试访问该对象的master属性,字符串类型自然没有这个属性,因此抛出异常。
解决方案
方案1:修改CTkScrollableFrame的内置方法
找到CustomTkinter安装目录下的windows/widgets/ctk_scrollable_frame.py文件,修改check_if_master_is_canvas方法,增加对字符串类型的判断:
def check_if_master_is_canvas(self, widget): # 先判断是否为字符串类型,直接返回False跳过处理 if isinstance(widget, str): return False if widget == self._canvas: return True elif widget.master is not None: return self.check_if_master_is_canvas(widget.master) else: return False
这个修改能让方法跳过无法识别的字符串类型对象,避免报错。
方案2:动态绑定/解绑滚轮事件
通过监听Combobox的下拉和关闭事件,临时解绑CTkScrollableFrame的全局滚轮事件,避免冲突:
import tkinter as tk import tkinter.ttk as ttk import customtkinter as ctk def display_combobox(root, scroll_frame): sample_values = ['test' for _ in range(30)] combo_box = ttk.Combobox(root, values=sample_values) combo_box.place(anchor='center', relx=0.5, rely=0.3) # 下拉弹出时解绑全局滚轮事件 def on_dropdown(): root.unbind_all("<MouseWheel>") root.unbind_all("<Button-4>") root.unbind_all("<Button-5>") # 选择完成后重新绑定滚轮事件 def on_close(event): scroll_frame._bind_mouse_wheel_all() combo_box.bind("<<ComboboxSelected>>", on_close) # 延迟执行确保下拉框已渲染完成 combo_box.bind("<Button-1>", lambda e: root.after(100, on_dropdown)) def display_scrollable(root): scrollabe_frame = ctk.CTkScrollableFrame(root, label_text='text', width=150) scrollabe_frame.place(anchor='center', relx=0.5, rely=0.6) return scrollabe_frame if __name__ == '__main__': root = tk.Tk() root.geometry('500x500') scroll_frame = display_scrollable(root) display_combobox(root, scroll_frame) root.mainloop()
内容的提问来源于stack exchange,提问作者t-poplawski
相关产品推荐
相关产品推荐

