Урок №8. Управление компоновкой виджетов в Qt5

  Дмитрий Бушуев  | 

  Обновл. 8 Фев 2021  | 

 18004

 ǀ   5 

На этом уроке мы поговорим об управлении компоновкой виджетов в Qt5.

Системы компоновки в Qt5

Системы компоновки в Qt5 предоставляют разработчику простой и вместе с тем мощный способ максимально эффективного использования доступного пространства при помощи автоматического размещения виджетов с применением менеджеров компоновки. В статье будут рассмотрены следующие классы менеджеров компоновки:

   QHBoxLayout;

   QVBoxLayout;

   QFormLayout;

   QGridLayout.

На предыдущих уроках мы узнали, что типичное Qt-приложение состоит из различных виджетов, позиционированием которых занимается программист, пишущий программу. Библиотека Qt поддерживает два способа размещения элементов управления на форме приложения:

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

   позиционирование с помощью менеджеров компоновки.

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


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

   размер и положение виджета не изменяются при изменении размера окна;

   приложения выглядят по-разному (часто плохо) на разных платформах;

   изменение шрифтов в приложении может испортить компоновку;

   если мы решим изменить расположение элементов на форме, то должны будем полностью переделать всю компоновку, что, в свою очередь, является довольно утомительным и трудоемким процессом.

Несомненно, есть примеры, в которых нам никто не запрещает использовать способ абсолютного позиционирования элементов. Но в большинстве реальных проектов программисты стараются вместо этого использовать менеджеры компоновки.

Перейдем к рассмотрению примера, в котором задействован метод setGeometry() для размещения виджета в окне с использованием абсолютных координат.

Файл реализации — absolute.cpp:

Здесь мы создаем виджет QTextEdit и вручную размещаем его. Метод setGeometry() выполняет две функции: позиционирует виджет в абсолютных координатах и изменяет его размер.

Результат выполнения программы:


Класс QVBoxLayout

Класс QVBoxLayout предназначен для создания вертикального ряда из выравниваемых объектов. Добавление виджетов в компоновку осуществляется с помощью метода addWidget().

Заголовочный файл — verticalbox.h:

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

Файл реализации — verticalbox.cpp:

Создаем объект класса QVBoxLayout и устанавливаем интервал между дочерними виджетами в 1 пиксель:

Далее мы создаем кнопку и устанавливаем для нее политику размера QSizePolicy::Expanding. Дочерние виджеты управляются менеджером компоновки. По умолчанию кнопка растягивается по горизонтали и имеет фиксированный размер по вертикали. Если мы хотим изменить его, то нужно и для вертикали установить новую политику размера (QSizePolicy::Expanding). Как видите, в нашем случае кнопка будет расширяться в обоих направлениях:

Добавляем дочерние виджеты в менеджер компоновки при помощи метода addWidget():

Сообщаем нашей программе, что необходимо использовать QVBoxLayout в качестве менеджера компоновки:

Основной файл приложения — main.cpp:

Результат выполнения программы:


Добавление кнопок


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

Заголовочный файл — buttons.h:

Файл реализации — buttons.cpp:

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

Создаем две кнопки:

Данные кнопки с помощью метода addWidget() располагаются внутри горизонтального менеджера компоновки. При этом они выравниваются по правому краю. Метод addWidget() имеет три параметра:

   Параметр №1: Дочерний виджет.

   Параметр №2: Коэффициент (фактор) растяжения.

   Параметр №3: Выравнивание.

Установив коэффициент растяжения равным 1 для кнопки OK, виджет не будет растягиваться на все выделенное ему пространство, тем самым остается свободное место от левой до правой стороны окна. Наконец, константа Qt::AlignRight выравнивает виджет по правому краю:

При помощи метода addStretch() вставляем между виджетами промежуток динамического размера. Затем добавляем менеджер горизонтальной компоновки внутрь менеджера вертикальной компоновки:

Основной файл приложения — main.cpp:

Результат выполнения программы:


Вложенные компоновки

Идея следующего примера состоит в том, чтобы показать, что менеджеры компоновки могут быть вложены друг в друга. Комбинируя даже простые способы размещения элементов на форме, мы можем создавать сложные формы и окна. Для того, чтобы вложить одну компоновку в другую, используется метод addLayout().

В следующем примере мы создадим окно, состоящее из 4-х кнопок и 1 виджета QListWidget. Кнопки сгруппированы в вертикальный столбец и расположены справа от виджета-списка. Если мы изменим размер окна, размеры QListWidget также изменятся.

Заголовочный файл — nesting.h:

Файл реализации — nesting.cpp:

QVBoxLayout будет столбцом для кнопок:

QHBoxLayout будет базовой компоновкой для виджетов:

Создаем QListWidget и наполняем его данными:

А теперь создаем наши кнопки:

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

Виджет списка и вертикальный блок кнопок помещаются в блок горизонтальной компоновки. Метод addLayout() используется для вложения одной компоновки в другую:

Устанавливаем базовую компоновку для родительского окна:

Основной файл приложения — main.cpp:

Результат выполнения программы:


Класс QFormLayout


Класс QFormLayout является вспомогательным классом компоновки, который размещает свои дочерние элементы в двух столбцах. Левый столбец содержит метки, а правый столбец содержит виджеты ввода (однострочные редакторы, счетчики и т.д.).

В следующем примере мы создадим форму, которая будет состоять из 3-х меток и 3-х полей для ввода.

Заголовочный файл — form.h:

Файл реализации — form.cpp:

Создаем экземпляр класса QFormLayout:

С помощью метода setLabelAlignment() устанавливаем выравнивание виджетов-меток:

