TKINTER TUTORIAL
Chương trình "Hello, World!" bằng Tkinter trong Python Tìm hiểu cửa sổ Tkinter trong Python Tìm hiểu về các Widget Tkinter trong Python 3 cách đặt Options cho Widget Tkinter trong Python Ràng buộc lệnh trong Tkinter Tìm hiểu Event Binding của Tkinter trong Python Giới thiệu widget Label trong Tkinter Cách sử dụng widget Entry trong Tkinter Trình quản lý hình học Pack trong Tkinter Trình quản lý hình học Grid trong Tkinter Trình quản lý hình học Place trong Tkinter Kích thước Widget trong Tkinter Tìm hiểu về widget Frame trong Tkinter Cách sử dụng widget Text của Tkinter Tìm hiểu về widget Scrollbar của Tkinter Cách sử dụng widget ScrolledText của Tkinter Cách sử dụng widget Separator của Tkinter Cách sử dụng Widget Checkbox Trong Tkinter Cách sử dụng widget radio button của Tkinter Cách tạo widget combobox trong Tkinter Cách sử dụng widget Listbox trong Tkinter Sử dụng widget PanedWindow trong Tkinter Cách tạo widget Spinbox trong Tkinter Hướng dẫn sử dụng Slider trong Tkinter Cách sử dụng widget Sizegrip trong Tkinter Cách sử dụng widget LabelFrame trong Tkinter Cách sử dụng widget Progressbar trong Tkinter Cách sử dụng widget Notebook trong Tkinter Cách sử dụng về widget Treeview trong Tkinter Hướng dẫn về Canvas trong Tkinter Cách thiết lập con trỏ widget trong Tkinter. Window hướng đối tượng trong Tkinter Các Frame hướng đối tượng trong Tkinter Cách sử dụng Object-Oriented Application trong Tkinter Phương thức tkraise() của Frame trong Tkinter Các kiểu (Styles) trong Tkinter Cách thay đổi theme trong Tkinter Các element của ttk trong Tkinter Tìm hiểu về đối tượng Tkinter StringVar trong Tkinter Cách sử dụng phương thức map() của ttk.Style Cách sử dụng phương thức after() của Tkinter Cách sử dụng threads trong Tkinter Cách hiển thị progress bar khi thread đang chạy trong Tkinter. Cách tạo nhiều cửa sổ trong một Tkinter Cách sử dụng widget PhotoImage của Tkinter Cấu trúc MVC trong Tkinter Cách sử dụng Validate trong Tkinter Cách hiển thị đồ thị từ thư viện Matplotlib trong Tkinter Ứng dụng System Tray với Tkinter
CÁC CHỦ ĐỀ
BÀI MỚI NHẤT
MỚI CẬP NHẬT

Thông báo: Download 4 khóa học Python từ cơ bản đến nâng cao tại đây.

Cấu trúc MVC trong Tkinter

Trong bài viết này, mình sẽ tìm hiểu cách áp dụng mẫu thiết kế Model-View-Controller (MVC) để cấu trúc một ứng dụng Tkinter. MVC là một trong những mẫu thiết kế phần mềm phổ biến, giúp tách biệt logic xử lý, giao diện người dùng, và dữ liệu, từ đó làm cho ứng dụng dễ bảo trì và mở rộng hơn. Khi ứng dụng của bạn phát triển, sự phức tạp cũng tăng lên, và việc áp dụng mẫu thiết kế này sẽ giúp bạn quản lý mã nguồn một cách hiệu quả hơn.

test php

banquyen png
Bài viết này được đăng tại freetuts.net, không được copy dưới mọi hình thức.

Giới thiệu về Tkinter MVC

Khi ứng dụng của bạn phát triển, độ phức tạp của nó cũng tăng lên. Để làm cho ứng dụng dễ quản lý hơn, bạn có thể sử dụng mẫu thiết kế Model-View-Controller (MVC).

Mẫu thiết kế MVC cho phép bạn chia ứng dụng thành ba thành phần chính: model (mô hình), view (giao diện hiển thị), và controller (bộ điều khiển). Cấu trúc này giúp bạn tập trung vào logic của từng phần và dễ dàng bảo trì hơn, đặc biệt khi ứng dụng phát triển lớn hơn.

Biểu đồ dưới đây minh họa cách thức hoạt động của mẫu thiết kế MVC:

Bài viết này được đăng tại [free tuts .net]

tkinter mvc design pattern png

Model (Mô hình)

