MyTetra Share
Делитесь знаниями!
Правила создания виджетов в Qt: на стеке или динамически?
Время создания: 09.08.2021 13:17
Автор: xintrea
Текстовые метки: qt, widget, виджет, диалог, создание, стек, динамическая память, куча, new, менеджер размещения, слой, layout
Раздел: Компьютер - Программирование - Язык C++ (Си++) - Библиотека Qt - Принципы написания кода
Запись: xintrea/mytetra_syncro/master/base/1628504234zmc35k1im9/text.html на raw.github.com

При программировании на Qt довольно часто возникает необходимость создать виджет или диалог, который будет в себе содержать другие виджеты и менеджеры размещения. Если при этом не пользоваться ui-файлами, то возникает вопрос: как правильно создать виджеты, из которых состоит текущий объект?


Ответ на этот вопрос надо разбить на два важных пункта. Первый - это понимание, каким образом взаимодействуют между собой основной виджет, менеджеры размещения (layout-ы), и виджеты, которые размещаются в этих самых лайаутах. Второй - это особенности уничтожения "подчиненных" объектов при срабатывании деструктора виджета.


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



Создание виджетов


Забегая вперед, следует сразу сказать, что создавать виджеты, которые являются полями класса основного виджета, следует в динамической памяти (то есть, в куче) через ключевое слово new. На это есть ряд причин, описанных ниже по тексту. В любом случае, если создавать объект формы на стеке, то гарантированно будут возникать проблемы, в том числе и трудноуловимые, из-за которых разработка может превратиться в бессмысленное мучение.


Итак, для начала нужно принять за аксиому, что виджеты должны создаваться в куче. Выглядит это примерно так:



Заголовок


class MyWidget : public QWidget

{

Q_OBJECT


public:

explicit MyWidget(QWidget *parent = nullptr);


private:


QTextEdit *m_textEdit;

QPushButton *m_pushButton;


};



Реализация


MyWidget::MyWidget(QWidget *parent) : QWidget(parent)

{

m_textEdit=new QTextEdit(this);

m_pushButton=new QPushButton(this);

...

}



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



QTextEdit (QWidget *parent = nullptr)


QPushButton (QWidget *parent = nullptr)



Этот указатель parent нужен для того, чтобы исключить утечку памяти. Метаобъектная система Qt предполагает, что когда будет удаляться основной объект, он вначале удалит все свои дочерние объекты. Для того чтобы основной виджет знал, какие объекты дочерние, метаобъектную систему Qt надо проинформировать о родителе подчиненного объекта. Это как раз и делается путем указания родителя в конструкторе подчиненного объекта.



Расстановка виджетов через менеджеры размещения


В Qt имеется несколько классов менеджеров размещения. Вот основные:



  • QHBoxLayout
  • QVBoxLayout
  • QGridLayout



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



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



Дело в том, что менеджеры размещения наследуются от абстрактного менеджера размещения QLayout, который, в свою очередь, наследуется от QObject. Таким образом, базовым классом менеджеров размещения является QObject, а не QWidget, как это происходит для всех виджетов.


А это значит, что, подчиненным виджетам невозможно установить в качестве родителя менеджер размещения, так как в качестве родителя виджета должен выступать объект класса QWidget, а не QObject. Зато самому менеджеру размещения можно установить в качестве родителя основной виджет, о чем говорит его второй вариант конструктора:



QLayout() - первый вариант конструктора

QLayout(QWidget *parent) - второй вариант конструктора



Что это значит на практике? Это значит, что структура иерархии объектов должна быть следующая: основной виджет имеет в качестве дочерних виджетов те виджеты, из которых он состоит. Кроме того, основной виджет имеет дочерний объект - менеджер размещения. Когда дочерние виджеты "вставляются" в менеджер размещения через метод addWidget(), этот менеджер размещения не становится владельцем (т. е. родителем) дочерних виджетов. Он всего лишь знает о тех виджетах, которые в него "вставлены", и занимается только расстановкой этих виджетов на экране.


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


Дописать...


Так же в этом разделе:
 
MyTetra Share v.0.65
Яндекс индекс цитирования