简体   繁体   English

如何在根TopLevel下的Python Tkinter框架中布局窗口小部件

[英]How to layout widgets in a Python Tkinter frame under a root TopLevel

Here is the code: 这是代码:

class Window(Frame):
    def __init__(self, master = None):
        Frame.__init__(self, master)
        self.master = master
        self.init_window()
    def init_window(self):
        self.master.title('GUI')

        row = 0
        self.s_date_label = Label(self, text = 'Start Date: ')
        self.s_date_label.grid(row=row, column=0, sticky = W)
        self.start_date = Entry(self, bd=1)
        self.start_date.grid(row=row, column=1, sticky = W)
        self.s_date_label2 = Label(self, text = 'example: 20160101')
        self.s_date_label2.grid(row=row, column=2, sticky = W)

        self.datetype_var = IntVar()
        R1 = Radiobutton(root, text="20160101", variable=self.datetype_var, value=8)
        R1.grid(row=row, column=1, sticky = W)
        R1.select()
        R2 = Radiobutton(root, text="201601", variable=self.datetype_var, value=6)
        R2.grid(row=row, column=2, sticky = W)
        R3 = Radiobutton(root, text="2016", variable=self.datetype_var, value=4)
        R3.grid(row=row, column=3, sticky = W)

root = Tk()
root.geometry('600x400')

app = Window(root)

root.mainloop()

Here is the output: 这是输出:

在此输入图像描述

My Label and Entry both do not show up. 我的标签和条目都没有出现。

I have found that Radiobutton do not have self. 我发现Radiobutton没有自我。 and Label and Entry have it. 和标签和条目有它。

However, I don't know why this will cause the result. 但是,我不知道为什么会导致结果。

Update 更新

I had suggested 2 approaches to solve this. 我提出了两种方法来解决这个问题。 First one is to use the root Tk object as the parent for all the widgets; 第一个是使用root Tk对象作为所有小部件的父对象; Second is to add the widgets to Window , and add Window object to the layout manager of the root . 其次是将窗口小部件添加到Window ,并将Window对象添加到root的布局管理器。 As Bryan Oakley mentioned (correctly) in the comments, the first approach is not as good (although it works, but still). 正如Bryan Oakley在评论中(正确地)提到的那样,第一种方法并不是那么好(尽管它有效,但仍然如此)。 So I'm suggesting to use the second approach (discussed below), which is: 所以我建议使用第二种方法(下面讨论),即:

Make sure all the widgets ( Radiobutton , Label , Entry , etc.) are using the Window object (referenced by self ) as the parent, and then add the window instance to the layout manager of the root . 确保所有小部件( RadiobuttonLabelEntry等)都使用Window对象(由self引用)作为父对象,然后将窗口实例添加到root的布局管理器中。 Either in init_window call, or after creating the object. init_window调用中,或在创建对象之后。

Approach 1 方法1

The widgets under the Window , should use self as the parent (to preserve hierarchy), and the Window itself, should be added to layout manager of master . Window下的小部件应该使用self作为父级(保留层次结构),而Window本身应该添加到master布局管理器中。 This way all the widgets will show correctly 这样所有小部件都将正确显示

You should have passed the Tk object (referenced by root ) when adding the widgets to the layout (in this case the grid geometry). 在将小部件添加到布局(在本例中为grid几何)时,您应该已经传递了Tk对象(由root引用)。

from Tkinter import *

class Window(Frame):
    def __init__(self, master = None):
        Frame.__init__(self, master)
        self.master = master
        self.init_window()

    def init_window(self):
        self.master.title('GUI')

        row = 0
        self.s_date_label = Label(self.master, text = 'Start Date: ')
        self.s_date_label.grid(row=row, column=0, sticky = W)
        self.start_date = Entry(self.master, bd=1)
        self.start_date.grid(row=row, column=1, sticky = W)
        self.s_date_label2 = Label(self.master, text = 'example: 20160101')
        self.s_date_label2.grid(row=row, column=2, sticky = W)

        self.datetype_var = IntVar()
        R1 = Radiobutton(self.master, text="20160101", variable=self.datetype_var, value=8)
        R1.grid(row=row, column=1, sticky = W)
        R1.select()
        R2 = Radiobutton(self.master, text="201601", variable=self.datetype_var, value=6)
        R2.grid(row=row, column=2, sticky = W)
        R3 = Radiobutton(self.master, text="2016", variable=self.datetype_var, value=4)
        R3.grid(row=row, column=3, sticky = W)

