Bài 16: QCombobox – Basic Widgets – PyQt6

Nếu bạn muốn hiển thị dữ liệu dạng danh sách và cho người dùng lựa chọn một phần tử trong danh sách này thì ta dùng QComboBox. Ví dụ như bạn muốn hiển thị danh sách Tỉnh Thành, hiển thị danh sách Danh mục Sản phẩm, hiển thị Danh sách Phòng ban…. thì QComboBox là một trong các lựa chọn khá phù hợp.

Một số thuộc tính, signal, phương thức thường dùng của QComboBox:

Thuộc tính/Phương thức/signalÝ nghĩa/ Chức năng
QCombobBox(self)Constructor để tạo đối tượng QComboBox
addItem(text, userData)Hàm thêm một phần tử vào cuối QComboBox. userData là đối tượng tương ứng lúc thêm phần tử, nó có thể None
insertItem(index, text, userData)Hàm chèn một phần tử vào vị trí index trong QComboBox. userData là đối tượng tương ứng lúc chèn phần tử, nó có thể None
addItems(texts)Hàm thêm nhiều phần tử vào QComboBox
insertItems(index,texts)Hàm chèn nhiều phần tử vào QComboBox bắt đầu từ vị trí index
currentData()Hàm trả về đối tượng tại phần tử đang chọn trên QComboBox
currentIndex()Hàm trả về vị trí hiện tại phần tử đang chọn trên QComboBox
currentText()Hàm trả về text hiện tại phần tử đang chọn trên QComboBox
setEditable(True/False)Hàm thiết lập cho phép nhập liệu trong QComboBox hay không
activatedsignal để xử lý lấy dữ liệu mà người dùng đang chọn phần tử trong QComboBox.

Dưới đây là các bước cơ bản để tạo và sử dụng QCombobox với dữ liệu cơ bản (các ví dụ sau ta sẽ sử dụng dữ liệu dạng đối tượng), tạo dự án “LearnQComboBox” trong Pycharm, sau đó tạo tập tin “QComboBoxBasicDemo.py” viết mã lệnh trong tập tin này:

Bước 1: Gọi thư viện để sử dụng QComboBox

from PyQt6.QtWidgets import QComboBox

Bước 2: Tạo đối tượng QComboBox

self.cboCategory = QComboBox(self)

Bước 3: Thêm các dữ liệu vào QComboBox bằng addItem hoặc insertItem

self.cboCategory.addItem('Laptop')
self.cboCategory.addItem('Phone & Tablet')
self.cboCategory.addItem('Smart Watch')
self.cboCategory.insertItem(1,"Head Phone")

Chúng ta cũng có thể chèn hoặc thêm nhiều dữ liệu cùng một lúc vào QComboBox:

self.cboCategory.insertItems(2,["Mouse","Mouse Pad"])
self.cboCategory.addItems(["Game & Stream","Monitor"])

Bước 4: Xử lý signal khi người dùng nhấn chọn phần tử trên QComboBox

self.cboCategory.activated.connect(self.processSelectedComboBox)

Slot processSelectedComboBox để lấy index và text của phần tử đang chọn trên QComboBox:

def processSelectedComboBox(self):
    index=self.cboCategory.currentIndex()
    text=self.cboCategory.currentText()
    self.result_label.setText(
        f'You selected index= {index}, text={text}')

Dưới đây là mã lệnh đầy đủ minh họa sử dụng và xử lý sự kiện cho QComboBox mức cơ bản:

import sys
from PyQt6.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout, QComboBox

class MainWindow(QWidget):
    def __init__(self):
        super().__init__()

        self.setWindowTitle('Tran Duy Thanh')
        self.setMinimumWidth(300)

        # create a grid layout
        layout = QVBoxLayout()
        self.setLayout(layout)

        cb_label = QLabel('Please select a Category:', self)

        # create a combobox
        self.cboCategory = QComboBox(self)
        self.cboCategory.addItem('Laptop')
        self.cboCategory.addItem('Phone & Tablet')
        self.cboCategory.addItem('Smart Watch')
        self.cboCategory.insertItem(1,"Head Phone")

        self.cboCategory.insertItems(2,["Mouse","Mouse Pad"])
        self.cboCategory.addItems(["Game & Stream","Monitor"])

        self.cboCategory.activated.connect(self.processSelectedComboBox)

        self.result_label = QLabel('', self)

        layout.addWidget(cb_label)
        layout.addWidget(self.cboCategory)
        layout.addWidget(self.result_label)

        self.show()

    def processSelectedComboBox(self):
        index=self.cboCategory.currentIndex()
        text=self.cboCategory.currentText()
        self.result_label.setText(
            f'You selected index= {index}, text={text}')
if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainWindow()
    sys.exit(app.exec())

Chạy phần mềm lên ta có giao diện, chọn một item bất kỳ:

Chọn Item bất kỳ ta có kết quả:

Bây giờ Tui sẽ nâng cấp bài trên bằng cách bổ sung các lớp mô hình lớp hướng đối tượng để lưu trữ, hiển thị và xử lý thao tác người dùng tốt hơn trong trường hợp dữ liệu có nhiều thông tin khác nhau.

Bổ sung vào dự án “LearnQComboBox” có cấu trúc như dưới đây:

  • Thư mục “images” để lưu trữ một số hình ảnh cho các danh mục sản phẩm
  • Category.py là lớp đối tượng Danh Mục có id và name
  • QComboBoxAdvancedDemo.py để tạo giao diện sử dụng QComboBox của dữ liệu là hướng đối tượng Category và các icon tương ứng.

Lớp Category.py có mã lệnh như dưới đây:

class Category:
    def __init__(self,id,name):
        self.id=id
        self.name=name
    def __str__(self):
        return str(self.id)+"-"+self.name

Lớp Category có 2 thuộc tính là id và name. Ta cung cấp 2 hàm, hàm thứ nhất là contructor nhận vào 2 đối số id, name. Hàm thứ 2 là __str__() hàm này tương đương với các hàm toString() của ngôn ngữ Java, C#… nó sẽ tự động được triệu gọi khi ta hiển thị đối tượng lên giao diện. Do đó, muốn dữ liệu hiển thị như thế nào thì ta có thể hiệu trong trong hàm __str__()

Lớp “QComboBoxAdvancedDemo.py” có sự khác biệt so với BasicDemo là ta thêm Icon và các Đối tượng cho QComboBox:

self.cboCategory = QComboBox(self)

laptop_icon=QIcon("images/laptop.png")
laptop_model=Category(100,"Laptop")
self.cboCategory.addItem(laptop_icon,laptop_model.name,laptop_model)

phone_icon = QIcon("images/phone.png")
phone_model = Category(200, "Phone")
self.cboCategory.addItem(phone_icon, phone_model.name, phone_model)

smart_icon = QIcon("images/smartwatch.png")
smart_model = Category(300, "Smart Watch")
self.cboCategory.addItem(smart_icon, smart_model.name, smart_model)

Mã lệnh ở trên khi đưa dữ liệu lên QComboBox sẽ gồm 3 dữ liệu: Icon, text hiển thị, và đối tượng tương ứng. Đối tượng tương ứng này sẽ rất hiểu quả trong quá trình xử lý dữ liệu được lựa chọn trên giao diện.

Để xử lý sự kiện, ta cũng gán signal như bình thường, ở đây ta có thể lấy currentData() để truy suất tới đối tượng lúc đưa vào QComboBox, lấy currentText(), lấy currentIndex(). Trong bài này Tui minh họa cách lấy currentData() còn các hàm khác làm tương tự như ví dụ trước đó:

self.cboCategory.activated.connect(self.processSelectedComboBox)
def processSelectedComboBox(self):
    data=self.cboCategory.currentData()
    self.result_label.setText(
        f'You selected index= {data}')

Khi data được hiển thị lên màn hình thì hàm __str__() sẽ tự động được triệu gọi.

Dưới đây là mã lệnh đầy đủ của “QComboBoxAdvancedDemo.py”:

import sys

from PyQt6.QtGui import QIcon
from PyQt6.QtWidgets import QApplication, QWidget, QLabel, QVBoxLayout, QComboBox

from Category import Category


class MainWindow(QWidget):
    def __init__(self):
        super().__init__()

        self.setWindowTitle('Tran Duy Thanh')
        self.setMinimumWidth(300)

        # create a grid layout
        layout = QVBoxLayout()
        self.setLayout(layout)

        cb_label = QLabel('Please select a Category:', self)

        # create a combobox
        self.cboCategory = QComboBox(self)

        laptop_icon=QIcon("images/laptop.png")
        laptop_model=Category(100,"Laptop")
        self.cboCategory.addItem(laptop_icon,laptop_model.name,laptop_model)

        phone_icon = QIcon("images/phone.png")
        phone_model = Category(200, "Phone")
        self.cboCategory.addItem(phone_icon, phone_model.name, phone_model)

        smart_icon = QIcon("images/smartwatch.png")
        smart_model = Category(300, "Smart Watch")
        self.cboCategory.addItem(smart_icon, smart_model.name, smart_model)


        self.cboCategory.activated.connect(self.processSelectedComboBox)

        self.result_label = QLabel('', self)

        layout.addWidget(cb_label)
        layout.addWidget(self.cboCategory)
        layout.addWidget(self.result_label)

        self.show()

def processSelectedComboBox(self):
    data=self.cboCategory.currentData()
    self.result_label.setText(
        f'You selected index= {data}')
if __name__ == '__main__':
    app = QApplication(sys.argv)
    window = MainWindow()
    sys.exit(app.exec())

Chạy phần mềm lên ta có kết quả:

Mã lệnh đầy đủ của 2 ví dụ về QComboBox này các bạn tải ở đây:

https://www.mediafire.com/file/8hjvyv8lncuyhae/LearnQComboBox.rar/file

Bây giờ Tui hướng dẫn các bạn ứng dụng QComboBox vào việc xây dựng phần mềm như dưới đây:

Tạo một dự án tên “LearnQComboBoxAdvanced” trong Pycharm:

Trong dự án ở trên, tạo thư mục “images”, bổ sung thêm các biểu tượng liên quan tới các thành phố.

Sau đó dùng Qt Designer để thiết kế giao diện như dưới đây (MainWindow.ui):

Các widget kéo ra như trên, định dạng và đặt tên cho các Widget này như hình:

ComboBox City ta kéo vào, và nhập dữ liệu cho widget này như sau (double click vào widget sau khi kéo vào giao diện):

  • Nhấn biểu tượng dấu “+” để nhập thêm item cho QComboBox
  • Nhấn biểu tượng dấu “-” để xóa item đang chọn ra khỏi QComboBox
  • Các biểu tượng mũi tên lên, xuống để thay đổi vị trí của các item trong QComboBox
  • Nút lệnh “Properties” để hiển thị chi tiết các thuộc tính của từng item trong QComboBox. Bạn muốn thêm Icon cho item thì bấm chọn Choose File… trong nhóm icon như hình trên.

Sau khi chọn đầy đủ các hình cho Item ta có kết quả như hình bên dưới:

Sau khi thiết kế xong giao diện, ta lưu lại với tên “MainWindow.ui” rồi qua lại Pycharm để Generate code Python thành “MainWindow.py”. Sau đó tiến hành bổ sung thêm 1 class tên là “Employee” có các thông số sau:

class Employee:
    def __init__(self,fullName,gender,city):
        self.fullName=fullName
        self.gender=gender
        self.city=city

Tiếp theo tạo “MainWindowEx.py” kế thừa từ lớp được Generate trong “MainWindow.py”:

from MainWindow import Ui_MainWindow

class MainWindowEx(Ui_MainWindow):
    def __init__(self):
        super().__init__()
    def setupUi(self, MainWindow):
        super().setupUi(MainWindow)
        self.MainWindow=MainWindow
    def show(self):
        self.MainWindow.show()

Các mã lệnh chi tiết xử lý sự kiện ta sẽ bổ sung sau cho MainWindowEx.

Tiếp theo 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()

Ta xem cấu trúc dữ liệu cuối cùng trong Pycharm:

Chạy “MyApp.py” ta có kết quả:

Như vậy ta có được giao diện như mong muốn.

Bây giờ ta bổ sung thêm hàm __str__ để trả về chuỗi json:

import json

class Employee:
    def __init__(self,fullName,gender,city):
        self.fullName=fullName
        self.gender=gender
        self.city=city
    def __str__(self):
        return json.dumps(self.__dict__)

Hàm __str__ sẽ được sử dụng khi ta hiển thị đối tượng lên giao diện

Sau đó ta tiếp tục bổ sung mã lệnh cho MainWindow.py để xử lý sự kiện:

from Employee import Employee
from MainWindow import Ui_MainWindow

class MainWindowEx(Ui_MainWindow):
    def __init__(self):
        super().__init__()
    def setupUi(self, MainWindow):
        super().setupUi(MainWindow)
        self.MainWindow=MainWindow
        self.pushButtonClose.clicked.connect(self.processClose)
        self.pushButtonConfirm.clicked.connect(self.processConfirmation)
    def processConfirmation(self):
        name=self.lineEditfullName.text()
        gender="Man"
        if self.chkGender.isChecked():
            gender="Woman"
        city=self.cboCity.currentText()
        emp=Employee(name,gender,city)
        self.plainTextEditOutput.setPlainText(str(emp))
    def processClose(self):
        self.MainWindow.close()
    def show(self):
        self.MainWindow.show()

Mã lệnh slot “processConfirmation” ở trên sẽ lắng nghe và xử lý lấy các dữ liệu từ giao diện mà người dùng cung cấp.

Dòng lệnh 18 sẽ khởi tạo 1 đối tượng Employee

Dòng lệnh 19 dùng str(emp) để chương trình tự động triệu gọi __str__ để lấy chuỗi JSON của đối tượng và sau đó hiển thị lên giao diện.

Khi nhấn nút “Confirm” sẽ hiển thị dữ liệu JSON vào màn hình OutPut, nhấn “Close” sẽ đóng cửa sổ:

Như vậy tới đây Tui đã trình bày xong các kiến thức liên quan tới QComboBox cũng như các kỹ thuật lập trình, ứng dụng QComboBox và các dự án mẫu. Thông qua 3 ví dụ ở trên các bạn đã có đủ kiến thức và kỹ năng để sử dụng và phát triển mở rộng QComboBox ở các trường hợp khác nhau. Các bạn cố gắng thực hành lại nhiều lần để hiểu rõ hơn về Widget này.

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

https://www.mediafire.com/file/evpr0v8xdx82el2/LearnQComboBoxAdvanced.rar/file

Bài học sau, Tui sẽ trình bày về wiget QListWidget, nó cũng là một dạng widget dùng để hiển thị dữ liệu dạng danh sách nhưng nó có một chút ít khác biết so với QComboBox.

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

Bài 15: QRadioButton – Basic Widgets – PyQt6

Trong bài trước chúng ta học về QCheckBox – widget dùng để cung cấp chức năng cho người sử dùng chọn nhiều lựa chọn trên giao diện. Trong bài học này Tui sẽ trình bày về QRadioButton, nó khác biệt với QCheckBox, đó là nó dùng để thiết kế giao diện cho phép người dùng tại một thời điểm chỉ chọn một lựa chọn.

QRadioButton cung cấp 2 trạng thái:

  • on(checked)
  • off(unchecked)

Và các QRadioButton cần được nhóm lại thông qua QButtonGroup nếu trên giao diện bạn muốn thiết kế nhiều nhóm, hoặc chúng ta kéo các Widget vào bên trong các QGroupBox khác nhau. Ví dụ như bạn muốn thiết kế 2 nhóm chọn lựa: Giao diện vừa có nhóm Chọn giới tính Nam hay Nữ, vừa có nhóm chọn trình độ học vấn như Đại học hay cao đẳng….

Tương tự như QCheckBox, thì QRadioButton cũng có một số thuộc tính, phương thức, signal thường dùng sau:

Thuộc tính/Phương thức/signalÝ nghĩa/ Chức năng
objectNameTên QRadioButton dùng để truy suất
setChecked(True/False)Hàm thiết lập Checked hoặc Unchecked cho QRadioButton
isChecked()Hàm trả về trạng thái của QRadioButton có được checked hay không
text()Hàm trả về chuỗi hiển thị trên QRadioButton
toggledSignal để kiểm tra trạng thái của QRadioButton có được checked hay không
clickedSignal để lấy trạng thái true khi người dùng click chọn QRadioButton

Để sử dụng QRadioButton ta có thể thiết kế bằng Qt Designer hoặc làm tuần tự các bước sau, dưới này Tui minh họa bài thay đổi màu nền của Cửa sổ khi người sử dụng nhấn vào các QRadioButton tương ứng:

Bước 1: Import QRadioButton từ module PyQt6.QtWidgets và các thư viện liên quan khác:

from PyQt6.QtWidgets import QApplication
from PyQt6.QtWidgets import QWidget
from PyQt6.QtWidgets import QRadioButton
from PyQt6.QtWidgets import QButtonGroup

Bước 2: Khai báo các đối tượng Widget, QRadioButton, ButtonGroup để tạo các nhóm:

from PyQt6.QtWidgets import QApplication
from PyQt6.QtWidgets import QWidget
from PyQt6.QtWidgets import QRadioButton
from PyQt6.QtWidgets import QButtonGroup

app = QApplication([])

w = QWidget()
w.setWindowTitle("Trần Duy Thanh - QRadioButton")
w.resize(300, 150)

radRed = QRadioButton("Red",w)
radRed.move(20, 20)

radGreen = QRadioButton("Green",w)
radGreen.move(20, 40)

radBlue = QRadioButton("Blue",w)
radBlue.move(20, 60)

