简体   繁体   English

一帧上的tkinter小部件影响另一帧对齐

[英]tkinter widgets on one frame affecting another frame alignment

this program works functionality wise so far anyway so that's not my issue. 到目前为止,该程序在功能上都是明智的,所以这不是我的问题。 My issue is something to do with how the alignment works for widgets with multiple frames. 我的问题与多帧小部件的对齐方式如何有关。 If I make the widgets on one frame longer width wise, than the other frames, it will center all the frames based on the widest frame rather than each individual frame. 如果我将小部件在宽度上比其他框架长一帧,它将基于最宽的框架而不是每个单独的框架将所有框架居中。 The widest frame right now in this code is 'ChangePasswordPage' as it will almost always use a long string for the label; 此代码中目前最宽的框架是“ ChangePasswordPage”,因为它几乎总是使用长字符串作为标签; this causes all of the other frames to shift to the left. 这会使所有其他框向左移动。 If I remove the 'sticky="nsew"' from frame.grid(column=0, row=0, sticky="nsew") it will allign everything properly but the frame doesn't fill outwards so you can see the other frames behind each other. 如果我从frame.grid(column = 0,row = 0,sticky =“ nsew”)中删除'sticky =“ nsew”',它将正确地对齐所有内容,但该框架不会向外填充,因此您可以看到其他框架彼此落后。

I am not sure how to fix this and would love some help. 我不确定如何解决此问题,希望获得帮助。 I have been trying different ways such as unloading each widget and then reloading it but getting errors there too. 我一直在尝试不同的方式,例如卸载每个小部件,然后重新加载它,但在那里也出现错误。 Any help will be highly appreciated. 任何帮助将不胜感激。

Here's my condensed code: 这是我的压缩代码:

import tkinter as tk
from tkinter import ttk, messagebox

class CCTV(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        container = tk.Frame(self)
        container.pack()
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)

        self.frames = {}

        for F in (LoginPage, ChangePasswordPage):
            frame = F(container, self)
            self.frames[F] = frame
            frame.grid(column=0, row=0, sticky="nsew")

        self.creatingAccount()

    def show_frame(self, cont):
        frame = self.frames[cont]
        frame.tkraise()

    def creatingAccount(self):
        self.show_frame(LoginPage)



class LoginPage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        self.createView()

    def createView(self):
        self.labelPassword = ttk.Label(self, text="Password")
        self.entryPassword = ttk.Entry(self, show = "*")
        self.buttonLogin = ttk.Button(self, text="Login", command=lambda: self.controller.show_frame(ChangePasswordPage))

        self.labelPassword.grid(row=2, column=3, sticky="w")
        self.entryPassword.grid(row=2, column=4, sticky="e")
        self.buttonLogin.grid(row=3, columnspan=6, pady=10)


class ChangePasswordPage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        self.createView()

    def createView(self):
        self.labelSecurityQuestion = ttk.Label(self, text="A very very very very very very very very very very long string")
        self.entrySecurityQuestion = ttk.Entry(self)
        self.buttonCreateAccount = ttk.Button(self, text="Change Password", command=lambda: self.controller.show_frame(LoginPage))

        self.labelSecurityQuestion.grid(row=3, column=0, sticky="w")
        self.entrySecurityQuestion.grid(row=3, column=1, sticky="e")
        self.buttonCreateAccount.grid(row=5, columnspan=2, pady=10)


app = CCTV()
app.geometry("800x600")
app.mainloop()

There are usually many ways to solve layout problems. 通常有很多方法可以解决布局问题。 It all depends on what you actually want to do, what actual widgets you're using, and how you expect widgets react when their containing windows or frames change. 这完全取决于您实际想要做的事情,正在使用的实际小部件以及当它们的包含的窗口或框架发生更改时您期望小部件如何反应。

When you use grid , by default it gives widgets only as much space as they need. 当您使用grid ,默认情况下,它仅为小部件提供所需的空间。 Any extra space within the containing widget will go unused. 包含的窗口小部件内的任何多余空间将不使用。 If you want grid to use all available space you must tell it how to allocate the extra space by giving rows and columns "weight". 如果要让grid使用所有可用空间,则必须通过赋予行和列“权重”来告诉它如何分配额外的空间。 You are not doing so, so extra space is not being used, and thus, your widgets aren't being centered. 您没有这样做,所以没有使用额外的空间,因此,您的小部件没有居中。

