Python: Bố cục PyQt: Tạo các ứng dụng GUI chuyên nghiệp

Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực
Bố cục PyQt: Tạo các ứng dụng GUI chuyên nghiệp

Mục lục bài viết:


Trình quản lý bố cục của PyQt cung cấp một cách thức thân thiện và hiệu quả để sắp xếp các thành phần đồ họa hoặc tiện ích con trên GUI. Bố trí các widget đúng cách sẽ làm cho các ứng dụng GUI của bạn trông bóng bẩy và chuyên nghiệp. Học cách làm như vậy một cách hiệu quả và hiệu quả là kỹ năng cơ bản để bạn bắt đầu và chạy với phát triển ứng dụng GUI bằng Python và PyQt.

Trong hướng dẫn này, bạn sẽ học:

  • Lợi ích của việc sử dụng trình quản lý bố cục của PyQt là gì
  • Cách bố trí các widget trên GUI theo chương trình bằng cách sử dụng trình quản lý bố cục của PyQt
  • Cách chọn trình quản lý bố cục phù hợp cho ứng dụng GUI của bạn
  • Cách bố trí các widget trong các ứng dụng dựa trên cửa sổ chính và dựa trên hộp thoại

Với kiến ​​thức và kỹ năng này, bạn sẽ có thể sử dụng Python và PyQt để tạo các ứng dụng GUI có giao diện chuyên nghiệp.

Để hiểu rõ hơn về cách sử dụng trình quản lý bố cục, một số kiến ​​thức trước đây về cách tạo ứng dụng PyQt GUI và cách làm việc với các tiện ích PyQt sẽ hữu ích.

Bố cục các thành phần đồ họa trên GUI

Khi bạn đang tạo các ứng dụng giao diện người dùng đồ họa (Graphical User Interface - GUI), một vấn đề thường gặp là làm thế nào để có được các thành phần đồ họa của bạn - các nútmenu, thanh công cụnhãn, v.v. - được bố trí mạch lạc trên biểu mẫu và cửa sổ của bạn. Quá trình này được gọi là bố cục GUI và đây là một bước quan trọng trong việc tạo các ứng dụng GUI.

Trước đây, nếu bạn muốn bố trí các thành phần đồ họa hoặc tiện ích con (widget) trên một cửa sổ, thì bạn sẽ làm theo một trong các cách tiếp cận sau:

  1. Quyết định và đặt thủ công kích thước và vị trí tĩnh cho từng tiện ích con trên cửa sổ.
  2. Tính toán và đặt động kích thước và vị trí của từng tiện ích con.

Cách tiếp cận đầu tiên khá trực tiếp, nhưng nó có những nhược điểm cơ bản sau:

  • Các cửa sổ của bạn sẽ không thể thay đổi kích thước, điều này có thể gây ra sự cố khi hiển thị chúng trên các độ phân giải màn hình khác nhau.
  • Các nhãn của bạn có thể không hỗ trợ bản địa hóa đúng cách vì độ dài của một văn bản nhất định thay đổi giữa các ngôn ngữ.
  • Các widget của bạn sẽ hiển thị khác nhau trên các nền tảng khác nhau, điều này gây khó khăn cho việc viết các ứng dụng đa nền cho đẹp mắt.

Cách tiếp cận thứ hai linh hoạt hơn. Tuy nhiên, nó cũng có nhược điểm:

  • Bạn phải thực hiện rất nhiều phép tính thủ công để xác định đúng kích thước và vị trí của từng widget.
  • Bạn phải thực hiện một số phép tính bổ sung để phản hồi chính xác với việc thay đổi kích thước cửa sổ .
  • Bạn phải thực hiện lại tất cả các phép tính bất kỳ lúc nào bạn sửa đổi bố cục của cửa sổ.

Mặc dù bạn vẫn có thể sử dụng một trong hai cách tiếp cận này để bố trí GUI của mình, nhưng hầu hết thời gian bạn sẽ muốn sử dụng cách tiếp cận thứ ba và thuận tiện hơn được thực hiện bởi hầu hết các khung hoặc bộ công cụ GUI hiện đại: trình quản lý bố cục (layout manager).

Lưu ý: Trong một số framework GUI, chẳng hạn như Tkinter, trình quản lý bố cục còn được gọi là trình quản lý hình học.

Trình quản lý bố cục tự động sắp xếp các widget trên GUI theo nhu cầu cụ thể của bạn. Chúng tránh được những nhược điểm về khả năng tương thích của cách tiếp cận đầu tiên cũng như những tính toán phức tạp và khó chịu của cách tiếp cận thứ hai.

Trong các phần tiếp theo, bạn sẽ tìm hiểu về các trình quản lý bố cục tích hợp của PyQt và cách sử dụng chúng để bố trí hiệu quả các thành phần đồ họa của các ứng dụng GUI của bạn.

Thư viện bố cục PyQt

Trong PyQt, widget là các thành phần đồ họa mà bạn sử dụng làm khối xây dựng cho các ứng dụng GUI của mình. Khi bạn đặt một loạt các widget trên một cửa sổ để tạo GUI, bạn cần sắp xếp thứ tự cho chúng. Bạn cần đặt kích thước và vị trí của các tiện ích trên cửa sổ, đồng thời bạn cũng cần xác định hành vi của chúng khi người dùng thay đổi kích thước cửa sổ bên dưới.

Lưu ý: Thật không may, tài liệu chính thức của PyQt5 có một số phần chưa hoàn chỉnh. Để giải quyết vấn đề này, bạn có thể xem tài liệu PyQt4tài liệu Qt cho Python hoặc tài liệu Qt gốc.

Trong hướng dẫn này, bạn sẽ thấy rằng hầu hết các liên kết sẽ đưa bạn đến tài liệu Qt gốc, đây là nguồn thông tin tốt hơn trong hầu hết các trường hợp.

Để sắp xếp các tiện ích con trên các cửa sổ hoặc biểu mẫu trong PyQt, bạn có thể sử dụng các kỹ thuật sau:

  • Sử dụng .resize() và .move() trên các widget của bạn để cung cấp kích thước và vị trí tuyệt đối.
  • Hoàn thiện .resizeEvent() và tính toán động kích thước và vị trí của các widget của bạn.
  • Sử dụng trình quản lý bố cục và để nó thực hiện tất cả các tính toán và công việc khó khăn cho bạn.

Các kỹ thuật này thường tương ứng với ba cách tiếp cận khác nhau để tạo ra một GUI mà bạn đã thấy trong phần trên.

Một lần nữa, tính toán kích thước và vị trí động có thể là một cách tiếp cận tốt, nhưng hầu hết thời gian, bạn sẽ tốt hơn khi sử dụng trình quản lý bố cục. Trong PyQt, trình quản lý bố cục là các lớp cung cấp chức năng cần thiết để tự động quản lý kích thước, vị trí và hành vi thay đổi kích thước của các tiện ích con trong bố cục.

Với trình quản lý bố cục, bạn có thể tự động sắp xếp các widget con trong bất kỳ widget cha hoặc vùng chứa, widget nào. Sử dụng trình quản lý bố cục sẽ đảm bảo rằng bạn tận dụng tốt không gian có sẵn trên GUI và ứng dụng của bạn vẫn có thể sử dụng được khi người dùng thay đổi kích thước cửa sổ.