color_group = QButtonGroup(w)
color_group.addButton(radRed)
color_group.addButton(radGreen)
color_group.addButton(radBlue)

w.show()

app.exec()

Chạy mã lệnh ở trên ta được giao diện như mong muốn.

Bước 3: Gán Signal cho 3 QRadioButton để đổi màu nền cửa sổ:

def changeBackgroundToRed(value):
    if value==True:
        w.setStyleSheet("background-color:red;");
def changeBackgroundToGreen(value):
    if value == True:
        w.setStyleSheet("background-color:green;")
def changeBackgroundToBlue(value):
    if value == True:
        w.setStyleSheet("background-color:blue;")
radRed.clicked.connect(changeBackgroundToRed)
radGreen.clicked.connect(changeBackgroundToGreen)
radBlue.clicked.connect(changeBackgroundToBlue)

Dưới đây là mã lệnh đầy đủ cho bài đổi màu nền:

from PyQt6.QtWidgets import QApplication
from PyQt6.QtWidgets import QWidget
from PyQt6.QtWidgets import QRadioButton
from PyQt6.QtWidgets import QButtonGroup

app = QApplication([])

w = QWidget()
w.setWindowTitle("Trần Duy Thanh - QRadioButton")
w.resize(300, 150)

radRed = QRadioButton("Red",w)
radRed.move(20, 20)

radGreen = QRadioButton("Green",w)
radGreen.move(20, 40)

radBlue = QRadioButton("Blue",w)
radBlue.move(20, 60)

color_group = QButtonGroup(w)
color_group.addButton(radRed)
color_group.addButton(radGreen)
color_group.addButton(radBlue)

def changeBackgroundToRed(value):
    if value==True:
        w.setStyleSheet("background-color:red;");
def changeBackgroundToGreen(value):
    if value == True:
        w.setStyleSheet("background-color:green;")
def changeBackgroundToBlue(value):
    if value == True:
        w.setStyleSheet("background-color:blue;")
radRed.clicked.connect(changeBackgroundToRed)
radGreen.clicked.connect(changeBackgroundToGreen)
radBlue.clicked.connect(changeBackgroundToBlue)

w.show()

app.exec()

Chạy phần mềm lên và lựa chọn các Màu ta có:

Tiếp theo ta ứng dụng QRadioButton để thiết kế màn hình nhập Personal Information với nhiều thông tin hơn, và đặc biệt ta dùng Qt Designer, GroupBox để group các QRadioButton trên các nhóm khác nhau:

Trong Pycharm ta tạo một dự án tên “LearnQRadioButton“:


Ta dùng Qt Designer để thiết kế giao diện, đặt tên “MainWindow.ui”:

Trước tiên ta kéo các “Group Box” ra giao diện như hình trên, đặt tiêu tiêu, căn lề tiêu đề cũng như chỉnh styleSheet đổi màu nền cho các Group Box.

Ở trên các bạn thấy Tui có tạo 2 Group Box có nền màu vàng (Personal Information) và Group có nền xanh (Other Information). Sau đó trong mỗi Group Box được kéo vào một số Widget như QLabel, QLineEdit, QRadioButton.

Các Group Box, bạn có thể định dạng như hình dưới đây:

  • Thuộc tính title: Để nhập tiêu đề cho Group Box
  • Thuộc tính alignment: Dùng để căn lề cho tiêu đề Group Box
  • Thuộc tính styleSheet: Dùng để định dạng CSS cho GroupBox ví dụ như đường viên, màu viền, màu nền….

Các QRadioButton nào được kéo vào GroupBox nào thì nó sẽ thuộc nhóm đó, tức là khi ta nhấn chọn Radio Button nào thì trong nhóm đó sẽ ảnh hưởng thôi, không liên quan tới GroupBox khác.

Các QRadioButton cũng có nhiều thuộc tính, trong đó các thuộc tính thường dùng như text, icon, checked:

QRadioButton nào muốn mặc định checked thì bạn tick vào thuộc tính checked như hình.

Cuối cùng bạn đặt tên các Widget như hình dưới đây để hỗ trợ cho việc viết mã lệnh tương tác được tốt nhất:

Sau khi đặt xong tên các Widget, các bạn lưu giao diện lại với file “MainWindow.ui” vào đúng dự án trong Pycharm “LearnQRadioButton

Sau đó dùng chức năng Generate Python code đã học để tạo mã lệnh Python cho “MainWindow.ui”, file mã lệnh tự động tạo ra sẽ là “MainWindow.py“:

Mã lệnh của file này như sau:

# 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(449, 378)
        font = QtGui.QFont()
        font.setPointSize(12)
        MainWindow.setFont(font)
        self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.groupBox = QtWidgets.QGroupBox(parent=self.centralwidget)
        self.groupBox.setGeometry(QtCore.QRect(20, 10, 411, 121))
        self.groupBox.setStyleSheet("border-color: rgb(0, 0, 255);\n"
"background-color: rgb(251, 255, 171);\n"
"")
        self.groupBox.setObjectName("groupBox")
        self.radWoman = QtWidgets.QRadioButton(parent=self.groupBox)
        self.radWoman.setGeometry(QtCore.QRect(110, 90, 91, 18))
        self.radWoman.setChecked(True)
        self.radWoman.setObjectName("radWoman")
        self.radMan = QtWidgets.QRadioButton(parent=self.groupBox)
        self.radMan.setGeometry(QtCore.QRect(220, 90, 83, 18))
        self.radMan.setObjectName("radMan")
        self.lineEditFullName = QtWidgets.QLineEdit(parent=self.groupBox)
        self.lineEditFullName.setGeometry(QtCore.QRect(110, 30, 281, 20))
        self.lineEditFullName.setObjectName("lineEditFullName")
        self.label = QtWidgets.QLabel(parent=self.groupBox)
        self.label.setGeometry(QtCore.QRect(20, 30, 81, 21))
        self.label.setObjectName("label")
        self.label_2 = QtWidgets.QLabel(parent=self.groupBox)
        self.label_2.setGeometry(QtCore.QRect(20, 90, 71, 21))
        self.label_2.setObjectName("label_2")
        self.lineEditEmail = QtWidgets.QLineEdit(parent=self.groupBox)
        self.lineEditEmail.setGeometry(QtCore.QRect(110, 60, 281, 20))
        self.lineEditEmail.setObjectName("lineEditEmail")
        self.label_3 = QtWidgets.QLabel(parent=self.groupBox)
        self.label_3.setGeometry(QtCore.QRect(20, 60, 81, 21))
        self.label_3.setObjectName("label_3")
        self.groupBox_2 = QtWidgets.QGroupBox(parent=self.centralwidget)
        self.groupBox_2.setGeometry(QtCore.QRect(20, 150, 411, 131))
        self.groupBox_2.setStyleSheet("background-color: rgb(232, 255, 225);")
        self.groupBox_2.setAlignment(QtCore.Qt.AlignmentFlag.AlignRight|QtCore.Qt.AlignmentFlag.AlignTrailing|QtCore.Qt.AlignmentFlag.AlignVCenter)
        self.groupBox_2.setObjectName("groupBox_2")
        self.radBachelor = QtWidgets.QRadioButton(parent=self.groupBox_2)
        self.radBachelor.setGeometry(QtCore.QRect(110, 60, 91, 18))
        self.radBachelor.setObjectName("radBachelor")
        self.radMaster = QtWidgets.QRadioButton(parent=self.groupBox_2)
        self.radMaster.setGeometry(QtCore.QRect(110, 80, 83, 18))
        self.radMaster.setObjectName("radMaster")
        self.lineEditAddress = QtWidgets.QLineEdit(parent=self.groupBox_2)
        self.lineEditAddress.setGeometry(QtCore.QRect(110, 30, 281, 20))
        self.lineEditAddress.setObjectName("lineEditAddress")
        self.label_4 = QtWidgets.QLabel(parent=self.groupBox_2)
        self.label_4.setGeometry(QtCore.QRect(20, 30, 81, 21))
        self.label_4.setObjectName("label_4")
        self.label_5 = QtWidgets.QLabel(parent=self.groupBox_2)
        self.label_5.setGeometry(QtCore.QRect(20, 60, 71, 21))
        self.label_5.setObjectName("label_5")
        self.radDoctoral = QtWidgets.QRadioButton(parent=self.groupBox_2)
        self.radDoctoral.setGeometry(QtCore.QRect(110, 100, 83, 18))
        self.radDoctoral.setChecked(True)
        self.radDoctoral.setObjectName("radDoctoral")
        self.pushButtonSendData = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButtonSendData.setGeometry(QtCore.QRect(150, 290, 121, 31))
        self.pushButtonSendData.setObjectName("pushButtonSendData")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 449, 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)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Trần Duy Thanh"))
        self.groupBox.setTitle(_translate("MainWindow", "Personal Information:"))
        self.radWoman.setText(_translate("MainWindow", "Woman"))
        self.radMan.setText(_translate("MainWindow", "Man"))
        self.lineEditFullName.setText(_translate("MainWindow", "Trần Phạm Thanh Trà"))
        self.label.setText(_translate("MainWindow", "Full Name:"))
        self.label_2.setText(_translate("MainWindow", "Gender:"))
        self.lineEditEmail.setText(_translate("MainWindow", "trachanh@gmail.com"))
        self.label_3.setText(_translate("MainWindow", "Email:"))
        self.groupBox_2.setTitle(_translate("MainWindow", "Other Information:"))
        self.radBachelor.setText(_translate("MainWindow", "Bachelor"))
        self.radMaster.setText(_translate("MainWindow", "Master"))
        self.lineEditAddress.setText(_translate("MainWindow", "The Earth"))
        self.label_4.setText(_translate("MainWindow", "Address:"))
        self.label_5.setText(_translate("MainWindow", "Education:"))
        self.radDoctoral.setText(_translate("MainWindow", "Doctoral"))
        self.pushButtonSendData.setText(_translate("MainWindow", "Send Data"))

Tiếp tục tạo một file “MainWindowEX.py” (lớp) kế thừa từ lớp được Generate Python Code trong “MyWindow.py”, mã lệnh tạm thời của “MainWindowEX.py” bạn gõ như dưới đây:

from MainWindow import Ui_MainWindow

class MainWindowEX(Ui_MainWindow):
    def __init__(self):
        pass
    def setupUi(self, MainWindow):
        super().setupUi(MainWindow)
        self.MainWindow=MainWindow
    def show(self):
        self.MainWindow.show()

Các mã lệnh liên quan tới signal cho các Widget ta sẽ xử lý sau.

Tiếp theo, ta tạo thêm “MyApp.py” có mã lệnh dưới đây để khởi tạo chạy 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 “MyApp.py” lên ta có kết quả:

Khi bạn chạy được giao diện như hình trên là đã thành công.

Bây giờ ta tiếp tục bổ sung một số mã lệnh cho “MainWindowEx.py” để xử lý các Signal:

  • Khi nhấn nút “Send Data” chương trình sẽ lấy các dữ liệu trên giao diện mà người dùng lựa chọn rồi dùng QMessageBox hiển thị các dữ liệu này lên.
import json

from PyQt6.QtWidgets import QMessageBox

from MainWindow import Ui_MainWindow

class MainWindowEX(Ui_MainWindow):
    def __init__(self):
        pass
    def setupUi(self, MainWindow):
        super().setupUi(MainWindow)
        self.MainWindow=MainWindow
        self.pushButtonSendData.clicked.connect(self.getInformation)
    def getInformation(self):
        fullName=self.lineEditFullName.text()
        email=self.lineEditEmail.text()
        gender="Woman"
        if not self.radWoman.isChecked():
            gender=self.radMan.text()
        address=self.lineEditAddress.text()
        degree="Bachelor"
        if self.radMaster.isChecked():
            degree=self.radMaster.text()
        elif self.radDoctoral.isChecked():
            degree=self.radDoctoral.text()
        information={"FullName":fullName,
                     "Email":email,
                     "Gender":gender,
                     "Address":address,
                     "Degree":degree
                     }
        self.msgBox=QMessageBox()
        self.msgBox.setWindowTitle("Information")
        self.msgBox.setText(json.dumps(information, ensure_ascii=False))
        self.msgBox.show()
    def show(self):
        self.MainWindow.show()

Mã lệnh ở trên, Tui chủ ý đưa dữ liệu về dạng JSON để các bạn làm quen ở các bài học trong tương lai, vì JSON là một trong các cấu trúc lưu trữ dữ liệu phổ biến nhất hiện nay.

Sau khi bổ sung xong mã lệnh cho “MainWindowEx.py”, bây giờ các bạn chạy lại “MyApp.py”, khi bấm “Send Data” ta sẽ có kết quả:

Như vậy tới đây Tui đã trình bày xong về lý thuyết cũng như cách kỹ thuật lập trình với QRadioButton. Các bạn đã biết được ý nghĩa của các thuộc tính, cách gán signal, và đã sử dụng được 2 cách xử lý sự kiện trên QRadioButton:

  • Xử lý sự kiện QRadioButton ngay lúc người dùng thay đổi lựa chọn trên các widget này
  • Xử lý sự kiện QRadioButton sau khi người dùng nhấn vào nút Send Data

Việc biết được các kiến thức và kỹ thuật xử lý này sẽ giúp ích cho các bạn trong cách vận dụng QRadioButton vào từng trường hợp cụ thể. Các bạn chú ý đọc kỹ và thực hành lại nhiều lần để hiểu rõ hơn về Widget này.

Mã lệnh của bài học QRadioButton các bạn có thể tải ở đây:

https://www.mediafire.com/file/fntsllw6njwapak/LearnQRadioButton.rar/file

Bài học sau Tui sẽ trình bày về cách sử dụng QComboBox QListWidget để hiển thị dữ liệu dạng danh sách, đây là một các Widget rất quan trọng để hỗ trợ việc hiển thị danh sách dữ liệu, cho phép người dùng chọn 1 phần tử hay chọn nhiều phần từ… Các bạn chú ý theo dõi.

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

Bài 14: QCheckBox – Basic Widgets – PyQt6

QCheckBox là một trong các Widget thuộc nhóm cơ bản và nó được dùng thường xuyên trong hầu hết các phần mềm, QCheckBox cung cấp một số trạng thái cho người sử dụng lựa chọn như:

  • Checked
  • Unchecked
  • hoặc Partially checked

Ta thường dùng QCheckBox trong trường hợp cung cấp giao diện cho Khách hàng chọn nhiều lựa chọn. Ví dụ như cung cấp giao diện hỏi về Sở thích (có nhiều sở thích), Cung cấp giao diện cho người dùng chọn nhiều cấu hình, hay chức năng lưu mật khẩu đăng nhập, nói chung là cho nhiều lựa chọn…. Ví dụ:

Một số thuộc tính và phương thức chính thường dùng của QCheckBox:

Thuộc tính/Phương thức/signalÝ nghĩa/ Chức năng
objectNameTên QCheckBox dùng để truy suất
Qt.CheckStateThuộc tính kiểm tra trạng thái của QCheckBox là có checked, unchecked hay partially checked
setChecked(True/False)Hàm thiết lập Checked hoặc Unchecked cho QCheckBox
isChecked()Hàm trả về trạng thái của QCheckBox có được checked hay không
text()Hàm trả về chuỗi hiển thị trên QCheckBox
setTristate(True/False)Thiết lập trạng thái thứ 3 của QCheckBox (Partially checked)
stateChangedSignal để gửi tín hiệu xác nhận người dùng checked hay unchecked QCheckBox

Để sử dụng QCheckBox ta có thể thiết kế bằng Qt Designer hoặc làm tuần tự các bước sau:

Bước 1: Import QCheckBox từ module PyQt6.QtWidgets

from PyQt6.QtWidgets import QCheckBox

Bước 2: Khai báo biến và tạo đối tượng QCheckBox

checkbox = QCheckBox(text)

Bước 3: Lắng nghe thay đổi trạng thái của QCheckBox bằng stateChanged signal

Với checkbox ta cần biết khi nào người sử dụng checked hay unchecked.

checkbox.stateChanged.connect(self.on_checkbox_changed)

stateChanged signal sẽ gửi tín hiệu tới slot on_checkbox_changed (dĩ nhiên ta thích đặt tên slot nào cũng được) để ta biết được trạng thái của checkbox thông qua đối tượng Qt.CheckState:

def on_checkbox_changed(self, value):
    state = Qt.CheckState(value)
    if state == Qt.CheckState.Checked:
        print('You Checked')
    elif state == Qt.CheckState.Unchecked:
        print('You Unchecked')

Qt.CheckState có 3 trạng thái chính như sau:

StateÝ nghĩa
Qt.CheckState.CheckedCheckbox được checked
Qt.CheckState.UncheckedCheckbox được unchecked
Qt.CheckState.PartiallyCheckedHiểu nôm na là Không xác định dạng thái checked hay là unchecked.

Trong bài học này, Tui sẽ minh họa 2 ví dụ về cách sử dụng QCheckBox. Trường hợp thứ nhất là trên giao diện co các QCheckBox, người sử dụng checked/unchecked tùy thích sau đó có QPushButton nhấn để xử lý xem các QCheckBox nào đang được checked. Trường hợp thứ 2 là trên giao diện người dùng lúc checked/unchecked thì ngay lập tức ta xử lý trạng thái của QCheckBox mà không cần thêm một thao tác nhấn QPushButton. Cả 2 trường hợp này ta đều gặp thường xuyên trong quá trình xử lý các trường hợp sử dụng của khách hàng.

Bây giờ ta vào trường hợp 1. Ta sẽ thiết kế và xử lý sự kiện cho bài sau:

