How to execute functions from different classes in a Model-View-Controller (MVC) architecture pattern?

41 views Asked by At

i am currently working on a python GUI and i use customtkinter (which is based on tkinter) for that. To enhance maintainability, scalability, and testability of the GUI i use the Model-View-Controller (MVC) architecture pattern, which decouples the business logic from the UI. This is giving me quite a problem because some of the functionalities of one model (reading a file) takes a few seconds. To indicate to the user that the GUI is not frozen i want to show the progress in a progressbar while executing the fie reading function in a thread.

Now my question is: How do i start the progressbar (which is in class B view) before the reading file function starts (which is in class A controller) and when its finished continue other functionalities?

This is my project simplified:

Structure overview

Project/
├── controllers/                    # Contains classes that act as intermediaries
                                    # between models and views. They handle user
                                    # input, update models, and reflect changes
                                    # in views.
│ ├── main_controller.py            # Handles interactions in the main window.
│ ├── class_a_controller.py         # Handles interactions in the class A view.
│ └── class_b_controller.py         # Handles interactions in the class B view.
├── models/                         # Contains classes that represent the data
                                    # and business logic of the application.
│ └── model.py
├── views/                          # Contains classes that define the visual
                                    # representation of the application.
│ ├── main_view.py                  # The visual representation of main window.
│ ├── class_a_view.py               # The visual representation of the class A view.
│ └── class_b_view.py               # The visual representation of the class B view.
└── app.py                          # Entry point of the application.

Code snippets

views/main_view.py

class MainView(ctk.CTkFrame):
        self.initialize_widgets()

    def initialize_widgets(self) -> None:
        """
        Initialize widgets.
        """
        self.classA_view: ctk.CTkFrame = ClassAView(self)
        self.classB_view: ctk.CTkFrame = ClassBView(self)

views/class_a_view.py

class ClassAView(ctk.CTkFrame):
    """
    Layout of the class A view.
    """

    def __init__(self, master: ctk.CTkFrame) -> None:
        super().__init__(master)

        self.initialize_widgets()

    def initialize_widgets(self) -> None:
        """
        Initialize widgets.
        """
        self.custom_frame_view: ctk.CTkFrame = CustomFrameView(self)


class CustomFrameView(ctk.CTkFrame):
    """
    Layout of the custom frame.
    """

    def __init__(self, master: ctk.CTkFrame) -> None:
        super().__init__(master)

        self.initialize_widgets()
        self.create_layout()

    def initialize_widgets(self) -> None:
        """
        Initialize widgets.
        """
        self.button= ctk.CTkButton(self, text="")

    def create_layout(self) -> None:
        """
        Create layout.
        """
        self.open_file_button.grid(row=0, column=1)

views/class_b_view.py

class ClassBView(ctk.CTkFrame):
    """
    Layout of the class B view.
    """

    def __init__(self, master: ctk.CTkFrame) -> None:
        super().__init__(master)

        self.initialize_widgets()

    def initialize_widgets(self) -> None:
        """
        Initialize widgets.
        """
        self.progressbar = ctk.CTkProgressBar(self)

controllers/main_controller.py

class MainController:
    """
    Functionality of the main application.
    """

    def __init__(self, master: ctk.CTk) -> None:
        self.__master: ctk.CTk = master
        self.__view: ctk.CTkFrame = MainView(master)

        self.__initialize_controller()

    def __initialize_controller(self) -> None:
        """
        Initialize controller.
        """
        self.classA_controller: ClassAController = ClassAController(self.__view.classA_view)
        self.classB_controller: ClassBController = ClassBController(self.__view.classB_view)

controllers/class_a_controller.py


class ClassAController:
    """
    Functionality of the class A view.
    """

    def __init__(self, view: ctk.CTkFrame) -> None:
        self.view: ctk.CTkFrame = view
        self.initialize_controller()

    def initialize_controller(self) -> None:
        """
        Initialize controller.
        """
        self.custom_frame_controller = CustomFrameController(self.view.custom_frame_view)


class CustomFrameController:
    """
    Functionality of the custom frame view.
    """

    def __init__(self, view: ctk.CTkFrame) -> None:
        self.view: ctk.CTkFrame = view

        self.view.button.configure(command=self.func1)

        self.setup_tracings()

    def setup_tracings(self) -> None:
        """
        Tracing variables from the widgets.
        """
        self.view.var.trace("w", self.func2)

    def func1(self) -> None:
        """
        HERE I DISABLE OTHER WIDGETS I USE IN THE CustomFrameView CLASS.
        """
        ####################################################################
        HERE I DISABLE ALL OTHER WIDGETS IN THE CLASS A VIEW.
        ####################################################################

        self.view.var.set("")

    def func2(self, *_: Any) -> None:
        var= self.view.var.get()

        ####################################################################
        HERE I WANT TO READ A FILE IN A THREAD WHICH TAKES A FEW SECONDS. I 
        WANT TO START A PROGRESSBAR FROM CLASS B AND LET IT RUN UNTIL THE 
        FILE IS COMPLETALLY READ.
        ####################################################################

        ####################################################################
        HERE I WANT TO ENABLE ALL OTHER WIDGETS IN THE CLASS A VIEW AGAIN.
        ####################################################################

controllers/class_b_controller.py


class ClassBController:
    """
    Functionality of the class B view.
    """

    def __init__(self, view: ctk.CTkFrame) -> None:
        self.view: ctk.CTkFrame = view
####################################################################
SOMEWHERE HERE I THINK IS THE BEST PLACE FOR THE FUNCTIONALITY OF
THE PROGRESSBAR.
####################################################################

app.py

class AppView(ctk.CTk):
    """
    Layout of the main application.
    """

    def __init__(self) -> None:
        super().__init__()


class AppController:
    """
    Functionality of the main application.
    """

    def __init__(self, view: ctk.CTk) -> None:
        self.view: ctk.CTk = view


def main() -> None:
    """
    Sets up the main window and event loop.
    """
    app_view = AppView()
    app = AppController(app_view)
    MainController(app.view)
    app.view.mainloop()

if __name__ == "__main__":
    main()

I tried using decoraters or and an observer pattern but failed completally.

PS: This is my first time asking something on StackOverflow. I hope i made it simple and clear and didnt forget anything. Thank you in advance!

0

There are 0 answers