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

Tkinter Canvas鼠标滚轮无限滚动问题:如何限制滚动至画布边界

解决Tkinter Canvas鼠标滚轮无限滚动问题

问题现象

使用Tkinter开发界面时,Canvas通过滚动条可正常滚动至画布边界,但鼠标滚轮操作会出现无限滚动,无法被限制在画布上下边界内。

问题原因

当前鼠标滚轮事件处理函数直接调用canvas.yview_scroll(),该方法不会自动检查滚动边界;而滚动条能正常限制是因为它与Canvas的scrollregion属性绑定,会自动约束滚动范围。

修复方案

修改鼠标滚轮事件处理函数,通过计算滚动后的位置并将其限制在0到1的范围内(对应画布顶部到底部),使用canvas.yview_moveto()实现带边界约束的滚动。同时,由于代码中所有控件直接添加在Canvas上,需在添加完所有控件后手动更新scrollregion。

修改后的关键代码

def _on_mousewheel(event):
    # 获取当前滚动位置(范围0.0到1.0)
    current_pos = canvas.yview()[0]
    # 计算滚动步长,可根据需求调整数值(0.1对应每次滚动10%画布高度)
    scroll_step = -1 * (event.delta / 1200)
    # 限制新位置在0.0到1.0之间
    new_pos = max(0.0, min(1.0, current_pos + scroll_step))
    # 应用新滚动位置
    canvas.yview_moveto(new_pos)

# 绑定鼠标滚轮事件
canvas.bind_all("<MouseWheel>", _on_mousewheel)

# 手动设置scrollregion为所有内容的边界
canvas.configure(scrollregion=canvas.bbox("all"))

完整修复后的代码

import tkinter as tk
from tkinter import ttk
from pathlib import Path
from PIL import Image,ImageTk


OUTPUT_PATH = Path(__file__).parent
ASSETS_PATH = OUTPUT_PATH / Path(r"C:\Users\scien\build\assets\frame0")

def submitform():
    pass

def relative_to_assets(path: str) -> Path:
    return ASSETS_PATH / Path(path)


root = tk.Tk()
root.configure(bg="#c3ff84")
root.title("PIL App")
container = ttk.Frame(root)
canvas = tk.Canvas(container, height="700", width="1150", bg="#d6eccc")
scrollbar = ttk.Scrollbar(container, orient="vertical", command=canvas.yview)
scrollable_frame = ttk.Frame(canvas)

scrollwindow = canvas.create_window((0, 0), window=scrollable_frame, anchor="nw")

canvas.configure(yscrollcommand=scrollbar.set)

# 添加所有文本控件
canvas.create_text(
    32.0,
    0.0,
    anchor="nw",
    text="What is your job description?",
    fill="#000000",
    font=("Consolas", 36 * -1)
)

canvas.create_text(
    32.0,
    177.0,
    anchor="nw",
    text="Type in your full address here",
    fill="#000000",
    font=("Consolas", 36 * -1)
)

canvas.create_text(
    33.0,
    531.0,
    anchor="nw",
    text="What is the crime that was committed?",
    fill="#000000",
    font=("Consolas", 36 * -1)
)

canvas.create_text(
    32.0,
    708.0,
    anchor="nw",
    text="To which High Court are you filing this PIL?",
    fill="#000000",
    font=("Consolas", 36 * -1)
)

canvas.create_text(
    32.0,
    1081.0,
    anchor="nw",
    text="How did you find about this crime?",
    fill="#000000",
    font=("Consolas", 36 * -1)
)

canvas.create_text(
    33.0,
    1258.0,
    anchor="nw",
    text="What is the damage caused by the crime?",
    fill="#000000",
    font=("Consolas", 36 * -1)
)

canvas.create_text(
    33.0,
    1630.0,
    anchor="nw",
    text="Have any representations been made on \nthis topic before? ",
    fill="#000000",
    font=("Consolas", 36 * -1)
)

canvas.create_text(
    32.0,
    1415.0,
    anchor="nw",
    text="How did you find out about this crime? \nWhat is the source of information?",
    fill="#000000",
    font=("Consolas", 36 * -1)
)

canvas.create_text(
    32.0,
    875.0,
    anchor="nw",
    text="Write brief facts about the matter \non which this PIL is being filed",
    fill="#000000",
    font=("Consolas", 36 * -1)
)

canvas.create_text(
    33.0,
    354.0,
    anchor="nw",
    text="Who/What business are you filing this PIL against?",
    fill="#000000",
    font=("Consolas", 36 * -1)
)    

# 添加所有输入框控件
entry_image_1 = tk.PhotoImage(
    file=relative_to_assets("entry_1.png"))
entry_bg_1 = canvas.create_image(
    455.0,
    113.0,
    image=entry_image_1)

entry1 = tk.Entry(canvas, bd= 2, relief="flat", width="45", font=('Consolas 24') )
canvas.create_window(455, 113, window= entry1)

entry_image_2 = tk.PhotoImage(
    file=relative_to_assets("entry_2.png"))
