PDA

View Full Version : Custom QObjet in QScript



Ben1
31st August 2010, 16:59
Hi everybody.

I've declare a custom QNetworkManager


class NetworkManager : public QObject
{
Q_OBJECT
public:
explicit NetworkManager(QObject *parent = 0);
NetworkManager(const NetworkRequest &);
virtual ~NetworkManager();

Q_INVOKABLE NetworkReply get(NetworkRequest request);
Q_INVOKABLE NetworkReply head(NetworkRequest request);
Q_INVOKABLE NetworkReply post(NetworkRequest request);
Q_INVOKABLE NetworkReply put(NetworkRequest request);

private:
QNetworkAccessManager m_manager;
};

class NetworkRequest : public QObject
{
Q_OBJECT
public:
explicit NetworkRequest(QUrl par_url, QObject *parent = 0);
NetworkRequest(const NetworkRequest &);
virtual ~NetworkRequest();

};

class NetworkReply : public QObject
{
Q_OBJECT
public:
NetworkReply(QObject *parent = 0);
explicit NetworkReply(QNetworkReply *par_reply, QObject *parent = 0);
NetworkReply(const NetworkReply &);
virtual ~NetworkReply();
private:
QNetworkReply *m_reply;

};


but when in script I do this :


var Request = NetworkRequest("www.google.fr");
var Manager = NetworkManager();
var Respond = Manager.get(Request);

I obtain this message :

