首先我要问,创建一个容器小部件来只保存一个小部件,没有额外的填充、布局或其他“开销”有什么意义?为什么不只显示将包含的小部件?
其次,没有什么说你必须有一个QLayout
内部 a QWidget
。布局简单地QWidget::setGeometry()
在子小部件上使用(或类似方式)移动任何包含的小部件。实现一个QWidget
使子小部件的大小与它自己的大小相匹配的方法是微不足道的,尽管它相当毫无意义,因为这就是QLayout
它的目的。但我在下面包含了这样一个例子(C++,抱歉)
上的顶级QLayout
集QWidget
具有默认的内容边距(围绕所包含的小部件填充)。这可以很容易地删除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.
}
添加:扩展我关于成为小部件与拥有(或管理)小部件的评论。
我碰巧正在开发一个实用程序应用程序,它在几个部分中使用了这两种范式。我不打算包含所有代码,但希望足以说明问题。有关它们的使用方式,请参见下面的屏幕截图。(该应用程序用于测试我正在执行的一些绘画和转换代码,与Qt 文档中的转换示例非常相似(并开始使用)。
什么下面实际上是部分代码做并不重要,关键是如何他们正在实施,再次明确意味着不同的方法说明一个“控制器”的视觉元素。
第一个例子是东西是一个小部件,即,从继承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;
};
