在Qt / PySide2中,有一个Qt小部件可以直接传递到包装的小部件,而无需添加任何额外的布局层等。
我来自Web前端背景,所以我的思维模型是React container component的模型,它添加了一些行为,但随后仅呈现了一个包装好的表示组件。
但是,在Qt中似乎没有一种方法,至少在包装小部件中不创建布局,即使该布局仅包含一个小部件也是如此。我可以看到,这可能会导致多层冗余布局,这可能会导致效率低下。
我承认最好不要尝试在Qt中复制React模式,因此也欢迎任何等效但更惯用的模式的建议。
答案 0 :(得分:1)
在Qt中有两种管理窗口小部件的方法:通过布局或通过父项。您是否尝试过使用'parent'
方法?
...The base class of everything that appears on the screen, extends the parent-child relationship. A child normally also becomes a child widget, i.e. it is displayed in its parent's coordinate system and is graphically clipped by its parent's boundaries.
因此,基本上,如果您使用setParent
来包含小部件,则无需创建布局。
答案 1 :(得分:1)
首先,我要问的是,创建一个容器小部件以仅容纳一个小部件而没有多余的填充,布局或其他“开销”的意义是什么?为什么不只显示将包含的小部件?
第二,没有任何内容表明您必须在QLayout
中包含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文档中的Transformations Example类似(并且开始使用)。)
以下代码部分的实际作用并不重要,关键是它们的实现方式,同样专门用于说明“控制器”的不同方法。 “用于视觉元素。
第一个示例是成为小部件,即从QWidget
(在本例中为QFrame
)继承并使用其他小部件来呈现“统一” UI和API。这是两个double
值的编辑器,例如大小的width / height或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;
};
答案 2 :(得分:1)
我以类似的方式使用了QFrame,所有属性都设置为0,Shape
设置为QFrame::NoFrame
。作为最终完成繁重工作的实际小部件的哑容器,它非常有用,QStackedWidget
是其中的一个用户。引用文档:
QFrame类也可以直接用于创建没有任何内容的简单占位符框架。
但是,由于我不熟悉您概述的React方法,因此我不确定它是否是您所寻找的100%。同样不确定在不使用布局的情况下可以合理走多远。