Tkinter frame problem : resizing does not work - python

I want to be able to use nested frames but there is a weird behavior : when I enter the height and width parameters they seem to not work. I use .grid() Is that what is causing the problem ? I use ttk Frame, is there some behavior I do not know about ?
I looked at the documentation but nothing seemed to be helping. I tried changing the parameters but I didn't help either.
from tkinter import *
from tkinter import ttk
root = Tk()
root.title("Tk test")
root.geometry("800x800")
frame_1 = ttk.Frame(root, height=400, width=400, relief="sunken")\
frame_1.grid(row=0, column=0, rowspan=1, columnspan=1)
frame_2 = ttk.Frame(frame_1, height=200, width=200, relief="sunken")\
frame_2.grid(row=0, column=0, rowspan=1, columnspan=1, sticky="N, S, W, E")
label_1 = ttk.Label(frame_2, text="Text")
label_1.grid(row=0, column=0, sticky="S, W, N, E")
root.mainloop()
Expected result : there is a sunken frame inside another sunken frame. Inside the nested frame there is a label named "Text"
Actual result : The label is always in the upper left corner and does not want to move.

You can give cells on a grid a minimum size using the grid_columnconfigure() and grid_rowconfigure methods, as documented here.
Applied to your code (along with other corrections & improvements):
import tkinter as tk
from tkinter import ttk
root = tk.Tk()
root.title("Tk test")
root.geometry("800x800")
frame_1 = ttk.Frame(root, height=400, width=400, relief="sunken")
frame_1.grid(row=0, column=0)
frame_2 = ttk.Frame(frame_1, height=200, width=200, relief="sunken")
frame_2.grid(row=0, column=0, sticky="NSWE")
frame_2.grid_rowconfigure(0, minsize=200)
frame_2.grid_columnconfigure(0, minsize=200)
label_1 = ttk.Label(frame_2, text="Text")
label_1.grid(row=0, column=0, sticky="NW")
root.mainloop()

Since the grid manager doesn't know how many rows and columns there are to be on the main window, it doesn't allot the frames with the defined height and width.
If you add padding to each frame, you will see that the Text widget in not the upper left corner. But the Text widget will always be in the upper left corner as it has been placed on the 0th row and column.
Also, use rowconfigure and columnconfigure to ensure that the frames take the space specified by you on the main window.

Related

tkinter deforms my frames after inserting objects

So I want to insert some objects in a frame, but when I firstly added a button the frames where were they weren't suppoused to.
Before
After
And this is the code:
import tkinter as tk
root = tk.Tk()
root.geometry("1200x700")
# Main frames
frame1 = tk.Frame(root, width=1200, height=625)
frame2 = tk.Frame(root, width=1200, bg="black", height=75)
frame1.grid(row=1, column=1)
frame2.grid(row=2, column=1)
# Secondary frames
frame1browser = tk.Frame(frame1, height=625, width=850, bg="grey")
frame1a = tk.Frame(frame1,height=625, width=(1200-850))
frame1browser.grid(row=1, column=1)
frame1a.grid(row=1, column=2)
# Last frames
frame1aa = tk.Frame(frame1a, width=(1200-850),height=525, bg="green")
frame1ab = tk.Frame(frame1a, width=(1200-850),height=100, bg="yellow")
frame1aa.grid(row=1, column=1)
frame1ab.grid(row=2, column=1, sticky="nswe")
# Elements that are not frames
Button1 = tk.Button(frame1ab, text="ur mother")
Button1.grid(column=1, row=1)
root.mainloop()
The frame ignores the width/height explicitly given if there is a widget inside it, by default. AFAIK, It finds and uses the minimum size required to fit all the widgets, also accommodating to extra properties like sticky, expand and so on.
To override this behavior, you will have to use the <grid/pack>_propagate(False) depending on whether you use pack or grid on the items inside the frame. Now the frame will grow/shrink as much as the size you specify.
frame1ab.grid_propagate(False)

tkinter LabelFrame center text

I'm trying to center text in the frame using LabelFrame function. My code is below.
import tkinter as tk
window = tk.Tk()
for i in range(9):
for j in range(9):
frame = tk.LabelFrame(
master=window,
relief=tk.RAISED,
borderwidth=5,
width=50,
height=50,
text=i+j,
labelanchor = 'n'
)
frame.grid(row=i, column=j)
window.geometry("500x500")
window.mainloop()
Argument labelanchor that specify position gives only options on the edge of the frame. Is there any simple way to center text inside of the frame using LabelFrame?
Is there any simple way to center text inside of the frame using LabelFrame?
The text of the labelframe can only appear along the edges of the frame. If you wish for text to appear inside the frame, you must create a Label and add it to the frame.
Using a frame and a label
If you wish to put other widgets in the frame and don't want this text to interfere with the other widgets, this is a perfect opportunity to use place.
The following example adds the label "Hello, world" to appear in the center of the widget. A button is placed in the frame just to show that its placement is not affected by the label, or vise versa. The screenshots show what the frame looks like naturally and when the window is resized.
import tkinter as tk
root = tk.Tk()
lf = tk.Frame(root, bd=2, relief="groove")
lf_label = tk.Label(lf, text="Hello, world")
lf_label.place(relx=.5, rely=.5, anchor="c")
lf.pack(padx=20, pady=20, fill="both", expand=True)
b = tk.Button(lf, text="Click me")
b.pack(padx=10, pady=10)
root.mainloop()
Using only a label
You can add widgets inside any other widget. So, instead of a frame you could just use a label. By default, the text will be centered, and just like with the previous example it won't affect the layout of other widgets.
I haven't included screenshots because the results are identical to the previous example.
import tkinter as tk
root = tk.Tk()
lf = tk.Label(text="Hello, world", bd=2, relief="groove")
lf.pack(padx=20, pady=20, fill="both", expand=True)
b = tk.Button(lf, text="Click me")
b.grid(row=0, column=0)
root.mainloop()

