Bài 18: QListWidget – Part 2– Basic Widgets – PyQt6

Trong bài QListWidget Part 1 các bạn đã nắm rõ và biết cách sử dụng các tính năng của Widget này. Trong Part 2 chúng ta sẽ tiếp tục củng cố các kiến thức và kỹ năng xử lý với QListWidget thông qua bài minh họa quản lý Employee, các bạn sẽ dùng Qt Designer để thiết kế giao diện, và thực hiện các chức năng nghiệp vụ trên QListWidget và các control khác bằng ngôn ngữ Python trong công cụ Pycharm được mô tả như dưới đây:

  • (1) Chức năng mô hình hóa hướng đối tượng: Lớp Employee được thiết kế và được mô hình hóa dữ liệu từ giao diện vào danh sách hướng đối tượng Employee cũng như mapping thành định dạng JSon Array để lưu vào ổ cứng (gọi là Json Encode). Chức năng này sẽ hỗ trợ các thao tác Thêm mới Employee, Chỉnh sửa Employee và Xóa Employee. Khi có sự thay đổi dữ liệu thì chương trình sẽ lưu dữ liệu xuống ổ cứng với định dạn JSon
  • (2) Chức năng phục hồi dữ liệu và mô hình hóa lại hướng đối tượng từ JSon Array được lưu trong ổ cứng lên bộ nhớ và được mapping và danh sách đối tượng Employee (gọi là Json Decode). Chức năng này sẽ hỗ trợ thao tác hiển thị lại dữ liệu danh sách Employee lên giao diện
  • (3) Chức năng “New“: Khi nhấn vào button này thì các QLineEdit sẽ bỏ trống và con trỏ văn bản được focus tới ô “Name” để người sử dụng dễ dàng nhập liệu
  • (4) Chức năng “Save“: Chức năng này có 2 nhiệm vụ là lưu mới Employee hoặc cập nhật Employee. Nếu Email tồn tại thì cập nhật, còn Email không tồn tại thì thêm mới. Các bạn có thể bổ sung thêm các thuộc tính khác như ID cho Employee. Khi “Save” thành công thì chương trình vừa hiển thị dữ liệu lên giao diện QListWidget vừa lưu danh sách dữ liệu từ giao diện xuống ổ cứng với định dạng JSon (JSon Array vì ta lưu danh sách, tuy nhiên để nói ngắn gọn ta có thể nói Json thì mọi người đều hiểu). Lưu ý rằng tương ứng với giới tính mà Icon hiển thị của mỗi QListWidgetItem sẽ có Icon khác nhau.
  • (5) Chức năng “Delete“: Chương trình sẽ kiểm tra tất cả các dòng Item nào được checked thì sẽ xác nhận xóa, nếu đồng ý xóa thì chương trình sẽ xóa những dòng Item nào được checked.
  • (6) Chức năng “Close“: Chức năng này dùng để đóng cửa sổ phần mềm, khi nhấn vào nút lệnh này chương trình sẽ hiển thị cửa sổ xác nhận Đóng hay không, nếu đồng ý đóng thì chương trình sẽ đóng cửa sổ.
  • (7) Chức năng “Selection“: Khi người sử dụng chọn dòng Item nào trong QListWidget thì thông tin chi tiết trong dòng đang chọn sẽ được hiển thị ngược lại lên giao diện.

Ta tiến hành làm dự án này.

Trong Pycharm, đặt tên dự án “LearnQListWidgetEmployee“, có cấu hình như dưới đây:

Thư mục images, các bạn có thể lấy bất kỳ hình ảnh nào có kích thước 32×32 để dùng.

Tạo “Employee.py” có mã lệnh như dưới đây:

class Employee:
    def __init__(self,name,email,gender):
        self.name=name
        self.email=email
        self.gender=gender
    def __str__(self):
        if self.gender==True:
            return self.name+" - "+self.email +"(Woman)"
        else:
            return self.name + " - " + self.email + "(Man)"

Mã lệnh ở trên tạo một lớp đối tượng Employee, nó sẽ lưu trữ các dữ liệu trên giao diện mà người dùng nhập liệu, Lớp này có 1 constructor có 3 đối số là name, email, và gender. Ngoài ra Tui còn bổ sung thêm phương thức __str__ để hiển thị thông tin dữ liệu của đối tượng lên giao diện, cách hiển thị như thế nào là do chúng ta quyết định ở hàm này.

