0

MVC with tkinter?

I'm trying to do a simple flashcards game with tkinter with MVC pattern but I do not know how to connect the view with their controller...the problem is that I want to create more screens and controllers but at first is impossible to when you destroy the frame and create the instance of a new frame (View), you have to call the corresponding controller and pass it the instance of the view so that it can interact with it but I'm going crazy...

Is this approach wrong? How can I do MVC pattern in tkinter? How could I activate or deactivate the controller depending on the view I am on? I mean activate controller 1, view 1 is activated, switch to controller 2 and view 2 is activated...

Anybody have some ideas? Any idea is welcome.

I found some examples to switch frame with destroy() method but that is not my approach:

My code

main.py

from manager import Manager
from controller.home_controller import HomeController


if _name_ == '_main_':
    controller = HomeController()

    manager = Manager(controller)
    controller.set_manager(manager)
    controller.switch_to_home()

    manager.mainloop()

home_controller.py

from view.home_view import HomeView


class HomeController:
    def _init_(self):
        self.manager = None

    def user_login(self, user_name, user_email, user_password):
        print('Hola')

    def set_manager(self, manager):
        self.manager = manager

    def switch_to_home(self):
        self.manager.switch_view(HomeView)

    def switch_to_login(self):
        self.manager.switch_view(LoginView)

home_view.py

import tkinter as tk


class HomeView(tk.Frame):
    def _init_(self, manager):
        super()._init_()
        self.manager = manager
        self.config(padx=50, pady=50)
        self.create_widgets()

    def create_widgets(self):
        self.register_button = tk.Button(self, text="Register", width=8, highlightthickness=0, command=...)
        self.register_button.grid(row=0, column=0)

manager.py

import tkinter as tk


class Manager(tk.Tk):
    def _init_(self, controller, *args, **kwargs):
        super()._init_(*args, **kwargs)
        self.title('FlashCards')
        self.config(padx=50, pady=50)
        # self.container = tk.Frame(self)
        self.controller = controller
        self.container = None
        # self.switch_view(HomeView)
        # self.controller.set_manager(self)

    def switch_view(self, frame_class):
        """Destroys current frame and replaces it with a new one."""
        new_frame = frame_class(self)
        if self.container is not None:
            self.container.destroy()
        self.container = new_frame
        self.container.pack(
            side='top',
            fill='both',
            expand=True
        )
        self.container.grid_columnconfigure(1, weight=1)
        self.container.grid_rowconfigure(1, weight=1)
martineau
  • 119,623
  • 25
  • 170
  • 301
  • See [Switch between two frames in tkinter?](https://stackoverflow.com/questions/7546050/switch-between-two-frames-in-tkinter) which can be adapted to handle more pages It does not destroy the pages, just "lifts" them to the top as needed — so you could make each controller a separate class. – martineau Apr 21 '22 at 19:06
  • I see the examples but I can't figure how to implement in my structure. Could you give me an example about it? Only basic structure without buttons or anything... I can't figure out how to put each view with each controller together. One more question, can I create multiple instances of `root`, or am I wrong? I'm so confused right now – amateurpython85 Apr 21 '22 at 20:08
  • Generally one only wants to have one `Tk()` (root) per tkinter application. In the accepted answer to the linked question that would be `SampleApp` subclass of `Tk`. Notice how it passes itself as the `controller` argument to each Page class when it creates instances of them — think of each of them as a View. The controller can also have an attribute that contains the Model — see the answer to [How to get variable data from a class?](https://stackoverflow.com/questions/32212408/how-to-get-variable-data-from-a-class) for how to store data in the controller (and share it among the Pages). – martineau Apr 21 '22 at 20:41
  • Another idea might be to make each a View an independent [`Toplevel`](https://tkdocs.com/shipman/toplevel.html) widget, each of which effectively being the `root` for some View. In this scenario, you could withdraw (hide) the default window and use it as the single Controller all the views sharl. – martineau Apr 21 '22 at 20:53
  • But the way that you say only use one controller, not? or I missing something? I want a different approach, because the application idea is more big and the things get a little messy. I update a class diagram – amateurpython85 Apr 21 '22 at 22:24
  • When I said one controller, I meant only having and using only one instance of the `tkinter.Tk` class. Things are getting a little confused because there's a Controller in the MVC pattern and something called the `controller` in the linked question's answer by @Bryan Oakley. Maybe this other answer of his — [Understanding parent and controller in Tkinter `__init__`](https://stackoverflow.com/a/32865334/355230) — will clarify things to some degree. – martineau Apr 21 '22 at 22:53
  • After looking at your diagram, I suggest that you start out trying to implement something simpler before attempting some that complex — just to get a feel for how `tkinter` works beforehand. – martineau Apr 21 '22 at 23:07
  • I try to do small but the problem is the same. Could you help me with this piece of code? – amateurpython85 May 18 '22 at 16:32

0 Answers0