0

Using the below code originally posted here how can i use a 4th module with the PageList() command and a new page class below it to display it in my WindowHandler() class? through other research i believe you would have to stop using Windowhandler() as an instance of the main application class.

The goal of this is to have the stacked frames in different files while each file can update the frames={} list without any having to add it in the main class. a similar example is given here for different pages but the call to those pages must still be added within the main class.

Nav.py

import tkinter as tk
from Page import PageList
import Mypages

class Windowhandler(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)

        PageList("Page1", container, self, Mypages.PageOne)
        PageList("Page2", container, self, Mypages.PageTwo)
        self.show_frame("Page1")

    def show_frame(self, cont):
        frameref = PageList.frames[cont]
        print(frameref)
        frameref.tkraise()



app = Windowhandler()
app.mainloop()

Page.py

class PageList():
frames = {}
def __init__(self, name, parent, cont, ref):
    self.frames[name] = ref(parent=parent, controller=cont)

Mypages.py

    import tkinter as tk

    class PageOne(tk.Frame):
        def __init__(self, parent, controller):
            this = tk.Frame.__init__(self, parent)
            label = tk.Label(this, text="Welcome to Page 1")
            label.pack(pady=10, padx=10)

            button1 = tk.Button(this, text="Back to Home",
                        command=lambda: controller.show_frame("Page2"))
            button1.pack()

    class PageTwo(tk.Frame):
        def __init__(self, parent, controller):
            this = tk.Frame.__init__(self, parent)
            label = tk.Label(this, text="Welcome to Page 2")
            label.pack(pady=10, padx=10)

            button1 = tk.Button(this, text="Back to Home",
                        command=lambda: controller.show_frame("NewPage"))
            button1.pack()

Psuedo.py

import tkinter as tk
import Nav

PageList("NewPage", Nav.container, WindowHandler, NewPage)

class NewPage(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent)
        btn1 = Nav(self, text="A Button" command=lambda: Controller.show_frame("Page1"))
        btn1.pack(pady=(10, 4))
Chris James
  • 151
  • 2
  • 14
  • 1
    It sounds like you're wanting to create a plugin system, where new pages can be plugged in simply by importing the file. Am I correct? – Bryan Oakley Dec 30 '19 at 20:35
  • You are correct I have a importlib setup where new files are imported into my main file and I'd like each new file to extend the menu so each files has its own menu, yes this may of been above my skill level. – Chris James Dec 30 '19 at 21:40

1 Answers1

0

The code below is what i have managed to come up with in regards to the above problem, each frame is stored in a different file within a sub folder although it is loaded using importlib as that is what my application required.

This was achieved through having a common function inside each file addnav that would gets called by the Main.py file the function uses another function in Resource.py to keep track of the frames and this is shared among all files with a final Dstore() class that returns data about the subfile.


Main.py

from os import listdir
from importlib import import_module
from Resource import *


# Create our main window under the Windowhandler class.
class Windowhandler(tk.Tk):
    def __init__(self, *args, **kwargs):
        tk.Tk.__init__(self, *args, **kwargs)
        tk.Tk.title(self, "Stacked frames + Stacked window with dynamic imports")
        tk.Tk.geometry(self, "800x800")
        tk.Tk.state(self, "zoomed")

    # Used for displaying stacked frames from frames list in Resource.py
    def show_frame(self, cont):
        frame = Addnav.frames[cont]
        frame.tkraise()


# Find all modules inside module subfolder, import and return list of references.
def importscript(**kwargs):
    kwargs["pdir"] = kwargs.get("pdir", "/Users/Main/PycharmProjects/Plugin-Stacked-Frames/Modules")
    pdir = kwargs["pdir"]
    mlist = {}

    for fi, file in enumerate(listdir(pdir)):
        if file.endswith(".py") and file != "__init__.py":
            module = import_module("Modules." + file.strip(".py"))
            module.Dstore()
            notify = module.Dstore.Info
            print("Imported " + notify["name"])
            mlist[fi] = module
    return mlist


# This is our root menu that has a button for each sub menu of our sub files.
class MenuHome(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent, bg="white")
        tk.Label(self, text="Root Menu").pack(pady=(20, 5))
        # Enumerate through frames and add a button object referenced to each sub files menu.
        for idx, fref in enumerate(Addnav.frames):
            sinfo = sub[idx].Dstore.Info
            btn = tk.Button(self, text=sinfo["name"], command=lambda fref=fref: controller.show_frame(fref))
            btn.pack()


sub = importscript()
app = Windowhandler()

# Create 2 containers 1 for the menu 1 for the main workspace.
container = tk.Frame(app)
container.pack(side="left", fill="both", expand=True)
container.grid_rowconfigure(0, weight=1)
container.grid_columnconfigure(0, weight=1)

for N in sub:
    pref = sub[N].Dstore.Info
    sub[N].addnav(pref["reference"], container, app)

Addnav("MenuHome", container, app, MenuHome)
Windowhandler.show_frame(container, "MenuHome")

app.mainloop()

Subfile1.py (placed in subfolder called Modules)

from Resource import *


# For getting information about the individual subfile.
class Dstore():
    Info = {}
    def __init__(self):
        self.Info["reference"] = "Menu1"
        self.Info["name"] = "Sub Menu 1"


# Calls the shared resource Addnav function to add the menu.
def addnav(name, container, app):
    Addnav(name, container, app, Menu1)


# The menu class for this file.
class Menu1(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent, bg="blue")
        tk.Label(self, text="Welcome to subfile menu 1").pack(pady=(20, 5))
        btn1 = tk.Button(self, text="Home", command=lambda: controller.show_frame("MenuHome"))
        btn1.pack()
        btn2 = tk.Button(self, text="Subfile menu 2", command=lambda: controller.show_frame("Menu2"))
        btn2.pack()

Subfile2.py (placed in subfolder called Modules)

from Resource import *


# For getting information about the individual subfile.
class Dstore():
    Info = {}
    def __init__(self):
        self.Info["reference"] = "Menu2"
        self.Info["name"] = "Sub Menu 2"


# Calls the shared resource Addnav function to add the menu.
def addnav(name, container, app):
    Addnav(name, container, app, Menu1)


# The menu class for this file.
class Menu1(tk.Frame):
    def __init__(self, parent, controller):
        tk.Frame.__init__(self, parent, bg="red")
        tk.Label(self, text="Welcome to subfile 2 menu").pack(pady=(20, 5))
        btn1 = tk.Button(self, text="Home", command=lambda: controller.show_frame("MenuHome"))
        btn1.pack()
        btn2 = tk.Button(self, text="Subfile Menu 1", command=lambda: controller.show_frame("Menu1"))
        btn2.pack()

Resource.py

import tkinter as tk


class Addnav:
    frames = {}

    def __init__(self, name, parent, cont, ref):
        self.frames[name] = ref(parent=parent, controller=cont)
        self.frames[name].grid(row=0, column=0, sticky="nsew")

Colours used to make visually obvious, please note i am not a python guru and there may be a dozen or more mistakes in this otherwise functional code, comment for edits.

Chris James
  • 151
  • 2
  • 14