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

Tkinter可调整大小左侧面板的可调整箭头指示器显示异常问题

问题原因

  • 缺少拖拽状态标识:按住左键拖动时鼠标容易移出Separator控件范围,触发事件隐藏箭头,与中的显示逻辑冲突,导致箭头不消失、位置不跟随的异常。
  • 坐标体系不统一:多处混用相对控件坐标、全局屏幕坐标,换算偏移导致箭头位置显示错误。
  • return (<Leave>事件无状态判断:只要鼠标离开Separator就直接隐藏箭头,拖拽过程中也会触发错误隐藏。
    )

修复方案

修改点如下:

  1. 新增拖拽状态变量self.dragging,标记当前是否正在调整面板宽度
  2. 绑定Separator的左键按下、左键松开事件,同步修改拖拽状态
  3. 调整_hide_divider逻辑,拖拽状态下不隐藏箭头,松开左键后再判断是否隐藏
  4. 统一坐标换算逻辑,移除不必要的全局鼠标坐标存储绑定
  5. 优化拖拽过程中的箭头位置更新逻辑,减少重复渲染,新增最小宽度限制避免面板被拖到过窄
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import tkinter as tk
import tkinter.ttk as ttk
    

class App(ttk.Frame):
    def __init__(self, master):
        self.master = master
        self.master.title('App')
        self.master.geometry('1500x140')
        # 新增拖拽状态标记
        self.dragging = False
        super().__init__(master, style='App.TFrame', borderwidth=20)
        self._set_style()
        self._create_widgets()

    def _set_style(self):
        self.style = ttk.Style()
        self.style.configure('App.TFrame', background='pink')
        self.style.configure('lframe.TFrame', background='green')
        self.style.configure('rframe.TFrame', background='orange')
        self.style.configure('TSeparator', background='red')

    def _create_widgets(self):
        self.lframe = ttk.Frame(self, style='lframe.TFrame', borderwidth=20)
        self.rframe = ttk.Frame(self, style='rframe.TFrame', borderwidth=20)
        self.divider = ttk.Separator(self, orient=tk.VERTICAL)
        self.lframe.grid(row=0, column=0, sticky='nsew')
        self.divider.grid(row=0, column=1, sticky='nsew', padx=20)
        self.rframe.grid(row=0, column=2, sticky='nsew')

        harrow = './resize-arrow-24.png'
        self.icon_harrow = tk.PhotoImage(file=harrow)
        self.harrow = ttk.Label(self, image=self.icon_harrow)
        self.harrow.place_forget()

        self.ltv = self._create_treeview(self.lframe)
        self.rtv = self._create_treeview(self.rframe)
        self.ltv.grid(row=0, column=0, sticky='nsew')
        self.rtv.grid(row=0, column=0, sticky='nsew')

        self.divider.bind('<Enter>', self._show_divider)
        self.divider.bind('<Leave>', self._hide_divider)
        self.divider.bind('<ButtonPress-1>', self._start_drag)
        self.divider.bind('<ButtonRelease-1>', self._end_drag)
        self.divider.bind("<B1-Motion>", self._button1_press_move)

    def _create_treeview(self, parent):
        # Create Treeview
        SearchCols = ('#01', '#02', '#03', '#04', '#05', '#06')
        tv = ttk.Treeview(parent, columns=SearchCols, height=2,
                          displaycolumn=['#05', '#06', '#01',
                                         '#02', '#03', '#04'],
                          style='search.Treeview',
                          selectmode='extended', takefocus=True)
        # Setup column & it's headings
        tv.column('#0', stretch=0, minwidth=100, width=100, anchor='w')
        tv.column('#01', stretch=0, anchor='n', width=70)
        tv.column('#02', stretch=0, anchor='n', width=80)
        tv.column('#03', stretch=0, anchor='n', width=75)
        tv.column('#04', stretch=0, anchor='w')
        tv.column('#05', stretch=0, anchor='e', width=80)
        tv.column('#06', stretch=0, anchor='n', width=70)
        tv.heading('#0', text=' Directory ', anchor='w')
        tv.heading('#01', text='#01', anchor='center')
        tv.heading('#02', text='#02', anchor='center')
        tv.heading('#03', text='#03', anchor='center')
        tv.heading('#04', text='#04', anchor='w')
        tv.heading('#05', text='#05', anchor='center')
        tv.heading('#06', text='#06', anchor='center')
        return tv

    # Event Handlers
    def _start_drag(self, event):
        self.dragging = True

    def _end_drag(self, event):
        self.dragging = False
        # 松开左键后判断鼠标是否还在分隔条上,不在就隐藏箭头
        if not (0 <= event.x < self.divider.winfo_width() and 0 <= event.y < self.divider.winfo_height()):
            self.harrow.place_forget()

    def _show_divider(self, event):
        # 直接从事件取当前坐标,换算为相对于App的坐标
        x = self.winfo_pointerx() - self.winfo_rootx()
        y = self.winfo_pointery() - self.winfo_rooty()
        self.harrow.place_configure(x=x-12, y=y-12)
        self.harrow.lower(belowThis=self.divider)

    def _hide_divider(self, event):
        # 拖拽状态下不隐藏箭头
        if not self.dragging:
            self.harrow.place_forget()

    def _button1_press_move(self, event):
        # 计算左侧面板新宽度,加最小宽度限制避免面板过窄
        new_width = self.winfo_pointerx() - self.lframe.winfo_rootx() - self.lframe['borderwidth']*2
        self.lframe['width'] = max(100, new_width)
        self.lframe.grid_propagate(0)
        # 更新箭头位置
        x = self.winfo_pointerx() - self.winfo_rootx()
        y = self.winfo_pointery() - self.winfo_rooty()
        self.harrow.place_configure(x=x-12, y=y-12)
        self.harrow.lower(belowThis=self.divider)

if __name__ == '__main__':
    root = tk.Tk()
    root.resizable(width=False, height=False)
    root.title('App')
    root.geometry('1300x400+0+24')
    root.rowconfigure(0, weight=1)
    root.columnconfigure(0, weight=1)

    app = App(root)
    app.grid(row=0, column=0, sticky='nsew')

    root.mainloop()

内容的提问来源于stack exchange,提问作者Sun Bear

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.27 23:54:06