Python 2.7 / Tkinter multiple frames and menu

China☆狼群 提交于 2019-12-11 17:49:12

问题


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:

  1. you aren't requesting that widgets fill the space given to them
  2. you aren't telling tkinter how to allocate extra space
  3. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!