Khi người dùng nhập Full Name, Email. Và checked chọn các Khóa học rồi nhấn Submit thì ra cửa sổ thông báo sau:

Ta tạo dự án “LearnCheckbox”, cấu trúc:

Ta dùng Qt Designer để thiết kế giao diện đặt tên file là “MainWindow.ui”:

Thiết kế giao diện và đặt tên các Widget như hình sau đó lưu lại tên “MainWindow.ui” vào dự án Pycharm, sau đó dùng tool để Generate python code thành “MainWindow.py”:

# 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(441, 334)
        self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.label = QtWidgets.QLabel(parent=self.centralwidget)
        self.label.setGeometry(QtCore.QRect(120, 10, 301, 41))
        font = QtGui.QFont()
        font.setPointSize(15)
        font.setBold(True)
        font.setItalic(True)
        font.setWeight(75)
        self.label.setFont(font)
        self.label.setStyleSheet("color: rgb(0, 0, 255);")
        self.label.setObjectName("label")
        self.label_2 = QtWidgets.QLabel(parent=self.centralwidget)
        self.label_2.setGeometry(QtCore.QRect(20, 60, 101, 21))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.label_2.setFont(font)
        self.label_2.setObjectName("label_2")
        self.lineEditFullName = QtWidgets.QLineEdit(parent=self.centralwidget)
        self.lineEditFullName.setGeometry(QtCore.QRect(130, 60, 251, 20))
        self.lineEditFullName.setObjectName("lineEditFullName")
        self.lineEditEmail = QtWidgets.QLineEdit(parent=self.centralwidget)
        self.lineEditEmail.setGeometry(QtCore.QRect(130, 90, 251, 20))
        self.lineEditEmail.setObjectName("lineEditEmail")
        self.label_3 = QtWidgets.QLabel(parent=self.centralwidget)
        self.label_3.setGeometry(QtCore.QRect(20, 90, 81, 21))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.label_3.setFont(font)
        self.label_3.setObjectName("label_3")
        self.label_4 = QtWidgets.QLabel(parent=self.centralwidget)
        self.label_4.setGeometry(QtCore.QRect(130, 120, 251, 21))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.label_4.setFont(font)
        self.label_4.setStyleSheet("background-color: rgb(255, 255, 0);")
        self.label_4.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
        self.label_4.setObjectName("label_4")
        self.chkMachineLearning = QtWidgets.QCheckBox(parent=self.centralwidget)
        self.chkMachineLearning.setGeometry(QtCore.QRect(130, 150, 241, 18))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.chkMachineLearning.setFont(font)
        self.chkMachineLearning.setObjectName("chkMachineLearning")
        self.chkDeepLearning = QtWidgets.QCheckBox(parent=self.centralwidget)
        self.chkDeepLearning.setGeometry(QtCore.QRect(130, 180, 241, 18))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.chkDeepLearning.setFont(font)
        self.chkDeepLearning.setObjectName("chkDeepLearning")
        self.chkSmartContract = QtWidgets.QCheckBox(parent=self.centralwidget)
        self.chkSmartContract.setGeometry(QtCore.QRect(130, 210, 241, 18))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.chkSmartContract.setFont(font)
        self.chkSmartContract.setObjectName("chkSmartContract")
        self.pushButtonSubmit = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButtonSubmit.setGeometry(QtCore.QRect(170, 240, 121, 41))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.pushButtonSubmit.setFont(font)
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap("images/ic_send.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonSubmit.setIcon(icon)
        self.pushButtonSubmit.setIconSize(QtCore.QSize(32, 32))
        self.pushButtonSubmit.setObjectName("pushButtonSubmit")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 441, 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"))
        self.label.setText(_translate("MainWindow", "Courses Registration"))
        self.label_2.setText(_translate("MainWindow", "Full Name:"))
        self.label_3.setText(_translate("MainWindow", "Email:"))
        self.label_4.setText(_translate("MainWindow", "Courses Selection:"))
        self.chkMachineLearning.setText(_translate("MainWindow", "Machine Learning"))
        self.chkDeepLearning.setText(_translate("MainWindow", "Deep Learning"))
        self.chkSmartContract.setText(_translate("MainWindow", "Smart Contract"))
        self.pushButtonSubmit.setText(_translate("MainWindow", "Submit"))

Tiếp tục tạo “MainWindowEx.py” kế thừa từ lớp Generate Python code để xử lý các sự kiện:

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

from MainWindow import Ui_MainWindow


class MainWindowEx(Ui_MainWindow):
    def __init__(self):
        pass
    def setupUi(self, MainWindow):
        super().setupUi(MainWindow)
        self.MainWindow=MainWindow
        self.pushButtonSubmit.clicked.connect(self.processSubmit)
    def show(self):
        self.MainWindow.show()
    def processSubmit(self):
        str=[]
        if self.chkMachineLearning.isChecked()==True:
            str.append(self.chkMachineLearning.text())
        if self.chkDeepLearning.isChecked()==True:
            str.append(self.chkDeepLearning.text())
        if self.chkSmartContract.isChecked()==True:
            str.append(self.chkSmartContract.text())
        separator=','
        infor="Full Name = "+self.lineEditFullName.text()+"\n"
        infor+="Email = "+self.lineEditEmail.text()+"\n"
        infor+="Selected courses:"+"\n"
        infor+=separator.join(str)
        self.msg=QMessageBox()
        self.msg.setWindowTitle("Selected Courses")
        self.msg.setText(infor)

        self.msg.show()

Hàm processsSubmit ở trên xử lý cho nút lệnh “Submit”, chương trình sẽ kiểm tra các QCheckBox nào được checked, sau đó tổng hợp các dữ liệu như Full Name, Email và các lựa chọn rồi hiển thị lên QMessageBox.

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

Mã lệnh của trường hợp này các bạn tải ở đây:

https://www.mediafire.com/file/l0z5tf7oatmg0no/LearnCheckBox.rar/file

Trong ví dụ thứ 2 dưới đây, Tui sẽ minh họa khi người dùng Checked trên QCheckBox thì ngay lập tức xử lý theo các trạng thái của Checkbox, tạo dự án trong Pycharm “LearnCheckBoxDirectly”:

Thiết kế giao diện như hình dưới đặt với tên “MainWindow.ui”, đặt tên widget như hình:

Dùng công cụ để Generate Python code MainWindow.py:

# 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(442, 521)
        self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.chkShowHide = QtWidgets.QCheckBox(parent=self.centralwidget)
        self.chkShowHide.setGeometry(QtCore.QRect(60, 30, 291, 21))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.chkShowHide.setFont(font)
        self.chkShowHide.setObjectName("chkShowHide")
        self.lblImage = QtWidgets.QLabel(parent=self.centralwidget)
        self.lblImage.setGeometry(QtCore.QRect(60, 60, 301, 371))
        self.lblImage.setText("")
        self.lblImage.setPixmap(QtGui.QPixmap("images/daughter.jpg"))
        self.lblImage.setScaledContents(True)
        self.lblImage.setObjectName("lblImage")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 442, 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"))
        self.chkShowHide.setText(_translate("MainWindow", "Show/Hide Image"))

Tạo thêm “MainWindowEx.py” kế thừa từ class generate ở trên để xử lý sự kiện:

from PyQt6.QtCore import Qt

from MainWindow import Ui_MainWindow


class MainWindowEx(Ui_MainWindow):
    def __init__(self):
        pass
    def setupUi(self, MainWindow):
        super().setupUi(MainWindow)
        self.MainWindow=MainWindow
        self.chkShowHide.setChecked(True)
        self.chkShowHide.stateChanged.connect(self.processChecked)
    def show(self):
        self.MainWindow.show()
    def processChecked(self,value):
        state=Qt.CheckState(value)
        if state==Qt.CheckState.Checked:
            self.lblImage.show()
        elif state==Qt.CheckState.Unchecked:
            self.lblImage.hide()

Hàm self.chkShowHide.setChecked(True) ở trên mặc định sẽ checked QCheckBox.

Hàm processChecked để lắng nghe xem khi nào QCheckBox được checked hay unchecked.

Lệnh self.lblImage.show() để hiển thị hình khi QCheckBox được checked

Lệnh self.lblImage.hide() để hiển thị hình khi QCheckBox được unchecked

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

Khi checked vào QCheckBox:

Khi Unchecked vào QCheckBox:

Mã lệnh của bài trong case 2 này các bạn tải ở đây:

https://www.mediafire.com/file/7so7gruuzxlzpxj/LearnCheckBoxDirectly.rar/file

Như vậy tới đây Tui đã trình bày xong widget QCheckBox một cách khá kỹ lưỡng, với phần trình bày lý thuyết các thành phần và chức năng của QCheckBox. Cũng như có 2 ví dụ minh họa cụ thể để các bạn có thể review lại, các bạn chú ý thực hành lại nhiều lần để hiểu rõ hơn về nó.

Bài học tiếp theo Tui sẽ trình bày về QRadioButton, các bạn chú ý theo dõi

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

Bài 13: QPushButton – Basic Widgets – PyQt6

QPushButton là widget cơ bản nhưng vô cùng quan trọng trong mọi phần mềm tương tác người dùng. QPushButton sẽ nhận lệnh từ người dùng và tiến hành xử lý các yêu cầu tương ứng. Ví dụ như Nút lệnh Thoát phần mềm, nhấn vào sẽ thoát phần mềm. Nút lệnh Đăng nhập, bấm vào sẽ xử lý đăng nhập vào hệ thống. Thông thường chúng ta sẽ dùng signal clicked của QPush Button để nhận và xử lý thao tác của người dùng khi họ nhấn vào Button. Button cũng có thể định dạng màu nền, màu chữ, icon…

Để sử dụng QPushButton ta thực hiện các bước như dưới đây:

Bước 1:

Ta import lớp QPushButton từ module PyQt6.QtWidgets:

from PyQt6.QtWidgets import QPushButton

Bước 2:

Tạo đối tượng QPushButton và các thuộc tính liên quan:

button = QPushButton('Click Me')

Nếu muốn thêm icon cho QPushButton ta sẽ import thêm thư viện:

from PyQt6.QtGui import QIcon

Chép hình vào dự án, sau đó gọi đối tượng setIcon như dưới đây cho QPushButton:

button.setIcon(QIcon('myicon.png'))

Để thiết lập kích thước của QPushButton cho đẹp hơn thì ta gọi hàm setFixedSize() như dưới đây:

button.setFixedSize(QSize(100, 30))

QSize thuộc module PyQt6.QtCore nên ta cần import thư viện này, QSize sẽ nhận chiều rộng (100) và chiều cao (30) và gán cho QPushButton.

Bước 3:

Gán signal clicked cho QPushButton với slit tên là on_clicked

button.clicked.connect(self.on_clicked)
def on_clicked(self):
    print("You clicked me!")

Bây giờ ta thiết màn hình dưới đây để minh họa cho QPushButton (mặc dù chúng ta đã dùng widget này rất nhiều lần ở các bài học trước):

Phần mềm trên là phần mềm BMI (Body Mass Index) phần mềm tính chỉ số cân đối của cơ thể. Chương trình nhận vào chiều cao và cân nặng. Các QPushButton có các Icon bên cạnh.

BMI = Cân nặng (kg)/ Chiều cao (mét)*Chiều cao (mét)

Do đó khi giao diện nhân được chiều cao là CM thì cần đổi qua mét và Comment được tính theo bảng Classification như dưới đây:

ClassificationBMI range – kg/m2
Severe Thinness< 16
Moderate Thinness16 – 17
Mild Thinness17 – 18.5
Normal18.5 – 25
Overweight25 – 30
Obese Class I30 – 35
Obese Class II35 – 40
Obese Class III> 40

Trong Pycharm ta tạo dự án “LearnQPushButton” với cấu trúc như hình dưới đây:

Thư mục “images” sẽ lưu 2 biểu tượng icon, các bạn có thể lựa chọn hình bất kỳ.

Tương tư như các bài học trước thì ta dùng công cụ Qt Designer được tích hợp trong Pycharm để thiết kế màn hình “MainWindow.UI”, các bài học trước chúng ta đã rất quen thuộc với thiết kế Qt Designer rồi, ở bài này ta bổ sung Icon cho QPushButton, chọn Button và trong mục Icon bấm chọn Choose From File rồi tìm tới hình ảnh và chọn lúc này ta có Icon như hình bên dưới :

Ta có thể gán signal cho Button Close.

Sau khi thiết kế và đặt tên cho các Widget như hình ở trên thì ta tiến hành Generate giao diện thành Python code “MainWindow.py”:

# 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(507, 405)
        self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.label = QtWidgets.QLabel(parent=self.centralwidget)
        self.label.setGeometry(QtCore.QRect(20, 10, 471, 51))
        font = QtGui.QFont()
        font.setPointSize(15)
        self.label.setFont(font)
        self.label.setStyleSheet("background-color: rgb(255, 255, 0);\n"
"color: rgb(0, 0, 127);")
        self.label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
        self.label.setObjectName("label")
        self.label_2 = QtWidgets.QLabel(parent=self.centralwidget)
        self.label_2.setGeometry(QtCore.QRect(30, 70, 81, 41))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.label_2.setFont(font)
        self.label_2.setObjectName("label_2")
        self.lineEditWeight = QtWidgets.QLineEdit(parent=self.centralwidget)
        self.lineEditWeight.setGeometry(QtCore.QRect(140, 80, 191, 31))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.lineEditWeight.setFont(font)
        self.lineEditWeight.setObjectName("lineEditWeight")
        self.label_3 = QtWidgets.QLabel(parent=self.centralwidget)
        self.label_3.setGeometry(QtCore.QRect(350, 70, 81, 41))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.label_3.setFont(font)
        self.label_3.setObjectName("label_3")
        self.label_4 = QtWidgets.QLabel(parent=self.centralwidget)
        self.label_4.setGeometry(QtCore.QRect(350, 130, 81, 41))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.label_4.setFont(font)
        self.label_4.setObjectName("label_4")
        self.lineEditHeight = QtWidgets.QLineEdit(parent=self.centralwidget)
        self.lineEditHeight.setGeometry(QtCore.QRect(140, 140, 191, 31))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.lineEditHeight.setFont(font)
        self.lineEditHeight.setObjectName("lineEditHeight")
        self.label_5 = QtWidgets.QLabel(parent=self.centralwidget)
        self.label_5.setGeometry(QtCore.QRect(30, 130, 81, 41))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.label_5.setFont(font)
        self.label_5.setObjectName("label_5")
        self.pushButtonCalculate = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButtonCalculate.setGeometry(QtCore.QRect(110, 290, 141, 51))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.pushButtonCalculate.setFont(font)
        icon = QtGui.QIcon()
        icon.addPixmap(QtGui.QPixmap("images/ic_calculate.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonCalculate.setIcon(icon)
        self.pushButtonCalculate.setIconSize(QtCore.QSize(32, 32))
        self.pushButtonCalculate.setObjectName("pushButtonCalculate")
        self.pushButtonClose = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButtonClose.setGeometry(QtCore.QRect(290, 290, 141, 51))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.pushButtonClose.setFont(font)
        icon1 = QtGui.QIcon()
        icon1.addPixmap(QtGui.QPixmap("images/ic_close.png"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off)
        self.pushButtonClose.setIcon(icon1)
        self.pushButtonClose.setIconSize(QtCore.QSize(32, 32))
        self.pushButtonClose.setObjectName("pushButtonClose")
        self.label_6 = QtWidgets.QLabel(parent=self.centralwidget)
        self.label_6.setGeometry(QtCore.QRect(30, 190, 81, 41))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.label_6.setFont(font)
        self.label_6.setObjectName("label_6")
        self.label_7 = QtWidgets.QLabel(parent=self.centralwidget)
        self.label_7.setGeometry(QtCore.QRect(30, 239, 91, 41))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.label_7.setFont(font)
        self.label_7.setObjectName("label_7")
        self.labelComment = QtWidgets.QLabel(parent=self.centralwidget)
        self.labelComment.setGeometry(QtCore.QRect(140, 240, 281, 41))
        palette = QtGui.QPalette()
        brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Active, QtGui.QPalette.ColorRole.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Active, QtGui.QPalette.ColorRole.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 127, 127))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Active, QtGui.QPalette.ColorRole.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 63, 63))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Active, QtGui.QPalette.ColorRole.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(127, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Active, QtGui.QPalette.ColorRole.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(170, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Active, QtGui.QPalette.ColorRole.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Active, QtGui.QPalette.ColorRole.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Active, QtGui.QPalette.ColorRole.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Active, QtGui.QPalette.ColorRole.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Active, QtGui.QPalette.ColorRole.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Active, QtGui.QPalette.ColorRole.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Active, QtGui.QPalette.ColorRole.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 127, 127))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Active, QtGui.QPalette.ColorRole.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Active, QtGui.QPalette.ColorRole.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Active, QtGui.QPalette.ColorRole.ToolTipText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Inactive, QtGui.QPalette.ColorRole.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Inactive, QtGui.QPalette.ColorRole.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 127, 127))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Inactive, QtGui.QPalette.ColorRole.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 63, 63))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Inactive, QtGui.QPalette.ColorRole.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(127, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Inactive, QtGui.QPalette.ColorRole.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(170, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Inactive, QtGui.QPalette.ColorRole.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Inactive, QtGui.QPalette.ColorRole.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Inactive, QtGui.QPalette.ColorRole.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Inactive, QtGui.QPalette.ColorRole.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Inactive, QtGui.QPalette.ColorRole.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Inactive, QtGui.QPalette.ColorRole.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Inactive, QtGui.QPalette.ColorRole.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 127, 127))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Inactive, QtGui.QPalette.ColorRole.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Inactive, QtGui.QPalette.ColorRole.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Inactive, QtGui.QPalette.ColorRole.ToolTipText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Disabled, QtGui.QPalette.ColorRole.WindowText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Disabled, QtGui.QPalette.ColorRole.Button, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 127, 127))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Disabled, QtGui.QPalette.ColorRole.Light, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 63, 63))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Disabled, QtGui.QPalette.ColorRole.Midlight, brush)
        brush = QtGui.QBrush(QtGui.QColor(127, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Disabled, QtGui.QPalette.ColorRole.Dark, brush)
        brush = QtGui.QBrush(QtGui.QColor(170, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Disabled, QtGui.QPalette.ColorRole.Mid, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Disabled, QtGui.QPalette.ColorRole.Text, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 255))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Disabled, QtGui.QPalette.ColorRole.BrightText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Disabled, QtGui.QPalette.ColorRole.ButtonText, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Disabled, QtGui.QPalette.ColorRole.Base, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Disabled, QtGui.QPalette.ColorRole.Window, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Disabled, QtGui.QPalette.ColorRole.Shadow, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Disabled, QtGui.QPalette.ColorRole.AlternateBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(255, 255, 220))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Disabled, QtGui.QPalette.ColorRole.ToolTipBase, brush)
        brush = QtGui.QBrush(QtGui.QColor(0, 0, 0))
        brush.setStyle(QtCore.Qt.BrushStyle.SolidPattern)
        palette.setBrush(QtGui.QPalette.ColorGroup.Disabled, QtGui.QPalette.ColorRole.ToolTipText, brush)
        self.labelComment.setPalette(palette)
        font = QtGui.QFont()
        font.setPointSize(12)
        self.labelComment.setFont(font)
        self.labelComment.setStyleSheet("color: rgb(255, 0, 0);")
        self.labelComment.setObjectName("labelComment")
        self.labelBMI = QtWidgets.QLabel(parent=self.centralwidget)
        self.labelBMI.setGeometry(QtCore.QRect(140, 190, 191, 41))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.labelBMI.setFont(font)
        self.labelBMI.setStyleSheet("color: rgb(0, 0, 255);")
        self.labelBMI.setObjectName("labelBMI")
        self.label_10 = QtWidgets.QLabel(parent=self.centralwidget)
        self.label_10.setGeometry(QtCore.QRect(350, 190, 81, 41))
        font = QtGui.QFont()
        font.setPointSize(12)
        self.label_10.setFont(font)
        self.label_10.setObjectName("label_10")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 507, 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)
        self.pushButtonClose.clicked.connect(MainWindow.close) # type: ignore
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Trần Duy Thanh"))
        self.label.setText(_translate("MainWindow", "BMI Application"))
        self.label_2.setText(_translate("MainWindow", "Weight:"))
        self.lineEditWeight.setText(_translate("MainWindow", "80"))
        self.label_3.setText(_translate("MainWindow", "(Kg)"))
        self.label_4.setText(_translate("MainWindow", "(CM)"))
        self.lineEditHeight.setText(_translate("MainWindow", "170"))
        self.label_5.setText(_translate("MainWindow", "Height:"))
        self.pushButtonCalculate.setText(_translate("MainWindow", "Calculate"))
        self.pushButtonClose.setText(_translate("MainWindow", "Close"))
        self.label_6.setText(_translate("MainWindow", "BMI:"))
        self.label_7.setText(_translate("MainWindow", "Comment:"))
        self.labelComment.setText(_translate("MainWindow", "Overweight"))
        self.labelBMI.setText(_translate("MainWindow", "27.7"))
        self.label_10.setText(_translate("MainWindow", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'MS Shell Dlg 2\'; font-size:12pt; font-weight:400; font-style:normal;\">\n"
"<p style=\" margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\">kg/m2</p></body></html>"))

