Управление макетом в 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 была посвящена управлению макетом.