Reputation: 98
Thus far I have:
Repurposed code from Drag and drop image object to allow multiple custom widgets to be draggable.
Repurposing the code has gone well with the exception of attempting to enforce draggable widgets to only move within the canvas dimensions.
If a single widget is added to the canvas the widget is draggable and bound to the canvas dimensions.
The Problem: If any number of widgets greater than one are added to the canvas, then only the last widget added is draggable and bound to the canvas dimensions. All other widgets fail to move due to logic in the mouse motion callback where the detected mouse motion is unusually large.
The draggable widgets are object-oriented, thus the logic for making a single draggable widget is identical to multiple other instantiations of the same widget type.
The manipulated code that I have thus far is:
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()
Upvotes: 1
Views: 348
Reputation: 386010
The root of the problem is this line of code:
self.canvas.bind("<Configure>", self.configure)
Whenever you call self.canvas.bind
, you replace any previous binding on the widget. Thus, when the binding is triggered, only the last CreateCanvasObject
will see the event because it will have replaced the bindings created by previous CreateCanvasObject
objects.
Because of that, you only ever update self.win_width
and self.win_height
of the last object created. And because you use those values to compute self.cond3
and self.cond4
, those conditions are always false and thus the object never moves.
A simple solution is to remove the binding and remove the configure
method, and instead compute the width and height inside your move
function. That, or make self.win_width
and self.win_height
class variables so when you update one you update all. Those aren't the only ways to solve the problem, but they are the simplest.
Upvotes: 1