Tiếp tục tạo lớp “MainWindowEx.py” kế thừa từ lớp giao diện được Generate ở trên để xử lý các sự kiện:

from MainWindow import Ui_MainWindow


class MainWindowEx(Ui_MainWindow):
    def __init__(self):
        pass
    def setupUi(self, MainWindow):
        super().setupUi(MainWindow)
        self.MainWindow=MainWindow
        self.pushButtonCalculate.clicked.connect(self.calculateBMI)
    def calculateBMI(self):
        weight=float(self.lineEditWeight.text())
        height=float(self.lineEditHeight.text())
        height=height/100
        BMI=weight/(height*height)
        BMI=round(BMI,2)
        comment=""
        if BMI<16:
            comment="Severe Thinness"
        elif BMI<17:
            comment="Moderate Thinness"
        elif BMI<18.5:
            comment="Mild Thinness"
        elif BMI<25:
            comment="Normal"
        elif BMI<30:
            comment="Overweight"
        elif BMI<35:
            comment="Obese Class I"
        elif BMI<40:
            comment="Obese Class II"
        else:
            comment = "Obese Class III"
        self.labelBMI.setText(str(BMI))
        self.labelComment.setText(comment)
    def show(self):
        self.MainWindow.show()

Lớp “MainWindowEx.py” ở trên được cung cấp slot calculateBMI cho Button “Calculate BMI” để tính chỉ số BMI cũng như đưa ra phân loại chỉ số cân đối cơ thể. Hàm này còn sử dụng round() để làm tròn 2 chữ số thập phân.

Cuối cùng ta viết “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.

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

https://www.mediafire.com/file/a1z1zl19ov7og68/LearnQPushButton.rar/file

Ngoài ra QPushButton còn có thể định dạng loại toggle:

button = QPushButton('Toggle Me')
button.setCheckable(True)
button.clicked.connect(self.on_toggle)

Hàm on_toggle để kiểm tra trạng thái:

def on_toggle(self, checked):
    print(checked)

Như vậy tới đây Tui đã trình bày xong QPushButton về cấu trúc, cách sử dụng cũng như kỹ thuật lập trình liên quan tới QPushButton, các bạn cũng biết cách tạo Icon cho nó, biết cách gán sự kiện và xử lý các tác vụ liên quan.

Bài học sau Tui sẽ trình bày về các Widget liên quan tới CheckBox và RadioButton. Các bạn chú ý theo dõi

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

Bài 12: QLineEdit – Basic Widgets – PyQt6

QLineEdit là Widget dùng để cung cấp ô nhập dữ liệu, ví dụ như nhập tên sản phẩm, nhập giá sản phẩm, nhập user name, nhập password….

Trong bài này chúng ta sẽ học:

  • Cách khai báo và sử dụng QLineEdit trong Qt Designer và trong code python,
  • Tìm hiểu một số thuộc tính thường sử dụng của QLineEdit,
  • Cách tạo ô nhập mật khẩu bằng QLineEdit,
  • Cách tạo chức năng auto-complete cho QLineEdit

Để tạo QLineEdit ta làm các bước sau:

Bước 1:

Ta import QLineEdit trong module PyQt6.QtWidgets

from PyQt6.QtWidgets import QLineEdit

Bước 2:

Tạo đối tượng QLineEdit, có thể không có đối số nào, hoặc có parent widget hoặc có chuỗi mặc định.

nameLineEdit = QLineEdit('Tran Duy Thanh', self)

QLineEdit cung cấp nhiều thuộc tính, trong đó có một số thuộc tính thường sử dụng như dưới đây:

Thuộc tínhKiểu dữ liệuMô tả
textstringThuộc tính lưu trữ nội dung nhập liệu của QLineEdit
readOnlyBooleanNếu thiết lập True thì QLineEdit chỉ đọc không thể sửa dữ liệu
placeholderTextstringThuộc tính hiển thị chuỗi gợi ý nếu như nội dung của QLineEdit đang rỗng
echoModeQLineEdit.EchoModeThuộc tính thay đổi cách thức hiển thị chuỗi, ví dụ như hiển thị dạng nhập mật khẩu với ký hiệu *
maxLengthintegerThuộc tính thiết lập số ký tự tối đa có thể nhập cho QLineEdit
clearButtonEnabledBooleanNếu True thì sẽ thêm button clear cho QLineEdit

Ta tiến hành tạo một dự án tên “LearnQLineEdit” trong Pycharm và thiết kế giao diện như dưới đây bằng Qt Designer:

Ta tiến hành lựa chọn layout management và đặt tên và gán nhãn cho các Widget như hình dưới đây (Tui không chụp hình lại các bước kéo thả Widget nữa vì các bài trước các bạn đã làm rồi, bài này tương tự):

Ô Full name ta bổ sung Hint và clearButton:

Ta nhấn chuột chọn QLineEdit cho Full Name trước, sau đó nhập dữ liệu cho thuộc tính “placeholderText” là “Enter your full name here” khi chạy giao diện lên nếu dữ liệu ô QLineEdit này chưa được nhập thì hint này sẽ được hiển thị để hướng dẫn hay gợi ý người sử dụng cách nhập liệu. Tiếp tục checked vào thuộc tính “clearButtonEnabled” chương trình sẽ thêm nút clear Button để giúp người sử dụng dễ dàng và nhanh chóng xóa dữ liệu trong ô nhập để nhập dữ liệu mới nhanh hơn.

Ta tiến hành chạy thử giao diện trong Qt Designer bằng cách nhấn tổ hợp phím “Ctrl + r“, chương trình sẽ chạy giao diện lên như hình:

Ta quan sát thấy ô nhập Full Name có placetextHolder hiển thị (gọi là hint) khi ô dữ liệu trống. Bây giờ ta nhập liệu cho Full Name và quan sát:

Ta thấy khi nhập liệu thì dòng chữ dạng hint bị biến mất, đồng thời xuất hiện button clear ở góc phải của QLineEdit, ta có thể nhấn vào button này để xóa dữ liệu cũ để nhập dữ liệu mới được nhanh hơn.

Tiếp theo cấu hình ký tự Password (*) cho các ô Password và Confirm password bằng cách chọn echoMode thành Password:

Nhấn tổ hợp phím “ctrl+r” để tiếp tục thử nghiệm nhập liệu password:

Cuối cùng ta gán signal và slot cho Close button:

Trong thẻ “Signal/Slot” ta chọn:

Sender: Chọn closepushButton

Signal: Chọn clicked()

Receiver: Chọn MainWindow

Slot: Chọn close()

Chạy phần mềm thử nghiệm ta nhấn nút “Close” chương trình sẽ tắt cửa sổ làm việc này.

Bây giờ ta lưu file giao diện này lại với tên “MainWindow.ui”

Tiến hành tạo Python code bằng chức năng Generate code như các bài trước đã hướng dẫn, lúc này “MainWindow.py” tạo ra là mã lệnh giao diện của “MainWindow.ui”:

Dưới đây là mã lệnh đầy đủ của “MainWindow.py“:

# 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(415, 357)
        self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName("verticalLayout")
        self.label = QtWidgets.QLabel(parent=self.centralwidget)
        self.label.setMaximumSize(QtCore.QSize(16777215, 50))
        self.label.setObjectName("label")
        self.verticalLayout.addWidget(self.label)
        self.gridLayout = QtWidgets.QGridLayout()
        self.gridLayout.setObjectName("gridLayout")
        self.label_2 = QtWidgets.QLabel(parent=self.centralwidget)
        self.label_2.setObjectName("label_2")
        self.gridLayout.addWidget(self.label_2, 0, 0, 1, 1)
        self.fullNamelineEdit = QtWidgets.QLineEdit(parent=self.centralwidget)
        self.fullNamelineEdit.setClearButtonEnabled(True)
        self.fullNamelineEdit.setObjectName("fullNamelineEdit")
        self.gridLayout.addWidget(self.fullNamelineEdit, 0, 1, 1, 1)
        self.label_3 = QtWidgets.QLabel(parent=self.centralwidget)
        self.label_3.setObjectName("label_3")
        self.gridLayout.addWidget(self.label_3, 1, 0, 1, 1)
        self.userNamelineEdit = QtWidgets.QLineEdit(parent=self.centralwidget)
        self.userNamelineEdit.setObjectName("userNamelineEdit")
        self.gridLayout.addWidget(self.userNamelineEdit, 1, 1, 1, 1)
        self.label_4 = QtWidgets.QLabel(parent=self.centralwidget)
        self.label_4.setObjectName("label_4")
        self.gridLayout.addWidget(self.label_4, 2, 0, 1, 1)
        self.emaillineEdit = QtWidgets.QLineEdit(parent=self.centralwidget)
        self.emaillineEdit.setObjectName("emaillineEdit")
        self.gridLayout.addWidget(self.emaillineEdit, 2, 1, 1, 1)
        self.label_5 = QtWidgets.QLabel(parent=self.centralwidget)
        self.label_5.setObjectName("label_5")
        self.gridLayout.addWidget(self.label_5, 3, 0, 1, 1)
        self.citylineEdit = QtWidgets.QLineEdit(parent=self.centralwidget)
        self.citylineEdit.setObjectName("citylineEdit")
        self.gridLayout.addWidget(self.citylineEdit, 3, 1, 1, 1)
        self.label_6 = QtWidgets.QLabel(parent=self.centralwidget)
        self.label_6.setObjectName("label_6")
        self.gridLayout.addWidget(self.label_6, 4, 0, 1, 1)
        self.passwordlineEdit = QtWidgets.QLineEdit(parent=self.centralwidget)
        self.passwordlineEdit.setEchoMode(QtWidgets.QLineEdit.EchoMode.Password)
        self.passwordlineEdit.setObjectName("passwordlineEdit")
        self.gridLayout.addWidget(self.passwordlineEdit, 4, 1, 1, 1)
        self.label_7 = QtWidgets.QLabel(parent=self.centralwidget)
        self.label_7.setObjectName("label_7")
        self.gridLayout.addWidget(self.label_7, 5, 0, 1, 1)
        self.confirmPasswordlineEdit = QtWidgets.QLineEdit(parent=self.centralwidget)
        self.confirmPasswordlineEdit.setEchoMode(QtWidgets.QLineEdit.EchoMode.Password)
        self.confirmPasswordlineEdit.setObjectName("confirmPasswordlineEdit")
        self.gridLayout.addWidget(self.confirmPasswordlineEdit, 5, 1, 1, 1)
        self.verticalLayout.addLayout(self.gridLayout)
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.registerpushButton = QtWidgets.QPushButton(parent=self.centralwidget)
        self.registerpushButton.setObjectName("registerpushButton")
        self.horizontalLayout.addWidget(self.registerpushButton, 0, QtCore.Qt.AlignmentFlag.AlignHCenter)
        self.closepushButton = QtWidgets.QPushButton(parent=self.centralwidget)
        self.closepushButton.setObjectName("closepushButton")
        self.horizontalLayout.addWidget(self.closepushButton, 0, QtCore.Qt.AlignmentFlag.AlignHCenter)
        self.verticalLayout.addLayout(self.horizontalLayout)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 415, 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)
        self.closepushButton.clicked.connect(MainWindow.close) # type: ignore
        QtCore.QMetaObject.connectSlotsByName(MainWindow)
        MainWindow.setTabOrder(self.fullNamelineEdit, self.userNamelineEdit)
        MainWindow.setTabOrder(self.userNamelineEdit, self.emaillineEdit)
        MainWindow.setTabOrder(self.emaillineEdit, self.citylineEdit)
        MainWindow.setTabOrder(self.citylineEdit, self.passwordlineEdit)
        MainWindow.setTabOrder(self.passwordlineEdit, self.confirmPasswordlineEdit)
        MainWindow.setTabOrder(self.confirmPasswordlineEdit, self.registerpushButton)
        MainWindow.setTabOrder(self.registerpushButton, self.closepushButton)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Trần Duy Thanh"))
        self.label.setText(_translate("MainWindow", "<html><head/><body><p align=\"center\"><span style=\" font-size:14pt; font-weight:600; color:#0000ff;\">Register Information</span></p></body></html>"))
        self.label_2.setText(_translate("MainWindow", "Full Name:"))
        self.fullNamelineEdit.setPlaceholderText(_translate("MainWindow", "Enter your full name here"))
        self.label_3.setText(_translate("MainWindow", "User Name:"))
        self.label_4.setText(_translate("MainWindow", "Email:"))
        self.label_5.setText(_translate("MainWindow", "City/Province:"))
        self.label_6.setText(_translate("MainWindow", "Password:"))
        self.label_7.setText(_translate("MainWindow", "Confirm Password:"))
        self.registerpushButton.setText(_translate("MainWindow", "Register"))
        self.closepushButton.setText(_translate("MainWindow", "Close"))

Tiếp theo tạo lớp kế thừa “MainWindowEx.py”:

from MainWindow import Ui_MainWindow


class MainWindowEx(Ui_MainWindow):
    def __init__(self):
        pass
    def setupUi(self, MainWindow):
        super().setupUi(MainWindow)
        self.MainWindow=MainWindow
    def show(self):
        self.MainWindow.show()

Cuối cùng tạo lớp “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ả:

Bây giờ ta tiến hành tạo chức năng auto-complete cho QLineEdit City/Province. Về lý thuyết ta làm theo bước sau:

Bước 1: Import QCompleter từ module PyQt6.QtWidgets

from PyQt6.QtWidgets import QCompleter

Bước 2: Tạo một danh sách dữ liệu để phục vụ cho auto-complete, thường nó là dữ liệu mảng một chiều. Ví dụ ta có:

cities=["Hà Nội","Huế", "Đà Nẵng", "Đà Lạt", "Hồ Chí Minh", "Cần Thơ"]

Bước 3: Tạo đối tượng QCompleter và gán dữ liệu danh sách ở bước 2 cho đối tượng này:

completer = QCompleter(cities)

Bước 4: Tạo đối tượng QLineEdit và gán setCompleter cho nó.

line_edit = QLineEdit(self)
line_edit.setCompleter(completer)

Bây giờ ta tiến hành bổ sung tính năng auto-complete cho City/Province, bằng cách hiệu chỉnh mã lệnh trong “MainWindowEx.py”:

from PyQt6.QtWidgets import QCompleter

from MainWindow import Ui_MainWindow


class MainWindowEx(Ui_MainWindow):
    def __init__(self):
        pass
    def setupUi(self, MainWindow):
        super().setupUi(MainWindow)
        self.MainWindow=MainWindow
        cities = ["Hà Nội", "Huế", "Đà Nẵng", "Đà Lạt", "Hồ Chí Minh", "Cần Thơ"]
        completer = QCompleter(cities)
        self.citylineEdit.setCompleter(completer)
    def show(self):
        self.MainWindow.show()

Tiến hành chạy “MyApp.py” ta có kết quả:

Hình trên ta thấy khi nhập City/Provice chương trình sẽ gợi ý danh sách auto-complete cho người sử dụng lựa chọn một cách nhanh chóng, đây cũng là một trong các tiêu chí về UI/UX tăng trải nghiệm sử dụng của khách hàng.

Như vậy tới đây Tui đã trình bày chi tiết và kỹ lượng xong widget QLineEdit, các bạn có thể ứng dụng QLineEdit vào các trường hợp cụ thể, biết cách sử dụng các thuộc tính quan trọng như text, clearButton, echoMode…, cũng như lập trình được auto-complete.

Mã nguồn chi tiết của bài này tải ở đây:

https://www.mediafire.com/file/55oxfrsqev3xbex/LearnQLineEdit.rar/file

Bài học sau Tui sẽ trình bày về QPushButton, là một Widget vô cùng quan trọng, hầu hết các phần mềm đều sử dụng để ra lệnh cho phần mềm tương tác. Các bạn chú ý theo dõi.

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

Bài 11: QLabel – Basic Widgets – PyQt6

Các bài học trước chúng ta đã được học chi tiết về cách tạo dự án trong Pycharm và tích hợp giao diện PyQt6-Qt Designer, các bạn đã biết cách thiết kế giao diện, sử dụng các Layout management để bố cục giao diện theo từng yêu cầu riêng, cũng như cách sử dụng Signals/Slots để xử lý sự kiện, đặc biệt là biết cách viết các lớp kế thừa để xử lý sự kiện mà không bị ảnh hưởng khi trong tương lai giao diện bị thay đổi. Các chuỗi bài học tiếp theo đây Tui sẽ hướng dẫn chi tiết từng Widget trong giao diện, từ cơ bản tới nâng cao, cũng như cách xử lý đa tiến trình, Styles, Chart…. để các bạn có thể dễ dàng thiết kế mọi giao diện đáp ứng được hầu hết các yêu cầu của khách hàng. Chuỗi các bài học Tui có tổng hợp trong link này để các bạn dễ dàng truy suất: https://tranduythanh.com/pyqt6-qt-designer/. Sau khi học xong các phần xử lý giao diện, Tui sẽ hướng dẫn các bạn cách build mô hình máy học và tích hợp mô hình máy học vào các dự án phần mềm tương tác người dùng, chẳng hạn như các mô hình dự báo kinh doanh, các mô hình gợi ý sản phẩm, phân tích cảm xúc khách hàng, thị giác máy tính…. Do đó để có thể vững lý thuyết chắc tay nghề thì các Bạn cần kiên trì, chăm chỉ, đọc kỹ lý thuyết và thực hành từng bước các bài hướng dẫn nhiều lần. Các bạn không nên nhảy cóc mà hãy học lần lượt từ bài số 1 cho tới bài cuối cùng, vì hầu hết các bài học này Tui hướng dẫn từ cơ bản tới nâng cao và có tính kế thừa, vận dụng lại các bài học trước đó.

Trong bài này Tui sẽ hướng dẫn các Bạn cách thức sử dụng QLabel, QLabel là Widget dùng để hiển thị dữ liệu dạng text, hình ảnh (nhiều định dạng như bmp, gif, jpg, jpeg, png, svg, …), Một số ví dụ minh họa Chúng ta sẽ thực hiện bao gồm:

  • Hiển thị dữ liệu dạng text thông thường
  • Hiện thị dữ liệu dạng text kết hợp căn lề, font chữ
  • Hiển thị dữ liệu dạng text có định dạng HTML
  • Hiển thị dữ liệu dạng hình ảnh dạng .bmp, .jpg, .jpeg, .png, .svg (QPixmap)
  • Hiển thị dữ liệu dạng hình ảnh dạng .gif (QMovie)

Chúng ta có thể viết code để sử dụng QLable lúc runtime hoặc sử dụng Qt Designer để sử dụng QLable lúc design time, tùy vào tình huống cụ thể mà ta lựa chọn tạo QLabel theo runtime hay design time.

Để sử dụng QLabel và hiển thị dữ liệu dạng text thông thườngta làm theo các bước sau:

Bước 1: Ta import widget QLabel trong gói module PyQt6.QtWidgets

from PyQt6.QtWidgets import QLabel

Bước 2: Tạo đối tượng từ lớp QLabel

label = QLabel('Hello, I am QLabel widget')

Mã lệnh ở trên tạo một đối tượng QLabel đồng thời khởi tạo chuỗi hiển thị mặc định. Ngoài ra ta có thể tạo đối tượng QLabel sau đó mới gán chuỗi hiển thị cho Label bằng cách sau:

label = QLabel()
label.setText('Hello, I am QLabel widget')

Bước 3: Truy suất và xóa dữ liệu hiển thị trong QLabel

Để truy suất tới nhãn hiển thị của QLabel ta gọi phương thức text():

label.text()

Để xóa dữ liệu hiển thị của QLabel ta gọi phương thức clear():

label.clear()

Để thay đổi font chữ cho QLabel ta dùng phương thức setFont như sau:

font = label.font()
font.setPointSize(30)
label.setFont(font)

Để căn lề cho chuỗi hiển thị trên QLabel ta gọi phương thức setAlignment: Ví dụ thiết lập căn lề nằm giữa theo chiều ngang:

label.setAlignment(Qt.AlignmentFlag.AlignHCenter)

Một số thuộc tính căn lề cần quan tâm:

Loại căn lềMô tả
Qt.AlignmentFlag.AlignLeftCăn chuỗi hiển thị bên trái QLabel
Qt.AlignmentFlag.AlignRightCăn chuỗi hiển thị bên phải QLabel
Qt.AlignmentFlag.AlignHCenterCăn chuỗi hiển thị nằm ở giữa QLabel theo phương ngang
Qt.AlignmentFlag.AlignJustifyCăn chuỗi hiển thị điều 2 bên của QLabel
Qt.AlignmentFlag.AlignTopCăn chuỗi hiển thị nằm ở phía bên trên Top QLabel
Qt.AlignmentFlag.AlignBottomCăn chuỗi hiển thị nằm ở phía bên dưới Bottom QLabel
Qt.AlignmentFlag.AlignVCenterCăn chuỗi hiển thị nằm ở giữa QLabel theo phương đứng

Chúng ta có thể dùng ký tự | để kết hợp nhiều loại căn lề, ví dụ căn lề phía bên trên góc trái:

align_top_left = Qt.AlignmentFlag.AlignLeft | Qt.AlignmentFlag.AlignTop
label.setAlignment(align_top_left)

Để hiển thị chuỗi có định dạng HTML cho QLabel ta viết như sau:

label.setText("<html><head/><body><p><span style=\" font-weight:600; color:#ff0000;\">Hello, I am QLable Widget</span></p></body></html>")

Để hiển thị dữ liệu hình ảnh định dạng .bmp, .jpg, .jpeg, .png, .svg cho QLabel ta dùng đối tượng QPixmap như dưới đây:

label = QLabel()
pixmap = QPixmap('myavatar.png')
label.setPixmap(pixmap)

Để hiển thị dữ liệu hình ảnh định dạng .gif cho QLabel ta dùng đối tượng QMovie như dưới đây:

from PyQt6.QtGui import QMovie
movie = QMovie('mygif.gif')
label = QLabel()
label.setMovie(movie)
movie.start()

Như vậy với các trình bày lý thuyết ở trên về cơ bản các bạn đã biết cách sử dụng QLabel, cách thức thao tác trên QLabel như tạo dữ liệu hiển thị, căn lề, hiển thị hình ảnh với nhiều định dạng khác nhau. Các bạn cần hiểu rõ các kiến thức này để áp dụng vào bài thiết kế thực tế.

Tiếp theo đây, chúng ta sẽ tiến hành tạo dự án tên “LearnQLabel” trong Pycharm, sử dụng Qt Designer để thiết kế cũng như thao tác với QLabel.

Sau khi tạo xong dự án “LearnQLabel“, ta tạo thêm một thư mục để lưu trữ hình ảnh .png cũng như .gif: Bấm chuột phải vào dự án chọn New/ Chọn Directory

Sau đó tạo thư mục tên là “images“:

Bạn chép 2 hình ảnh có định dạng png gif vào thư mục “images”, vì các hình ảnh bmp, jpg, jpeg, svg xử lý giống như png nên Tui chỉ minh họa png.

Tiếp theo ta sử dụng Qt Designer để thiết kế giao diện như dưới đây: Bấm chuột phải vào dự án/ chọn External Tools/ Chọn Create New Qt Designer:

Sau đó chọn QMainWindow để tạo giao diện: Trong công cụ Widget Box, ta kéo Label ra màn hình MainWindow:

Ta có thể hiệu chỉnh các thuộc tính của QLabel bằng cách bấm chuột phải vào QLabel rồi chọn “Change plain text…” để đổi nhãn hiển thị mà không có định dạng, hoặc chọn “Change rich text…” để đổi nhãn hiển thị cùng với định dạng HTML, hay đổi tên của QLabel bằng cách chọn “Change objectName…”, ngoài ra nếu muốn hiệu chỉnh nhiều thuộc tính khác của QLabel thì sau khi nhấn chuột vào QLabel, ta nhìn vào công cụ “Property Editor“, các thuộc tính của QLabel sẽ hiển thị chi tiết như text, texFormat, pixmap, alignment, margin…. tùy vào nhu cầu sử dụng mà ta thay đổi giá trị của các thuộc tính này cho nó hợp lý.

Ví dụ giả sử Tui chọn “Change rich text…” để hiệu chỉnh nhãn hiển thị có định dạng HTML:

Trong công cụ Edit Text ở trên, để thay đổi định dạng của chuỗi hiện thị, trước tiên ta bôi đen chuỗi hiển thị, sau đó chọn thay đổi size font chữ (to nhỏ), kiểu chữ (In đậm, in nghiêng, gạch chân), căn lề (trái, giữa, phải, đều 2 bên), hay hiệu chỉnh hình nền, màu nền màu chữ bằng các biểu tượng ở góc phải bên trên của cửa sổ Edit Text. Nhập dữ liệu và định dạng xong thì nhấn nút OK, ta có giao diện:

Bây giờ ta bổ sung thêm các Widgets như hình dưới đây:

Bạn cần kéo thả các Widgets và đặt tên giống như hình trên.

Ngoài ra bạn cũng có thể thêm hình vào lúc đang thiết kế, ví dụ Tui để mặc định hình hiển thị là mydaugther.PNG: Chọn QLabel, sau đó chọn pixmap-> Rồi chọn Choose File...

Sau đó chọn hình mà mình muốn hiển thị lên QLabel:

Chọn hình xong bấm Open, kết quả:

Ta checked cho thuộc tính “scaledContents” để thấy hình được stretch trong QLabel.

Lưu file giao diện với tên MainWindow.ui, nó tự động cập nhật trong dự án Pycharm:

Tiếp theo ta dùng chức năng Generate Python code cho giao diện “MainWindow.ui” lúc này file mã lệnh “MainWindow.py” được generate:

# 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(542, 450)
        self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.Titlelabel = QtWidgets.QLabel(parent=self.centralwidget)
        self.Titlelabel.setGeometry(QtCore.QRect(40, 10, 381, 41))
        self.Titlelabel.setTextFormat(QtCore.Qt.TextFormat.AutoText)
        self.Titlelabel.setObjectName("Titlelabel")
        self.ShowPNGpushButton = QtWidgets.QPushButton(parent=self.centralwidget)
        self.ShowPNGpushButton.setGeometry(QtCore.QRect(140, 100, 121, 31))
        self.ShowPNGpushButton.setObjectName("ShowPNGpushButton")
        self.showGIFpushButton = QtWidgets.QPushButton(parent=self.centralwidget)
        self.showGIFpushButton.setGeometry(QtCore.QRect(270, 100, 121, 31))
        self.showGIFpushButton.setObjectName("showGIFpushButton")
        self.imageLabel = QtWidgets.QLabel(parent=self.centralwidget)
        self.imageLabel.setGeometry(QtCore.QRect(120, 140, 271, 231))
        self.imageLabel.setText("")
        self.imageLabel.setPixmap(QtGui.QPixmap("images/mydaughter.PNG"))
        self.imageLabel.setScaledContents(True)
        self.imageLabel.setObjectName("imageLabel")
        self.changeTextpushButton = QtWidgets.QPushButton(parent=self.centralwidget)
        self.changeTextpushButton.setGeometry(QtCore.QRect(30, 60, 75, 23))
        self.changeTextpushButton.setObjectName("changeTextpushButton")
        self.alignLeftpushButton = QtWidgets.QPushButton(parent=self.centralwidget)
        self.alignLeftpushButton.setGeometry(QtCore.QRect(240, 60, 75, 23))
        self.alignLeftpushButton.setObjectName("alignLeftpushButton")
        self.alignCenterpushButton = QtWidgets.QPushButton(parent=self.centralwidget)
        self.alignCenterpushButton.setGeometry(QtCore.QRect(330, 60, 75, 23))
        self.alignCenterpushButton.setObjectName("alignCenterpushButton")
        self.alignRightpushButton = QtWidgets.QPushButton(parent=self.centralwidget)
        self.alignRightpushButton.setGeometry(QtCore.QRect(410, 60, 75, 23))
        self.alignRightpushButton.setObjectName("alignRightpushButton")
        self.fontSizepushButton = QtWidgets.QPushButton(parent=self.centralwidget)
        self.fontSizepushButton.setGeometry(QtCore.QRect(120, 60, 75, 23))
        self.fontSizepushButton.setObjectName("fontSizepushButton")
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 542, 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)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.Titlelabel.setText(_translate("MainWindow", "<html><head/><body><p><span style=\" font-size:12pt; font-weight:600; color:#ff0000;\">Trần Duy Thanh</span></p></body></html>"))
        self.ShowPNGpushButton.setText(_translate("MainWindow", "Show .PNG Format"))
        self.showGIFpushButton.setText(_translate("MainWindow", "Show .GIF Format"))
        self.changeTextpushButton.setText(_translate("MainWindow", "Change Text"))
        self.alignLeftpushButton.setText(_translate("MainWindow", "Align Left"))
        self.alignCenterpushButton.setText(_translate("MainWindow", "Align Center"))
        self.alignRightpushButton.setText(_translate("MainWindow", "Align Right"))
        self.fontSizepushButton.setText(_translate("MainWindow", "Font Size ++"))

Tiếp theo ta tạo một lớp “MainWindowEx” kế thừa từ lớp Generate Python code ở trên, mục đích để xử lý sự kiện người dùng mà không bị ảnh hưởng giao diện khi trong tương lai nó thay đổi.

from MainWindow import Ui_MainWindow


class MainWindowEx(Ui_MainWindow):
    def __init__(self):
        pass
    def setupUi(self, MainWindow):
        super().setupUi(MainWindow)
        self.MainWindow=MainWindow
    def show(self):
        self.MainWindow.show()

Tiếp theo ta tạo lớp/file “MyApp.py” để chạy 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ả:

Phần mềm trên có 7 chức năng mà ta cần lập trình: Đổi text, thay đổi font chữ, căn lề trái, căn giữa, căn lề phải, đổi 2 hình. Ta tiếp tục hiệu chỉnh mã lệnh cho lớp “MainWindowEx.py” để xử lý đủ 7 chức năng này:

  • Đổi Text: Gọi Signal và gán Slots cho Button đổi text, nhấn vào thì thay nhãn bằng “https://tranduythanh.com”
from MainWindow import Ui_MainWindow


class MainWindowEx(Ui_MainWindow):
    def __init__(self):
        pass
    def setupUi(self, MainWindow):
        super().setupUi(MainWindow)
        self.MainWindow=MainWindow
        #change text
        self.changeTextpushButton.clicked.connect(self.processChangeText)
    def show(self):
        self.MainWindow.show()
    def processChangeText(self):
        self.Titlelabel.setText("https://tranduythanh.com")

Chạy phần lên, nhấn nút “Change Text” ta có kết quả:

  • Đổi font size: Tiếp tục gán sự kiện cho nút đổi font size
from MainWindow import Ui_MainWindow


class MainWindowEx(Ui_MainWindow):
    def __init__(self):
        pass
    def setupUi(self, MainWindow):
        super().setupUi(MainWindow)
        self.MainWindow=MainWindow
        #change text
        self.changeTextpushButton.clicked.connect(self.processChangeText)
        #change font
        self.fontSizepushButton.clicked.connect(self.processChangeFontSize)
    def show(self):
        self.MainWindow.show()
    def processChangeText(self):
        self.Titlelabel.setText("https://tranduythanh.com")
    def processChangeFontSize(self):
        #get font object
        font=self.Titlelabel.font()
        #change font size
        font.setPointSize(20)
        #set italic
        font.setItalic(True)
        #set bold
        font.setBold(True)
        #set font name
        font.setFamily("cambria")
        #re-assign font
        self.Titlelabel.setFont(font)

