I have tried to replicate the structure of the code in such a way that it only shows what is necessary. I'll be brief, please help me. Let me explain, I am looking for the user to be able to move the window they want by clicking anywhere in the window, be it a frame, a label or a button. If anyone could help me find a solution or if not put an end to my hopes, I would really appreciate it. I tried to bind each widget to a similar method to be able to move it, but the code would grow enormously. Thanks
from tkinter import *
class Move():
def __init__(self):
self._x = 0
self._y = 0
def start_move2(self, event):
self._x = event.x
self._y = event.y
def stop_move2(self, event):
self._x = None
self._y = None
def on_move2(self, event):
deltax = event.x - self._x
deltay = event.y - self._y
new_position = " {} {}".format(self.master.master.winfo_x() deltax, self.master.master.winfo_y() deltay)
self.master.master.geometry(new_position)
self.master.master.master.geometry(new_position)
class A (Frame, Move):
def __init__(self, master=None, **kwargs):
super().__init__(master, **kwargs)
self.master = master
self.btn = Button(self, text='opens', command=self.open)
self.btn .pack()
def open(self):
self.w1 = Toplevel(self.master)
self.w2 = Toplevel(self.master)
self.w1 .geometry('300x300')
self.w2 .geometry('300x300')
self.frm1 = Frame(self.w1, bg='green')
self.frm2 = Frame(self.w2, bg='blue')
self.lbl1 = Label(self.frm1, text='windows 1', bg='green2')
self.lbl2 = Label(self.frm2, text='windows 2', bg='gray')
self.frm1 .pack()
self.frm2 .pack()
self.lbl1 .pack()
self.lbl2 .pack()
root = Tk()
app = A(root, bg='black')
app .pack()
root .mainloop()
It should be noted that all the windows are without the window manager, I did not include it in the code because what is sought is to be able to move the window from any part of its surface.
CodePudding user response:
There are a couple of things you need to change in order for your code to work.
First, you need to make sure to call Move.__init__(self)
explicitly if you want it to be called. It won't be called automatically.
class A (Frame, Move):
def __init__(self, master=None, **kwargs):
super().__init__(master, **kwargs)
Move.__init__(self)
...
Strictly speaking this isn't necessary in this specific case, because the __init__
doesn't really do anything important. Because of how the functions work, you'll end up initializing self._x
and self._y
before you try to use them even if the __init__
isn't called.
Second, you need to add bindings so that the movement functions are called when the mouse is clicked and moved. There are multiple ways to do that, depending on the behavior you want.
If you want the behavior to be attached to literally all widgets in all windows, you can use bind_all
. For example:
class A (Frame, Move):
def __init__(self, master=None, **kwargs):
...
self.bind_all("<ButtonPress-1>", self.start_move2)
self.bind_all("<B1-Motion>", self.on_move2)
self.bind_all("<ButtonRelease-1>", self.stop_move2)
If you only want the behavior to be on the extra windows, you'll need to bind them individually (or add a custom bind tag and bind to it, but that's beyond the scope of this answer). This works because every widget inherits the bindings on the window it is in.
def open(self):
self.w1 = Toplevel(self.master)
self.w2 = Toplevel(self.master)
self.w1.bind("<ButtonPress-1>", self.start_move2)
self.w1.bind("<B1-Motion>", self.on_move2)
self.w1.bind("<ButtonRelease-1>", self.stop_move2)
self.w2.bind("<ButtonPress-1>", self.start_move2)
self.w2.bind("<B1-Motion>", self.on_move2)
self.w2.bind("<ButtonRelease-1>", self.stop_move2)
Third, you need to modify your Move
class so that the functions don't have to depend on self.master.master
. To do that, you can use the method winfo_toplevel
which returns the window associated with the given widget.
For example:
def on_move2(self, event):
deltax = event.x - self._x
deltay = event.y - self._y
win = event.widget.winfo_toplevel()
new_position = " {} {}".format(win.winfo_x() deltax, win.winfo_y() deltay)
win.geometry(new_position)