Tkinter Toplevel窗口不可移动

时间:2017-08-08 15:36:03

标签: python tkinter

在我的Tkinter应用程序中,我有一个按钮,可以打开一个Toplevel窗口来显示事件日志。我需要Toplevel Window能够做的一些事情:

  1. 打开时显示以前的日志条目,并使用新条目进行更新。
  2. 禁用用户随时移动窗口的能力,同时让用户能够关闭窗口
  3. 让窗口始终固定,右上角位于根窗口的右上角
  4. 我已经找到了#1。我可以打开窗口并显示以前的条目,并在窗口打开时更新这些条目。我的问题是#2和#3。

    对于#2我不知道如何禁用用户移动窗口的能力。我假设这也可能会禁用用户关闭窗口的能力,因此我不确定如何保持该功能完好无损。也许是一个按self.quit()的按钮?

    至于#3,我不知道如何去做。也许我像谷歌搜索一样,但我似乎无法找到如何实现这一点。

    这是我目前的代码,它能够正确实现功能#1。

    import tkinter as tk
    
    class guiapp(tk.Frame):
    
        def __init__(self, master):
            tk.Frame.__init__(self, master)
            self.master = master
            self.value = 0.0
            self.alive = True
            self.list_for_toplevel = []
            btn = tk.Button(self.master, text = "Click", command = self.TextWindow)
            btn.pack()
    
        def TextWindow(self):
            self.textWindow = tk.Toplevel(self.master)
            self.textFrame = tk.Frame(self.textWindow)
            self.textFrame.pack()
            self.textArea = tk.Text(self.textWindow, height = 10, width = 30)
            self.textArea.pack(side = "left", fill = "y")
    
            bar = tk.Scrollbar(self.textWindow)
            bar.pack(side = "right", fill = "y")
            bar.config(command = self.textArea.yview)
            self.alive = True
            self.timed_loop()
    
        def timed_loop(self):
            if self.alive == True and tk.Toplevel.winfo_exists(self.textWindow):
                self.master.after(1000, self.timed_loop)
                self.value += 1
                self.list_for_toplevel.append(self.value)
                self.textArea.delete(1.0, "end-1c")
                for item in self.list_for_toplevel:
                    self.textArea.insert('end', "{}\n".format(item))
                    self.textArea.see('end')
            else:
                self.alive = False
    
    if __name__ == "__main__":
    
        root = tk.Tk()
        root.geometry("800x480")
        myapp = guiapp(root)
        root.mainloop()
    

1 个答案:

答案 0 :(得分:1)

我们可以从toplevel窗口的顶部删除工具栏,并阻止用户使用self.textWindow.overrideredirect(True)移动窗口。

然后,我们可以通过获取根窗口位置确保toplevel窗口位于右上角,然后将toplevel窗口设置为self.master.winfo_x()的同一位置, self.master.winfo_y()

最后我会添加一个关闭窗口的按钮,因为我们不再拥有toplevel窗口的工具栏。

更新:我添加了toplevel窗口保持在根窗口顶部的功能,并在拖动root时使用根窗口移动。

我们可以使用bind()来跟踪移动根窗口的时间,然后使用一个函数来更新toplevel窗口位置以匹配根窗口。

我们还可以使用self.textWindow.attributes("-topmost", True)告诉tkinter在所有其他窗口之上保留toplevel窗口。

请查看下面代码的修改版本。让我知道您的想法或者您是否有任何问题。

import tkinter as tk

class guiapp(tk.Frame):

    def __init__(self, master):
        tk.Frame.__init__(self, master)
        self.master = master
        self.textWindow = None
        self.master.bind("<Configure>", self.move_me)
        self.value = 0.0
        self.list_for_toplevel = []
        btn = tk.Button(self.master, text = "Click", command = self.TextWindow)
        btn.pack()

    def TextWindow(self):
        x = self.master.winfo_x()
        y = self.master.winfo_y()

        self.textWindow = tk.Toplevel(self.master)
        self.textFrame = tk.Frame(self.textWindow)
        self.textWindow.overrideredirect(True)
        self.textFrame.pack()
        self.textWindow.attributes("-topmost", True)

        self.textWindow.geometry('+{}+{}'.format(x+10, y+30))
        self.close_toplevel = tk.Button(self.textWindow, text = "close", command = self.close_textWindow)
        self.close_toplevel.pack()
        self.textArea = tk.Text(self.textWindow, height = 10, width = 30)
        self.textArea.pack(side = "left", fill = "y")

        bar = tk.Scrollbar(self.textWindow)
        bar.pack(side = "right", fill = "y")
        bar.config(command = self.textArea.yview)
        self.alive = True
        self.timed_loop()

    def close_textWindow(self):
        self.textWindow.destroy()
        self.textWindow = None

    def move_me(self, event):
        if self.textWindow != None:
            x = self.master.winfo_x()
            y = self.master.winfo_y()
            self.textWindow.geometry('+{}+{}'.format(x+10, y+30))

    def timed_loop(self):
        if self.textWindow != None:
            self.master.after(1000, self.timed_loop)
            self.value += 1
            self.list_for_toplevel.append(self.value)
            self.textArea.delete(1.0, "end-1c")
            for item in self.list_for_toplevel:
                self.textArea.insert('end', "{}\n".format(item))
                self.textArea.see('end')


if __name__ == "__main__":

    root = tk.Tk()
    root.geometry("800x480")
    myapp = guiapp(root)
    root.mainloop()