使用 tkinter 的框架布局中的框架

kru*_*sty 0 python tkinter

我想创建 tkinter 网格布局 5 帧。在替换 Frame 内容之前,它看起来是我想要的样子。

self.Frame1 = tk.Frame(self.parent, bg="grey")
Run Code Online (Sandbox Code Playgroud)

self.Frame1 = LeftUpperWindow(self.parent)
Run Code Online (Sandbox Code Playgroud)

当我最大化窗口时,我看到 LeftUpperWindow 框架中的所有内容都乱七八糟,布局不正确。一张图值一千字 所以我有这个

在此处输入图片说明

并且想要这个

在此处输入图片说明

import sys
import os

import Tkinter as tk
import ttk as ttk

class LeftUpperWindow(tk.Frame):

    def __init__(self, master=None):
        self.parent = master
        tk.Frame.__init__(self, self.parent, bg='#ffffff', borderwidth=1, relief="sunken")
        self.__create_layout()

    def __create_layout(self):

        self.parent.grid()

        for r in range(3):
            self.parent.rowconfigure(r, weight=1)
        for c in range(2):
            self.parent.columnconfigure(c, weight=1)

        self.editArea = tk.Text(self.parent, wrap=tk.NONE, undo=True,
                           relief=tk.SUNKEN, borderwidth=5, highlightthickness=0, insertbackground="white")

        self.editArea.config(font=('courier', 10, 'normal'))
        self.editArea.configure(bg="black", fg="white")
        self.editArea.grid(row=0, column=0, sticky=(tk.N, tk.S, tk.W, tk.E))

        self.scrollbarY = tk.Scrollbar(self.parent, orient=tk.VERTICAL)
        self.scrollbarY.grid(row=0, column=1, sticky=(tk.N, tk.S), rowspan=2)

        self.scrollbarX = tk.Scrollbar(self.parent, orient=tk.HORIZONTAL)
        self.scrollbarX.grid(row=1, column=0, sticky=(tk.W, tk.E))

        self.status = tk.Label(self.parent, text="Status label", bd=1, relief=tk.SUNKEN, anchor=tk.W,  bg='lightgray')
        self.status.grid(row=2, column=0, sticky=(tk.N, tk.S, tk.W, tk.E), columnspan=2)

    def __config_window(self):
        pass

    def close_quit(self, event=None):
        self.parent.destroy()

    def dummy_func(self):
        print("dummy")
        pass

class MainWindow(tk.Frame):
    def __init__(self, master=None):
        self.parent = master
        tk.Frame.__init__(self, self.parent, bg='#ffffff', borderwidth=1, relief="sunken")
        self.__create_layout()
        self.__create_menu()
        self.__config_mainWindow()

    def __create_layout(self):
        self.parent.grid()

        for r in range(6):
            self.parent.rowconfigure(r, weight=1)
        for c in range(10):
            self.parent.columnconfigure(c, weight=1)

        self.Frame1 = LeftUpperWindow(self.parent)  #tk.Frame(self.parent, bg="grey")
        self.Frame1.grid(row=0, column=0, rowspan=4, columnspan=8, sticky=(tk.N, tk.S, tk.W, tk.E))

        self.Frame2 = tk.Frame(self.parent, bg="blue")
        self.Frame2.grid(row=0, column=8, rowspan=4, columnspan=2, sticky=(tk.N, tk.S, tk.W, tk.E))

        self.Frame3 = tk.Frame(self.parent, bg="green")
        self.Frame3.grid(row=4, column=0, rowspan=2, columnspan=5, sticky=(tk.N, tk.S, tk.W, tk.E))

        self.Frame4 = tk.Frame(self.parent, bg="brown")
        self.Frame4.grid(row=4, column=5, rowspan=2, columnspan=5, sticky=(tk.N, tk.S, tk.W, tk.E))

        self.Frame5 = tk.Frame(self.parent, bg="pink")
        self.Frame5.grid(row=5, column=0, rowspan=1, columnspan=10, sticky=(tk.N, tk.S, tk.W, tk.E))

    def close_quit(self, event=None):
        self.parent.destroy()

    def __config_mainWindow(self):
        self.parent.config(menu=self.menubar)

    def __create_menu(self):
        self.menubar = tk.Menu(self.parent)

        self.filemenu = tk.Menu(self.menubar, tearoff=0)
        self.filemenu.add_command(label="Open", command=self.dummy_func)
        self.filemenu.add_separator()

        self.filemenu.add_command(label="Exit", command=self.close_quit)
        self.menubar.add_cascade(label="File", menu=self.filemenu)

        helpmenu = tk.Menu(self.menubar, tearoff=0)
        helpmenu.add_command(label="About...", command=self.dummy_func)
        self.menubar.add_cascade(label="Help", menu=helpmenu)

    def dummy_func(self):
        print("dummy")
        pass