Trình quản lý bố cục hoạt động như các vùng chứa cho cả tiện ích con và các bố cục khác. Để thêm tiện ích con vào trình quản lý bố cục, bạn gọi .addWidget(). Để thêm một bố cục vào một bố cục khác, bạn gọi .addLayout(). Bạn sẽ đi sâu hơn vào các bố cục lồng nhau trong phần Lồng các bố cục để xây dựng GUI phức tạp.

Khi bạn đã thêm tất cả các tiện ích cần thiết vào trình quản lý bố cục, bạn đặt trình quản lý bố cục trên một tiện ích cụ thể bằng cách sử dụng .setLayout(). Bạn có thể đặt trình quản lý bố cục trên bất kỳ lớp con nào của QWidget, bao gồm cửa sổ hoặc biểu mẫu.

Lưu ý: QMainWindow là một lớp PyQt mà bạn có thể sử dụng để tạo các ứng dụng kiểu window chính. Lớp này có trình quản lý bố cục tích hợp riêng của nó . Vì vậy, nếu bạn đang sử dụng QMainWindow, thì bạn thường không cần đặt trình quản lý bố cục trên các đối tượng cửa sổ chính của mình.

Tất cả các tiện ích con trong một bố cục được tự động đặt làm con của tiện ích con mà bạn cài đặt bố cục, không phải của chính bố cục đó. Đó là bởi vì các widget chỉ có thể có các widget khác, không phải bố cục, làm cha của chúng.

Trình quản lý bố cục của PyQt cung cấp một số tính năng thú vị giúp bạn dễ dàng hơn rất nhiều khi tạo các ứng dụng GUI đẹp mắt:

  • Xử lý kích thước và vị trí của các widget mà không cần bất kỳ phép tính nào
  • Xử lý việc thay đổi kích thước và định vị lại tiện ích con khi người dùng thay đổi kích thước cửa sổ bên dưới
  • Thay đổi kích thước nhãn để hỗ trợ quốc tế hóa tốt hơn
  • Cung cấp bố cục cửa sổ gốc cho các ứng dụng đa nền

Sử dụng trình quản lý bố cục cũng sẽ làm tăng đáng kể năng suất của bạn và cải thiện khả năng bảo trì mã của bạn trong thời gian dài.

PyQt cung cấp bốn lớp trình quản lý bố cục có mục đích chung:

  1. QHBoxLayout sắp xếp các tiện ích trong một hộp nằm ngang.
  2. QVBoxLayout sắp xếp các tiện ích trong một hộp dọc.
  3. QGridLayout sắp xếp các tiện ích trong một lưới.
  4. QFormLayout sắp xếp các tiện ích trong hai cột.

Trong một vài phần tiếp theo, bạn sẽ tìm hiểu những kiến ​​thức cơ bản về cách sử dụng các trình quản lý bố cục có mục đích chung này.

Sử dụng trình quản lý bố cục có mục đích chung

Khi tạo các ứng dụng GUI với PyQt, bạn sẽ thường sử dụng một hoặc nhiều trong số bốn bố cục có mục đích chung mà bạn đã thấy ở cuối phần trên để bố trí tiện ích con trên các cửa sổ và biểu mẫu của bạn.

Trong một vài phần tiếp theo, bạn sẽ học cách tạo và sử dụng bốn trình quản lý bố cục có mục đích chung với sự trợ giúp của một số ví dụ.

Xây dựng bố cục ngang: QHBoxLayout

Trình quản lý bố cục hộp lấy không gian nó nhận được từ bố cục hoặc tiện ích con, chia nó thành một số box hoặc ô và làm cho mỗi tiện ích con trong bố cục lấp đầy một box.

QHBoxLayout là một trong hai bố cục box (hộp) có sẵn trong PyQt. Trình quản lý bố cục này cho phép bạn sắp xếp các widget theo chiều ngang, widget này cạnh widget kia. Các widget được thêm vào bố cục từ trái sang phải. Điều này có nghĩa là tiện ích con mà bạn thêm vào đầu tiên trong mã của mình sẽ là tiện ích con ngoài cùng bên trái trong bố cục.

Để thêm widget vào một đối tượng QHBoxLayout, bạn gọi .addWidget(widget, stretch, alignment) trên đối tượng bố cục. Phương thức này nhận một đối số bắt buộc và hai đối số tùy chọn:

  1. widget là một đối số bắt buộc chứa tiện ích cụ thể mà bạn muốn thêm vào bố cục.
  2. stretch là một đối số tùy chọn chứa một số nguyên đại diện cho hệ số giãn để áp dụng widget. Các tiện ích có hệ số giãn cao hơn sẽ phát triển nhiều hơn khi thay đổi kích thước cửa sổ. Nó mặc định là 0, có nghĩa là tiện ích con không được chỉ định hệ số giãn.
  3. alignment là một đối số tùy chọn chứa các cờ ngang và dọc. Bạn có thể kết hợp các cờ này để tạo ra sự liên kết mong muốn của tiện ích con bên trong ô chứa nó. Nó mặc định là 0, có nghĩa là tiện ích sẽ lấp đầy toàn bộ ô.

Dưới đây là một ứng dụng nhỏ hướng dẫn cách tạo bố cục ngang bằng cách sử dụng QHBoxLayout. Trong ví dụ này, bạn sẽ sử dụng các đối tượng QPushButton để hình dung rõ hơn vị trí mỗi tiện ích con sẽ được đặt trong bố cục theo thứ tự mà bạn thêm các tiện ích con vào mã của mình:

 1import sys
 2
 3from PyQt5.QtWidgets import (
 4    QApplication,
 5    QHBoxLayout,
 6    QPushButton,
 7    QWidget,
 8)
 9
10class Window(QWidget):
11    def __init__(self):
12        super().__init__()
13        self.setWindowTitle("QHBoxLayout Example")
14        # Create a QHBoxLayout instance
15        layout = QHBoxLayout()
16        # Add widgets to the layout
17        layout.addWidget(QPushButton("Left-Most"))
18        layout.addWidget(QPushButton("Center"), 1)
19        layout.addWidget(QPushButton("Right-Most"), 2)
20        # Set the layout on the application's window
21        self.setLayout(layout)
22        print(self.children())
23
24if __name__ == "__main__":
25    app = QApplication(sys.argv)
26    window = Window()
27    window.show()
28    sys.exit(app.exec_())

Giải thích:

Trong đoạn code trên, dòng 15 bạn tạo một đối tượng QHBoxLayout gọi là layout. Trên các dòng từ 17 đến 19, bạn thêm ba nút vào layout sử dụng .addWidget(). Lưu ý rằng bạn truyền 1 và 2 đến tham số stretch trong các nút Center và Right-Mode tương ứng. Trên dòng 21, bạn đặt layout làm bố cục cấp cao nhất của cửa sổ bằng cách sử dụng .setLayout().

Lưu ý: Nếu bạn là người mới lập trình GUI với PyQt, thì bạn có thể xem qua Python và PyQt: Xây dựng Máy tính để bàn GUI để hiểu rõ hơn về cách tạo ứng dụng GUI với PyQt.

Nếu bạn chạy ứng dụng này, thì bạn sẽ thấy cửa sổ sau trên màn hình của mình:

Ví dụ về QHBoxLayout

Cửa sổ này chứa ba nút được sắp xếp theo chiều ngang. Lưu ý rằng nút Ngoài cùng bên trái tương ứng với nút đầu tiên bạn thêm vào mã của mình. Vì vậy, các nút được hiển thị theo cùng thứ tự (từ trái sang phải) mà bạn thêm chúng vào mã của mình (từ trên xuống dưới).

