Python tkinter窗口小部件在一个帧上影响另一个帧对齐

Python tkinter窗口小部件在一个帧上影响另一个帧对齐,python,tkinter,alignment,Python,Tkinter,Alignment,到目前为止,这个程序在功能方面仍然有效,所以这不是我的问题。我的问题与多帧小部件的对齐方式有关。如果我使一帧上的窗口小部件的宽度比其他帧长,它将基于最宽的帧而不是每个单独的帧来居中所有帧。现在代码中最宽的框架是“ChangePasswordPage”,因为它几乎总是使用一个长字符串作为标签;这会导致所有其他帧向左移动。如果我从frame.grid(column=0,row=0,sticky=“nsew”)中删除“sticky=”nsew“,它将正确地对齐所有内容,但框架不会向外填充,因此您可以看

到目前为止,这个程序在功能方面仍然有效,所以这不是我的问题。我的问题与多帧小部件的对齐方式有关。如果我使一帧上的窗口小部件的宽度比其他帧长,它将基于最宽的帧而不是每个单独的帧来居中所有帧。现在代码中最宽的框架是“ChangePasswordPage”,因为它几乎总是使用一个长字符串作为标签;这会导致所有其他帧向左移动。如果我从frame.grid(column=0,row=0,sticky=“nsew”)中删除“sticky=”nsew“,它将正确地对齐所有内容,但框架不会向外填充,因此您可以看到彼此后面的其他帧

我不知道如何解决这个问题,希望能得到一些帮助。我一直在尝试不同的方法,比如卸载每个小部件,然后重新加载,但也会出现错误。我们将非常感谢您的帮助

以下是我的简明代码:

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

通常有很多方法来解决布局问题。这完全取决于您实际想要做什么,您使用的实际小部件是什么,以及您希望小部件在其包含的窗口或框架发生变化时如何反应

当您使用
网格时
,默认情况下,它只提供小部件所需的空间。包含小部件的任何额外空间都将被闲置。如果希望
grid
使用所有可用空间,则必须告诉它如何通过赋予行和列“权重”来分配额外空间。您没有这样做,因此没有使用额外的空间,因此,您的小部件没有居中

根据经验,对于使用网格的任何容器(通常是框架),必须为至少一行和一列指定权重。如果您有一个画布或文本小部件,通常它所在的行和列就是您想要获得未分配空间的行和列。如果您有要增长和收缩的条目小部件,通常会为包含条目小部件的列赋予权重。通常,您会有多个行和/或列,希望获得额外的空间,尽管有时您希望所有内容都居中,并沿边缘分配额外的空间

在您的特定情况下,要在密码屏幕中居中显示所有内容,有几种解决方案。我将详细介绍几个

仅使用网格,并将所有小部件直接放置在页面上 当您希望将所有小部件绝对地安排在一个框架中,并使用
网格管理它们,并且希望所有内容都集中在窗口中时,您可以将所有权重赋予围绕内容的行和列

这里有一个例子。请注意,小部件位于第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)
使用嵌套框架 另一种方法是将小部件放入一个完全适合其内容的内部框架中。当您这样做时,您只需担心“页面”中的一个框架居中,因为当您居中框架时,根据定义,框架中的所有内容也将相对于整个窗口居中

当您这样做时,您可以跳过担心行和列的权重,因为您希望内部框架收缩以适合内容,因此不必担心未分配的空间。然而,我个人认为总是至少给出一行和一列的权重是好的

下面是一个使用内部框架技术的示例。请注意小部件是如何放置在内部框架中而不是
self
,并且内部框架使用
pack
,没有任何选项,这会导致它位于其父框架顶部的中心。您还可以使用
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)
总结 布局小部件需要有系统的方法。它有助于临时为每个帧指定不同的颜色,以便您可以看到哪些帧正在增长或收缩,以适合其内容和/或其父帧。通常情况下,这足以看出问题是出在框架、框架中的内容还是包含框架中的内容上


此外,在使用
pack
place
grid
时,几乎不应该不给出明确的参数。当您使用
grid
时,请始终至少为一行和一列赋予权重。

将元素保留在字典中(即
self.widgets
),然后您可以执行简单的
self.widgets[elem].grid\u remove()
而不是可怕的
buffer=“self.+elem+”.grid\u remove();eval(buffer)
这是出于测试目的,与应用程序其余部分的工作方式无关。谢谢你。现在我将在代码中添加一条注释。您真的需要登录和帐户模块来说明这个问题,以及其他许多代码吗?请阅读,然后尝试将问题简化为更简单的问题。@BryanOakley好的,我将在压缩代码后编辑它。非常感谢!我真的很感激你在回答中提到了这么多细节。这有助于我了解更多。真的,非常感谢——我已经尝试解决这个问题好几天了。祝您愉快:)