root = Tk()
root.geometry('600x400')

app = Window(root)

root.mainloop()

Note that the parent (first argument passed to Label and Entry constructors), are changed from self to self.master . 请注意,父(传递给LabelEntry构造函数的第一个参数)从self更改为self.master

Also the parent for Radiobutton are also changed from root to self.master , because then root is a reference to the global variable, and not the actual parent set for Window object. Radiobutton的父级也从root更改为self.master ,因为root是对全局变量的引用,而不是Window对象的实际父级。

Update (description) 更新(描述)

The code on the question is adding the Label and Entry widgets to self (the Window object), so when calling grid on them it's using the grid layout of the window object . 问题的代码是将LabelEntry小部件添加到selfWindow对象),因此当在它们上调用grid时,它使用窗口对象grid布局。 But the window object itself, was not added to the root (Tk) , so basically the window object and it's attached widgets would not show at all. 但是窗口对象本身并未添加到root (Tk) ,因此基本上window对象及其附加的窗口小部件根本不会显示。

The Radiobutton instances however, were attached to root , so calling grid on them would add them to layout manager of the root (Tk) , that's why they were showing. 然而, Radiobutton实例被附加到root ,因此在它们上面调用grid会将它们添加到root (Tk)布局管理器中,这就是它们显示的原因。

One fix for this, is to keep using root as the parent for all widgets, which causes the window object to be like a data container, but not a widget on the UI. 对此的一个解决方法是继续使用root作为所有窗口小部件的父窗口,这会导致窗口对象像数据容器,但不是UI上的窗口小部件。

Approach 2 方法2

Add the window object itself to the layout manager of the root . 将窗口对象本身添加到root的布局管理器。 Something like this: 像这样的东西:

#all the code is the same as the question
# ...
app = Window(root)
app.grid(row=0, column=0, sticky=NW)
# the rest of the code

But remember that if you'd like to use this approach (using the window object as a Widget container as well), then again I'd suggest that all the Radiobutton to use self (the window object) as their parents instead of root . 但请记住,如果你想使用这种方法(使用窗口对象作为Widget容器),那么我再次建议所有Radiobutton使用self (窗口对象)作为他们的父母而不是root Because when using root as the parent, calling grid(row=0, column=0) on radio buttons, they would be using the same row/column that the window object (frame) is using. 因为当使用root作为父节点时,在单选按钮上调用grid(row=0, column=0) ,它们将使用窗口对象(框架)正在使用的相同行/列。

app is an instance of Window . appWindow一个实例。 Window is a Frame . Window是一个Frame The widgets created by init_window sometimes have self as the parent, and sometimes root . init_window创建的小部件有时会将self作为父级,有时则是root The first thing you need to fix is that they all need to have a parent of self . 你需要解决的第一件事情是,它们都需要有家长self The point of this sort of design, which you've no doubt copied from somewhere, is that everything inside this class should be contained in this frame. 这种设计的意义,你毫无疑问是从某个地方复制过来的,这个类中的所有东西都应该包含在这个框架中。

Because app is an instance of Window and Window is a subclass of Frame , everything that goes in this widget will be invisible unless app is visible. 由于app是一个实例WindowWindow是的子类Frame ,在这个小部件去将是无形的,除非一切app是可见的。 So, the second part of the solution is to make app visible. 因此,解决方案的第二部分是使app可见。 You can do this however you want. 你可以随心所欲地做到这一点。 In this particular case, since it's the only widget in the root window, I would use pack: 在这种特殊情况下,由于它是根窗口中唯一的小部件,我会使用pack:

app = Window(root)
app.pack(fill="both", expand=True)

You're making the label and entry their own parents and not starting their own event loops. 你正在制作标签并输入他们自己的父母,而不是开始他们自己的事件循环。 Instead of self , try changing their parent to root . 而不是self ,尝试将其父级更改为root

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

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