I'm trying to make a listbox with the ability to do drag and drop onto a canvas. I've done drag and drop before, but it was only between canvas.create_text loosely based on the code for a this checkers program I found here. I've seen a couple of questions about drag and drop listboxes but they only deal with changing the order of elements in the listbox. What I'm dealing with is a listbox which has a list of names, and a canvas with some create_text objects on the canvas, and I want to be able to drag a name from the listbox onto the canvas. If figure I'd need to make a Listbox subclass, but I'm unsure of where to go from there.
So I've got a DialogWindow (subclass of Toplevel), and have my canvas and listbox in the DialogWindow. I've conjured up a way of getting a name from the listbox: when I click on a name, I convert it to a canvas.create_text object and then drag that. My issue is the drop. I try to use the canvas.canvasx to convert to canvas coordinates but it hasn't worked for me. x and y are still in listbox coordinates.
def onRelease(self, event):
x = self.canvas.canvasx(event.x)
y = self.canvas.canvasx(event.y)
print(event.x, event.y)
print(x, y) #Prints the same thing as the previous line
The key to drag and drop boils down to needing to do three things:
<ButtonPress-1>
to select the item to be dragged<B1-Motion>
to do the drag<ButtonRelease-1>
to do the dropNone of this requires any subclassing. All of these bindings are on the listbox widget. You'll probably want to create an instance of Toplevel
with a text label in it and the window decorations removed (using wm_overrideredirect(True)
) to represent the item being dragged.
On the drop, you'll need to convert the coordinates of the mouse to canvas coordinates using the canvasx
and canvasy
methods of the canvas. Instead of starting with event.x
and event.y
(which are relative to the listbox), use the winfo_pointerxy
method to get the screen coordinates of the mouse, then do a little math.
Here's an example of how you could do the drop:
def _on_drag_drop(self, event):
i = self.listbox.curselection()[0]
text = self.listbox.get(i)
wx, wy = self.canvas.winfo_rootx(), self.canvas.winfo_rooty()
x,y = self.winfo_pointerxy()
cx = self.canvas.canvasx(x-wx)
cy = self.canvas.canvasy(y-wy)
self.canvas.create_text(cx,cy,text=text, anchor="sw")
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With