You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

下方存在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 00:27:27