[PyQt] PySide2 教程 #4: Layout使用,让GUI更加整齐又富有弹性

Every UI need a Layout

 

Photo by Clark Van Der Beken on Unsplash

沒有Layout的世界是脆弱的,看似整齊的表面之下,隱藏著極為脆弱或是僵化的體制與腐敗,可謂是金玉其外敗絮其內。Layout讓UI再擁有整齊的架構之下,同時保有足夠的彈性。本篇會介紹各種PySide2 中的Layout,包括 QgridLayout, QVBoxLayout, QHBoxLayout, 以及 QFormLayout.

關於Layout

在Qt中的Layout包含了常見的各種類型,包括 vertical, horizontal, grid, 以及form layout. 這邊會一一向大家介紹。

在此之前,先和大家分享一點小技巧,使用layout的大方向 大致上會分為以下幾點:

  • 小型多個獨立物件,是VBox 與 HBox 的最愛
  • 複雜群組、不規則形狀,肯定是該用Grid
  • 最外層,請用Grid的糖衣
  • 最後,form除了非常整齊的輸入表格外,能少用就少用

VBoxLayout / HBoxLayout

讓我們由最簡單、最基礎的開始吧。

我利用前幾篇所建構的MainWindow class來做Layout的擴充,加入一個method(方法)來新建VBoxLayout以及HBoxLayout,同時用GridLayout為主Layout來包裹另外兩個Layout。

Source

#!venv/bin/python3 from PySide2.QtWidgets import QWidget from PySide2.QtWidgets import QMainWindow from PySide2.QtWidgets import QPushButton from PySide2.QtWidgets import QVBoxLayout, QHBoxLayout, QGridLayout, QFormLayout class MainWindow(QMainWindow): def __init__(self, parent=None): """Main window, holding all user interface including. Args: parent: parent class of main window Returns: None Raises: None """ super(MainWindow, self).__init__(parent) self._widget = QWidget() self._width = 400 self._height = 300 self.setFixedSize(self._width, self._height) g_layout = QGridLayout() v_layout = self.build_v_layout() h_layout = self.build_h_layout() g_layout.addItem(v_layout, 0, 0, 2, 1) g_layout.addItem(h_layout, 0, 1, 1, 1) self._widget.setLayout(g_layout) self.setCentralWidget(self._widget) @staticmethod def build_v_layout(): hello_btn = QPushButton('Hello') work_btn = QPushButton('Working') play_btn = QPushButton('Playing') sleep_btn = QPushButton('Sleeping') v_layout = QVBoxLayout() v_layout.addWidget(hello_btn) v_layout.addWidget(work_btn) v_layout.addWidget(play_btn) v_layout.addWidget(sleep_btn) return v_layout @staticmethod def build_h_layout(): hello_btn = QPushButton('Hello') work_btn = QPushButton('Working') play_btn = QPushButton('Playing') sleep_btn = QPushButton('Sleeping') h_layout = QHBoxLayout() h_layout.addWidget(hello_btn) h_layout.addWidget(work_btn) h_layout.addWidget(play_btn) h_layout.addWidget(sleep_btn) return h_layout

GridLayout

上面可以看到我用 addItem()方法將兩個Layout加入GridLayout之中,這邊提一下,加入Layout需使用 addItem ,而加入物件則需使用 addWidget 方法,如我下方VBoxLayout及HBoxLayout所使用。

權重

GridLayout好用的地方便在於彈性極高,可以透過網格的方式定位,同時支援物件於行列的權重,讓物件可以以不同的比例做呈現。

站长备注: 网格布局中addwidget函数可以传入五个参数,分别如下五个参数 arg__1:PySide2.QtWidgets.QWidget, row:int, column:int, rowSpan:int, columnSpan:int Widget 水平位置 垂直位置 水平占据网格个数 垂直占据网格个数 备注结束。

以下我修改兩種GridLayout的權重比例

Left Pic: 
g_layout.addItem(v_layout, 0, 0, 1, 1)
g_layout.addItem(h_layout, 0, 1, 1, 1)
Right Pic: 
g_layout.addItem(v_layout, 0, 0, 2, 1)
g_layout.addItem(h_layout, 0, 1, 1, 1)

FormLayout

我們再加入 build_f_layout方法來建立FormLayout物件,如下

@staticmethod
def build_form_layout():
    hello_btn = QPushButton('Hello')
    work_btn = QPushButton('Working')
    play_btn = QPushButton('Playing')
    sleep_btn = QPushButton('Sleeping')
    hello_line = QLineEdit()
    work_line = QLineEdit()
    play_line = QLineEdit()
    sleep_line = QLineEdit()

    f_layout = QFormLayout()
    f_layout.addRow(hello_btn, hello_line)
    f_layout.addRow(work_btn, work_line)
    f_layout.addRow(play_btn, play_line)
    f_layout.addRow(sleep_btn, sleep_line)
    
    return f_layout

並加入GridLayout中,同時我稍微調整了一下權重

f_layout = self.build_f_layout()

g_layout.addItem(v_layout, 1, 0, 1, 1)
g_layout.addItem(h_layout, 0, 0, 1, 2)
g_layout.addItem(f_layout, 1, 1, 1, 1)

Result

Source Code

完整代碼請看:Layout

 

結論

Layout在UI中非常實用,其實也不難,但常常會看到新手在學習Qt時使用QtDesigner拉入物件後手動作排版,這種方式只要主視窗的大小一經改動整個畫面就會被切掉或是留白,也非常不美觀,物件距離肯定不對等。

Layout不只能幫忙定位,同時具有讓內容物件等比例縮放的效果,這麼好的東西何不試試看呢?

系列教程下一篇:

[PyQt] PySide2 教程 #5: Signal & Slot