【问题标题】:Tkinter window not updating after changing the window.columnconfigure()更改 window.columnconfigure() 后 Tkinter 窗口未更新
【发布时间】:2020-08-23 14:44:58
【问题描述】:

我刚开始使用 python,我正在尝试制作一个由 3 帧组成的简单 gui。一个在左边,一个在右边,一个在中间。稍后我想在这些框架中添加按钮和东西,但现在仅此而已。如果按退出键,我希望左框架消失或再次出现。为此,我编写了以下代码:

from tkinter import Tk, Button, Label, Frame

class Main:

    def __init__(self):
        self.root = Tk()
        self.init_gui()

    def init_gui(self):
        self.root.title("Gui Testing")
        self.root.minsize(900,600)

        self.root.bind("<Escape>", self.toggle_left_menu)

        self.root.grid_columnconfigure(0, minsize=200)
        self.root.grid_columnconfigure(1, weight=1)
        self.root.grid_columnconfigure(2, minsize=250)
        self.root.grid_rowconfigure(0, weight=1)

        # main 3 panels
        self.left_menu_active = True
        self.left_menu = Frame(self.root, bg="#333")
        self.left_menu.grid(row=0, column=0, sticky="nsew")

        self.center = Frame(self.root, bg="white")
        self.center.grid(row=0, column=1, sticky="nsew")

        self.right_menu = Frame(self.root, bg="#888")
        self.right_menu.grid(row=0, column=2, sticky="nsew")
        
        self.toggle_left_menu()

    def toggle_left_menu(self, event=None):
        if self.left_menu_active == True:
            self.left_menu_active = False
            
            self.root.grid_columnconfigure(0, minsize=0)
            self.left_menu.grid_forget()
        else:
            self.left_menu_active = True

            self.root.grid_columnconfigure(0, minsize=200)
            self.left_menu.grid(row=0, column=0, sticky="nsew")


    def start(self):
        self.root.mainloop()
        
Main().start()

问题是当我按下退出键时,什么也没有发生。但是,当我通过单击并拖动它来移动窗口时,它会突然更新,并以我想要的方式显示窗口。所以代码似乎可以工作,但由于某种原因窗口没有更新。

我不知道我能做些什么。我发现它确实会更新左侧和中心框架的网格位置,但grid_configure() 似乎不会在不移动窗口的情况下更新。

有没有办法更新框架或以其他方式实现框架切换?

编辑:

问题已通过在每一帧中添加一个按钮来解决。现在框架不再是空的,它似乎有效。我还稍微编辑了toggle_left_menu() 函数。这是我改变的:

添加按钮:

    self.test_button1 = Button(self.left_menu, text="left", padx=10, pady=5)
    self.test_button1.grid(row=0, column=0)

    self.test_button2 = Button(self.center, text="center", padx=10, pady=5)
    self.test_button2.grid(row=0, column=0)

    self.test_button3 = Button(self.right_menu, text="right", padx=10, pady=5)
    self.test_button3.grid(row=0, column=0)
    

编辑toggle_left_menu():

def toggle_left_menu(self, event=None):
    if self.left_menu.winfo_viewable():
        self.root.grid_columnconfigure(0, minsize=0)
        self.left_menu.grid_remove()
    else:
        self.root.grid_columnconfigure(0, minsize=200)
        self.left_menu.grid()

这对我有用,谢谢!

【问题讨论】:

  • 你在 OSX 上运行吗?
  • @BryanOakley 不,我在 Windows 10 上运行
  • 如果你把东西放在框架里,问题就会消失。
  • @acw1668 成功了,谢谢!但是,当您的框架中没有任何内容时,为什么它不起作用?

标签: python tkinter


【解决方案1】:

扩展 Bryan Oakley 的示例 ~ 您还必须切换 minsize。顺便说一句,我把你所有的 gui 都分类了,然后把 Main 变成了 root。所有self.root.thisself.root.that 都是不必要的。此外,除非您打算将整个 gui 内容转储到您的 init_gui 方法中,否则无论如何您都必须这样做。如果您的应用程序很大,那么跟踪这将是一场噩梦。作为额外的奖励,我使整个toggle_menu 方法动态化,因此它可以切换任一菜单。您可以将键绑定更改为任何内容。我将Escape then l 用于left_menu,将Escape then r 用于right_menu

from tkinter import Tk, Button, Label, Frame


class LeftMenu(Frame):
    @property
    def minsize(self):
        return 200
        
    def __init__(self, master, row=0, column=0, **kwargs):
        Frame.__init__(self, master, **kwargs)
        self.grid(row=row, column=column, sticky='nswe')
        
        
class RightMenu(Frame):
    @property
    def minsize(self):
        return 250
        
    def __init__(self, master, row=0, column=0, **kwargs):
        Frame.__init__(self, master, **kwargs)
        self.grid(row=row, column=column, sticky='nswe')
       