Các nút Center và Right-Mode có yếu tố giãn khác nhau, do đó chúng mở rộng tương ứng với những yếu tố này khi bạn thay đổi kích thước cửa sổ.

Ngoài ra, tất cả các nút trong layout và bản thân bố cục được đặt là con của Window. Điều này được thực hiện tự động bởi đối tượng bố cục, đối tượng này gọi nội bộ .setParent() trên mỗi tiện ích con. Lời gọi đến print() ở dòng 22 in ra một danh sách các con của Window trên thiết bị đầu cuối của bạn làm bằng chứng về hành vi này.

Xây dựng bố cục dọc: QVBoxLayout

QVBoxLayout sắp xếp các widget theo chiều dọc, widget này bên dưới widget kia. Bạn có thể sử dụng lớp này để tạo bố cục dọc và sắp xếp các widget của mình từ trên xuống dưới. Vì QVBoxLayout là một bố cục hộp khác, nên phương thức .addWidget() của nó hoạt động giống như trong QHBoxLayout.

Dưới đây là một ứng dụng PyQt chỉ ra cách tạo và sử dụng một đối tượng QVBoxLayout để tạo các sắp xếp theo chiều dọc của các tiện ích con trong GUI của bạn:

 1import sys
 2
 3from PyQt5.QtWidgets import (
 4    QApplication,
 5    QPushButton,
 6    QVBoxLayout,
 7    QWidget,
 8)
 9
10class Window(QWidget):
11    def __init__(self):
12        super().__init__()
13        self.setWindowTitle("QVBoxLayout Example")
14        self.resize(270, 110)
15        # Create a QVBoxLayout instance
16        layout = QVBoxLayout()
17        # Add widgets to the layout
18        layout.addWidget(QPushButton("Top"))
19        layout.addWidget(QPushButton("Center"))
20        layout.addWidget(QPushButton("Bottom"))
21        # Set the layout on the application's window
22        self.setLayout(layout)
23
24if __name__ == "__main__":
25    app = QApplication(sys.argv)
26    window = Window()
27    window.show()
28    sys.exit(app.exec_())

Giải thích:

Trong ví dụ trên, dòng 16 bạn tạo một phiên bản của QVBoxLayout. Trên các dòng từ 18 đến 20, bạn thêm ba nút vào layout. Cuối cùng, bạn đặt layout làm bố cục cấp cao nhất của cửa sổ.

Nếu bạn chạy ứng dụng này, thì bạn sẽ nhận được cửa sổ dạng như sau:

Ví dụ về QVBoxLayout

Cửa sổ của bạn hiển thị ba nút theo chiều dọc, nút này bên dưới nút kia. Các nút xuất hiện theo cùng một thứ tự (từ trên xuống dưới) khi bạn thêm chúng vào mã của mình (từ trên xuống dưới).

Sắp xếp các widget trong một lưới: QGridLayout

Bạn có thể sử dụng QGridLayout để sắp xếp các widget trong một lưới các hàng và cột. Mỗi widget sẽ có một vị trí tương đối trong lưới. Để xác định vị trí của tiện ích con hoặc ô trong lưới, bạn sử dụng một cặp tọa độ của biểu mẫu (row, column). Các tọa độ này phải là số nguyên.

QGridLayout lấy không gian có sẵn trên cha của nó, chia nó thành các hàng và cột và đặt mỗi tiện ích con vào ô hoặc hộp riêng của nó. QGridLayout tự động tính toán bố cục cuối cùng sẽ có bao nhiêu hàng và cột tùy thuộc vào số lượng tiện ích con và tọa độ của chúng. Nếu bạn không thêm tiện ích con vào một ô nhất định, thì ô đó sẽ trống.

Để thêm các widget vào bố cục lưới, bạn gọi .addWidget() trên bố cục. Phương thức này có hai cách triển khai nạp chồng khác nhau:

  1. addWidget(widget, row, column, alignment) thêm widget vào ô tại (rowcolumn).
  2. addWidget(widget, fromRow, fromColumn, rowSpan, columnSpan, alignment) thêm widget vào ô, mở rộng nhiều hàng, cột hoặc cả hai.

Việc triển khai đầu tiên có các đối số sau:

  1. widget là một đối số bắt buộc chứa tiện ích cụ thể mà bạn cần thêm vào bố cục.
  2. row là một đối số bắt buộc chứa một số nguyên đại diện cho tọa độ của một hàng trong lưới.
  3. column là một đối số bắt buộc chứa một số nguyên đại diện cho tọa độ của một cột trong lưới.
  4. alignment là một đối số tùy chọn giữ căn chỉnh của tiện ích con bên trong ô chứa nó. Nó mặc định là 0, có nghĩa là tiện ích sẽ lấp đầy toàn bộ ô.

Dưới đây là một ví dụ về cách sử dụng QGridLayout để tạo một lưới các tiện ích con:

 1import sys
 2
 3from PyQt5.QtWidgets import (
 4    QApplication,
 5    QGridLayout,
 6    QPushButton,
 7    QWidget,
 8)
 9
10class Window(QWidget):
11    def __init__(self):
12        super().__init__()
13        self.setWindowTitle("QGridLayout Example")
14        # Create a QGridLayout instance
15        layout = QGridLayout()
16        # Add widgets to the layout
17        layout.addWidget(QPushButton("Button at (0, 0)"), 0, 0)
18        layout.addWidget(QPushButton("Button at (0, 1)"), 0, 1)
19        layout.addWidget(QPushButton("Button at (0, 2)"), 0, 2)
20        layout.addWidget(QPushButton("Button at (1, 0)"), 1, 0)
21        layout.addWidget(QPushButton("Button at (1, 1)"), 1, 1)
22        layout.addWidget(QPushButton("Button at (1, 2)"), 1, 2)
23        layout.addWidget(QPushButton("Button at (2, 0)"), 2, 0)
24        layout.addWidget(QPushButton("Button at (2, 1)"), 2, 1)
25        layout.addWidget(QPushButton("Button at (2, 2)"), 2, 2)
26        # Set the layout on the application's window
27        self.setLayout(layout)
28
29if __name__ == "__main__":
30    app = QApplication(sys.argv)
31    window = Window()
32    window.show()
33    sys.exit(app.exec_())

Giải thích:

Trong ví dụ trên, dòng 15 bạn tạo đối tượng QGridLayout. Sau đó, trên các dòng từ 17 đến 25, bạn thêm các widget vào bố cục bằng cách sử dụng .addWidget(). Để xem cách bố trí lưới quản lý các ô mà không có tiện ích con được chỉ định, bạn hãy rào (comment) một hoặc nhiều dòng này và chạy lại ứng dụng.

Nếu bạn chạy mã này từ dòng lệnh của mình, thì bạn sẽ nhận được một cửa sổ dạng như sau:

Ví dụ về QGridLayout

Mỗi tiện ích con trong đối tượng QGridLayout chiếm ô được xác định bởi cặp tọa độ mà bạn cung cấp trong .addWidget(). Văn bản trên mỗi nút phản ánh các tọa độ đó. Các tọa độ dựa trên 0, vì vậy ô đầu tiên là (0, 0).

