Концепция model-view-controller (MVC) — как работает

По мере роста вашего приложения его сложность также увеличивается. Чтобы сделать приложение Tkinter в Python более управляемым, вы можете использовать концепцию проектирования model-view-controller (MVC).

Содержание

Что такое Tkinter MVC

Шаблон проектирования MVC позволяет разделить приложение на три основных компонента: модель, вид и контроллер. Эта структура помогает сосредоточиться на логике каждой части и сделать ее более поддерживаемой, особенно когда приложение растет.

Следующая диаграмма иллюстрирует шаблон проектирования MVC:

Шаблон проектирования MVC

Модель

Модель в MVC представляет данные. Модель занимается получением данных из хранилища или записью данных в него, например, в базу данных или файл. Модель также может содержать логику для проверки данных, чтобы гарантировать целостность данных.

Модель не должна зависеть от представления и контроллера. Другими словами, вы можете повторно использовать модель в других не-Tkinter приложениях, таких как веб- и мобильные приложения.

Вид

Вид (представление) — это пользовательский интерфейс, представляющий данные в модели. Он не взаимодействует с моделью напрямую. В идеале вид должно иметь очень мало логики для отображения данных.

Вид напрямую взаимодействует с контроллером. В приложениях Tinker вид — это корневое окно, состоящее из виджетов.

Контроллер

Контроллер выступает в качестве посредника между видами и моделями. Контроллер маршрутизирует данные между ними.

Например, если пользователь нажимает кнопку сохранения в представлении, контроллер направляет действие «сохранить» в модель, чтобы сохранить данные в базе данных и уведомить вид о необходимости отображения сообщения.

Пример Tkinter MVC

Рассмотрим простой пример, иллюстрирующий применение шаблона проектирования MVC в приложении Tkinter:

Пример tkinter mvc

Приложение, которое вы создадите, содержит запись для ввода адреса электронной почты. Когда вы нажимаете кнопку сохранения, контроллер вызывает модель для проверки адреса электронной почты.

Если адрес электронной почты действителен, модель сохраняет его в текстовом файле, а в представлении отображается сообщение об успешном завершении:

Приложение для ввода адреса электронной почты

Если адрес электронной почты недействителен, в представлении отображается сообщение об ошибке:

Если адрес электронной почты недействителен

Мы скроем сообщение через 3 секунды.

Класс Model

Ниже определяется класс Model, имеющий свойство электронной почты:

class Model:
    def __init__(self, email):
        self.email = email

    @property
    def email(self):
        return self.__email

    @email.setter
    def email(self, value):
        """
        Validate the email
        :param value:
        :return:
        """
        pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
        if re.fullmatch(pattern, value):
            self.__email = value
        else:
            raise ValueError(f'Invalid email address: {value}')

    def save(self):
        """
        Save the email into a file
        :return:
        """
        with open('emails.txt', 'a') as f:
            f.write(self.email + '\n')

Как это работает:

  • Сеттер email проверяет email перед назначением его атрибуту __email. Если email недействителен, он выдаст ValueError.
  • Метод save() записывает email в простой текстовый файл. В реальных приложениях вы можете захотеть сохранить его в базе данных.

View

Ниже определено представление, отображающее форму для ввода адреса электронной почты:

class View(ttk.Frame):
    def __init__(self, parent):
        super().__init__(parent)

        # create widgets
        # label
        self.label = ttk.Label(self, text='Email:')
        self.label.grid(row=1, column=0)

        # email entry
        self.email_var = tk.StringVar()
        self.email_entry = ttk.Entry(self, textvariable=self.email_var, width=30)
        self.email_entry.grid(row=1, column=1, sticky=tk.NSEW)

        # save button
        self.save_button = ttk.Button(self, text='Save', command=self.save_button_clicked)
        self.save_button.grid(row=1, column=3, padx=10)

        # message
        self.message_label = ttk.Label(self, text='', foreground='red')
        self.message_label.grid(row=2, column=1, sticky=tk.W)

        # set the controller
        self.controller = None

    def set_controller(self, controller):
        """
        Set the controller
        :param controller:
        :return:
        """
        self.controller = controller

    def save_button_clicked(self):
        """
        Handle button click event
        :return:
        """
        if self.controller:
            self.controller.save(self.email_var.get())

    def show_error(self, message):
        """
        Show an error message
        :param message:
        :return:
        """
        self.message_label['text'] = message
        self.message_label['foreground'] = 'red'
        self.message_label.after(3000, self.hide_message)
        self.email_entry['foreground'] = 'red'

    def show_success(self, message):
        """
        Show a success message
        :param message:
        :return:
        """
        self.message_label['text'] = message
        self.message_label['foreground'] = 'green'
        self.message_label.after(3000, self.hide_message)

        # reset the form
        self.email_entry['foreground'] = 'black'
        self.email_var.set('')

    def hide_message(self):
        """
        Hide the message
        :return:
        """
        self.message_label['text'] = ''

