На этом уроке мы поговорим об управлении компоновкой виджетов в Qt5.
Системы компоновки в Qt5
Системы компоновки в Qt5 предоставляют разработчику простой и вместе с тем мощный способ максимально эффективного использования доступного пространства при помощи автоматического размещения виджетов с применением менеджеров компоновки. В статье будут рассмотрены следующие классы менеджеров компоновки:
QHBoxLayout
;
QVBoxLayout
;
QFormLayout
;
QGridLayout
.
На предыдущих уроках мы узнали, что типичное Qt-приложение состоит из различных виджетов, позиционированием которых занимается программист, пишущий программу. Библиотека Qt поддерживает два способа размещения элементов управления на форме приложения:
абсолютное позиционирование;
позиционирование с помощью менеджеров компоновки.
Абсолютное позиционирование
При абсолютном позиционировании программист указывает в пикселях положение и размер каждого виджета. При использовании этого способа размещения виджетов следует знать, что:
размер и положение виджета не изменяются при изменении размера окна;
приложения выглядят по-разному (часто плохо) на разных платформах;
изменение шрифтов в приложении может испортить компоновку;
если мы решим изменить расположение элементов на форме, то должны будем полностью переделать всю компоновку, что, в свою очередь, является довольно утомительным и трудоемким процессом.
Несомненно, есть примеры, в которых нам никто не запрещает использовать способ абсолютного позиционирования элементов. Но в большинстве реальных проектов программисты стараются вместо этого использовать менеджеры компоновки.
Перейдем к рассмотрению примера, в котором задействован метод setGeometry() для размещения виджета в окне с использованием абсолютных координат.
Файл реализации — absolute.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
#include <QApplication> #include <QDesktopWidget> #include <QTextEdit> class Absolute : public QWidget { public: Absolute(QWidget *parent = 0); }; Absolute::Absolute(QWidget *parent) : QWidget(parent) { QTextEdit *ledit = new QTextEdit(this); ledit->setGeometry(5, 5, 200, 150); } int main(int argc, char *argv[]) { QApplication app(argc, argv); Absolute window; window.setWindowTitle("Absolute"); window.show(); return app.exec(); } |
Здесь мы создаем виджет QTextEdit
и вручную размещаем его. Метод setGeometry() выполняет две функции: позиционирует виджет в абсолютных координатах и изменяет его размер.
1 2 |
QTextEdit *edit = new QTextEdit(this); ledit->setGeometry(5, 5, 200, 150); |
Результат выполнения программы:
Класс QVBoxLayout
Класс QVBoxLayout предназначен для создания вертикального ряда из выравниваемых объектов. Добавление виджетов в компоновку осуществляется с помощью метода addWidget().
Заголовочный файл — verticalbox.h:
1 2 3 4 5 6 7 8 9 |
#pragma once #include <QWidget> class VerticalBox : public QWidget { public: VerticalBox(QWidget *parent = 0); }; |
В нашем примере у нас есть один менеджер вертикальной компоновки, в который мы устанавливаем пять кнопок. При этом параметры кнопок задаются так, чтобы они имели возможность расширяться в обоих направлениях.
Файл реализации — verticalbox.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
#include "verticalbox.h" #include <QVBoxLayout> #include <QPushButton> VerticalBox::VerticalBox(QWidget *parent) : QWidget(parent) { QVBoxLayout *vbox = new QVBoxLayout(this); vbox->setSpacing(1); QPushButton *settings = new QPushButton("Settings", this); settings->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); QPushButton *accounts = new QPushButton("Accounts", this); accounts->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); QPushButton *loans = new QPushButton("Loans", this); loans->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); QPushButton *cash = new QPushButton("Cash", this); cash->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); QPushButton *debts = new QPushButton("Debts", this); debts->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); vbox->addWidget(settings); vbox->addWidget(accounts); vbox->addWidget(loans); vbox->addWidget(cash); vbox->addWidget(debts); setLayout(vbox); } |
Создаем объект класса QVBoxLayout
и устанавливаем интервал между дочерними виджетами в 1 пиксель:
1 2 |
QVBoxLayout *vbox = new QVBoxLayout(this); vbox->setSpacing(1); |
Далее мы создаем кнопку и устанавливаем для нее политику размера QSizePolicy::Expanding
. Дочерние виджеты управляются менеджером компоновки. По умолчанию кнопка растягивается по горизонтали и имеет фиксированный размер по вертикали. Если мы хотим изменить его, то нужно и для вертикали установить новую политику размера (QSizePolicy::Expanding
). Как видите, в нашем случае кнопка будет расширяться в обоих направлениях:
1 2 |
QPushButton *settings = new QPushButton("Settings", this); settings->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); |
Добавляем дочерние виджеты в менеджер компоновки при помощи метода addWidget():
1 2 3 |
vbox->addWidget(settings); vbox->addWidget(accounts); ... |
Сообщаем нашей программе, что необходимо использовать QVBoxLayout
в качестве менеджера компоновки:
1 |
setLayout(vbox); |
Основной файл приложения — main.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include "verticalbox.h" #include <QApplication> int main(int argc, char *argv[]) { QApplication app(argc, argv); VerticalBox window; window.resize(240, 230); window.setWindowTitle("VerticalBox"); window.show(); return app.exec(); } |
Результат выполнения программы:
Добавление кнопок
В следующем примере мы создадим две кнопки в правом нижнем углу клиентской области окна.
Заголовочный файл — buttons.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#pragma once #include <QWidget> #include <QPushButton> class Buttons : public QWidget { public: Buttons(QWidget *parent = 0); private: QPushButton *okBtn; QPushButton *applyBtn; }; |
Файл реализации — buttons.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#include "buttons.h" #include <QVBoxLayout> #include <QHBoxLayout> Buttons::Buttons(QWidget *parent) : QWidget(parent) { QVBoxLayout *vbox = new QVBoxLayout(this); QHBoxLayout *hbox = new QHBoxLayout(); okBtn = new QPushButton("OK", this); applyBtn = new QPushButton("Apply", this); hbox->addWidget(okBtn, 1, Qt::AlignRight); hbox->addWidget(applyBtn, 0); vbox->addStretch(1); vbox->addLayout(hbox); } |
Нам потребуются два менеджера компоновки: один вертикальный и один горизонтальный.
1 2 |
QVBoxLayout *vbox = new QVBoxLayout(this); QHBoxLayout *hbox = new QHBoxLayout(); |
Создаем две кнопки:
1 2 |
okBtn = new QPushButton("OK", this); applyBtn = new QPushButton("Apply", this); |
Данные кнопки с помощью метода addWidget() располагаются внутри горизонтального менеджера компоновки. При этом они выравниваются по правому краю. Метод addWidget() имеет три параметра:
Параметр №1: Дочерний виджет.
Параметр №2: Коэффициент (фактор) растяжения.
Параметр №3: Выравнивание.
Установив коэффициент растяжения равным 1
для кнопки OK
, виджет не будет растягиваться на все выделенное ему пространство, тем самым остается свободное место от левой до правой стороны окна. Наконец, константа Qt::AlignRight
выравнивает виджет по правому краю:
1 2 |
hbox->addWidget(okBtn, 1, Qt::AlignRight); hbox->addWidget(applyBtn, 0); |
При помощи метода addStretch() вставляем между виджетами промежуток динамического размера. Затем добавляем менеджер горизонтальной компоновки внутрь менеджера вертикальной компоновки:
1 2 |
vbox->addStretch(1); vbox->addLayout(hbox); |
Основной файл приложения — main.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include <QApplication> #include "buttons.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); Buttons window; window.resize(290, 170); window.setWindowTitle("Buttons"); window.show(); return app.exec(); } |
Результат выполнения программы:
Вложенные компоновки
Идея следующего примера состоит в том, чтобы показать, что менеджеры компоновки могут быть вложены друг в друга. Комбинируя даже простые способы размещения элементов на форме, мы можем создавать сложные формы и окна. Для того, чтобы вложить одну компоновку в другую, используется метод addLayout().
В следующем примере мы создадим окно, состоящее из 4 кнопок и 1 виджета QListWidget
. Кнопки сгруппированы в вертикальный столбец и расположены справа от виджета-списка. Если мы изменим размер окна, размеры QListWidget
также изменятся.
Заголовочный файл — nesting.h:
1 2 3 4 5 6 7 8 9 |
#pragma once #include <QWidget> class Layouts : public QWidget { public: Layouts(QWidget *parent = 0); }; |
Файл реализации — nesting.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
#include <QVBoxLayout> #include <QPushButton> #include <QListWidget> #include "nesting.h" Layouts::Layouts(QWidget *parent) : QWidget(parent) { QVBoxLayout *vbox = new QVBoxLayout(); QHBoxLayout *hbox = new QHBoxLayout(this); QListWidget *lw = new QListWidget(this); lw->addItem("The Omen"); lw->addItem("The Exorcist"); lw->addItem("Notes on a scandal"); lw->addItem("Fargo"); lw->addItem("Capote"); QPushButton *add = new QPushButton("Add", this); QPushButton *rename = new QPushButton("Rename", this); QPushButton *remove = new QPushButton("Remove", this); QPushButton *removeall = new QPushButton("Remove All", this); vbox->setSpacing(3); vbox->addStretch(1); vbox->addWidget(add); vbox->addWidget(rename); vbox->addWidget(remove); vbox->addWidget(removeall); vbox->addStretch(1); hbox->addWidget(lw); hbox->addSpacing(15); hbox->addLayout(vbox); setLayout(hbox); } |
QVBoxLayout
будет столбцом для кнопок:
1 |
QVBoxLayout *vbox = new QVBoxLayout(); |
QHBoxLayout
будет базовой компоновкой для виджетов:
1 |
QHBoxLayout *hbox = new QHBoxLayout(this); |
Создаем QListWidget
и наполняем его данными:
1 2 3 4 5 6 |
QListWidget *lw = new QListWidget(this); lw->addItem("The Omen"); lw->addItem("The Exorcist"); lw->addItem("Notes on a scandal"); lw->addItem("Fargo"); lw->addItem("Capote"); |
А теперь создаем наши кнопки:
1 2 3 4 |
QPushButton *add = new QPushButton("Add", this); QPushButton *rename = new QPushButton("Rename", this); QPushButton *remove = new QPushButton("Remove", this); QPushButton *removeall = new QPushButton("Remove All", this); |
Создается вертикальная компоновка с четырьмя кнопками. Мы вставили немного свободного места между нашими кнопками. Обратите внимание, что мы добавляем коэффициент растяжения к верхней и нижней части вертикальной компоновки. Таким образом, кнопки расположены вертикально по центру:
1 2 3 4 5 6 7 |
vbox->setSpacing(3); vbox->addStretch(1); vbox->addWidget(add); vbox->addWidget(rename); vbox->addWidget(remove); vbox->addWidget(removeall); vbox->addStretch(1); |
Виджет списка и вертикальный блок кнопок помещаются в блок горизонтальной компоновки. Метод addLayout() используется для вложения одной компоновки в другую:
1 2 3 |
hbox->addWidget(lw); hbox->addSpacing(15); hbox->addLayout(vbox); |
Устанавливаем базовую компоновку для родительского окна:
1 |
setLayout(hbox); |
Основной файл приложения — main.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include <QApplication> #include "nesting.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); Layouts window; window.setWindowTitle("Layouts"); window.show(); return app.exec(); } |
Результат выполнения программы:
Класс QFormLayout
Класс QFormLayout является вспомогательным классом компоновки, который размещает свои дочерние элементы в двух столбцах. Левый столбец содержит метки, а правый столбец содержит виджеты ввода (однострочные редакторы, счетчики и т.д.).
В следующем примере мы создадим форму, которая будет состоять из 3 меток и 3 полей для ввода.
Заголовочный файл — form.h:
1 2 3 4 5 6 7 8 9 |
#pragma once #include <QWidget> class FormEx : public QWidget { public: FormEx(QWidget *parent = 0); }; |
Файл реализации — form.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
#include <QFormLayout> #include <QLabel> #include <QLineEdit> #include "form.h" FormEx::FormEx(QWidget *parent) : QWidget(parent) { QLineEdit *nameEdit = new QLineEdit(this); QLineEdit *addrEdit = new QLineEdit(this); QLineEdit *occpEdit = new QLineEdit(this); QFormLayout *formLayout = new QFormLayout; formLayout->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter); formLayout->addRow("Name:", nameEdit); formLayout->addRow("Email:", addrEdit); formLayout->addRow("Age:", occpEdit); setLayout(formLayout); } |
Создаем экземпляр класса QFormLayout
:
1 |
QFormLayout *formLayout = new QFormLayout; |
С помощью метода setLabelAlignment() устанавливаем выравнивание виджетов-меток:
1 |
formLayout->setLabelAlignment(Qt::AlignRight | Qt::AlignVCenter); |
Метод addRow() добавляет новую строку в нижнюю часть компоновки формы с заданной меткой и виджетом ввода:
1 |
formLayout->addRow("Name:", nameEdit); |
Основной файл приложения — main.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include <QApplication> #include "form.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); FormEx window; window.setWindowTitle("Form example"); window.show(); return app.exec(); } |
Результат выполнения программы:
Класс QGridLayout
Класс QGridLayout размещает виджеты в сетке. Механизм работы QGridLayout
следующий: он занимает отведенное ему место (отведенное родительским компоновщиком или parentWidget()), разбивает его на строки и столбцы и помещает каждый подконтрольный виджет в соответствующую ячейку.
Заголовочный файл — calculator.h:
1 2 3 4 5 6 7 8 9 10 |
#pragma once #include <QWidget> class Calculator : public QWidget { public: Calculator(QWidget *parent = 0); }; |
Файл реализации — calculator.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#include <QGridLayout> #include <QPushButton> #include "calculator.h" Calculator::Calculator(QWidget *parent) : QWidget(parent) { QGridLayout *grid = new QGridLayout(this); grid->setSpacing(2); QList<QString> values({ "7", "8", "9", "/", "4", "5", "6", "*", "1", "2", "3", "-", "0", ".", "=", "+" }); int pos = 0; for (int i=0; i<4; i++) { for (int j=0; j<4; j++) { QPushButton *btn = new QPushButton(values[pos], this); btn->setFixedSize(40, 40); grid->addWidget(btn, i, j); pos++; } } setLayout(grid); } |
Мы создаем компоновку по сетке и устанавливаем 2 пикселя пространства между дочерними виджетами:
1 2 |
QGridLayout *grid = new QGridLayout(this); grid->setSpacing(2); |
А вот символы, которые отображаются на кнопках:
1 2 3 4 5 |
QList<QString> values({ "7", "8", "9", "/", "4", "5", "6", "*", "1", "2", "3", "-", "0", ".", "=", "+" }); |
Далее помещаем шестнадцать виджетов в компоновку. Все кнопки имеют фиксированный размер:
1 2 3 4 5 6 7 8 9 |
for (int i=0; i<4; i++) { for (int j=0; j<4; j++) { QPushButton *btn = new QPushButton(values[pos], this); btn->setFixedSize(40, 40); grid->addWidget(btn, i, j); pos++; } } |
Основной файл приложения — main.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include <QApplication> #include "calculator.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); Calculator window; window.move(300, 300); window.setWindowTitle("Calculator"); window.show(); return app.exec(); } |
Результат выполнения программы:
Еще один пример
Теперь постараемся создать более сложное окно с помощью менеджера QGridLayout
. В следующем примере мы создадим приложение, в котором можно будет написать отзыв о литературных произведениях авторов.
Заголовочный файл — review.h:
1 2 3 4 5 6 7 8 9 |
#pragma once #include <QWidget> class Review : public QWidget { public: Review(QWidget *parent = 0); }; |
Файл реализации — review.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
#include <QGridLayout> #include <QLabel> #include <QLineEdit> #include <QTextEdit> #include "review.h" Review::Review(QWidget *parent) : QWidget(parent) { QGridLayout *grid = new QGridLayout(this); grid->setVerticalSpacing(15); grid->setHorizontalSpacing(10); QLabel *title = new QLabel("Title:", this); grid->addWidget(title, 0, 0, 1, 1); title->setAlignment(Qt::AlignRight | Qt::AlignVCenter); QLineEdit *edt1 = new QLineEdit(this); grid->addWidget(edt1, 0, 1, 1, 1); QLabel *author = new QLabel("Author:", this); grid->addWidget(author, 1, 0, 1, 1); author->setAlignment(Qt::AlignRight | Qt::AlignVCenter); QLineEdit *edt2 = new QLineEdit(this); grid->addWidget(edt2, 1, 1, 1, 1); QLabel *review = new QLabel("Review:", this); grid->addWidget(review, 2, 0, 1, 1); review->setAlignment(Qt::AlignRight | Qt::AlignTop); QTextEdit *te = new QTextEdit(this); grid->addWidget(te, 2, 1, 3, 1); setLayout(grid); } |
Создаем менеджер QGridLayout
:
1 |
QGridLayout *grid = new QGridLayout(this); |
Добавляем вертикальный интервал с помощью метода setVerticalSpacing() и горизонтальный интервал с помощью метода setHorizontalSpacing():
1 2 |
grid->setVerticalSpacing(15); grid->setHorizontalSpacing(10); |
Эти строки кода создают виджет-метку и помещают её в компоновку. Метод addWidget() имеет пять параметров:
первый параметр — это дочерний виджет, метка в нашем случае;
следующие два параметра — это строка и столбец в сетке компоновки, куда мы помещаем наш виджет;
последние параметры определяют, сколько строк будет занимать текущий виджет (в нашем случае метка будет охватывать только один столбец и одну строку).
Код:
1 2 |
QLabel *title = new QLabel("Title", this); grid->addWidget(title, 0, 0, 1, 1); |
Метод setAlignment() задает выравнивание метки-заголовка в своей ячейке. По горизонтали — выравнивание по правому краю, по вертикали — выравнивание по центру:
1 |
title->setAlignment(Qt::AlignRight | Qt::AlignVCenter); |
Виджет QTextEdit
помещается в третью строку и второй столбец, он охватывает три строки и один столбец:
1 2 |
QTextEdit *te = new QTextEdit(this); grid->addWidget(te, 2, 1, 3, 1); |
Основной файл приложения — main.cpp:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include <QApplication> #include "review.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); Review window; window.setWindowTitle("Review"); window.show(); return app.exec(); } |
Результат выполнения программы:
Заключение
Мы разобрали несколько примеров с использованием различных менеджеров компоновки. Теперь вы можете самостоятельно попробовать изменить параметры компоновок и понаблюдать за результатом. Посмотрите, как будут перестраиваться виджеты в форме при изменении размеров формы. Вспомните, как задать минимальный размер формы, и задайте его так, чтобы не допускать искажения виджетов. Попробуйте создать собственные проекты с использованием рассмотренных менеджеров компоновки. Используя данную статью, расширяйте свое знакомство с виджетами, самостоятельно устанавливая их в ячейки компоновщика.
Может быть кому-нибудь будет полезно…
В калькуляторе решил добавить обработчики нажатия кнопок. Ниже в коментариях нашел как получить доступ к каждому элементу списка кнопок:
В результате получилась громоздкая конструкция из коннектов
и куча почти одинаковых слотов нажатия кнопок.
Чтобы избавиться от дублирования кода надо как-то подключить к разным сигналам один слот и передать в слот аргумент — номер нажатой клавиши, например просто элемент массива QList<QString> values = { «7», «8»,….
Дочитал уроки по Qt до конца но решения так и не нашел. Гугление и изучение заняло довольно много времени…
Решением оказалось простым — применение лямбда-функции. Описание тут: https://wiki.qt.io/New_Signal_Slot_Syntax
В цикле создания кнопок добавилась одна строчка:
И единственный метод в слоте private slots:
Дошел до девятого урока, но остались вопросы по компоновке.
1. Как работает int stretch когда мы добавляем новый виджет при помощи addWidget(QWidget*, int stretch);
Я только понял, что принимать он может значения от 0 до 1 и в первом случае виджет "растягивается" (но как-то непредсказуемо), а во втором случае — нет.
И как работает это всё работает вместе с, ну допустим, hbox->setStretch(1)?
2. Как работают флаги выравнивания Qt::AlignCenter, Qt::AlignLeft и прочие.
Допустим, есть у нас QLabel *lbl. B чём разница между этими командами:
Пару дней потратил на эксперементы, меняя эти значения самым случайным образом, однако никакой вразумительной системы обнаружить не смог.
Здравствуйте! Скажите, пожалуйста, зачем указатели на некоторые виджеты мы сохраняем в переменных — членах класса (например, в buttons.h и buttonts.cpp)?
Я, конечно, не волшебник, а только учусь, но в заголовочном файле *.h обычно создаются те указатели, с которыми потом будут взаимодействовать слоты.
Простой пример:
Есть три указателя:
QLineEdit — для ввода текста
QLabel — изначально пустую
QPushButton — которая по нажатию будет присваивать виджету QLabel значение, введённое в поле QLineEdit.
Если объект QPushButton можно со спокойной душой объявить в конструкторе класса (который в файле с исходниками *.cpp) то указатели QLineEdit и QLabel придётся объявить в заголовочном файле. Если же объявить их в конструкторе класса, то и область их видимости будет ограничена конструктором. Слот для обработки события &QPushButton::clicked их просто не увидит, а значит не сможет вытянуть значение из QLineEdit и присвоить его QLabel.
Но, лучше всего, попробуйте сами и отпишитесь. В отличие от курса по С++ курс по Qt не так детально разжевывается и многие моменты остаются непонятыми даже после чтения doc.qt.io. Возможно, есть какой-то способ запихнуть все указатели в конструктор, чтобы при этом всё работало. Но у меня ничего не получилось.
В строчке "QVBoxLayout *vbox = new QVBoxLayout();" в конструктор не передаётся указатель this. Из-за этого, насколько я понял, появлятся возможность утечки памяти. Мне кажется, стоит везде в конструктор передавать this, а потом использовать setLayout, чтобы назначить нужную компановку.
В калькуляторе все кнопки будут иметь указатель btn ?
Как потом их различать ,чтобы использовать ?
Хороший вопрос.
Вообще говоря, основная задача данного примера заключена в том, чтобы продемонстрировать только лишь возможности компоновки виджетов.
Если же брать непосредственно функционал кнопок, то здесь есть один нюанс. Если я не ошибаюсь, переменная-указатель 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 расположены в том порядке, в котором вновь создаваемые нами кнопки привязывались к своему объекту-родителю.
Как-то так…
спасибо, получилось
Почему в разделе "Добавление кнопок" нет в конце файла setLayout(vbox), как в остальных разделах? Было попробовано убрать setLayout() в других примерах — это не повлияло на внешний вид приложения! В чем секрет?
Секрет в этой строчке:
Дело в том, что компоновку можно назначить как через вызов функции setLayout(), так и при создании самой компоновки, передавая в качестве параметра указатель на родительский объект (т.е. указатель но тот объект, которому собираемся назначить компоновку). Получается, что в таких случаях при помощи вышеописанной строчки мы устанавливаем компоновку еще до вызова setLayout() и именно поэтому, убрав вызов setLayout(), вы не наблюдаете никаких изменений.
Попробуйте вместо:
…использовать…
P.S.: Спасибо за замечание, исправим 🙂