TypeError: cannot call get(): unknown return type `NetworkReply' (register the type with qScriptRegisterMetaType())
get(NetworkRequest(name = ""))@:-1

so I add in the .h

Q_DECLARE_METATYPE(NetworkReply*)
QScriptValue NetworkReplyToScriptValue(QScriptEngine *engine, NetworkReply* const &in);
void NetworkReplyFromScriptValue(const QScriptValue &object, NetworkReply* &out);
Q_DECLARE_METATYPE(NetworkRequest*)
QScriptValue NetworkRequestToScriptValue(QScriptEngine *engine, NetworkRequest* const &in);
void NetworkRequestFromScriptValue(const QScriptValue &object, NetworkRequest* &out);
and in cpp

QScriptValue NetworkRequestToScriptValue(QScriptEngine *engine, NetworkRequest* const &in) { return engine->newQObject(in); }
void NetworkRequestFromScriptValue(const QScriptValue &object, NetworkRequest* &out) { out = qobject_cast<NetworkRequest*>(object.toQObject()); }

QScriptValue NetworkReplyToScriptValue(QScriptEngine *engine, NetworkReply* const &in) { return engine->newQObject(in); }
void NetworkReplyFromScriptValue(const QScriptValue &object, NetworkReply* &out) { out = qobject_cast<NetworkReply*>(object.toQObject()); }

void NetworkRequestRegister::Register(QScriptEngine *engine)
{
par_engine->globalObject().setProperty("NetworkRequest", par_engine->newFunction(createNetworkRequest));
par_engine->globalObject().setProperty("NetworkManager", par_engine->newFunction(createNetworkManager));
par_engine->globalObject().setProperty("NetworkReply", par_engine->newFunction(createNetworkReply));
qScriptRegisterMetaType(engine, NetworkReplyToScriptValue, NetworkReplyFromScriptValue);
qScriptRegisterMetaType(engine, NetworkRequestToScriptValue, NetworkRequestFromScriptValue);
}

But the error is the same.
Somebody know why ?

Thanks in advance.

Urthas
31st August 2010, 21:28
What happens if you replace the third "var" with "NetworkReply"?

Ben1
1st September 2010, 09:37
var Request = NetworkRequest("www.google.fr");
var Manager = NetworkManager();
NetworkReply Respond = Manager.get(Request);

it says there is a malformed script when I use QScriptSyntaxCheckResult to check the syntax, a ';' is missing.

wysota
1st September 2010, 09:45
You are registering "NetworkReply*" but using "NetworkReply" which is not registered with the metatype system.

Ben1
1st September 2010, 10:22
Thanks
But I add this, and the error is the same :


Q_DECLARE_METATYPE(NetworkReply)

QScriptValue NetworkReplyToScriptValue2(QScriptEngine *engine, NetworkReply const &in) { return engine->newQObject((QObject *)&in); }
void NetworkReplyFromScriptValue2(const QScriptValue &object, NetworkReply &out) { out = (NetworkReply)(object.toQObject()); }

...

qScriptRegisterMetaType(engine, NetworkReplyToScriptValue2, NetworkReplyFromScriptValue2);

What I don't understand ?

wysota
1st September 2010, 10:27
Please provide a minimal compilable example reproducing the problem. By the way, you know QObjects can't be copied, right? So deriving NetworkReply from QObject probably doesn't make much sense...

Ben1
1st September 2010, 12:00
That's why I add NetworkReply(const NetworkReply &); (if I don't understand everything, it's because I don't often speak English, sorry)

The problem is really when I try to use a custom QObject in other via QScript

I try to add minimal example but I don't compile it :


#ifndef NETWORKREQUEST_H
#define NETWORKREQUEST_H

#include <QObject>
#include <QScriptEngine>

#include <QNetworkReply>
#include <QNetworkAccessManager>
#include <QNetworkRequest>

class NetworkRequestRegister
{
public:
static void Register(QScriptEngine *engine);
};

class NetworkRequest : public QObject
{
Q_OBJECT
public:
explicit NetworkRequest(QUrl url, QObject *parent = 0);
NetworkRequest(const NetworkRequest &);
virtual ~NetworkRequest();

QNetworkRequest getRequest() { return request; }

private:
QNetworkRequest request;

};

class NetworkReply;
class NetworkManager : public QObject
{
Q_OBJECT
public:
explicit NetworkManager(QObject *parent = 0);
NetworkManager(const NetworkRequest &);
virtual ~NetworkManager();

QNetworkReply *Qget(NetworkRequest request);
Q_INVOKABLE NetworkReply get(NetworkRequest request);

private:
QNetworkAccessManager manager;
};

class NetworkReply : public QObject
{
Q_OBJECT
public:
NetworkReply(QObject *parent = 0);
explicit NetworkReply(QNetworkReply *par_reply, QObject *parent = 0);
NetworkReply(const NetworkReply &);
virtual ~NetworkReply();

NetworkReply &operator =(const NetworkReply &);

private:
QNetworkReply *reply;

};

Q_SCRIPT_DECLARE_QMETAOBJECT(NetworkReply, QObject*);
Q_DECLARE_METATYPE(NetworkReply)
Q_DECLARE_METATYPE(NetworkReply*)
QScriptValue NetworkReplyToScriptValue(QScriptEngine *engine, NetworkReply* const &in);
void NetworkReplyFromScriptValue(const QScriptValue &object, NetworkReply* &out);
Q_DECLARE_METATYPE(NetworkRequest*)
QScriptValue NetworkRequestToScriptValue(QScriptEngine *engine, NetworkRequest* const &in);
void NetworkRequestFromScriptValue(const QScriptValue &object, NetworkRequest* &out);

#endif // NETWORKREQUEST_H



#include <math.h>

#include "networkrequest.h"

QScriptValue createNetworkRequest(QScriptContext *context, QScriptEngine *engine)
{
if(par_context->argumentCount() != 1 && context->argument(0).isString())
{
return engine->nullValue();
}

NetworkRequest *loc_request = new NetworkRequest(QUrl(context->argument(0).toString()), engine);
return par_engine->newQObject(request);
}

QScriptValue createNetworkManager(QScriptContext *context, QScriptEngine *engine)
{
if(par_context->argumentCount() != 0)
{
return engine->nullValue();
}

NetworkManager *manager = new NetworkManager(engine);
return engine->newQObject(manager);
}

QScriptValue NetworkRequestToScriptValue(QScriptEngine *engine, NetworkRequest* const &in) { return engine->newQObject(in); }
void NetworkRequestFromScriptValue(const QScriptValue &object, NetworkRequest* &out) { out = qobject_cast<NetworkRequest*>(object.toQObject()); }

QScriptValue NetworkReplyToScriptValue(QScriptEngine *engine, NetworkReply* const &in) { return engine->newQObject(in); }
void NetworkReplyFromScriptValue(const QScriptValue &object, NetworkReply* &out) { out = qobject_cast<NetworkReply*>(object.toQObject()); }

QScriptValue NetworkReplyToScriptValue2(QScriptEngine *engine, NetworkReply const &in) { return engine->newQObject((QObject *)&in); }
void NetworkReplyFromScriptValue2(const QScriptValue &object, NetworkReply &out) { out = (NetworkReply)(object.toQObject()); }

QScriptValue createNetworkReply(QScriptContext *context, QScriptEngine *engine)
{
if(context->argumentCount() != 0)
{
return engine->nullValue();
}

NetworkReply *manager = new NetworkReply(engine);
return engine->newQObject(manager);
}

void NetworkRequestRegister::Register(QScriptEngine *engine)
{
par_engine->globalObject().setProperty("NetworkRequest", engine->newFunction(createNetworkRequest));
par_engine->globalObject().setProperty("NetworkManager", engine->newFunction(createNetworkManager));
par_engine->globalObject().setProperty("NetworkReply", engine->newFunction(createNetworkReply));
qScriptRegisterMetaType(engine, NetworkReplyToScriptValue, NetworkReplyFromScriptValue);
qScriptRegisterMetaType(engine, NetworkReplyToScriptValue2, NetworkReplyFromScriptValue2);
qScriptRegisterMetaType(engine, NetworkRequestToScriptValue, NetworkRequestFromScriptValue);
}

NetworkRequest::NetworkRequest(QUrl url, QObject *parent) :
QObject(parent),
request(QNetworkRequest(url))
{
}

NetworkRequest::NetworkRequest(const NetworkRequest &request)
{
m_request = par_request.request;
}

NetworkRequest::~NetworkRequest()
{
file->close();
delete file;
}

NetworkReply::NetworkReply(QNetworkReply *reply, QObject *parent) : QObject(parent)
{
reply = reply;
if(!reply->open(QIODevice::ReadOnly))
qDebug(QString("Network Reply > cannot open reply IO Device").toLatin1());
}

NetworkReply::NetworkReply(QObject *parent) : QObject(parent)
{
}

NetworkReply::NetworkReply(const NetworkReply &reply)
{
reply = reply.reply;
}

NetworkReply::~NetworkReply()
{
//reply->close();
//delete reply;
}

NetworkReply &NetworkReply::operator =(const NetworkReply &reply)
{
return NetworkReply(reply);
}

NetworkManager::NetworkManager(QObject *parent) : QObject(parent), manager(QNetworkAccessManager(parent))
{
}

NetworkManager::~NetworkManager()
{
}

QNetworkReply *NetworkManager::Qget(NetworkRequest request)
{
return m_manager.get(request.getRequest());
}

NetworkReply NetworkManager::get(NetworkRequest request)
{
return NetworkReply(m_manager.get(request.getRequest()), parent());
}


in the main


QFile *file = new QFile("scenario.txt");
file->open(QIODevice::ReadOnly);
QString scenario = file->readAll();
QScriptEngine *engine = new QScriptEngine(parent);
NetworkRequestRegister::Register(engine);

QScriptSyntaxCheckResult checkResult = QScriptEngine::checkSyntax(screnario);
if(checkResult.state() == QScriptSyntaxCheckResult::Valid)
{
engine->evaluate(screnario);
}


And in scenatio.txt



function fileGetTest() {
var Request = NetworkRequest("www.google.fr");
var Manager = NetworkManager();
var Respond = Manager.get(Request);
}

fileGetTest();

There is a way to add a file in a post ?

wysota
1st September 2010, 12:32
That's why I add NetworkReply(const NetworkReply &);
That doesn't change anything. You return copies of QObject from your NetworkManager class. I don't know why you made NetworkRequest and NetworkReply descendants of QObject anyway - you are not using any of QObject features in these classes.


I try to add minimal example but I don't compile it :
Minimal is "less than 30 lines of code long".


There is a way to add a file in a post ?
Yes, of course. "Go Advanced" and click on the paper clip icon.

According to me you are overcomplicating things. Focus on your goal and not on QObjects. None of your classes have to be QObjects. If you are deriving them from QObject just to use Q_INVOKABLE then your NetworkManager should return pointers to NetworkReply and NetworkRequest. Also you have to tell QtScript how to create instances of NetworkRequest and NetworkReply.

Ben1
1st September 2010, 13:54
I use QObject because it seems a simplest way to use object in QtScript (and I don't really understand the documentation). How can I do ?
I have the problem to return myC++Object and use it in parameter.
Use of pointer, like this Q_INVOKABLE NetworkReply *get(NetworkRequest request); doesn't work.

Sorry for my (non-)minimal code !

wysota
1st September 2010, 14:08
"It doesn't work" statement is not very useful :)

Not all simplest (or requiring the least work) solutions are good solutions. If you have a value-based class (such as (Q)NetworkRequest) with only a few methods, there are better approaches to use it than going through QObject.

Ben1
1st September 2010, 14:25
The "it doesn't work" means the error is always

"TypeError: cannot call get(): argument 1 has unknown type `NetworkRequest' (register the type with qScriptRegisterMetaType()) : line => 66
get(NetworkRequest(name = ""))@:-1
fileGetTest()@:66
<global>()@:85

