Tkinter Canvas鼠标位置偏移及组件残留等问题排查
问题解决代码及说明
三个问题的修复方案
1. 网格偏移问题
每次生成新网格前,重置Canvas的缩放变换和视图位置,确保坐标从原始起点(x=30,y=30)开始:
- 清空Canvas所有元素后,将滚动视图移到初始位置
- 重置Canvas的缩放矩阵,消除之前缩放操作的影响
2. 鼠标点击涂黑错误
移除循环内的重复事件绑定,将<Button-1>事件绑定放在初始化阶段仅执行一次,避免多次触发干扰坐标判断。
3. Spinbox无法删除问题
用全局列表存储所有Spinbox的引用,每次生成新网格前,先销毁列表中的旧Spinbox,再清除Canvas元素(Canvas的delete("all")仅能删除绘图元素,无法自动销毁Widget对象)。
修改后的完整代码
import tkinter as tk from tkinter import ttk root = tk.Tk() btn_frame = tk.Frame(root) btn_frame.grid(row=0, column=0, columnspan=3) grille_frame = tk.Canvas(root, width=400, height=600) grille_frame.grid(row=1, column=0) yscrollbar_grille = ttk.Scrollbar(root, orient="vertical", command=grille_frame.yview) yscrollbar_grille.grid(row=0, column=3, rowspan=3, sticky="ns") xscrollbar_grille = ttk.Scrollbar(root, orient="horizontal", command=grille_frame.xview) xscrollbar_grille.grid(row=2, column=0, columnspan=3, sticky="ew") grille_frame.config(yscrollcommand=yscrollbar_grille.set, xscrollcommand=xscrollbar_grille.set) rows = 12 cols = 12 cell_size = 40 x = 30 y = 30 # 存储所有Spinbox引用,用于后续销毁 spinboxes = [] def zoom(): grille_frame.scale("all", 30, 30, 0.5, 0.5) scale_btn = tk.Button(btn_frame, text="scale", command=zoom) scale_btn.grid(row=0, column=0) def mouse_position(event): grille_frame.event_generate("<Motion>", warp=True, x=30, y=30) print("mouse position on canvas", event.x, event.y) print("mouse postition on screen", event.x_root, event.y_root) root.bind("<Key>", mouse_position) # 只绑定一次点击事件,避免重复绑定 grille_frame.bind("<Button-1>", click_1case) def matrice(): global spinboxes # 先销毁所有旧的Spinbox for spin in spinboxes: spin.destroy() spinboxes.clear() # 重置Canvas缩放和视图 grille_frame.delete("all") grille_frame.xview_moveto(0) grille_frame.yview_moveto(0) grille_frame.scale("all", 30, 30, 1, 1) y = 30 for r in range(rows): x = 30 spinbox_row = tk.Spinbox(grille_frame, from_=0, to=cols, width=2) grille_frame.create_window((x, y+cell_size/2), window=spinbox_row, anchor="e") spinboxes.append(spinbox_row) # 添加到引用列表 temp = [] for c in range(cols): cell = grille_frame.create_rectangle((x, y), (x + cell_size, y + cell_size), fill="white", outline= "black") temp.append(cell) x = x + cell_size y = y + cell_size x = y = 30 for c in range(cols): spinbox_col = tk.Spinbox(grille_frame, from_=0, to=rows, width=2) grille_frame.create_window((x+cell_size/2, y), window=spinbox_col, anchor="s") spinboxes.append(spinbox_col) # 添加到引用列表 x = x + cell_size grille_frame.update_idletasks() grille_frame.config(scrollregion=grille_frame.bbox("all")) matrice_btn = tk.Button(btn_frame, text="Matrice", command=matrice) matrice_btn.grid(row=0, column=2) def creer_hanjie(): """ affiche une grille de cases blanches """ # 重置Canvas缩放和视图 grille_frame.delete("all") grille_frame.xview_moveto(0) grille_frame.yview_moveto(0) grille_frame.scale("all", 30, 30, 1, 1) x = 30 y = 30 for r in range(rows): x = 30 for c in range(cols): cell = grille_frame.create_rectangle((x, y), (x + cell_size, y + cell_size ), fill="white", outline= "black") x = x + cell_size print("coordonnées case:", grille_frame.coords(cell)) y = y + cell_size grille_frame.update_idletasks() print("canvaas taille", grille_frame.bbox("all")) grille_frame.config(scrollregion=grille_frame.bbox("all")) grille_btn = tk.Button(btn_frame, text="create grid", command=creer_hanjie) grille_btn.grid(row=0, column=1) def click_1case(event): """ change la couleur de la case par click simple, 1 case à la fois """ print("clic souris", event.x, event.y) case = grille_frame.find_closest(event.x, event.y) couleur_actuelle = grille_frame.itemcget(case, "fill") if couleur_actuelle == "white": nouvelle_couleur = "black" else: nouvelle_couleur = "white" grille_frame.itemconfigure(case, fill=nouvelle_couleur) root.mainloop()
内容的提问来源于stack exchange,提问作者joMonk
相关产品推荐
相关产品推荐

