Python Tkinter 如何实现仅拖动canvas内绘制的多边形而不移动画布
问题根因
你参考的拖动教程是针对独立Tkinter控件(如按钮、Frame)的实现逻辑,而你在Canvas上绘制的多边形属于画布内部图形元素,不是独立控件:
- 你给图形绑定事件后,触发事件时
e.widget返回的是画布对象本身,不是被点击的多边形 - 你代码里调用的
place()方法是用来调整控件位置的,所以实际移动的是整个画布,才会出现全局移动的问题
修复方案
只需要修改两个拖动相关的函数,改用Canvas自带的元素操作接口实现单个图形移动:
- 点击时记录起始坐标和当前选中的图形ID
- 拖动时计算鼠标偏移量,调用
canvas.move()方法仅移动选中的图形
修复后可运行代码
#!/usr/bin/python import re import sys import os import tkinter as tk HEIGHT = 1200 WIDTH = 1600 # 补充原代码缺失的pipeline空实现,避免运行报错 def pipeline(canvas): # 你的业务逻辑可在此处实现 pass def domain_button(canvas): domain = canvas.create_polygon([420, 100, 420, 100, 420, 175, 420, 175, 380, 175, 380, 120, 420, 120], outline='#000000',fill='#007ECB', width=2, tags="obj1Tag") canvas.tag_bind(domain,"<Button-1>", drag_start) canvas.tag_bind(domain,"<B1-Motion>", drag_motion) def button_hover(e): button["bg"] = "white" status_label.config(text="Get Input") def button_hover_leave(e): button["bg"] = "grey" status_label.config(text="") def button_hover_polygon(label): status_label.config(text=label) def button_hover_polygon_leave(e): status_label.config(text="") # 修改后的拖动起始函数 def drag_start(e): canvas = e.widget # 记录鼠标起始坐标 canvas.startX = e.x canvas.startY = e.y # 获取当前点击的图形元素ID canvas.selected_item = canvas.find_closest(e.x, e.y)[0] # 修改后的拖动逻辑函数 def drag_motion(e): canvas = e.widget # 计算鼠标偏移量 dx = e.x - canvas.startX dy = e.y - canvas.startY # 仅移动选中的图形元素 canvas.move(canvas.selected_item, dx, dy) # 更新起始坐标为当前位置,避免偏移错误 canvas.startX = e.x canvas.startY = e.y ###############Main programme####################### root = tk.Tk() canvas = tk.Canvas(root, height=HEIGHT, width=WIDTH, bg='#E7E0E6') canvas.pack() frame = tk.Frame(root, bg = '#80c1ff',bd=5) frame.place(relwidth=1,relheight=1) ###Input box textBox = tk.Text(frame, font=40) textBox.place(relx=0.01,rely = 0.05, relwidth=0.4,relheight=0.3) ###Option box frame2 = tk.Frame(frame, bg = '#D3D3D3') frame2.place(relx=0.01, rely = 0.45,relheight = 0.35, relwidth = 0.4) InsertVLDomainButton= tk.Button(frame2,text="insert domain",bg = "grey", command=lambda: domain_button(lower_canvas)) InsertVLDomainButton.place(relx = 0.01, rely = 0.01, relheight = 0.2, relwidth=0.2) status_label = tk.Label(root, text='test', bd=1) status_label.place(rely = 0.98, relheight = 0.02, relwidth = 1) ##Big button button = tk.Button(frame, text = "Go!", bg = "grey", font=40, command=lambda: pipeline(lower_canvas)) button.place(relx=0.1,rely=0.85,relheight=0.1, relwidth=0.2) button.bind("<Enter>", button_hover) button.bind("<Leave>", button_hover_leave) ###Results canvas lower_frame = tk.Frame(root, bg = '#80c1ff', bd=10) lower_frame.place(relx=0.45, rely=0.05, relwidth=0.55,relheight=0.9) lower_canvas = tk.Canvas(lower_frame) lower_canvas.place(relheight=1,relwidth=1) root.mainloop()
内容的提问来源于stack exchange,提问作者JSJ998
相关产品推荐
相关产品推荐

