tkinter widgets on one frame affecting another frame alignment
<p>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; 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. </p>

<p>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.</p>

<p>Here's my condensed code:</p>

<pre><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()
</code></pre>

<p>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. </p>

<p>When you use <code>grid</code>, by default it gives widgets only as much space as they need. Any extra space within the containing widget will go unused. If you want <code>grid</code> to use all available space you must tell it how to allocate the extra space by giving rows and columns "weight". You are not doing so, so extra space is not being used, and thus, your widgets aren't being centered. </p>

<p>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.</p>

<p>In your specific case, to center everything in the password screen there are several solutions. I will detail a couple. </p>

<h2>Using only grid, and placing all widgets directly on the page</h2>

<p>When you want to arrange absolutely all of your widgets in one frame and manage them with <code>grid</code>, and you want everything centered in the window, you can give all of the weight to rows and columns that surround your content.</p>

<p>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. </p>

<pre><code>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)
</code></pre>

<h2>Using a nested frame</h2>

<p>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. </p>

<p>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 <em>always</em> give at least one row and one column weight. </p>

<p>Here's an example using the inner frame technique. Notice how the widgets are placed in the inner frame rather than <code>self</code>, and the inner frame uses <code>pack</code> with no options which causes it to be centered at the top of its parent. You could also use <code>place</code>, which is particularly convenient if you want the password prompt centered vertically as well.</p>

<pre><code>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)
</code></pre>

<h2>Summary</h2>

<p>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.</p>

<p>Also, you should almost never use <code>pack</code>, <code>place</code> or <code>grid</code> without giving explicit arguments. When you use <code>grid</code>, always give at least one row and one column a weight.</p>

python tkinter alignment