entry_bg_2 = canvas.create_image(
    456.0,
    290.0,
    image=entry_image_2
    
)
entry2 = tk.Entry(canvas, bd= 2, relief="flat", width="45", font=('Consolas 24') )
canvas.create_window(455, 290, window= entry2)

entry_image_3 = tk.PhotoImage(
    file=relative_to_assets("entry_3.png"))
entry_bg_3 = canvas.create_image(
    456.0,
    467.0,
    image=entry_image_3
)   
entry3 = tk.Entry(canvas, bd= 2, relief="flat", width="45", font=('Consolas 24') )
canvas.create_window(455, 467, window= entry3)

entry_image_4 = tk.PhotoImage(
    file=relative_to_assets("entry_4.png"))
entry_bg_4 = canvas.create_image(
    455.0,
    644.0,
    image=entry_image_4
)

entry4 = tk.Entry(canvas, bd= 2, relief="flat", width="45", font=('Consolas 24') )
canvas.create_window(455, 644, window= entry4)

entry_image_5 = tk.PhotoImage(
    file=relative_to_assets("entry_5.png"))
entry_bg_5 = canvas.create_image(
    456.0,
    821.0,
    image=entry_image_5
)

entry5 = tk.Entry(canvas, bd= 2, relief="flat", width="45", font=('Consolas 24') )
canvas.create_window(455, 821, window= entry5)

entry_image_6 = tk.PhotoImage(
    file=relative_to_assets("entry_6.png"))
entry_bg_6 = canvas.create_image(
    455.0,
    998.0,
    image=entry_image_6
)

entry6 = tk.Entry(canvas, bd= 2, relief="flat", width="45", font=('Consolas 24') )
canvas.create_window(455, 998, window= entry6)

entry_image_7 = tk.PhotoImage(
    file=relative_to_assets("entry_7.png"))
entry_bg_7 = canvas.create_image(
    455.0,
    1194.0,
    image=entry_image_7
)

entry7 = tk.Entry(canvas, bd= 2, relief="flat", width="45", font=('Consolas 24') )
canvas.create_window(455, 1194, window= entry7)

entry_image_8 = tk.PhotoImage(
    file=relative_to_assets("entry_8.png"))
entry_bg_8 = canvas.create_image(
    456.0,
    1371.0,
    image=entry_image_8
)

entry8 = tk.Entry(canvas, bd= 2, relief="flat", width="45", font=('Consolas 24') )
canvas.create_window(455, 1371, window= entry8)

entry_image_9 = tk.PhotoImage(
    file=relative_to_assets("entry_9.png"))
entry_bg_9 = canvas.create_image(
    455.0,
    1548.0,
    image=entry_image_9
)

entry9 = tk.Entry(canvas, bd= 2, relief="flat", width="45", font=('Consolas 24') )
canvas.create_window(455, 1548, window= entry9)

entry_image_10 = tk.PhotoImage(
    file=relative_to_assets("entry_10.png"))
entry_bg_10 = canvas.create_image(
    455.0,
    1757.0,
    image=entry_image_10
)

entry10 = tk.Entry(canvas, bd= 2, relief="flat", width="45", font=('Consolas 24') )
canvas.create_window(455, 1757, window= entry10)

# 添加提交按钮
submit_imagesource = Image.open(relative_to_assets("submitbutton2.png"))
resize = submit_imagesource.resize((276,100), Image.ANTIALIAS)
submit_image = ImageTk.PhotoImage(resize)

submit = tk.Button(canvas, text="Submit", font=('Consolas 24'),image= submit_image, command= submitform, 
                   relief="flat", bg="white", borderwidth=0, activebackground="#d6eccc", background="#d6eccc" )
canvas.create_window(580, 1850, window= submit)

imageup = ImageTk.PhotoImage(Image.open(relative_to_assets("uptop.png")))

label1 = tk.Label(root, text="PIL App", font=("Consolas", 26), bg="#c3ff84")
label1.pack(side="top")

# 鼠标滚轮事件处理函数
def _on_mousewheel(event):
    current_pos = canvas.yview()[0]
    scroll_step = -1 * (event.delta / 1200)
    new_pos = max(0.0, min(1.0, current_pos + scroll_step))
    canvas.yview_moveto(new_pos)
    
canvas.bind_all("<MouseWheel>", _on_mousewheel)

# 手动设置滚动区域
canvas.configure(scrollregion=canvas.bbox("all"))

container.pack()
canvas.pack(side="left", fill="both", expand=True,)
scrollbar.pack(side="right", fill="y")

root.resizable(False, False)

root.mainloop()

额外说明

  1. 移除了原代码中无用的scrollable_frame绑定事件,因为所有控件直接添加在Canvas上,该绑定不会生效。
  2. 修正了submit按钮的command参数,原代码中command=submitform()会直接执行函数,改为command=submitform才是正确的绑定方式。

内容的提问来源于stack exchange,提问作者M.Pranav Kiruthik

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 12:52:31