如何拥有多个可拖动 canvas 小部件?
How to have multiple draggable canvas widgets?
到目前为止我有:
重新调整了 Drag and drop image object 中的代码以允许拖动多个自定义小部件。
除了尝试强制可拖动小部件仅在 canvas 维度内移动外,重新调整代码的用途进展顺利。
如果将单个小部件添加到 canvas,则小部件可拖动并绑定到 canvas 尺寸。
问题: 如果向 canvas 添加任意数量的大于 1 的小部件,则只有最后添加的小部件是可拖动的,并且绑定到 canvas 维度。 由于检测到鼠标运动异常大的鼠标运动回调中的逻辑,所有其他小部件无法移动。
可拖动小部件是面向对象的,因此制作单个可拖动小部件的逻辑与同一小部件类型的多个其他实例化相同。
我到目前为止的操作代码是:
import os
import tkinter as tk
APP_TITLE = "Drag & Drop Tk Canvas Images"
APP_XPOS = 100
APP_YPOS = 100
APP_WIDTH = 300
APP_HEIGHT = 200
class CreateCanvasObject(object):
def __init__(self, canvas, block_name):
self.canvas = canvas
self.block_width = 250
self.block_height = 250
self.name = block_name
self.max_speed = 30
self.win_width = self.canvas.winfo_width()
self.win_height = self.canvas.winfo_height()
self.block_main = tk.Frame(self.canvas, bd=10, relief=tk.RAISED)
self.block_main.pack(side=tk.TOP, fill=tk.BOTH, expand=False, padx=0, pady=0)
self.block_name = tk.Label(self.block_main, text=block_name, anchor=tk.CENTER, font='Helvetica 10 bold', cursor='fleur')
self.block_name.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
self.image_obj = self.canvas.create_window( (0, 0), window=self.block_main, anchor="nw", width=self.block_width, height=self.block_height)
self.block_name.bind( '<Button1-Motion>', self.move)
self.block_name.bind( '<ButtonRelease-1>', self.release)
self.move_flag = False
self.canvas.bind("<Configure>", self.configure)
def configure(self, event):
self.win_width = event.width
self.win_height = event.height
def move(self, event):
print('Moving [%s]'%self.name)
print(event)
if self.move_flag:
dx, dy = event.x, event.y
abs_coord_x, abs_coord_y = self.canvas.coords( self.image_obj )
cond_1 = abs_coord_x + dx >= 0
cond_2 = abs_coord_y + dy >= 0
cond_3 = (abs_coord_x + self.block_width + dx) <= self.win_width
cond_4 = (abs_coord_y + self.block_height + dy) <= self.win_height
print('abs_coord_x = %3.2f ; abs_coord_y = %3.2f'%(abs_coord_x, abs_coord_y))
print('dx = %3.2f ; dy = %3.2f'%(dx, dy))
print('self.block_width = %3.2f'%(self.block_width))
print('self.block_height = %3.2f'%(self.block_height))
print('Cond 1 = %s; Cond 2 = %s; Cond 3 = %s; Cond 4 = %s\n\n'%(cond_1, cond_2, cond_3, cond_4))
if cond_1 and cond_2 and cond_3 and cond_4:
self.canvas.move(self.image_obj, dx, dy)
else:
self.move_flag = True
self.canvas.tag_raise(self.image_obj)
def release(self, event):
self.move_flag = False
class Application(tk.Frame):
def __init__(self, master):
self.master = master
self.master.protocol("WM_DELETE_WINDOW", self.close)
tk.Frame.__init__(self, master)
self.canvas = tk.Canvas(self, width=800, height=800, bg='steelblue', highlightthickness=0)
self.canvas.pack(fill=tk.BOTH, expand=True)
self.block_1 = CreateCanvasObject(canvas=self.canvas, block_name='Thing 1')
self.block_2 = CreateCanvasObject(canvas=self.canvas, block_name='Thing 2')
def close(self):
print("Application-Shutdown")
self.master.destroy()
def main():
app_win = tk.Tk()
app_win.title(APP_TITLE)
app_win.geometry("+{}+{}".format(APP_XPOS, APP_YPOS))
Application(app_win).pack(fill='both', expand=True)
app_win.mainloop()
if __name__ == '__main__':
main()
问题的根源是这行代码:
self.canvas.bind("<Configure>", self.configure)
无论何时调用 self.canvas.bind
,您都会替换小部件上的任何先前绑定。因此,当绑定被触发时,只有最后一个 CreateCanvasObject
会看到该事件,因为它将替换之前 CreateCanvasObject
对象创建的绑定。
因此,您只能更新最后创建的对象的 self.win_width
和 self.win_height
。因为您使用这些值来计算 self.cond3
和 self.cond4
,这些条件始终为假,因此对象永远不会移动。
一个简单的解决方案是删除绑定并删除 configure
方法,而是在 move
函数中计算宽度和高度。那,或者制作 self.win_width
和 self.win_height
class 变量,这样当你更新一个时,你就更新了所有。这些不是解决问题的唯一方法,但它们是最简单的。
到目前为止我有:
重新调整了 Drag and drop image object 中的代码以允许拖动多个自定义小部件。
除了尝试强制可拖动小部件仅在 canvas 维度内移动外,重新调整代码的用途进展顺利。
如果将单个小部件添加到 canvas,则小部件可拖动并绑定到 canvas 尺寸。
问题: 如果向 canvas 添加任意数量的大于 1 的小部件,则只有最后添加的小部件是可拖动的,并且绑定到 canvas 维度。 由于检测到鼠标运动异常大的鼠标运动回调中的逻辑,所有其他小部件无法移动。
可拖动小部件是面向对象的,因此制作单个可拖动小部件的逻辑与同一小部件类型的多个其他实例化相同。
我到目前为止的操作代码是:
import os
import tkinter as tk
APP_TITLE = "Drag & Drop Tk Canvas Images"
APP_XPOS = 100
APP_YPOS = 100
APP_WIDTH = 300
APP_HEIGHT = 200
class CreateCanvasObject(object):
def __init__(self, canvas, block_name):
self.canvas = canvas
self.block_width = 250
self.block_height = 250
self.name = block_name
self.max_speed = 30
self.win_width = self.canvas.winfo_width()
self.win_height = self.canvas.winfo_height()
self.block_main = tk.Frame(self.canvas, bd=10, relief=tk.RAISED)
self.block_main.pack(side=tk.TOP, fill=tk.BOTH, expand=False, padx=0, pady=0)
self.block_name = tk.Label(self.block_main, text=block_name, anchor=tk.CENTER, font='Helvetica 10 bold', cursor='fleur')
self.block_name.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)
self.image_obj = self.canvas.create_window( (0, 0), window=self.block_main, anchor="nw", width=self.block_width, height=self.block_height)
self.block_name.bind( '<Button1-Motion>', self.move)
self.block_name.bind( '<ButtonRelease-1>', self.release)
self.move_flag = False
self.canvas.bind("<Configure>", self.configure)
def configure(self, event):
self.win_width = event.width
self.win_height = event.height
def move(self, event):
print('Moving [%s]'%self.name)
print(event)
if self.move_flag:
dx, dy = event.x, event.y
abs_coord_x, abs_coord_y = self.canvas.coords( self.image_obj )
cond_1 = abs_coord_x + dx >= 0
cond_2 = abs_coord_y + dy >= 0
cond_3 = (abs_coord_x + self.block_width + dx) <= self.win_width
cond_4 = (abs_coord_y + self.block_height + dy) <= self.win_height
print('abs_coord_x = %3.2f ; abs_coord_y = %3.2f'%(abs_coord_x, abs_coord_y))
print('dx = %3.2f ; dy = %3.2f'%(dx, dy))
print('self.block_width = %3.2f'%(self.block_width))
print('self.block_height = %3.2f'%(self.block_height))
print('Cond 1 = %s; Cond 2 = %s; Cond 3 = %s; Cond 4 = %s\n\n'%(cond_1, cond_2, cond_3, cond_4))
if cond_1 and cond_2 and cond_3 and cond_4:
self.canvas.move(self.image_obj, dx, dy)
else:
self.move_flag = True
self.canvas.tag_raise(self.image_obj)
def release(self, event):
self.move_flag = False
class Application(tk.Frame):
def __init__(self, master):
self.master = master
self.master.protocol("WM_DELETE_WINDOW", self.close)
tk.Frame.__init__(self, master)
self.canvas = tk.Canvas(self, width=800, height=800, bg='steelblue', highlightthickness=0)
self.canvas.pack(fill=tk.BOTH, expand=True)
self.block_1 = CreateCanvasObject(canvas=self.canvas, block_name='Thing 1')
self.block_2 = CreateCanvasObject(canvas=self.canvas, block_name='Thing 2')
def close(self):
print("Application-Shutdown")
self.master.destroy()
def main():
app_win = tk.Tk()
app_win.title(APP_TITLE)
app_win.geometry("+{}+{}".format(APP_XPOS, APP_YPOS))
Application(app_win).pack(fill='both', expand=True)
app_win.mainloop()
if __name__ == '__main__':
main()
问题的根源是这行代码:
self.canvas.bind("<Configure>", self.configure)
无论何时调用 self.canvas.bind
,您都会替换小部件上的任何先前绑定。因此,当绑定被触发时,只有最后一个 CreateCanvasObject
会看到该事件,因为它将替换之前 CreateCanvasObject
对象创建的绑定。
因此,您只能更新最后创建的对象的 self.win_width
和 self.win_height
。因为您使用这些值来计算 self.cond3
和 self.cond4
,这些条件始终为假,因此对象永远不会移动。
一个简单的解决方案是删除绑定并删除 configure
方法,而是在 move
函数中计算宽度和高度。那,或者制作 self.win_width
和 self.win_height
class 变量,这样当你更新一个时,你就更新了所有。这些不是解决问题的唯一方法,但它们是最简单的。