Search code examples
pythontkinterdrag-and-drop

Tkinter Drag & Drop Mouse Pointer Problem


I'm working on a minesweeper game and wanted to make a custom title bar. Everything works fine so far, but I have a little weird bug on the drag & drop mechanism.

def drag_app(event):
    x = event.x + root.winfo_x()
    y = event.y + root.winfo_y()
    root.geometry(f"+{x}+{y}")


test.bind("<B1-Motion>", drag_app)

That's my code so far. For some reason, when I start to drag, the mouse hops to the top-left part of the clicked widget (in this case "test", which is just a label).

I'm using grid to manage the program

Thanks in advance

EDIT (Minimal, Reproducible Example):

from tkinter import *

root = Tk()
root.title("Mineswepper")
root.overrideredirect(True)
root.resizable(False, False)
mainframe = Frame(root, bg="#41a2cd") 
mainframe.grid(column=0, row=0, sticky=(N, W, E, S))
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)

title_bar = Frame(mainframe, bg="#41a2cd", relief="raised")
title_bar.grid(row=0, column=0)
test = Label(title_bar, text="titlebar")
test.grid(row=0, column=0)

example_button = Button(mainframe, text="minesweeper", font=("helvetica", 20))
example_button.grid(row=1, column=0)

def drag_app(event):
    x = event.x + root.winfo_x()
    y = event.y + root.winfo_y()
    root.geometry(f"+{x}+{y}")

test.bind("<B1-Motion>", drag_app)

root.mainloop()

in this example you can drag the window with clicking on the label "titlebar"


Solution

  • You have not provided your complete, reproducible code. It is hard to help without it. Assuming you are trying to drag your window with the custom title bar.

    Solution

    • Record where exactly on the widget the mouse is clicked
    • Set geometry of the window to be mouse position + starting position

    Example

    import tkinter as tk
    
    
    class Window(tk.Tk):
        def __init__(self, master=None, *args ,**kwargs):
            super().__init__(master, *args, **kwargs)
    
            self.overrideredirect(True)
            self.geometry('400x100+200+200')
    
            title_bar = tk.Frame(self, bg='grey')
            title_bar.pack(fill=tk.X, side=tk.TOP)
    
            close_button = tk.Button(title_bar, text='⤬', bg='darkgrey')
            close_button.config(command=self.destroy, relief=tk.FLAT)
            close_button.pack(side=tk.RIGHT)
    
            title_bar.bind('<B1-Motion>', self.move_window)
            title_bar.bind('<Button-1>', self.get_pos)
    
        def get_pos(self, event):
            self.ywin = self.winfo_x() - event.x_root
            self.xwin = self.winfo_y() - event.y_root
    
        def move_window(self, event):
            self.geometry("+{0}+{1}".format(event.x_root + self.xwin, event.y_root + self.ywin))
    
    
    root = Window()
    root.mainloop()
    

    Complete code

    # try not to import globally
    import tkinter as tk
    
    root = tk.Tk()
    root.title("Mineswepper")
    root.overrideredirect(True)
    root.resizable(False, False)
    
    mainframe = tk.Frame(root, bg="#41a2cd") 
    mainframe.grid(column=0, row=0, sticky=(tk.N, tk.W, tk.E, tk.S))
    root.columnconfigure(0, weight=1)
    root.rowconfigure(0, weight=1)
    
    title_bar = tk.Frame(mainframe, bg="#41a2cd", relief="raised")
    title_bar.grid(row=0, column=0)
    test = tk.Label(title_bar, text="titlebar")
    test.grid(row=0, column=0)
    
    example_button = tk.Button(mainframe, text="minesweeper", font=("helvetica", 20))
    example_button.grid(row=1, column=0)
    
    xwin = 0
    ywin = 0
    
    def refresh_pos(event):
        global xwin, ywin
    
        xwin = root.winfo_x() - event.x_root
        ywin = root.winfo_y() - event.y_root
    
    def drag_app(event):
        root.geometry("+{0}+{1}".format(event.x_root + xwin, event.y_root + ywin))
    
    test.bind('<Button-1>', refresh_pos)
    test.bind("<B1-Motion>", drag_app)
    
    root.mainloop()