EJ Song
EJ Song

Reputation: 113

Drag and drop the object in Tkinter UI

I am working on making an HPE annotation tool using tkinter, and making a drag/drop UI.

I am very new to tkinter so I slightly modified the code in other stackoverflow issue and it is as following.

from tkinter import *
window = Tk()
window.state('zoomed')
window.configure(bg = 'white')

def drag(event):
    event.widget.place(x=event.x_root, y=event.y_root,anchor=CENTER)

card = Canvas(window, width=10, height=10, bg='blue2')
card.place(x=300, y=600,anchor=CENTER)
card.bind("<B1-Motion>", drag)

another_card = Canvas(window, width=10, height=10, bg='red3')
another_card.place(x=600, y=600,anchor=CENTER)
another_card.bind("<B1-Motion>", drag)

window.mainloop()

Here I observed that the card and another card goes down-right when I started to drag the objects. How can I solve it ?

Upvotes: 0

Views: 2329

Answers (2)

Tim Roberts
Tim Roberts

Reputation: 54620

Just to add an explanation, did you notice that the amount your dragging was off was equal to the position of the window on the screen? If the window was maximized, then your code was pretty close. If you reduced the size of the window and moved farther from the upper left, the delta got worse. That's because event.x_root and event.y_root are absolute coordinates, starting from the upper left of the screen, but the parameters to place are relative to the upper left of your window. You always need to be conscious of what your coordinates are relative to.

I came up with the following, but it's not any better than the TheLizzard's answer.

from tkinter import *
window = Tk()
window.state('zoomed')
window.configure(bg = 'white')

def drag(event):
    new_x = event.x_root - window.winfo_rootx()
    new_y = event.y_root - window.winfo_rooty()
    event.widget.place(x=new_x, y=new_y,anchor=CENTER)

card = Canvas(window, width=10, height=10, bg='blue2')
card.place(x=300, y=600,anchor=CENTER)
card.bind("<B1-Motion>", drag)

another_card = Canvas(window, width=10, height=10, bg='red3')
another_card.place(x=600, y=600,anchor=CENTER)
another_card.bind("<B1-Motion>", drag)

window.mainloop()

Upvotes: 2

TheLizzard
TheLizzard

Reputation: 7680

Try this:

from tkinter import *

window = Tk()
# window.state("zoomed")
window.configure(bg="white")

def drag(event):
    x = event.x + event.widget.winfo_x()
    y = event.y + event.widget.winfo_y()
    event.widget.place(x=x, y=y, anchor="center")

card = Canvas(window, width=10, height=10, bg="blue")
card.place(x=50, y=50, anchor="center")
card.bind("<B1-Motion>", drag)

another_card = Canvas(window, width=10, height=10, bg="red")
another_card.place(x=100, y=50, anchor="center")
another_card.bind("<B1-Motion>", drag)

window.mainloop()

event.x gives the x position of the cursor according to the widget.

event.widget.winfo_x() gives the x position of the widget according to the window.

By the way it would have been much simpler if you moved both of the widgets inside a canvas but it still works.

Upvotes: 3

Related Questions