reactjs - Qt "passthrough"或 "container"小部件

标签 reactjs qt design-patterns pyside2

在 Qt/PySide2 中,是否有 Qt 小部件这样的东西,它可以简单地传递到包装的小部件,而不添加任何额外的布局层等。

我有 Web 前端背景,所以我的思维模型是 React container component它添加了一些行为,但随后简单地呈现了一个包装的演示组件。

但是,在 Qt 中似乎没有一种方法可以在不至少在包装小部件中创建布局的情况下执行此类操作,即使该布局仅包含一个小部件。我可以看到这可能会导致多层冗余布局,这可能会导致效率低下。

我承认最好不要尝试在 Qt 中复制 React 模式,因此任何等效但更惯用的模式的建议也将受到欢迎。

最佳答案

首先我要问,创建一个容器小部件来仅容纳一个小部件,而不需要额外的填充、布局或其他“开销”有什么意义?为什么不只显示将包含的小部件?

其次,没有任何规定您必须在 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.
}

添加:扩展我关于成为小部件与拥有(或管理)小部件的评论。

我恰好正在开发一个实用程序应用程序,它在几个部分中使用了这两种范例。我不会包含所有代码,但希望足以阐明要点。请参阅下面的屏幕截图了解它们的使用方式。 (该应用程序用于测试我正在执行的一些绘制和转换代码,与 Qt 文档中的 Transformations Example 非常相似(并且开始时为)。)

下面的代码部分实际做什么并不重要,重点是如何它们的实现,再次专门用于说明“ Controller ”的不同方法“用于视觉元素。

第一个示例是一个小部件,即继承自 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;
};

现在来看另一个例子,使用“ Controller ”QObject 来管理一组小部件,但本身不显示任何内容。管理应用程序可以根据需要放置小部件,而 Controller 则提供统一的 API 来与小部件和数据进行交互。可以根据需要创建或销毁 Controller 。

此示例管理一个 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;
};

enter image description here

关于reactjs - Qt "passthrough"或 "container"小部件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58520625/

相关文章:

qt - 如何从 Qt Creator 的文本字段中获取文本?

c# - 我的网络应用程序应该遵循什么设计模式?

design-patterns - 单例模式有哪些缺点或缺点?

javascript - 如何为多个导入的模块使用单个命名空间?

reactjs - react - createMuiTheme 和 createTheme 之间的区别

javascript - ReactJs - 条件映射

javascript - 如何将四张卡片围绕在一张居中的卡片周围?

c++ - Boost::ASIO VS Qt 网络

c++ - 如何在Qt中播放QByteArray中的声音

php - 事件驱动的 PHP 框架?