Tkinter Grid Again if Object Is Redefined
Hey there! This week we're returning to GUI development with Tkinter, and in this post we're going to be getting to grips with the grid
geometry manager.
If y'all missed out earlier posts on Tkinter, you can check them out in the links below. In these posts we cover some foundational information on working with Tkinter, and we embrace another geometry manager called pack
.
https://weblog.tecladocode.com/tkinters-pack-geometry-manager/
https://blog.tecladocode.com/side-values-in-tkinters-pack-geometry-manager/
Why should we care virtually grid
?
As I just mentioned, we've already dedicated ii posts to a different geometry manager called pack
, so why do we need another 1?
That's really a really good question, and equally it turns out grid
is a much newer add-on to Tk than pack
, and was introduced to overcome some of the shortcomings of the pack
geometry manager. It's now been a staple in Tk for ii decades, then it must be doing something correct!
As I mentioned in the first postal service on pack
, complex interfaces rapidly go overwhelming when using the pack
geometry manager, because it can become difficult to mentally go along track of how the various widgets in a given container collaborate. There are a lot of moving parts, and a lot of pieces of configuration to keep track of.
grid
is a lot more hands on than pack
, then we have to rely less on Tkinter automatically negotiating an acceptable layout for all of the relevant widgets. With grid
, nosotros instead ascertain a ii-dimensional table structure, and nosotros tell widgets to occupy certain cells withing that tabular array. This makes information technology a lot easier to reason nearly widget placement in more elaborate interfaces.
A minor downside is that we by and large have to specify more information up front, so pack
and its very succinct layout definitions is nonetheless nifty for simpler interfaces.
Without further ado, let's kickoff looking at how to work with grid
to position our widgets.
Defining the grid
The commencement step when using grid
is oftentimes to ascertain the nature of the actual filigree: the 2-dimensional table we'll use to position widgets.
The configuration for the grid is done in a parent container, rather than on the grid
aligned widgets themselves. This makes a lot of sense, because the configuration nosotros ascertain here is common to all of those widgets. All the widgets need to worry nearly is where they sit down within this table.
Defining a grid is not strictly necessary, and if nosotros don't supply one, Tkinter volition infer a table structure from the configuration we supply to the child widgets. More on this in a little bit.
We have 2 methods available to us for defining the layout of the grid: columnconfigure
and rowconfigure
.
They both have the same parameters, but as the names imply, they control different dimensions of the grid.
About of the time, you're going to specify two pieces of configuration for columnconfigure
and rowconfigure
: the columns or rows yous're configuring, and the weights of those columns or rows.
The weight
of a row or cavalcade determines how much of the available space a row or column should occupy relative to the other rows or columns. For example, a column with a weight
of 2
will exist twice equally wide equally a column with aweight
of 1
, assuming there's space for the widgets to fit.
In code, we might define two columns like this, where the container for our widgets is the root container:
import tkinter as tk root = tk.Tk() root.geometry("600x400") root.columnconfigure(0, weight=1) root.columnconfigure(1, weight=2) root.mainloop()
Once again we've gone dorsum to our trusty setup of a stock-still size window, and we've just included our usual Tkinter boilterplate. If you're not clear on the code higher up, take a look at our earlier pack
post where we cover a lot of this stuff: https://blog.tecladocode.com/tkinters-pack-geometry-manager/
In add-on to the usual lawmaking, we've defined two filigree columns, cavalcade 0
and column i
, where column 1
is twice as wide equally column 0
.
If we want to specify the same configuration for multiple columns, we tin can pass in a tuple of column numbers instead of just a unmarried number:
import tkinter as tk root = tk.Tk() root.geometry("600x400") root.columnconfigure((0, 2, iii), weight=1) root.columnconfigure(1, weight=2) root.mainloop()
Now we take four defined columns, where column ane
is twice every bit wide as whatsoever of the others.
rowconfigure
works exactly the aforementioned way.
Now that we've seen how to configure the container in society to set up a grid structure, we need to learn how to put items in the filigree.
For this example, we're going to return to our trusty rectangles, and nosotros're going to first by using the showtime, simpler grid nosotros defined above.
import tkinter as tk root = tk.Tk() root.geometry("600x400") root.columnconfigure(0, weight=one) root.columnconfigure(1, weight=ii) rectangle_1 = tk.Characterization(root, text="Rectangle one", bg="dark-green", fg="white") rectangle_2 = tk.Label(root, text="Rectangle 2", bg="cherry", fg="white") root.mainloop()
Here nosotros've defined a couple of coloured rectangles using the Label
widget. If the options used here are not articulate to y'all, have a look at our first pack
post.
At present that we've defined our rectangles, let'south utilise grid
to identify them in the window. Just as with the pack examples, we'll start using grid
with no meaningful configuration and so we tin see the default behaviour. Just like earlier, we'll exist adding a pocket-size corporeality of internal padding using ipadx
and ipady
to make the Characterization
text easier to read.
import tkinter as tk root = tk.Tk() root.geometry("600x400") root.columnconfigure(0, weight=ane) root.columnconfigure(ane, weight=two) rectangle_1 = tk.Label(root, text="Rectangle ane", bg="green", fg="white") rectangle_1.grid(ipadx=10, ipady=ten) rectangle_2 = tk.Label(root, text="Rectangle 2", bg="blood-red", fg="white") rectangle_2.filigree(ipadx=x, ipady=10) root.mainloop()
As we tin see the default configuration for grid
looks much the same every bit pack
. Nosotros just telephone call the grid
method on the relevant widget and whatever configuration is passed in as an argument.
Then what does this expect like in the window?
I don't know nigh y'all, merely that is not what I would have expected. Didn't we specify a two column grid for the main window?
Every bit it turns out, grid
is always going to put our widgets in new rows unless we specify otherwise. There'due south a pretty good reason for this, which is that there are actually an infinite number of rows and columns. Fifty-fifty if we only configure two columns, we tin can actually put things in column 2, 5, or 67, with empty columns collapsing to nil width.
Tkinter therefore opts to puts widgets one on tiptop of the other, because vertical scrolling is relatively common, and a default layout where 60 widgets are placed side by side is generally not going to be desirable. It usually takes less configuration to put widgets in the same row where necessary, rather than to define a row value for every single widget.
So how exercise nosotros specify where the widgets should go? We tin apply the row
and cavalcade
parameters.
import tkinter equally tk root = tk.Tk() root.geometry("600x400") root.columnconfigure(0, weight=1) root.columnconfigure(ane, weight=2) rectangle_1 = tk.Label(root, text="Rectangle ane", bg="green", fg="white") rectangle_1.grid(cavalcade=0, ipadx=ten, ipady=x) rectangle_2 = tk.Label(root, text="Rectangle 2", bg="red", fg="white") rectangle_2.grid(cavalcade=one, ipadx=ten, ipady=ten) root.mainloop()
Hither I've only defined a cavalcade value, putting the first rectangle in column 0
, and the second rectangle in column ane
. We might expect Tkinter to at present put the widgets in the aforementioned row, but this isn't the example. Instead they cease upwardly in the specified columns, only on unlike rows.
We therefore have to specify both row
and cavalcade
values for widgets to define the layout we desire in this example:
import tkinter every bit tk root = tk.Tk() root.geometry("600x400") root.columnconfigure(0, weight=1) root.columnconfigure(1, weight=2) rectangle_1 = tk.Label(root, text="Rectangle one", bg="green", fg="white") rectangle_1.filigree(column=0, row=0, ipadx=ten, ipady=10) rectangle_2 = tk.Label(root, text="Rectangle 2", bg="red", fg="white") rectangle_2.grid(column=1, row=0, ipadx=10, ipady=10) root.mainloop()
We now end upwards with both rectangles on the same row, occupying the ii different columns:
Note that we never actually defined whatever configuration for this row, so Tkinter is using a default row configuration. This means that the widgets are assigned merely the amount of vertical space required to house the widgets.
Because this row is using a completely default configuration, we could besides specify whatever row
value we wanted and the layout wouldn't change at all, as long as the 2 widgets shared the same row
value. Every bit I mentioned previously, empty rows and columns collapse to 0 superlative and width respectively.
That being said, using logical row and column numbering is definitely a good idea.
One matter you probably noticed is that our widgets are sitting in the middle of our 2 differently weighted columns, but neither widget is taking upward the space we allocated to it. Using pack
we had fill
and expand
properties which allowed usa to assign more space to widgets, and to tell them to occupy that space. When using grid
, we take a different property called sticky
.
gummy
accepts compass directions as values, and dissimilar combinations of these directions yield dissimilar results. You can call up of sticky
equally a combination of an alignment and fill up pick.
Specifying a single compass direction causes the widgets to "stick" to a single edge of its assigned area. For instance, we can set rectangle_1
to stick to the left side of its assigned area with "West"
, and rectangle_2
to stick to the right side of its assigned expanse with "Eastward"
.
import tkinter every bit tk root = tk.Tk() root.geometry("600x400") root.columnconfigure(0, weight=i) root.columnconfigure(1, weight=2) rectangle_1 = tk.Label(root, text="Rectangle 1", bg="green", fg="white") rectangle_1.grid(column=0, row=0, ipadx=10, ipady=10, sticky="Due west") rectangle_2 = tk.Label(root, text="Rectangle 2", bg="red", fg="white") rectangle_2.filigree(cavalcade=1, row=0, ipadx=ten, ipady=ten, sticky="E") root.mainloop()
This gives us something like this:
However, if we specify opposing compass directions, the widgets are stuck to both sides, which causes the widget to stretch:
import tkinter as tk root = tk.Tk() root.geometry("600x400") root.columnconfigure(0, weight=1) root.columnconfigure(1, weight=two) rectangle_1 = tk.Label(root, text="Rectangle one", bg="green", fg="white") rectangle_1.filigree(column=0, row=0, ipadx=ten, ipady=10, gummy="EW") rectangle_2 = tk.Label(root, text="Rectangle 2", bg="red", fg="white") rectangle_2.grid(column=1, row=0, ipadx=10, ipady=10, sticky="EW") root.mainloop()
We can now very conspicuously run across our column weights at piece of work, with rectangle_2
taking up twice every bit much space as rectangle_1
.
Then what happens when we set a widget to stick to all edges of its assigned area?
import tkinter as tk root = tk.Tk() root.geometry("600x400") root.columnconfigure(0, weight=1) root.columnconfigure(one, weight=2) rectangle_1 = tk.Label(root, text="Rectangle 1", bg="light-green", fg="white") rectangle_1.grid(column=0, row=0, ipadx=ten, ipady=10, gummy="NSEW") rectangle_2 = tk.Label(root, text="Rectangle 2", bg="red", fg="white") rectangle_2.grid(cavalcade=1, row=0, ipadx=x, ipady=10, mucilaginous="NSEW") root.mainloop()
Hither we've specified a "NSEW"
equally our viscous value for both widgets, which means information technology will stick to all edges of its assigned area. You can likewise use a tuple if you prefer, and Tkinter has constants divers which you tin use instead of strings:
rectangle_1.grid(column=0, row=0, ipadx=ten, ipady=10, gluey=("Due north", "S", "E", "West")) rectangle_1.filigree(column=0, row=0, ipadx=10, ipady=10, viscid=(tk.N, tk.Due south, tk.E, tk.W)) rectangle_1.filigree(cavalcade=0, row=0, ipadx=10, ipady=x, gluey=tk.NSEW)
They're all the exact same, so utilize whichever version you adopt.
However, the code might not produce the result yous expected:
Call up that the sticky
option causes the widget to stick to a given edge of its assigned surface area, only earlier nosotros said that past default, Tkinter just assigns an corporeality of row space equal to the meridian of the widgets inside. They don't need whatever more, after all.
Nosotros can specify a bit of configuration for row 0
to rectify this, giving it a weight of ane
.
import tkinter as tk root = tk.Tk() root.geometry("600x400") root.columnconfigure(0, weight=ane) root.columnconfigure(1, weight=ii) root.rowconfigure(0, weight=one) rectangle_1 = tk.Label(root, text="Rectangle i", bg="green", fg="white") rectangle_1.grid(column=0, row=0, ipadx=10, ipady=x, sticky="NSEW") rectangle_2 = tk.Label(root, text="Rectangle 2", bg="scarlet", fg="white") rectangle_2.grid(column=1, row=0, ipadx=10, ipady=10, mucilaginous="NSEW") root.mainloop()
And merely similar that, our widgets fill up the whole window:
Notation that by default, widgets have a viscid
value of None
, and then they volition be centred in the assigned area. Adding a row configuration, just no sticky
values will give us something like this:
Giving weights to empty rows
Before in the post I noted that empty rows collapse to naught tiptop, but this is merely true when they have a weight of 0
.
We can assign weights to empty rows to influence the layout of widgets in other rows.
For example, nosotros can give a weight of 1
to an empty row below the master content to limit the outset row to only half the screen:
import tkinter as tk root = tk.Tk() root.geometry("600x400") root.columnconfigure(0, weight=i) root.columnconfigure(i, weight=2) root.rowconfigure((0, 1), weight=1) rectangle_1 = tk.Label(root, text="Rectangle 1", bg="dark-green", fg="white") rectangle_1.grid(column=0, row=0, ipadx=10, ipady=ten, sticky="NSEW") rectangle_2 = tk.Label(root, text="Rectangle 2", bg="cherry-red", fg="white") rectangle_2.filigree(column=ane, row=0, ipadx=10, ipady=10, sticky="NSEW") root.mainloop()
Spanning rows and columns
The last matter nosotros take to talk about is spanning multiple rows and columns. When defining our grid, we're mostly concerned with the the layout of the smaller widgets within the window, but what if we need a header bar? Or if we need a large selection box on the side, like in a music player.
Luckily this is very easy in Tkinter, and takes a single slice of configuration.
Let's add together a third rectangle, then that we stop up with 1 spanning the entire top row:
import tkinter every bit tk root = tk.Tk() root.geometry("600x400") root.columnconfigure(0, weight=1) root.columnconfigure(1, weight=2) root.rowconfigure(ane, weight=1) rectangle_1 = tk.Characterization(root, text="Rectangle i", bg="green", fg="white") rectangle_1.filigree(column=0, row=0, columnspan=2, ipadx=20, ipady=xx, viscid="NSEW") rectangle_2 = tk.Characterization(root, text="Rectangle two", bg="cerise", fg="white") rectangle_2.grid(cavalcade=0, row=i, ipadx=10, ipady=10, viscous="NSEW") rectangle_3 = tk.Characterization(root, text="Rectangle 3", bg="bluish", fg="white") rectangle_3.grid(column=1, row=1, ipadx=10, ipady=10, sticky="NSEW") root.mainloop()
There were quite a few changes here, and then let'southward brand certain we properly understand what's going on.
First our rowconfigure
method is prepare to only affect row 1
, which is the second row. The first row is therefore going to be using the default configuration.
rectangle_1
now has a little bit of extra internal padding and has a new keyword argument of columnspan=ii
. This is what allows the widget to span multiple columns as we'll come across in a moment.
rectangle_2
is now on row 1
, and sits in the first column.
A new rectangle_3
was added, which sits in the second row and column, but is otherwise the same as rectangle_2
, just with some different text and a different colour background.
Here is what information technology all looks like:
If you lot need a widget to bridge multiple rows instead of multiple columns, you can apply rowspan
instead, only it otherwise works in exactly the same way.
Wrapping up
With that you should be able to create some pretty interesting layouts with Tkinter and the grid
geometry managing director. I really hope y'all learnt something new.
If you lot're interested in learning more about Tkinter, we've released an in-depth course where nosotros teach you everything you need to know, plus we build a tonne of cool Tkinter apps too. Bank check out the GUI Development with Python and Tkinter class!
Source: https://blog.teclado.com/tkinters-grid-geometry-manager/
0 Response to "Tkinter Grid Again if Object Is Redefined"
Post a Comment