Trong lần triển khai thứ hai của .addWidget(), các đối số widget và alignment giữ nguyên, và bạn có bốn đối số bổ sung cho phép bạn đặt tiện ích con trên một số hàng hoặc cột:

  1. fromRow lấy một số nguyên đại diện cho hàng mà tiện ích con sẽ bắt đầu.
  2. fromColumn lấy một số nguyên đại diện cho cột mà tiện ích con sẽ bắt đầu.
  3. rowSpan lấy một số nguyên đại diện cho số hàng mà tiện ích con sẽ chiếm trong lưới.
  4. columnSpan lấy một số nguyên đại diện cho số cột mà tiện ích con sẽ chiếm trong lưới.

Đây là một ứng dụng hiển thị cách .addWidget() hoạt động của biến thể này:

 1import sys
 2
 3from PyQt5.QtWidgets import (
 4    QApplication,
 5    QGridLayout,
 6    QPushButton,
 7    QWidget,
 8)
 9
10class Window(QWidget):
11    def __init__(self):
12        super().__init__()
13        self.setWindowTitle("QGridLayout Example")
14        # Create a QGridLayout instance
15        layout = QGridLayout()
16        # Add widgets to the layout
17        layout.addWidget(QPushButton("Button at (0, 0)"), 0, 0)
18        layout.addWidget(QPushButton("Button at (0, 1)"), 0, 1)
19        layout.addWidget(QPushButton("Button Spans two Cols"), 1, 0, 1, 2)
20        # Set the layout on the application's window
21        self.setLayout(layout)
22
23if __name__ == "__main__":
24    app = QApplication(sys.argv)
25    window = Window()
26    window.show()
27    sys.exit(app.exec_())

Giải thích:

Trong đoạn code trên, dòng 19 bạn sử dụng cách triển khai thứ hai của .addWidget() để thêm một nút chiếm hai cột trong lưới. Nút bắt đầu ở hàng thứ hai (fromRow=1) và ở cột đầu tiên (fromColumn=0). Cuối cùng, nút chiếm một hàng (rowSpan=1) và hai cột (columnSpan=2).

Lưu ý: Vì PyQt là một liên kết Python cho Qt, mà Qt là một tập hợp các thư viện C++, nên đôi khi bạn không thể sử dụng các đối số từ khóa khi gọi các phương thức PyQt. Các đối số từ khóa được sử dụng trong đoạn trên có mục đích duy nhất là hiển thị giá trị nào được gán cho mỗi đối số.

Dưới đây là cửa sổ mà bạn sẽ thấy trên màn hình của mình nếu bạn chạy ứng dụng này:

Ví dụ về khoảng cách QGridLayout

Trong kiểu bố trí này, bạn có thể tạo một widget chiếm nhiều ô, giống như cách bạn đã làm với nút Button Spans two Cols.

Tạo biểu mẫu nhanh chóng: QFormLayout

Nếu bạn liên tục tạo biểu mẫu để thực hiện các hành động như nhập dữ liệu vào cơ sở dữ liệu, thì ta sẽ sử dụng QFormLayout. Lớp này sắp xếp các widget theo bố cục hai cột. Cột đầu tiên thường sẽ hiển thị một nhãn mô tả đầu vào dự định, và cột thứ hai thường chứa các widget đầu vào như QLineEditQComboBox hoặc QSpinBox cho phép người dùng nhập vào hoặc chỉnh sửa dữ liệu.

Để thêm tiện ích con vào bố cục biểu mẫu, bạn sử dụng .addRow(). Phương thức này có một số biến thể nhưng thường sẽ chọn trong số hai cách sau:

  1. .addRow(label, field) thêm một hàng mới vào cuối bố cục biểu mẫu. Hàng phải chứa một đối tượng QLabel (label) và một tiện ích con đầu vào (field).
  2. .addRow(labelText, field) tự động tạo và thêm một đối tượng QLabel mới với labelText làm văn bản của nó. field giữ một widget đầu vào.

Dưới đây là một ứng dụng mẫu sử dụng một đối tượng QFormLayout để sắp xếp các widget:

 1import sys
 2
 3from PyQt5.QtWidgets import (
 4    QApplication,
 5    QFormLayout,
 6    QLabel,
 7    QLineEdit,
 8    QWidget,
 9)
10
11class Window(QWidget):
12    def __init__(self):
13        super().__init__()
14        self.setWindowTitle("QFormLayout Example")
15        self.resize(270, 110)
16        # Create a QFormLayout instance
17        layout = QFormLayout()
18        # Add widgets to the layout
19        layout.addRow("Name:", QLineEdit())
20        layout.addRow("Job:", QLineEdit())
21        emailLabel = QLabel("Email:")
22        layout.addRow(emailLabel, QLineEdit())
23        # Set the layout on the application's window
24        self.setLayout(layout)
25
26if __name__ == "__main__":
27    app = QApplication(sys.argv)
28    window = Window()
29    window.show()
30    sys.exit(app.exec_())

Giải thích:

Trong đoạn code trên, dòng 17 bạn tạo một đối tượng QFormLayout. Sau đó, trên các dòng từ 19 đến 22, bạn thêm một số hàng vào bố cục. Lưu ý rằng trên dòng 19 và 20, bạn sử dụng đối tượng thứ hai của phương thức và trên dòng 22, bạn sử dụng đối tượng đầu tiên, truyền một đối tượng QLabel làm đối số đầu tiên tới .addRow().

Kết quả bạn sẽ thấy cửa sổ sau trên màn hình của mình:

Ví dụ về QFormLayout

Với mỗi QFormLayout, bạn có thể sắp xếp các widget của mình theo cách sắp xếp hai cột. Cột đầu tiên chứa các nhãn yêu cầu người dùng cung cấp một số thông tin. Cột thứ hai hiển thị các widget cho phép người dùng nhập hoặc chỉnh sửa thông tin đó.

Lồng bố cục để xây dựng GUI phức tạp

Bạn có thể sử dụng các bố cục lồng nhau để tạo các GUI phức tạp mà khó tạo bằng một trong các trình quản lý bố cục của PyQt có mục đích chung. Để làm điều đó, bạn cần phải gọi .addLayout() trên một bố cục bên ngoài. Bằng cách này, bố cục bên trong trở thành con của bố cục bên ngoài.

Giả sử bạn cần tạo hộp thoại hiển thị nhãn và chỉnh sửa dòng trong bố cục biểu mẫu và bên dưới các tiện ích con đó, bạn muốn đặt một số hộp kiểm trong bố cục dọc. Đây là bản mô phỏng hộp thoại của bạn:

Sơ đồ bố cục lồng nhau

Hình chữ nhật màu xanh lam đại diện cho bố cục bên ngoài của bạn. Hình chữ nhật màu xanh lá cây là bố cục biểu mẫu sẽ chứa nhãn và chỉnh sửa dòng. Hình chữ nhật màu đỏ là bố cục dọc để chứa các hộp kiểm tùy chọn. Cả bố cục màu xanh lá cây và bố cục màu đỏ đều được lồng vào bố cục màu xanh lam, đó là bố cục dọc.

