简体   繁体   English

使用tkinter在帧布局中的帧

[英]frames in frames layout using tkinter

I want to create tkinter grid layout 5 frames. 我想创建tkinter网格布局5帧。 It looked the way I wanted before replacing Frame content. 在替换Frame内容之前,它看起来像我想要的样子。

self.Frame1 = tk.Frame(self.parent, bg="grey")

with

self.Frame1 = LeftUpperWindow(self.parent)

When I maximize the window I see everything in LeftUpperWindow frame in messed up, layout incorrectly. 当我最大化窗口时,我看到LeftUpperWindow框架中的所有内容都弄乱了,布局不正确。 A picture is worth a thousand words so I have this 一幅图片值一千个字,所以我有这个

在此处输入图片说明

and would like to have this 并希望有这个

在此处输入图片说明

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

Overview 总览

There is no quick fix for your problem. 没有快速解决您的问题的方法。 You are doing several things in your code that stack the odds against you, and which makes it really hard to debug your code. 您正在代码中做几件事情,这些事情使您不胜其烦,这使得调试代码真的非常困难。 A rewrite is the best solution. 重写是最好的解决方案。

Here are things I've found that make the problem more difficult than they need to be, and which can be solved by a rewrite. 我发现有些问题使问题变得比原本更难解决,并且可以通过重写解决。

First, don't have a widget be responsible for placing itself in its parent. 首先,没有小部件负责将其放置在其父级中。 In your case you're going one step further by having a widget place it's parent into its grandparent. 在您的情况下,您可以通过将小部件的父项放到其祖父母项中来进一步。 That's simply the wrong way to do it. 那简直就是错误的做法。 A containing window should be responsible for layout out it's children, and nothing more 包含窗口应负责布局其子级,仅此而已

Second, put as much of your layout code together as possible. 其次,将尽可能多的布局代码放在一起。 By "all" I mean "all for a given container". “全部”是指“给定容器的全部”。 So, for example, instead of this: 因此,例如,代替此:

x=Label(...)
x.grid(...)
y =Label(...)
y.grid(...)

... do it like this: ... 像这样做:

x = Label(...)
y = Label(...)
x.grid(...)
y.grid(...)

This makes it much easier to visualize your layout in the code. 这使得在代码中可视化布局变得更加容易。

Third, don't try to solve multiple layout problems at once. 第三,不要试图一次解决多个布局问题。 Solve one problem at a time. 一次解决一个问题。 Since you posted your whole program, I assume you have a problem with the whole program and not just the upper-left widgets. 自从您发布了整个程序以来,我假设您对整个程序有问题,而不仅仅是左上角的小部件。 When I ran your code I observed resize behavior that seemed off to me, reinforcing that belief. 当我运行您的代码时,我发现调整大小的行为对我来说似乎很奇怪,从而增强了这种信念。

Fourth, if you create a class like MainWindow that is designed to contain other widgets, all of these other widgets shild be children of the window, not children of the parent. 第四,如果您创建了一个类似MainWindow的旨在包含其他小部件的类,则所有其他小部件都应是窗口的子级,而不是父级的子级。

Fifth, as a general rule of thumb you want only a single row and single column with a container to have a non-zero weight. 第五,作为一般经验法则,您只希望带有容器的单行单列的权重为非零。 This is only a guideline, because there are often times when you want more than one widget to expand or contract. 这只是一个准则,因为很多时候您希望多个窗口小部件展开或收缩。 In the case of scrollbars and statuslabels, you typically don't want them to be given extra space. 对于滚动条和状态标签,通常不希望为它们提供额外的空间。 Part of the problem in your code is that you give everything a weight, so extra space is allocated to every widget. 代码中的部分问题是您要赋予所有内容一定的权重,因此会为每个小部件分配额外的空间。

The solution 解决方案

The solution is work on one section at a time. 解决方案是一次只处理一个部分。 Get it working, then move on to the next section. 使它正常工作,然后继续进行下一部分。 In your case I see the effort breaking down into four phases. 在您的情况下,我认为工作分为四个阶段。 First, get the main window created and laid out. 首先,创建并布局主窗口。 Next, add the widgets that are immediate children of the main window. 接下来,添加作为主窗口直接子级的小部件。 After that, create the container for the upper-left window. 之后,为左上角的窗口创建容器。 Finally, add the widgets to the upper left window. 最后,将小部件添加到左上方的窗口中。 After each phase, be sure to run the code and verify that everything looks right. 在每个阶段之后,请确保运行代码并确认一切看起来正确。

The main window 主视窗

