Управление макетом в PyQt5

Значимая сторона в программировании графических приложений – управление макетом, то есть расположение виджетов в окне приложения. Управление макетом можно осуществлять двумя основными способами: с помощью абсолютного позиционирования или специальных классов.

Абсолютное позиционирование

Программист указывает позицию и размер каждого виджета в пикселях. При использовании абсолютного позиционирования, у нас есть следующие ограничения:

  • Размеры и позиция виджета не меняются, если мы меняем размер окна
  • Приложения могут выглядеть по-разному на разных платформах
  • Изменение шрифтов в нашем приложении может испортить макет
  • Если мы решаем изменить наш макет, мы должны полностью переделать его, что утомительно и занимает много времени

Следующий пример размещает виджеты с помощью абсолютного позиционирования.

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import QWidget, QLabel, QApplication


class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()


    def initUI(self):

        lbl1 = QLabel('Zetcode', self)
        lbl1.move(15, 10)

        lbl2 = QLabel('tutorials', self)
        lbl2.move(35, 40)

        lbl3 = QLabel('for programmers', self)
        lbl3.move(55, 70)

        self.setGeometry(300, 300, 250, 150)
        self.setWindowTitle('Absolute')
        self.show()


if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

Мы используем метод move(), чтобы разместить наши виджеты. В нашем случае ими являются метки. Мы размещаем их путём предоставления координат x и y. Начало координатной системы – левый верхний угол экрана. Значения x возрастают слева направо. Значения y – сверху вниз.

lbl1 = QLabel('Zetcode', self)
lbl1.move(15, 10)

Виджет метки располагается в x=15 и y=10.

Абсолютное позиционирование

Блочный макет

Управление макетом классами макета является более гибким и практичным. Это предпочтительный путь размещения виджетов в окне. QHBoxLayout и QVBoxLayout – это основные классы макета, которые выстраивают виджеты горизонтально или вертикально.

Представьте, что мы хотим разместить две кнопки в правом нижнем углу. Чтобы создать такой макет, мы будем использовать один горизонтальный и один вертикальный блок. Чтобы создать необходимое свободное пространство, мы добавим показатель растяжения.

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import (QWidget, QPushButton,
    QHBoxLayout, QVBoxLayout, QApplication)


class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()


    def initUI(self):

        okButton = QPushButton("OK")
        cancelButton = QPushButton("Cancel")

        hbox = QHBoxLayout()
        hbox.addStretch(1)
        hbox.addWidget(okButton)
        hbox.addWidget(cancelButton)

        vbox = QVBoxLayout()
        vbox.addStretch(1)
        vbox.addLayout(hbox)

        self.setLayout(vbox)

        self.setGeometry(300, 300, 300, 150)
        self.setWindowTitle('Buttons')
        self.show()


if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

В этом примере мы размещаем две кнопки в нижнем правом углу окна. Они остаются там, когда мы изменяем размер окна приложения. Мы можем использовать и QHBoxLayout, и QVBoxLayout.

okButton = QPushButton("OK")
cancelButton = QPushButton("Cancel")

Здесь мы создаём две кнопки.

hbox = QHBoxLayout()
hbox.addStretch(1)
hbox.addWidget(okButton)
hbox.addWidget(cancelButton)

Мы создаём макет горизонтального блока, добавляем показатель растяжения и обе кнопки. Растяжение добавляет растянутое свободное пространство перед двумя кнопками. Это прижмёт их к правому краю окна.

vbox = QVBoxLayout()
vbox.addStretch(1)
vbox.addLayout(hbox)

Чтобы создать необходимый макет, мы поставим горизонтальный макет в вертикальный. Показатель растяжения в вертикальном блоке прижмёт горизонтальный блок с кнопками к нижней части окна.

self.setLayout(vbox)

Наконец, мы устанавливаем главный макет окна.

Кнопки

QGridLayout (сеточный макет)

Самый универсальный класс макета – это сеточный макет. Этот макет делит пространство на строки и столбцы. Чтобы создать сеточный макет, мы используем класс QGridLayout.

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import (QWidget, QGridLayout,
    QPushButton, QApplication)


class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()


    def initUI(self):

        grid = QGridLayout()
        self.setLayout(grid)

        names = ['Cls', 'Bck', '', '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
            button = QPushButton(name)
            grid.addWidget(button, *position)

        self.move(300, 150)
        self.setWindowTitle('Calculator')
        self.show()


if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

В нашем примере, мы создаём сетку из кнопок. Чтобы заполнить один промежуток, мы добавляем один виджет QLabel.

grid = QGridLayout()
self.setLayout(grid)

Создаётся экземпляр QGridLayout, он назначается как макет окна приложения.

names = ['Cls', 'Bck', '', '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
    button = QPushButton(name)
    grid.addWidget(button, *position)

С помощью метода addWidget, создаются и добавляются кнопки к макету.

Каркас калькулятора

Обзорный пример

Виджеты могут занимать несколько столбцов и строк в сетке. В следующем примере мы продемонстрируем это.

#!/usr/bin/python3
# -*- coding: utf-8 -*-

import sys
from PyQt5.QtWidgets import (QWidget, QLabel, QLineEdit,
    QTextEdit, QGridLayout, QApplication)


class Example(QWidget):

    def __init__(self):
        super().__init__()

        self.initUI()


    def initUI(self):

        title = QLabel('Title')
        author = QLabel('Author')
        review = QLabel('Review')

        titleEdit = QLineEdit()
        authorEdit = QLineEdit()
        reviewEdit = QTextEdit()

        grid = QGridLayout()
        grid.setSpacing(10)

        grid.addWidget(title, 1, 0)
        grid.addWidget(titleEdit, 1, 1)

        grid.addWidget(author, 2, 0)
        grid.addWidget(authorEdit, 2, 1)

        grid.addWidget(review, 3, 0)
        grid.addWidget(reviewEdit, 3, 1, 5, 1)

        self.setLayout(grid)

        self.setGeometry(300, 300, 350, 300)
        self.setWindowTitle('Review')
        self.show()


if __name__ == '__main__':

    app = QApplication(sys.argv)
    ex = Example()
    sys.exit(app.exec_())

Мы создаём окно, в котором у нас есть три метки, две строки редактирования и один виджет редактирования текста. Макет сделан с помощью QGridLayout.

grid = QGridLayout()
grid.setSpacing(10)

Мы создаём сеточный макет и устанавливаем промежуток между виджетами.

grid.addWidget(reviewEdit, 3, 1, 5, 1)

Если мы добавляем виджет к сетке, мы можем обеспечить объединение строк и столбцов виджета. В нашем примере, мы делаем так, что виджет reviewEdit объединяет 5 строк.

Обзорный пример

Эта часть руководства по PyQt5 была посвящена управлению макетом.

Для вставки кода на Python в комментарий заключайте его в теги <pre><code class="python3">Ваш код</code></pre>
Опечатка в тексте:
Послать сообщение об ошибке автору?