注:原创不易,转载请务必注明原作者和出处,感谢支持!

一 写在开头

1.1 本文内容

本文内容为PyQt5中的布局管理。具体内容为:

  • 水平布局(QHBoxLayout)
  • 垂直布局(QVBoxLayout)
  • 网格布局(QGridLayout)
  • 表单布局(QFormLayout)
  • 布局管理器QSplitter
  • 二 绝对布局

    绝对布局主要用到两个方法,分别是move()和setGeometry()。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    import sys
    from PyQt5.QtWidgets import QWidget, QLabel, QApplication
    class Window(QWidget):
        def __init__(self):
            super().__init__()
            self.initUI()
        def initUI(self):
            lb1 = QLabel('欢迎', self)
            lb1.move(15, 10)
            lb2 = QLabel('学习', self)
            lb2.move(35, 40)
            lb3 = QLabel('PyQt 5', self)
            lb3.move(55, 70)
            self.setGeometry(300, 300, 250, 150)
            self.setWindowTitle('PyQt 5中的绝对定位')
            self.show()
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        w = Window()
        sys.exit(app.exec_())
    

    绝对布局的优点:

  • 可以直接定位每个控件的位置
  • 绝对布局的缺点:

  • 如果改变一个窗口的大小,窗口中控件的大小和位置不会随之改变
  • 所生成的窗口在不同的操作系统下看起来可能不一样
  • 在程序中改变字体时可能会破坏布局
  • 如果修改布局,比如新增一个控件,就必须全部重新布局,既烦琐又费时
  • 三 布局类

    3.1 水平布局(QHBoxLayout)和垂直布局(QVBoxLayout)

    本小节涉及到的方法及其描述如下:

    addWidget(self, QWidget, stretch, Qt.Alignment alignment) 在布局中添加控件,stretch为伸缩量,alignment为对齐方式 addSpacing(self, int) 添加伸缩量,具体使用方法见下面的例子

    对齐方式Qt.Alignment可能取值有:

    from PyQt5.QtWidgets import QWidget, QPushButton, QHBoxLayout, QApplication class Window(QWidget): def __init__(self): super().__init__() hbox = QHBoxLayout() hbox.addWidget(QPushButton(str(1))) hbox.addWidget(QPushButton(str(2))) hbox.addWidget(QPushButton(str(3))) hbox.addWidget(QPushButton(str(4))) hbox.addWidget(QPushButton(str(5))) self.setLayout(hbox) self.setWindowTitle('QHBoxLayout') self.show() if __name__ == '__main__': app = QApplication(sys.argv) w = Window() sys.exit(app.exec_())
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    import sys
    from PyQt5.QtCore import Qt
    from PyQt5.QtWidgets import QWidget, QPushButton, QHBoxLayout, QApplication
    class Window(QWidget):
        def __init__(self):
            super().__init__()
            hbox = QHBoxLayout()
            hbox.addWidget(QPushButton(str(1)), 0, Qt.AlignTop)
            hbox.addWidget(QPushButton(str(2)), 0, Qt.AlignTop | Qt.AlignLeft)
            hbox.addWidget(QPushButton(str(3)), 0)
            hbox.addWidget(QPushButton(str(4)), 0, Qt.AlignLeft | Qt.AlignBottom)
            hbox.addWidget(QPushButton(str(5)), 0, Qt.AlignTop)
            self.setLayout(hbox)
            self.setWindowTitle('水平布局')
            self.show()
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        w = Window()
        sys.exit(app.exec_())
    

    关于addSpacing()的用法见下面的实例。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    import sys
    from PyQt5.QtWidgets import QWidget, QPushButton, QHBoxLayout, QApplication
    class Window(QWidget):
        def __init__(self):
            super().__init__()
            btn1 = QPushButton()
            btn2 = QPushButton()
            btn3 = QPushButton()
            btn1.setText(str(1))
            btn2.setText(str(2))
            btn3.setText(str(3))
            # addSpacing(1)表示设置伸缩量为1
            hbox = QHBoxLayout()
            hbox.addStretch(1)
            hbox.addWidget(btn1)
            hbox.addStretch(1)
            hbox.addWidget(btn2)
            hbox.addStretch(1)
            hbox.addWidget(btn3)
            hbox.addStretch(1)
            self.setLayout(hbox)
            self.setWindowTitle('addStretch')
            self.show()
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        w = Window()
        sys.exit(app.exec_())
    
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    import sys
    from PyQt5.QtWidgets import QWidget, QPushButton, QHBoxLayout, QApplication
    class Window(QWidget):
        def __init__(self):
            super().__init__()
            btn1 = QPushButton()
            btn2 = QPushButton()
            btn3 = QPushButton()
            btn1.setText(str(1))
            btn2.setText(str(2))
            btn3.setText(str(3))
            hbox = QHBoxLayout()
            hbox.addStretch(0)
            hbox.addWidget(btn1)
            hbox.addWidget(btn2)
            hbox.addWidget(btn3)
            hbox.addStretch(0)
            self.setLayout(hbox)
            self.setWindowTitle('addStretch')
            self.show()
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        w = Window()
        sys.exit(app.exec_())
    

    3.3 网格布局(QGridLayout)

    网格布局需要用到的方法及其描述如下。

    addWidget(QWidget widget, int row, int col, int alignment = 0) 给网格布局添加控件。
    widget:要添加的控件
    row:位置所在行
    col:位置所在列
    alignment:对齐方式 addWidget(QWidget widget, int fromRow, int fromColumn, int rowSpan, int columnSpan, int alignment) 当所添加的控件跨越多行或多列时,使用这个函数
    fromRow:起始行
    fromColumn:起始列
    rowSpan:控件跨越的行
    columnSpan:控件跨越的列 setSpacing(int spacing) 设置控件在水平和垂直方向的间隔

    3.4 网格布局实例

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    import sys
    from PyQt5.QtWidgets import QWidget, QGridLayout, QPushButton, QApplication
    class Window(QWidget):
        def __init__(self):
            super().__init__()
            self.initUI()
        def initUI(self):
            grid = QGridLayout()
            self.setLayout(grid)
            names = ['Cls', 'Back', '', 'Close',
                    '7', '8', '9', '/',
                    '4', '5', '6', '*',
                    '1', '2', '3', '-',
                    '0', '.', '=', '+']
            positions = [(i, j) for i in range(5) for j in range(4)]
            for position, name in zip(positions, names):
                if name == '':
                    continue
                btn = QPushButton(name)
                grid.addWidget(btn, *position)
            self.move(300, 150)
            self.setWindowTitle('网格布局')
            self.show()
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        w = Window()
        sys.exit(app.exec_())
    
    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    import sys
    from PyQt5.QtWidgets import QWidget, QLabel, QLineEdit, QTextEdit, QGridLayout, QApplication
    class Window(QWidget):
        def __init__(self):
            super().__init__()
            self.initUI()
        def initUI(self):
            title = QLabel('标题')
            author = QLabel('提交人')
            review = QLabel('申告内容')
            titleEdit = QLineEdit()
            authorEdit = QLineEdit()
            reviewEdit = QTextEdit()
            grid = QGridLayout()
            grid.setSpacing(10)
            self.setLayout(grid)
            grid.addWidget(title, 0, 0)
            grid.addWidget(titleEdit, 0, 1)
            grid.addWidget(author, 1, 0)
            grid.addWidget(authorEdit, 1, 1)
            grid.addWidget(review, 2, 0)
            grid.addWidget(reviewEdit, 2, 1, 2, 1)
            self.setGeometry(300, 300, 350, 300)
            self.setWindowTitle('故障申报')
            self.show()
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        w = Window()
        sys.exit(app.exec_())
    

    3.5 表单布局(QFormLayout)

    QFormLayout是label-field式的表单布局,顾名思义,就是实现表单方式的布局。表单是提示用户进行交互的一种模式,其主要由两列组成,第一列用于显示信息,给用户提示,一般叫做label域;第二列需要用户进行选择或者输入,一般叫做field域。label与field的关系就是label关联field。

    QFormLayout需要用到addRow(label, field)方法。

    3.6 表单布局实例

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    import sys
    from PyQt5.QtWidgets import QWidget, QFormLayout, QLabel, QLineEdit, QApplication
    class Window(QWidget):
        def __init__(self):
            super().__init__()
            self.initUI()
        def initUI(self):
            form = QFormLayout()
            lb1 = QLabel('标签1')
            le1 = QLineEdit()
            lb2 = QLabel('标签2')
            le2 = QLineEdit()
            lb3 = QLabel('标签3')
            le3 = QLineEdit()
            form.addRow(lb1, le1)
            form.addRow(lb2, le2)
            form.addRow(lb3, le3)
            self.resize(400, 100)
            self.setLayout(form)
            self.setWindowTitle('QFormLayout')
            self.show()
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        w = Window()
        sys.exit(app.exec_())
    

    3.7 嵌套布局

    所谓嵌套布局就是综合应用上述的布局进行嵌套以完成整体窗口的布局。下面是有关嵌套布局的两个等价实例。注意第二个实例更加简单。

    #!/usr/bin/env python3
    # -*- coding: utf-8 -*-
    import sys
    from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QVBoxLayout, \
                                QGridLayout, QFormLayout, QPushButton
    class Window(QWidget):
        def __init__(self):
            super().__init__()
            self.setWindowTitle('嵌套布局')
            # 全局布局:水平
            wlayout = QHBoxLayout()
            # 局部布局:水平,垂直,网格,表单
            hlayout = QHBoxLayout()
            vlayout = QVBoxLayout()
            glayout = QGridLayout()
            flayout = QFormLayout()
            # 为局部布局添加控件
            hlayout.addWidget(QPushButton(str(1)))
            hlayout.addWidget(QPushButton(str(2)))
            vlayout.addWidget(QPushButton(str(3)))
            vlayout.addWidget(QPushButton(str(4)))
            glayout.addWidget(QPushButton(str(5)), 0, 0)
            glayout.addWidget(QPushButton(str(6)), 0, 1)
            glayout.addWidget(QPushButton(str(7)), 1, 0)
            glayout.addWidget(QPushButton(str(8)), 1, 1)
            flayout.addWidget(QPushButton(str(9)))
            flayout.addWidget(QPushButton(str(10)))
            flayout.addWidget(QPushButton(str(11)))
            flayout.addWidget(QPushButton(str(12)))
            # 准备4个控件
            hwg = QWidget()
            vwg = QWidget()
            gwg = QWidget()
            fwg = QWidget()
            # 使用4个控件设置局部布局
            hwg.setLayout(hlayout)
            vwg.setLayout(vlayout)
            gwg.setLayout(glayout)
            fwg.setLayout(flayout)
            # 将4个控件添加到全局布局中
            wlayout.addWidget(hwg)
            wlayout.addWidget(vwg)
            wlayout.addWidget(gwg)
            wlayout.addWidget(fwg)
            self.setLayout(wlayout)
            self.show()
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        w = Window()
        sys.exit(app.exec_())
    
    #!/usr/bin/env python3
    import sys
    from PyQt5.QtWidgets import QApplication, QWidget, QHBoxLayout, QVBoxLayout, \
                                QGridLayout, QFormLayout, QPushButton
    class Window(QWidget):
        def __init__(self):
            super().__init__()
            self.setWindowTitle('嵌套布局')
            self.resize(700, 200)
            # 全局控件(注意参数self),用于承载全局布局
            wwg = QWidget(self)
            # 全局布局
            wl = QHBoxLayout(wwg)
            hlayout = QHBoxLayout()
            vlayout = QVBoxLayout()
            glayout = QGridLayout()
            flayout = QFormLayout()
            # 为局部布局添加控件
            hlayout.addWidget(QPushButton(str(1)))
            hlayout.addWidget(QPushButton(str(2)))
            vlayout.addWidget(QPushButton(str(3)))
            vlayout.addWidget(QPushButton(str(4)))
            glayout.addWidget(QPushButton(str(5)), 0, 0)
            glayout.addWidget(QPushButton(str(6)), 0, 1)
            glayout.addWidget(QPushButton(str(7)), 1, 0)
            glayout.addWidget(QPushButton(str(8)), 1, 1)
            flayout.addWidget(QPushButton(str(9)))
            flayout.addWidget(QPushButton(str(10)))
            flayout.addWidget(QPushButton(str(11)))
            flayout.addWidget(QPushButton(str(12)))
            # 在布局布局wl中添加局部布局
            wl.addLayout(hlayout)
            wl.addLayout(vlayout)
            wl.addLayout(glayout)
            wl.addLayout(flayout)
            self.show()
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        w = Window()
        sys.exit(app.exec_())
    

    3.8 布局管理器QSplitter

    PyQt提供了一个特殊的布局管理器QSplitter,它可以动态地拖动子控件之间的边界,算是一个动态的布局管理器。

    与QSplitter相关的方法及其描述如下。

    import sys from PyQt5.QtWidgets import QWidget, QHBoxLayout, QSplitter, QTextEdit, \ QApplication, QFrame from PyQt5.QtCore import Qt class Window(QWidget): def __init__(self): super().__init__() self.initUI() def initUI(self): hbox = QHBoxLayout(self) self.setWindowTitle('QSplitter') self.setGeometry(300, 300, 300, 200) topleft = QFrame() topleft.setFrameShape(QFrame.StyledPanel) bottom = QFrame() bottom.setFrameShape(QFrame.StyledPanel) sp1 = QSplitter(Qt.Horizontal) te = QTextEdit() sp1.addWidget(topleft) sp1.addWidget(te) sp1.setSizes([100, 200]) sp2 = QSplitter(Qt.Vertical) sp2.addWidget(sp1) sp2.addWidget(bottom) hbox.addWidget(sp2) self.setLayout(hbox) self.show() if __name__ == '__main__': app = QApplication(sys.argv) w = Window() sys.exit(app.exec_())