The first step is to start with the main window. 第一步是从主窗口开始。 Get it to look right, and behave properly when you resize the main window. 使它看起来正确,并在调整主窗口大小时正常运行。 When I ran your code the bottom and right windows would vanish when the window became small, and the bottom frames grew when the window was large. 当我运行您的代码时,当窗口变小时,底部和右侧窗口将消失,而当窗口变大时,底部框架将增大。 I'm guessing that's not the correct behavior. 我猜这不是正确的行为。 Regardless, start with what you think is the correct behavior and make it work before adding any more widgets. 无论如何,请先从您认为正确的行为入手,然后使其开始添加其他小部件。

Start with the following code. 从以下代码开始。 Verify that when you resize the window, everything expands and contracts the way you expect. 验证您在调整窗口大小时是否一切都按预期方式扩展和收缩。 Notice that I made a couple of simple changes. 注意,我做了几个简单的更改。 The main thing to notice is that the function that creates the main window is responsible for making that window visible. 需要注意的主要事情是创建主窗口的函数负责使该窗口可见。 I use pack since it's the only widget in the root window, and it means I can do it all in one line without having to worry about row and column weights with grid . 我使用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()

The widgets inside of MainWindow MainWindow内部的小部件

Once you have the MainWindow shell behaving properly, it's time to add more widgets. 一旦MainWindow shell行为正确,就该添加更多小部件了。 It looks like you have five frames, though one of them is a custom class. 看起来您有五个框架,尽管其中一个是自定义类。 For now we'll use a regular frame to keep things simple. 现在,我们将使用常规框架使事情保持简单。 It's good that you give each one a color, that's a great way to visualize the layout as you build up the window. 最好给每个颜色一种颜色,这是在构建窗口时可视化布局的好方法。

Modify __create_layout to look like the following. 修改__create_layout如下所示。 Note two important changes: every widget is a child of self rather than self.parent , and I've grouped all of the layout code together. 请注意两个重要的更改:每个小部件都是self的子级,而不是self.parent ,并且我将所有布局代码分组在一起。

The resize behavior looks off to me, but I don't know exactly what you intend. 调整大小的行为对我来说似乎很奇怪,但我不知道您打算做什么。 For me it seems odd to have the green, red and pink frames resize the way they do, though maybe that's what you want. 对我来说,绿色,红色和粉红色的框架调整其大小似乎很奇怪,尽管这也许就是您想要的。 Regardless, now is the time to get it right. 无论如何,现在是时候把它弄对了。

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)

The LeftUpperWindow widget LeftUpperWindow小部件

Next, let's define the LeftUpperWindow class, and make sure we haven't broken anything. 接下来,让我们定义LeftUpperWindow类,并确保我们没有破坏任何东西。 Add a stub for the class, give the window a distinct color, and make sure the window still looks ok when it appears and when it is resized. 为该类添加一个存根,为窗口提供不同的颜色,并确保窗口在出现和调整大小时仍然看起来不错。

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

Remember to modify MainWindow.__create_layout to create an instance of this class rather than frame: 请记住修改MainWindow.__create_layout来创建此类的实例,而不是框架:

self.frame1 = LeftUpperWindow(self)

The widgets in LeftUpperWindow LeftUpperWindow中的小部件

Finally, it's time to add the widgets in LeftUpperWindow . 最后,是时候在LeftUpperWindow添加小部件了。 Like we did in MainWindow , the widgets are all children of self , and the widget creation and widget layout are done in separate groups. 就像我们在MainWindow所做的一样,小部件都是self ,而小部件的创建和小部件布局是在单独的组中完成的。 Also, it's usually best to only give a single row and a single column a non-zero weight. 另外,通常最好只给单行和单列赋予非零的权重。 This is usually the row and column that a text or canvas widget is in. 通常是文本或画布小部件所在的行和列。

You can do what you want, of course. 您当然可以做您想做的。 Just be mindful of the fact that because you gave each row and column a weight in your original code, that contributed to all of the space between widgets. 请注意以下事实:由于您在原始代码中为每一行和每一列赋予了权重,因此造成了小部件之间的所有空间。

Also, because you're giving the GUI an explicit size (via root.geometry(...) ), you want to give the text widget a small size. 另外,由于root.geometry(...) GUI显式的大小(通过root.geometry(...) ),因此您希望为文本小部件提供较小的大小。 Otherwise, the default width and height will push out the other widgets since tkinter tries to give each widget its default size. 否则,默认的宽度和高度将推出其他小部件,因为tkinter会尝试为每个小部件提供其默认大小。 Since you're constraining the overall window size you want the text widget to be as small as possible, and then let grid expand it to fill the area it's been given. 由于要限制整个窗口的大小,因此您希望文本小部件尽可能小,然后让grid将其扩展以填充指定的区域。

Here's what __create_layout should look like: 这是__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)

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM