Bài 25: Xử lý dữ liệu dạng bảng- QTableWidget và SQLite database–Part 4

Trong bài này Tui sẽ tiếp tục trình bày chi tiết về SQLite database trong Python với framework PyQt6. Các bạn sẽ tự tay tạo ra một cơ sở dữ liệu SQLite database bằng DB Browswer, rồi từ PyQt6 viết các mã lệnh Python để: Xem, thêm, sửa, xóa dữ liệu. Giao diện phần mềm của bài này mà chúng ta xây dựng sẽ như dưới đây:

  • Tạo Cơ sở dữ liệu SQLite bằng DB Browser, thiết kế các bảng dữ liệu có cột Id Primary Key là Auto Increment.
  • Viết chức năng đọc toàn bộ dữ liệu trong CSDL SQLite lên giao diện QTableWidget trong mục List Products
  • Xử lý sự kiện người dùng chọn các dòng dữ liệu trên QTableWidget và hiển thị thông tin chi tiết xuống phần Product Details
  • Chức năng “New” sẽ xóa dữ liệu đang nhập ở các ô QLineEdit và focus tới ô Product code
  • Chức năng “Save”, viết mã lệnh để chương trình tự xử lý 2 trường hợp là lưu mới dữ liệu xuống SQLite hoặc là lưu cập nhập xuống SQLite
  • Chức năng “Remove”, viết mã lệnh để xóa dòng dữ liệu đang chọn, cho người dùng xác thực trước khi xóa.

Chúng ta thực hiện chi tiết từng bước như dưới đây:

Bước 1: Tạo một dự án tên “LearnQTableWidgetPart4

  • Thư mục “database” sẽ lưu trữ SQLite “MyDatabase.sqlite” mà ta sẽ làm chi tiết ở bước sau.
  • Thư mục “images” lưu trữ các hình ảnh, icon của phần mềm
  • File “MainWindow.ui” là file giao diện thiết kế bằng Qt Designer
  • File “MainWindow.py” là file Generate Python code
  • File “MainWindowEx.py” là file mã lệnh kế thừa từ MainWindow.py để xử lý các nghiệp vụ phần mềm mà không lệ thuộc vào giao diện có thay đổi hay không trong tương lai
  • File “MyApp.py” là file thực thi phần mềm

Bước 2: Thiết kế cơ sở dữ liệu SQLite, đặt tên “MyDatabase.sqlite”

Từ phần mềm DB Browser (đã học ở bài 24), Các bạn chọn “New Database

Sau đó chọn nơi lưu trữ, ta lưu vào thư mục “database” trong bước 1.

Đặt tên “MyDatabase.sqlite” rồi bấm “Save” lúc này màn hình tạo Table sẽ hiển thị ra như dưới đây:

Trong mục Table ta đặt tên, rồi nhấn vào nút “Add” để thêm các thuộc tính. Ví dụ ta tạo bảng User như dưới đây:

  • Mỗi thuộc tính nó sẽ có kiểu dữ liệu tương ứng, ở trên ta thấy thuộc tính Id Tui chọn Type là INTEGER và tick vào PK(Primary Key) và AI (Auto Increment) là khóa chính tự động tăng.
  • Thuộc tính UserName, Password có type là TEXT

Sau khi tạo xong các thuộc tính ta nhấn nút OK và xem kết quả:

Tiếp theo ta nhập một vài dữ liệu mẫu cho bảng User này bằng cách nhấn vào thẻ “Browse Data”:

Để thêm dữ liệu cho bảng thì bấm vào biểu tượng New Record mà Tui tô khung đỏ ở trên, sau đó nhập dữ liệu và các dòng và cột tương ứng ở chỗ mũi tên màu đỏ.

Tương tự như thế, ta tạo bảng tiếp theo có tên “Product” bằng cách bấm vào biểu tượng “Create Table” trong thẻ “Database Structure”:

Ta thiết kế bảng Product như dưới đây:

Tương tự như bảng User, bảng Product Tui cũng cấu hình Id là cột khóa chính (PK) và tự động tăng (AI – Auto Increment)

  • cột ProductCode để lưu mã Product có kiểu TEXT
  • cột productName để lưu tên Product có kiểu TEXT
  • và cuối cùng là cột UnitPrice để lưu giá Product có kểu REAL

sau khi cấu hình xong thì bấm OK , ta có kết quả:

  • Tương tự như bảng User ta nhập một số dữ liệu mẫu ban đầu cho Product:

Ta nhấn CTRL+S để lưu sự thay đổi của SQLITE mà ta mới cấu hình.

  • Bảng User Tui sẽ không hướng dẫn code truy vấn. Bài này là bài tập các bạn cần thiết kết màn hình đăng nhập, nếu đăng nhập thành công thì vào màn hình quản lý sản phẩm
  • Bảng Product Tui sẽ hướng dẫn chi tiết để quản lý sản phẩm: Xem, thêm, sửa, xóa….

Bước 3: Thiết kế giao diện “MainWindow.ui” bằng Qt Designer được tích hợp trong Pycharm mà Tui đã hướng dẫn ở những bài đầu tiên của chuỗi bài học.

Các bạn kéo thả các Widget và cấu hình cũng như đặt tên cho các Widget như hình trên.

Bước 4: Generate Python code cho file “MainWindow.ui”, lúc này file mã lệnh “MainWindow.py” tự động được 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(491, 481)
        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.groupBox = QtWidgets.QGroupBox(parent=self.centralwidget)
        self.groupBox.setGeometry(QtCore.QRect(20, 360, 461, 71))
        self.groupBox.setStyleSheet("background-color: rgb(241, 255, 202);")
        self.groupBox.setObjectName("groupBox")
        self.pushButtonRemove = QtWidgets.QPushButton(parent=self.groupBox)
        self.pushButtonRemove.setGeometry(QtCore.QRect(340, 20, 101, 41))
        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(32, 32))
        self.pushButtonRemove.setObjectName("pushButtonRemove")
        self.pushButtonNew = QtWidgets.QPushButton(parent=self.groupBox)
        self.pushButtonNew.setGeometry(QtCore.QRect(40, 20, 93, 41))
        self.pushButtonNew.setStyleSheet("background-color: rgb(255, 170, 255);")
        icon2 = QtGui.QIcon()
        icon2.addPixmap(QtGui.QPixmap("images/ic_new.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonNew.setIcon(icon2)
        self.pushButtonNew.setIconSize(QtCore.QSize(32, 32))
        self.pushButtonNew.setObjectName("pushButtonNew")
        self.pushButtonSave = QtWidgets.QPushButton(parent=self.groupBox)
        self.pushButtonSave.setGeometry(QtCore.QRect(190, 20, 101, 41))
        self.pushButtonSave.setStyleSheet("background-color: rgb(255, 170, 255);")
        icon3 = QtGui.QIcon()
        icon3.addPixmap(QtGui.QPixmap("images/ic_save.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonSave.setIcon(icon3)
        self.pushButtonSave.setIconSize(QtCore.QSize(32, 32))
        self.pushButtonSave.setObjectName("pushButtonSave")
        self.groupBox_2 = QtWidgets.QGroupBox(parent=self.centralwidget)
        self.groupBox_2.setGeometry(QtCore.QRect(20, 240, 461, 111))
        self.groupBox_2.setStyleSheet("background-color: rgb(234, 254, 255);")
        self.groupBox_2.setObjectName("groupBox_2")
        self.lineEditUnitPrice = QtWidgets.QLineEdit(parent=self.groupBox_2)
        self.lineEditUnitPrice.setGeometry(QtCore.QRect(110, 80, 341, 22))
        self.lineEditUnitPrice.setStyleSheet("background-color: rgb(241, 255, 202);")
        self.lineEditUnitPrice.setObjectName("lineEditUnitPrice")
        self.lineEditProductCode = QtWidgets.QLineEdit(parent=self.groupBox_2)
        self.lineEditProductCode.setGeometry(QtCore.QRect(110, 20, 341, 22))
        self.lineEditProductCode.setStyleSheet("background-color: rgb(241, 255, 202);")
        self.lineEditProductCode.setObjectName("lineEditProductCode")
        self.lineEditProductName = QtWidgets.QLineEdit(parent=self.groupBox_2)
        self.lineEditProductName.setGeometry(QtCore.QRect(110, 50, 341, 22))
        self.lineEditProductName.setStyleSheet("background-color: rgb(241, 255, 202);")
        self.lineEditProductName.setObjectName("lineEditProductName")
        self.label_2 = QtWidgets.QLabel(parent=self.groupBox_2)
        self.label_2.setGeometry(QtCore.QRect(10, 50, 91, 16))
        self.label_2.setObjectName("label_2")
        self.label = QtWidgets.QLabel(parent=self.groupBox_2)
        self.label.setGeometry(QtCore.QRect(10, 20, 81, 16))
        self.label.setObjectName("label")
        self.label_3 = QtWidgets.QLabel(parent=self.groupBox_2)
        self.label_3.setGeometry(QtCore.QRect(10, 80, 91, 16))
        self.label_3.setObjectName("label_3")
        self.groupBox_3 = QtWidgets.QGroupBox(parent=self.centralwidget)
        self.groupBox_3.setGeometry(QtCore.QRect(20, 30, 461, 201))
        self.groupBox_3.setStyleSheet("background-color: rgb(241, 255, 202);")
        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(255, 255, 255);")
        self.tableWidgetProduct.setObjectName("tableWidgetProduct")
        self.tableWidgetProduct.setColumnCount(4)
        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)
        item = QtWidgets.QTableWidgetItem()
        self.tableWidgetProduct.setHorizontalHeaderItem(3, item)
        self.verticalLayout.addWidget(self.tableWidgetProduct)
        self.label_4 = QtWidgets.QLabel(parent=self.centralwidget)
        self.label_4.setGeometry(QtCore.QRect(70, 0, 341, 31))
        font = QtGui.QFont()
        font.setPointSize(15)
        font.setBold(True)
        font.setItalic(True)
        font.setWeight(75)
        self.label_4.setFont(font)
        self.label_4.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
        self.label_4.setObjectName("label_4")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 491, 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 - SQLite"))
        self.groupBox.setTitle(_translate("MainWindow", "Action"))
        self.pushButtonRemove.setText(_translate("MainWindow", "Remove"))
        self.pushButtonNew.setText(_translate("MainWindow", "New"))
        self.pushButtonSave.setText(_translate("MainWindow", "Save"))
        self.groupBox_2.setTitle(_translate("MainWindow", "Product Detail:"))
        self.label_2.setText(_translate("MainWindow", "Product Name:"))
        self.label.setText(_translate("MainWindow", "Product Code:"))
        self.label_3.setText(_translate("MainWindow", "Unit Price:"))
        self.groupBox_3.setTitle(_translate("MainWindow", "List Products:"))
        item = self.tableWidgetProduct.horizontalHeaderItem(0)
        item.setText(_translate("MainWindow", "Id"))
        item = self.tableWidgetProduct.horizontalHeaderItem(1)
        item.setText(_translate("MainWindow", "Product Code"))
        item = self.tableWidgetProduct.horizontalHeaderItem(2)
        item.setText(_translate("MainWindow", "Product Name"))
        item = self.tableWidgetProduct.horizontalHeaderItem(3)
        item.setText(_translate("MainWindow", "Unit Price"))
        self.label_4.setText(_translate("MainWindow", "Product - SQLite"))

Bước 5: Viết file mã lệnh “MainWindowEx.py” kế thừa từ “MainWindow.py” để xử lý sự kiện người dùng, cũng như không bị lệ thuộc vào giao diện trong tương lai thay đổi mà phải generate lại mã nguồn giao diện.

from PyQt6.QtSql import QSqlDatabase, QSqlTableModel, QSqlRecord
from PyQt6.QtWidgets import QTableWidgetItem, QMessageBox
from MainWindow import Ui_MainWindow

class MainWindowEx(Ui_MainWindow):
    def __init__(self):
        self.databasePath="database/MyDatabase.sqlite"
        self.selectedRecord=None
        self.selectedRow=None

Trong MainWindowEx, Tui định nghĩa 3 biến:

  • databasePath để lưu trữ đường dẫn tới SQLite mà ta thiết kế
  • selectedRecord là biến lưu trữ đối tượng QSqlRecord đang chọn để hỗ trợ cho việc Lưu mới hay cập nhật dữ liệu tiện lợi nhất
  • selectedRow là biến lưu trữ dòng hiện tại đang chọn (index) để hỗ trợ cho việc Lưu mới hay cập nhật dữ liệu tiện lợi nhất

Hàm setupUi được override để định mặc định gọi các kết nối và hiển thị dữ liệu danh sách Product lên QTableWidget, cũng như gán các signal để xử lý sử kiện người dùng:

def setupUi(self, MainWindow):
    super().setupUi(MainWindow)
    self.MainWindow=MainWindow
    self.connectDatabase()
    self.loadProduct()
    self.pushButtonNew.clicked.connect(self.processNew)
    self.tableWidgetProduct.itemSelectionChanged.connect(self.processItemSelection)
    self.pushButtonSave.clicked.connect(self.processSave)
    self.pushButtonRemove.clicked.connect(self.processRemove)

Hàm connectDatabase sẽ gọi các kết nối tới cơ sở dữ liệu SQLite:

def connectDatabase(self):
    # create QSqlDatabase object
    self.db = QSqlDatabase("QSQLITE")
    # set the database selected path
    self.db.setDatabaseName(self.databasePath)
    # Open the SQLite database
    self.db.open()
    # Create QSqlTableModel object, and self.db is assigned
    self.model = QSqlTableModel(db=self.db)

Đối tượng QSqlitableModel được kích hoạt và được giao cho biến model quản lý

biến model này sẽ trỏ tới bất kỳ bảng dữ liệu nào mà ta mong muốn truy suất.

Hàm loadProduct để truy vấn toàn bộ dữ liệu trong bảng Product và hiển thị lên QTableWidget:

def loadProduct(self):
    # select table name to invoke data
    tableName = "Product"
    self.model.setTable(tableName)
    # active for selecting data
    self.model.select()
    # reset QTableWidget to 0 row
    self.tableWidgetProduct.setRowCount(0)
    # loop for insert new row:
    for i in range(self.model.rowCount()):
        # insert new row:
        self.tableWidgetProduct.insertRow(i)
        # get a record with i index:
        record = self.model.record(i)
        itemId = QTableWidgetItem(str(record.value(0)))
        itemProductCode = QTableWidgetItem(str(record.value(1)))
        itemProductName = QTableWidgetItem(str(record.value(2)))
        itemUnitPrice = QTableWidgetItem(str(record.value(3)))
        self.tableWidgetProduct.setItem(i, 0, itemId)
        self.tableWidgetProduct.setItem(i, 1, itemProductCode)
        self.tableWidgetProduct.setItem(i, 2, itemProductName)
        self.tableWidgetProduct.setItem(i, 3, itemUnitPrice)

hàm processNew để xóa toàn bộ dữ liệu trong QLineEdit và focus tới ô Code để hỗ trợ nhập liệu nhanh chóng. Đồng thời các biến selectedRecord và selectedRow cũng được reset về None để đánh dấu rằng khi nhấn “Save” là lưu mới 1 record:

def processNew(self):
    self.lineEditProductCode.setText("")
    self.lineEditProductName.setText("")
    self.lineEditUnitPrice.setText("")
    self.lineEditProductCode.setFocus()
    self.selectedRecord=None
    self.selectedRow=None

Hàm processItemSelection sẽ xử lý sự kiện người dùng chọn từng dòng trên QTableWidget, nó truy vấn dữ liệu trong model và hiển thị lên phần Product Details:

def processItemSelection(self):
    #Get current row index on the QTableWidget
    self.selectedRow=self.tableWidgetProduct.currentRow()
    if self.selectedRow==-1:
        return
    #call record(index) method from model
    self.selectedRecord=self.model.record(self.selectedRow)
    #Get detail information from QSqlRecord
    #id=self.selectedRecord.value(0)
    productCode=self.selectedRecord.value(1)
    productName=self.selectedRecord.value(2)
    unitPrice=self.selectedRecord.value(3)
    # show detail information into the QLineEdit
    self.lineEditProductCode.setText(productCode)
    self.lineEditProductName.setText(productName)
    self.lineEditUnitPrice.setText(str(unitPrice))

Hàm processSave sẽ thực hiện 2 tác vụ: Lưu mới và lưu cập nhật, nếu selectedRecord là None thì lưu mới, còn selectedRecord là khác None thì lưu cập nhật:

def processSave(self):
    #Get lasted row
    row = self.model.rowCount()
    if self.selectedRecord==None:#if new product
        #Get the QSqlRecord from record(row)
        record=self.model.record(row)
        #assign the value for QSqlRecord
        #record.setValue(0, None)
        record.setValue(1,self.lineEditProductCode.text())
        record.setValue(2, self.lineEditProductName.text())
        record.setValue(3, float(self.lineEditUnitPrice.text()))
        #call the insertRecord for storing a new record into SQLite
        result=self.model.insertRecord(row,record)
        #if saving successful then result =True
        if result==True:
            #save the lasted record and reload products
            self.selectedRecord=record
            self.selectedRow=row
            self.loadProduct()
    else:#if updating the QSqlRecord
        # assign the value for QSqlRecord
        self.selectedRecord.setValue(1, self.lineEditProductCode.text())
        self.selectedRecord.setValue(2, self.lineEditProductName.text())
        self.selectedRecord.setValue(3, float(self.lineEditUnitPrice.text()))
        # call the updateRowInTable for updating selected record into SQLite
        result=self.model.updateRowInTable(self.selectedRow,self.selectedRecord)
        # if saving successful then result =True
        if result == True:
            #reload products
            self.loadProduct()
  • hàm insertRecord(row,record) để lưu mới, nếu lưu thành công thì nó trả kết quả về là True
  • hàm updateRowInTable(row,record) để lưu cập nhật, nếu lưu thành công thì nó trả kết quả về là True

Khi lưu thành công thì chương trình sẽ nạp lại dữ liệu lên QTableWidget.

Hàm processRemove dùng để xóa QSqlRecord đang chọn trên QTableWidget:

def processRemove(self):
    dlg = QMessageBox(self.MainWindow)
    if self.selectedRecord == 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:
        #call removeRow method to remove QSqlRecord from the SQLite
        result=self.model.removeRow(self.selectedRow)
        # if saving successful then result =True
        if result == True:
            # save the lasted record and reload products
            self.loadProduct()
            self.processNew()
  • Tui coding dùng QMessageBox để hiển thị cửa sổ xác nhận có muốn xóa hay không
  • Hàm removeRow(row) dùng để xóa dòng dữ liệu đang chọn ra khỏi bảng. Nếu xóa thành công thì kết quả trả về là True, lúc này ta nạp lại dữ liệu lên giao diện QTableWidget, đồng thời gọi hàm processNew để xóa dữ liệu trong QLineEdit đi

Dưới đây là coding đầy đủ của chương trình:

from PyQt6.QtSql import QSqlDatabase, QSqlTableModel, QSqlRecord
from PyQt6.QtWidgets import QTableWidgetItem, QMessageBox
from MainWindow import Ui_MainWindow

class MainWindowEx(Ui_MainWindow):
    def __init__(self):
        self.databasePath="database/MyDatabase.sqlite"
        self.selectedRecord=None
        self.selectedRow=None
    def setupUi(self, MainWindow):
        super().setupUi(MainWindow)
        self.MainWindow=MainWindow
        self.connectDatabase()
        self.loadProduct()
        self.pushButtonNew.clicked.connect(self.processNew)
        self.tableWidgetProduct.itemSelectionChanged.connect(self.processItemSelection)
        self.pushButtonSave.clicked.connect(self.processSave)
        self.pushButtonRemove.clicked.connect(self.processRemove)

    def connectDatabase(self):
        # create QSqlDatabase object
        self.db = QSqlDatabase("QSQLITE")
        # set the database selected path
        self.db.setDatabaseName(self.databasePath)
        # Open the SQLite database
        self.db.open()
        # Create QSqlTableModel object, and self.db is assigned
        self.model = QSqlTableModel(db=self.db)

    def loadProduct(self):
        # select table name to invoke data
        tableName = "Product"
        self.model.setTable(tableName)
        # active for selecting data
        self.model.select()
        # reset QTableWidget to 0 row
        self.tableWidgetProduct.setRowCount(0)
        # loop for insert new row:
        for i in range(self.model.rowCount()):
            # insert new row:
            self.tableWidgetProduct.insertRow(i)
            # get a record with i index:
            record = self.model.record(i)
            itemId = QTableWidgetItem(str(record.value(0)))
            itemProductCode = QTableWidgetItem(str(record.value(1)))
            itemProductName = QTableWidgetItem(str(record.value(2)))
            itemUnitPrice = QTableWidgetItem(str(record.value(3)))
            self.tableWidgetProduct.setItem(i, 0, itemId)
            self.tableWidgetProduct.setItem(i, 1, itemProductCode)
            self.tableWidgetProduct.setItem(i, 2, itemProductName)
            self.tableWidgetProduct.setItem(i, 3, itemUnitPrice)

    def processNew(self):
        self.lineEditProductCode.setText("")
        self.lineEditProductName.setText("")
        self.lineEditUnitPrice.setText("")
        self.lineEditProductCode.setFocus()
        self.selectedRecord=None
        self.selectedRow=None

    def processItemSelection(self):
        #Get current row index on the QTableWidget
        self.selectedRow=self.tableWidgetProduct.currentRow()
        if self.selectedRow==-1:
            return
        #call record(index) method from model
        self.selectedRecord=self.model.record(self.selectedRow)
        #Get detail information from QSqlRecord
        #id=self.selectedRecord.value(0)
        productCode=self.selectedRecord.value(1)
        productName=self.selectedRecord.value(2)
        unitPrice=self.selectedRecord.value(3)
        # show detail information into the QLineEdit
        self.lineEditProductCode.setText(productCode)
        self.lineEditProductName.setText(productName)
        self.lineEditUnitPrice.setText(str(unitPrice))

    def processSave(self):
        #Get lasted row
        row = self.model.rowCount()
        if self.selectedRecord==None:#if new product
            #Get the QSqlRecord from record(row)
            record=self.model.record(row)
            #assign the value for QSqlRecord
            #record.setValue(0, None)
            record.setValue(1,self.lineEditProductCode.text())
            record.setValue(2, self.lineEditProductName.text())
            record.setValue(3, float(self.lineEditUnitPrice.text()))
            #call the insertRecord for storing a new record into SQLite
            result=self.model.insertRecord(row,record)
            #if saving successful then result =True
            if result==True:
                #save the lasted record and reload products
                self.selectedRecord=record
                self.selectedRow=row
                self.loadProduct()
        else:#if updating the QSqlRecord
            # assign the value for QSqlRecord
            self.selectedRecord.setValue(1, self.lineEditProductCode.text())
            self.selectedRecord.setValue(2, self.lineEditProductName.text())
            self.selectedRecord.setValue(3, float(self.lineEditUnitPrice.text()))
            # call the updateRowInTable for updating selected record into SQLite
            result=self.model.updateRowInTable(self.selectedRow,self.selectedRecord)
            # if saving successful then result =True
            if result == True:
                #reload products
                self.loadProduct()
    def processRemove(self):
        dlg = QMessageBox(self.MainWindow)
        if self.selectedRecord == 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:
            #call removeRow method to remove QSqlRecord from the SQLite
            result=self.model.removeRow(self.selectedRow)
            # if saving successful then result =True
            if result == True:
                # save the lasted record and reload products
                self.loadProduct()
                self.processNew()
    def show(self):
        self.MainWindow.show()

Bước 6: Cuối cùng ta 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 chương trình ta có kết quả như mong muốn:

Như vậy là tới đây các Bạn đã biết cách xây dựng một phần mềm hoàn chỉnh có tương tác SQLite từ khâu: Xem, thêm, sửa xóa. Cũng như ôn tập lại các signal, xử lý sự kiện QMessageBox…

Các bạn có thể áp dụng bài này vào các bài quản lý khác như quản lý Nhân viên, quản lý kho….

Mã lệnh đầy đủ của dự án bạn tải ở đây:

https://www.mediafire.com/file/mcvu25j1fibpliu/LearnQTableWidgetPart4.rar/file

Bài học tiếp theo Tui trình bày về ViewModel để hiển thị dữ liệu dạng Bảng nhưng nó cao cấp hơn. Các bạn chú ý theo dõi:

Chúc các bạn thành công

Leave a Reply