如何在 tkinter canvas 中一起移动多个对象?

How to move multiple objects together in tkinter canvas?

我正在尝试使用鼠标拖放在 canvas 周围移动一些带有文本的矩形。我正在使用 find_overlapping 到 select 个要移动的矩形。这意味着最初作为 class 对象 Rect 的一部分创建的文本不会被移动。有没有办法修改我的代码以移动 class 对象中的所有对象或者使用 find_overlapping?

找到 class 对象 ID

矩形上的文本可以相同,如示例所示。用随机标签标记 class 对象中的所有元素以将它们组合在一起是我的第一个想法,但使用 find_ovelapping 检索此类标签信息并未成功。

import tkinter as tk

root=tk.Tk()
PAB=tk.Canvas(width=400, height=400)

#checks if a certain canvas object has a certain tag
def hastag(tag, id):
    if any(tag in i for i in PAB.gettags(id)):return True
    else:return False


class Rect:
    def __init__(self, x1, y1, name):
        rec = PAB.create_rectangle(x1,y1,x1+40,y1+40, fill='#c0c0c0', tag=('movable', name))
        text = PAB.create_text(x1+20,y1+20, text=name)
#mouse click find object to move
def get_it(event):
    delta=5
    global cur_rec
    for i in PAB.find_overlapping(event.x-delta, event.y-delta, event.x+delta, event.y-delta):
        if hastag('movable', i):
            cur_rec = i
    
PAB.bind('<Button-1>', get_it)

#mouse movement moves object
def move_it(event):
    xPos, yPos = event.x, event.y
    xObject, yObject = PAB.coords(cur_rec)[0],PAB.coords(cur_rec)[1]
    PAB.move(cur_rec, xPos-xObject, yPos-yObject)
PAB.bind('<B1-Motion>', move_it)

#test rects
bob = Rect(20,20,'Bob')
rob = Rect(80,80,'Rob')
different_bob = Rect(160,160,'Bob')

PAB.pack()
root.mainloop()

谢谢。如果需要任何说明,我很乐意提供帮助。

这可行,但我不确定这是最好的方法。

基本上它使用了文本被添加到矩形之后的 canvas 的事实,因此可以使用 cur_rec+1.

来识别它
def move_it(event):
    xPos, yPos = event.x, event.y
    xObject, yObject = PAB.coords(cur_rec)[0],PAB.coords(cur_rec)[1]
    # move rectangle
    PAB.move(cur_rec, xPos-xObject, yPos-yObject)
    # move text associated with rectangle
    PAB.move(cur_rec+1, xPos-xObject, yPos-yObject)

更好的方法是对要一起移动的所有项目使用相同的标签,因此在您的情况下,矩形和文本必须具有相同的标签。

import tkinter as tk

root=tk.Tk()
PAB=tk.Canvas(width=400, height=400, bg="gray")

class Rect:
    def __init__(self, x1, y1, name):

        tag = f"movable{id(self)}"
        rec = PAB.create_rectangle(x1,y1,x1+40,y1+40, fill='#c0c0c0', tag=(tag, ))
        text = PAB.create_text(x1+20,y1+20, text=name, tag=(tag,))

def in_bbox(event, item):  # checks if the mouse click is inside the item
    bbox = PAB.bbox(item)

    return bbox[0] < event.x < bbox[2] and bbox[1] < event.y < bbox[3]
    
#mouse click find object to move
def get_it(event):
    delta=5
    global cur_rec
    cur_rec = PAB.find_closest(event.x, event.y)  # returns the closest object

    if not in_bbox(event, cur_rec):  # if its not in bbox then sets current_rec as None
        cur_rec = None

#mouse movement moves object
def move_it(event):
    if cur_rec:
        xPos, yPos = event.x, event.y
        xObject, yObject = PAB.coords(cur_rec)[0],PAB.coords(cur_rec)[1]
                
        PAB.move(PAB.gettags(cur_rec)[0], xPos-xObject, yPos-yObject) 

PAB.bind('<Button-1>', get_it)
PAB.bind('<B1-Motion>', move_it)
#test rects
bob = Rect(20,20,'Bob')
rob = Rect(80,80,'Rob')
different_bob = Rect(160,160,'Bob')

PAB.pack()
root.mainloop()

对文本和矩形应用相同的标签。然后,调用move时使用标签。这是一种方法:

class Rect:
    def __init__(self, x1, y1, name):
        identifier = f"id:{id(self)}"
        rec = PAB.create_rectangle(x1,y1,x1+40,y1+40, fill='#c0c0c0', tags=('movable', name, identifier))
        text = PAB.create_text(x1+20,y1+20, text=name, tags=('movable', identifier))

然后您可以 return 标识符而不是所选项目的索引:

def get_it(event):
    delta=5
    global cur_rec
    for i in PAB.find_overlapping(event.x-delta, event.y-delta, event.x+delta, event.y-delta):
        if hastag('movable', i):
            identifier = [tag for tag in PAB.gettags(i) if tag.startswith("id:")][0]
            cur_rec = identifier