Skip to content

MeMa Page Framework

Toby Benjamin Clark edited this page Jul 20, 2023 · 6 revisions

What is the MeMa Page Framework?

MeMa's interface stucture is optimized around ease of expansion and ease of modification. Each page is it's own sub-class of the content frame superclass in codebank/mema_content_frame.py, and should be created in it's own file, similar to the State Design Pattern, however instead of states, MeMa uses pages. This might bring the question ...

Common Questions & Answers

Here are some common questions about the MeMa Page Framework, regarding how it works, and it's reasoning and advantages.

What is a page in MeMa? Is it like a HTML Page?

In MeMa, a page is a frame that displays content to the user. In order to maintain integrity with expansion, the MeMa Page Framework doesn't facilitate direct input through the page, as this can become inaccessible if there isn't a direct physical button mapping.

So, this in turn will beg the question: How can I manage inputs? Well, in the framework, inputs are handled through a simple callback function that will get called when a button gets pressed, the user says something or the dial is rotated on the machine. These are passed to the page through json-style dictionaries, representing what was done, and through what means.

Why implement this through a framework?

The MeMa Page Framework has several notable advantages over direct application design, One of these is that the process of updating the buttons has been abstracted to a single function where you pass the new button names, callbacks and a boolean representing whether or not to read the new buttons aloud to the user.

The Framework still facilitates and caters for all Tkinter widgets, meaning that you can still access direct widget creation, modification and deletion. Alongside this, it helps maintain individual responsibility between pages, and ease of transition between pages. In short, the page framework should make it fast and easy to expand on and add to the MeMa System.

Where is the control flow between pages handled?

MeMa Pages are created and hosted within the main_window class, which essentially acts as an interface between the io_handler and the current page. The main_window is also responsible for switching between pages and controlling the button_frame and requests to the button_frame from the current page.

How to add a new page to the MeMa Framework?

Creating a new page and adding it to MeMa is an easy-process. If you don't want to type out the functions, here is a boilerplate implementation of a MeMa page that can be expanded to any need below.

Please note that this doesn't have imports, and will require both codebank/mema_constants.py and codebank/mema_content_frame.py. Imports aren't included in this boilerplate as they are relative and may differ dependent on where the file is located. See here for more information regarding imports in Python.

class content_page_name(content_frame):

    def __init__(self, parent, *args, **kwargs)-> None:

        # Calling the Superclass Constructor
        content_frame.__init__(self, *args, **kwargs)

        # Setting the parent of the Frame
        self.parent = parent

        # Declare any relevant use variables here.

        self.update_buttons()
        self.setup_gui()

    def update_buttons(self) -> None:

        # This is how to interface with the buttons frame in MeMa
        #
        # The index of the button represents the position so
        # buttons[0] is at the top and buttons[3] is at the bottom.
        #
        # Each index has either:
        #
        #   1. Tuple representing the text that is displayed on the
        #      button and the callback string.
        #
        #   2. MEMA_EMPTY_BUTTON, indicating the slot should be greyed
        #      and no button should be displayed.
        #
        # Buttons are set using the parent.set_input(buttons, spoken)
        # Where buttons is the array of name, callback tuples and spoken
        # is a boolean representing whether to read the button name.
        #

        buttons: list[(str, str)] = [0, 0, 0, 0]
        buttons[0] = ("Button One", "BUTTON_1_PRESSED")
        buttons[1] = MEMA_EMPTY_BUTTON
        buttons[2] = ("Button Three", "BUTTON_3_PRESSED")
        buttons[3] = ("Button Four", "BUTTON_4_PRESSED")
        self.parent.set_input(buttons, False)

    def setup_gui(self) -> None:

        # Create GUI elements here, since content_frame inherits directly
        # from tk.Frame, we can set widget masters to self. e.g.
        #
        # label = Label(self, text = "Example")
        # label.pack()

        pass

    def callback(self, callback_request: dict[str:str]) -> None:

        # Handle callback requests here. More information about
        # the callback request is available in the wiki

        match callback_request["content"]:

            case "BUTTON_1_PRESSED":
                # Do something
                pass

            case "BUTTON_3_PRESSED":
                # Do something
                pass

            case "BUTTON_4_PRESSED":
                # Do something
                pass

How can I manage button inputs?

This is marked in the boilerplate, but a more detailed explanation is as follows. When a content_frame is created, the first argument passed will always be the parent, which is a main_window instance.

buttons: list[(str, str)] = [0, 0, 0, 0]
buttons[0] = ("Button One", "BUTTON_1_PRESSED")
buttons[1] = MEMA_EMPTY_BUTTON
buttons[2] = ("Button Three", "BUTTON_3_PRESSED")
buttons[3] = ("Button Four", "BUTTON_4_PRESSED")
self.parent.set_input(buttons, False)

Setting the button display is done through this class, by calling the parent.set_input() function. This takes 2 inputs: buttons and spoken. Buttons is a list of 4 tuples representing the buttons text and the callback, spoken is a boolean and represents whether the new buttons should be spoken aloud via Text-To-Speech

The buttons parameter should consist of 4 tuples of type (String, String), where the first element represents the text to be displayed on the button and the second element is the string that will be passed to the callback when the button is pressed.

Please note that an empty button can be supplied by either passing a tuple of (None, None) or the MEMA_EMPTY_BUTTON constant. The button frame will detect this and leave a blank, grey space where that button slot would normally be.

How can I transition between pages in the MeMa Framework?

Changing the screens content is an integral part of any application. The MeMa Framework has abstracted this proccess down to a single function call inside of the main_application/parent. Simply call .switch_content(type, *args) on the parent of the current page. This function supports arguments so that data can easily be transferred between pages. Here's a working example from codebank/mema_content_memory_create_name.py:

self.parent.switch_content(content_memory_create_home, mempath)

In this snippet, content_memory_create_home is the new page to be created, however we do not pass in an instance of the class, instead we pass the type instead. The class is instantiated inside of main_window during the handling process.

Please note that returning back to the main menu may cause a circular dependency within the codebase and there is a special command to do this, to avoid the circular dependency, this is self.parent.reset_path(), which resets the page back to the main menu.