This question already has answers here:
TKinter - widgets not 'sticking' in Frame using grid layout
(2 answers)
Closed 2 years ago.
I want to create a resizable window, and I want all the widgets in there to resize when I resize the window.
For this, I'm trying to use grid() and give relative positions to the widgets.
This is the code:
c = tk.Canvas(width=400, height=320)
c.grid(row=0, column=0, sticky='w')
Label(root, textvariable=balance_string_var).grid(row=1, column=0, sticky="we")
balance_string_var.set("LABEL")
listbox = Listbox(root, height=30, width=100)
listbox.grid(column=1, row=0, rowspan=2, sticky="e")
I want the Listbox to be aligned to the right (look image).
How can I do it? Thanks
According to the document for the weight option of columnconfigure():
weight=
A relative weight used to distribute additional space between columns.
A column with the weight 2 will grow twice as fast as a column with weight 1.
The default is 0, which means that the column will not grow at all.
The listbox widget is put in column 1 of the grid layout of root and column 1 is the last column that contains widget, so calling root.rowconfigure(1, weight=1) will make column 1 to use all the remaining horizontal space.
And so setting sticky='e' in listbox.grid(..., sticky='e') will then put the listbox at the right-most of root window.
Use place instead, .place(relx=0, rely=0.05, relheight=0.03, relwidth=1) !! using 0-1.0 instead of pixels totally scales them properly, it's more flexible than grid
or use pack(side="right")
Related
I'm trying to resize a frame in tkinter, but the width does not change and function winfo_width() returns 1. How can i fix it?
from tkinter import *
root = Tk()
root.geometry('400x300')
Frame = LabelFrame(root, text="Test", width = 200)
Frame.grid(row = 0, column = 0)
label = Label(Frame, text = '').grid(row = 0, column=0)
print(Frame.winfo_width()) #output is 1 instead of 200
root.mainloop()
The width is returning 1 because the window hasn't been drawn yet. The actual width depends on the window being drawn since the actual width depends on many factors which can't be known before the window is actually drawn.
If you call root.update() before calling Frame.winfo_width() to force the window to be drawn, you will see it displaying the actual value.
As for how to change the width, that question is too broad to answer. Normally it's not wise to directly set the width of a frame. Tkinter by default will automaticaly resize a frame to fit its children. So, one way to make the frame wider is to add more widgets.
The width can also depend on how it is added to the display - whether you're using pack or grid or place, and how you have configured them. So, another way to make the frame wider is to use non-default options that cause the frame to grow or shrink to fit the space given to it.
If you want to specify an explicit size and ignore tkinter's automatic resizing, you can do that by turning off geometry propagation and then setting the width and height parameters for the frame. Depending on whether you're using grid or pack, you can call grid_propagate or pack_propagate with a False argument to disable propagation (place doesn't support geometry propagation).
Note that turning off geometry propagation is usually the least desirable solution because it requires you to do a lot more work to create a responsive UI. The best way to design GUI with tkinter is to focus on the size of the inner widgets and let tkinter compute the most efficient size for frames and the window itself.
As the others have pointed out how to set a static size frame using grid_propagate() I will show you how to set up your frame to resize automatically.
You need to tell the row and column to expand that the frame is in. This is done with columnconfigure() and rowconfigure(). Then you need to tell the frame to stick to all sides with sticky='nsew'. Adding widgets to the frame is no different then any other container. Simply tell the widget to be in the frame.
One potention issue I see is you are overwriting Frame() on this line: Frame = LabelFrame(root, text="Test", width = 200). This is a good example why you should not use import *. Instead do import tkinter as tk and use the tk. prefix for anything that needs it.
Example:
import tkinter as tk
root = tk.Tk()
root.geometry('400x300')
root.columnconfigure(0, weight=1)
root.rowconfigure(0, weight=1)
frame = tk.LabelFrame(root, text='Test', width=200)
frame.grid(row=0, column=0, sticky='nsew')
label = tk.Label(frame, text='label').grid(row=0, column=0)
root.mainloop()
Results:
Update:
If you do want something static make sure you define both height and width. If you only define one or the other then you will not see the frame in the window.
For a testable example for a static frame size:
import tkinter as tk
root = tk.Tk()
root.geometry('400x300')
root.rowconfigure(0, weight=1)
root.columnconfigure(0, weight=1)
frame = tk.LabelFrame(root, text='Test', height=200, width=200)
frame.grid(row=0, column=0)
frame.grid_propagate(False)
label = tk.Label(frame, text='label').grid(row=0, column=0)
root.mainloop()
Results:
Your frame can propagate on the grid based on the widgets on it, and not have fixed dimensions.
The output of 1 is due there being nothing on the Frame other than an empty Label. (It would still show 1 if there was no Label)
To get the output as 200, set the grid_propagate flag to False (only after setting your height and widht parameters), as follows:
frame = Frame(..., width=200)
frame.grid(row=0, column=0)
frame.grid_propagate(False)
I am attempting to create three frames: top, middle, and bottom. I successfully added widgets to my top and bottom frames and oriented them how I wanted them.
However, I am simply trying to add two entry widgets in the middle frame that will span across the entire width of the frame/window.
Due to the length of the code for all the widgets in the top and bottom frames, I'm just going to include a snippet of code for how my window and frames are configured:
root = Tk()
root.resizable(False, False)
top_frame = Frame(root)
middle_frame = Frame(root)
bottom_frame = Frame(root)
# I think this block is irrelavant to the question, but including it anyway just incase
rows = 0
while rows < 36:
top_frame.rowconfigure(rows, weight=1)
top_frame.columnconfigure(rows, weight=1)
bottom_frame.rowconfigure(rows, weight=1)
bottom_frame.columnconfigure(rows, weight=1)
rows += 1
top_frame.grid(row=0, column=0)
middle_frame.grid(row=1, column=0)
bottom_frame.grid(row=2, column=0)
Here is the code I'm using for the two entry widgets:
entry_1 = Entry(middle_frame)
entry_2 = Entry(middle_frame)
entry_1.grid(row=0, column=0, sticky=E+W)
entry_2.grid(row=1, column=0, sticky=E+W)
However, they just stick to the center of the middle frame. I've tried many solutions but nothing seems to change how these entry widgets look within the frame--always centered, never changing size. I even tried just packing them and setting their fill to X. I'm probably overlooking something very simple, but I can't quite figure it out.
Here is a picture for reference
The root of the problem is that the middle frame isn't configured to fill the full width of the window, so the widgets inside won't fill the full width of the window.
The first step is to use the sticky option on the middle window:
middle_frame.grid(row=1, column=0, sticky="ew")
Next, you haven't told grid what to do with extra space in the root window. As a rule of thumb any widget that uses grid to manage its children should have at least one row and one column with a weight greater than zero.
To get the middle frame to take up the full width of the window, give the column it is in a non-zero weight:
root.grid_columnconfigure(0, weight=1)
The same problem exists within the middle frame: you aren't instructing grid how to handle extra space. According to the rule of thumb we need to give column zero a weight within the middle frame:
middle_frame.grid_columnconfigure(0, weight=1)
That will allow the entry widgets in the middle frame to fill the middle frame, and we've configure the app so that the middle frame fills the full width of the window.
I'm new to tkinter and I've having trouble getting notebooks to fill horizontal space. I would like to place two notebooks at the top of the window, side-by-side. Each notebook should occupy half of the horizontal space of the window.
I initially tried using pack, but that made the notebooks expand unevenly, the right notebook took up more space than the left notebook. I thought that grid would work better, but I can't figure out how to get them to expand, even with passing weight=1 to columnconfigure.
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
root.geometry("800x100")
status = tk.Frame(root)
left_tabs = ttk.Notebook(status)
tab_a = tk.Frame(left_tabs)
tab_b = tk.Frame(left_tabs)
left_tabs.add(tab_a, text="a")
left_tabs.add(tab_b, text="b")
# left_tabs.pack(side=tk.LEFT, fill=tk.X, expand=True)
left_tabs.grid(row=0, column=0)
right_tabs = ttk.Notebook(status)
tab_c = tk.Frame(right_tabs)
tab_d = tk.Frame(right_tabs)
tab_e = tk.Frame(right_tabs)
right_tabs.add(tab_c, text="c")
right_tabs.add(tab_d, text="d")
right_tabs.add(tab_e, text="e")
# right_tabs.pack(side=tk.LEFT, fill=tk.X, expand=True)
right_tabs.grid(row=0, column=1)
tk.Label(tab_a, text="testing").pack()
tk.Label(tab_c, text="testing").pack()
status.columnconfigure(0, weight=1)
status.columnconfigure(1, weight=1)
status.pack(fill=tk.X)
title = tk.Label(root, text="title", bg="grey")
title.pack(fill=tk.X)
tk.mainloop()
grid allows you to request that two or more rows or columns have a uniform width or height. This option name is uniform - if two or more columns have the same setting for uniform, they will always be the same width. The value of uniform doesn't matter, as long as it is the same.
status.columnconfigure(0, weight=1, uniform="x")
status.columnconfigure(1, weight=1, uniform="x")
In addition, you need to use the sticky option so that your notebooks fill the space that has been allocated to them.
left_tabs.grid(row=0, column=0, sticky="nsew")
right_tabs.grid(row=0, column=1, sticky="nsew")
To make a gridded layout have all columns have the same width, you've got to configure those columns to have the same weight and to be in the same uniform group. This configuration is associated with the master widget, not any of the contained widgets (because columns can contain many widgets, of course).
sticky
Defines how to expand the widget if the resulting cell is larger than
the widget itself. This can be any combination of the constants S, N,
E, and W, or NW, NE, SW, and SE.
For example, W (west) means that the widget should be aligned to the
left cell border. W+E means that the widget should be stretched
horizontally to fill the whole cell. W+E+N+S means that the widget
should be expanded in both directions. Default is to center the widget
in the cell.
specifying the sticky argument when you do grid:
left_tabs.grid(row=0, column=0, sticky="NESW")
status.columnconfigure(0, weight=1, uniform="group_name")
right_tabs.grid(row=0, column=1, sticky="NESW")
status.columnconfigure(1, weight=1, uniform="group_name")
For the life of me, I cannot understand grid within Frame. I'm trying to create the below configuration, but I'm getting something different (highlighted area is the troublesome part).
Here is the code:
from tkinter import *
root = Tk()
weather_root = Frame(root,width=1000, height=5, bg = 'white')
weather_root.pack(side=TOP)
quote_root = Frame(root,width=1000, height =5, bg = 'white')
quote_root.pack(side=TOP)
news_root = Frame(root,width=1000, height =100, bg = 'white')
news_root.pack(side=TOP, fill= BOTH)
financial_root= Frame(root,width=1000, height =100, bg = 'white')
financial_root.pack(side=TOP, fill= BOTH)
# PROBLEM BOX
time_root = Frame(root, bg = 'yellow')
time_root.pack(side = RIGHT, fill= BOTH)
I'm very new to this still, so I'm sure it's something obvious, but what is it? (In the picture I have it split as two frames - that's the ultimate goal, but in the near term, I'm just trying to get the frame to show up against the right of the current placed frames). Thanks very much!
The expected output:
The actual output:
The pack geometry manager is not good for laying things out in a grid. Unsurprisingly, grid is the better choice. It is going to be very difficult to do what you want with pack unless you add additional frames specifically to aid in layout.
Doing this with grid is very straight-forward. It would look something like this:
weather_root.grid( row=0, column=0, sticky="nsew")
quote_root.grid( row=1, column=0, sticky="nsew")
news_root.grid( row=2, column=0, sticky="nsew")
financial_root.grid(row=3, column=0, sticky="nsew")
time_root.grid( row=0, column=1, sticky="nsew", rowspan=4)
You would also need to use root.grid_rowconfigure and root.grid_columnconfigure to apply weights to the rows and columns that should grow or shrink when the window is resized.
If you want to use pack, I recommend adding two extra frames: one for the left (gray), and one for the right (yellow). You can use pack for those two. Then, on the left you could use pack to stack the areas top-to-bottom. Whether that's the right solution in your specific case, I don't know.
Notes:
I strongly recommend grouping your calls grid or pack in this way. It's much easier to manage when they are all in one spot rather than interleaved with the other code.
I don't recommend using extra whitespace as showin in the example. I did it just to make it easier for you to see how the rows and columns relate.
For the canonical description of how pack works, see http://tcl.tk/man/tcl8.5/TkCmd/pack.htm#M26
The easiest way to accomplish the desired output would be to create two separate sub frames. You can pack the weather, quote, news and financial root frames into the left subframe, then pack the time frame into a right subframe. Last, you would pack them both into root, one using SIDE=LEFT and the other using SIDE=RIGHT. Additionally, it is possible to use Grid and Pack effectively within one app, but one individual widget (frame, for example) can only be managed using one layout manager (grid vs pack) at a time. So you can grid widgets such as frames into a frame, then pack that frame into another frame. Or, you could pack things into a subframe, then grid it into the main frame of the window.
a. Have placed a widget in the row 0 in the grid as shown below.
self.a_button = Button(root, text="A Button")
self.a_button.grid(row=0, column=1)
b. And tried placing another widget in row 2 inside the grid.
self.b_button = Button(root, text="B Button")
self.b_button.grid(row=2, column=1)
But when I run the program, I don't see any space between the widgets, rather its stacked once after the other.
So, how do I program to allow space between two widgets placed in different rows? Share your comments !!
When you pack the widget you can use
self.a_button = Button(root, text="A Button")
self.a_button.grid(row=0, column=1, padx=10, pady=10)
Using padx and pady you can add padding to the outer side of the button and alternatively if you want to increase the size of the button you can add inner padding using ipadx and ipady.
If you want more on the Grid function you can view all the options and uses here.
I think that you already got the answer, but I will share my solution to have space between two lines which works for me well.
spacer1 = tk.Label(win, text="")
spacer1.grid(row=4, column=0)
you can have this between to labels or entries as empty space at location row= 4, column= 0. You may want to modify the size of the space by adding pad sizes to spacer1.grid(row=4, column=0, padx= 10, pady= 10) or modify the label like spacer1 = tk.Label(win, text="", font=('Times New Roman, 40)) which ever works for you. The out put will be the space between two rows(3 & 5). I hope the solution helps you.