but change to

Q_INVOKABLE NetworkReply *get(NetworkRequest *request);
doesn't make an error like the above. I can't get my page but it seems to be an other problem.

I do this quickly to prove to my colleague it is possible to use QtScript but I'm a beginner in QtScript.
Thank you for your help.

wysota
1st September 2010, 15:06
Let's start with the fact that you don't need NetworkRequest at all. All it does in your case is that it carries a string. But if I'm correct at what you are trying to accomplish then you don't need any of the three classes at all. You just need wrappers for them in QtScript environment. You could do it with i.e. QScriptClass or two simple converter functions per class.

Edit: here is something similar to what you wanted:

#include <QtScript>
#include <QtGui>

Q_SCRIPT_DECLARE_QMETAOBJECT(QLineEdit, QWidget*)


int main(int argc, char **argv){
QApplication app(argc, argv);
QScriptEngine engine;
QScriptValue lineEditClass = engine.scriptValueFromQMetaObject<QLineEdit>();
engine.globalObject().setProperty("QLineEdit", lineEditClass);
QScriptValue val = engine.evaluate("var le = new QLineEdit; le.show();");
if(val.isError())
qDebug() << val.toString();
app.exec();
}

Ben1
1st September 2010, 15:37
What I need if I want to use my own class (no a Qt class)