Tiếp theo nữa là các bạn dùng công cụ Qt Designer đã được học rất kỹ lưỡng ở những bài học trước và nó đã được tích hợp vào Pycharm để thiết kế giao diện có tên “MainWindow.ui”, giao diện như hình dưới đây:

Các bạn tiến hành kéo thả các Widget ra giao diện bao gồm QLineEdit, QPushButton, QLabel, QListWidget và đặt tên các Widget như hình trên.

Sau đó dùng chức năng Generate Python Code cho “MainWindow.ui” thành “MainWindow.py” có mã lệnh như dưới đây (tự động tạo):

# Form implementation generated from reading ui file 'MainWindow.ui'
#
# Created by: PyQt6 UI code generator 6.5.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(490, 528)
        font = QtGui.QFont()
        font.setPointSize(12)
        MainWindow.setFont(font)
        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, 40, 451, 121))
        self.groupBox.setObjectName("groupBox")
        self.label = QtWidgets.QLabel(parent=self.groupBox)
        self.label.setGeometry(QtCore.QRect(20, 30, 71, 16))
        self.label.setObjectName("label")
        self.lineEditName = QtWidgets.QLineEdit(parent=self.groupBox)
        self.lineEditName.setGeometry(QtCore.QRect(120, 30, 311, 22))
        self.lineEditName.setObjectName("lineEditName")
        self.lineEditEmail = QtWidgets.QLineEdit(parent=self.groupBox)
        self.lineEditEmail.setGeometry(QtCore.QRect(120, 60, 311, 22))
        self.lineEditEmail.setObjectName("lineEditEmail")
        self.label_2 = QtWidgets.QLabel(parent=self.groupBox)
        self.label_2.setGeometry(QtCore.QRect(20, 60, 71, 16))
        self.label_2.setObjectName("label_2")
        self.radWoman = QtWidgets.QRadioButton(parent=self.groupBox)
        self.radWoman.setGeometry(QtCore.QRect(120, 90, 95, 20))
        self.radWoman.setChecked(True)
        self.radWoman.setObjectName("radWoman")
        self.radMan = QtWidgets.QRadioButton(parent=self.groupBox)
        self.radMan.setGeometry(QtCore.QRect(260, 90, 95, 20))
        self.radMan.setObjectName("radMan")
        self.groupBox_2 = QtWidgets.QGroupBox(parent=self.centralwidget)
        self.groupBox_2.setGeometry(QtCore.QRect(20, 230, 451, 241))
        self.groupBox_2.setObjectName("groupBox_2")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.groupBox_2)
        self.verticalLayout.setObjectName("verticalLayout")
        self.listWidgetEmployee = QtWidgets.QListWidget(parent=self.groupBox_2)
        self.listWidgetEmployee.setObjectName("listWidgetEmployee")
        self.verticalLayout.addWidget(self.listWidgetEmployee)
        self.groupBox_3 = QtWidgets.QGroupBox(parent=self.centralwidget)
        self.groupBox_3.setGeometry(QtCore.QRect(20, 160, 451, 71))
        self.groupBox_3.setObjectName("groupBox_3")
        self.pushButtonNew = QtWidgets.QPushButton(parent=self.groupBox_3)
        self.pushButtonNew.setGeometry(QtCore.QRect(10, 20, 81, 41))
        icon1 = QtGui.QIcon()
        icon1.addPixmap(QtGui.QPixmap("images/ic_add.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonNew.setIcon(icon1)
        self.pushButtonNew.setObjectName("pushButtonNew")
        self.pushButtonSave = QtWidgets.QPushButton(parent=self.groupBox_3)
        self.pushButtonSave.setGeometry(QtCore.QRect(110, 20, 91, 41))
        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.setObjectName("pushButtonSave")
        self.pushButtonDelete = QtWidgets.QPushButton(parent=self.groupBox_3)
        self.pushButtonDelete.setGeometry(QtCore.QRect(230, 20, 91, 41))
        icon3 = QtGui.QIcon()
        icon3.addPixmap(QtGui.QPixmap("images/ic_delete.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonDelete.setIcon(icon3)
        self.pushButtonDelete.setObjectName("pushButtonDelete")
        self.pushButtonClose = QtWidgets.QPushButton(parent=self.groupBox_3)
        self.pushButtonClose.setGeometry(QtCore.QRect(340, 20, 91, 41))
        icon4 = QtGui.QIcon()
        icon4.addPixmap(QtGui.QPixmap("images/ic_close.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonClose.setIcon(icon4)
        self.pushButtonClose.setObjectName("pushButtonClose")
        self.label_3 = QtWidgets.QLabel(parent=self.centralwidget)
        self.label_3.setGeometry(QtCore.QRect(50, 0, 411, 31))
        font = QtGui.QFont()
        font.setPointSize(13)
        font.setBold(True)
        font.setWeight(75)
        self.label_3.setFont(font)
        self.label_3.setStyleSheet("color: rgb(0, 0, 127);")
        self.label_3.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
        self.label_3.setObjectName("label_3")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 490, 22))
        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.lineEditName, self.lineEditEmail)
        MainWindow.setTabOrder(self.lineEditEmail, self.radWoman)
        MainWindow.setTabOrder(self.radWoman, self.radMan)
        MainWindow.setTabOrder(self.radMan, self.pushButtonNew)
        MainWindow.setTabOrder(self.pushButtonNew, self.pushButtonSave)
        MainWindow.setTabOrder(self.pushButtonSave, self.pushButtonDelete)
        MainWindow.setTabOrder(self.pushButtonDelete, self.pushButtonClose)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Trần Duy Thanh - Employee Demo"))
        self.groupBox.setTitle(_translate("MainWindow", "Employee Information:"))
        self.label.setText(_translate("MainWindow", "Name:"))
        self.label_2.setText(_translate("MainWindow", "Email:"))
        self.radWoman.setText(_translate("MainWindow", "Woman"))
        self.radMan.setText(_translate("MainWindow", "Man"))
        self.groupBox_2.setTitle(_translate("MainWindow", "List of Employee:"))
        self.groupBox_3.setTitle(_translate("MainWindow", "Action:"))
        self.pushButtonNew.setText(_translate("MainWindow", "New"))
        self.pushButtonSave.setText(_translate("MainWindow", "Save"))
        self.pushButtonDelete.setText(_translate("MainWindow", "Delete"))
        self.pushButtonClose.setText(_translate("MainWindow", "Close"))
        self.label_3.setText(_translate("MainWindow", "Employee Management"))

Sau đó ta cần tạo thêm một lớp “MainWindowEx.py” như đã học ở những bài trước, lớp này kế thừa từ UI_Main trong Generate Python code ở trên, mục đích của nó là viết mã lệnh xử lý các sự kiện và có thể dễ dàng bổ sung, mở rộng các mã lệnh mới mà không sợ bị ảnh hưởng khi giao diện thay đổi.
Bây giờ Tui giải thích từng đoạn mã lệnh tương ứng với từng chức năng, sau đó Tui sẽ cùng cấp mã lệnh đầy đủ của chương trình ở bên dưới:

Chức năng “New” ta bổ sung signal và slot như bên dưới:

self.pushButtonNew.clicked.connect(self.processNew)
def processNew(self):
    self.lineEditName.setText("")
    self.lineEditEmail.setText("")
    self.lineEditName.setFocus()

Mã lệnh “processNew” sẽ xóa trắng các dữ liệu trong các ô nhập liệu, và focus tới ô nhập Tên.

-Chức năng mapping và lưu dữ liệu từ bộ nhớ xuống Ổ cứng với định dạng JSon:

def writeEmployeeToJson(self):
    dataset=[]
    for i in range(0,self.listWidgetEmployee.count()):
        item=self.listWidgetEmployee.item(i)
        emp=item.data(Qt.ItemDataRole.UserRole)
        dataset.append(emp)
    jsonString=json.dumps([emp.__dict__ for emp in dataset])
    jsonFile=open("database.json", "w")
    jsonFile.write(jsonString)
    jsonFile.close()

Mã lệnh “wireEmployeeToJson” sẽ quét toàn bộ Employee trong QListWidget và đưa vào biến mảng dataset. Sau đó dataset sẽ được json.dumps để lưu toàn bộ dữ liệu xuống ổ cứng với định dạng Json. Ví dụ minh họa cấu trúc Json được lưu xuống ổ cứng “database.json”:

[{"name": "Albert Einstein", "email": "Einstein@gmail.com", "gender": false}, {"name": "Isaac Newton", "email": "newton@hotmail.com", "gender": false}, {"name": "Marie Curie", "email": "Curie@gmail.com", "gender": true}, {"name": "Jane Goodall", "email": "Goodall@gmail.com", "gender": true}, {"name": "Stephen Hawking", "email": "hawking@uel.edu.vn", "gender": false}, {"name": "Aristotle", "email": "Aristotle@uel.edu.vn", "gender": false}, {"name": "Thomas Edison", "email": "Edison@uel.edu.vn", "gender": false}, {"name": "Barbara McClintock", "email": "Barbara@uel.edu.vn", "gender": true}]
  • Tiếp theo là chức năng đọc dữ liệu Json từ ổ cứng lên bộ nhớ thông qua cách Mapping ngược lại lên danh sách hướng đối tượng List Employee:
def readEmployeeFromJson(self):
    if os.path.isfile("database.json") ==False:
        return
    file = open('database.json', "r")
    # Reading from file
    self.dataset = json.loads(file.read(), object_hook=lambda d: Employee(**d))
    file.close()
    for emp in self.dataset:
        item = QListWidgetItem()
        item.setData(Qt.ItemDataRole.UserRole, emp)
        item.setText(str(emp))
        item.setCheckState(Qt.CheckState.Unchecked)
        if emp.gender==True:
            item.setIcon(QIcon("images/ic_woman.png"))
        else:
            item.setIcon(QIcon("images/ic_man.png"))
        self.listWidgetEmployee.addItem(item)

Mã lệnh “readEmployeeFromJson” ở trên sẽ kiểm tra xem file “database.json” có tồn tại hay không, nếu có tồn tại thì chương trình sẽ tiến hành đọc và mapping dữ liệu JSON Array qua list object (danh sách hướng đối tượng Employee) và lưu vào biến dataset. Sau đó ta dùng vòng lặp để hiển thị lên giao diện QListWidget. Lưu ý trong bài này các bạn có thể tách riêng lớp đọc và ghi dữ liệu độc lập với hiển thị lên giao diện. Tuy nhiên để các bạn dễ hiểu hơn nên Tui gộp lại cho các bạn.

-Chức năng “Save” dùng để lưu mới hoặc cập nhật Employee:

self.pushButtonSave.clicked.connect(self.processSave)
def processSave(self):
    insertEmployee=Employee(self.lineEditName.text(),self.lineEditEmail.text(),self.radWoman.isChecked())
    isDuplicated=False
    for i in range(0,self.listWidgetEmployee.count()):
        item=self.listWidgetEmployee.item(i)
        data=item.data(Qt.ItemDataRole.UserRole)
        if insertEmployee.email.lower()==data.email.lower():
            isDuplicated=True
            break
    if not isDuplicated:
        item=QListWidgetItem()
    item.setData(Qt.ItemDataRole.UserRole,insertEmployee)
    item.setText(str(insertEmployee))
    item.setCheckState(Qt.CheckState.Unchecked)
    if self.radWoman.isChecked():
        item.setIcon(QIcon("images/ic_woman.png"))
    else:
        item.setIcon(QIcon("images/ic_man.png"))
    if not isDuplicated:
        self.listWidgetEmployee.addItem(item)
    self.writeEmployeeToJson()

Chức năng “processSave” ở trên, Tui xử lý cho 2 trường hợp: Nếu Employee có email không trùng nhau là thêm mới, nếu email trùng nhau là cập nhật lại tên Employee. Nếu dữ liệu được lưu hoặc cập nhật thành công thì chúng ta gọi chức năng “writeEmployeeToJson” để lưu toàn bộ dữ liệu xuống ổ cứng với định dạng Json. Khi lưu thành công rồi thì ta có thể chạy lại phần mềm, hoặc bất cứ khi nào phần mềm được khởi chạy thì ta gọi hàm “readEmployeeFromJson” lúc này toàn bộ danh sách Employee sẽ được hiển thị ngược lại lên QListWidget.

-Chức năng “Delete” dùng để xóa các Employee được checked trên QListWidget:

def processDelete(self):
    answer = QMessageBox.question(
        self.MainWindow,
        'Confirmation',
        'Do you want to remove checked Items?',
        QMessageBox.StandardButton.Yes |
        QMessageBox.StandardButton.No
    )
    if answer == QMessageBox.StandardButton.No:
        return
    for index in range(self.listWidgetEmployee.count()-1,-1,-1):
        item=self.listWidgetEmployee.item(index)
        if item.checkState()==Qt.CheckState.Checked:
            current_item = self.listWidgetEmployee.takeItem(index)
            del current_item
    self.processNew()
    self.writeEmployeeToJson()

Mã lệnh “processDelete” sẽ hiển thị màn hình xác nhận muốn xóa hay không, nếu xác nhận có thì chương trình sẽ tiến hành dùng vòng lặp chạy qua từng phần tử và kiểm tra xem phần tử đó có checkState() là Qt.CheckState.Checked hay không, nếu có thì ta tiến hành gọi lệnh takeItem để xóa Item này ra khỏi QListWidget. Chú ý rằng vì chúng ta xóa nhiều Item nên cần chạy ngược vòng lặp từ cuối dành sách trở về đầu tiên để mỗi khi item bị xóa khỏi danh sách thì các Item trước đó vẫn không bị thay đổi index (nếu bạn chạy vòng lặp xuôi chắc chắn sẽ sai):

Sau khi xóa thành công thì ta gọi lệnh “writeEmployeeToJson” để lưu lại các Employee còn lại trên giao diện, coi như đây là danh sách mới nhất.

-Chức năng “Close” để đóng cửa sổ chương trình:

self.pushButtonClose.clicked.connect(self.processClose)
def processClose(self):
    msg = QMessageBox()
    msg.setText(f"Are you sure you want to exit ?")
    msg.setWindowTitle("Exit Confirmation")
    msg.setIcon(QMessageBox.Icon.Question)
    buttons = QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
    msg.setStandardButtons(buttons)
    result = msg.exec()
    if result == QMessageBox.StandardButton.Yes:
       self.MainWindow.close()

Mã lệnh “processClose” sẽ mở cửa sổ xác nhận thoát phần mềm hay không, nếu xác nhận có thì thoát phần mềm.

-Cuối cùng là chức năng khi người dùng lựa chọn trên QListWidgetItem (nhấn chuột hoặc di chuyển phím mũi tên) thì dữ liệu chi tiết của Item sẽ hiển thị ngược lại lên các ô nhập liệu và QRadioButton:

self.listWidgetEmployee.itemSelectionChanged.connect(self.processItemSelectionChanged)
def processItemSelectionChanged(self):
    current_row=self.listWidgetEmployee.currentRow()
    if current_row<0:
        return
    item=self.listWidgetEmployee.item(current_row)
    emp=item.data(Qt.ItemDataRole.UserRole)
    self.lineEditName.setText(emp.name)
    self.lineEditEmail.setText(emp.email)
    if emp.gender==True:
        self.radWoman.setChecked(True)
    else:
        self.radMan.setChecked(True)

Ví dụ như khi người sử dụng bấm chuột hoặc dùng phím mũi tên lên xuống để chọn Item “Stephen Hawking“, dữ liệu sẽ hiển chi tiết thị lên các ô nhập liệu và QRadioButton:

Dưới đây là mã lệnh đầy đủ của “MainWindowEx.py” với đầy đủ chức năng mô tả ở trên:

import json
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QIcon
from PyQt6.QtWidgets import QListWidgetItem, QMessageBox

from Employee import Employee
from MainWindow import Ui_MainWindow
import os.path

class MainWindowEx(Ui_MainWindow):
    def __init__(self):
        self.dataset=[]
    def setupUi(self, MainWindow):
        super().setupUi(MainWindow)
        self.MainWindow=MainWindow
        self.pushButtonNew.clicked.connect(self.processNew)
        self.pushButtonSave.clicked.connect(self.processSave)
        self.listWidgetEmployee.itemSelectionChanged.connect(self.processItemSelectionChanged)
        self.pushButtonDelete.clicked.connect(self.processDelete)
        self.pushButtonClose.clicked.connect(self.processClose)
        self.readEmployeeFromJson()
    def show(self):
        self.MainWindow.show()
    def processNew(self):
        self.lineEditName.setText("")
        self.lineEditEmail.setText("")
        self.lineEditName.setFocus()
    def processSave(self):
        insertEmployee=Employee(self.lineEditName.text(),self.lineEditEmail.text(),self.radWoman.isChecked())
        isDuplicated=False
        for i in range(0,self.listWidgetEmployee.count()):
            item=self.listWidgetEmployee.item(i)
            data=item.data(Qt.ItemDataRole.UserRole)
            if insertEmployee.email.lower()==data.email.lower():
                isDuplicated=True
                break
        if not isDuplicated:
            item=QListWidgetItem()
        item.setData(Qt.ItemDataRole.UserRole,insertEmployee)
        item.setText(str(insertEmployee))
        item.setCheckState(Qt.CheckState.Unchecked)
        if self.radWoman.isChecked():
            item.setIcon(QIcon("images/ic_woman.png"))
        else:
            item.setIcon(QIcon("images/ic_man.png"))
        if not isDuplicated:
            self.listWidgetEmployee.addItem(item)
        self.writeEmployeeToJson()
    def processItemSelectionChanged(self):
        current_row=self.listWidgetEmployee.currentRow()
        if current_row<0:
            return
        item=self.listWidgetEmployee.item(current_row)
        emp=item.data(Qt.ItemDataRole.UserRole)
        self.lineEditName.setText(emp.name)
        self.lineEditEmail.setText(emp.email)
        if emp.gender==True:
            self.radWoman.setChecked(True)
        else:
            self.radMan.setChecked(True)
    def processDelete(self):
        answer = QMessageBox.question(
            self.MainWindow,
            'Confirmation',
            'Do you want to remove checked Items?',
            QMessageBox.StandardButton.Yes |
            QMessageBox.StandardButton.No
        )
        if answer == QMessageBox.StandardButton.No:
            return
        for index in range(self.listWidgetEmployee.count()-1,-1,-1):
            item=self.listWidgetEmployee.item(index)
            if item.checkState()==Qt.CheckState.Checked:
                current_item = self.listWidgetEmployee.takeItem(index)
                del current_item
        self.processNew()
        self.writeEmployeeToJson()
    def processClose(self):
        msg = QMessageBox()
        msg.setText(f"Are you sure you want to exit ?")
        msg.setWindowTitle("Exit Confirmation")
        msg.setIcon(QMessageBox.Icon.Question)
        buttons = QMessageBox.StandardButton.Yes | QMessageBox.StandardButton.No
        msg.setStandardButtons(buttons)
        result = msg.exec()
        if result == QMessageBox.StandardButton.Yes:
           self.MainWindow.close()
    def writeEmployeeToJson(self):
        dataset=[]
        for i in range(0,self.listWidgetEmployee.count()):
            item=self.listWidgetEmployee.item(i)
            emp=item.data(Qt.ItemDataRole.UserRole)
            dataset.append(emp)
        jsonString=json.dumps([emp.__dict__ for emp in dataset])
        jsonFile=open("database.json", "w")
        jsonFile.write(jsonString)
        jsonFile.close()
    def readEmployeeFromJson(self):
        if os.path.isfile("database.json") ==False:
            return
        file = open('database.json', "r")
        # Reading from file
        self.dataset = json.loads(file.read(), object_hook=lambda d: Employee(**d))
        file.close()
        for emp in self.dataset:
            item = QListWidgetItem()
            item.setData(Qt.ItemDataRole.UserRole, emp)
            item.setText(str(emp))
            item.setCheckState(Qt.CheckState.Unchecked)
            if emp.gender==True:
                item.setIcon(QIcon("images/ic_woman.png"))
            else:
                item.setIcon(QIcon("images/ic_man.png"))
            self.listWidgetEmployee.addItem(item)

Để thực thi chương trình. Ta tạo thêm “MyApp.py” có mã lệnh như dưới đây:

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 lên ta có kết quả như mong muốn.

Mã lệnh chi tiết của chương trình này các bạn tải ở đây:

https://www.mediafire.com/file/n0y4punkxmr00cb/LearnQListWidgetEmployee.rar/file

Như vậy tới đây Tui đã củng cố xong kiến thức và các kỹ thuật xử lý liên quan tới QListWidget. Các bạn đã biết cách ứng dụng QListWidget vào triển khai dự án, sử dụng Widget để hiển thị danh sách dữ liệu, cũng như cách thức xử lý mô hình hóa hướng đối tượng, lưu và phục hồi dữ liệu với định dạng JSon.

Bài học sau Tui trình bày về các Widget liên quan tới Date, DateTime để hỗ trợ xử lý dữ liệu với thời gian, các bạn chú ý theo dõi

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

Leave a Reply