Model trong MVC đại diện cho dữ liệu của ứng dụng. Model chịu trách nhiệm lấy dữ liệu từ hoặc ghi dữ liệu vào các kho lưu trữ như cơ sở dữ liệu hoặc file . Ngoài ra, model cũng có thể chứa logic để kiểm tra tính hợp lệ của dữ liệu, đảm bảo tính toàn vẹn của dữ liệu.

Model không được phụ thuộc vào view và controller. Điều này có nghĩa là bạn có thể tái sử dụng model trong các ứng dụng không phải Tkinter, như ứng dụng web hoặc di động.

View (Giao diện hiển thị)

View là giao diện người dùng, thể hiện dữ liệu trong model. View không giao tiếp trực tiếp với model. Lý tưởng nhất, view nên có rất ít logic chỉ để hiển thị dữ liệu.

View giao tiếp trực tiếp với controller. Trong các ứng dụng Tkinter, view là cửa sổ gốc chứa các widget.

Controller (Bộ điều khiển)

Controller đóng vai trò trung gian giữa view và model. Controller truyền dữ liệu giữa view và model.

Ví dụ, khi người dùng nhấn nút "Save" (Lưu) trên view, controller sẽ chuyển yêu cầu "lưu" đến model để lưu dữ liệu vào cơ sở dữ liệu và thông báo cho view hiển thị một thông báo.

Ví dụ về Tkinter MVC

Chúng ta sẽ lấy một ví dụ đơn giản để minh họa cách áp dụng mẫu thiết kế MVC trong một ứng dụng Tkinter:

python tkinter mvc png

Ứng dụng mà bạn sẽ xây dựng có một ô nhập liệu để nhập email. Khi bạn nhấn nút "Save", controller sẽ gọi model để kiểm tra tính hợp lệ của email.

tkinter mvc png

  • Nếu email hợp lệ, model sẽ lưu email vào một file văn bản và view sẽ hiển thị thông báo thành công.
  • Nếu email không hợp lệ, view sẽ hiển thị thông báo lỗi.

tkinter mvc example png

Mình sẽ ẩn thông báo sau 3 giây.

Lớp Model

Lớp Model dưới đây có thuộc tính email:

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

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

    @email.setter
    def email(self, value):
        """
        Kiểm tra tính hợp lệ của 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'Email không hợp lệ: {value}')

    def save(self):
        """
        Lưu email vào file
        :return:
        """
        with open('emails.txt', 'a') as f:
            f.write(self.email + '\n')

Cách thức hoạt động:

  • Setter email kiểm tra tính hợp lệ của email trước khi gán nó cho thuộc tính __email. Nếu email không hợp lệ, nó sẽ sinh ra một ngoại lệ ValueError.
  • Phương thức save() ghi email vào một file văn bản. Trong các ứng dụng thực tế, bạn có thể muốn lưu nó vào một cơ sở dữ liệu.

View

Dưới đây là lớp View hiển thị một form để nhập email:

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

        # tạo các widget
        # nhãn
        self.label = ttk.Label(self, text='Email:')
        self.label.grid(row=1, column=0)

        # ô nhập email
        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)

        # nút lưu
        self.save_button = ttk.Button(self, text='Save', command=self.save_button_clicked)
        self.save_button.grid(row=1, column=3, padx=10)

        # thông báo
        self.message_label = ttk.Label(self, text='', foreground='red')
        self.message_label.grid(row=2, column=1, sticky=tk.W)

        # thiết lập controller
        self.controller = None

    def set_controller(self, controller):
        """
        Thiết lập controller
        :param controller:
        :return:
        """
        self.controller = controller

    def save_button_clicked(self):
        """
        Xử lý sự kiện nhấn nút
        :return:
        """
        if self.controller:
            self.controller.save(self.email_var.get())

    def show_error(self, message):
        """
        Hiển thị thông báo lỗi
        :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):
        """
        Hiển thị thông báo thành công
        :param message:
        :return:
        """
        self.message_label['text'] = message
        self.message_label['foreground'] = 'green'
        self.message_label.after(3000, self.hide_message)

        # đặt lại form
        self.email_entry['foreground'] = 'black'
        self.email_var.set('')

    def hide_message(self):
        """
        Ẩn thông báo
        :return:
        """
        self.message_label['text'] = ''

Cách thức hoạt động:

  • Tạo các widget trong phương thức __init__().
  • Định nghĩa phương thức set_controller() để thiết lập một controller.
  • Gọi phương thức save() của controller trong sự kiện nhấn nút lưu.
  • Định nghĩa các phương thức show_error(), show_success(), và hide_message() để hiển thị/ẩn thông báo.