Как это работает.

  • Сначала создайте виджеты в методе __init__().
  • Во-вторых, определите метод set_controller() для установки контроллера.
  • В-третьих, вызовите метод save() контроллера в обработчике событий нажатия кнопки сохранения.
  • Наконец, определите методы show_error(), show_success() и hide_message() для отображения/скрытия сообщения.

Controller

Ниже дано определение контроллера:

class Controller:
    def __init__(self, model, view):
        self.model = model
        self.view = view

    def save(self, email):
        """
        Save the email
        :param email:
        :return:
        """
        try:

            # save the model
            self.model.email = email
            self.model.save()

            # show a success message
            self.view.show_success(f'The email {email} saved!')

        except ValueError as error:
            # show an error message
            self.view.show_error(error)

Как работает контроллер.

  • Сначала назначьте модель и представление в методе __init__()
  • Во-вторых, определите метод save(), который сохраняет модель в текстовый файл. Если модель успешно сохранена, покажите сообщение об успехе. В противном случае покажите сообщение об ошибке.

Приложение

Ниже определяется класс приложения, который использует классы модели, представления и контроллера:

class App(tk.Tk):
    def __init__(self):
        super().__init__()

        self.title('Tkinter MVC Demo')

        # create a model
        model = Model('[email protected]')

        # create a view and place it on the root window
        view = View(self)
        view.grid(row=0, column=0, padx=10, pady=10)

        # create a controller
        controller = Controller(model, view)

        # set the controller to view
        view.set_controller(controller)


if __name__ == '__main__':
    app = App()
    app.mainloop()

Как это работает.

  • Сначала создайте модель.
  • Во-вторых, создайте представление и поместите его в корневое окно.
  • В-третьих, создайте контроллер и настройте его на представление.

Соберите все вместе.

import re
import tkinter as tk
from tkinter import ttk


class Model:
    def __init__(self, email):
        self.email = email

    @property
    def email(self):
        return self.__email

    @email.setter
    def email(self, value):
        """
        Validate the email
        :param value:
        :return:
        """
        pattern = r'\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b'
        if re.fullmatch(pattern, value):
            self.__email = value
        else:
            raise ValueError(f'Invalid email address: {value}')

    def save(self):
        """
        Save the email into a file
        :return:
        """
        with open('emails.txt', 'a') as f:
            f.write(self.email + '\n')

class View(ttk.Frame):
    def __init__(self, parent):
        super().__init__(parent)

        # create widgets
        # label
        self.label = ttk.Label(self, text='Email:')
        self.label.grid(row=1, column=0)

        # email entry
        self.email_var = tk.StringVar()
        self.email_entry = ttk.Entry(self, textvariable=self.email_var, width=30)
        self.email_entry.grid(row=1, column=1, sticky=tk.NSEW)

        # save button
        self.save_button = ttk.Button(self, text='Save', command=self.save_button_clicked)
        self.save_button.grid(row=1, column=3, padx=10)

        # message
        self.message_label = ttk.Label(self, text='', foreground='red')
        self.message_label.grid(row=2, column=1, sticky=tk.W)

        # set the controller
        self.controller = None

    def set_controller(self, controller):
        """
        Set the controller
        :param controller:
        :return:
        """
        self.controller = controller

    def save_button_clicked(self):
        """
        Handle button click event
        :return:
        """
        if self.controller:
            self.controller.save(self.email_var.get())

    def show_error(self, message):
        """
        Show an error message
        :param message:
        :return:
        """
        self.message_label['text'] = message
        self.message_label['foreground'] = 'red'
        self.message_label.after(3000, self.hide_message)
        self.email_entry['foreground'] = 'red'

    def show_success(self, message):
        """
        Show a success message
        :param message:
        :return:
        """
        self.message_label['text'] = message
        self.message_label['foreground'] = 'green'
        self.message_label.after(3000, self.hide_message)

        # reset the form
        self.email_entry['foreground'] = 'black'
        self.email_var.set('')

    def hide_message(self):
        """
        Hide the message
        :return:
        """
        self.message_label['text'] = ''            


class Controller:
    def __init__(self, model, view):
        self.model = model
        self.view = view

    def save(self, email):
        """
        Save the email
        :param email:
        :return:
        """
        try:

            # save the model
            self.model.email = email
            self.model.save()

            # show a success message
            self.view.show_success(f'The email {email} saved!')

        except ValueError as error:
            # show an error message
            self.view.show_error(error)        

class App(tk.Tk):
    def __init__(self):
        super().__init__()

        self.title('Tkinter MVC Demo')

        # create a model
        model = Model('[email protected]')

        # create a view and place it on the root window
        view = View(self)
        view.grid(row=0, column=0, padx=10, pady=10)

        # create a controller
        controller = Controller(model, view)

        # set the controller to view
        view.set_controller(controller)


if __name__ == '__main__':
    app = App()
    app.mainloop()
Похожие посты
Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *