问题
I want to make a admin tool with a menu to the left and changing frames to the right. I read that one tutorial about changing frames and tried to start from there. The issue is that the rightframe should always be 1200x750.. And that does not work. I know python but still got some learning to do on object oriented python... Does anybody see what i am missing here?
import Tkinter as tk
import tkFont as tkfont
class SampleApp(tk.Tk):
def __init__(self, *args, **kwargs):
tk.Tk.__init__(self, *args, **kwargs)
self.title_font = tkfont.Font(family='Helvetica', size=18, weight="bold", slant="italic")
self.grid_columnconfigure(1, weight=0)
self.grid_columnconfigure(2, weight=1)
self.grid_rowconfigure(1, weight=1)
leftframe = tk.Frame(self, width=300, height=750, bd=1, relief = tk.SOLID, bg="white")
rightframe = tk.Frame(self, width=1200, height=750, bd=1, relief = tk.SOLID, bg="white")
bottomframe = tk.Frame(self, width=1500, height=50, bd=1, relief = tk.SOLID, bg="white")
leftframe.grid(row=1,column=1, sticky="nsew")
rightframe.grid(row=1, column=2, sticky="nsew")
bottomframe.grid(row=2, column=1, columnspan=2, sticky="nsew")
leftframe.pack_propagate(0) # <-- still got this in to make the menu width fixed
button1 = tk.Button(leftframe, text="Start page" , anchor="w", bg="white", bd=0, command=lambda: self.show_frame("StartPage")).pack(padx=10, anchor="sw", fill="x")
button2 = tk.Button(leftframe, text="Page 1" , anchor="w", bg="white", bd=0, command=lambda: self.show_frame("PageOne")).pack(padx=10, fill="x")
button3 = tk.Button(leftframe, text="Page 2" , anchor="w", bg="white", bd=0, command=lambda: self.show_frame("PageTwo")).pack(padx=10, fill="x")
button4 = tk.Button(leftframe, text="Page 3" , anchor="w", bg="white", bd=0, command=lambda: self.show_frame("PageOne")).pack(padx=10, fill="x")
button5 = tk.Button(leftframe, text="Page 4" , anchor="w", bg="white", bd=0, command=lambda: self.show_frame("PageOne")).pack(padx=10, fill="x")
button5 = tk.Button(leftframe, text="Page 5" , anchor="w", bg="white", bd=0, command=lambda: self.show_frame("PageOne")).pack(padx=10, fill="x")
self.frames = {}
for F in (StartPage, PageOne, PageTwo):
page_name = F.__name__
frame = F(parent=rightframe, controller=self)
self.frames[page_name] = frame
# put all of the pages in the same location;
# the one on the top of the stacking order
# will be the one that is visible.
frame.config(bg="white")
frame.grid(row=0, column=0, sticky="news")
self.show_frame("StartPage")
self.init_topmenu()
def show_frame(self, page_name):
'''Show a frame for the given page name'''
frame = self.frames[page_name]
frame.tkraise()
def init_topmenu(self):
menubar = tk.Menu(self)
self.config(menu=menubar)
fileMenu = tk.Menu(menubar)
fileMenu.add_command(label="Page 1", command=lambda: self.show_frame("PageOne"))
fileMenu.add_command(label="Page 2", command=lambda: self.show_frame("PageTwo"))
menubar.add_cascade(label="File", menu=fileMenu)
class StartPage(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is the Startpage", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
class PageOne(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 1", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
class PageTwo(tk.Frame):
def __init__(self, parent, controller):
tk.Frame.__init__(self, parent)
self.controller = controller
label = tk.Label(self, text="This is page 2", font=controller.title_font)
label.pack(side="top", fill="x", pady=10)
if __name__ == "__main__":
app = SampleApp()
window_width = 1500
window_height = 800
width = app.winfo_screenwidth()
height = app.winfo_screenheight()
app.geometry('%dx%d+%d+%d' % (window_width, window_height, width*0.5-(window_width/2), height*0.5-(window_height/2)))
app.mainloop()
回答1:
Overview
You are making three common mistakes:
- you aren't requesting that widgets fill the space given to them
- you aren't telling tkinter how to allocate extra space
- you are turning off geometry propagation
Making widgets fill their allocated space
The first problem can be solved by using the sticky
attribute when calling grid
. For example:
leftframe.grid(row=1,column=1, sticky="nsew")
rightframe.grid(row=1, column=2, sticky="nsew")
bottomframe.grid(row=2, column=1, columnspan=2, sticky="nsew")
You also need to do this with each page:
frame.grid(row=0, column=0, sticky="nesw")
Allocating unused space
The second problem can be solved by giving rows and columns a weight. Tkinter will use the weight
of a row or column to determine if that row or column should be allocated any leftover space. This is perhaps the most common mistake people make when using grid
.
As a rule of thumb, any time you use grid
, the master widget should have at least one row and one column with a non-zero weight.
self.grid_columnconfigure(1, weight=0)
self.grid_columnconfigure(2, weight=1)
self.grid_rowconfigure(1, weight=1)
Don't turn off geometry propagation
You are turning off geometry propagation, which is rarely a good solution. I strongly encourage you to remove all calls to grid_propagate
and pack_propagate
. The best way to use tkinter is to configure the inner widgets, and let the containing widgets grow or shrink to accomodate. When you set up your GUI properly it will be exactly the right size no matter what resolution or fonts are active at the time.
I've used tk or tkinter for over 20 years, and have needed to turn off geometry propagation no more than a very, very few times in all those years. It's a somewhat advanced option that's really only useful for some specific edge cases.
Organize your code
This isn't contributing to the problem, but it contributes to making your code easier to understand. I strongly encourage you to separate the creation of widgets from organizing them on the screen. When you read "widget, grid, widget, grid, widget, grid", it's hard to make a mental image of what you're building. Instead, if you have "widget, widget, widget" followed by "grid, grid, grid" it becomes completely clear what you're building.
For example, I would rearrange the start of your code to look like this:
self.grid_columnconfigure(1, weight=0)
self.grid_columnconfigure(2, weight=1)
self.grid_rowconfigure(1, weight=1)
leftframe = tk.Frame(self, width=300, height=750, bd=1, relief = tk.SOLID, bg="white")
rightframe = tk.Frame(self, width=1200, height=750, bd=1, relief = tk.SOLID, bg="white")
bottomframe = tk.Frame(self, width=1500, height=50, bd=1, relief = tk.SOLID, bg="white")
leftframe.grid(row=1,column=1, sticky="nsew")
rightframe.grid(row=1, column=2, sticky="nsew")
bottomframe.grid(row=2, column=1, columnspan=2, sticky="nsew")
Notice how I also explicitly set the parent when creating leftframe
, rightframe
and bottomframe
. There's no reason not to, and it makes the code more self-documenting.
来源:https://stackoverflow.com/questions/46161761/python-2-7-tkinter-multiple-frames-and-menu