#
#   MAIN
#
def main():
    root = tk.Tk()

    root.title("Frames")
    root.geometry("550x300+525+300")

    root.configure(background="#808080")
    root.option_add("*font", ("Courier New", 9, "normal"))

    window = MainWindow(master=root)
    root.protocol("WM_DELETE_WINDOW", window.close_quit)
    root.mainloop()

if __name__ == '__main__':
    main()
Run Code Online (Sandbox Code Playgroud)

Bry*_*ley 6

概述

您的问题没有快速解决方法。你在你的代码中做了几件事情,这些事情对你不利,这使得调试你的代码变得非常困难。重写是最好的解决方案。

以下是我发现使问题变得比实际需要更困难的事情,并且可以通过重写来解决。

首先,不要让小部件负责将自己放置在其父级中。在您的情况下,您通过让小部件将其父级放入其祖父级来更进一步。这只是错误的做法。一个包含窗口应该负责布局它的子窗口,仅此而已

其次,将尽可能多的布局代码放在一起。“所有”是指“所有给定容器”。因此,例如,而不是这样:

x=Label(...)
x.grid(...)
y =Label(...)
y.grid(...)
Run Code Online (Sandbox Code Playgroud)

... 像这样做:

x = Label(...)
y = Label(...)
x.grid(...)
y.grid(...)
Run Code Online (Sandbox Code Playgroud)

这使得在代码中可视化布局变得更加容易。

第三,不要试图一次解决多个布局问题。一次解决一个问题。由于您发布了整个程序,我假设您对整个程序有问题,而不仅仅是左上角的小部件。当我运行你的代码时,我观察到在我看来不对的调整大小行为,强化了这种信念。

第四,如果您创建一个像 MainWindow 这样旨在包含其他小部件的类,则所有这些其他小部件都应该是窗口的子级,而不是父级的子级。

第五,作为一般经验法则,您只希望带有容器的单行单列具有非零权重。这只是一个指南,因为有时您需要多个小部件来扩展或收缩。对于滚动条和状态标签,您通常不希望它们有额外的空间。您的代码中的部分问题是您给所有东西都赋予了权重,因此为每个小部件分配了额外的空间。

解决方案

解决方案是一次只处理一个部分。让它工作,然后继续下一部分。在你的情况下,我看到工作分为四个阶段。首先,创建并布置主窗口。接下来,添加作为主窗口的直接子级的小部件。之后,为左上角的窗口创建容器。最后,将小部件添加到左上角的窗口。在每个阶段之后,一定要运行代码并验证一切是否正常。

主窗口

第一步是从主窗口开始。让它看起来正确,并在调整主窗口大小时表现正确。当我运行您的代码时,当窗口变小时,底部和右侧的窗口会消失,而当窗口变大时,底部的框架会增长。我猜这不是正确的行为。无论如何,从您认为正确的行为开始,并在添加更多小部件之前使其工作。

从以下代码开始。验证当您调整窗口大小时,一切都会按照您期望的方式展开和收缩。请注意,我做了一些简单的更改。需要注意的主要事情是创建主窗口的函数负责使该窗口可见。我使用pack它是因为它是根窗口中唯一的小部件,这意味着我可以在一行中完成所有操作,而不必担心grid.

import sys
import os

import Tkinter as tk
import ttk as ttk

class MainWindow(tk.Frame):
    def __init__(self, master=None):
        self.parent = master
        tk.Frame.__init__(self, self.parent, bg='bisque', borderwidth=1, relief="sunken")
        self.__create_layout()

    def __create_layout(self):
        pass

#
#   MAIN
#
def main():
    root = tk.Tk()

    root.title("Frames")
    root.geometry("550x300+525+300")

    root.configure(background="#808080")
    root.option_add("*font", ("Courier New", 9, "normal"))

    window = MainWindow(master=root)
    window.pack(side="top", fill="both", expand=True)
    root.mainloop()

if __name__ == '__main__':
    main()
Run Code Online (Sandbox Code Playgroud)

MainWindow 内的小部件

一旦MainWindowshell 运行正常,就可以添加更多小部件了。看起来您有五个框架,但其中一个是自定义类。现在我们将使用常规框架来保持简单。给每个人一种颜色很好,这是在构建窗口时可视化布局的好方法。

