如何解决Tkinter 自定义矩形小部件
我想创建一个自定义矩形小部件(如下所示),只需单击并移动鼠标即可调整大小。我为小部件创建了一个类:
from tkinter import *
class Rect(Canvas):
def __init__(self,parent,x1,y1,x2,y2,color = 'yellow',transparentcolor = 'grey',default="",**kwargs):
Canvas.__init__(self,parent)
self.parent=parent
self.canvas = Canvas(parent,width = x2+10,height = y2+10,bg='grey',cursor='hand2')
self.current= self.canvas
self.rect = self.canvas.create_rectangle(x1,width=5,outline=color)
self.corner1 = self.canvas.create_oval(x1-10,y1-10,x1+10,y1+10,fill=color) # Top-left
self.corner2 = self.canvas.create_oval(x2-10,x2+10,fill=color) # Top-right
self.corner3 = self.canvas.create_oval(x1-10,y2-10,y2+10,fill=color) # Below-left
self.corner4 = self.canvas.create_oval(x2-10,fill=color) # Below-right
self.canvas.grid()
输出:
这是我的完整代码:
from tkinter import *
from custom_rect import Rect
from tkinter import Canvas
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.rect = Rect(self.image,color='green')
self.image.tag_bind(self.rect,'<Button-1>',self.on_click_rectangle)
self.image.tag_bind(self.rect,'<Button1-Motion>',self.on_motion)
def on_click_rectangle(self,tag,event):
self.current = tag
global x1,y2
if abs(event.x-x1) < abs(event.x-x2):
x1,x2 = x2,x1
if abs(event.y-y1) < abs(event.y-y2):
y1,y2 = y2,y1
self.start = x1,y1
print(x1,y2)
def on_motion(self,event):
self.coords(self.rect,*self.start,event.x,event.y)
def main():
main = DrawCircles()
main.pack()
main.mainloop()
if __name__ == '__main__':
main()
_tkinter.TclError: invalid boolean operator in tag search expression
我不确定,但错误是由运动部件引起的吗?
解决方法
对于您想要实现的目标,您不应该使窗口透明。您可以在矩形的每个角上创建一个普通矩形和点(椭圆)并添加标签。
您还需要绑定按钮按下、运动,并不断检查鼠标是在矩形内还是在 4 个点内。
在下面的代码中,我将向您展示如何创建一个可调整大小的矩形,大部分代码取自我的 previous post :
import tkinter as tk
from PIL import Image,ImageTk
class Canvas(tk.Canvas):
TOP_LEFT = 0
TOP_RIGHT = 1
BOTTOM_LEFT = 3
BOTTOM_RIGHT = 4
cursors = {TOP_LEFT: 'size_nw_se',TOP_RIGHT: 'size_ne_sw',BOTTOM_LEFT: 'size_ne_sw',BOTTOM_RIGHT: 'size_nw_se'} # WINDOWS SPECIFIC CURSORS IN MAC IT MIGHT BE resizetopright,resizetopright etc
def __init__(self,*args,**kwargs):
super(Canvas,self).__init__(*args,**kwargs)
self.config(bg='#1e1e1e')
self._tag = 'resize' # not necessary you can remove all the tags
self.resizePoints = {} # stores the resize points
self.previous = (0,0) # previous mouse coordinates
self.bind('<Motion>',self.updateCursor)
self.bind('<1>',self.setResizePoint)
self.bind('<ButtonRelease-1>',self.release)
self.createResizeRect()
def createResizeRect(self): # adds a rect around the canvas item
color = '#008000'
self._current_resize_rect = self.create_rectangle(80,50,100,tags=(self._tag),outline=color,width=3) # draws rectangle
bbox = self.bbox(self._current_resize_rect)
# the below are the points at 4 corners of resize rect
self.resizePoints[self.TOP_LEFT] = self.create_oval(bbox[0]-5,bbox[1]-5,bbox[0]+5,bbox[1]+5,fill=color,tags=(self._tag))
self.resizePoints[self.TOP_RIGHT] = self.create_oval(bbox[2]-5,bbox[2]+5,tags=(self._tag))
self.resizePoints[self.BOTTOM_RIGHT] = self.create_oval(bbox[2]-5,bbox[3]-5,bbox[3]+5,tags=(self._tag))
self.resizePoints[self.BOTTOM_LEFT] = self.create_oval(bbox[0]-5,tags=(self._tag))
def updateCursor(self,event): # method that updates cursor when hovering over resize points
point = self.checkInPoints(event.x,event.y)
if point:
key = list(self.resizePoints.keys())[list(self.resizePoints.values()).index(point)]
self.config(cursor=self.cursors[key])
else:
self.config(cursor='')
def checkInPoints(self,x,y): # checks if the mouse is over the resizePoints
for item in self.resizePoints.values():
if self.check_in_bbox(item,y):
return item
return None
def check_in_bbox(self,item,y): # checks if (x,y) points are inside the bounding box
box = self.bbox(item)
return box[0] < x < box[2] and box[1] < y < box[3]
def setResizePoint(self,event):
self._current_point = self.checkInPoints(event.x,event.y)
if self._current_point is not None:
self.bind('<B1-Motion>',self.resize)
else:
self.previous = (event.x,event.y)
self.bind('<B1-Motion>',self.moveItem)
def release(self,event):
self.tag_unbind(self._tag,'<B1-Motion>')
self.unbind('<B1-Motion>')
def moveItem(self,event): # moves the canvas item
xc,yc = self.canvasx(event.x),self.canvasy(event.y)
self.move(self._current_resize_rect,xc-self.previous[0],yc-self.previous[1])
self.updateResizeRect()
self.previous = (xc,yc)
def updateResizeRect(self): # updates the position of the resize rectangle
new_coord = self.bbox(self._current_resize_rect)
# note: depending on your tkinter version moveto might not be available. So use the .coords method
# eg: coords(self.resizePoints[self.TOP_LEFT],new_coords[0]-5,new_coords[1]-5,new_coords[0]+5,new_coords[1]+5)
# check how the coords are assigned in the addRect method and adjust accordingly if your tkinter version does't have `moveto`
self.moveto(self.resizePoints[self.TOP_LEFT],new_coord[0]-5,new_coord[1]-5)
self.moveto(self.resizePoints[self.TOP_RIGHT],new_coord[2]-5,new_coord[1]-5)
self.moveto(self.resizePoints[self.BOTTOM_RIGHT],new_coord[3]-5)
self.moveto(self.resizePoints[self.BOTTOM_LEFT],new_coord[3]-5)
def resize(self,event): # resizes the canvas item
item_coords = self.coords(self._current_resize_rect)
if self.resizePoints[self.TOP_LEFT] == self._current_point:
self.coords(self._current_resize_rect,event.x,event.y,item_coords[2],item_coords[3])
elif self.resizePoints[self.TOP_RIGHT] == self._current_point:
self.coords(self._current_resize_rect,item_coords[0],item_coords[3])
elif self.resizePoints[self.BOTTOM_RIGHT] == self._current_point:
self.coords(self._current_resize_rect,item_coords[1],event.y)
elif self.resizePoints[self.BOTTOM_LEFT] == self._current_point:
self.coords(self._current_resize_rect,event.y)
self.updateResizeRect()
root = tk.Tk()
canvas = Canvas(root)
canvas.pack(fill='both',expand=True)
ph_image = tk.PhotoImage(file=r"image.png")
canvas.create_image(50,image=ph_image)
canvas.tag_raise(canvas._tag)
root.mainloop()
- 不要忘记
tag_raise()
否则矩形将在您的图像后面。
输出:
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。