Tkinter自定义可拖拽调整矩形控件运行报错如何解决?
问题原因
你触发的_tkinter.TclError: invalid boolean operator in tag search expression错误和拖拽逻辑本身无关,是调用tag_bind时参数不符合要求导致的:
Canvas.tag_bind的第一个参数必须是当前Canvas上绘制的图形元素ID/预设的标签字符串,你传入的self.rect是自定义的Rect类实例,Tkinter无法将其识别为合法的图形标签,因此解析时报错。- 你的
Rect类设计冗余:本身已经继承了Canvas,不需要在内部再新建一个self.canvas属性存放新画布,所有绘制操作直接在自身实例上执行即可。 - 原有代码的
on_motion方法里调用self.coords也会报错:coords是Canvas的方法,不能直接通过Frame子类的实例调用。
修复方案
- 重构Rect类,去掉冗余的内部Canvas,给矩形和四个角的控制点绑定同一个自定义标签,方便后续统一绑定事件和调整坐标
- 绑定事件时使用统一的自定义标签
- 封装坐标更新方法,同步调整矩形和四个控制点的位置
修改后完整代码
custom_rect.py
from tkinter import * class Rect(Canvas): def __init__(self, parent,x1,y1,x2,y2,color = 'yellow',**kwargs): # 直接初始化继承的Canvas,不需要再新建内部Canvas super().__init__(parent, width = x2+10,height = y2+10,bg='grey',cursor='hand2',**kwargs) self.x1 = x1 self.y1 = y1 self.x2 = x2 self.y2 = y2 self.color = color # 给所有元素绑定同一个tag,方便统一操作 self.item_tag = "resizable_rect" self.rect = self.create_rectangle(x1,y1,x2,y2, width=5,outline=color, tags=self.item_tag) # 四个角的控制点也绑定同一个tag self.corner1 = self.create_oval(x1-10,y1-10,x1+10,y1+10,fill=color, tags=self.item_tag) # 左上角 self.corner2 = self.create_oval(x2-10,y1-10,x2+10,y1+10,fill=color, tags=self.item_tag) # 右上角 self.corner3 = self.create_oval(x1-10,y2-10,x1+10,y2+10,fill=color, tags=self.item_tag) # 左下角 self.corner4 = self.create_oval(x2-10,y2-10,x2+10,y2+10,fill=color, tags=self.item_tag) # 右下角 self.grid() # 封装调整坐标的方法,同步更新矩形和四个控制点的位置 def update_rect(self, new_x2, new_y2): self.coords(self.rect, self.x1, self.y1, new_x2, new_y2) self.coords(self.corner1, self.x1-10, self.y1-10, self.x1+10, self.y1+10) self.coords(self.corner2, new_x2-10, self.y1-10, new_x2+10, self.y1+10) self.coords(self.corner3, self.x1-10, new_y2-10, self.x1+10, new_y2+10) self.coords(self.corner4, new_x2-10, new_y2-10, new_x2+10, new_y2+10) self.x2 = new_x2 self.y2 = new_y2
主运行代码
from tkinter import * from custom_rect import Rect x1 = 12 y1 = 12 x2 = 400 y2 = 400 class DrawCircles(Frame): def __init__(self, master=None, **kwargs): super().__init__(master, **kwargs) self.image = Canvas(self, width=800,height=800) self.image.pack() self.rect = Rect(self.image,x1,y1,x2,y2,color='green') # 直接给Rect实例上的自定义标签绑定事件 self.rect.tag_bind(self.rect.item_tag, '<Button-1>', self.on_click_rectangle) self.rect.tag_bind(self.rect.item_tag, '<Button1-Motion>', self.on_motion) def on_click_rectangle(self, event): # 判断点击位置最近的角,交换坐标保证拖拽方向正确 if abs(event.x - self.rect.x1) < abs(event.x - self.rect.x2): self.rect.x1, self.rect.x2 = self.rect.x2, self.rect.x1 if abs(event.y - self.rect.y1) < abs(event.y - self.rect.y2): self.rect.y1, self.rect.y2 = self.rect.y2, self.rect.y1 def on_motion(self, event): # 调用Rect封装的更新方法同步更新所有元素坐标 self.rect.update_rect(event.x, event.y) def main(): root = Tk() app = DrawCircles(root) app.pack() root.mainloop() if __name__ == '__main__': main()
内容的提问来源于stack exchange,提问作者ablmmcu
相关产品推荐
相关产品推荐

