如何在没有窗口管理器的情况下在tkinter中从其表面的任何位置移动窗口



我试图复制代码的结构,使其只显示必要的内容。我会很简短,请帮帮我。让我解释一下,我希望用户能够通过点击窗口中的任何位置来移动他们想要的窗口,无论是框架、标签还是按钮。如果有人能帮我找到一个解决方案,或者如果没有结束我的希望,我会非常感激。我试图将每个小部件绑定到一个类似的方法来移动它,但代码会大幅增长。感谢

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()

需要注意的是,所有窗口都没有窗口管理器,我没有将其包含在代码中,因为所寻求的是能够将窗口从其表面的任何部分移动。

为了让代码正常工作,您需要更改一些内容。

首先,如果希望调用Move.__init__(self),则需要确保显式调用它。它不会自动调用。

class A (Frame, Move):
def __init__(self, master=None, **kwargs):
super().__init__(master, **kwargs)
Move.__init__(self)
...

严格地说,在这种特定的情况下,这是不必要的,因为__init__实际上并没有做任何重要的事情。由于函数的工作方式,即使没有调用__init__,在尝试使用它们之前,您也会初始化self._xself._y

其次,您需要添加绑定,以便在单击和移动鼠标时调用移动函数。有多种方法可以做到这一点,具体取决于您想要的行为。

如果您希望将行为附加到所有窗口中的所有窗口小部件,则可以使用bind_all。例如:

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)

如果你只想在额外的窗口上进行行为,你需要单独绑定它们(或者添加一个自定义绑定标记并绑定到它,但这超出了这个答案的范围)。这是因为每个小部件都继承了它所在窗口上的绑定

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)

第三,您需要修改Move类,这样函数就不必依赖于self.master.master。为此,可以使用方法winfo_toplevel,该方法返回与给定小部件关联的窗口。

例如:

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)

最新更新