Dưới đây là một ví dụ về cách tạo bố cục này bằng PyQt:

 1import sys
 2
 3from PyQt5.QtWidgets import (
 4    QApplication,
 5    QCheckBox,
 6    QFormLayout,
 7    QLineEdit,
 8    QVBoxLayout,
 9    QWidget,
10)
11
12class Window(QWidget):
13    def __init__(self):
14        super().__init__()
15        self.setWindowTitle("Nested Layouts Example")
16        # Create an outer layout
17        outerLayout = QVBoxLayout()
18        # Create a form layout for the label and line edit
19        topLayout = QFormLayout()
20        # Add a label and a line edit to the form layout
21        topLayout.addRow("Some Text:", QLineEdit())
22        # Create a layout for the checkboxes
23        optionsLayout = QVBoxLayout()
24        # Add some checkboxes to the layout
25        optionsLayout.addWidget(QCheckBox("Option one"))
26        optionsLayout.addWidget(QCheckBox("Option two"))
27        optionsLayout.addWidget(QCheckBox("Option three"))
28        # Nest the inner layouts into the outer layout
29        outerLayout.addLayout(topLayout)
30        outerLayout.addLayout(optionsLayout)
31        # Set the window's main layout
32        self.setLayout(outerLayout)
33
34if __name__ == "__main__":
35    app = QApplication(sys.argv)
36    window = Window()
37    window.show()
38    sys.exit(app.exec_())

Trong đoạn code trên:

  • Trên dòng 17, bạn tạo bố cục bên ngoài hoặc cấp cao nhất, mà bạn sẽ sử dụng làm bố cục cha và làm bố cục chính của cửa sổ. Trong trường hợp này, bạn sử dụng QVBoxLayout vì bạn muốn các widget được sắp xếp theo chiều dọc trên biểu mẫu của bạn. Trong mô hình của bạn, đây là bố cục màu xanh lam.
  • Trên dòng 19, bạn tạo bố cục biểu mẫu để giữ nhãn và chỉnh sửa dòng.
  • Trên dòng 21, bạn thêm các widget cần thiết vào bố cục. Điều này tương đương với bố cục xanh lá của bạn.
  • Trên dòng 23, bạn tạo một bố cục dọc để chứa các hộp kiểm.
  • Trên các dòng từ 25 đến 27, bạn thêm các hộp kiểm bắt buộc. Đây là bố cục màu đỏ của bạn.
  • Trên dòng 29 và 30, bạn lồng topLayout và optionsLayout dưới outerLayout.

Kết quả bạn sẽ thấy một cửa sổ như sau:

Ví dụ về bố cục lồng nhau

Trong ứng dụng này, bạn lồng hai bố cục khác nhau dưới một bố cục bên ngoài để tạo bố cục chung cho cửa sổ của bạn. Ở đầu cửa sổ, bạn sử dụng bố cục ngang để đặt nhãn và chỉnh sửa dòng. Sau đó, bạn đặt một số hộp kiểm bên dưới bằng cách sử dụng bố cục dọc.

Sử dụng Bố cục và Tiện ích Đa trang

Cho đến nay, bạn đã thấy cách sử dụng trình quản lý bố cục truyền thống hoặc mục đích chung để sắp xếp các widget trong cửa sổ ứng dụng của mình. Các trình quản lý bố cục này sẽ sắp xếp các widget trên bố cục một trang. Nói cách khác, GUI của bạn sẽ luôn hiển thị cùng một bộ tiện ích cho người dùng.

Đôi khi bạn cần tạo một bố cục hiển thị một tập hợp các tiện ích con khác nhau để đáp ứng các hành động nhất định của người dùng trên GUI. Ví dụ: nếu bạn đang tạo hộp thoại tùy chọn cho một ứng dụng nhất định, thì bạn có thể muốn giới thiệu cho người dùng bố cục dựa trên tab hoặc nhiều trang, trong đó mỗi tab hoặc trang chứa một tập hợp các tùy chọn có liên quan chặt chẽ khác nhau. Mỗi khi người dùng nhấp vào một tab hoặc trang, ứng dụng sẽ hiển thị một tập hợp các tiện ích con khác nhau.

PyQt cung cấp một bố cục dựng sẵn gọi là QStackedLayout và một số tiện ích con tiện lợi như QTabWidget sẽ cho phép bạn tạo loại bố cục nhiều trang này. Một số phần tiếp theo sẽ hướng dẫn bạn một số công cụ này.

Tạo một ngăng xếp các widget

QStackedLayout cung cấp một trình quản lý bố cục cho phép bạn sắp xếp các widget của mình trên một ngăn xếp chồng lên nhau. Trong loại bố cục này, chỉ một tiện ích con được hiển thị tại một thời điểm nhất định.

Để điền một bố cục xếp chồng lên nhau bằng các widget, bạn cần gọi .addWidget() trên đối tượng bố cục. Thao tác này sẽ thêm từng tiện ích con vào cuối danh sách tiện ích con bên trong của bố cục. Bạn cũng có thể chèn hoặc xóa một widget tại một vị trí nhất định trong danh sách các widget bằng cách sử dụng .insertWidget(index) hoặc .removeWidget(widget) tương ứng.

Mỗi widget trong danh sách widget được hiển thị như một trang độc lập. Nếu bạn muốn hiển thị một số tiện ích con trên một trang, thì hãy sử dụng một đối tượng QWidget cho mỗi trang và đặt bố cục các tiện ích con thích hợp cho tiện ích con trang. Nếu bạn cần lấy tổng số widget (trang) trong bố cục, thì bạn có thể gọi .count().

Một điểm quan trọng cần ghi nhớ khi làm việc với các đối tượng QStackedLayout là bạn cần cung cấp cơ chế chuyển đổi giữa các trang một cách rõ ràng. Nếu không, bố cục của bạn sẽ luôn hiển thị cùng một trang cho người dùng. Để chuyển đổi giữa các trang, bạn cần gọi đối tượng bố cục .setCurrentIndex().

Dưới đây là một ví dụ cho thấy cách sử dụng bố cục xếp chồng với hộp tổ hợp để chuyển đổi giữa các trang:

 1import sys
 2
 3from PyQt5.QtWidgets import (
 4    QApplication,
 5    QComboBox,
 6    QFormLayout,
 7    QLineEdit,
 8    QStackedLayout,
 9    QVBoxLayout,
10    QWidget,
11)
12
13class Window(QWidget):
14    def __init__(self):
15        super().__init__()
16        self.setWindowTitle("QStackedLayout Example")
17        # Create a top-level layout
18        layout = QVBoxLayout()
19        self.setLayout(layout)
20        # Create and connect the combo box to switch between pages
21        self.pageCombo = QComboBox()
22        self.pageCombo.addItems(["Page 1", "Page 2"])
23        self.pageCombo.activated.connect(self.switchPage)
24        # Create the stacked layout
25        self.stackedLayout = QStackedLayout()
26        # Create the first page
27        self.page1 = QWidget()
28        self.page1Layout = QFormLayout()
29        self.page1Layout.addRow("Name:", QLineEdit())
30        self.page1Layout.addRow("Address:", QLineEdit())
31        self.page1.setLayout(self.page1Layout)
32        self.stackedLayout.addWidget(self.page1)
33        # Create the second page
34        self.page2 = QWidget()
35        self.page2Layout = QFormLayout()
36        self.page2Layout.addRow("Job:", QLineEdit())
37        self.page2Layout.addRow("Department:", QLineEdit())
38        self.page2.setLayout(self.page2Layout)
39        self.stackedLayout.addWidget(self.page2)
40        # Add the combo box and the stacked layout to the top-level layout
41        layout.addWidget(self.pageCombo)
42        layout.addLayout(self.stackedLayout)
43
44    def switchPage(self):
45        self.stackedLayout.setCurrentIndex(self.pageCombo.currentIndex())
46
47if __name__ == "__main__":
48    app = QApplication(sys.argv)
49    window = Window()
50    window.show()
51    sys.exit(app.exec_())

Giải thích:

Trên các dòng từ 21 đến 23, bạn tạo một đối tượng QComboBox cho phép bạn chuyển đổi giữa các trang trong bố cục. Sau đó, bạn thêm hai tùy chọn vào hộp tổ hợp trong danh sách và kết nối nó với .switchPage(), mục đích để xử lý việc chuyển trang.

Bên trong .switchPage(), bạn gọi đối tượng bố cục .setCurrentIndex(), truyền chỉ mục hiện thời của hộp tổ hợp làm đối số. Bằng cách này, khi người dùng thay đổi tùy chọn trong hộp tổ hợp, trang trên bố cục xếp chồng sẽ thay đổi tương ứng.

Trên dòng 25, bạn tạo đối tượng QStackedLayout. Trên các dòng 27 đến 32, bạn thêm trang đầu tiên vào bố cục và trên các dòng 34 đến 39, bạn thêm trang thứ hai. Mỗi trang được thể hiện bằng một đối tượng QWidget chứa một số tiện ích con trong một bố cục thuận tiện.

Bước cuối cùng để mọi thứ hoạt động là thêm hộp tổ hợp và bố cục vào bố cục chính của ứng dụng.

Kết quả:

Ví dụ về QStackedLayout

Trong trường hợp này, bạn có hai trang trong bố cục ứng dụng của mình. Mỗi trang được đại diện bởi một đối tượng QWidget. Khi bạn chọn một trang mới trong hộp tổ hợp trên đầu cửa sổ, bố cục sẽ thay đổi để hiển thị trang đã chọn.

Lưu ý: PyQt cung cấp một lớp thuận tiện gọi là QStackedWidget, được xây dựng trên lớp QStackedLayout. Bạn cũng có thể sử dụng lớp này để tạo bố cục nhiều trang.

Lớp này cung cấp một chồng (stack) các widget trong đó chỉ một widget được hiển thị tại một thời điểm. Cũng giống như bố cục xếp chồng lên nhau, QStackedWidget không cung cấp cơ chế nội tại để chuyển đổi giữa các trang.

Bên cạnh bố cục xếp chồng và tiện ích con xếp chồng lên nhau, bạn có thể sử dụng QTabWidget để tạo giao diện người dùng nhiều trang. Bạn sẽ tìm hiểu cách thực hiện trong phần tiếp theo.

Sử dụng các tiện ích tab của PyQt

Một cách phổ biến khác để tạo sắp xếp nhiều trang trong PyQt là sử dụng một lớp được gọi là QTabWidget. Lớp này cung cấp một thanh tab và một vùng trang. Bạn sử dụng thanh tab để chuyển đổi giữa các trang và vùng trang để hiển thị trang được liên kết với tab đã chọn.

Theo mặc định, thanh tab nằm ở đầu khu vực trang. Tuy nhiên, bạn có thể thay đổi hành vi này bằng cách sử dụng .setTabPosition() và một trong bốn vị trí tab có thể có là:

Vị trí tab Vị trí thanh tab
QTabWidget.North Đầu trang
QTabWidget.South Cuối trang
QTabWidget.West Bên trái của các trang
QTabWidget.East Bên phải của các trang

Để thêm các tab vào một tiện ích tab, bạn sử dụng .addTab(). Phương thức này có hai biến thể hoặc triển khai quá tải:

  1. .addTab(page, label)
  2. .addTab(page, icon, label)

Trong cả hai trường hợp thì phương thức đều thêm một tab mới, với label làm tiêu đề của tab. page cần phải là một tiện ích con đại diện cho trang được liên kết với tab hiện tại.

Trong biến thể thứ hai của phương thức, icon cần phải là một đối tượng QIcon. Nếu bạn truyền một biểu tượng đến .addTab(), thì biểu tượng đó sẽ được hiển thị ở bên trái tiêu đề của tab.

Một thực tế phổ biến khi tạo tiện ích tab là sử dụng một đối tượng QWidget cho mỗi trang. Bằng cách này, bạn sẽ có thể thêm các tiện ích bổ sung vào trang bằng cách sử dụng bố cục chứa các tiện ích cần thiết.

Trong đa số tình huống, bạn sẽ sử dụng các tiện ích tab để tạo hộp thoại cho các ứng dụng GUI của mình. Kiểu bố trí này cho phép bạn trình bày cho người dùng một số tùy chọn trong một không gian tương đối nhỏ. Bạn cũng có thể tận dụng hệ thống tab để sắp xếp các tùy chọn của mình theo một số tiêu chí phân loại.

Đây là một ứng dụng mẫu hiển thị những điều cơ bản về cách tạo và sử dụng một đối tượng QTabWidget:

 1import sys
 2
 3from PyQt5.QtWidgets import (
 4    QApplication,
 5    QCheckBox,
 6    QTabWidget,
 7    QVBoxLayout,
 8    QWidget,
 9)
10
11class Window(QWidget):
12    def __init__(self):
13        super().__init__()
14        self.setWindowTitle("QTabWidget Example")
15        self.resize(270, 110)
16        # Create a top-level layout
17        layout = QVBoxLayout()
18        self.setLayout(layout)
19        # Create the tab widget with two tabs
20        tabs = QTabWidget()
21        tabs.addTab(self.generalTabUI(), "General")
22        tabs.addTab(self.networkTabUI(), "Network")
23        layout.addWidget(tabs)
24
25    def generalTabUI(self):
26        """Create the General page UI."""
27        generalTab = QWidget()
28        layout = QVBoxLayout()
29        layout.addWidget(QCheckBox("General Option 1"))
30        layout.addWidget(QCheckBox("General Option 2"))
31        generalTab.setLayout(layout)
32        return generalTab
33
34    def networkTabUI(self):
35        """Create the Network page UI."""
36        networkTab = QWidget()
37        layout = QVBoxLayout()
38        layout.addWidget(QCheckBox("Network Option 1"))
39        layout.addWidget(QCheckBox("Network Option 2"))
40        networkTab.setLayout(layout)
41        return networkTab
42
43if __name__ == "__main__":
44    app = QApplication(sys.argv)
45    window = Window()
46    window.show()
47    sys.exit(app.exec_())

Giải thích:

Trong ví dụ này, bạn sử dụng tiện ích tab để giới thiệu cho người dùng hộp thoại ngắn gọn hiển thị các tùy chọn liên quan đến phần General và Network của menu tùy chọn giả định. Trên dòng 20, bạn tạo đối tượng QTabWidget. Sau đó, bạn thêm hai tab vào tiện ích tab bằng cách sử dụng .addTab().

Trong .generalTabUI() và networkTabUI(), bạn tạo GUI cụ thể cho từng tab. Để làm điều này, bạn sử dụng một đối tượng QWidget, một đối tượng QVBoxLayout và một số hộp kiểm để giữ các tùy chọn.

Kết quả là bạn sẽ nhận được hộp thoại sau trên màn hình của mình:

Ví dụ về QTabWidget

Như vậy là bạn đã có một GUI dựa trên tab đầy đủ chức năng. Lưu ý rằng để chuyển đổi giữa các trang, bạn chỉ cần nhấp vào tab tương ứng.

Bố cục cửa sổ chính của ứng dụng

