Bài 23: Xử lý dữ liệu dạng bảng- QTableWidget–Part 2

Trong bài 22 các bạn đã biết cách thiết kế, lưu trữ và hiển thị dữ liệu trên QTableWidget, cũng như biết cách xử lý sự kiện người dùng chọn từng item trong Widget này. Tuy nhiên các dữ liệu đang được hardcode, còn trong bài học này Tui sẽ hướng dẫn các bạn cách thức tạo dữ liệu động lúc runtime cho QTableWidget, đây là trường hợp mà chúng ta thường xuyên sử dụng trong quá trình triển khai dự án trong thực tế.

Lý thuyết về QTableWidget đã được trình bày chi tiết ở bài học trước, nên bài học này Tui đi thẳng vào việc ứng dụng chúng để xây dựng một phần mềm hoàn chỉnh, các chức năng bao gồm:

  • Mô hình hóa hướng đối tượng trong Python
  • Chức năng thêm, sửa, xóa dữ liệu
  • Chức năng hiển thị dữ liệu, những Sản phẩm nào có giá <10 triệu thì tô nền vàng chữ đỏ.
  • Chức năng lưu dữ liệu từ QTableWidget xuống JSON
  • Chức năng đọc dữ liệu từ JSON lên QTableWidget
  • Chức năng xử lý sự kiện người dùng chọn các Item trên QTableWidget
  • Cũng như cách dùng QMessageBox để điều hướng các lựa chọn của người dùng.

Bước 1: Chúng ta tạo một dự án tên “LearnQTableWidgetPart2” có cấu trúc như dưới đây:

  • “images” thư mục chứa các hình ảnh của ứng dụng như: Icon của cửa sổ, icon thêm mới, icon lưu, icon xóa
  • “database.json” là file dữ liệu khi người dùng thêm mới, thay đổi Product thì danh sách dữ liệu sẽ được cập nhật trong file này
  • “Product.py” là lớp để mô hình hóa hướng đối tượng cho dữ liệu liên quan tới Sản phẩm, các thuộc tính báo gồm: mã, tên giá
  • “FileFactory.py” File dùng để Serilize danh sách Product xuống ổ cứng và Deserialize phục hồi lại danh sách đối tượng Product lên bộ nhớ để xử lý
  • “MainWindow.ui” là file thiết kế giao diện của ứng dụng, thiết kế bằng Qt Designer
  • “MainWindow.py” là file generate python code của MainWindow.ui
  • “MainWindowEx.py” là file mã lệnh thiết kế class kế thừa từ lớp generate python để dễ dàng xử lý các sự kiện, bổ sung các mã lệnh mà không bị lệ thuộc vào giao diện khi nó bị thay đổi
  • “MyApp.py” là file mã lệnh để thực thi chương trình

Bây giờ chúng ta đi vào chi tiết từng bước thiết kế và lập trình phần mềm Quản lý sản phẩm này.

Bước 2: Tạo lớp đối tượng Product trong file mã lệnh “Product.py”

class Product:
    def __init__(self,ProductId,ProductName,Price):
        self.ProductId=ProductId
        self.ProductName=ProductName
        self.Price=Price
    def __str__(self):
        return str(self.ProductId) +" - "+self.ProductName +"-"+str(self.Price)

Lớp Product ở trên được định nghĩa constructor mặc định có 3 đối số: ProductId, ProductName, Price

Các bạn cần thiết kế đúng như trên để sau này ta Serialize và Deserialize được nhanh chóng và chính xác.

Bước 3: Tạo lớp đối tượng FileFactory trong file mã lệnh “FileFactory.py”

import json
import os

class FileFactory:
    #path: path to serialize array of product
    #arrData: array of Product
    def writeData(self,path,arrData):
        jsonString = json.dumps([item.__dict__ for item in arrData],default=str)
        jsonFile = open(path, "w")
        jsonFile.write(jsonString)
        jsonFile.close()
    #path: path to deserialize array of Product
    #ClassName: Product
    def readData(self,path,ClassName):
        if os.path.isfile(path) == False:
            return []
        file = open(path, "r")
        # Reading from file
        self.arrData = json.loads(file.read(), object_hook=lambda d: ClassName(**d))
        file.close()
        return self.arrData

