6

I'm running into some strange behaviour where a property can be accessed directly through QObject's property function, but not through JavaScript:

#include <QApplication>
#include <QDebug>
#include <QScriptEngine>
#include <QStringList>

class Item : public QObject
{
    Q_OBJECT
public:
    Q_PROPERTY(int typeId READ typeId)
    Q_PROPERTY(int usesLeft READ usesLeft)

    Item() :
        mTypeId(0),
        mUsesLeft(-1)
    {
    }

    Item(int typeId) :
        mTypeId(typeId)
    {
        if (typeId != 0) {
            mUsesLeft = 5;
        }
    }

    Item(const Item &item) :
        QObject(0)
    {
        *this = item;
    }

    ~Item()
    {
    }

    Item& operator=(const Item& rhs)
    {
        mTypeId = rhs.mTypeId;
        mUsesLeft = rhs.mUsesLeft;
        return *this;
    }

    int typeId() const { return mTypeId; }

    int usesLeft() const { return mUsesLeft; }
    void setUsesLeft(int usesLeft) { mUsesLeft = usesLeft; }

    friend QDataStream &operator<<(QDataStream &out, const Item &item);
    friend QDataStream &operator>>(QDataStream &in, Item &item);
    friend QDebug operator<<(QDebug debug, const Item &item);
private:
    int mTypeId;
    int mUsesLeft;
};

QDataStream &operator<<(QDataStream &out, const Item &item)
{
    out << item.typeId()
        << item.usesLeft();
    return out;
}

QDataStream &operator>>(QDataStream &in, Item &item)
{
    in >> item.mTypeId
       >> item.mUsesLeft;
    return in;
}

QDebug operator<<(QDebug debug, const Item &item)
{
    debug.nospace() << "(Item typeId=" << item.typeId()
                    << ", usesLeft=" << item.usesLeft();
    return debug.space();
}

Q_DECLARE_METATYPE(Item)

class ItemStack : public QObject
{
    Q_OBJECT
public:
    Q_PROPERTY(Item *item READ item)
    Q_PROPERTY(int size READ size)

    ItemStack() :
        mSize(0)
    {
    }

    ItemStack(const ItemStack &rhs) :
        QObject()
    {
        *this = rhs;
    }

    ItemStack(const Item &item, int size) :
        mItem(item),
        mSize(size)
    {
    }

    ~ItemStack()
    {
    }

    ItemStack& operator=(const ItemStack& rhs)
    {
        if(this == &rhs) return *this;

        mItem = rhs.mItem;
        mSize = rhs.mSize;

        return *this;
    }

    Item* item()
    {
        return &mItem;
    }

    const Item *item() const
    {
        return &mItem;
    }

    int size() const
    {
        return mSize;
    }

    friend QDataStream &operator<<(QDataStream &out, const ItemStack &itemStack);
    friend QDataStream &operator>>(QDataStream &in, ItemStack &itemStack);
    friend QDebug operator<<(QDebug debug, const ItemStack &itemStack);
private:
    Item mItem;
    int mSize;
};

QDataStream &operator<<(QDataStream &out, const ItemStack &itemStack)
{
    out << *itemStack.item()
        << itemStack.size();
    return out;
}

QDataStream &operator>>(QDataStream &in, ItemStack &itemStack)
{
    in >> itemStack.mItem
       >> itemStack.mSize;
    return in;
}

QDebug operator<<(QDebug debug, const ItemStack &itemStack)
{
    debug.nospace() << "(ItemStack item=" << *itemStack.item()
                    << ", size=" << itemStack.size()
                    << ")";
    return debug.space();
}

Q_DECLARE_METATYPE(ItemStack)

class GunEntity : public QObject
{
    Q_OBJECT
public:
    Q_PROPERTY(ItemStack roundsLoaded READ roundsLoaded)

    GunEntity() : mRoundsLoaded(Item(1), 7) {}

    ItemStack roundsLoaded() { return mRoundsLoaded; }
private:
    ItemStack mRoundsLoaded;
};

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);

    // Accessing directly through properties.
    GunEntity ge;
    qDebug() << "Can convert ge.roundsLoaded to ItemStack?" << ge.property("roundsLoaded").canConvert<ItemStack>();
    ItemStack is = ge.property("roundsLoaded").value<ItemStack>();
    qDebug() << is;
    qDebug() << "Can convert is.item to Item?" << is.property("item").canConvert<Item*>();
    qDebug() << *is.property("item").value<Item*>();
    qDebug() << "Can convert is.size to int?" << is.property("size").canConvert<int>();
    qDebug() << is.property("size").toInt();

    // Accessing through QScriptEngine.
    QScriptEngine se;
    se.evaluate("function blah(gun) { print(gun.roundsLoaded); print(gun.roundsLoaded.item); print(gun.roundsLoaded.size); }");
    if (se.hasUncaughtException()) {
        qDebug() << se.uncaughtException().toString() << ":"
                 << se.uncaughtExceptionLineNumber() << se.uncaughtExceptionBacktrace();
    }
    QScriptValueList args;
    args << se.newQObject(&ge);
    QScriptValue ret = se.globalObject().property("blah").call(se.globalObject(), args);

    if (se.hasUncaughtException()) {
        qDebug() << se.uncaughtException().toString() << ":"
                 << se.uncaughtExceptionLineNumber() << se.uncaughtExceptionBacktrace();
    }

    return 0;
}

#include "main.moc"

What am I doing wrong?

Mitch
  • 23,716
  • 9
  • 83
  • 122
  • An example with simpler access to the properties through `QScriptEngine` that also exhibits the problem: http://paste.kde.org/741698/. – Mitch May 11 '13 at 10:15

1 Answers1

1

I can suggest a few things.

The object name should be set. The objects' name within the script is set that way.

setObjectName( "Blah" );

I don't see where you're instantiating a specific object and telling the script engine about it:

   ScriptEngine->globalObject().setProperty( objectName(), ScriptEngine->newQObject( myObject, QScriptEngine::AutoOwnership, QScriptEngine::ExcludeSuperClassContents ) );
Jay
  • 13,803
  • 4
  • 42
  • 69
  • Which object? `blah` is the name of the function I'm calling within the script. [The documentation](http://qt-project.org/doc/qt-5.0/qtscript/qtscript-index.html#making-a-qobject-available-to-the-script-engine) says that setting `objectName` isn't necessary. I have used this same code successfully up until now; here's that same example with another function added that just operates on `ItemStack` which works fine: http://paste.kde.org/735524/ – Mitch May 01 '13 at 08:18
  • And by "same code" I mean that (as demonstrated in the paste.kde.org link) accessing properties of other objects has worked fine. – Mitch May 01 '13 at 10:11