3

私の問題を示すために、クラスの非常に簡単な(そして部分的な)説明をしています。基本的に、2 つのプロパティをセットアップしました。

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しかし、明らかに返されるプロパティにアクセスしてFruitからそのプロパティを使用しようとすると、うまくいきpriceません。これはこのように機能するはずではありませんか?

Text {
    id: myText
    anchors.centerIn: parent
    text: basket.price // shows correctly
    //text: basket.fruit.price // doesn't show
}

2番目のものはFruitどちらもプロパティであり、プロパティを持っていpriceますが、そのプロパティにアクセスしていないようです? これは機能するはずですか?

更新しました

ソースコードを含めています。で新しいデモを作成しましたがHardwareComponent、この方法の方が理にかなっています。受け取った回答に基づいて機能させようとしましたが、うまくいきませんでした。

HardwareComponentclass は と の基本クラスComputerですCPU

main.cpp

#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"

main.qml

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
    }
}

これは、すべてのプロジェクト ファイルの完全なソース コードです。

実行すると、次の出力が得られます。

C:\Users\User\Documents\My Qt Projects\build-hardware-Desktop_Qt_5_4_0_MSVC2010_OpenGL_32bit-Debug\debug\hardware.exe を起動しています... QML デバッグが有効になっています。安全な環境でのみ使用してください。qrc:/MainForm.ui.qml:20: ReferenceError: コンピューターが定義されていません

20 行目 (computer.price) で警告が表示されますが、それでも機能し、コンピュータの価格 (=500) が表示されます。変更するcomputer.cpu.priceと、同じ警告が報告されますが、価格が表示されなくなります - 機能していないようです。

問題は、価格が仮想プロパティであるため、機能することです! しかし、このプロパティをコンピュータ コンポーネント内の別のハードウェア コンポーネントで使用すると、機能しません! Mido によって投稿されたコード/回答は、これに対する解決策があることを願っています。かなり近いようです! 私はこれがうまくいくことを願っています。

4

2 に答える 2

5

特にQMLに関連して、いくつかの間違いがありました:

  1. エンジンが qml ファイルをロードする前に、コンテキスト プロパティを設定する必要があります。

  2. HardwareComponentタイプが登録されていません。詳細については、C++ からの QML 型の定義を参照してください。

QML の側面とは別に、ハードウェアをツリー構造の複合体にしたいようです。aQObjectはすでにコンポジットであるため、これを有利に活用できます。ハードウェア アイテムのツリーは、オブジェクトのツリーにすることができます。はQObject、子が追加または削除されたときに親に通知します。したがって、子が追加または削除されても、価格を最新に保つのは簡単です。

各コンポーネントには がありunitPriceます。これはコンポーネント単体の価格です。コンポーネントのprice単価とその子の価格の合計です。

ツリー全体をトラバースして合計価格を取得する代わりに、それをキャッシュして、単価が変更されたとき、または子の価格が変更されたときにのみ更新することができます。同様に、現在の CPU をキャッシュしたり、任意の時点で単一の CPU を適用したりできます。この例は機能的ですが、最小限のスケッチにすぎません。

また、コンピューターの CPU タイプを変更する方法も示しました。価格変更とCPU変更の両方の通知があります。QML UI は、cpuプロパティが変更されたときだけでなく、プロパティが変更されたときにも適切に反応しpriceます。

例のスクリーンショット

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) }
        }
    }
}
于 2015-07-20T15:02:40.360 に答える
2

あなたの例のようなクラスを作成しましたが、Fruitうまく機能しています。の新しいインスタンスを作成しFruit、値を取得できpriceます。これが私のコードです:

フルーツ.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

フルーツ.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)
    }
}
于 2015-07-16T19:43:48.917 に答える