class Center(Frame):
    def __init__(self, master, row=0, column=0, **kwargs):
        Frame.__init__(self, master, **kwargs)
        self.grid(row=row, column=column, sticky='nswe')


class Main(Tk):
    def __init__(self):
        Tk.__init__(self)

        self.bind("<Escape><l>", self.toggle_menu)
        self.bind("<Escape><r>", self.toggle_menu)

        self.grid_columnconfigure(0, minsize=200)
        self.grid_columnconfigure(1, weight=1)
        self.grid_columnconfigure(2, minsize=250)
        self.grid_rowconfigure(0, weight=1)

        # main 3 panels
        self.left_menu  = LeftMenu(self, 0, 0, bg="#333")
        self.center     = Center(self, 0, 1, bg="white")
        self.right_menu = RightMenu(self, 0, 2, bg="#888")
        
        self.toggle_menu(menu=self.left_menu)

    def toggle_menu(self, event=None, menu=None):
        if event and event.char in 'lr':
            menu = self.left_menu if event.char == 'l' else self.right_menu
            
        if menu:
            if menu.winfo_viewable():
                self.grid_columnconfigure(menu.grid_info()['column'], minsize=0)
                menu.grid_remove()
            else:
                menu.grid()
                self.grid_columnconfigure(menu.grid_info()['column'], minsize=menu.minsize)
                
            self.after_idle(self.event_generate, '<Configure>')
           
        
        
if __name__ == "__main__":
    root = Main()
    root.title("Gui Testing")
    root.minsize(900,600)
    root.mainloop()

【讨论】:

  • 可能是因为我是初学者,你能解释一下@property 是什么吗?还是会?
  • 谢谢!我并没有真正打算做大事,但我认为按照你的方式制作框架是一种很好的做法。读起来好多了。通过使用class example(Frame),您真的是在扩展 Frame 类吗?就像你可以在java中说class example extends Frame一样?哦,if __name__ == "__main__" 是做什么的?
  • @CoolCloud ~ 当然,这只是一个getter。如果我还想制作setter,我会使用@minsize.setter。没有二传手“你不能设置它”。我把它放在引号中,因为你可以破解 getter 并设置它。但是,如果没有 setter,仅执行此操作 self.left_menu.minsize = 500 将不起作用。基本上,如果你玩得好,@property 是只读的,没有对应的setter
  • @MichaelGuidry 我刚刚在您回复时编辑了我的评论,我还有一个问题。希望你看到了。
  • @TijmenGroot ~ 这是初始化主脚本的正确 pep8 方式。主要,我的意思是你告诉 python 运行的脚本(即python myscript.py)~'myscript' 将是主要的。
【解决方案2】:

部分问题在于minsize 仅影响最小 大小。如果左框架可见且宽度大于零像素,则将minsize 设置为零不会使框架变小。因此,第一步是删除第 0 列的 minsize=200 选项。

由于您使用grid,隐藏或显示框架的最佳方法是使用grid_remove 删除小部件,然后使用grid 恢复它。 grid_remove 将从窗口中删除小部件,但记住它的所有设置。当您随后调用.grid() 时,将使用之前的所有设置。

您也可以只检查窗口是否可见,而无需管理布尔标志,因为您的功能是一个切换。这稍微简化了代码。

另外,我认为某些版本的 tk(构建 tkinter 的库)存在一个错误,它会阻止窗口在这种特定类型的情况下刷新。对我有用的是在根窗口上综合生成 &lt;Configure&gt; 事件。

将所有这些结合在一起,这个版本的切换功能适用于我在 OSX 上,无需对您的代码进行任何其他修改。

def toggle_left_menu(self, event=None):
    if self.left_menu.winfo_viewable():
        self.left_menu.grid_remove()
        self.root.after_idle(self.root.event_generate, '<Configure>')
    else:
        self.left_menu.grid()
        self.root.after_idle(self.root.event_generate, '<Configure>')

【讨论】:

  • OP 可能还想在init_gui() 中添加一个self.root.grid_propagate(0),以便框架在左侧出现和出现时保持其大小。
  • @martineau:我不同意。几乎没有任何时候grid_propagate(0) 是合适的。最终,他们会将其他小部件放入这些框架中,这只会让创建响应式 UI 变得更加困难。
  • 我已经在寻找一种方法来使用winfo_ 来检查框架是否可见,但不知何故错过了那个,非常感谢您指出这一点!我已经尝试了您的其余代码。它现在确实更新了,但是当它消失时,中心窗口仍然没有填满左侧菜单的区域。
  • @TijmenGroot:哦,我明白了。我没有注意到它,因为在 OSX 上,根窗口的背景是白色的。
  • @TijmenGroot:我已更新我的答案以在 UI 空闲后生成 &lt;Configure&gt; 事件。这使它可以在我的系统上运行。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-28
  • 2017-05-09
  • 1970-01-01
相关资源
最近更新 更多