修改__create_layout为如下所示。注意两个重要的变化:每个小部件都是self而不是的子项self.parent,并且我已经将所有布局代码组合在一起。

调整大小的行为看起来不对我,但我不知道你到底想要什么。对我来说,让绿色、红色和粉色框架按照它们的方式调整大小似乎很奇怪,尽管也许这就是您想要的。无论如何,现在是时候把它做好了。

def __create_layout(self):
    self.Frame1 = tk.Frame(self, bg="grey")
    self.Frame2 = tk.Frame(self, bg="blue")
    self.Frame3 = tk.Frame(self, bg="green")
    self.Frame4 = tk.Frame(self, bg="brown")
    self.Frame5 = tk.Frame(self, bg="pink")

    self.Frame1.grid(row=0, column=0, rowspan=4, columnspan=8, sticky=(tk.N, tk.S, tk.W, tk.E))
    self.Frame2.grid(row=0, column=8, rowspan=4, columnspan=2, sticky=(tk.N, tk.S, tk.W, tk.E))
    self.Frame3.grid(row=4, column=0, rowspan=2, columnspan=5, sticky=(tk.N, tk.S, tk.W, tk.E))
    self.Frame4.grid(row=4, column=5, rowspan=2, columnspan=5, sticky=(tk.N, tk.S, tk.W, tk.E))
    self.Frame5.grid(row=5, column=0, rowspan=1, columnspan=10, sticky=(tk.N, tk.S, tk.W, tk.E))

    for r in range(6):
        self.rowconfigure(r, weight=1)
    for c in range(10):
        self.columnconfigure(c, weight=1)
Run Code Online (Sandbox Code Playgroud)

LeftUpperWindow 小部件

接下来,让我们定义LeftUpperWindow类,并确保我们没有破坏任何东西。为该类添加一个存根,为窗口指定不同的颜色,并确保窗口在出现和调整大小时看起来仍然正常。

class LeftUpperWindow(tk.Frame):
    def __init__(self, master=None):
        self.parent = master
        tk.Frame.__init__(self, self.parent, bg='bisque', borderwidth=1, relief="sunken")
        self.__create_layout()

    def __create_layout(self):
        pass
Run Code Online (Sandbox Code Playgroud)

记得修改MainWindow.__create_layout创建这个类的实例而不是框架:

self.frame1 = LeftUpperWindow(self)
Run Code Online (Sandbox Code Playgroud)

LeftUpperWindow 中的小部件

最后,是时候在LeftUpperWindow. 就像我们在 中所做的那样MainWindow,小部件都是 的子代self,小部件创建和小部件布局在不同的组中完成。此外,通常最好只给一行和一列非零权重。这通常是文本或画布小部件所在的行和列。

当然,你可以做你想做的。请注意一个事实,因为您在原始代码中为每一行和每一列赋予了权重,这导致了小部件之间的所有空间。

此外,因为您为 GUI 提供了明确的大小(通过root.geometry(...)),所以您希望为文本小部件提供一个小尺寸。否则,默认宽度和高度将推出其他小部件,因为 tkinter 尝试为每个小部件提供其默认大小。由于您限制了整个窗口大小,因此您希望文本小部件尽可能小,然后grid将其扩展以填充其指定的区域。

这是__create_layout应该的样子:

def __create_layout(self):
    self.editArea = tk.Text(self, wrap=tk.NONE, undo=True,
                            width=1, height=1,
                            relief=tk.SUNKEN, borderwidth=5, 
                            highlightthickness=0, insertbackground="white")
    self.editArea.config(font=('courier', 10, 'normal'))
    self.editArea.configure(bg="black", fg="white")

    self.scrollbarY = tk.Scrollbar(self, orient=tk.VERTICAL)
    self.scrollbarX = tk.Scrollbar(self, orient=tk.HORIZONTAL)
    self.status = tk.Label(self, text="Status label", bd=1, relief=tk.SUNKEN, 
                           anchor=tk.W,  bg='lightgray')

    self.editArea.grid(row=0, column=0, sticky=(tk.N, tk.S, tk.W, tk.E))
    self.scrollbarY.grid(row=0, column=1, sticky=(tk.N, tk.S), rowspan=2)
    self.scrollbarX.grid(row=1, column=0, sticky=(tk.W, tk.E))
    self.status.grid(row=2, column=0, sticky=(tk.N, tk.S, tk.W, tk.E), columnspan=2)

    self.rowconfigure(0, weight=1)
    self.columnconfigure(0, weight=1)
Run Code Online (Sandbox Code Playgroud)