动态实例化页面Tkinter

时间:2017-04-14 15:34:24

标签: python-2.7 oop user-interface tkinter

我正在使用最初来自另一个堆栈交换答案的代码构建GUI。我已修改它,因为我想将变量传递给后续页面并让它们显示值。作为一种方法,我不是在按钮事件上显示,而是尝试使用show()方法创建页面:

import Tkinter as tk
#import vpnRename12 as Rename
#import vpnRename_pullconfig as pullConfig

"""
Create multiple pages to go through in the same frame. Use lift() to bring
the desired page into view and stack them on top of one another. Define
page-specific methods in each page.
"""

class vpnRenameProgram(tk.Tk):
    """
    Create a page class that will allow you to lift to front the applicable
    page with the proper functions to keep the script running.
    """
    def __init__(self,*args,**kwargs):

        tk.Tk.__init__(self,*args,**kwargs)

        # Create an empty dictionary to contain all of the page names
        self.frames = {}

        self.show("MainView")

    # Instantiate the next page on call, rather than at start to allow 
    flexibility
    def instantiate_page(self, cls):
        """
        Since all of the pages are defined in the same scope/namespace, we 
        can use
        the globals()[] dict to find and instantiate the pages dynamically 
        with the show() method.
        cls is the class argument we are doing a lookup on in the global() 
        dict. 
        """

        try:
            newframe = globals()[cls](container,self)
            page_name = newframe.__name__
        except:
            print("\nError defining inline class %s"%cls)#("Class %s is not 
                defined" %cls)
            newframe = None
            page_name=globals()[cls].__name__

        return newframe, page_name




    # Create lift function to bring desired page to front of view, 
    #instantiate page if
    # it isn't already (check frames dict)
    def show(self, cls):
        if cls not in self.frames.keys():
            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, page_name = self.instantiate_page(cls)

            if frame==None:
                frame = globals()[cls](parent=container, controller=self)

            self.frames[page_name] = frame
            frame.grid(row=0, column=0, sticky="news")

        frame = self.frames[cls]
        frame.lift()

    def get_page(self, classname):
        """
        Return instance of page when it's class name is passed in as string
        """
        for page in self.frames.values():
            if str(page.__class__.__name__) == classname:
                return page
            return None

class MainView(tk.Frame):

    def __init__(self,parent, controller,**kwargs):
        tk.Frame.__init__(self,parent)
        self.controller = controller
        self.edit_directory="edit_dir"
        self.complete_directory ="comp_dir"

        TitleLabel = tk.Label(self, text="VPN Script Editor")
        TitleLabel.pack({"side":"top"})

        EditLabel = tk.Label(self, text="Edit File Directory: 
            %s"%self.edit_directory)
        EditLabel.pack()

        CompLabel = tk.Label(self, text="Completed File Directory: 
            %s"%self.complete_directory)
        CompLabel.pack()

        Next = tk.Button(self, text="Next", command=lambda: 
            controller.show("listVPN"))
        Next.pack()


class listVPN(tk.Frame):
    """
    This is the second page, it contains a text box where you will list the 
    names of the vpn's that you want to edit. It will also display the 
    directories
    obtained by the pullconfig script.
    """

    def read_list(self):
        vpn_list=str(self.var_vpn_list.get()).upper()
        return vpn_list

    def __init__(self, parent, controller, **kwargs):
        self.controller = controller
        tk.Frame.__init__(self, parent)

        self.var_vpn_list = tk.StringVar()


        label=tk.Label(self, text="Please list the VPNs desired to edit")
        label.pack()

        #Create text box to submit a list of vpns back to the main program
        vpnLabel = tk.Label(self, text="VPN Names").pack()
        self.TextBox = tk.Entry(self, textvariable=self.var_vpn_list)
        self.TextBox.pack()

        vpnListSubmit = tk.Button(self, text="Enter", command= lambda: 
        self.read_list() and self.controller.show("pickFiles"))
        vpnListSubmit.pack()


class pickFiles(tk.Frame):
    """
    Second page that allows you to select your desired files from the 
    edit directory specified in the config file. Check all desired files, 
    list will be returned to the program.
    """

    def get_vpn_list(self):
        list = self.controller.get_page("listVPN").var_vpn_list.get()
        self.vpn_list = str(list).upper()

        self.vpn_label.configure(text="VPN List: %s"%self.vpn_list)

        return self.vpn_list

    def __init__(self, parent, controller,**kwargs):

        # Inherits from the tk.Frame class 
        tk.Frame.__init__(self, parent)
        self.controller = controller
        self.vpn_list = tk.StringVar()

        list = self.controller.get_page("listVPN").var_vpn_list.get()
        self.vpn_list = str(list).upper()



        show_vpn = tk.Button(self, text="Show vpnlist", command = 
            self.get_vpn_list)
        show_vpn.pack()

        self.vpn_label = tk.Label(self, text="VPN List: %s" %self.vpn_list)
        self.vpn_label.pack()

        # todo: get external module function to run with variable input
        #file_list = Rename.searchFile(vpnlist)

# Execute program on calling the parent class       
if __name__=="__main__":
    app = vpnRenameProgram()
    app.mainloop()

编辑: 上面是我用导入注释掉的自定义脚本的整个代码。我的主要问题是关于布局。我希望框架叠加在一起,但它们不是。为什么要这样做以及什么能让我按照我想要的布局进行?

1 个答案:

答案 0 :(得分:0)

您的代码的主要问题是您正在创建多个容器。作为程序基础的代码专门设计为容器内有多个框架的单个容器。

第一步是创建容器,并保存一个引用,以便以后使用:

class vpnRenameProgram(tk.Tk):

    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)

        self.container = tk.Frame(self)
        self.container.pack(side="top", fill="both", expand=True)
        self.container.grid_rowconfigure(0, weight=1)
        self.container.grid_columnconfigure(0, weight=1)

        self.frames = {}
        self.show_frame("MainView")

我还建议您传递实际的类而不是类名。这样您就不必深入了解globals()以尝试根据名称找到合适的类。

将上面的最后一行更改为:

    self.show_frame(MainView)

您还需要更改get_page,但它现在只是一个简单的查找:

def get_page(self, page_class):
    return self.frames.get(page_class, None)

最后一步是重新定义show以按需创建框架。您已经创建了一个名为instantiate_page的方法,但我认为没有理由不将它全部放在一个函数中,因为它只需要几行代码:

def show(self, page_class):
    if page_class in self.frames:
        frame = self.frames[page_class]
    else
        frame = page_class(parent=self.container, controller=self)
        frame.grid(row=0, column=0, sticky="nsew")
        self.frames[page_class] = frame

    frame.tkraise()

这就是它的全部内容。在调用showget_page时,您只需要记住传递而不是类的名称(例如:controller.show(listVPN),controller.get_page(pickFiles) )`等等)