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

How widgets dynamically expand the GUI

You probably noticed in previous screenshots and by running the code that widgets have a capability to extend themselves to the space they need to visually display their text.

Note

Java introduced the concept of dynamic GUI layout management. In comparison, visual development IDEs like VS.NET lay out the GUI in a visual manner, and are basically hard-coding the x and y coordinates of UI elements.

Using tkinter, this dynamic capability creates both an advantage and a little bit of a challenge, because sometimes our GUI dynamically expands when we would prefer it rather not to be so dynamic! Well, we are dynamic Python programmers, so we can figure out how to make the best use of this fantastic behavior!

Getting ready

At the beginning of the previous recipe we added a label frame widget. This moved some of our controls to the center of column 0. We might not wish this modification to our GUI layout. Next, we will explore some ways to fix this.

How to do it...

Let us first become aware of the subtle details that are going on in our GUI layout, in order to understand it better.

We are using the grid layout manager widget and it lays out our widgets in a zero-based grid.

Using the grid layout manager, what is happening is that the width of any given column is determined by the longest name or widget in that column. This affects all rows.

By adding our LabelFrame widget and giving it a title that is longer than some hard-coded size widget like the top-left label and the text entry below it, we dynamically move those widgets to the center of column 0, adding space to the left and right sides of those widgets.

Incidentally, because we used the sticky property for the Checkbutton and ScrolledText widgets, those remain attached to the left side of the frame.

Let's look in more detail at the screenshot from the first recipe of this chapter:

We added the following code to create the LabelFrame and then placed labels into this frame:

# Create a container to hold labels
labelsFrame = ttk.LabelFrame(win, text=' Labels in a Frame ')
labelsFrame.grid(column=0, row=7)

Since the text property of the LabelFrame, which is displayed as the title of the LabelFrame, is longer than both our Enter a name: label and the textbox entry below it, those two widgets are dynamically centered with the new width of column 0.

The Checkbutton and Radiobutton widgets in column 0 did not get centered because we used the sticky=tk.W property when we created those widgets.

For the ScrolledText widget we used sticky=tk.WE, which binds the widget to both the west (aka left) and east (aka right) side of the frame.

Let's remove the sticky property from the ScrolledText widget and observe the effect this change has.

scr = scrolledtext.ScrolledText(win, width=scrolW, height=scrolH, wrap=tk.WORD)
#### scr.grid(column=0, sticky='WE', columnspan=3)
scr.grid(column=0, columnspan=3)

Now our GUI has new space around the ScrolledText widget both on the left and right sides. Because we used the columnspan=3 property, our ScrolledText widget still spans all three columns.

If we remove columnspan=3, we get the following GUI, which is not what we want. Now our ScrolledText only occupies column 0, and, because of its size, it stretches the layout.

One way to get our layout back to where we were before adding the LabelFrame is to adjust the grid column position. Change the column value from 0 to 1.

labelsFrame.grid(column=1, row=7, padx=20, pady=40)

Now our GUI looks like this:

How it works...

Because we are still using individual widgets, our layout can get messed up. By moving the column value of the LabelFrame from 0 to 1, we were able to get the controls back to where they used to be and where we prefer them to be. At least the left-most label, text, checkbox, scrolledtext, and radio button widgets are now located where we intended them to be. The second label and text Entry located in column 1 have aligned themselves to the center of the length of the Labels in a Frame widget, so we basically moved our alignment challenge one column to the right. It is not so visible because the size of the Choose a number: label is almost the same as the size of the Labels in a Frame title, and so the column width was already close to the new width generated by the LabelFrame.

There's more...

In the next recipe, we will embed frames within frames to avoid the accidental misalignment of widgets we just experienced in this recipe.