Tkinter MVC

2024. 4. 3. 19:10GUI/tkinter

요약 : 이 튜토리얼에서는 모델-뷰-컨트롤러(MVC) 패턴을 사용하여 Tkinter 애플리케이션을 구성하는 방법을 배웁니다.

 

Tkinter MVC 소개

 

애플리케이션이 커짐에 따라 복잡성도 증가합니다. 애플리케이션을 보다 쉽게 ​​관리할 수 있도록 모델-뷰-컨트롤러 디자인 패턴을 사용할 수 있습니다.

MVC 디자인 패턴을 사용하면 애플리케이션을 모델, 뷰 및 컨트롤러의 세 가지 주요 구성 요소로 나눌 수 있습니다. 이 구조는 각 부분의 논리에 집중하고 특히 애플리케이션이 커질 때 유지 관리를 더욱 용이하게 만드는 데 도움이 됩니다.

다음 다이어그램은 MVC 디자인 패턴을 보여줍니다.

 

 

모델

 

MVC의 모델은 데이터를 나타냅니다. 모델은 데이터베이스나 파일과 같은 저장소에서 데이터를 가져오거나 저장소에 데이터를 쓰는 작업을 처리합니다. 모델에는 데이터 무결성을 보장하기 위해 데이터를 검증하는 논리도 포함될 수 있습니다.

모델은 뷰와 컨트롤러에 의존해서는 안 됩니다. 즉, 웹 및 모바일 앱과 같은 Tkinter가 아닌 다른 응용 프로그램에서 모델을 재사용할 수 있습니다.

 

view

 

뷰는 모델의 데이터를 나타내는 사용자 인터페이스입니다. 뷰는 모델과 직접 통신하지 않습니다. 이상적으로는 뷰에 데이터를 표시하는 논리가 거의 없어야 합니다.

뷰는 컨트롤러와 직접 통신합니다. Tinker 애플리케이션에서 view는 위젯으로 구성된 루트 창 입니다.

 

제어 장치

 

컨트롤러는 view와 모델 사이의 중개자 역할을 합니다. 컨트롤러는 view와 모델 간에 데이터를 라우팅합니다.

예를 들어, 사용자가 view에서 저장 버튼을 클릭하면 컨트롤러는 "저장" 작업을 모델로 라우팅하여 데이터를 데이터베이스에 저장하고 view에 메시지를 표시하도록 알립니다.

 

Tkinter MVC 예

 

Tkinter 애플리케이션에 MVC 디자인 패턴을 적용하는 방법을 설명하기 위해 간단한 예를 들어 보겠습니다.

 

 

빌드할 애플리케이션에는 이메일을 입력하기 위한 항목이 포함되어 있습니다. 저장 버튼을 클릭하면 컨트롤러가 모델을 호출하여 이메일을 검증합니다.

이메일이 유효하면 모델은 이메일을 텍스트 파일에 저장하고 view에 성공 메시지가 표시됩니다.

 

 

이메일이 유효하지 않으면 보기에 오류 메시지가 표시됩니다.

 

 

3초 후에 메시지를 숨깁니다.

 

모델 클래스

 

다음은 email 속성이 있는 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 속성에 할당하기 전에 이메일의 유효성을 검사합니다. 이메일이 유효하지 않으면 ValueError가 나타납니다.
  • save() 메서드는 이메일을 간단한 텍스트 파일에 기록합니다. 실제 응용 프로그램에서는 이를 데이터베이스에 저장할 수 있습니다.
 

view

 

다음은 이메일을 입력하는 양식을 보여주는 view를 정의합니다.

 
import re
import tkinter as tk
from tkinter import ttk

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() 메서드를 정의합니다.
 

제어 장치

 

다음은 컨트롤러를 정의합니다.

 
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__() 메소드에 모델과 view를 할당합니다.
  • 둘째, 모델을 텍스트 파일로 저장하는 save() 메서드를 정의합니다. 모델이 성공적으로 저장되면 성공 메시지를 표시합니다. 그렇지 않으면 오류 메시지를 표시합니다.
 

애플리케이션

 

다음은 Model, View 및 Controller 클래스를 사용하는 App 클래스를 정의합니다.

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

        self.title('Tkinter MVC Demo')

        # create a model
        model = Model('hello@pythontutorial.net')

        # 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()
 
동작 방식.

- 먼저 모델을 생성합니다.
- 둘째, view를 생성하여 루트 창에 배치합니다.
- 셋째, 컨트롤러를 생성하고 view에 설정합니다.

전체 코드를 넣어보세요.
 
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('hello@pythontutorial.net')

        # 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()            
 

요약

 
  • MVC를 사용하여 Tkinter 애플리케이션을 구조화하여 더 체계적으로 만듭니다.
 

 

'GUI > tkinter' 카테고리의 다른 글

Tkinter Matplotlib  (0) 2024.04.05
Tkinter Validation  (0) 2024.04.04
How to Display a Progress Bar while a Thread is Running in Tkinter  (0) 2024.04.02
How to Schedule an Action with Tkinter after() method  (0) 2024.04.02
Tkinter Thread  (0) 2024.03.31