【问题标题】:Tkinter Toplevel Window Not MovableTkinter 顶层窗口不可移动
【发布时间】:2017-08-08 15:36:02
【问题描述】:

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

  1. 打开时显示以前的日志条目,并使用新条目进行更新。
  2. 禁用用户移动窗口的能力,同时也使用户能够关闭窗口
  3. 始终将窗口固定在其右上角,使其右上角位于根窗口的右上角

我想出了#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()

【问题讨论】:

    标签: python tkinter


    【解决方案1】:

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

    然后我们可以确保toplevel窗口位于右上角,方法是获取根窗口位置,然后将toplevel窗口设置为与self.master.winfo_x()self.master.winfo_y()相同的位置。

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

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

    我们可以使用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()
    

    【讨论】:

    • 这基本上就是我要找的。非常感谢:)
    • @Skitzafreak:我必须解决一些关于如何检查窗口是否存在的错误。我刚刚更新了我的答案,以防止由于某些 if 语句而发生的错误。如果您对更新后的代码有任何疑问,请告诉我。
    猜你喜欢
    • 2017-07-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-11-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多