首先我要问,创建一个容器小部件以仅容纳一个小部件,没有额外的填充、布局或其他“开销”有什么意义?为什么不只显示将包含的小部件?
其次,没有什么说您必须在 QWidget 中包含 QLayout。布局只是在子小部件上使用QWidget::setGeometry()(或类似的)移动任何包含的小部件。实现一个 QWidget 来调整子部件的大小以匹配它自己的大小是微不足道的,尽管它相当没有意义,因为这就是 QLayout 的用途。但我在下面包含了这样一个示例(C++,抱歉)
在QWidget 上设置的顶级QLayout 具有默认内容边距(包含的小部件周围的填充)。这可以通过QLayout::setContentMargins(0, 0, 0, 0) 轻松删除(如之前的评论中所述)。
“无布局”“直通”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.
}
添加:扩展我的 cmets 关于成为小部件与拥有(或管理)小部件的关系。
我恰好正在开发一个实用程序应用程序,该应用程序在几个部分中使用了这两种范例。我不会包含所有代码,但希望足以说明问题。请参阅下面的屏幕截图以了解它们的使用方式。 (该应用程序用于测试我正在做的一些绘画和转换代码,与 Qt 文档中的Transformations Example 非常相似(并开始使用)。)
什么下面的代码部分实际上做了什么并不重要,重点是如何它们是如何实现的,再次专门用于说明“控制器”的不同方法" 用于视觉元素。
第一个示例是成为小部件,即从QWidget(或本例中的QFrame)继承并使用其他小部件来呈现“统一”的 UI 和 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,它是一个“渲染区域”,用于进行一些自定义绘画,以及一个“设置”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;
};