Nếu bạn đang sử dụng PyQt để tạo các ứng dụng GUI, thì phần lớn thời gian bạn sẽ sử dụng QMainWindow để tạo GUI trên đó. Lớp này cho phép bạn tạo các ứng dụng kiểu cửa sổ chínhQMainWindow sẽ chuyển với bố cục định nghĩa trước của riêng nó. Bố cục này sẽ cho phép bạn thêm các thành phần đồ họa sau vào cửa sổ chính của mình:

  • Một thanh menu ở phía trên cùng của cửa sổ
  • Một hoặc nhiều thanh công cụ ở bất kỳ phía nào trong bốn cạnh của cửa sổ
  • Một thanh trạng thái ở dưới cùng của cửa sổ
  • Một hoặc nhiều công cụ dock ở bất kỳ vị trí nào trong bốn cạnh của cửa sổ (nhưng không chiếm diện tích thanh công cụ)
  • Một widget trung tâm ở chính giữa cửa sổ

Đối với hầu hết các ứng dụng, tất cả các thành phần đồ họa này là tùy chọn ngoại trừ widget trung tâm, được yêu cầu để ứng dụng của bạn hoạt động.

Lưu ý: Nếu bạn đang tạo các ứng dụng GUI bằng cách sử dụng QMainWindow, thì bạn phải có một widget trung tâm, ngay cả khi nó chỉ là một trình giữ chỗ.

Một số ứng dụng sử dụng một widget duy nhất và đầy đủ chức năng làm widget trung tâm của chúng. Ví dụ: nếu bạn đang viết code một trình soạn thảo văn bản, thì bạn có thể sẽ sử dụng một đối tượng QTextEdit làm widget trung tâm của trình soạn thảo của mình.

Các loại ứng dụng GUI khác có thể yêu cầu một widget trung tâm phức tạp hơn. Trong trường hợp đó, bạn có thể sử dụng một đối tượng QWidget làm widget trung tâm và sau đó tạo một bố cục chứa cách sắp xếp tiện ích cụ thể mà bạn cần cho GUI của ứng dụng. Bước cuối cùng là đặt bố cục đó làm bố cục của tiện ích con trung tâm của bạn.

Trong hầu hết các tình huống thì bố cục QMainWindow cung cấp đủ để tạo bất kỳ loại ứng dụng GUI nào. Bố cục này sẽ quản lý hiệu quả hành vi của các widget trên cửa sổ, vì vậy bạn không phải lo lắng về điều đó.

Bố cục hộp thoại (dialog) của ứng dụng

Các ứng dụng GUI thường được xây dựng bằng cách sử dụng một cửa sổ chính và một hoặc nhiều hộp thoại. Hộp thoại là các cửa sổ nhỏ cho phép bạn giao tiếp với người dùng của mình. PyQt cung cấp QDialog để xử lý việc tạo các hộp thoại.

Không giống như QMainWindowQDialog không có bố cục cấp cao nhất được xác định trước hoặc mặc định. Đó là bởi vì các hộp thoại có thể khá đa dạng và bao gồm nhiều cách sắp xếp và kết hợp widget.

Khi bạn đặt tất cả các tiện ích con trên GUI của hộp thoại, bạn cần đặt bố cục cấp cao nhất trên hộp thoại đó. Để làm điều này, bạn phải gọi .setLayout() trên đối tượng hộp thoại giống như cách bạn làm với bất kỳ tiện ích con nào khác.

Dưới đây là một ứng dụng kiểu hộp thoại hiển thị cách đặt bố cục cấp cao nhất cho một đối tượng QDialog:

 1import sys
 2
 3from PyQt5.QtWidgets import (
 4    QApplication,
 5    QDialog,
 6    QDialogButtonBox,
 7    QFormLayout,
 8    QLineEdit,
 9    QVBoxLayout,
10)
11
12class Dialog(QDialog):
13    def __init__(self):
14        super().__init__()
15        self.setWindowTitle("QDialog's Top-Level Layout Example")
16        dlgLayout = QVBoxLayout()
17        # Create a form layout and add widgets
18        formLayout = QFormLayout()
19        formLayout.addRow("Name:", QLineEdit())
20        formLayout.addRow("Job:", QLineEdit())
21        formLayout.addRow("Email:", QLineEdit())
22        # Add a button box
23        btnBox = QDialogButtonBox()
24        btnBox.setStandardButtons(
25            QDialogButtonBox.Ok | QDialogButtonBox.Cancel
26        )
27        # Set the layout on the dialog
28        dlgLayout.addLayout(formLayout)
29        dlgLayout.addWidget(btnBox)
30        self.setLayout(dlgLayout)
31
32if __name__ == "__main__":
33    app = QApplication(sys.argv)
34    dlg = Dialog()
35    dlg.show()
36    sys.exit(app.exec_())

Giải thích:

Cửa sổ của ứng dụng kế thừa từ QDialog, vì vậy bạn có một ứng dụng kiểu hộp thoại. Trên dòng 16, bạn tạo bố cục mà bạn sẽ sử dụng làm bố cục cấp cao nhất của hộp thoại. Trên các dòng từ 18 đến 21, bạn tạo bố cục biểu mẫu để sắp xếp một số tiện ích con trong một biểu mẫu.

Trên dòng 24, bạn thêm một đối tượng QDialogButtonBox. Bạn sẽ thường sử dụng QDialogButtonBox để xử lý các nút trên hộp thoại. Trong ví dụ này, bạn sử dụng hai nút, nút Ok và nút Cancel. Các nút này sẽ không có bất kỳ chức năng nào — chúng chỉ nhằm mục đích làm cho hộp thoại trở nên thực tế hơn.

Khi bạn đã có tất cả các tiện ích và bố cục, bạn có thể thêm chúng vào bố cục cấp cao nhất. Đó là những gì bạn làm trên dòng 28 và 29. Bước cuối cùng, trên dòng 30, là đặt bố cục cấp cao nhất làm bố cục hộp thoại của bạn bằng cách sử dụng .setLayout().

Kết quả là bạn sẽ thấy cửa sổ sau trên màn hình của mình:

Ví dụ về bố cục cấp cao nhất của QDialog

Cách tốt nhất là đặt bố cục cấp cao nhất cho tất cả các hộp thoại của bạn. Điều này đảm bảo rằng GUI của hộp thoại sẽ hoạt động mạch lạc khi người dùng thay đổi kích thước cửa sổ bên dưới. Nếu không, hộp thoại của bạn có thể trông vô tổ chức và không được đẹp trong mắt người dùng.

Quản lý không gian trong một bố cục PyQt

Khi nói đến việc sử dụng trình quản lý bố cục của PyQt để sắp xếp các widget trên một cửa sổ hoặc biểu mẫu, thì việc quản lý không gian — không gian trống, không gian giữa các widget, v.v. — là một vấn đề phổ biến. Có thể quản lý không gian này là một kỹ năng quan trọng cần có.

Bố cục quản lý không gian có sẵn trên cửa sổ bằng cách sử dụng một số thuộc tính tiện ích con sau:

  • .sizeHint() chứa kích thước được đề xuất của tiện ích con
  • .minimumSizeHint() chứa kích thước nhỏ nhất mà tiện ích có thể có trong khi vẫn có thể sử dụng được
  • .sizePolicy() giữ hành vi mặc định của tiện ích con trong một bố cục

Bố cục sử dụng các thuộc tính này để tự động định vị và thay đổi kích thước tiện ích con, gán một lượng không gian nhất định cho mỗi tiện ích con theo không gian có sẵn. Điều này đảm bảo rằng các widget được sắp xếp nhất quán và vẫn có thể sử dụng được.

Trong ba phần tiếp theo, bạn sẽ tìm hiểu cách các loại bố cục khác nhau quản lý không gian trong PyQt.

