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?
The “cannot read property of undefined” error occurs when you attempt to access a property or method of a variable that is undefined . You can fix it by adding an undefined check on the variable before accessing it.
Undefined means that a variable has been declared but has not been assigned a value. In JavaScript, properties and functions can only belong to objects. Since undefined is not an object type, calling a function or a property on such a variable causes the TypeError: Cannot read property of undefined .
The undefined property indicates that a variable has not been assigned a value, or not declared at all.
The JavaScript warning "reference to undefined property" occurs when a script attempted to access an object property which doesn't exist.
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 ) );
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With