example :

class MyObj
{
MyObj();
~ MyObj();
void calculate() { 3 + 5;}
}

I just have to use Q_SCRIPT_DECLARE_QMETAOBJECT(MyObj, QObject*) and


int main(int argc, char **argv){
QApplication app(argc, argv);
QScriptEngine engine;
QScriptValue MyObjClass = engine.scriptValueFromQMetaObject<MyObj>();
engine.globalObject().setProperty("MyObj", MyObjClass );
QScriptValue val = engine.evaluate("var le = new MyObj; le.calculate();");
if(val.isError())
qDebug() << val.toString();
app.exec();
}

It's just to be sure what I can do.

wysota
1st September 2010, 15:48
First you can open the docs and read why QScriptEngine::scriptValueFromQMetaObject() is not suitable for your MyObj class.


class MyObj : public QObject {
Q_OBJECT
public:
MyObj(){}
~MyObj(){}
public slots:
void calculate() { qDebug() << Q_FUNC_INFO; }
};

#include "main.moc"

int main(int argc, char **argv){
QScriptEngine engine;
QScriptValue MyObjClass = engine.scriptValueFromQMetaObject<MyObj>();
engine.globalObject().setProperty("MyObj", MyObjClass );
QScriptValue val = engine.evaluate("var le = new MyObj; le.calculate();");
if(val.isError())
qDebug() << val.toString();
}

or use QScriptClass or QScriptable to avoid QObject legacy. You can also use one of the prototype based approaches, i.e.:


QScriptValue MyObj_ctor(QScriptContext *context, QScriptEngine *engine) {
return engine->undefinedValue();
}

QScriptValue MyObj_prototype_calculate(QScriptContext *context, QScriptEngine *engine)
{
qDebug() << "calculate";
return engine->undefinedValue();
}

// ...

QScriptEngine engine;
QScriptValue ctor = engine.newFunction(MyObj_ctor);
ctor.property("prototype").setProperty("calculate", engine.newFunction(MyObj_prototype_calculate));
QScriptValue global = engine.globalObject();
global.setProperty("MyObj", ctor);

What you choose depends on how you intend to use the class.

Ben1
1st September 2010, 16:16
The problem is I don't really know how to use my class. I try to provide a framework that can be used by users who can write QtScript using this framework.

wysota
1st September 2010, 16:30
The problem is I don't really know how to use my class.
I don't know how to use your class too. You'll probably be the first to know how to use your class. If not then it's probably not a good idea to dwell about the problem at all.