As a rule of thumb, for any container (typically, a Frame) that uses grid, you must give a weight to at least one row, and one column. 根据经验,对于使用网格的任何容器(通常是框架),必须至少对一行和一列赋予权重。 If you have a canvas or text widget, usually the row and column it is in is the one you want to get the un-allocated space. 如果您有画布或文本小部件,通常它所在的行和列就是您要获得未分配空间的空间。 If you have entry widgets that you want to grow and shrink, you'll often give a weight to the column(s) that contain the entry widgets. 如果您有想要增加和缩小的条目窗口小部件,则通常会对包含条目窗口小部件的列进行加权。 Often you'll have multiple rows and/or columns that you want to be given extra space, though there are also times where you want everything centered, with extra space being allocated along the edges. 通常,您会想为多行和/或多列分配额外的空间,尽管有时也需要将所有内容居中,并沿边缘分配额外的空间。

In your specific case, to center everything in the password screen there are several solutions. 在您的特定情况下,要将所有内容放在密码屏幕的中央,有几种解决方案。 I will detail a couple. 我会详细介绍几个。

Using only grid, and placing all widgets directly on the page 仅使用网格,并将所有小部件直接放置在页面上

When you want to arrange absolutely all of your widgets in one frame and manage them with grid , and you want everything centered in the window, you can give all of the weight to rows and columns that surround your content. 如果您想将所有小部件绝对排列在一个框架中,并使用grid管理,并且希望所有内容都集中在窗口中,则可以将所有权重赋予围绕内容的行和列。

Here's an example. 这是一个例子。 Notice that the widgets are in rows 1 and 2, and columns 1 and 2, and all extra space is being given to rows 0 and 3, and columns 0 and 3. 请注意,小部件位于第1和2行,第1和2列,并且所有额外的空间都分配给第0和3行以及第0和3列。

def createView(self):
    ...
    self.labelPassword.grid(row=1, column=1, sticky="e")
    self.entryPassword.grid(row=1, column=2, sticky="ew")
    self.buttonLogin.grid(row=2, column=1, columnspan=2)

    self.grid_rowconfigure(0, weight=1)
    self.grid_rowconfigure(3, weight=1)
    self.grid_columnconfigure(0, weight=1)
    self.grid_columnconfigure(3, weight=1)

Using a nested frame 使用嵌套框架

Another approach is to place the widgets into an inner frame that exactly fits its contents. 另一种方法是将小部件放置在完全适合其内容的内部框架中。 When you do that, you only have to worry about centering that one frame within the "page" since, when you center the frame, by definition everything in the frame will also be centered relative to the window as a whole. 这样做时,您只需要担心在“页面”中将一个框架居中,因为根据定义,当您将框架居中时,框架中的所有内容也将相对于整个窗口居中。

When you do that you can skip worrying about row and column weights because you want the inner frame to shrink to fit the contents and thus not have to worry about un-allocated space. 这样做时,您无需担心行和列的权重,因为您希望缩小内部框架以适合内容,从而不必担心未分配的空间。 However, I personally think it's good to always give at least one row and one column weight. 但是,我个人认为最好总是至少赋予一行和一列的权重。

Here's an example using the inner frame technique. 这是使用内框技术的示例。 Notice how the widgets are placed in the inner frame rather than self , and the inner frame uses pack with no options which causes it to be centered at the top of its parent. 请注意,小部件是如何放置在内部框架而不是self ,并且内部框架使用pack且没有任何选项,这导致其在其父项的顶部居中。 You could also use place , which is particularly convenient if you want the password prompt centered vertically as well. 您还可以使用place ,如果您还希望密码提示垂直居中,则特别方便。

def createView(self):
    inner_frame = tk.Frame(self)
    inner_frame.pack(side="top", fill="none")

    self.labelPassword = ttk.Label(inner_frame, text="Password")
    self.entryPassword = ttk.Entry(inner_frame, show = "*")
    self.buttonLogin = ttk.Button(inner_frame, text="Login", command=lambda: self.controller.show_frame(ChangePasswordPage))

    self.labelPassword.grid(row=1, column=1, sticky="e")
    self.entryPassword.grid(row=1, column=2, sticky="ew")
    self.buttonLogin.grid(row=2, column=1, columnspan=2)

Summary 摘要

Layout out widgets requires a methodical approach. 布局小部件需要一种有条理的方法。 It helps to temporarily give each frame a distinct color so that you can see which frames are growing or shrinking to fit their contents and/or their parents. 这有助于暂时为每个框架赋予不同的颜色,以便您可以查看正在增长或收缩的框架以适合其内容和/或其父级。 Often times that is enough to see whether the problem is with a frame, the contents in the frame, or the contents in the containing frame. 通常,足以查看问题出在框架,框架中的内容还是包含框架中的内容。

Also, you should almost never use pack , place or grid without giving explicit arguments. 另外,在没有给出明确参数的情况下,几乎永远不要使用packplacegrid When you use grid , always give at least one row and one column a weight. 使用grid ,请始终至少赋予一行和一列权重。

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

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