I have the menu frame on the left side and the main container on the right side but when I fire it up I only see them in a small box inside the main window.
How can I make the frames fit the window no matter what size it is?
Code:
class GUI(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title("GUI Project")
# self.resizable(0, 0)
menu = tk.Frame(self, relief="solid")
container = tk.Frame(self, relief="ridge")
menu.grid(row=0, column=0, rowspan=4, sticky="nsew")
container.grid(row=0, column=1, sticky="nsew")
menu.grid_rowconfigure(0, weight=1)
container.rowconfigure(0, weight=0)
container.columnconfigure(1, weight=0)
self.frames = ["Menu", "FutureFeature2", "TestPing", "FutureFeature3", "FutureFeature"]
self.frames[0] = Menu(parent=menu, controller=self)
self.frames[1] = FutureFeature2(parent=container, controller=self)
self.frames[2] = TestPing(parent=container, controller=self)
self.frames[3] = FutureFeature3(parent=container, controller=self)
self.frames[4] = FutureFeature(parent=container, controller=self)
self.frames[0].grid(row=0, column=0, sticky="nsew")
self.frames[1].grid(row=0, column=0, sticky="nsew")
self.frames[2].grid(row=0, column=0, sticky="nsew")
self.frames[3].grid(row=0, column=0, sticky="nsew")
self.frames[4].grid(row=0, column=0, sticky="nsew")
self.show_frame(1)
def show_frame(self, page_name):
frame = self.frames[page_name]
print(frame)
frame.tkraise()
frame.grid(row=0, column=0, columnspan=5, rowspan=5, sticky="nsew")
class Menu(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
button1 = tk.Button(self, text="Ping Test", bg="royalblue2",
command=lambda: controller.show_frame(2))
button2 = tk.Button(self, text="FutureFeature", bg="dark violet",
command=lambda: controller.show_frame(4))
buttun3 = tk.Button(self, text="FutureFeature", bg="pale goldenrod",
command=lambda: controller.show_frame(1))
button4 = tk.Button(self, text="Quit", bg="gray40",
command=lambda: self.terminate())
button1.pack(fill="both", expand=True)
button2.pack(fill="both", expand=True)
buttun3.pack(fill="both", expand=True)
button4.pack(fill="both", expand=True)
def terminate(self):
path = fr'c:/users/{os.getlogin()}/desktop/Gui-Skeleton'
try:
os.rmdir(path)
except OSError as err:
print(f"Error Deleting tmp folder! {err}")
exit()
if __name__ == "__main__":
path = fr'c:/users/{os.getlogin()}/desktop/Gui-Skeleton'
try:
if os.path.isdir(path):
pass
else:
os.mkdir(path)
except OSError as err:
print(f"[!] Operation failed! {err}")
app = GUI()
app.geometry("800x600")
app.mainloop()
.....................................................................................................................................................................................................................................................................................
The specific problem that you're asking about is due to the fact you are putting menu and container in the root window using grid but you haven't given any rows or columns in the root window a weight. Therefore, both menu and grid will use as little space as necessary.
A general rule of thumb is that when you use grid, you should give at least one row and one column a positive weight so that there is no unused space. You are failing to do that in the root window. For more information on grid weights see What does 'weight' do in tkinter?
The solution is simple: make sure that you've given at least one row and one column a positive weight in the root window:
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(1, weight=1)
That being said, I would recommend using pack for menu and container since they are the only widgets directly in the root window. pack is generally better than grid when laying out widgets in row or column, if for no other reason that you don't have to take the extra step of assigning weights.
menu.pack(side="left", fill="y")
container.pack(side="right", fill="both", expand=True)
Related
class View(tkinter.Tk):
def __init__(self):
tkinter.Tk.__init__(self)
self.geometry("500x500")
self.title("Switch Frame Example")
container_frame = tkinter.Frame(self)
container_frame.pack(side="top", fill="both", expand=True)
container_frame.grid_rowconfigure(0, weight=1)
container_frame.grid_columnconfigure(0, weight=1)
self.frames = {}
for F in (RedFrame, GreenFrame):
frame_name = F.__name__
frame = F(container_frame, self)#container_frame is the parent, self (the window) is the controller
self.frames[frame_name] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("RedFrame")
def show_frame(self, frame_name):
frame = self.frames[frame_name]
frame.tkraise()
class RedFrame(tkinter.Frame):
def __init__(self, parent, window):
tkinter.Frame.__init__(self, parent, bg="red", width="500", height="500")
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
self.grid_propagate(0)
goto_green_button = tkinter.Button(self, text='green frame', fg="green", width=25, command=lambda: window.tkraise("GreenFrame"))
goto_green_button.grid(row=0, column=0)
class GreenFrame(tkinter.Frame):
def __init__(self, parent, window):
tkinter.Frame.__init__(self, parent, bg="green", width="500", height="500")
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
self.grid_propagate(0)
goto_red_button = tkinter.Button(self, text='red frame', fg="red", width=25, command=lambda: window.tkraise("RedFrame"))
goto_red_button.grid(row=0, column=0)
The title of this post is the error I get when trying I click the goto_green_button. I used Bryan Oakley's code taken from this post as the basis for mine, but I'm not sure what I've done wrong, since his code actually works as it should, while mine does not. Why is "GreenFrame" a bad window path? It has the exact same name as the "GreenFrame" class, so shouldn't it work? Or does it have to do with the order of the frames and which is above the other?
Just started learning tkinter and im tackling on grid management.
I set the a frame to have 1 weight on column 0, therefore I expected label1 to be stretched to the end of the frame, then label2 added on column 1 with the length relative to its text size.
expected output: https://gyazo.com/65cf907e2cdea07d08844bdc8c62c7b2
output: https://gyazo.com/3c9e9c9f86372e01e96283545387c51e
Heres my code:
import tkinter as tk
class App(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, *kwargs)
container = tk.Frame(self)
container.pack(side="top", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)
frame_list = (MenuFrame,
)
self.geometry("1024x768")
self.frames = {}
for F in frame_list:
page_name = F.__name__
frame = F(parent=container, controller=self)
self.frames[page_name] = frame
frame.grid(row=0, column=0, sticky="nsew")
self.show_frame("MenuFrame")
def show_frame(self, page_name):
frame = self.frames[page_name]
frame.tkraise()
class MenuFrame(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
self.create_widgets(self)
def create_widgets(self, parent):
frame = tk.Frame(self, bg="white")
frame.grid_columnconfigure(0, weight=1)
frame.grid_rowconfigure(0, weight=1)
label1 = tk.Label(frame, text="Label one", bg="red")
label2 = tk.Label(frame, text="Label two", bg="blue", fg="white")
frame.grid(row=0, column=0, sticky="nsew")
label1.grid(row=0, column=0)
label2.grid(row=0, column=1)
if __name__ == "__main__":
app = App()
app.mainloop()
You are properly configuring the weight for frame. However, frame hasn't been properly configured to fill the window. Because of that, it's just barely wide enough for the two labels and thus the weight has no effect.
Since frame seems to be the only widget inside the class MenuFrame, I would use pack instead of grid since it only requires one line:
frame.pack(fill="both", expand=True)
If you prefer using grid, then you need to also configure the weight for its parent.
self.grid_rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
frame.grid(row=0, column=0, sticky="nsew")
I am using a template that I saw in a youtube video for my app. I have split the window in 3 using 3 frames. All 3 frames are nested into a main frame. The main frame is attached directly to the root window.
I am using pack manager in order to place mainframe inside the root window.
I am using grid manager in order to place the 3 frames inside the mainframe.
The issue that I'm facing is that when I try to resize the window the left frame gets resized more than the other 2 frames, and I don't understand why that happens. I need the frames to resize proportionately to each other. Why is the left frame getting resized more than the others? and how can I correct this?
import tkinter as tk
from tkinter import ttk
Normal_Font = ('Verdana', 9)
Large_font = ('Verdana', 12)
class MainApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
mainframe = tk.Frame(self)
mainframe.pack(side='top', fill='both', expand=True)
mainframe.grid_rowconfigure(0, weight=1)
mainframe.grid_columnconfigure(0, weight=1)
#mainframe.grid(row=0, column=0, sticky='nesw')
#left frame
employerframe = tk.Frame(mainframe)
employerframe.grid(row=0, column=0, sticky='nesw')
employerframe.grid_rowconfigure(0, weight=1)
employerframe.grid_columnconfigure(0, weight=1, uniform=1)
label1 = tk.Label(employerframe, text='Employer Frame', font=Large_font)
label1.grid(row=0, column=0, sticky='nesw')
ttk.Separator(mainframe, orient=tk.VERTICAL) .grid(row=0, column=1, sticky='ns')
#right frame
candidateframe = tk.Frame(mainframe)
candidateframe.grid(row=0, column=4, sticky='nesw')
candidateframe.grid_rowconfigure(0, weight=1)
candidateframe.grid_columnconfigure(0, weight=1, uniform=1)
label2 = tk.Label(candidateframe, text='Candidate Frame', font=Large_font)
label2.grid(row=0, column=0, sticky='nesw')
ttk.Separator(mainframe, orient=tk.VERTICAL) .grid(row=0, column=3, sticky='ns')
#middle frame
container = tk.Frame(mainframe)
container.grid(row=0, column=2, sticky='nesw')
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1, uniform=1)
self.frames = {}
frame = StartPage(container, self)
self.frames[StartPage] = frame
frame.grid(row=0, column=0, sticky='nesw')
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text='Start Page', font=Large_font)
label.pack(pady=10, padx=10)
app = MainApp()
app.mainloop()
It's because you are placing employerframe, container, candidateframe to column=0, column=2, column=4. But giving weight=1 to only column=0. you have to add the following lines also to give column, 0, 2, 4 equal weight.
mainframe.grid_columnconfigure(2, weight=1)
mainframe.grid_columnconfigure(4, weight=1)
Try This:
import tkinter as tk
from tkinter import ttk
Normal_Font = ('Verdana', 9)
Large_font = ('Verdana', 12)
class MainApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
mainframe = tk.Frame(self)
mainframe.pack(side='top', fill='both', expand=True)
mainframe.grid_rowconfigure(0, weight=1)
mainframe.grid_columnconfigure(0, weight=1)
# ----- Added lines -----
mainframe.grid_columnconfigure(2, weight=1)
mainframe.grid_columnconfigure(4, weight=1)
#left frame
employerframe = tk.Frame(mainframe)
employerframe.grid(row=0, column=0, sticky='nesw')
label1 = tk.Label(employerframe, text='Employer Frame', font=Large_font)
label1.grid(row=0, column=0, sticky='nesw')
ttk.Separator(mainframe, orient=tk.VERTICAL) .grid(row=0, column=1, sticky='ns')
#right frame
candidateframe = tk.Frame(mainframe)
candidateframe.grid(row=0, column=4, sticky='nesw')
label2 = tk.Label(candidateframe, text='Candidate Frame', font=Large_font)
label2.grid(row=0, column=0, sticky='nesw')
ttk.Separator(mainframe, orient=tk.VERTICAL) .grid(row=0, column=3, sticky='ns')
#middle frame
container = tk.Frame(mainframe)
container.grid(row=0, column=2, sticky='nesw')
self.frames = {}
frame = StartPage(container, self)
self.frames[StartPage] = frame
frame.grid(row=0, column=0, sticky='nesw')
self.show_frame(StartPage)
def show_frame(self, cont):
frame = self.frames[cont]
frame.tkraise()
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
label = tk.Label(self, text='Start Page', font=Large_font)
label.pack(pady=10, padx=10)
app = MainApp()
app.mainloop()
I am developping an application with tkinter. It is made of several frame (windows) put one onto the other. I can pass from one window to the other with a push button. I built it upon an example I saw here on Stack overflow, but I cannot recall the link to it.
I suspect that due to this choice I made, the tkinter does not close properly after I call the method self.quit().
The example below is a simplified version of my app, although it has the same structure than the full app.
With the code below, you can see that the application won't close before the end of the full program, i.e. after the 10 sec delay I put.
import tkinter as tk
import tkinter.ttk as ttk
import time
class App:
"""
Inherited from the Frame class
"""
def __init__(self):
self.root = tk.Tk()
self.root.title('Test')
# This frame will contain all windows
container = ttk.Frame(self.root)
container.pack(side="top", fill="both", expand=True)
# Create different pages/windows of the application
self.frames = {}
self.frames["Window1"] = Window1(parent=container, controller=self)
self.frames["Window2"] = Window2(parent=container, controller=self)
self.frames["Window3"] = Window3(parent=container, controller=self)
self.frames["Window1"].grid(row=0, column=0, sticky="nsew")
self.frames["Window2"].grid(row=0, column=0, sticky="nsew")
self.frames["Window3"].grid(row=0, column=0, sticky="nsew")
self.show_frame("Window1")
def run(self):
self.root.deiconify
self.root.mainloop()
def show_frame(self, page_name):
'''
Show a frame for the given page name
'''
frame = self.frames[page_name]
frame.tkraise()
class Window1(ttk.Frame):
def __init__(self, parent, controller):
ttk.Frame.__init__(self, parent)
self.controller = controller
self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
# Go to window 2
self.buttonW2 = ttk.Button(self, text='Go to 2', command=lambda: controller.show_frame("Window2"))
self.buttonW2.grid(row=0, column=0, sticky='NSE')
# Go to window 3
self.buttonW3 = ttk.Button(self, text='Go to 3', command=lambda: controller.show_frame("Window3"))
self.buttonW3.grid(row=0, column=1, sticky='NSE')
# EXIT button
self.buttonExit = ttk.Button(self, text='Exit', command=self.quit)
self.buttonExit.grid(row=0, column=2, sticky='NSE')
class Window2(ttk.Frame):
def __init__(self, parent, controller):
ttk.Frame.__init__(self, parent)
self.controller = controller
self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
# Go back button
buttonBack = ttk.Button(self, text="Back to 1", command=lambda: controller.show_frame("Window1"))
buttonBack.grid(row=0, column=3, sticky="NSE")
class Window3(ttk.Frame):
def __init__(self, parent, controller):
ttk.Frame.__init__(self, parent)
self.controller = controller
self.columnconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
# Go back button
buttonBack = ttk.Button(self, text="Back to 1", command=lambda: controller.show_frame("Window1"))
buttonBack.grid(row=1, column=1, sticky="E")
if __name__ == '__main__':
app = App()
app.run()
print('The App should be closed')
time.sleep(10)
print('The App is now closed')
Does anyone can help me for this?
Thanks
Tkinters quit command just stops the mainloop but doesn't destroy the window. If you want the window to be removed use destroy.
So create the exitbutton like:
self.buttonExit = ttk.Button(self, text='Exit', command=self.controller.root.destroy)
self.buttonExit.grid(row=0, column=2, sticky='NSE')
I need this simple form to:
1) correctly expand the fields when the window size is adjusted and
2) correctly scroll the list of fields.
I've tried every way I can think of and only 1 of the 2 above conditions are ever true. This code expands properly but does not scroll. Without frame2, and adding the fields to frame or canvas the opposite is true.
class test(Tkinter.Tk):
def __init__(self,parent):
Tkinter.Tk.__init__(self,parent)
self.parent = parent
self.initialize()
def makeform(self, root, fields):
i = 0
for field in fields:
Label(root, text=field + ": ", anchor=W).grid(row=i)
entry = Entry(root)
entry.grid(row=i, column=1, sticky=E+W)
entries[field] = entry
i += 1
def initialize(self):
frame = Frame(self)
frame.rowconfigure(0, weight=1)
frame.columnconfigure(0, weight=1)
frame.grid(sticky=N+S+E+W)
canvas = Canvas(frame, width=900, height=800, bg='pink')
canvas.grid(row=0, column=0, sticky=N+S+E+W)
canvas.columnconfigure(0,weight=1)
canvas.rowconfigure(0,weight=1)
frame2 = Frame(canvas)
frame2.grid(row=0, column=0, sticky=N+S+E+W)
frame2.columnconfigure(1, weight=1)
vscrollbar = Scrollbar(frame2,orient=VERTICAL)
vscrollbar.grid(row=0, column=2, sticky=N+S)
vscrollbar.config(command=canvas.yview)
canvas.configure(yscrollcommand=vscrollbar.set)
self.grid_rowconfigure(0, weight=1)
self.rowconfigure(0, weight=1)
self.grid_columnconfigure(0, weight=1)
self.columnconfigure(0, weight=1)
names = {'a','long','list','of','names','here'}
self.makeform(frame2, names)
Button(self, text='Quit', command=self.quit).grid(row=1, column=0, sticky=W, pady=4)
canvas.create_window(0, 0)
canvas.config(scrollregion=canvas.bbox(ALL))
self.grid()
if __name__ == "__main__":
entries = {}
app = test(None)
app.title('Hi ')
app.mainloop()
Update
Integrating Bryan's example below, this works for scrolling but does not expand the fields when the window is resized. I tried adding weight=1 to the second column of the frame but it does not help. How do I prevent the frame from shrinking?
class test(Tkinter.Frame):
def __init__(self,parent):
Tkinter.Frame.__init__(self, root)
self.canvas = Tkinter.Canvas(root, borderwidth=0, background="#ffffff")
self.frame = Tkinter.Frame(self.canvas, background="#ffffff")
self.vsb = Tkinter.Scrollbar(root, orient="vertical", command=self.canvas.yview)
self.canvas.configure(yscrollcommand=self.vsb.set)
self.vsb.pack(side="right", fill="y")
self.canvas.pack(side="left", fill="both", expand=True)
self.canvas.create_window((0,0), window=self.frame, anchor="nw",
tags="self.frame")
self.frame.bind("<Configure>", self.onFrameConfigure)
self.populate()
def onFrameConfigure(self, event):
'''Reset the scroll region to encompass the inner frame'''
self.canvas.configure(scrollregion=self.canvas.bbox("all"))
def makeform(self, fields):
i = 0
for field in fields:
Label(self.frame, text=field + ": ", anchor=W).grid(row=i)
entry = Entry(self.frame)
entry.grid(row=i, column=1, sticky=E+W)
entries[field] = entry
i += 1
Button(self.frame, text='Quit', command=self.quit).grid(row=i, column=0, sticky=W, pady=4)
def populate(self):
names = {'a','long','list','of','names','here'}
self.makeform(names)
self.frame.columnconfigure(1, weight=1)
self.frame.grid_columnconfigure(1, weight=1)
if __name__ == "__main__":
entries = {}
root=Tk()
test(root).pack(side="top", fill="both", expand=True)
root.mainloop()
There are at least three problems in your code.
First, the frame to be scrolled must be a part of the canvas. You can't use pack or grid to place it in the canvas, you must use create_window. You're calling create_window but you aren't telling it what window to add.
Second, the scrollbars are children of the frame, but I'm assuming those are the scrollbars you want to scroll the canvas. The need to be outside of the inner frame, and outside of the canvas.
Third, you need to set up a binding to the canvas's <Configure> event so that you can resize the inner frame and recompute the scrollregion of the canvas.
A complete working example of a scrollable frame is in this answer: https://stackoverflow.com/a/3092341/7432