josephholten
josephholten

Reputation: 3

How to display the labels inside frames, not instead of them

I'm making a my first GUI and am confused about how frames work. How do I place a label inside a frame?

class GUI:
    def __init__(self, window):
        self.window = window
        self.window.title("RPG")
        self.window.geometry("600x400")
        self.window.configure(bg="black")
        self.window.grid_anchor(anchor=CENTER)

        self.header = Frame(self.window, bg="green", height=25, width=600)
        self.frame = Frame(self.window, bg="red", height=375, width=600)

        self.header.grid(row=0)
        self.frame.grid(row=1)

        self.label1 = Label(self.header, text="Hello World", bg="black", fg="white")
        self.label2 = Label(self.frame, text="This is the Game")

        self.label1.grid(row=0, column=0)
        self.label2.grid(row=0, column=0)


root = Tk()
gui = GUI(root)
root.mainloop()

I would like to display both frames, just as color-blocks for now, and have a centered label in each, but it doesn't display the frames at all, just the labels.

Upvotes: 0

Views: 285

Answers (2)

Bryan Oakley
Bryan Oakley

Reputation: 386352

By default, frames (and all other widgets) will shrink or expand to fit their contents. So, when you put the labels inside the frames, the frames shrink to fit the label.

Also, you're using grid, and grid will create the smallest possible rows and columns to fit what is in the row and column. grid only uses as much space as necessary, and won't make use of extra space unless you've configured it to do so. Not only that, but grid will center items in the space allocated, so even if the column or row is larger than the item, the item won't fill that row or column.

While it may seem counter-intuitive at first, these rules actually work quite well once you understand how grid works.

Assuming that you want header and frame to fill the window, you need to configure grid to know what to do with extra available space. This is done by giving the row or column a "weight".

For example, assuming you want self.header to be a horizontal stip across the top, and self.frame to take up all of the extra space, you need to add the following statement:

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

With row zero having the default weight of 0 (zero), grid will give all extra vertical space to self.frame. By setting a weight of 1 (one) to column zero, that column will get all extra horizontal space.

With that, the frame will be allocated just enough space at the top for the header, and all of the extra space will be allocated to self.frame. There's one more step that needs to be done.

Even though self.frame has been given all of the extra space, you need to tell grid to stretch the frame to fit all of it's allocated space. You do this with the sticky option. It takes letters representing points on a compass: n (north), s (south), e (east), and w (west). To have the frame fill all of it's allocated space you want it to "stick" to all four sides:

self.frame.grid(row=1, column=0, sticky="nsew")

Similarly, to get the header to stretch to fill all the way across horizontally, you can tell it to "stick" to the east and west:

self.header.grid(row=0, column=0, sticky="ew")

With that, the green header frame will fill the top part of the screen, and the red frame will fill all of the rest of the space.

screenshot

This is probably still not exactly what you want, but the problem is identical. If you want the label to be allocated all of the space in the header you need to use give row and column 0 a weight. Doing that will center the label. If you want the label to fill the space allowed, use sticky.

Likewise, if you want all of the other label to fill the bottom frame, give that row and column a weight and the label will be centered in the space. If you want it to fill all of the allocated space, use sticky.

For example:

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

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

screenshot

Upvotes: 1

martineau
martineau

Reputation: 123531

The default borderwidth of a Frame is 0 which mean no border. If you want one, then specify a value greater the zero. For example — note the added bd=1 keyword arguments (you can also use borderwidth=1):

In addition, if you want the text centered in the labels, you have to give them a minimum width (number of characters) and specify how you want the text to aligned within that space via an anchor option.

import tkinter as tk


class GUI:
    def __init__(self, window):
        self.window = window
        self.window.title("RPG")
        self.window.geometry("600x400")
        self.window.configure(bg="black")
        self.window.grid_anchor(anchor="center")

        self.header = tk.Frame(self.window, bg="green", height=25, width=600, bd=1)
        self.frame = tk.Frame(self.window, bg="red", height=375, width=600, bd=1)

        self.header.grid(row=0)
        self.frame.grid(row=1)

        self.label1 = tk.Label(self.header, text="Hello World", bg="black", fg="white",
                      width=15, anchor='center')  # ADDED
        self.label2 = tk.Label(self.frame, text="This is the Game",
                      width=15, anchor='center')  # ADDED

        self.label1.grid(row=0, column=0)
        self.label2.grid(row=0, column=0)


root = tk.Tk()
gui = GUI(root)
root.mainloop()

Result: Screenshot showing result

Upvotes: 0

Related Questions