Метод addRow() добавляет новую строку в нижнюю часть компоновки формы с заданной меткой и виджетом ввода:

Основной файл приложения — main.cpp:

Результат выполнения программы:


Класс QGridLayout

Класс QGridLayout размещает виджеты в сетке. Механизм работы QGridLayout следующий: он занимает отведенное ему место (отведенное родительским компоновщиком или parentWidget()), разбивает его на строки и столбцы и помещает каждый подконтрольный виджет в соответствующую ячейку.

Заголовочный файл — calculator.h:

Файл реализации — calculator.cpp:

Мы создаем компоновку по сетке и устанавливаем 2 пикселя пространства между дочерними виджетами:

А вот символы, которые отображаются на кнопках:

Далее помещаем шестнадцать виджетов в компоновку. Все кнопки имеют фиксированный размер:

Основной файл приложения — main.cpp:

Результат выполнения программы:


Еще один пример


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

Заголовочный файл — review.h:

Файл реализации — review.cpp:

Создаем менеджер QGridLayout:

Добавляем вертикальный интервал с помощью метода setVerticalSpacing() и горизонтальный интервал с помощью метода setHorizontalSpacing():

Эти строки кода создают виджет-метку и помещают её в компоновку. Метод addWidget() имеет пять параметров:

   первый параметр — это дочерний виджет, метка в нашем случае;

   следующие два параметра — это строка и столбец в сетке компоновки, куда мы помещаем наш виджет;

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

Код:

Метод setAlignment() задает выравнивание метки-заголовка в своей ячейке. По горизонтали — выравнивание по правому краю, по вертикали — выравнивание по центру:

Виджет QTextEdit помещается в третью строку и второй столбец, он охватывает три строки и один столбец:

Основной файл приложения — main.cpp:

Результат выполнения программы:


Заключение

Мы разобрали несколько примеров с использованием различных менеджеров компоновки. Теперь вы можете самостоятельно попробовать изменить параметры компоновок и понаблюдать за результатом. Посмотрите, как будут перестраиваться виджеты в форме при изменении размеров формы. Вспомните, как задать минимальный размер формы, и задайте его так, чтобы не допускать искажения виджетов. Попробуйте создать собственные проекты с использованием рассмотренных менеджеров компоновки. Используя данную статью, расширяйте свое знакомство с виджетами, самостоятельно устанавливая их в ячейки компоновщика.

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (35 оценок, среднее: 4,83 из 5)
Загрузка...

Комментариев: 5

  1. Аватар SafinGleb:

    В калькуляторе все кнопки будут иметь указатель btn ?
    Как потом их различать ,чтобы использовать ?

    1. Дмитрий Бушуев Дмитрий Бушуев:

      Хороший вопрос.
      Вообще говоря, основная задача данного примера заключена в том, чтобы продемонстрировать только лишь возможности компоновки виджетов.

      Если же брать непосредственно функционал кнопок, то здесь есть один нюанс. Если я не ошибаюсь, переменная-указатель QPushButton *btn из двойного цикла for() будет создаваться/уничтожаться всякий раз, когда начинается/заканчивается итерация внешнего цикла for(). Но при этом память, выделяемая под QPushButton(values[pos], this), не освобождается. Если бы речь шла только о С++ без Qt, то мы бы получили стандартную утечку памяти (память выделили, а переменную-указатель — уничтожили, доступа к выделенной памяти больше нет).

      Но! В Qt есть такая штука, как "Иерархии объектов", суть которой вот в чём: каждый объект может иметь "родительские" и "дочерние" объекты, образуя тем самым иерархические структуры по типу дерева. Подобное отношение между объектами организуется (при создании) через последний параметр конструктора, представляющий собой указатель на объект-родитель.

      Обратите внимание на второй параметр, который мы передаём в конструктор кнопки при её создании:

      Параметр this — это указатель на объект-родитель (в нашем случае, это объект window класса Calculator), с которым мы связываем создаваемую кнопку. Т.е. каждый раз создавая в двойном цикле for() очередную кнопку, мы автоматически привязываем её к объекту window.

      Чтобы получить доступ к списку дочерних объектов, применяется функция findChildren<тип_дочернего_объекта>(), которая вернет список типа QList<тип_дочернего_объекта> дочерних объектов. На практике это можно реализовать следующим образом:

      (или, если находимся внутри объекта window, QList<QPushButton*> plist=this->findChildren<QPushButton*>(); )

      А дальше работать с plist как с обычным массивом:

      При этом стоит иметь в виду, что элементы в списке plist расположены в том порядке, в котором вновь создаваемые нами кнопки привязывались к своему объекту-родителю.
      Как-то так…

      1. Аватар SafinGleb:

        спасибо, получилось

  2. Аватар Jane:

    Почему в разделе "Добавление кнопок" нет в конце файла setLayout(vbox), как в остальных разделах? Было попробовано убрать setLayout() в других примерах — это не повлияло на внешний вид приложения! В чем секрет?

    1. Дмитрий Бушуев Дмитрий Бушуев:

      Секрет в этой строчке:

      Дело в том, что компоновку можно назначить как через вызов функции setLayout(), так и при создании самой компоновки, передавая в качестве параметра указатель на родительский объект (т.е. указатель но тот объект, которому собираемся назначить компоновку). Получается, что в таких случаях при помощи вышеописанной строчки мы устанавливаем компоновку еще до вызова setLayout() и именно поэтому, убрав вызов setLayout(), вы не наблюдаете никаких изменений.

      Попробуйте вместо:

      …использовать…

      P.S.: Спасибо за замечание, исправим 🙂

Добавить комментарий

Ваш E-mail не будет опубликован. Обязательные поля помечены *