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

Tkinter ttk组件需失焦重获焦点或调窗口才更新问题求助

解决ttk Frame拖拽宽度实时更新问题

问题根源

  1. 仅更新self.width变量但未立即同步到Frame组件,手动while循环的更新逻辑与tkinter自身事件循环冲突
  2. Frame使用sticky="news"且窗口未配置列权重,导致Frame被强制填充列空间,手动设置的width被忽略
  3. 拖拽事件绑定在主窗口而非Frame上,坐标计算逻辑不准确

修改后的代码

from tkinter import ttk
from ttkthemes import ThemedTk
from tkinter import N, W

window = ThemedTk(theme="equilux")
window.resizable(height=None, width=None)
# 配置行权重,列权重设为0避免Frame被强制填充
window.rowconfigure(0, weight=1)
window.columnconfigure(0, weight=0)

class EventManager():
    def __init__(self, target_widget):
        self.events = {}
        self.eventsHandler = {}
        # 事件绑定到目标Widget而非主窗口
        self.eventHandlerCreater = lambda eventName: target_widget.bind(eventName, lambda e: [i(e) for i in self.events[eventName]])
    def registerEvent(self, event, function):
        if self.events.get(event, None) is None:
            self.events[event] = []
            self.eventsHandler[event] = self.eventHandlerCreater(event)
        self.events[event].append(function)

class Widget():
    def __init__(self):
        self.width = 500
        # 给Frame设置固定高度方便观察拖拽效果
        self.frame = ttk.Frame(window, width=self.width, height=400)
        # 取消sticky="news",仅让Frame顶左对齐
        self.frame.grid(row=0, column=0, sticky=N+W)
        self.frame.bind("<ButtonPress-1>", self.record_start_pos)
        self.initial_width = 0
        self.start_x = 0
        # 事件管理器绑定到Frame自身
        self.events = EventManager(self.frame)
        self.events.registerEvent("<B1-Motion>", self.resize)

    def record_start_pos(self, event):
        # 记录拖拽起始时的Frame宽度和鼠标位置
        self.initial_width = self.frame.winfo_width()
        self.start_x = event.x

    def resize(self, event):
        # 计算宽度变化量
        delta = event.x - self.start_x
        new_width = self.initial_width + delta
        # 限制最小宽度防止异常
        if new_width < 100:
            new_width = 100
        # 直接更新Frame宽度并实时刷新布局
        self.frame.config(width=new_width)
        self.frame.update_idletasks()

wid = Widget()
# 使用tkinter标准事件循环替代手动while循环
window.mainloop()

关键修改点

  • 将拖拽事件绑定到Frame自身,确保坐标计算基于Frame而非主窗口
  • 在resize函数中直接修改Frame的width并调用update_idletasks()实时刷新布局
  • 调整窗口列权重为0,取消Frame的sticky="news",避免Frame被强制填充列空间
  • 替换手动while循环为tkinter标准的mainloop(),避免事件循环冲突
  • 添加拖拽起始点记录,确保宽度计算更准确,同时限制最小宽度防止异常

内容的提问来源于stack exchange,提问作者Carson Coder

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 10:25:16