Если у вас сложный виджет вроде панели с заголовком (как во вложении), а ниже заголовка -контейнер для виджетов, которые в него надо добавлять в дизайнере и надо таких панелей много, то чтобы не писать все вручную (добавление виджетов в контейнер), а визуально все лицезреть и руками добавлять в контейнер виджеты, можно сделать
небольшой хак.
Для таких целей правильнее сделать плагин для дизайнера и правильно все делать все тогда... но у нас нет времени или мы не знаем, как это делать, или и то и другое (как например, у меня (плюсуем "влом").
Тогда есть в дизайнере возможность преобразовать виджет к нужному типу (правой кнопка мыши по виджету и "преобразовать в", указываем имя класса и заголовочный файл, жмемь добавить, преобразовать). Надо отметить, что дизайнер будет генерировать код, который как бы работает с QWidget, но на самом деле использует в качестве имени класса тот, который мы указали. Поэтому с этим мы и поиграемся.
Итак поместим объект QWidget на форму, преобразуем его в WidgetsGroup (который будет представлять из себя панель с заголовком и контейнером для кнопок, лейболов... чего угодно.
Напишем этот класс (я сделал это, добавивь класс формы, как во вложении).
Приведем код заголовочного файла класса.
#ifndef WIDGETSGROUP_H
#define WIDGETSGROUP_H
#include <QWidget>
#include <QVBoxLayout>
namespace Ui {
class WidgetsGroup;
}
class WidgetsGroup : public QWidget
{
Q_OBJECT
public:
explicit WidgetsGroup(QWidget *parent = 0);
~WidgetsGroup();
void addWidget(QWidget* widget);
private:
Ui::WidgetsGroup *ui;
};
#endif // WIDGETSGROUP_H
И приведем файл реализации. Все в принципе обычно, как делает креатор по умолчанию.
#include "WidgetsGroup.h"
#include "ui_WidgetsGroup.h"
#include <QChildEvent>
#include <QVBoxLayout>
#include <QDebug>
#include <QCoreApplication>
WidgetsGroup::WidgetsGroup(QWidget *parent) :
QWidget(parent),
ui(new Ui::WidgetsGroup)
{
ui->setupUi(this);
}
WidgetsGroup::~WidgetsGroup()
{
delete ui;
}
Теперь будем думать. Для того, чтобы дизайнер, а если быть точнее UI Compiler сгенерировал код, который бы сам добавлял виджеты в контейнер Contents, то было бы очень просто, мы просто добавили метод setLayout в этот класс, с помощью которого бы потом все виджеты добавлись куда нужно. Но код генеруется примерно такой:
widget = new WidgetsGroup(frame);
widget->setObjectName(QString::fromUtf8("widget"));
verticalLayout_2 = new QVBoxLayout(widget);
То есть назначение лейаута идет в указании парента в конструкторе лейаута. Это немного все усложняет. Но только немного.
При добавлении чайлда к потомкам QObject (коим является и этот класс) происходит событие childEvent. Переопределим метод класса, который обрабатывает это событие.
void WidgetsGroup :: childEvent (QChildEvent* event)
{
}
Поместим туда определение название класса, который добавлеятся в качестве чайлда и, если он QObject, то наверняка это тот самый потомок QLayout, которого добавляет код формы к нашему классу вместе с виджетами чайлдами (кнопками, лейблами...). Если все-таки это виджет, то это те самые кнопки и лейблы.
void WidgetsGroup :: childEvent (QChildEvent* event)
{
if (event->added())
{
if (event->child()->metaObject()->className() == QString("QObject").toLatin1())
{
}
else if (event->child()->isWidgetType())
{
}
}
}
Здесь отметим, что если мы проведем реперант лейаута сразу:
ui->ContentsFrame->setLayout(static_cast<QLayout*>(event->child()));
То наткнемся на сег. фолт, который будет происходит, когда что-то в мутексе пытается обратиться к полю d->recursive и т.д. (глубокая отладка ничего прояснить не дала по этому поводу) в одном из файлов Qt. Поэтому сразу репарент производить не следует, для этого воспользуемся сигнал-слотовым механизмом Qt, т.к. postEvent(Contents, child) тоже не дал нужных результатов.
Добавим к классу сигнал wantAddLayout (QLayout* layout), который сигнализирует о том, что класс хочет добавить лейаут в свой виджет-контейнер Contents:
void wantAddLayout(QLayout* layout);
И прайвет-слот, который будет принимать этот сигнал:
private slots:
void addLayout (QLayout* layout);
Теперь свяжем в конструкторе сигнал со слотом, используя тип соединения QueuedConnection, что позволит отложить на время репарент, дав тем самым всему что генерировало сег. фолт "инициализировывывы...ваться".
connect(this, SIGNAL(wantAddLayout(QLayout*)), this, SLOT(addLayout(QLayout*)), Qt::QueuedConnection);
В обработчике события childEvent добавим следующие строки
void WidgetsGroup :: childEvent (QChildEvent* event)
{
if (event->added())
{
if (event->child()->metaObject()->className() == QString("QObject").toLatin1())
{
emit wantAddLayout(static_cast<QLayout*>(event->child()));
event->ignore();
}
else if (event->child()->isWidgetType())
{
event->ignore();
}
}
}
Теперь надо подумать о том, что не только тот код дизайнере захочет добавить лейаут к виджету, который мы будем использовать позже для дизайна главной формы, но еще и код, который располагает виджеты внутри этого класса по местам. Поэтому добавим флаг, означающий что эта форма построена и все последующие добавления чайлдов это уже из главной формы.
bool uiwasntbuilt;
Используем этот флаг в конструкторе:
WidgetsGroup::WidgetsGroup(QWidget *parent) :
QWidget(parent),
ui(new Ui::WidgetsGroup)
{
uiwasntbuilt = true;
ui->setupUi(this);
uiwasntbuilt = false;
connect(this, SIGNAL(wantAddLayout(QLayout*)), this, SLOT(addLayout(QLayout*)), Qt::QueuedConnection);
}
... и в обработчике события:
void WidgetsGroup :: childEvent (QChildEvent* event)
{
if (!uiwasntbuilt)
{
if (event->added())
...
}
else
{
event->accept();
QWidget::childEvent(event);
}
}
Таким образом все готово. Можно использовать этот класс для того, чтобы мутить много панелей в главной форме и в них располагать виджеты-кнопки, лейблы... (как показано во вложении)
Код и исполняемый файл релиз-версии находится двумя постами ниже (из-за переполнения вложений для этого поста).