Lớp FileFactory này các bạn đã làm quen nhiều lần ở các bài học trước, nhiệm vụ của writeData là Serialize danh sách Product xuống ổ cứng với định dạng JSON Array. Hàm readData để Deserialize chuỗi JSON Array dưới ổ cứng lên bộ nhớ bằng mô hình hóa hướng đối tượng các Products.

Bước 4: Thiết kế giao diện cho MainWindow.ui và đặt tên cho các Widget như hình dưới đây (xem lại các bài học trước để kéo thả các widget cho phù hợp):

Bước 5: Tiến hành Generate Python code cho MainWindow.ui, lúc này MainWindow.py sẽ được tự động tạo ra như dưới đây:

# 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(456, 485)
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap("images/ic_logo.jpg"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        MainWindow.setWindowIcon(icon)
        self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.label = QtWidgets.QLabel(parent=self.centralwidget)
        self.label.setGeometry(QtCore.QRect(20, 0, 371, 41))
        font = QtGui.QFont()
        font.setPointSize(12)
        font.setBold(True)
        font.setWeight(75)
        self.label.setFont(font)
        self.label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
        self.label.setObjectName("label")
        self.groupBox = QtWidgets.QGroupBox(parent=self.centralwidget)
        self.groupBox.setGeometry(QtCore.QRect(20, 370, 411, 61))
        self.groupBox.setStyleSheet("background-color: rgb(236, 255, 199);")
        self.groupBox.setObjectName("groupBox")
        self.pushButtonRemove = QtWidgets.QPushButton(parent=self.groupBox)
        self.pushButtonRemove.setGeometry(QtCore.QRect(290, 20, 91, 28))
        self.pushButtonRemove.setStyleSheet("background-color: rgb(255, 170, 255);")
        icon1 = QtGui.QIcon()
        icon1.addPixmap(QtGui.QPixmap("images/ic_delete.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonRemove.setIcon(icon1)
        self.pushButtonRemove.setIconSize(QtCore.QSize(24, 24))
        self.pushButtonRemove.setObjectName("pushButtonRemove")
        self.pushButtonSave = QtWidgets.QPushButton(parent=self.groupBox)
        self.pushButtonSave.setGeometry(QtCore.QRect(160, 20, 81, 28))
        self.pushButtonSave.setStyleSheet("background-color: rgb(255, 170, 255);")
        icon2 = QtGui.QIcon()
        icon2.addPixmap(QtGui.QPixmap("images/ic_save.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonSave.setIcon(icon2)
        self.pushButtonSave.setIconSize(QtCore.QSize(24, 24))
        self.pushButtonSave.setObjectName("pushButtonSave")
        self.pushButtonNew = QtWidgets.QPushButton(parent=self.groupBox)
        self.pushButtonNew.setGeometry(QtCore.QRect(40, 20, 71, 28))
        self.pushButtonNew.setStyleSheet("background-color: rgb(255, 170, 255);")
        icon3 = QtGui.QIcon()
        icon3.addPixmap(QtGui.QPixmap("images/ic_new.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonNew.setIcon(icon3)
        self.pushButtonNew.setIconSize(QtCore.QSize(24, 24))
        self.pushButtonNew.setObjectName("pushButtonNew")
        self.groupBox_2 = QtWidgets.QGroupBox(parent=self.centralwidget)
        self.groupBox_2.setGeometry(QtCore.QRect(20, 250, 411, 111))
        self.groupBox_2.setStyleSheet("background-color: rgb(255, 201, 243);")
        self.groupBox_2.setObjectName("groupBox_2")
        self.label_4 = QtWidgets.QLabel(parent=self.groupBox_2)
        self.label_4.setGeometry(QtCore.QRect(20, 80, 101, 16))
        self.label_4.setObjectName("label_4")
        self.label_3 = QtWidgets.QLabel(parent=self.groupBox_2)
        self.label_3.setGeometry(QtCore.QRect(20, 50, 101, 16))
        self.label_3.setObjectName("label_3")
        self.lineEditProductName = QtWidgets.QLineEdit(parent=self.groupBox_2)
        self.lineEditProductName.setGeometry(QtCore.QRect(130, 50, 261, 22))
        self.lineEditProductName.setStyleSheet("background-color: rgb(251, 255, 206);")
        self.lineEditProductName.setObjectName("lineEditProductName")
        self.lineEditUnitPrice = QtWidgets.QLineEdit(parent=self.groupBox_2)
        self.lineEditUnitPrice.setGeometry(QtCore.QRect(130, 80, 261, 22))
        self.lineEditUnitPrice.setStyleSheet("background-color: rgb(251, 255, 206);")
        self.lineEditUnitPrice.setObjectName("lineEditUnitPrice")
        self.label_2 = QtWidgets.QLabel(parent=self.groupBox_2)
        self.label_2.setGeometry(QtCore.QRect(20, 20, 101, 16))
        self.label_2.setObjectName("label_2")
        self.lineEditProductId = QtWidgets.QLineEdit(parent=self.groupBox_2)
        self.lineEditProductId.setGeometry(QtCore.QRect(130, 20, 261, 22))
        self.lineEditProductId.setStyleSheet("background-color: rgb(251, 255, 206);")
        self.lineEditProductId.setObjectName("lineEditProductId")
        self.groupBox_3 = QtWidgets.QGroupBox(parent=self.centralwidget)
        self.groupBox_3.setGeometry(QtCore.QRect(20, 40, 411, 201))
        self.groupBox_3.setStyleSheet("background-color: rgb(251, 255, 206);")
        self.groupBox_3.setObjectName("groupBox_3")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.groupBox_3)
        self.verticalLayout.setObjectName("verticalLayout")
        self.tableWidgetProduct = QtWidgets.QTableWidget(parent=self.groupBox_3)
        self.tableWidgetProduct.setStyleSheet("background-color: rgb(207, 255, 235);")
        self.tableWidgetProduct.setObjectName("tableWidgetProduct")
        self.tableWidgetProduct.setColumnCount(3)
        self.tableWidgetProduct.setRowCount(0)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidgetProduct.setHorizontalHeaderItem(0, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidgetProduct.setHorizontalHeaderItem(1, item)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidgetProduct.setHorizontalHeaderItem(2, item)
        self.verticalLayout.addWidget(self.tableWidgetProduct)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 456, 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)
        MainWindow.setTabOrder(self.tableWidgetProduct, self.lineEditProductId)
        MainWindow.setTabOrder(self.lineEditProductId, self.lineEditProductName)
        MainWindow.setTabOrder(self.lineEditProductName, self.lineEditUnitPrice)
        MainWindow.setTabOrder(self.lineEditUnitPrice, self.pushButtonNew)
        MainWindow.setTabOrder(self.pushButtonNew, self.pushButtonSave)
        MainWindow.setTabOrder(self.pushButtonSave, self.pushButtonRemove)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Trần Duy Thanh - QTableWidget"))
        self.label.setText(_translate("MainWindow", "Product Management"))
        self.groupBox.setTitle(_translate("MainWindow", "Actions:"))
        self.pushButtonRemove.setText(_translate("MainWindow", "Remove"))
        self.pushButtonSave.setText(_translate("MainWindow", "Save"))
        self.pushButtonNew.setText(_translate("MainWindow", "New"))
        self.groupBox_2.setTitle(_translate("MainWindow", "Product Details:"))
        self.label_4.setText(_translate("MainWindow", "Unit Price:"))
        self.label_3.setText(_translate("MainWindow", "Product Name:"))
        self.label_2.setText(_translate("MainWindow", "Product Id:"))
        self.groupBox_3.setTitle(_translate("MainWindow", "List of Products:"))
        item = self.tableWidgetProduct.horizontalHeaderItem(0)
        item.setText(_translate("MainWindow", "Product Id"))
        item = self.tableWidgetProduct.horizontalHeaderItem(1)
        item.setText(_translate("MainWindow", "Product Name"))
        item = self.tableWidgetProduct.horizontalHeaderItem(2)
        item.setText(_translate("MainWindow", "Unit Price"))

Bước 6: Tạo “MainWindowEx.py” để viết lớp kế thừa từ Generate Python code ở bước trước nhằm xử lý các sự kiện người dùng như: Thêm, Lưu, Xóa, Xem dữ liệu…. mà không bị lệ thuộc khi trong tương lai giao diện có thay đổi

from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QTableWidgetItem, QMessageBox

from FileFactory import FileFactory
from MainWindow import Ui_MainWindow
from Product import Product


class MainWindowEx(Ui_MainWindow):
    def __init__(self):
        self.products=[]
        self.selectedProduct=None
        self.fileFactory=FileFactory()

“MainWindowEx.py” được thiết kế constructor khởi tạo 3 biến đối tượng:

  • products là biến lưu danh sách Product
  • selectedProduct là biến lưu Product đang được chọn trên giao diện hiện hành, dựa vào biến này để ta biết đang lưu mới Product hay cập nhật dữ liệu cho Product hiện tại
  • fileFactory là biến đối tượng để Serialize và Deserialize danh sách Product

“MainWindowEx.py” sẽ Override hàm setupUI để nạp giao diện cũng như gán các signal và các hàm mặc định khi thực thi ứng dụng:

def setupUi(self, MainWindow):
    super().setupUi(MainWindow)
    self.MainWindow=MainWindow
    self.products=self.fileFactory.readData("database.json",Product)
    self.loadDataIntoTableWidget()
    self.pushButtonNew.clicked.connect(self.processNew)
    self.pushButtonSave.clicked.connect(self.processSave)
    self.tableWidgetProduct.itemSelectionChanged.connect(self.processItemSelection)
    self.pushButtonRemove.clicked.connect(self.processDelete)

Mặc định khi khởi tạo xong giao diện, chương trình sẽ đọc danh sách dữ liệu từ JSON thông qua đối tượng FileFactory, sau đó gọi hàm loadDataIntoTableWidget để hiển thị danh sách Product nếu đọc dữ liệu thành công:

def loadDataIntoTableWidget(self):
    self.tableWidgetProduct.setRowCount(0)
    for i in range(len(self.products)):
        product=self.products[i]
        row = self.tableWidgetProduct.rowCount()
        self.tableWidgetProduct.insertRow(row)
        self.tableWidgetProduct.setItem(row, 0, QTableWidgetItem(str(product.ProductId)))
        self.tableWidgetProduct.setItem(row, 1, QTableWidgetItem(product.ProductName))
        itemPrice = QTableWidgetItem()
        itemPrice.setText(str(product.Price))
        if product.Price < 10000000:
            itemPrice.setForeground(Qt.GlobalColor.red)
            itemPrice.setBackground(Qt.GlobalColor.yellow)
        self.tableWidgetProduct.setItem(row, 2, itemPrice)

Hàm trên sẽ duyệt qua toàn bộ danh sách các Product và đưa nó lên giao diện QTableWidget, mỗi một thuộc tính sẽ được đưa vào ô dữ liệu tương ứng là QTableWidgetItem. Nếu giá của sản phẩm nào <10 triệu thì sẽ tô nền vàng và chữ đỏ.

row = self.tableWidgetProduct.rowCount() sẽ trả về số dòng trong QTableWidget, tương đương với dòng cuối cùng

Hà insertRow(row) sẽ chèn dòng mới vào dòng cuối cùng của QTableWidget

Tiếp tới là các hàm setItem để thay đổi giá trị tại các ô của dòng đó.

Ngoài ra Tui cố tình viết 2 cách. Nếu khi ta cần dòng phức tạp thì tách riêng ra QTableWidgetItem để dễ định dạng (ví dụ định dạng cho price)

Hàm ProcessNew sẽ xóa dữ liệu hiện tại để cho người dùng nhập liệu:

def processNew(self):
    self.lineEditProductId.setText("")
    self.lineEditProductName.setText("")
    self.lineEditUnitPrice.setText("")
    self.lineEditProductId.setFocus()
    self.selectedProduct=None

Khi người dùng nhấn vào nút “New” chương trình sẽ xóa dữ liệu trên giao diện QLineEdit và focus tới ô ID, cũng như gán selectedProduct=None để đánh dấu là thêm mới.

Hàm processSave sẽ xử lý 2 tình huống: Lưu mới và lưu cập nhật Product tùy thuộc vào selectedProduct:

def processSave(self):
    product=Product(self.lineEditProductId.text(),self.lineEditProductName.text(),float(self.lineEditUnitPrice.text()))
    if self.selectedProduct==None:
        self.products.append(product)
        row = self.tableWidgetProduct.rowCount()
        self.tableWidgetProduct.insertRow(row)
    else:
        row=self.products.index(self.selectedProduct)
    self.selectedProduct = product
    self.products[row] = self.selectedProduct
    self.tableWidgetProduct.setItem(row,0,QTableWidgetItem(str(product.ProductId)))
    self.tableWidgetProduct.setItem(row,1,QTableWidgetItem(product.ProductName))
    itemPrice=QTableWidgetItem()
    itemPrice.setText(str(product.Price))
    if product.Price<10000000:
        itemPrice.setForeground(Qt.GlobalColor.red)
        itemPrice.setBackground(Qt.GlobalColor.yellow)
    self.tableWidgetProduct.setItem(row, 2, itemPrice)
    self.fileFactory.writeData("database.json", self.products)

Hàm processSave ở trên sẽ kiểm tra biến selectedProduct để điều hướng là lưu mới hay lưu Cập nhật. Sau khi xử lý thành công thì chương trình sẽ hiển thị dữ liệu lên QTableWidget đồng thời lưu dữ liệu xuống ổ cứng bằng hàm writeData. Dữ liệu là mảng JSON sẽ được Serialize xuống ổ cứng.

Hàm processItemSelection sẽ xử lý người dùng lựa chọn dữ liệu trên các dòng của QTableWidget:

def processItemSelection(self):
    row=self.tableWidgetProduct.currentRow()
    if row ==-1 or row>=len(self.products):
        return
    #id=self.tableWidgetProduct.item(row,0).text()
    #name=self.tableWidgetProduct.item(row,1).text()
    #price = self.tableWidgetProduct.item(row, 2).text()
    product=self.products[row]
    self.selectedProduct=product
    id=product.ProductId
    name=product.ProductName
    price=product.Price
    self.lineEditProductId.setText(str(id))
    self.lineEditProductName.setText(name)
    self.lineEditUnitPrice.setText(str(price))

Dòng nào được lựa chọn thì thông tin chi tiết sẽ được hiển thị vào các ô QLineEdit.

Cuối cùng là hàm processDelete để xóa Product đang chọn trên QTableWidget:

def processDelete(self):
    dlg = QMessageBox(self.MainWindow)
    if self.selectedProduct == None:
        dlg.setWindowTitle("Deleteing error")
        dlg.setIcon(QMessageBox.Icon.Critical)
        dlg.setText("You have to select a Product to delete")
        dlg.exec()
        return
    dlg.setWindowTitle("Confirmation Deleting")
    dlg.setText("Are you sure you want to delete?")
    buttons = QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
    dlg.setStandardButtons(buttons)
    button = dlg.exec()
    if button == QMessageBox.StandardButton.Yes:
        row=self.products.index(self.selectedProduct)
        self.products.remove(self.selectedProduct)
        self.selectedProduct=None
        self.tableWidgetProduct.removeRow(row)
        self.processNew()
        self.fileFactory.writeData("database.json", self.products)

Chương trình sẽ dùng QMessageBox để xác nhận xem người sử dụng có muốn xóa hay không. Nếu xác nhận xóa thì chương trình sẽ xóa đồng thời lưu lại dữ liệu xuống ổ cứng.

Dưới đây là Full code của MainWindowEx:

from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QTableWidgetItem, QMessageBox

from FileFactory import FileFactory
from MainWindow import Ui_MainWindow
from Product import Product


class MainWindowEx(Ui_MainWindow):
    def __init__(self):
        self.products=[]
        self.selectedProduct=None
        self.fileFactory=FileFactory()
    def setupUi(self, MainWindow):
        super().setupUi(MainWindow)
        self.MainWindow=MainWindow
        self.products=self.fileFactory.readData("database.json",Product)
        self.loadDataIntoTableWidget()
        self.pushButtonNew.clicked.connect(self.processNew)
        self.pushButtonSave.clicked.connect(self.processSave)
        self.tableWidgetProduct.itemSelectionChanged.connect(self.processItemSelection)
        self.pushButtonRemove.clicked.connect(self.processDelete)
    def loadDataIntoTableWidget(self):
        self.tableWidgetProduct.setRowCount(0)
        for i in range(len(self.products)):
            product=self.products[i]
            row = self.tableWidgetProduct.rowCount()
            self.tableWidgetProduct.insertRow(row)
            self.tableWidgetProduct.setItem(row, 0, QTableWidgetItem(str(product.ProductId)))
            self.tableWidgetProduct.setItem(row, 1, QTableWidgetItem(product.ProductName))
            itemPrice = QTableWidgetItem()
            itemPrice.setText(str(product.Price))
            if product.Price < 10000000:
                itemPrice.setForeground(Qt.GlobalColor.red)
                itemPrice.setBackground(Qt.GlobalColor.yellow)
            self.tableWidgetProduct.setItem(row, 2, itemPrice)
    def processNew(self):
        self.lineEditProductId.setText("")
        self.lineEditProductName.setText("")
        self.lineEditUnitPrice.setText("")
        self.lineEditProductId.setFocus()
        self.selectedProduct=None
    def processSave(self):
        product=Product(self.lineEditProductId.text(),self.lineEditProductName.text(),float(self.lineEditUnitPrice.text()))
        if self.selectedProduct==None:
            self.products.append(product)
            row = self.tableWidgetProduct.rowCount()
            self.tableWidgetProduct.insertRow(row)
        else:
            row=self.products.index(self.selectedProduct)
        self.selectedProduct = product
        self.products[row] = self.selectedProduct
        self.tableWidgetProduct.setItem(row,0,QTableWidgetItem(str(product.ProductId)))
        self.tableWidgetProduct.setItem(row,1,QTableWidgetItem(product.ProductName))
        itemPrice=QTableWidgetItem()
        itemPrice.setText(str(product.Price))
        if product.Price<10000000:
            itemPrice.setForeground(Qt.GlobalColor.red)
            itemPrice.setBackground(Qt.GlobalColor.yellow)
        self.tableWidgetProduct.setItem(row, 2, itemPrice)
        self.fileFactory.writeData("database.json", self.products)
    def processItemSelection(self):
        row=self.tableWidgetProduct.currentRow()
        if row ==-1 or row>=len(self.products):
            return
        #id=self.tableWidgetProduct.item(row,0).text()
        #name=self.tableWidgetProduct.item(row,1).text()
        #price = self.tableWidgetProduct.item(row, 2).text()
        product=self.products[row]
        self.selectedProduct=product
        id=product.ProductId
        name=product.ProductName
        price=product.Price
        self.lineEditProductId.setText(str(id))
        self.lineEditProductName.setText(name)
        self.lineEditUnitPrice.setText(str(price))
    def processDelete(self):
        dlg = QMessageBox(self.MainWindow)
        if self.selectedProduct == None:
            dlg.setWindowTitle("Deleteing error")
            dlg.setIcon(QMessageBox.Icon.Critical)
            dlg.setText("You have to select a Product to delete")
            dlg.exec()
            return
        dlg.setWindowTitle("Confirmation Deleting")
        dlg.setText("Are you sure you want to delete?")
        buttons = QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
        dlg.setStandardButtons(buttons)
        button = dlg.exec()
        if button == QMessageBox.StandardButton.Yes:
            row=self.products.index(self.selectedProduct)
            self.products.remove(self.selectedProduct)
            self.selectedProduct=None
            self.tableWidgetProduct.removeRow(row)
            self.processNew()
            self.fileFactory.writeData("database.json", self.products)
    def show(self):
        self.MainWindow.show()

Bước 7: Tạo “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 phần mềm lên ta sẽ có kết quả như mong muốn:

Như vậy là Tui đã hướng dẫn xong cách ứng dụng QTableWidget để lưu trữ và xử lý dữ liệu dạng lưới, cụ thể là bài quản lý sản phẩm. Chương trình kết hợp nhiều kỹ thuật như mô hình hóa đối tượng, thêm mới, sửa, xóa…

Các bạn thực hiện lại bài này nhiều lần để ứng dụng được QTableWidget vào các trường hợp cụ thể của mình.

Source code bài này các bạn tải ở đây:

https://www.mediafire.com/file/acl70y1850nfl37/LearnQTableWidgetPart2.rar/file

Bài học sau Tui sẽ mình họa QTableWidget với cơ sở dữ liệu SQLite các bạn chú ý theo dõi

Leave a Reply