How do you make a child label smaller than its parent frame?

There is only one frame in my GUI, and it resizes itself to the size of the window. The frame has a child label, and I want the label to always be 1/3 the height of the frame and 1/1.5 the width of the frame. The code below tries to do that but the label always resizes itself to the size of the frame.
import tkinter
tk = tkinter.Tk()
tk.geometry("400x400")
f = tkinter.Frame(tk, bd=5, bg="white")
f.pack(padx=10, pady=10)
def callback(event):
f.config(height=tk.winfo_height(), width=tk.winfo_width())
l.config(width=int(f.winfo_width()/1.5), height=int(f.winfo_height()/3))
l = tkinter.Label(f, text="lead me lord", bg="yellow", relief=tkinter.RAISED, bd=5)
l.pack(side="bottom")
tk.bind("<Configure>", callback)
tk.mainloop()
The width and height of the label are in characters. In order to use pixels, you need to add an empty image to the label:
img = tkinter.PhotoImage() # an image of size 0
l = tkinter.Label(f, text="lead me lord", bg="yellow", relief=tkinter.RAISED, bd=5,
image=img, compound='center')
Actually you don't need to resize the frame in the callback if you add fill="both", expand=1 into f.pack(...):
import tkinter
tk = tkinter.Tk()
tk.geometry("400x400")
f = tkinter.Frame(tk, bd=5, bg="white")
f.pack(padx=10, pady=10, fill="both", expand=1)
def callback(event):
l.config(width=int(f.winfo_width()/1.5), height=int(f.winfo_height()/3))
#l.config(width=event.width*2//3, height=event.height//3) # same as above line if bind on frame
img = tkinter.PhotoImage()
l = tkinter.Label(f, text="lead me lord", bg="yellow", relief=tkinter.RAISED, bd=5,
image=img, compound='center')
l.pack(side="bottom")
f.bind("<Configure>", callback) # bind on frame instead of root window
tk.mainloop()
Given your precise specifications, the best solution is to use place since it lets you use relative widths and heights. However, if you plan to have other widgets in the window, place is rarely the right choice.
This example will do exactly what you asked: place the label at the bottom with 1/3 the height and 1/1.5 the width. There is no need to have a callback for when the window changes size.
Note: I had to change the call to pack for the frame. The text of your question said it would expand to fill the window but the code you had wasn't doing that. I added the fill and expand options.
import tkinter
tk = tkinter.Tk()
tk.geometry("400x400")
f = tkinter.Frame(tk, bd=5, bg="white")
f.pack(padx=10, pady=10, fill="both", expand=True)
l = tkinter.Label(f, text="lead me lord", bg="yellow", relief=tkinter.RAISED, bd=5)
l.place(relx=.5, rely=1.0, anchor="s", relheight=1/3., relwidth=1/1.5)
tkinter.mainloop()

Tkinter: Too much space between label and button frame

