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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.01 02:20:26