为同时具备垂直和水平滚动条的框架添加鼠标滚轮滚动支持
import tkinter as tk
root = tk.Tk()
root.geometry("400x300")
outer_frame = tk.Frame(root)
outer_frame.pack(fill="both", expand=True)
vertical_scrollbar = tk.Scrollbar(outer_frame, orient="vertical")
vertical_scrollbar.pack(side="right", fill="y")
horizontal_scrollbar = tk.Scrollbar(outer_frame, orient="horizontal")
horizontal_scrollbar.pack(side="bottom", fill="x")
canvas = tk.Canvas(outer_frame)
canvas.pack(side="left", fill="both", expand=True)
canvas.configure(yscrollcommand=vertical_scrollbar.set)
canvas.configure(xscrollcommand=horizontal_scrollbar.set)
inner_frame = tk.Frame(canvas)
inner_frame.bind("<Configure>", lambda event: canvas.configure(scrollregion=canvas.bbox("all")))
canvas.create_window((0, 0), window=inner_frame, anchor="nw")
vertical_scrollbar.configure(
command=lambda *args, **kwargs: None
if canvas.bbox("all")[3] <= canvas.winfo_height()
else canvas.yview(*args, **kwargs),
)
horizontal_scrollbar.configure(
command=lambda *args, **kwargs: None
if canvas.bbox("all")[2] <= canvas.winfo_width()
else canvas.xview(*args, **kwargs),
)
horizontal_scrollbar.update()
vertical_scrollbar.pack_configure(pady=(0, horizontal_scrollbar.winfo_height()))
# count_x, count_y = 1, 1
# count_x, count_y = 100, 1
# count_x, count_y = 1, 100
count_x, count_y = 100, 100
for i in range(count_y):
text = " ".join(f"Label{i + 1}" for _ in range(count_x))
tk.Label(inner_frame, text=text).pack(side="top", anchor="nw")
root.mainloop()
取决于 count_x 与 count_y 的取值,示例窗口有时会长成这样:

在这四种情景下
- 没有滚动
- 仅垂直滚动
- 仅水平滚动
- 同时具备垂直和水平滚动
通过滚动条滚动框架内容(或不滚动)的效果如预期。这也包括通过鼠标滚轮滚动(在水平滚动条的情况下,通过 Shift 搭配鼠标滚轮进行滚动)。
然而,当光标没有悬停在相应的滚动条上时,鼠标滚轮滚动将不起作用。
解决方案
由于在悬停滚动条时鼠标滚轮已经可以工作,我们可以在框架画布上监听相应的事件(用于垂直滚动条的是 <MouseWheel> 或 <Button-4>,取决于你的平台,并配合 <Button-5>;同样,水平滚动条使用 <Shift-MouseWheel> 或 <Shift-Button-4>,以及 <Shift-Button-5>),并将它们传播给滚动条。
for event_name in (
"<MouseWheel>",
"<Button-4>",
"<Button-5>",
"<Shift-MouseWheel>",
"<Shift-Button-4>",
"<Shift-Button-5>",
):
def _callback(event, event_name=event_name):
widget = event.widget
while widget:
if widget == canvas:
target = horizontal_scrollbar if "Shift" in event_name else vertical_scrollbar
kwargs = {"delta": event.delta} if "MouseWheel" in event_name else {}
return target.event_generate(event_name, **kwargs)
widget = widget.master
canvas.bind_all(event_name, _callback, add="+")
将此集成到上面的代码中,以获得一个完整可运行的示例:
import tkinter as tk
root = tk.Tk()
root.geometry("400x300")
outer_frame = tk.Frame(root)
outer_frame.pack(fill="both", expand=True)
vertical_scrollbar = tk.Scrollbar(outer_frame, orient="vertical")
vertical_scrollbar.pack(side="right", fill="y")
horizontal_scrollbar = tk.Scrollbar(outer_frame, orient="horizontal")
horizontal_scrollbar.pack(side="bottom", fill="x")
canvas = tk.Canvas(outer_frame)
canvas.pack(side="left", fill="both", expand=True)
canvas.configure(yscrollcommand=vertical_scrollbar.set)
canvas.configure(xscrollcommand=horizontal_scrollbar.set)
inner_frame = tk.Frame(canvas)
inner_frame.bind("<Configure>", lambda event: canvas.configure(scrollregion=canvas.bbox("all")))
canvas.create_window((0, 0), window=inner_frame, anchor="nw")
vertical_scrollbar.configure(
command=lambda *args, **kwargs: None
if canvas.bbox("all")[3] <= canvas.winfo_height()
else canvas.yview(*args, **kwargs),
)
horizontal_scrollbar.configure(
command=lambda *args, **kwargs: None
if canvas.bbox("all")[2] <= canvas.winfo_width()
else canvas.xview(*args, **kwargs),
)
horizontal_scrollbar.update()
vertical_scrollbar.pack_configure(pady=(0, horizontal_scrollbar.winfo_height()))
# count_x, count_y = 1, 1
# count_x, count_y = 100, 1
# count_x, count_y = 1, 100
count_x, count_y = 100, 100
for i in range(count_y):
text = " ".join(f"Label{i + 1}" for _ in range(count_x))
tk.Label(inner_frame, text=text).pack(side="top", anchor="nw")
for event_name in (
"<MouseWheel>",
"<Button-4>",
"<Button-5>",
"<Shift-MouseWheel>",
"<Shift-Button-4>",
"<Shift-Button-5>",
):
def _callback(event, event_name=event_name):
widget = event.widget
while widget:
if widget == canvas:
target = horizontal_scrollbar if "Shift" in event_name else vertical_scrollbar
kwargs = {"delta": event.delta} if "MouseWheel" in event_name else {}
return target.event_generate(event_name, **kwargs)
widget = widget.master
canvas.bind_all(event_name, _callback, add="+")
root.mainloop()
站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。