Ở các bài trước chúng ta đã ứng dụng thành thạo kiến trúc Model View để tương tác dữ liệu Employee trên QListview. Trong bài này Tui tiếp tục hướng dẫn các bạn cách dùng Model View cho QTableView để hiển thị dữ liệu dạng bảng. Bạn có thể nạp Ma trận dữ liệu vào QTableView hoặc nạp Danh sách dữ liệu đối tượng dạng List vào QTableView. Sự khác biệt giữa QListView và QTableView là QListView không cần dùng các cột dữ liệu, còn QTableView cần dùng các cột dữ liệu để chi tiết hóa các thuộc tính của đối tượng.
Trong bài này Tui minh họa ví dụ đơn giản về nạp Ma trận Product lên QTableView dùng kiến trúc Model View. Giả sử ta có Ma trận dữ liệu như dưới đây:
data = [["p1", "Coca", 100],
["p2", "Pepsi", 50],
["p3", "Sting", 300],
["p4", "Aqua", 70],
["p5", "Redbull", 200],
["p6", "", 120]]
columns = ["ID", "Name", "Price"]
Làm thế nào để nạp ma trận “data” lên QTableView cùng với các cột được lưu trong mảng “columns“? Đồng thời nếu giá <100 thì tô chữ đỏ, nếu tên rỗng thì tô nền vàng:
Nếu hiểu và triển khai được bài này thì bạn có thể dễ dàng nạp ma trận dữ liệu cần phân tích lên giao diện, ví dụ ma trận về lịch sử kinh doanh.
Các bạn tiến hành làm từng bước như Tui hướng dẫn dưới đây.
Bước 1: Tạo dự án “LearnModelViewPart3” trong Pycharm có cấu trúc như dưới đây:
- Trung tâm của dự án chính là lớp “TableModel.py“. Lớp này kế thừa từ “QAbstractTableModel” để thực hiện kiến trúc Model View. Tui sẽ trình bày chi tiết ở bước sau
- “MainWindow.ui” là giao diện thiết kế của phần mềm bằng Qt Designer
- “MainWindow.py” là generate python code của giao diện “MainWindow.ui”
- “MainWindowEx.py” là lớp kế thừa từ generate python code “MainWindow.py” nó được dùng để nạp giao diện, xử lý sự kiện người dùng, gán model view mà không bị ảnh hưởng khi ta tiếp tục generate code từ giao diện khi thay đổi.
- “MyApp.py” là file mã lệnh thực thi chương trình
Bước 2: Tạo lớp mã lệnh “TableModel.py” như dưới đây:
from PyQt6 import QtGui
from PyQt6.QtCore import Qt, QAbstractTableModel
class TableModel(QAbstractTableModel):
def __init__(self, data,columns):
super().__init__()
self.data = data
self.columns=columns
def data(self, index, role):
value=self.data[index.row()][index.column()]
if role == Qt.ItemDataRole.DisplayRole:
return value
if role==Qt.ItemDataRole.BackgroundRole:
if index.column()==1 and value=="":
return QtGui.QColor(Qt.GlobalColor.yellow)
if role==Qt.ItemDataRole.ForegroundRole:
if index.column() == 2 and value<100:
return QtGui.QColor(Qt.GlobalColor.red)
def rowCount(self, index):
return len(self.data)
def columnCount(self, index):
return len(self.columns)
def headerData(self, section, orientation, role):
if role == Qt.ItemDataRole.DisplayRole:
if orientation == Qt.Orientation.Horizontal:
return str(self.columns[section])
if orientation==Qt.Orientation.Vertical:
return str(section+1)
Tui giải thích chi tiết các hàm của TableModel.py như sau:
- Constructor __init__(self, data,columns) nhận vào 2 đối số. Đối số data là ma trận dữ liệu Product đã mô tả ở trên, đối số columns là mảng các cột của Product.
- Hàm data(self, index, role) được override để hiển thị dữ liệu cũng như các định dạng dữ liệu (màu nền, màu chữ). Trong hàm này Tui có sử dụng 3 Role:
- Qt.ItemDataRole.DisplayRole (dùng để hiển thị dữ liệu thông thường)
- Qt.ItemDataRole.BackgroundRole (để hiển thị màu nền, trong trường hợp này là nếu dữ liệu của cột tên Product là rỗng thì ta tô nền vàng)
- Qt.ItemDataRole.ForegroundRole (để hiển thị màu chữ, trong trường hợp này là nếu dữ liệu của cột giá Product nhỏ hơn 100 thì tô chữ đỏ)
- Hàm rowCount(self, index) được override để trả về số dòng dữ liệu trong model
- Hàm columnCount(self, index) được override để trả về số cột trong model
- Hàm headerData(self, section, orientation, role) được override để vẽ tiêu đề của Cột nằm đứng và header của dòng nằm ngang.
5 Hàm bắt buộc cần được định nghĩa đối với TableModel ở trên. Tương tự cho các loại dữ liệu khác thì bạn cũng tạo 5 hàm này, và điều chỉnh mã lệnh theo nhu cầu sử dụng khác nhau. Còn những hàm nâng cao khác để hỗ trợ thêm mới, chỉnh sửa, cũng như xóa dữ liệu Tui sẽ trình bày chi tiết ở bài học sau.
Bước 3: Thiết kế giao diện “MainWindow.ui” và đặt tên cho widget như hình dưới đây:
Bài này thì ta chỉ cần kéo 1 QTableView ra mà thôi.
Bước 4: Generate python code “MainWindow.py” cho giao diện “MainWindow.ui”
# Form implementation generated from reading ui file 'MainWindow.ui'
#
# Created by: PyQt6 UI code generator 6.4.2
#
# WARNING: Any manual changes made to this file will be lost when pyuic6 is
# run again. Do not edit this file unless you know what you are doing.
from PyQt6 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(335, 297)
self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.tableViewProduct = QtWidgets.QTableView(parent=self.centralwidget)
self.tableViewProduct.setGeometry(QtCore.QRect(10, 10, 301, 231))
self.tableViewProduct.setObjectName("tableViewProduct")
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
self.menubar.setGeometry(QtCore.QRect(0, 0, 335, 26))
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(parent=MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
_translate = QtCore.QCoreApplication.translate
MainWindow.setWindowTitle(_translate("MainWindow", "Trần Duy Thanh - QTableView-ModelView"))
Bước 5: Tạo “MainWindowEx.py” kế thừa từ Generate Python code ở trên để nạp giao diện và gán Model View
from MainWindow import Ui_MainWindow
from TableModel import TableModel
class MainWindowEx(Ui_MainWindow):
def __init__(self):
super().__init__()
def setupUi(self, MainWindow):
super().setupUi(MainWindow)
self.MainWindow=MainWindow
data = [["p1", "Coca", 100],
["p2", "Pepsi", 50],
["p3", "Sting", 300],
["p4", "Aqua", 70],
["p5", "Redbull", 200],
["p6", "", 120]]
columns = ["ID", "Name", "Price"]
self.model = TableModel(data, columns)
self.tableViewProduct.setModel(self.model)
def show(self):
self.MainWindow.show()
Dòng lệnh 18:
self.model = TableModel(data, columns)
Dòng lệnh 18 này dùng để khởi tạo đối tượng tableModel, nó nhận vào Ma trận Product và mảng columns
Dòng lệnh 19:
self.tableViewProduct.setModel(self.model)
Dòng lệnh 19 gán model cho QTableView.
Lúc này các hàm trong TableModel sẽ tự động được thực hiện, rất chuyên nghiệp và ảo ma canada.
Bước 6: Tạo file lệnh “MyApp.py” để thực thi chương trình
from PyQt6.QtWidgets import QApplication, QMainWindow
from MainWindowEx import MainWindowEx
app=QApplication([])
myWindow=MainWindowEx()
myWindow.setupUi(QMainWindow())
myWindow.show()
app.exec()
Chạy MyApp.py ta có kết quả như mong muốn:
Bạn quan sát thấy cột Price sẽ tô màu đỏ những Price nào <100, như vậy giá 50 và 70 tự động được tô màu đỏ. Và cột Name có dữ liệu nào rỗng sẽ tô nền vàng.
Như vậy Tui đã trình bày xong cách ứng dụng kiến trúc Model View vào quản lý và hiển thị dữ liệu dạng Ma trận, đã trình bày kỹ cách tạo TableModel kế thừa từ QAbstractTableModel. Cũng như đã trình bày chi tiết ý nghĩa và cách lập trình các hàm quan trọng trong lớp này để ta có thể dễ dàng tùy chỉnh cách thức mà dữ liệu hiển thị trên giao diện như mong muốn.
Source code đây đủ của bài này các bạn tải ở đây:
https://www.mediafire.com/file/o3s1ytr0fi1hn0w/LearnModelViewPart3.rar/file
Bài học sau Tui sẽ mở rộng bài này bằng cách bổ sung các chức năng: Thêm, Sửa, Xóa dữ liệu ngay trên giao diện QTableView để các bạn củng cố hơn nữa cách ứng dụng kiến trúc Model View cũng như nhìn thấy được lợi ích của mô hình này. Các bạn chú ý theo dõi
Chúc các bạn thành công.
And variants are possible still?