Hàm/Slot processChangeFontSize ở trên dùng để thay đổi font.

  • Tiếp tục ta bổ sung mã lệnh để căn trái, phải, giữa cho text nằm trong QLable:
from PyQt6.QtCore import Qt

from MainWindow import Ui_MainWindow


class MainWindowEx(Ui_MainWindow):
    def __init__(self):
        pass
    def setupUi(self, MainWindow):
        super().setupUi(MainWindow)
        self.MainWindow=MainWindow
        #change text
        self.changeTextpushButton.clicked.connect(self.processChangeText)
        #change font
        self.fontSizepushButton.clicked.connect(self.processChangeFontSize)
        #set align text
        self.alignLeftpushButton.clicked.connect(self.processAlignLeft)
        self.alignCenterpushButton.clicked.connect(self.processAlignCenter)
        self.alignRightpushButton.clicked.connect(self.processAlignRight)
    def show(self):
        self.MainWindow.show()
    def processChangeText(self):
        self.Titlelabel.setText("https://tranduythanh.com")
    def processChangeFontSize(self):
        #get font object
        font=self.Titlelabel.font()
        #change font size
        font.setPointSize(20)
        #set italic
        font.setItalic(True)
        #set bold
        font.setBold(True)
        #set font name
        font.setFamily("cambria")
        #re-assign font
        self.Titlelabel.setFont(font)
    def processAlignLeft(self):
        self.Titlelabel.setAlignment(Qt.AlignmentFlag.AlignLeft)
    def processAlignCenter(self):
        self.Titlelabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
    def processAlignRight(self):
        self.Titlelabel.setAlignment(Qt.AlignmentFlag.AlignRight)
  • Cuối cùng ta viết sự kiện để thay đổi 2 hình ảnh cho các Button:
from PyQt6.QtCore import Qt
from PyQt6.QtGui import QPixmap, QMovie

from MainWindow import Ui_MainWindow


class MainWindowEx(Ui_MainWindow):
    def __init__(self):
        pass
    def setupUi(self, MainWindow):
        super().setupUi(MainWindow)
        self.MainWindow=MainWindow
        #change text
        self.changeTextpushButton.clicked.connect(self.processChangeText)
        #change font
        self.fontSizepushButton.clicked.connect(self.processChangeFontSize)
        #set align text
        self.alignLeftpushButton.clicked.connect(self.processAlignLeft)
        self.alignCenterpushButton.clicked.connect(self.processAlignCenter)
        self.alignRightpushButton.clicked.connect(self.processAlignRight)
        #change image with PNG format
        self.ShowPNGpushButton.clicked.connect(self.processChangePNG)
        #change image with gif format
        self.showGIFpushButton.clicked.connect(self.processChangeGIF)
    def show(self):
        self.MainWindow.show()
    def processChangeText(self):
        self.Titlelabel.setText("https://tranduythanh.com")
    def processChangeFontSize(self):
        #get font object
        font=self.Titlelabel.font()
        #change font size
        font.setPointSize(20)
        #set italic
        font.setItalic(True)
        #set bold
        font.setBold(True)
        #set font name
        font.setFamily("cambria")
        #re-assign font
        self.Titlelabel.setFont(font)
    def processAlignLeft(self):
        self.Titlelabel.setAlignment(Qt.AlignmentFlag.AlignLeft)
    def processAlignCenter(self):
        self.Titlelabel.setAlignment(Qt.AlignmentFlag.AlignCenter)
    def processAlignRight(self):
        self.Titlelabel.setAlignment(Qt.AlignmentFlag.AlignRight)
    def processChangePNG(self):
        #create QPixmap instance
        pixmap=QPixmap("images/mydaughter.PNG")
        #set pixmap for label
        self.imageLabel.setPixmap(pixmap)
    def processChangeGIF(self):
        #create QMovie instance
        movie=QMovie("images/PyQt-Grid-AlignmentFlag.gif")
        #set movie object for label
        self.imageLabel.setMovie(movie)
        #call start method
        movie.start()

Như vậy ta đã có đầy đủ mã lệnh, chạy phần mềm lên ta được:

Như vậy Tui đã trình bày khá chi tiết cách sử dụng QLabel, và các bạn đã biết cách ứng dụng QLabel với các trường hợp khác nhau trong triển khai phần mềm. Bạn có thể kết hợp nhiều cách sử dụng QLabel vào từng trường hợp cụ thể.

Dưới đây là mã lệnh đầy đủ của bài học này:

https://www.mediafire.com/file/4a6e3xelgpmemns/LearnQLabel.rar/file

Các bạn cố gắng học phần lý thuyết và thực hành lại nhiều lần các bài hướng dẫn ở trên.

Các bài học tiếp theo Tui sẽ trình bày về QLineEdit và QPushButton. Các bạn chú ý theo dõi.

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

Bài 10: QFormLayout-Layout Management

Như vậy chúng ta đã thao tác được với các Layout: QVBoxLayout để sắp xếp widgets theo phương thẳng đứng, QHBoxLayout để sắp xếp Widgets theo phương ngang, QGridLayout để sắp xếp Widgets theo dạng dòng và cột. Trong bài này ta sẽ vào layout cuối cùng đó là QFormLayout để sắp xếp các Widget theo dạng Data- Entry, nghĩa là khi thiết kế giao diện ta muốn vừa có label vừa có nhập liệu ví dụ như thiết kế màn hình đăng nhập, thiết kế màn hình đăng ký….Thì mỗi một dòng tạo ra nó tự động có 2 cột, cột 1 là label, cột 2 là ô nhập liệu.

Bước 1: Trong Pycharm, tạo một dự án tên “LearnQFormLayout“, sử dụng Qt Designer để thiết kế, kéo FormLayout ra giao diện:

Bước 2: Để thêm các row layout, ví dụ trong bài này ta thiết kế màn hình đăng ký: Bấm chuột phải vào QFormLayout -> Chọn Add form layout row…

Thêm nhãn Full Name và QLineEdit nhập full name

Label text: Nhập tiêu đề

Label name: Đặt tên cho Label (dùng cho lập trình truy suất widget)

Field type: Chọn QLineEdit là ô nhập liệu

Field name: Đặt tên cho QLineEdit (dùng cho lập trình truy suất widget)

Sau đó nhấn OK để tạo widget.

Lưu ý tùy vào mục đích sử dụng mà ta có thể chọn các Field type khác nhau:

Bước 3: Tương tự, ta lần lượt tạo thêm User Name, Password và Confirm Password:

Bước 4: Ta bổ sung thêm QPushButton và cấu hình như bên dưới:

Kéo PushButton vào ô thứ 2, chỉnh width và height trong Property Editor như hình.

Ngoài ra các ô Password và Confirm Password, ta chỉnh echoMode qua Password:

Lưu lại giao diện với tên “MainWindow.ui

Bước 5: Generate Python code cho giao diện “MainWindow.ui” trong file “MainWindow.py”:

# 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(423, 285)
        self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName("verticalLayout")
        self.formLayout = QtWidgets.QFormLayout()
        self.formLayout.setObjectName("formLayout")
        self.fullNameLabel = QtWidgets.QLabel(parent=self.centralwidget)
        self.fullNameLabel.setObjectName("fullNameLabel")
        self.formLayout.setWidget(0, QtWidgets.QFormLayout.ItemRole.LabelRole, self.fullNameLabel)
        self.fullNameLineEdit = QtWidgets.QLineEdit(parent=self.centralwidget)
        self.fullNameLineEdit.setObjectName("fullNameLineEdit")
        self.formLayout.setWidget(0, QtWidgets.QFormLayout.ItemRole.FieldRole, self.fullNameLineEdit)
        self.userNameLabel = QtWidgets.QLabel(parent=self.centralwidget)
        self.userNameLabel.setObjectName("userNameLabel")
        self.formLayout.setWidget(1, QtWidgets.QFormLayout.ItemRole.LabelRole, self.userNameLabel)
        self.userNameLineEdit = QtWidgets.QLineEdit(parent=self.centralwidget)
        self.userNameLineEdit.setObjectName("userNameLineEdit")
        self.formLayout.setWidget(1, QtWidgets.QFormLayout.ItemRole.FieldRole, self.userNameLineEdit)
        self.passwordLabel = QtWidgets.QLabel(parent=self.centralwidget)
        self.passwordLabel.setObjectName("passwordLabel")
        self.formLayout.setWidget(2, QtWidgets.QFormLayout.ItemRole.LabelRole, self.passwordLabel)
        self.passwordLineEdit = QtWidgets.QLineEdit(parent=self.centralwidget)
        self.passwordLineEdit.setEchoMode(QtWidgets.QLineEdit.EchoMode.Password)
        self.passwordLineEdit.setObjectName("passwordLineEdit")
        self.formLayout.setWidget(2, QtWidgets.QFormLayout.ItemRole.FieldRole, self.passwordLineEdit)
        self.confirmPasswordLabel = QtWidgets.QLabel(parent=self.centralwidget)
        self.confirmPasswordLabel.setObjectName("confirmPasswordLabel")
        self.formLayout.setWidget(3, QtWidgets.QFormLayout.ItemRole.LabelRole, self.confirmPasswordLabel)
        self.confirmPasswordLineEdit = QtWidgets.QLineEdit(parent=self.centralwidget)
        self.confirmPasswordLineEdit.setEchoMode(QtWidgets.QLineEdit.EchoMode.Password)
        self.confirmPasswordLineEdit.setObjectName("confirmPasswordLineEdit")
        self.formLayout.setWidget(3, QtWidgets.QFormLayout.ItemRole.FieldRole, self.confirmPasswordLineEdit)
        self.pushButton = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButton.setMinimumSize(QtCore.QSize(100, 30))
        self.pushButton.setMaximumSize(QtCore.QSize(100, 30))
        self.pushButton.setObjectName("pushButton")
        self.formLayout.setWidget(4, QtWidgets.QFormLayout.ItemRole.FieldRole, self.pushButton)
        self.verticalLayout.addLayout(self.formLayout)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 423, 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)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "Tran Duy Thanh"))
        self.fullNameLabel.setText(_translate("MainWindow", "Full Name:"))
        self.userNameLabel.setText(_translate("MainWindow", "User Name:"))
        self.passwordLabel.setText(_translate("MainWindow", "Password:"))
        self.confirmPasswordLabel.setText(_translate("MainWindow", "Confirm Password:"))
        self.pushButton.setToolTip(_translate("MainWindow", "<html><head/><body><p><span style=\" font-size:11pt; font-weight:600; color:#ff0000;\">Click here to Signup</span></p></body></html>"))
        self.pushButton.setText(_translate("MainWindow", "Sign Up"))

Bước 6: Tạo lớp Python kế thừa “MainWindowEx.py” từ “MainWindow.py” để xử lý các sự kiện

from MainWindow import Ui_MainWindow


class MainWindowEx(Ui_MainWindow):
    def __init__(self):
        pass
    def setupUi(self, MainWindow):
        super().setupUi(MainWindow)
        self.MainWindow=MainWindow
    def show(self):
        self.MainWindow.show()

Bước 7: Tạo “MyApp.py” để chạy 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 có kết quả:

Các bạn có thể tải mã nguồn của bài QFormLayout ở đây:

https://www.mediafire.com/file/u3553qa1fxit4q8/LearnQFormLayout.rar/file

Như vậy tới đây Tui đã trình bày xong các Layout quan trọng và thường sử dụng trong Qt Designer/ PyQt6. Các bạn có thể phối hợp các layout lại để thiết kế các bố cục giao diện theo yêu cầu của khách hàng.

Các bài học sau Tui sẽ trình bày chi tiết về các Widgets cơ bản trong PyQt6, thiết kế trong Qt Designer cũng như cách thức lập trình xử lý sự kiện tương tác người dùng. Mỗi bài Tui sẽ cố gắng cung cấp các ví dụ sát với thực tế để các bạn dễ hiểu bài, vững lý thuyết chắc tay nghề.

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

Bài 9: QGridLayout-Layout Management

Như vậy các bạn đã biết cách bố cục giao diện bằng QVBoxLayout theo phương đứng và QHBoxLayout theo phương ngang, tuy nhiên có nhiều giao diện chúng ta phải bố cục theo dạng dòng và cột (Grid). PyQt6 cung cấp GridLayout để chúng ta thực hiện điều này.

GridLayout chia giao diện thành các dòng và cột, dòng chạy từ trên xuống dưới theo index bắt đầu từ 0, cột chạy từ trái qua phải theo index bắt đầu từ 0.

Giao giữa Dòng và Cột được gọi là Ô. Mỗi Ô như vậy ta có thể để 1 Control/widget vào bên trong, do đó muốn có nhiều control/widget trong một Ô thì ta có thể khai báo thêm 1 Layout, layout này chứa nhiều control/widget.

Trong Qt Designer, ta kéo GridLayout ra giao diện, sau đó kéo các Widget vào GridLayout. Mỗi lần kéo Widget vào layout nó sẽ xuất hiện các đường kẻ màu xanh để cho chúng ta lựa chọn nơi đặt:

Ngoài ra ta có thể trộn nhiều cột, trộn nhiều dòng lại với nhau. Chúng ta chỉ cần kéo Widget chiếm qua các ô khác thì sẽ được trộn dòng hoặc cột tùy hướng chúng ta kéo (ta cần làm các ô mà ta muốn trộn ở trạng thái rỗng không có chứa bất kỳ controls/widgets nào). Ví dụ:

Bây giờ ta trộn 3 cột cho Button có nhãn (0,1), lúc này ta chỉ cần chỉnh size của Button này bằng cách kéo nó qua luôn 3 ô liên tục:

Hay trộn Button (3,1) với 2 cột và 3 dòng:

  • Kéo size Button qua trái chiếm 2 cột:
  • Kéo size Button xuống dưới chiếm 3 dòng:

Tương tự, Nếu muốn trộn 3 dòng cho Button có nhãn (3,4) thì ta kéo size của button này chiếm 3 dòng:

Nếu Widget trong mỗi Ô chiếm không gian chưa hết Ô đó thì ta có thể căn lên các Widget này theo nhiều cách:

(gif: nguồn pythontutorial.net)

Dưới đây là một số thuộc tính liên quan tới căn lề mà chúng ta có thể lập trình:

Alignment FlagÝ nghĩa
AlignAbsoluteNếu hướng từ trái sang phải thì AlignLeft là căn chỉnh với cạnh phải. Tuy nhiên, nếu muốn AlignLeft luôn được căn chỉnh với cạnh trái, thì kết hợp AlignLeft với tùy chọn AlignAbsolute .
AlignBaselineCăn chỉnh widget với đường cơ sở.
AlignBottomCăn chỉnh widget ở bên dưới
AlignCenter
căn chỉnh widget ở giữa
AlignHCenterCăn wiget nằm giữa theo chiều ngang
AlignHorizontal_MaskCăn widget nằm ngang bằng cách kết hợp nhiều loại: AlignLeft | AlignRight | AlignHCenter | AlignJustify | AlignAbsolute
AlignJustifyCăn điều text
AlignLeftCăn widget theo lề trái
AlignRightCăn widget theo lề phải
AlignTopCăn widget theo hướng bên trên
AlignVCenterCăn wiget nằm giữa theo chiều đứng
AlignVertical_MaskCăn widget nằm đứng bằng cách kết hợp nhiều loại: AlignTop | AlignBottom | AlignVCenter | AlignBaseline

Ví dụ 1:

Ứng dụng GridLayout thiết kế màn hình đăng nhập bằng Qt Designer:

Dưới đây là cấu trúc tập tin trong dự án:

“MainWindow.ui” là file giao diện được thiết kế bởi Qt Designer, và “MainWindow.py” là file mã lệnh tạo giao diện bằng cách Generate code Python:

# 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(524, 261)
        self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName("verticalLayout")
        self.gridLayout = QtWidgets.QGridLayout()
        self.gridLayout.setObjectName("gridLayout")
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.pushButtonLogin = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButtonLogin.setObjectName("pushButtonLogin")
        self.horizontalLayout.addWidget(self.pushButtonLogin)
        self.pushButtonExit = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButtonExit.setObjectName("pushButtonExit")
        self.horizontalLayout.addWidget(self.pushButtonExit)
        self.gridLayout.addLayout(self.horizontalLayout, 4, 1, 1, 1)
        self.label = QtWidgets.QLabel(parent=self.centralwidget)
        self.label.setMinimumSize(QtCore.QSize(0, 48))
        self.label.setMaximumSize(QtCore.QSize(16777215, 50))
        self.label.setStyleSheet("color: rgb(255, 0, 0);\n"
"font: 75 14pt \"MS Shell Dlg 2\";")
        self.label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
        self.label.setObjectName("label")
        self.gridLayout.addWidget(self.label, 0, 0, 1, 2)
        self.lineEditPassword = QtWidgets.QLineEdit(parent=self.centralwidget)
        self.lineEditPassword.setEchoMode(QtWidgets.QLineEdit.EchoMode.Password)
        self.lineEditPassword.setObjectName("lineEditPassword")
        self.gridLayout.addWidget(self.lineEditPassword, 2, 1, 1, 1)
        self.chkSaveInformation = QtWidgets.QCheckBox(parent=self.centralwidget)
        self.chkSaveInformation.setObjectName("chkSaveInformation")
        self.gridLayout.addWidget(self.chkSaveInformation, 3, 1, 1, 1)
        self.lineEditUsername = QtWidgets.QLineEdit(parent=self.centralwidget)
        self.lineEditUsername.setObjectName("lineEditUsername")
        self.gridLayout.addWidget(self.lineEditUsername, 1, 1, 1, 1)
        self.label_3 = QtWidgets.QLabel(parent=self.centralwidget)
        self.label_3.setObjectName("label_3")
        self.gridLayout.addWidget(self.label_3, 2, 0, 1, 1)
        self.label_2 = QtWidgets.QLabel(parent=self.centralwidget)
        self.label_2.setObjectName("label_2")
        self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
        self.verticalLayout.addLayout(self.gridLayout)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 524, 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", "Tran Duy Thanh"))
        self.pushButtonLogin.setText(_translate("MainWindow", "Login"))
        self.pushButtonExit.setText(_translate("MainWindow", "Exit"))
        self.label.setText(_translate("MainWindow", "Login Screen"))
        self.chkSaveInformation.setText(_translate("MainWindow", "Save login information"))
        self.label_3.setText(_translate("MainWindow", "Password:"))
        self.label_2.setText(_translate("MainWindow", "User Name:"))

