tkinter中的小部件文本中断鼠标滚轮

问题描述

tkinter.Text小部件允许将其他小部件(例如按钮)与纯文本一起插入。

tkinter.Text小部件通过滚动内容来响应鼠标滚轮。但是,如果光标恰好位于子窗口小部件上,则该窗口小部件将获得鼠标滚轮事件,并且Text不会滚动。相反,我希望Text获得此鼠标滚轮事件。

什么是解决此问题的好方法


这是tkinter.Text内小部件的认行为,但这是一些代码来演示该问题。

import tkinter as tk
root = tk.Tk()
s = '\nTesting mouse wheel scroll with widgets inside tkinter.Text.\n'
txt = tk.Text(root,width=40,height=6)
for i in range(5):
    b = tk.Button(txt,text='I Break Scroll')
    txt.window_create(tk.END,window=b,padx=5,pady=5)
    txt.insert(tk.END,s)
txt.pack()
root.mainloop()

解决方法

MouseWheel事件发送到光标下方的小部件。这样就可以用鼠标控制多个可滚动的小部件。在较旧的tkinter版本中,它会以焦点滚动窗口。

对于不可滚动的小部件,没有默认行为。当您在按钮或标签上移动鼠标滚轮时,滚动将停止,因为事件将转到按钮或标签上而不是文本上。

您似乎不希望出现这种情况,因此需要为不可滚动的小部件的鼠标滚轮提供自己的绑定。如果将这些绑定应用于窗口小部件类而不是单个窗口小部件,则不必绑定到每个单独的窗口小部件。不过,您可以根据需要绑定到各个小部件。

下面是一个示例,该示例为ButtonLabel小部件类添加绑定以将事件传递给其父级。

import tkinter as tk

root = tk.Tk()
text = tk.Text(root,wrap="word")
vsb = tk.Scrollbar(root,command=text.yview)
text.configure(yscrollcommand=vsb.set)

vsb.pack(side="right",fill="y")
text.pack(side="left",fill="both",expand=True)

for i in range(200):
    text.insert("end",f"Item #{i}")
    if i%5 == 0:
        b = tk.Button(text,text=f"A button")
        text.window_create("end",window=b)
    elif i%3 == 0:
        l = tk.Button(text,text=f"A label")
        text.window_create("end",window=l)
    text.insert("end","\n")

def scroll_parent(event):
    parent = root.nametowidget(event.widget.winfo_parent())
    parent.event_generate("<MouseWheel>",delta=event.delta,when="now")

root.bind_class("Button","<MouseWheel>",scroll_parent)
root.bind_class("Label",scroll_parent)

root.mainloop()

注意:如果您使用的是基于X11的系统,则需要调整此代码以绑定到<Button-4><Button-5>而不是<MouseWheel>