代码之家  ›  专栏  ›  技术社区  ›  Jens Munk

将Q_属性从一个类转发到另一个类

  •  0
  • Jens Munk  · 技术社区  · 5 年前

    我使用QtWidgets编写了一个GUI组件,并通过继承为该组件创建了一个QtQuick包装器 QQuickItem 。原始组件公开了许多可以直接公开的属性,而有些属性需要引入从中派生的新类 QQuickItem .有没有更优雅的方法。

    class MyQuickItem : public QQuickItem {
      Q_OBJECT
     public:
      ...
      Q_PROPERTY(int someProperty READ getSomeProperty WRITE setSomeProperty);
      int getSomeProperty() const {
        return m_myQtWidget->getSomeProperty();
        // return m_myQtWidget->property("someProperty").toInt();
      }
      void setSomeProperty(int val) {
        m_myQtWidget->setSomeProperty(val);
      }
     private:
      MyQtWidget* m_myQtWidget;
    };
    
    class MyQtWidget : public QWidget {
     Q_OBJECT
     ...
    };
    

    有没有办法绕过这个愚蠢的锅炉铭牌编码。我在想,有没有可能为 Q_PROPERTY 哪个接受要计算的表达式?

    0 回复  |  直到 5 年前
        1
  •  1
  •   Jack White    5 年前

    这里的问题是,我们不能同时从这两方面继承 QtWidget QtQuickItem 因为两者都是 QObject Qt-MOC根本不支持这一点。它也不支持基于QObject的类的模板。

    所以不幸的是,我们必须编写额外的代码来以这种或那种方式传递属性。

    继承

    首先,我想指出,没有理由嵌入毫无意义的内容 QtWidget 她喜欢 QQuickItem s、 所以我建议将普通逻辑分成如下基类:

    class MyBaseItem {...};
    //Please note that QObject-based class MUST always be first in inheritance list!
    class MyQuickItem : public QQuickItem, public MyBaseItem {...};
    class MyQtWidget : public QWidget, public MyBaseItem {...};
    

    为了减少传递代码的数量,我们需要定义如下宏:

    #define PASSTHROUGH_Q_PROPERTY_GETSET(VALTYPE,PROPNAME) \
      Q_PROPERTY( VALTYPE PROPNAME READ get_ ## PROPNAME WRITE set_ ## PROPNAME )
    
    #define PASSTHROUGH_Q_PROPERTY_VALUE(VALTYPE,PROPNAME) \
      PASSTHROUGH_Q_PROPERTY_GETSET(VALTYPE, PROPNAME) \
      VALTYPE get ## PROPNAME () const { return m_ ## PROPNAME (); } \
      void set ## PROPNAME (VALTYPE val) { m_ ## PROPNAME = val; } 
    

    说明:

    尽管moc从Qt5开始解析头文件而不是编译头文件 we can apparently use Q_macros in another macros

    此代码利用 token-pasting preprocessor operator ## 它将标识符的两部分粘在一起,包括宏参数。所以 #define F(A) my ## A 将导致 int F(thing); 相当于 int mything;

    您需要通过以下方式创建基本属性:

    class MyBaseItem 
    {
    public:
      int get_someProperty() { return 42; } // obtain value
      void set_someProperty(int val) {} // set data to val
    
    protected:
      float m_myValueProperty; //a second property, this is a simple number
    };
    

    然后在这两个继承人类中,您可以将它们添加为 Q_PROPERTY 这么说:

    class MyQuickItem : public QQuickItem, public MyBaseItem 
    {
      Q_OBJECT
    public:
      PASSTHROUGH_Q_PROPERTY_GETSET(int, someProperty)
      PASSTHROUGH_Q_PROPERTY_VALUE(float, myValueProperty)
    };
    

    由于moc不支持模板,因此必须将该代码复制到两个继承人类中的每一个类中。我认为它不支持中产阶级 #include 但我还没有测试过。

    如何添加对信号和只读属性等的支持应该很容易理解。

    不幸的是,C/C++预处理器不允许更改标识符的大小写,因此最终会导致错误 set_myProperty 而不是正确的案例 setMyProperty .如果你非常想要后者,你需要一个额外的宏参数 UPCASE_PROPNAME 设置为大写 MyProperty -除了 myProperty 这仍然是需要的。请参阅下一节中此方法的示例。

    没有遗产

    如果由于某种原因创建基类是不可行的,这里是您最初请求的代码,但要注意嵌入过时的QWidget可能会导致的问题

    #define PASSTHROUGH_Q_PROPERTY(VALTYPE,PROPNAME,UNAME) \
      Q_PROPERTY(VALTYPE PROPNAME READ get ## UNAME WRITE set ## UNAME ); \
      VALTYPE get ## UNAME () const { return m_myQtWidget->get ## UNAME (); } \
      void set ## UNAME (VALTYPE val) { m_myQtWidget->set ## UNAME (val); } 
    

    用法:

    PASSTHROUGH_Q_PROPERTY(int, someProperty, SomeProperty)