Controller

Lớp Controller dưới đây thực hiện các chức năng sau:

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

    def save(self, email):
        """
        Lưu email
        :param email:
        :return:
        """
        try:
            # lưu model
            self.model.email = email
            self.model.save()

            # hiển thị thông báo thành công
            self.view.show_success(f'Email {email} đã được lưu!')

        except ValueError as error:
            # hiển thị thông báo lỗi
            self.view.show_error(error)

Cách thức hoạt động của controller:

  • Gán model và view trong phương thức __init__().
  • Định nghĩa phương thức save() để lưu model vào file văn bản. Nếu model được lưu thành công, hiển thị thông báo thành công. Ngược lại, hiển thị thông báo lỗi.

Application

Lớp App dưới đây sử dụng các lớp Model, View, và Controller:

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

        self.title('Tkinter MVC Demo')

        # tạo model
        model = Model('hello@pythontutorial.net')

        # tạo view và đặt nó lên cửa sổ gốc
        view = View(self)
        view.grid(row=0, column=0, padx=10, pady=10)

        # tạo controller
        controller = Controller(model, view)

        # thiết lập controller cho view
        view.set_controller(controller)

Cách thức hoạt động:

  • Tạo model.
  • Tạo view và đặt nó lên cửa sổ gốc.
  • Tạo controller và thiết lập nó cho 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()            

Kết bài

Sử dụng mẫu thiết kế MVC để cấu trúc ứng dụng Tkinter không chỉ giúp tổ chức mã nguồn rõ ràng và dễ quản lý hơn mà còn tạo điều kiện thuận lợi cho việc bảo trì và mở rộng ứng dụng trong tương lai. Bằng cách tách biệt các thành phần model, view, và controller, bạn có thể phát triển ứng dụng một cách có hệ thống, giảm thiểu sự phụ thuộc lẫn nhau giữa các phần, từ đó nâng cao chất lượng và hiệu suất của ứng dụng.

Cùng chuyên mục:

Hướng dẫn xây dựng Command-Line Interface (CLI) bằng Quo trong Python

Hướng dẫn xây dựng Command-Line Interface (CLI) bằng Quo trong Python

Hướng dẫn toàn diện về module datetime trong Python

Hướng dẫn toàn diện về module datetime trong Python

Cách truy cập và thiết lập biến môi trường trong Python

Cách truy cập và thiết lập biến môi trường trong Python

Lớp dữ liệu (Data Classes) trong Python với decorator @dataclass

Lớp dữ liệu (Data Classes) trong Python với decorator @dataclass

Từ khóa yield trong Python

Từ khóa yield trong Python

Sự khác biệt giữa sort() và sorted() trong Python

Sự khác biệt giữa sort() và sorted() trong Python

Sử dụng Poetry để quản lý dependencies trong Python

Sử dụng Poetry để quản lý dependencies trong Python

Định dạng chuỗi Strings trong Python

Định dạng chuỗi Strings trong Python

Một tác vụ phổ biến khi làm việc với danh sách trong Python

Một tác vụ phổ biến khi làm việc với danh sách trong Python

Làm việc với các biến môi trường trong Python

Làm việc với các biến môi trường trong Python

Sự khác biệt giữa set() và frozenset() trong Python

Sự khác biệt giữa set() và frozenset() trong Python

Sự khác biệt giữa iterator và iterable trong Python

Sự khác biệt giữa iterator và iterable trong Python

Cách làm việc với file tarball/tar trong Python

Cách làm việc với file tarball/tar trong Python

Chuyển đổi kiểu dữ liệu trong Python

Chuyển đổi kiểu dữ liệu trong Python

Sự khác biệt giữa toán tử == và is trong Python

Sự khác biệt giữa toán tử == và is trong Python

Làm việc với file ZIP trong Python

Làm việc với file ZIP trong Python

Cách sử dụng ThreadPoolExecutor trong Python

Cách sử dụng ThreadPoolExecutor trong Python

Sự khác biệt giữa byte objects và string trong Python

Sự khác biệt giữa byte objects và string trong Python

Xử lý độ chính xác các hàm floor, ceil, round, trunc, format  trong Python

Xử lý độ chính xác các hàm floor, ceil, round, trunc, format trong Python

Cách lặp qua nhiều list với hàm zip() trong Python

Cách lặp qua nhiều list với hàm zip() trong Python

Top