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.

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]

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:

Ứ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.

- 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.

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
emailkiể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('[email protected]')
# 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('[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() 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ác kiểu dữ liệu trong C ( int - float - double - char ...)
Thuật toán tìm ước chung lớn nhất trong C/C++
Cấu trúc lệnh switch case trong C++ (có bài tập thực hành)
ComboBox - ListBox trong lập trình C# winforms
Random trong Python: Tạo số random ngẫu nhiên
Lệnh cin và cout trong C++
Cách khai báo biến trong PHP, các loại biến thường gặp
Download và cài đặt Vertrigo Server
Thẻ li trong HTML
Thẻ article trong HTML5
Cấu trúc HTML5: Cách tạo template HTML5 đầu tiên
Cách dùng thẻ img trong HTML và các thuộc tính của img
Thẻ a trong HTML và các thuộc tính của thẻ a thường dùng