Быстрый в изучении - мощный в программировании
>> Telegram ЧАТ для Python Программистов

Свободное общение и помощь советом и решением проблем с кодом! Заходите в наш TELEGRAM ЧАТ!

>> Python Форум Помощи!

Мы создали форум где отвечаем на все вопросы связанные с языком программирования Python. Ждем вас там!

>> Python Канал в Telegram

Обучающие статьи, видео и новости из мира Python. Подпишитесь на наш TELEGRAM КАНАЛ!

Управление макетом в PyQt5 [Урок №4]

Управление макетом в PyQt5 [Урок №4]

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

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

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

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

Следующий пример размещает виджеты в абсолютных координатах.

#!/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_())

Пример размещает две кнопки в нижнем правом углу окна. Они остаются там, когда мы изменяем размер окна приложения. Мы можем использовать и HBoxLayout, и 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_())
Купить подписчиков в группу Телеграмма от 1,2 рублей можно только на https://doctorsmm.com/. На данном сайте представлен широкий спектр услуг по продвижению в данной социальной сети. Здесь Вы сможете найти качественные аккаунты с высоким скоростным режимом (до 1000 ед. в сутки) и большими оптовыми скидками. Все предложения действуют ограниченное время, поэтому торопитесь сделать самый выгодный заказ!

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

grid = QGridLayout()
grid.setSpacing(10)

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

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

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

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

Рисунок: Обзорный пример

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

Продолжение: События и сигналы в PyQt5 [Урок №5]

Прошлый урок: Меню и панели инструментов в PyQt5 [Урок №3]

Комментариев: 1
  1. Если у вас возникнет резонный вопрос, почему не работает gridLayout в mainwindow, а в QWidget нельзя создать меню и панель инструментов, то вот решение:

    class Example(QMainWindow):

    def __init__(self):

    super().__init__()

    self.initUI()

    def initUI(self):

    widget = QWidget(self)

    self.setCentralWidget(widget)

    grid = QGridLayout(widget)

    ...

    grid.addWidget(Что угодно)

    Мы создаем экземпляр mainwindow, а уже в нем - объект QWidget, который назначаем центральным окном.

    Меню и тулбар спокойно создаются, как и раньше в самом mainwindow

    ...

    menubar = self.menuBar()

    ...

    self.toolbar = self.addToolBar(Что угодно)