如何将QObject指针的属性公开给QML

时间:2015-07-16 05:43:15

标签: qt qml qtquick2 qproperty

我正在对我的班级进行非常简短(和部分)的描述以显示我的问题。基本上我已经设置了两个属性。

class Fruit : public QObject
{
Q_OBJECT
  ....
public:
    Q_PROPERTY( int price READ getPrice NOTIFY priceChanged)

    Q_PROPERTY(Fruit * fruit READ fruit WRITE setFruit NOTIFY fruitChanged)
}

在我的QML中,如果我访问price属性,它运行良好。但是,如果我访问显然返回fruit的{​​{1}}属性,然后尝试使用其Fruit属性,那么这不起作用。这不应该以这种方式工作吗?

price

第二个返回Text { id: myText anchors.centerIn: parent text: basket.price // shows correctly //text: basket.fruit.price // doesn't show } ,它也是一个属性,它有Fruit属性,但它似乎无法访问该属性?这有用吗?

已更新

我包含了我的源代码。我用price创建了一个新的演示,这种方式更有意义。我试着根据收到的答案让它工作但没有运气。

HardwareComponent类是HardwareComponentComputer的基类。

的main.cpp

CPU

main.qml

#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>

class HardwareComponent : public QObject
{
   Q_OBJECT
public:
   HardwareComponent() : m_price(0) {}
   virtual int price() = 0;
   virtual Q_INVOKABLE void add(HardwareComponent * item) { m_CPU = item; }
   HardwareComponent * getCPU() const { return m_CPU; }

   Q_SLOT virtual void setPrice(int arg)
   {
      if (m_price == arg) return;
      m_price = arg;
      emit priceChanged(arg);
   }
   Q_SIGNAL void priceChanged(int arg);

protected:
   Q_PROPERTY(HardwareComponent * cpu READ getCPU);
   Q_PROPERTY(int price READ price WRITE setPrice NOTIFY priceChanged)

   HardwareComponent * m_CPU;
   int m_price;
};

class Computer : public HardwareComponent
{
   Q_OBJECT
public:
   Computer() { m_price = 500; }

   int price() { return m_price; }
   void setprice(int arg) { m_price = arg; }
};

class CPU : public HardwareComponent
{
   Q_OBJECT
public:
   CPU() { m_price = 100; }

   int price() { return m_price; }
   void setprice(int arg) { m_price = arg; }
};

int main(int argc, char *argv[])
{
   HardwareComponent * computer = new Computer;
   CPU * cpu = new CPU;
   computer->add( cpu );

   QGuiApplication app(argc, argv);
   QQmlApplicationEngine engine;

   engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
   engine.rootContext()->setContextProperty("computer", computer); 

   return app.exec();
}

#include "main.moc"

这是我所有项目文件的完整源代码。

当我运行它时,我得到了这个输出:

  

启动C:\ Users \ User \ Documents \ My Qt   项目\集结硬件Desktop_Qt_5_4_0_MSVC2010_OpenGL_32bit-调试\调试\ hardware.exe ...   启用QML调试。只能在安全的环境中使用它。   qrc:/MainForm.ui.qml:20:ReferenceError:未定义计算机

即使它在第20行(computer.price)行发出警告,它仍然可以工作并显示计算机的价格(= 500)。如果更改import QtQuick 2.3 import QtQuick.Window 2.2 Window { visible: true width: 360 height: 360 Text { anchors.centerIn: parent text: computer.price // works //text: computer.cpu.price // doesn't work, doesn't show any value } } ,则会报告相同的警告,但价格不再显示 - 它似乎无法正常工作。

问题是因为价格是虚拟财产,它有效!但如果我在计算机组件内的另一个硬件组件上使用此属性,它就不起作用! Mido发布的代码/答案让我希望有一个解决方案,它看起来非常接近!我希望我能做到这一点。

2 个答案:

答案 0 :(得分:2)

我在您的示例中创建了一个Fruit类,但它工作正常。我创建了Fruit的新实例,我可以获得price值。这是我的代码:

fruit.h

#ifndef FRUIT_H
#define FRUIT_H

#include <QObject>
#include <QQuickView>

class Fruit : public QObject
{
    Q_OBJECT
    Q_PROPERTY( int price READ getPrice WRITE setPrice  NOTIFY priceChanged)
    Q_PROPERTY(Fruit * fruit READ fruit WRITE setFruit NOTIFY fruitChanged)

public:
    Fruit();
    ~Fruit();
    Fruit *fruit();
    void setFruit(Fruit * value);
    int getPrice();
    void setPrice(int value);

signals:
    void fruitChanged();
    void priceChanged(int price);

private:
    int _price;
    Fruit *_fruit;
};

#endif // FRUIT_H

fruit.cpp

#include "fruit.h"

Fruit::Fruit() :
    _price(1),_fruit(this)
{
}

Fruit::~Fruit()
{
    if(_fruit)
    {
        delete _fruit;
        _fruit = NULL;
    }
}

Fruit *Fruit::fruit()
{
    //Uncomment this line to test the set
    //_fruit = new Fruit;
    return _fruit;
}

void Fruit::setFruit(Fruit *value)
{
    _fruit = value;
    emit fruitChanged();
}

int Fruit::getPrice()
{
    return _price;
}

void Fruit::setPrice(int value)
{
    _price = value;
    emit priceChanged(_price);
}

的main.cpp

#include <QGuiApplication>
#include <QQuickView>
#include <QQmlContext>
#include "fruit.h"

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);
    QQuickView view;
    view.rootContext()->setContextProperty("basket", new Fruit);
    view.setSource(QStringLiteral("qml/main.qml"));
    view.show();
    return app.exec();
}

main.qml

import QtQuick 2.2

Rectangle {
    width:800
    height: 480
    property var obj
    color: "yellow"

    Text{
        text: basket.fruit.price
        font.pixelSize: 20
    }

    Component.onCompleted: {
        basket.price = 20
        console.log(basket.fruit.price)
    }
}

答案 1 :(得分:2)

只有一些错误,特别是与QML相关:

  1. 必须在引擎加载qml文件之前设置上下文属性。

  2. 您尚未注册HardwareComponent类型。有关详细信息,请参阅Defining QML Types from C++

  3. 除了QML方面,您似乎希望硬件是具有树结构的复合材料。由于QObject已经是复合词,因此您可以利用此优势。硬件项目树可以是对象树。添加或删除子项时,QObject会通知父母:因此,在添加和删除子项时,可以很容易地保持价格最新。

    每个组件都有unitPrice:这是隔离组件的价格。 price是组件单价的总和,以及其子级的价格。

    您可以缓存它并仅在单位价格更改或子价格更改时更新它,而不是遍历整个树以获取总价格。类似地,当前的CPU可以被缓存,您可以在任何给定的时间强制执行单个CPU,等等。该示例是功能性的,但只有最小的草图。

    我还展示了如何更改计算机中的CPU类型。有价格变化和CPU变化的通知。当cpu属性发生变化时,以及任何price属性发生变化时,QML UI会做出适当的反应。

    screenshot of the example

    的main.cpp

    #include <QGuiApplication>
    #include <QtQml>
    
    class HardwareComponent : public QObject {
       Q_OBJECT
       Q_PROPERTY(QString category MEMBER m_category READ category CONSTANT)
       Q_PROPERTY(HardwareComponent * cpu READ cpu WRITE setCpu NOTIFY cpuChanged)
       Q_PROPERTY(int price READ price NOTIFY priceChanged)
       Q_PROPERTY(int unitPrice MEMBER m_unitPrice READ unitPrice WRITE setUnitPrice NOTIFY unitPriceChanged)
       QString m_category;
       int m_unitPrice;
       bool event(QEvent * ev) Q_DECL_OVERRIDE {
          if (ev->type() != QEvent::ChildAdded && ev->type() != QEvent::ChildRemoved)
             return QObject::event(ev);
          auto childEvent = static_cast<QChildEvent*>(ev);
          auto child = qobject_cast<HardwareComponent*>(childEvent->child());
          if (! child) return QObject::event(ev);
          if (childEvent->added())
             connect(child, &HardwareComponent::priceChanged,
                     this, &HardwareComponent::priceChanged, Qt::UniqueConnection);
          else
             disconnect(child, &HardwareComponent::priceChanged,
                        this, &HardwareComponent::priceChanged);
          emit priceChanged(price());
          if (child->category() == "CPU") emit cpuChanged(cpu());
          return QObject::event(ev);
       }
    public:
       HardwareComponent(int price, QString category = QString(), QObject * parent = 0) :
          QObject(parent), m_category(category), m_unitPrice(price) {}
       HardwareComponent * cpu() const {
          for (auto child : findChildren<HardwareComponent*>())
             if (child->category() == "CPU") return child;
          return 0;
       }
       Q_INVOKABLE void setCpu(HardwareComponent * newCpu) {
          Q_ASSERT(!newCpu || newCpu->category() == "CPU");
          auto oldCpu = cpu();
          if (oldCpu == newCpu) return;
          if (oldCpu) oldCpu->setParent(0);
          if (newCpu) newCpu->setParent(this);
          emit cpuChanged(newCpu);
       }
       Q_SIGNAL void cpuChanged(HardwareComponent *);
       virtual int price() const {
          int total = unitPrice();
          for (auto child : findChildren<HardwareComponent*>(QString(), Qt::FindDirectChildrenOnly))
             total += child->price();
          return total;
       }
       Q_SIGNAL void priceChanged(int);
       int unitPrice() const { return m_unitPrice; }
       void setUnitPrice(int unitPrice) {
          if (m_unitPrice == unitPrice) return;
          m_unitPrice = unitPrice;
          emit unitPriceChanged(m_unitPrice);
          emit priceChanged(this->price());
       }
       Q_SIGNAL void unitPriceChanged(int);
       QString category() const { return m_category; }
    };
    
    struct Computer : public HardwareComponent {
       Computer() : HardwareComponent(400) {}
    };
    
    class FluctuatingPriceComponent : public HardwareComponent {
       QTimer m_timer;
       int m_basePrice;
    public:
       FluctuatingPriceComponent(int basePrice, const QString & category = QString(), QObject * parent = 0) :
          HardwareComponent(basePrice, category, parent),
          m_basePrice(basePrice) {
          m_timer.start(250);
          connect(&m_timer, &QTimer::timeout, [this]{
             setUnitPrice(m_basePrice + qrand()*20.0/RAND_MAX - 10);
          });
       }
    };
    
    int main(int argc, char *argv[])
    {
       QGuiApplication app(argc, argv);
       QQmlApplicationEngine engine;
       Computer computer;
       HardwareComponent memoryBay(40, "Memory Bay", &computer);
       HardwareComponent memoryStick(60, "Memory Stick", &memoryBay);
       FluctuatingPriceComponent cpu1(100, "CPU", &computer);
       HardwareComponent cpu2(200, "CPU");
    
       qmlRegisterUncreatableType<HardwareComponent>("bar.foo", 1, 0, "HardwareComponent", "");
       engine.rootContext()->setContextProperty("computer", &computer);
       engine.rootContext()->setContextProperty("cpu1", &cpu1);
       engine.rootContext()->setContextProperty("cpu2", &cpu2);
       engine.load(QUrl("qrc:/main.qml"));
       return app.exec();
    }
    
    #include "main.moc"
    

    main.qml

    import QtQuick 2.0
    import QtQuick.Controls 1.2
    import QtQuick.Window 2.0
    
    Window {
        visible: true
        Column {
            anchors.horizontalCenter: parent.horizontalCenter
            anchors.verticalCenter: parent.verticalCenter
            Text {
                text: "Computer price: " + computer.price
            }
            Text {
                text: "CPU price: " + (computer.cpu ? computer.cpu.price : "N/A")
            }
            Button {
                text: "Use CPU 1";
                onClicked: { computer.setCpu(cpu1) }
            }
            Button {
                text: "Use CPU 2";
                onClicked: { computer.setCpu(cpu2) }
            }
            Button {
                text: "Use no CPU";
                onClicked: { computer.setCpu(undefined) }
            }
        }
    }