Python GUI Programming Cookbook
上QQ阅读APP看书,第一时间看更新

Aligning the GUI widgets by embedding frames within frames

We have much better control of our GUI layout if we embed frames within frames. This is what we will do in this recipe.

Getting ready

The dynamic behavior of Python and its GUI modules can create a little bit of a challenge to really get our GUI looking the way we want. Here we will embed frames within frames to get more control of our layout. This will establish a stronger hierarchy among the different UI elements, making the visual appearance easier to achieve.

We will continue to use the GUI we created in the previous recipe.

How to do it...

Here, we will create a top-level frame that will contain other frames and widgets. This will help us to get our GUI layout just the way we want.

In order to do so, we will have to embed our current controls within a central ttk.LabelFrame. This ttk.LabelFrame is a child of the main parent window and all controls will be children of this ttk.LabelFrame.

Up to this point in our recipes, we have assigned all widgets to our main GUI frame directly. Now we will only assign our LabelFrame to our main window, and after that, we will make this LabelFrame the parent container for all the widgets.

This creates the following hierarchy in our GUI layout:

In this diagram, win is the variable that references our main GUI tkinter window frame; monty is the variable that references our LabelFrame and is a child of the main window frame (win); and aLabel and all other widgets are now placed into the LabelFrame container (monty).

Add the following code towards the top of our Python module (see comment # 1):

# Create instance
win = tk.Tk()

# Add a title       
win.title("Python GUI")    

# We are creating a container frame to hold all other widgets # 1
monty = ttk.LabelFrame(win, text=' Monty Python ')
monty.grid(column=0, row=0)

Next, we will modify all the following controls to use monty as the parent, replacing win. Here is an example of how to do this:

# Modify adding a Label
aLabel = ttk.Label(monty, text="A Label")

Note how all the widgets are now contained in the Monty Python LabelFrame, which surrounds all of them with a barely visible thin line. Next, we can reset the Labels in a Frame widget to the left without messing up our GUI layout:

Oops - maybe not. While our frame within another frame aligned nicely to the left, it again pushed our top widgets into the center (a default).

In order to align them to the left, we have to force our GUI layout by using the sticky property. By assigning it "W" (West), we can control the widget to be left-aligned.

# Changing our Label
ttk.Label(monty, text="Enter a name:").grid(column=0, row=0, sticky='W')

How it works...

Note how we aligned the label, but not the text box below it. We have to use the sticky property for all the controls we want to left-align. We can do that in a loop, using the winfo_children() and grid_configure(sticky='W') properties, as we did before in recipe 2 of this chapter.

The winfo_children() function returns a list of all the children belonging to the parent. This enables us to loop through all of the widgets and change their properties.

Note

Using tkinter to force left, right, top, bottom the naming is very similar to Java: west, east, north and south, abbreviated to: "W" and so on. We can also use the following syntax: tk.W instead of "W".

In a previous recipe, we combined both "W" and "E" to make our ScrolledText widget attach itself both to the left and right sides of its container using "WE". We can add more combinations: "NSE" will stretch our widget to the top, bottom and right side. If we have only one widget in our form, for example a button, we can make it fill in the entire frame by using all options: "NSWE". We can also use tuple syntax: sticky=(tk.N, tk.S, tk.W, tk.E).

Let's change the very long label back and align the entry in column 0 to the left.

ttk.Label(monty, text="Enter a name:").grid(column=0, row=0, sticky='W')

name = tk.StringVar()
nameEntered = ttk.Entry(monty, width=12, textvariable=name)
nameEntered.grid(column=0, row=1, sticky=tk.W)

Note

In order to separate the influence that the length of our Labels in a Frame LabelFrame has on the rest of our GUI layout, we must not place this LabelFrame into the same LabelFrame as the other widgets. Instead we assign it directly to the main GUI form (win).

We will do this in later chapters.