I'm pretty new to Tkinter and I build a little window with different widgets.
My Code looks like this:
import tkinter as tk
from tkinter import ttk
class Application(tk.Frame):
def __init__(self, master):
super().__init__(master)
self.master = master
self.master.geometry("800x600")
self.master.title("Tkinter Sandbox")
self.master.grid_rowconfigure(0, weight=1)
self.master.grid_columnconfigure(1, weight=1)
self._create_left_frame()
self._create_button_bar()
self._create_label_frame()
def _create_left_frame(self):
frame = tk.Frame(self.master, bg="red")
tree_view = ttk.Treeview(frame)
tree_view.column("#0", stretch=tk.NO)
tree_view.heading("#0", text="Treeview")
tree_view.pack(fill=tk.Y, expand=1)
frame.grid(row=0, column=0, rowspan=2, sticky=tk.N + tk.S)
def _create_button_bar(self):
frame = tk.Frame(self.master, bg="blue")
button_run_single = tk.Button(frame, text="Button 1")
button_run_all = tk.Button(frame, text="Button 2")
button_details = tk.Button(frame, text="Button 3")
button_run_single.grid(row=0, column=0)
button_run_all.grid(row=0, column=1, padx=(35, 35))
button_details.grid(row=0, column=2)
frame.grid(row=0, column=1, sticky=tk.N)
def _create_label_frame(self):
frame = tk.Frame(self.master, bg="blue")
name_label = tk.Label(frame, text="Label 1")
performance_label = tk.Label(frame, text="Label 2")
name_entry = tk.Entry(frame)
performance_entry = tk.Entry(frame)
name_label.grid(row=0, column=0)
name_entry.grid(row=0, column=1)
performance_label.grid(row=1, column=0)
performance_entry.grid(row=1, column=1)
frame.grid(row=1, column=1)
if __name__ == '__main__':
root = tk.Tk()
app = Application(root)
app.mainloop()
Between the three buttons and the label + entry frame is a huge space. I want the button and label + entry frame right under each other, without the huge space but the treeview should also expand vertically over the whole application window.
I think the problem might be my row and column configuration but I don't know how to solve this problem.
The way you've structured your code makes it hard to see the problem. As a good general rule of thumb, all calls to grid or pack for widgets within a single parent should be in one place. Otherwise, you create dependencies between functions that are hard to see and understand.
I recommend having each of your helper functions return the frame rather than calling grid on the frame. That way you give control to Application.__init__ for the layout of the main sections of the window.
For example:
left_frame = self._create_left_frame()
button_bar = self._create_button_bar()
label_frame = self._create_label_frame()
left_frame.pack(side="left", fill="y")
button_bar.pack(side="top", fill="x")
label_frame.pack(side="top", fill="both", expand=True)
I used pack here because it requires less code than grid for this type of layout. However, if you choose to switch to grid, or wish to add more widgets to the root window later, you only have to modify this one function rather than modify the grid calls in multiple functions.
Note: this requires that your functions each do return frame to pass the frame back to the __init__ method. You also need to remove frame.grid from each of your helper functions.
With just that simple change you end up with the button bar and label/entry combinations at the top of the section on the right. In the following screenshot I changed the background of the button_bar to green so you can see that it fills the top of the right side of the UI.
You need to change line
self.master.grid_rowconfigure(0, weight=1)
to
self.master.grid_rowconfigure(1, weight=1)
so that the second row takes all the space. Then you need to stick widgets from the label frame to its top by adding sticky parameter to the grid call in _create_label_frame:
frame.grid(row=1, column=1, sticky=tk.N)
I prefer to use the Pack Function since it gives a more open window - its easy to configure. When you use Pack() you can use labels with no text and just spaces to create a spacer, by doing this you won't run into the problem your facing.

Expand a listbox over 2 columns Tkinter

I have the following interface: 3 frames from which 2 are on the left-hand, frame1 and frame2 and another one which is on the right, frame3.
I want to create another frame, frame4 which will have the label Output and under it there should be a Listbox. I want that both these widgets to span over the previous frames, each being places in a cell by using the grid manager.
Also I am not sure if Listbox is the widget I should be using. I want something which will contain the output of the program I will run through my application. I also thought of Entry but I need something in which I can display more than one line.
This is the code I have so far:
from Tkinter import *
root = Tk()
frame1 = Frame(root)
frame1.grid(row=0,column=0)
frame2 = Frame(root)
frame2.grid(row=1,column=0)
frame3 = Frame(root)
frame3.grid(row=0,column=1)
frame4 = Frame(root)
frame4.grid(row=2,columnspan=2)
l5 = Label(frame4, text='Output:').grid(row=2,columnspan=2)
output = Listbox(frame4, height=5)
#output.grid(row=2,column=0,columnspan=2)
#output.pack(side=LEFT, fill=BOTH, expand=1)
root.mainloop()
I managed to make the label to span across the other frames, but when I uncommented either of the last 2 lines, the interface didn't open and the program just froze.
How can I solve this?
I slightly ammended your code:
from tkinter import * # for python 2.7 use Tkinter
root = Tk()
frame1 = Frame(root, bg='red', height=20)
frame1.grid(row=0,column=0, sticky=W+E)
frame3 = Frame(root, bg='blue', height=20)
frame3.grid(row=0,column=1, sticky=W+E)
frame2 = Frame(root, bg='green', height=20)
frame2.grid(row=1,column=0, sticky=W+E)
frame4 = Frame(root)
frame4.grid(row=2,columnspan=2, sticky=E+W)
l5 = Label(frame4, text='Output:', bg='orange').grid(row=0, column=0, sticky=E+W)
output = Listbox(frame4, height=5, width=50)
output.grid(row=1,column=0)
#output.pack(side=LEFT, fill=BOTH, expand=1)
root.mainloop()
This results in:
So basically what I did was to add bg, height and sticky parameters to frames and label to easily visual what is happening and how they frames are laid out. Also I modified grid parameters listbox and label.
Hope this is what you are after, or it will help you to get there.
If you use both .grid() and .pack() Tkinter (python 2.x) or tkinter (python 3.x) will happily spend the rest of your life trying to find a way to satisfy both, you must use one within the same window or Frame, see http://www.openbookproject.net/py4fun/gui/tkPhone.html

Categories

Resources