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()
额外说明
- 移除了原代码中无用的
scrollable_frame绑定事件,因为所有控件直接添加在Canvas上,该绑定不会生效。 - 修正了
submit按钮的command参数,原代码中command=submitform()会直接执行函数,改为command=submitform才是正确的绑定方式。
内容的提问来源于stack exchange,提问作者M.Pranav Kiruthik
相关产品推荐
相关产品推荐

