0

I am trying to align a label in the center of the window with two buttons under it, also centered. I have been googling and looking on here to figure out how to do it and I have found grid to be helpful but it is not doing what I expect. It works as I would expect if I put each of the widgets in a different row and column but if I put them in different rows and the same column, they just stay aligned left. What am I doing wrong with grid? Also, any suggestions on how I can improve the code overall would be appreciated.

I left out the LoadedMachine and CreateMachine classes because I don't feel they are needed. If they would be helpful, I can edit the question to add them.

class App(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        container = tk.Frame(self)
        container.pack(side='top', fill='both', expand=True)

        self.frames = {}

        for F in (StartPage, LoadedMachine, CreateMachine):
            page_name = F.__name__
            frame = F(parent=container, controller=self)
            self.frames[page_name] = frame
            frame.grid(row=0, column=0)
            frame.config(bg='white')

        self.show_frame('StartPage')

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


class StartPage(tk.Frame):

    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        self.controller = controller
        self.rowconfigure(0, weight=1)
        self.rowconfigure(1, weight=1)
        self.rowconfigure(2, weight=1)
        self.columnconfigure(0, weight=1)
        self.columnconfigure(1, weight=1)
        self.columnconfigure(2, weight=1)

        welcome_label = tk.Label(self, text='Welcome', bg='green', fg='white', font=('Verdana', 80))
        welcome_label.grid(row=0, column=1)

        loadButton = tk.Button(self, text='Load an existing state machine', command=lambda: controller.show_frame('LoadedMachine'))
        loadButton.config(highlightbackground='green', font=('Verdana', 18))
        loadButton.grid(row=1, column=1)

        createButton = tk.Button(self, text='Create a new state machine', command=lambda: controller.show_frame('CreateMachine'))
        createButton.config(highlightbackground='green', font=('Verdana', 18))
        createButton.grid(row=2, column=1)    


if __name__ == '__main__':
app = App()
app.title('Cognitive State Machine')
app.geometry('800x600')
app.mainloop()

This is what I get:

enter image description here

I want the buttons to be closer together and closer to the label.

Jordan Ward
  • 29
  • 1
  • 7

2 Answers2

1

One suggestion is to first add some background colors when you create your frames for troubleshooting.

class App(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        container = tk.Frame(self,bg="yellow")
        container.pack(side='top', fill='both', expand=True)
        container.grid_rowconfigure(0, weight=1)
        container.grid_columnconfigure(0, weight=1)
        ...

When you run this, you will see a bunch of yellow color, which means your StartPage frame is not filling up the space. So you need to change it:

for F in (StartPage,):
        page_name = F.__name__
        frame = F(parent=container, controller=self)
        self.frames[page_name] = frame
        frame.grid(row=0,column=0,sticky="nesw")
        frame.config(bg='green')

Now you can see your background becomes green colour which means your StartPage frame correctly scales. Finally you can work on your labels:

def __init__(self, parent, controller):
    tk.Frame.__init__(self, parent)
    self.controller = controller
    self.columnconfigure(0, weight=1)
    ...

On why you need to add a weight to your columns, there is an excellent post here.

Henry Yik
  • 22,275
  • 4
  • 18
  • 40
  • Thank you this helped a bunch. Except in the for F in (StartPage,): loop if I change it to frame.pack(fill='both', expand=True), it displays everything I have for all the frames without hitting a button to actually switch to another frame. How do I fix this? – Jordan Ward Mar 01 '19 at 16:07
  • Without the other two pages, I am not sure how you want your frames to show. But you can use `grid` as well and set the weight accordingly even for your frames. – Henry Yik Mar 01 '19 at 16:09
  • I want to switch frames entirely when I click either the loadButton or createButton and only show the widgets that are on that frame. – Jordan Ward Mar 01 '19 at 16:15
  • I have edited the answer. In that case you need to set the `weight` on your `container` frame as well. – Henry Yik Mar 01 '19 at 16:21
  • Awesome, I really appreciate it! Works like a charm. – Jordan Ward Mar 01 '19 at 16:26
  • One more problem. There is too much space between the label and the first button. Also too much space between the two buttons themselves. How can I make them closer to the label and each other? – Jordan Ward Mar 01 '19 at 16:39
  • Well i overlooked that you start your column index with 1 instead of 0. In that case, `self.columnconfigure(0, weight=1)` in your StartPage frame. – Henry Yik Mar 01 '19 at 16:49
  • I have that but they are still far apart. I have updated my code and added a picture of what I am getting. What am I doing wrong to make them so far apart? – Jordan Ward Mar 01 '19 at 17:01
0

Add padding to the grid to align it how you want

You can add padx or pady according to your need

loadButton.grid(row=1, column=1, padx=10, pady=20)

Helpfull link to further play with grid layout

Also instead of 'lambda' you could use 'partial' as we need to call the function from the command function and define it there.

mr_pool_404
  • 488
  • 5
  • 16