Search code examples
pythontkintercustomtkinter

Why this code (drag ans drop widgets) works in Tkinter but not customtkinter? Any way I could make it work?


These 2 codes are exactly the same, the only difference is that 1 uses tkinter and the other one uses customtkinter

I am wondering how I could make the customtkinter code work. Is there something that I am missing or something I should change for the second code to work?

In the second code the "drag and drop" functions do not work, while in the first code (tkinter), everything works just fine. I have included a side note (#this changed) in the second code, to indentify what are the things that changed.

NOTE: the reason I add the comlete code is because all functions shown are needed for the "drag and drop" to work and I do not know where the mistake is.

See below:

With tkinter:

import tkinter

def changeOrder(widget1,widget2,initial):

    target=widget1.grid_info()
    widget1.grid(row=initial['row'],column=initial['column'])
    widget2.grid(row=target['row'],column=target['column'])

def on_click(event):

    widget=event.widget
    print(widget)
    if isinstance(widget,tkinter.Label):
        start=(event.x,event.y)
        grid_info=widget.grid_info()
        widget.bind("<B1-Motion>",lambda event:drag_motion(event,widget,start))
        widget.bind("<ButtonRelease-1>",lambda event:drag_release(event,widget,grid_info))
    else:
        root.unbind("<ButtonRelease-1>")

def drag_motion(event,widget,start):


    x = widget.winfo_x()+event.x-start[0]
    y = widget.winfo_y()+event.y-start[1]
    widget.lift()
    widget.place(x=x,y=y)

def drag_release(event,widget,grid_info):


    widget.lower()
    x,y=root.winfo_pointerxy()
    target_widget=root.winfo_containing(x,y)
    if isinstance(target_widget,tkinter.Label): 
        changeOrder(target_widget,widget,grid_info)
    else:
        widget.grid(row=grid_info['row'],column=grid_info['column'])

root = tkinter.Tk()
_entry={}
rows=3
columns = 2
for row in range(rows):
    for column in range(columns):
        index=(row,column)
        myTextLabel1 = tkinter.Label(root,text="Label 1",bg='yellow')
        myTextLabel1.grid(row=row,column=column,padx=5,pady=5)#sticky=E+W+S+N)
        _entry[index] = myTextLabel1


_entry[index].configure(bg='red')

root.bind("<Button-1>",on_click)

root.mainloop()

With Customtkinter:

#WITH CUSTOMTKINTER
import customtkinter

def changeOrder(widget1,widget2,initial):

    target=widget1.grid_info()
    widget1.grid(row=initial['row'],column=initial['column'])
    widget2.grid(row=target['row'],column=target['column'])

def on_click(event):

    widget=event.widget
    print(widget)
    if isinstance(widget,customtkinter.CTkLabel): #this changed
        start=(event.x,event.y)
        grid_info=widget.grid_info()
        widget.bind("<B1-Motion>",lambda event:drag_motion(event,widget,start))
        widget.bind("<ButtonRelease-1>",lambda event:drag_release(event,widget,grid_info))
    else:
        root.unbind("<ButtonRelease-1>")

def drag_motion(event,widget,start):


    x = widget.winfo_x()+event.x-start[0]
    y = widget.winfo_y()+event.y-start[1]
    widget.lift()
    widget.place(x=x,y=y)

def drag_release(event,widget,grid_info):


    widget.lower()
    x,y=root.winfo_pointerxy()
    target_widget=root.winfo_containing(x,y)
    if isinstance(target_widget,customtkinter.CTkLabel): #this changed
        changeOrder(target_widget,widget,grid_info)
    else:
        widget.grid(row=grid_info['row'],column=grid_info['column'])

root = customtkinter.CTk()
_entry={}
rows=3
columns = 2
for row in range(rows):
    for column in range(columns):
        index=(row,column)
        myTextLabel1 = customtkinter.CTkLabel(root,text="Label 1",bg_color='yellow') #this changed
        myTextLabel1.grid(row=row,column=column,padx=5,pady=5)#sticky=E+W+S+N)
        _entry[index] = myTextLabel1


_entry[index].configure(bg_color='red') #this changed

root.bind("<Button-1>",on_click)

root.mainloop()

Thank you in advance


Solution

  • Note that customtkinter widget is basically tkinter.Frame which contains other tkinter widgets inside it. For example, customtkinter.CTkLabel is a tkinter.Frame containing a tkinter.Canvas and tkinter.Label. So clicking on a customtkinter.Label will either clicking on the internal canvas or label based on the position of the mouse cursor actually.

    Therefore you need to check the parent of the clicked widget instead and move that parent instead of the clicked widget:

    ...
    
    def on_click(event):
        widget = event.widget
        # check widget.master instead
        if isinstance(widget.master, customtkinter.CTkLabel): #this changed
            start = (event.x, event.y)
            # use widget.master instead in below lines
            grid_info = widget.master.grid_info()
            widget.bind("<B1-Motion>", lambda event:drag_motion(event, widget.master, start))
            widget.bind("<ButtonRelease-1>", lambda event:drag_release(event, widget.master, grid_info))
        else:
            root.unbind("<ButtonRelease-1>")
    ...
    
    def drag_release(event, widget, grid_info):
        widget.lower()
        x, y = root.winfo_pointerxy()
        target_widget = root.winfo_containing(x, y)
        if target_widget.master != widget and isinstance(target_widget.master, customtkinter.CTkLabel): #this changed
            changeOrder(target_widget.master, widget, grid_info)
        else:
            widget.grid(row=grid_info['row'], column=grid_info['column'])
    
    ...