Quản lý không gian trong bố cục hộp (box)

Bố cục hộp thực hiện một công việc tuyệt vời khi phân phối không gian có sẵn giữa các vật dụng. Tuy nhiên, đôi khi hành vi mặc định của chúng là không đủ và bạn cần phải xử lý thủ công không gian có sẵn. Để giúp bạn trong tình huống này, PyQt cung cấp QSpacerItem. Lớp này cho phép bạn thêm không gian trống (hoặc các hộp trống) vào bố cục hộp.

Thông thường, bạn không cần phải sử dụng QSpacerItem trực tiếp. Thay vào đó, bạn gọi một số phương thức sau trên các đối tượng bố cục hộp của mình:

  • .addSpacing(i) thêm một không gian không thể co giãn (hoặc hộp trống) có kích thước cố định i vào bố cục. i phải là một số nguyên đại diện cho kích thước của không gian tính bằng pixel.
  • .addStretch(i) thêm không gian có thể co giãn với kích thước tối thiểu 0 và hệ số giãn i vào bố cục hộp. i phải là số nguyên.
  • .insertSpacing(index, size) chèn một không gian không thể co giãn tại vị trí index, với kích thước size. Nếu index là số âm, thì khoảng trống được thêm vào cuối bố cục hộp.
  • insertStretch(index, stretch) chèn một không gian có thể co giãn tại vị trí index, với kích thước tối thiểu là 0 và hệ số giãn là stretch. Nếu index là số âm, thì khoảng trống được thêm vào cuối bố cục hộp.

Các phần đệm có thể co giãn sẽ mở rộng hoặc thu nhỏ để lấp đầy không gian trống khi người dùng thay đổi kích thước cửa sổ bên dưới. Các phần đệm không thể co giãn sẽ vẫn giữ nguyên kích thước bất kể thay đổi về kích thước của cửa sổ bên dưới.

Quay lại ví dụ về cách sử dụng bố cục dọc và chạy lại ứng dụng đó. Nếu bạn kéo đường viền của cửa sổ xuống, thì bạn sẽ nhận thấy rằng càng có nhiều khoảng trống xuất hiện giữa các nút khi bạn kéo càng kéo xuống:

Ví dụ về bộ đệm không co giãn

Điều này xảy ra vì bố cục xử lý không gian mới có sẵn bằng cách tự động mở rộng các hộp của nó. Bạn có thể thay đổi hành vi này bằng cách thêm một đối tượng QSpacerItem co dãn vào cuối bố cục.

Vậy giờ ta sẽ cập nhật trình khởi tạo Window như sau:

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QVBoxLayout Example")
        self.resize(270, 110)
        # Tạo một đối tượng QVBoxLayout
        layout = QVBoxLayout()
        # Thêm các widget vào layout
        layout.addWidget(QPushButton("Top"))
        layout.addWidget(QPushButton("Center"))
        layout.addWidget(QPushButton("Bottom"))
        layout.addStretch()
        # Thiết lập layout trên window của ứng dụng
        self.setLayout(layout)

Trong dòng được đánh dấu vàng ở trên, bạn thêm một đối tượng QSpacerItem có thể co giãn vào cuối bố cục bằng cách gọi .addStretch() trên bố cục. Nếu bạn chạy lại ứng dụng, thì bạn sẽ nhận được hiện tượng sau:

Ví dụ về bộ đệm có thể kéo giãn

Giờ đây, tất cả không gian thừa sẽ được tự động gán cho đối tượng QSpacerItem có thể co giãn ở cuối bố cục mà không ảnh hưởng đến vị trí hoặc kích thước của các tiện ích còn lại. Bạn có thể sử dụng kỹ thuật này và các kỹ thuật quản lý không gian khác để làm cho các ứng dụng GUI của bạn trông đẹp và bóng bẩy hơn.

Quản lý không gian trong bố cục lưới và biểu mẫu

Bố cục dạng lưới và biểu mẫu xử lý không gian có sẵn theo một cách khác. Trong các loại bố cục này, bạn chỉ có thể xử lý không gian dọc và ngang giữa các tiện ích con. Các bố cục này cung cấp ba phương thức để quản lý các không gian này:

  1. setSpacing(spacing) đặt cả chiều dọc và chiều ngang giữa các tiện ích con thành spacing.
  2. setVerticalSpacing(spacing) chỉ đặt khoảng cách theo chiều dọc giữa các tiện ích con trong bố cục thành spacing.
  3. setHorizontalSpacing(spacing) chỉ đặt khoảng cách ngang giữa các tiện ích con trong bố cục thành spacing.

Trong mọi trường hợp, spacing là một số nguyên đại diện cho pixel. Bây giờ ta quay lại ví dụ về cách tạo bố cục biểu mẫu và cập nhật trình khởi tạo Window như sau:

class Window(QWidget):
    def __init__(self):
        super().__init__()
        self.setWindowTitle("QFormLayout Example")
        self.resize(270, 110)
        # Tạo một đối tượng QHBoxLayout
        layout = QFormLayout()
        # Thêm các widget vào layout
        layout.setVerticalSpacing(30)
        layout.addRow("Name:", QLineEdit())
        layout.addRow("Job:", QLineEdit())
        emailLabel = QLabel("Email:")
        layout.addRow(emailLabel, QLineEdit())
        # Thiết lập layout trên cửa sổ của ứng dụng
        self.setLayout(layout)

Trong dòng được đánh dấu, bạn đặt không gian dọc giữa các tiện ích thành 30pixel. Nếu bạn chạy lại ứng dụng, thì bạn sẽ thấy cửa sổ sau:

Ví dụ về khoảng cách QFormLayout

Bây giờ có nhiều không gian hơn giữa các hàng widget. Bạn cũng có thể thử sửa đổi ví dụ về cách sử dụng bố cục lưới bằng cách thêm một số không gian dọc hoặc ngang chỉ để xem tất cả các cơ chế co giãn này hoạt động như thế nào nhé.

Phần kết luận

Tạo các ứng dụng GUI chất lượng cao đòi hỏi phải bố trí tất cả các thành phần đồ họa theo một cách sắp xếp chặt chẽ và bóng bẩy. Trong PyQt, một cách hiệu quả để làm điều đó là sử dụng trình quản lý bố cục của PyQt, cung cấp cách tiếp cận hiệu quả và thân thiện với người dùng.

Trong hướng dẫn này, bạn đã học được:

  • Các lợi ích khi đặt các widget trên GUI
  • Cách sắp xếp theo chương trình các widget bằng trình quản lý bố cục tích hợp sẵn của PyQt
  • Cách lựa chọn trình quản lý bố cục cho trường hợp sử dụng cụ thể của bạn
  • Cách bố trí các ứng dụng kiểu cửa sổ chính và kiểu hộp thoại trong PyQt

Với kiến ​​thức này, bạn sẽ có thể tạo các ứng dụng GUI đẹp mắt và chuyên nghiệp bằng cách sử dụng các bố cục có sẵn của PyQt.

» Tiếp: f-Strings của Python 3: Cú pháp định dạng chuỗi được cải thiện
« Trước: Các kiểu dữ liệu cơ bản trong Python
Các khóa học qua video:
Python SQL Server PHP C# Lập trình C Java HTML5-CSS3-JavaScript
Học trên YouTube <76K/tháng. Đăng ký Hội viên
Viết nhanh hơn - Học tốt hơn
Giải phóng thời gian, khai phóng năng lực
Copied !!!