Tkinter/CustomTkinter递归深度超限:部署与编译exe时如何规避?
问题分析与解决方案
问题核心
递归错误的根源是绑定根窗口的<Configure>事件后,在回调函数中修改子控件尺寸会再次触发<Configure>事件,形成无限递归循环。每次调用frames[i].configure()都会引发窗口布局变化,再次触发rescale函数,直到超出Python递归深度限制。
解决步骤
1. 临时解绑事件打断递归链
在执行控件尺寸修改操作前,先解绑<Configure>事件,操作完成后重新绑定,避免重复触发回调:
def rescale(event, root, frames, scrolls): # 临时解绑事件,防止递归触发 root.unbind("<Configure>") scale_factor = ctypes.windll.shcore.GetScaleFactorForDevice(0)/100 width = root.winfo_width()//scale_factor height = root.winfo_height()//scale_factor for i in range(3): frames[i].configure(width = width-10,height=(height//3)-30) scrolls[i].configure(width = (width/2)-40,height=(height//3)-50) scrolls[i+3].configure(width = (width/2)-40,height=(height//3)-50) # 重新绑定事件 root.bind("<Configure>", lambda event, root=root, frames=frames, scrolls=scrolls: rescale(event, root, frames, scrolls)) root.update_idletasks()
2. 用标志位控制递归触发
设置全局/闭包标志位,标记当前是否正在执行缩放操作,直接跳过重复的回调触发:
is_rescaling = False def rescale(event, root, frames, scrolls): global is_rescaling if is_rescaling: return is_rescaling = True scale_factor = ctypes.windll.shcore.GetScaleFactorForDevice(0)/100 width = root.winfo_width()//scale_factor height = root.winfo_height()//scale_factor for i in range(3): frames[i].configure(width = width-10,height=(height//3)-30) scrolls[i].configure(width = (width/2)-40,height=(height//3)-50) scrolls[i+3].configure(width = (width/2)-40,height=(height//3)-50) is_rescaling = False root.update_idletasks()
3. 利用CustomTkinter内置缩放机制
CustomTkinter原生支持DPI自适应缩放,无需手动获取缩放因子。初始化时设置全局缩放参数,让控件自动适配系统缩放比例:
import ctypes scale_factor = ctypes.windll.shcore.GetScaleFactorForDevice(0)/100 # 全局设置控件和窗口缩放比例 ctk.set_widget_scaling(scale_factor) ctk.set_window_scaling(scale_factor)
这会让大多数控件自动调整尺寸,减少手动修改的需求,从根源避免递归问题。
4. 优化布局管理替代手动尺寸设置
使用grid的weight参数配置行列权重,让控件自动填充空间,无需手动设置固定尺寸:
# 根窗口行列权重配置,让控件随窗口拉伸 root.grid_rowconfigure(0, weight=1) root.grid_rowconfigure(1, weight=1) root.grid_rowconfigure(2, weight=1) root.grid_columnconfigure(0, weight=1) root.grid_columnconfigure(1, weight=1) # 框架内部行列权重配置 for frame in frames: frame.grid_columnconfigure(0, weight=1) frame.grid_columnconfigure(1, weight=1) frame.grid_rowconfigure(0, weight=1)
窗口缩放时,控件会自动根据权重分配空间,彻底避免因手动修改尺寸触发的递归。
部署与编译注意事项
- 编译为exe时,使用
pyinstaller打包最新版本的CustomTkinter及依赖,示例命令:pyinstaller --onefile --windowed your_script.py - 部署时添加DPI感知初始化,确保不同系统缩放比例下的适配:
import ctypes try: ctypes.windll.shcore.SetProcessDpiAwareness(1) except: pass - 减少不必要的
update_idletasks()调用,避免增加UI更新开销导致延迟。
内容的提问来源于stack exchange,提问作者Alejandro
相关产品推荐
相关产品推荐