“MainWindowEx.py” là file mã lệnh được viết kế thừa nhằm xử lý các sự kiện liên quan mà không ảnh hưởng tới giao diện khi có thay đổi trong tương lai:

from MainWindow import Ui_MainWindow


class MainWindowEx(Ui_MainWindow):
    def __init__(self):
        pass
    def setupUi(self, MainWindow):
        super().setupUi(MainWindow)
        self.MainWindow=MainWindow
    def show(self):
        self.MainWindow.show()

Cuối cùng như thường lệ được trình bày trong các bài học trước, ta tạp file “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 có kết quả:

Như vậy ta thấy sau khi tạo GridLayout, việc thêm widget vào layout sẽ làm theo các bước:

Bước 1:

Tạo đối tượng QGridLayout:

layout = QGridLayout()

Bước 2:

Thiết lập layout cho parent:

parent.setLayout(layout)

Bước 3:

Gán các Widget/Control vào trong layout:

layout.addWidget(widget, row, column, rowSpan, columnSpan, alignment)
  • widget là các control/widget mà ta muốn sắp xếp trên layout QGridLayout
  • row là vị trí dòng mà ta muốn gán wiget/control trong QGridLayout, tính theo index từ 0
  • column là vị trí cột mà ta muốn gán wiget/control trong QGridLayout, tính theo index từ 0.
  • rowSpan là số dòng mà ta muốn widget sẽ chiếm trên giao diện.
  • columnSpan là số cột mà ta muốn widget sẽ chiếm trên giao diện.
  • alignment là cách căn lề widget trong ô trên QGridLayout

Code của ví dụ 1 các bạn có thể tải ở đây:

https://www.mediafire.com/file/8ky1vqmb9vrgf8w/LearnQGridLayout.rar/file

Ví dụ 2: Caro game

Tiếp theo Tui sẽ minh họa cách dùng QGridLayout để vẽ giao diện màn hình chơi Caro lúc runtime, đồng thời hướng dẫn cách xử lý gán sự kiện cho các Button bằng signal lúc runtime để biết được Button nào đang thực hiện:

Tạo một dự án tên “Carogame” có cấu trúc như dưới đây:

“MainWindow.ui” được thiết kế bởi Qt Designer có giao diện như sau:

Giao diện trên ta tiết lập QVBoxLayout cho màn hình chính, các ô nhập liệu và button “Draw Caro” nằm trong QHBoxLayout. Cuối cùng ở bên dưới ta có 1 ScrollArea, ScrollArea này chứa QGridLayout để vẽ bàn cờ Caro.

“MainWindow.py” khi generate python code của giao diện:

# 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(758, 722)
        self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setSizeConstraint(QtWidgets.QLayout.SizeConstraint.SetMinimumSize)
        self.horizontalLayout.setContentsMargins(-1, -1, -1, 0)
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.label = QtWidgets.QLabel(parent=self.centralwidget)
        self.label.setObjectName("label")
        self.horizontalLayout.addWidget(self.label)
        self.lineEditRows = QtWidgets.QLineEdit(parent=self.centralwidget)
        self.lineEditRows.setObjectName("lineEditRows")
        self.horizontalLayout.addWidget(self.lineEditRows)
        self.label_2 = QtWidgets.QLabel(parent=self.centralwidget)
        self.label_2.setObjectName("label_2")
        self.horizontalLayout.addWidget(self.label_2)
        self.lineEditColumn = QtWidgets.QLineEdit(parent=self.centralwidget)
        self.lineEditColumn.setObjectName("lineEditColumn")
        self.horizontalLayout.addWidget(self.lineEditColumn)
        self.pushButtonDrawCaro = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButtonDrawCaro.setObjectName("pushButtonDrawCaro")
        self.horizontalLayout.addWidget(self.pushButtonDrawCaro)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.verticalLayout_2.addLayout(self.verticalLayout)
        self.scrollArea = QtWidgets.QScrollArea(parent=self.centralwidget)
        self.scrollArea.setWidgetResizable(True)
        self.scrollArea.setObjectName("scrollArea")
        self.scrollAreaWidgetContents_2 = QtWidgets.QWidget()
        self.scrollAreaWidgetContents_2.setGeometry(QtCore.QRect(0, 0, 734, 608))
        self.scrollAreaWidgetContents_2.setObjectName("scrollAreaWidgetContents_2")
        self.verticalLayout_3 = QtWidgets.QVBoxLayout(self.scrollAreaWidgetContents_2)
        self.verticalLayout_3.setObjectName("verticalLayout_3")
        self.gridLayoutCaro = QtWidgets.QGridLayout()
        self.gridLayoutCaro.setSpacing(0)
        self.gridLayoutCaro.setObjectName("gridLayoutCaro")
        self.verticalLayout_3.addLayout(self.gridLayoutCaro)
        self.scrollArea.setWidget(self.scrollAreaWidgetContents_2)
        self.verticalLayout_2.addWidget(self.scrollArea)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 758, 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", "MainWindow"))
        self.label.setText(_translate("MainWindow", "Number Of Rows:"))
        self.lineEditRows.setText(_translate("MainWindow", "50"))
        self.label_2.setText(_translate("MainWindow", "Number Of Columns:"))
        self.lineEditColumn.setText(_translate("MainWindow", "50"))
        self.pushButtonDrawCaro.setText(_translate("MainWindow", "Draw Caro"))

Tương tự như các bài trước, ta tạo “MainWindowEx.py” kế thừa từ lớp generate giao diện để xử lý các sự kiện cũng như không bị ảnh hưởng mã lệnh khi tương lai Giao diện có sự thay đổi:

from functools import partial

from PyQt6.QtCore import Qt
from PyQt6.QtWidgets import QPushButton

from MainWindow import Ui_MainWindow


class MainWindowEx(Ui_MainWindow):
    def __init__(self):
        self.previous=''
    def setupUi(self, MainWindow):
        super().setupUi(MainWindow)
        self.MainWindow=MainWindow
        self.pushButtonDrawCaro.clicked.connect(self.processDrawCaro)
    def processDrawCaro(self):
        row=int(self.lineEditRows.text())
        column=int(self.lineEditColumn.text())
        for i in  range(row):
            self.gridLayoutCaro.setRowStretch(i,1)
            for j in range(column):
                self.gridLayoutCaro.setColumnStretch(j,1)
                btn= QPushButton()
                btn.setFixedWidth(50)
                btn.setFixedHeight(50)
                btn.sizePolicy().setHorizontalStretch(1)
                btn.sizePolicy().setVerticalStretch(1)
                self.gridLayoutCaro.addWidget(btn, i, j,
                         alignment=Qt.AlignmentFlag.AlignVertical_Mask|Qt.AlignmentFlag.AlignHorizontal_Mask)
                btn.clicked.connect(partial(self.processClicked, btn))
    def processClicked(self,btn):
        if len(btn.text()) > 0:
            return
        if self.previous=="X":
            self.previous="O"
        else:
            self.previous = "X"
        if len(btn.text())==0:
            btn.setText(self.previous)
        else:
            btn.setText(self.previous)
    def show(self):
        self.MainWindow.show()

Ta quan sát slot “processDrawCaro” để vẽ các Button lúc runtime.

trong dòng 30 ta học thêm kỹ thuật mới đó là “partial” có 2 đối số: đối số thứ nhất là slot xử lý sự kiện khi Button được nhấn, đối số thứ 2 ta truyền đối tượng button vào để quá trình xử lý ta biết được chính xác Button nào được nhấn.

Slot “processClicked” sẽ có 2 đối số, đối số thứ 2 là biến btn , biến này chính là PushButton được nhấn, dựa vào Button này ta sẽ xử lý chính xác Button nào đang được nhấn.

Ngoài ra coding còn bổ sung biến previous, biến này đánh dấu là trước đó ‘X’ hay ‘O’ đã được chọn.

Cuối cùng ta tạo lớp “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 lên ta có giao diện:

Ta nhấn nút “Draw Caro”, chương trình sẽ vẽ:

Coding đầy đủ của bài “Caro Game” này các bạn tải ở đây:

https://www.mediafire.com/file/ilcxryavbdk6teq/Carogame.rar/file

Như vậy Tui đã trình bày xong cách sử dụng QGridLayout, với 2 ví dụ quan trọng: thiết kế giao diện lúc Design Time và thiết kế giao diện lúc Runtime thông qua bài vẽ bàn cơ caro. Khi thiết kế màn hình với dạng dòng và cột thì các bạn có thể liên tưởng tới cách sử dụng QGridLayout.

Ngoài ra trong các ví dụ ta cũng thấy được sự kết hợp của nhiều loại layout khác nhau, cũng như cách dùng ScrollArea để thiết kế các màn hình mà có nội dung vượt quá khả năng lưu trữ của màn hình.

Bài học sau Tui sẽ hướng dẫn QFormlayout để sử dụng trong việc thiết kế giao diện có dạng nhập liệu (data-entry) form.

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

Bài 8: QHBoxLayout-Layout Management

Trong bài 7 Chúng ta đã biết cách dùng QVBoxLayout để sắp xếp các controls/widgets theo phương đứng. Trong bài này Tui sẽ trình bày về QHBoxLayout để bố cục các controls/widgets theo phương ngang. Cũng như cách thức phối hợp QVBoxLayout và QHBoxLayout để thiết kế màn hình giải phương trình bậc 1.

Tạo một dự án tên là “LearnQHBoxLayout” trong Pycharm, sau đó dùng Qt Designer để thiết kế màn hình theo các bước như dưới đây:

Bấm chuột phải vào dự án ==> chọn External Tools==> Chọn Create new Qt Designer.

Ta kéo thả “Horizontal Layout” vào giao diện của MainWindow

Tiếp tục kéo thả thêm 3 Button như hình dưới đây:

Quan sát thấy 3 Button sẽ được sắp xếp theo phương nằm ngang

Tương tự như QVBoxLayout, ta có thể căn Trái, Căn Phải, Căn Giữa các Controls/Widgets trong layout bằng cách dùng Spacer, cụ thể là dùng Horizontal Spacer.

Để căn trái ta kéo Horizontal Spacer vào layout như hình dưới đây:

Nếu kéo Horizontal Spacer như trên thì các Controls/Widgets sẽ được căn trái trong Layout, tương tự như chức năng Dock Left.

Để căn phải ta kéo Horizontal Spacer vào layout như hình dưới đây:

Ta lưu ý khi nhấn Spacer hay bất kỳ 1 Widget nào vào layout thì sau khi kéo Widget vào ta khoan hãy nhả chuột ra, mà cần rê rê chuột tới vị trí ta muốn thả, khi nào có đường kẻ màu xanh xuất hiện thì ta nhả chuột ra thì nó sẽ được bố cục chính xác nơi mà ta mong muốn:

Để căn giữa layout ta kéo 2 Spacer nằm ở 2 bên của Layout thì lúc này các Widgets sự được căn giữa layout.

Để thăm các Spacer giữa các Widgets trong một layout ta chỉ việc kéo Spacer đó chèn vào giữa các Widgets mà ta muốn bố cục. Ví dụ:

Lưu ý là muốn Spacer nằm ở đâu thì lúc kéo vào giao diện ta rê rê chuột, khi nào thanh màu xanh xuất hiện thì ta nhả chuột ra:

Tương tự như QVBoxLayout, thì layout này cũng hỗ trợ các Margin để ta định vị thêm các khoảng cách tương đối của các Widgets nằm bên trong Layout so với nó. Khoảng cách này ta gọi là Margin:

Ví dụ như sau khi chỉnh sửa Margin ta có:

Ở giao diện trên ta chỉnh:

  • layoutLeftMargin =85
  • layoutTopMargin=92
  • layoutRightMargin=100
  • layoutBottomMargin=90

Bây giờ ta thử kết hợp QVBoxLayout và QHBoxLayout để thiết kế giao diện màn hình giải phương trình bậc 1:

Bước 1:

Tiếp tục bấm chuột phải vào dự án “LearnQHBoxLayout” ==> Chọn External Tools==>Chọn Create new Qt Designer.

Chọn QMainWindow để thiết kế giao diện, Lưu giao diện lại với tên “MainWindowFirstDegreeEquation.ui

Bước 2:

Kéo Vertical Layout vào giao diện

Tiếp tục hiệu chỉnh layout của MainWindow qua Lay out Vertically bằng cách bấm chuột phải vào vùng trống bất kỳ của MainWindow==> Chọn “Lay out” ==> Chọn “Lay Out Vertically“:

Lúc này giao diện sẽ tự động bố cục như dưới đây:

Bước 3:

Kéo thêm một Label vào giao QVBoxLayout và đặt nhãn là “First Degree Equation“, chỉnh Font Size=15 và Bold=True trong cửa sổ Property:

Bước 4:

Kéo thêm một QHBoxLayout vào bên trong QVBoxLayout và cho nó ở bên dưới “First Degree Equation”, layout này để hiển thị nhãn Hệ số A và ô nhập liệu Hệ số A:

Kéo Label và LineEdit vào QHBoxLayout ở trên để làm ô nhập liệu cho hệ số a:

Bước 5:

Tương tự như vậy, ta lặp lại bước 4 để làm cho hệ số b.

Kéo thêm một QHBoxLayout vào bên trong QVBoxLayout và cho nó ở bên dưới QHBoxLayout hệ số a:

Kéo Label và LineEdit vào QHBoxLayout ở trên để làm ô nhập liệu cho hệ số b:

Bước 6:

Tương tự như vậy, ta lặp lại bước 5 để thêm QHBoxLayout kết quả:

Kéo thêm một QHBoxLayout vào bên trong QVBoxLayout và cho nó ở bên dưới QHBoxLayout Hệ số b:

Kéo Label và LineEdit vào QHBoxLayout ở trên để làm ô hiển thị kết quả giải phương trình:

Bước 7:

Tương tự như vậy, ta lặp lại bước 6 để thêm 3 Button “Solution” (giải phương trình), “Clear” (xóa dữ liệu trên giao diện để nhập phương trình mới), “Exit” (Thoát phần mềm).

Kéo thêm một QHBoxLayout vào bên trong QVBoxLayout và cho nó ở bên dưới QHBoxLayout Kết quả:

Tiếp tục lần lượt kéo 3 Button vào QHBoxLayout ở trên:

Trong bài này chúng ta chưa tiến hành giải phương trình, mà chúng ta chỉ dừng lại ở việc thiết kế giao diện thông qua sự kết hợp giữa QHBoxLayout và QVBoxLayout. Các bạn có thể bổ sung thêm các Spacer, hay hiệu chỉnh lại font chữ của các Widget cho phù hợp theo ý mình.

Các bạn tiến hành đặt tên cho các control/widgets tương ứng với các nhãn (Bấm chuột phải vào Widget/chọn change Object Name.., Hoặc trong Property sửa object Name). Chi tiết các control này sẽ được học trong phần Basic Widgets.

Sau khi cập nhập giao diện thì nhấn nút lưu để đảm bảo giao diện này được cập nhật mới nhất vì nó đồng bộ qua dự án bên Pycharm.

Bước 8:

Generate code giao diện “MainWindowFirstDegreeEquation.ui” bằng cách bấm chuột phải vào nó ==> Chọn “External Tools” ==> Chọn “Generate Python Code with PyUIC”:

Lúc này file mã nguồn của giao diện sẽ tự động được tạo ra “MainWindowFirstDegreeEquation.py”:

