Сначала я должен спросить, какой смысл создавать контейнерный виджет, чтобы он просто содержал один виджет, без дополнительных отступов, макетов или других «накладных расходов»? Почему бы просто не показать виджет, который будет содержаться?
Во-вторых, ничто не говорит о том, что у вас должно быть QLayout
внутри QWidget
. Макет просто перемещает любые содержащиеся в нем виджеты, используя QWidget::setGeometry()
(или аналогичный) для дочерних виджетов. Тривиально реализовать QWidget
, который устанавливает размер дочернего виджета в соответствии с его собственным размером, хотя это довольно бессмысленно, потому что именно для этого QLayout
. Но я включил такой пример ниже (C ++, извините)
У верхнего уровня QLayout
, установленного на QWidget
, есть поля содержимого по умолчанию (с отступом вокруг содержащихся виджетов). Это может быть легко удалено с помощью QLayout::setContentMargins(0, 0, 0, 0)
(как упомянуто в предыдущем комментарии).
"Без макета" "passthrough" QWidget
:
#include <QWidget>
class PassthroughWidget : public QWidget
{
Q_OBJECT
public:
PassthroughWidget(QWidget *child, QWidget *parent = nullptr) :
QWidget(parent),
m_child(child)
{
if (m_child)
m_child->setParent(this); // assume ownership
}
protected:
void resizeEvent(QResizeEvent *e) override
{
QWidget::resizeEvent(e);
if (m_child)
m_child->setGeometry(contentsRect()); // match child widget to content area
}
QWidget *m_child; // Actually I'd make it a QPointer<QWidget> but that's another matter.
}
ДОБАВЛЕНО : Чтобы расширить мои комментарии относительно , являющегося виджетом, против , имеющего (или управляющего) виджета (ов).
Просто я работаю над служебным приложением, которое использует обе парадигмы для нескольких частей. Я не собираюсь включать весь код, но, надеюсь, достаточно, чтобы понять суть. Смотрите скриншот ниже, чтобы узнать, как они используются. (Приложение для тестирования некоторого кода рисования и преобразования, которое я делаю, очень похоже на (и начал жизнь как) Пример преобразований в документах Qt.)
Что части кода, приведенные ниже, на самом деле не важны, дело в том, как они реализованы, опять-таки специально для иллюстрации различных подходов к "контроллеру" для визуальных элементов.
Первый пример - это то, что является виджетом, то есть наследуется от QWidget
(или QFrame
в данном случае) и использует другие виджеты для представления "унифицированного" пользовательского интерфейса и API. Это редактор для двух значений double
, например, для ширины / высоты размера или значения координаты x / y. Два значения могут быть связаны, поэтому изменение одного из них также приведет к изменению другого.
class ValuePairEditor : public QFrame
{
Q_OBJECT
public:
typedef QPair<qreal, qreal> ValuePair;
explicit ValuePairEditor(QWidget *p = nullptr) :
QFrame(p)
{
setFrameStyle(QFrame::NoFrame | QFrame::Plain);
QHBoxLayout *lo = new QHBoxLayout(this);
lo->setContentsMargins(0,0,0,0);
lo->setSpacing(2);
valueSb[0] = new QDoubleSpinBox(this);
...
connect(valueSb[0], QOverload<double>::of(&QDoubleSpinBox::valueChanged),
this, &ValuePairEditor::onValueChanged);
// ... also set up the 2nd spin box for valueSb[1]
linkBtn = new QToolButton(this);
linkBtn->setCheckable(true);
....
lo->addWidget(valueSb[0], 1);
lo->addWidget(linkBtn);
lo->addWidget(valueSb[1], 1);
}
inline ValuePair value() const
{ return { valueSb[0]->value(), valueSb[1]->value() }; }
public slots:
inline void setValue(qreal value1, qreal value2) const
{
for (int i=0; i < 2; ++i) {
QSignalBlocker blocker(valueSb[i]);
valueSb[i]->setValue(!i ? value1 : value2);
}
emit valueChanged(valueSb[0]->value(), valueSb[1]->value());
}
inline void setValue(const ValuePair &value) const
{ setValue(value.first, value.second); }
signals:
void valueChanged(qreal value1, qreal value2) const;
private slots:
void onValueChanged(double val) const {
...
emit valueChanged(valueSb[0]->value(), valueSb[1]->value());
}
private:
QDoubleSpinBox *valueSb[2];
QToolButton *linkBtn;
};
Теперь для другого примера используется «контроллер» QObject
, который управляет набором виджетов, но неСам ничего не отображаю. Виджеты доступны управляющему приложению для размещения по мере необходимости, в то время как контроллер предоставляет унифицированный API для взаимодействия с виджетами и данными. Контроллеры могут быть созданы или уничтожены по мере необходимости.
В этом примере управляется QWidget
, который является «областью рендеринга» для выполнения некоторой пользовательской рисования, и «settings» QWidget
, которая изменяет свойства в области рендеринга,Виджет настроек имеет дополнительные подвиджеты, но они не предоставляются напрямую управляющему приложению. Фактически он также использует ValuePairEditor
сверху.
class RenderSet : public QObject
{
Q_OBJECT
public:
RenderSet(QObject *p = nullptr) :
QObject(p),
area(new RenderArea()),
options(new QWidget())
{
// "private" widgets
typeCb = new QComboBox(options);
txParamEdit = new ValuePairEditor(options);
...
QHBoxLayout *ctrLo = new QHBoxLayout(options);
ctrLo->setContentsMargins(0,0,0,0);
ctrLo->addWidget(typeCb, 2);
ctrLo->addWidget(txParamEdit, 1);
ctrLo->addLayout(btnLo);
connect(txParamEdit, SIGNAL(valueChanged(qreal,qreal)), this, SIGNAL(txChanged()));
}
~RenderSet() override
{
if (options)
options->deleteLater();
if (area)
area->deleteLater();
}
inline RenderArea *renderArea() const { return area.data(); }
inline QWidget *optionsWidget() const { return options.data(); }
inline Operation txOperation() const
{ return Operation({txType(), txParams()}); }
inline TxType txType() const
{ return (typeCb ? TxType(typeCb->currentData().toInt()) : NoTransform); }
inline QPointF txParams() const
{ return txParamEdit ? txParamEdit->valueAsPoint() : QPointF(); }
public slots:
void updateRender(const QSize &bounds, const QPainterPath &path) const {
if (area)
...
}
void updateOperations(QList<Operation> &operations) const {
operations.append(txOperation());
if (area)
...
}
signals:
void txChanged() const;
private:
QPointer<RenderArea> area;
QPointer<QWidget> options;
QPointer<QComboBox> typeCb;
QPointer<ValuePairEditor> txParamEdit;
};