# Form implementation generated from reading ui file 'MainWindowFirstDegreeEquation.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(500, 235)
        self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        self.label = QtWidgets.QLabel(parent=self.centralwidget)
        font = QtGui.QFont()
        font.setPointSize(15)
        font.setBold(True)
        font.setWeight(75)
        self.label.setFont(font)
        self.label.setAlignment(QtCore.Qt.AlignmentFlag.AlignCenter)
        self.label.setObjectName("label")
        self.verticalLayout.addWidget(self.label)
        self.horizontalLayout = QtWidgets.QHBoxLayout()
        self.horizontalLayout.setObjectName("horizontalLayout")
        self.label_2 = QtWidgets.QLabel(parent=self.centralwidget)
        self.label_2.setObjectName("label_2")
        self.horizontalLayout.addWidget(self.label_2)
        self.lineEditCoefficientA = QtWidgets.QLineEdit(parent=self.centralwidget)
        self.lineEditCoefficientA.setObjectName("lineEditCoefficientA")
        self.horizontalLayout.addWidget(self.lineEditCoefficientA)
        self.verticalLayout.addLayout(self.horizontalLayout)
        self.horizontalLayout_3 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_3.setObjectName("horizontalLayout_3")
        self.label_3 = QtWidgets.QLabel(parent=self.centralwidget)
        self.label_3.setObjectName("label_3")
        self.horizontalLayout_3.addWidget(self.label_3)
        self.lineEditCoefficientB = QtWidgets.QLineEdit(parent=self.centralwidget)
        self.lineEditCoefficientB.setObjectName("lineEditCoefficientB")
        self.horizontalLayout_3.addWidget(self.lineEditCoefficientB)
        self.verticalLayout.addLayout(self.horizontalLayout_3)
        self.horizontalLayout_4 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_4.setObjectName("horizontalLayout_4")
        self.label_4 = QtWidgets.QLabel(parent=self.centralwidget)
        self.label_4.setMinimumSize(QtCore.QSize(0, 0))
        self.label_4.setObjectName("label_4")
        self.horizontalLayout_4.addWidget(self.label_4)
        self.lineEditsolution = QtWidgets.QLineEdit(parent=self.centralwidget)
        self.lineEditsolution.setObjectName("lineEditsolution")
        self.horizontalLayout_4.addWidget(self.lineEditsolution)
        self.verticalLayout.addLayout(self.horizontalLayout_4)
        self.horizontalLayout_6 = QtWidgets.QHBoxLayout()
        self.horizontalLayout_6.setObjectName("horizontalLayout_6")
        self.pushButtonSolution = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButtonSolution.setObjectName("pushButtonSolution")
        self.horizontalLayout_6.addWidget(self.pushButtonSolution)
        self.pushButtonClear = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButtonClear.setObjectName("pushButtonClear")
        self.horizontalLayout_6.addWidget(self.pushButtonClear)
        self.pushButtonExit = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButtonExit.setObjectName("pushButtonExit")
        self.horizontalLayout_6.addWidget(self.pushButtonExit)
        self.verticalLayout.addLayout(self.horizontalLayout_6)
        self.verticalLayout_2.addLayout(self.verticalLayout)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 500, 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", "MainWindow"))
        self.label.setText(_translate("MainWindow", "First Degree Equation"))
        self.label_2.setText(_translate("MainWindow", "Coefficient a:"))
        self.label_3.setText(_translate("MainWindow", "Coefficient b:"))
        self.label_4.setText(_translate("MainWindow", "The Solution:"))
        self.pushButtonSolution.setText(_translate("MainWindow", "Solution"))
        self.pushButtonClear.setText(_translate("MainWindow", "Clear"))
        self.pushButtonExit.setText(_translate("MainWindow", "Exit"))

Tạo một lớp “MainWindowFirstDegreeEquationEx.py” kế thừa từ “MainWindowFirstDegreeEquation.py”

from MainWindowFirstDegreeEquation import Ui_MainWindow


class MainWindowFirstDegreeEquationEX(Ui_MainWindow):
    def __init__(self):
        pass
    def setupUi(self, MainWindow):
        super().setupUi(MainWindow)
        self.MainWindow=MainWindow
        self.MainWindow.setWindowTitle("Tran Duy Thanh - Demo Layout Management")
    def show(self):
        self.MainWindow.show()

Tiếp tục tạo một lớp “MyApp.py”

from PyQt6.QtWidgets import QApplication, QMainWindow

from MainWindowFirstDegreeEquatioEx import MainWindowFirstDegreeEquationEX

app=QApplication([])
myWindow= MainWindowFirstDegreeEquationEX()
myWindow.setupUi(QMainWindow())
myWindow.show()
app.exec()

Cấu trúc dự án đầy đủ:

Chạy “MyApp.py” ta có kết quả:

Như vậy giao diện đã chạy đúng như mong muốn thiết kế của chúng ta

Bước 9:

Bây giờ chúng ta tiếp tục bổ sung mã lệnh cho “MainWindowFirstDegreeEquatioEx.py” để xử lý các chức năng cho các Button “Solution”, “Clear” và “Exit”:

from MainWindowFirstDegreeEquation import Ui_MainWindow


class MainWindowFirstDegreeEquationEX(Ui_MainWindow):
    def __init__(self):
        pass
    def setupUi(self, MainWindow):
        super().setupUi(MainWindow)
        self.MainWindow=MainWindow
        self.MainWindow.setWindowTitle("Tran Duy Thanh - Demo Layout Management")
        self.pushButtonExit.clicked.connect(self.process_exit)
        self.pushButtonClear.clicked.connect(self.process_clear)
        self.pushButtonSolution.clicked.connect(self.process_solution)
    def process_solution(self):
        a = float(self.lineEditCoefficientA.text())
        b = float(self.lineEditCoefficientB.text())
        if a==0 and b ==0:
            self.lineEditsolution.setText("Infinities solutions")
        elif a==0 and b!=0:
            self.lineEditsolution.setText("No solution")
        else:
            self.lineEditsolution.setText("X="+str(-b/a))
    def process_clear(self):
        self.lineEditCoefficientA.setText("")
        self.lineEditCoefficientB.setText("")
        self.lineEditsolution.setText("")
        self.lineEditCoefficientA.setFocus()
    def process_exit(self):
        self.MainWindow.close()
    def show(self):
        self.MainWindow.show()

Chạy “MyApp.py” ta thử nghiệm một số phương trình và có các kết quả như hình dưới đây:

  • Thử nghiệm với phương trình vô số nghiệm
  • Thử nghiệm với phương trình vô nghiệm
  • Thử nghiệm với phương trình có nghiệm

Như vậy tới đây Tui đã hướng dẫn đầy đủ và chi tiết cách thức sử dụng QHBoxLayout cũng như cách phối hợp với QVBoxLayout để thiết kế giao diện, cùng với việc sử dụng một số Widget cơ bản như QLineEdit, QPushButton, QLabel để thiết kế giao diện giải phương trình bậc 1. Đồng thời cũng ôn tập lại cách thức khai báo và sử dụng Signal với Slot.

Dựa vào ví dụ ở trên để các bạn có thể thiết kế các màn hình giao diện tương tự, ví dụ như màn hình Giải phương trình bậc 2, hay các màn hình tính toán số học, máy tính bỏ túi…

Giao diện và mã lệnh của dự án này các bạn tải ở đây:

https://www.mediafire.com/file/n68gedyqrejy8hr/LearnQHBoxLayout.rar/file

Bài học sau Tui sẽ hướng dẫn chi tiết về cách sử dụng QGridLayout

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

Bài 7: QVBoxLayout-Layout Management

PyQt cũng tương tự như các nền tảng khác, nó cung cấp một số layout management để giúp ta có thể thêm nhiều control/Widget và bố cục giao diện một cách dễ dàng hơn. Trong các bài học này Tui sẽ trình bày về:

  • QVBoxLayout (sắp xếp các control/Widget theo chiều đứng)
  • QHBoxLayout(sắp xếp các control/Widget theo chiều ngang)
  • QGridLayout (sắp xếp các control/Widget theo dòng và cột)
  • QFormLayout (sắp xếp các control/Widget theo dạng Data-Entry)

Trước tiên ta làm quen với QVBoxLayout . Tạo một dự án tên “LearnQVBoxLayout” trong Pycharm, sau đó dùng QT Designer để thiết kế giao diện đặt tên file là MainWindow.ui , sau đó sử dụng QVBoxLayout:

Cách tạo dự án và cách sử dụng QT Designer đã được hướng dẫn chi tiết ở các bài trước, nên bài này Tui không nói lại:

Trong mục Layouts, ta kéo “Vertical Layout” vào màn hình thiết kế.

Ngoài ra, với QMainWindow, nếu bạn muốn thiết lập layout cho nó thì nhấn chuột phải vào vị trí trống bất kỳ trên giao diện -> sau đó chọn “Lay out”-> rồi chọn các loại Layout mà mình muốn bố cục, ví dụ này Tui chọn “Layout vertically”

Lúc này QVBoxLayout mà bạn kéo vào nó có hình dạng như dưới đây:

Và nếu bạn muốn bỏ thiết lập layout thì ta Bấm chuột phải vào Layout đó rồi chọn “Layout out” –> chọn “Break Layout“:

Bây giờ trong QVBoxLayout mà bạn kéo thả ở trên, ta kéo thêm 4 Buttons vào bên trong:

Ta quan sát thấy, các Control khi kéo vào nó sẽ sắp xếp theo phương đứng như trên.

Chúng ta có thể căn lề các Controls/Widgets bằng cách dùng Spacer, ví dụ muốn căn lề Bottom (muốn các Controls/widgets chỉ nằm ở phía bên dưới của Layout) ta kéo Vertical Spacer như sau:

Bạn có thể Generate Python code (MainWindow.py) để xem cách thức PyQt6 tạo giao diện:

# 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(397, 284)
        self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding)
        self.verticalLayout.addItem(spacerItem)
        self.pushButton_2 = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButton_2.setObjectName("pushButton_2")
        self.verticalLayout.addWidget(self.pushButton_2)
        self.pushButton_3 = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButton_3.setObjectName("pushButton_3")
        self.verticalLayout.addWidget(self.pushButton_3)
        self.pushButton_4 = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButton_4.setObjectName("pushButton_4")
        self.verticalLayout.addWidget(self.pushButton_4)
        self.pushButton_5 = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButton_5.setObjectName("pushButton_5")
        self.verticalLayout.addWidget(self.pushButton_5)
        self.verticalLayout_2.addLayout(self.verticalLayout)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 397, 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)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton_2.setText(_translate("MainWindow", "PushButton 1"))
        self.pushButton_3.setText(_translate("MainWindow", "PushButton 2"))
        self.pushButton_4.setText(_translate("MainWindow", "PushButton 3"))
        self.pushButton_5.setText(_translate("MainWindow", "PushButton 4"))

Ta thấy khi Generate code, dòng 22, 23 là các mã lệnh để thêm Spacer vào Layout.

Muốn căn lề Top (Muốn các Controls/Widgets nằm ở phía bên trên layout), ta kéo (di chuyển) Vertical Spacer như sau:

Bạn có thể hiểu nó giống như chức năng DOCK, gắn cố định các control ở một góc nào đó trên giao diện.

Bạn có thể Generate Python code (MainWindow.py) để xem cách thức PyQt6 tạo giao diện có liên quan tới căn lề Top ở trên:

# 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(397, 284)
        self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        self.pushButton_2 = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButton_2.setObjectName("pushButton_2")
        self.verticalLayout.addWidget(self.pushButton_2)
        self.pushButton_3 = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButton_3.setObjectName("pushButton_3")
        self.verticalLayout.addWidget(self.pushButton_3)
        self.pushButton_4 = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButton_4.setObjectName("pushButton_4")
        self.verticalLayout.addWidget(self.pushButton_4)
        self.pushButton_5 = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButton_5.setObjectName("pushButton_5")
        self.verticalLayout.addWidget(self.pushButton_5)
        spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding)
        self.verticalLayout.addItem(spacerItem)
        self.verticalLayout_2.addLayout(self.verticalLayout)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 397, 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)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton_2.setText(_translate("MainWindow", "PushButton 1"))
        self.pushButton_3.setText(_translate("MainWindow", "PushButton 2"))
        self.pushButton_4.setText(_translate("MainWindow", "PushButton 3"))
        self.pushButton_5.setText(_translate("MainWindow", "PushButton 4"))

Ta thấy khi Generate code, dòng 34, 35 là các mã lệnh để thêm Spacer vào Layout.

Muốn Căn lề giữa, ta kéo 2 Verticle Spacer:

Bạn có thể Generate Python code (MainWindow.py) để xem cách thức PyQt6 tạo giao diện có liên quan tới căn lề Giữa ở trên:

# 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(397, 284)
        self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding)
        self.verticalLayout.addItem(spacerItem)
        self.pushButton_2 = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButton_2.setObjectName("pushButton_2")
        self.verticalLayout.addWidget(self.pushButton_2)
        self.pushButton_3 = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButton_3.setObjectName("pushButton_3")
        self.verticalLayout.addWidget(self.pushButton_3)
        self.pushButton_4 = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButton_4.setObjectName("pushButton_4")
        self.verticalLayout.addWidget(self.pushButton_4)
        self.pushButton_5 = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButton_5.setObjectName("pushButton_5")
        self.verticalLayout.addWidget(self.pushButton_5)
        spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding)
        self.verticalLayout.addItem(spacerItem1)
        self.verticalLayout_2.addLayout(self.verticalLayout)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 397, 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)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton_2.setText(_translate("MainWindow", "PushButton 1"))
        self.pushButton_3.setText(_translate("MainWindow", "PushButton 2"))
        self.pushButton_4.setText(_translate("MainWindow", "PushButton 3"))
        self.pushButton_5.setText(_translate("MainWindow", "PushButton 4"))

Ta thấy khi Generate code, dòng 22,23 và 36, 37 là các mã lệnh để thêm Spacer vào Layout để căn lề giữa.

Ngoài ra ta có thể thêm các Spacer giữa các Control/Widgets với nhau, ví dụ:

Bạn có thể Generate Python code (MainWindow.py) để xem cách thức PyQt6 tạo giao diện có liên quan tới căn lề Giữa ở trên:

# 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(397, 284)
        self.centralwidget = QtWidgets.QWidget(parent=MainWindow)
        self.centralwidget.setObjectName("centralwidget")
        self.verticalLayout_2 = QtWidgets.QVBoxLayout(self.centralwidget)
        self.verticalLayout_2.setObjectName("verticalLayout_2")
        self.verticalLayout = QtWidgets.QVBoxLayout()
        self.verticalLayout.setObjectName("verticalLayout")
        spacerItem = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding)
        self.verticalLayout.addItem(spacerItem)
        self.pushButton_2 = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButton_2.setObjectName("pushButton_2")
        self.verticalLayout.addWidget(self.pushButton_2)
        self.pushButton_3 = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButton_3.setObjectName("pushButton_3")
        self.verticalLayout.addWidget(self.pushButton_3)
        self.pushButton_4 = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButton_4.setObjectName("pushButton_4")
        self.verticalLayout.addWidget(self.pushButton_4)
        spacerItem1 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding)
        self.verticalLayout.addItem(spacerItem1)
        self.pushButton_5 = QtWidgets.QPushButton(parent=self.centralwidget)
        self.pushButton_5.setObjectName("pushButton_5")
        self.verticalLayout.addWidget(self.pushButton_5)
        spacerItem2 = QtWidgets.QSpacerItem(20, 40, QtWidgets.QSizePolicy.Policy.Minimum, QtWidgets.QSizePolicy.Policy.Expanding)
        self.verticalLayout.addItem(spacerItem2)
        self.verticalLayout_2.addLayout(self.verticalLayout)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtWidgets.QMenuBar(parent=MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 397, 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)

    def retranslateUi(self, MainWindow):
        _translate = QtCore.QCoreApplication.translate
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow"))
        self.pushButton_2.setText(_translate("MainWindow", "PushButton 1"))
        self.pushButton_3.setText(_translate("MainWindow", "PushButton 2"))
        self.pushButton_4.setText(_translate("MainWindow", "PushButton 3"))
        self.pushButton_5.setText(_translate("MainWindow", "PushButton 4"))

Ta thấy khi Generate code, dòng 22,23 và 33, 34 và 38,39 là các mã lệnh để thêm Spacer vào Layout để căn lề cho các Controls/Widgets.

Để thử nghiệm giao diện thì ta vào menu Form/ Chọn Preview:

Qt Designer sẽ hiển thị giao diện cho ta thử nghiệm, nó sẽ chạy đúng như vậy khi ta nạp vào mã lệnh Python trong Pycharm hoặc các tool desktop application khác.

Dưới đây là mã lệnh Python:

  • MainWindow.ui –> là file giao diện được tạo ra từ Qt Designer
  • MainWindow.py –> là file Python được Generate từ PyUIC (đã cung cấp ở bên trên)
  • MainWindowEx.py–> là file Python mà ta sẽ viết 1 lớp kế thừa từ MainWindow.py (UI_MainWindow), mục đích tách ra lớp kế thừa để bất cứ khi nào ta đổi giao diện và generate lại python thì không bị ảnh hưởng tới các mã lệnh mà ta đã viết trước đó.
  • MyApp.py –> file python khai báo để chạy dự án này

Dưới đây là mã lệnh của “MainWindowEx.py“:

from MainWindow import Ui_MainWindow

class MainWindowEx(Ui_MainWindow):
    def __init__(self):
        pass
    def setupUi(self, MainWindow):
        super().setupUi(MainWindow)
        self.MainWindow=MainWindow
    def show(self):
        self.MainWindow.show()

Như vậy, khi ta muốn bổ sung thêm bất kỳ mã lệnh nào để xử lý cho giao diện này thì chỉ nên viết trong “MainWindowEx.py“. Không nên viết trong file “MainWindow.py”, vì nếu viết trong “MainWindow.py” thì khi có nhu cầu thay đổi giao diện và generate lại code python sẽ làm mất các mã lệnh mới của mình trước đó.

Còn đây là mã lệnh của MyApp.py:

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 “MyApp.py” ta có kết quả như mong muốn.

QVBoxLayout còn nhiều thuộc tính và các tính năng khác, tuy nhiên chúng ta chỉ cần nắm tới đây là đủ để bố trí giao diện nằm đứng theo ý mình rồi.

Mã lệnh bài này các bạn tải ở đây:

https://www.mediafire.com/file/ma2kylupmdh175c/LearnQVBoxLayout.rar/file

Bài sau Tui sẽ trình bày về cách sử dụng QHBoxLayout để bố cục các control/widgets theo phương nằm ngang.

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