PDA

View Full Version : Using QProcess in QtScript



chiiph
5th November 2010, 13:41
Hello everyone,

I'm trying to use QProcess from QtScript.

I've tried a lot of things, but it seems the only way I can make it build is by using Q_SCRIPT_DECLARE_QMETAOBJECT(QProcess, QObject*), and then do:



QScriptValue proc = engine.scriptValueFromQMetaObject<QProcess>();
engine.globalObject().setProperty("Process", proc);


But then I do in the script:



var n = new Process();
n.start("someapp");


And it results in TypeError: Result of expression 'n.start' [undefined] is not a function.

What am I doing wrong?

Thanks in advance

tbscope
5th November 2010, 13:48
Easy, Process is a QObject

You need to tell the script engine that Process is actually a QProcess.

What you have is correct but not complete. Also use qScriptRegisterMetaType()

chiiph
5th November 2010, 14:23
Can you be more specific?
It seems I need to specify a toScriptValue and fromScriptValue functions, but I'm not really sure how to do it.

Thanks!

tbscope
5th November 2010, 15:47
Something in the lines of:


class MyObject : public QObject
{
Q_OBJECT
...
};

Q_DECLARE_METATYPE(MyObject*)

QScriptValue myObjectToScriptValue(QScriptEngine *engine, MyObject* const &in)
{ return engine->newQObject(in); }

void myObjectFromScriptValue(const QScriptValue &object, MyObject* &out)
{ out = qobject_cast<MyObject*>(object.toQObject()); }

...

qScriptRegisterMetaType(&engine, myObjectToScriptValue, myObjectFromScriptValue);

Edit: changed the code as mentioned on the Qt documentation website

chiiph
5th November 2010, 16:41
I had to change a bit the functions since QProcess can't be copied.

But now I'm having this issue:


/usr/include/qt4/QtCore/qmetatype.h: In static member function ‘static int QMetaTypeId2<T>::qt_metatype_id() [with T = QProcess]’:
/usr/include/qt4/QtCore/qmetatype.h:230: instantiated from ‘int qMetaTypeId(T*) [with T = QProcess]’
/usr/include/qt4/QtCore/qmetatype.h:243: instantiated from ‘int qRegisterMetaType(T*) [with T = QProcess]’
/usr/include/qt4/QtScript/qscriptengine.h:402: instantiated from ‘int qScriptRegisterMetaType(QScriptEngine*, QScriptValue (*)(QScriptEngine*, const T&), void (*)(const QScriptValue&, T&), const QScriptValue&, T*) [with T = QProcess]’
mainwindow.cpp:69: instantiated from here
/usr/include/qt4/QtCore/qmetatype.h:169: error: ‘qt_metatype_id’ is not a member of ‘QMetaTypeId<QProcess>’


Any ideas?

Thanks again

tbscope
5th November 2010, 16:45
Yes, do not forget Q_DECLARE_METATYPE

chiiph
5th November 2010, 16:48
Right... I thought that was implicit since it's already a QObject...

But now:


/usr/include/qt4/QtCore/qprocess.h: In function ‘void* qMetaTypeConstructHelper(const T*) [with T = QProcess]’:
/usr/include/qt4/QtCore/qmetatype.h:196: instantiated from ‘int qRegisterMetaType(const char*, T*) [with T = QProcess]’
mainwindow.cpp:4: instantiated from here
/usr/include/qt4/QtCore/qprocess.h:224: error: ‘QProcess::QProcess(const QProcess&)’ is private
/usr/include/qt4/QtCore/qmetatype.h:142: error: within this context


:) this isn't as easy as it sounded... at least to me...

tbscope
5th November 2010, 16:55
:-)

This isn't easy :-)

Wrap your QProcess in a QSharedPointer.
This whole QScript mess is very complex. On one hand you have to have copy constructors, but on the other hand, they are not allowed.
You can solve this by using a QSharedPointer

chiiph
5th November 2010, 17:11
I'm going to come out as the stupidest guy in here :D but... how can I wrap it with an object that isn't a QObject?

tbscope
5th November 2010, 17:18
Note: I didn't compile this, so there might be a syntax error.

Example:


QScriptValue qScriptValueFromProcess(QScriptEngine *engine, const QSharedPointer<QProcess> &object)
{
return engine->newQObject(object.data());
}

void qScriptValueToProcess(const QScriptValue & value, QSharedPointer<QProcess> & object)
{
}

Q_SCRIPT_DECLARE_QMETAOBJECT(QProcess, QObject*)
Q_DECLARE_METATYPE(QSharedPointer<QProcess>)

QScriptValue processValue = m_engine->scriptValueFromQMetaObject<QProcess>();
m_engine->globalObject().setProperty("Process", processValue);

qScriptRegisterMetaType(m_engine, qScriptValueFromProcess, qScriptValueToProcess);

m_engine is a member variable containing a pointer to a script engine.

chiiph
5th November 2010, 17:37
Great, so now it built, but it's the same problem as before, I can declare a variable, assign new Process(), but when I call variable.start("ls") it's the same error...

Any ideas?

tbscope
5th November 2010, 17:53
Ohh now I remember.

The functions you want to use need to be defined as a property or a slot :-(
Try the kill() or terminate() functions, they will work.
You can write a wrapper, which is just a QProcess subclass defining the functions as slots.

Sigh... now I remember why it took me a week to get the Qt Creator cpp parser to work in scripts.

chiiph
5th November 2010, 17:56
GREAT!!! You're right...

At least I have some kind of idea on how to do this... because I'll need to wrap a lot more objects probably :)

Thanks a lot tbscope...

tbscope
5th November 2010, 18:04
I just tried it, and this works:

Note, I created a widget with a plain text edit and a button.

Header

class MyProcess : public QProcess
{
Q_OBJECT

public:
MyProcess(QObject* parent = 0) : QProcess(parent) {}
~MyProcess() {}

public slots:
void startMyProcess(const QString& proc);
};

class Widget : public QWidget
{
Q_OBJECT

public:
explicit Widget(QWidget *parent = 0);
~Widget();

public slots:
void runScript();

private:
Ui::Widget *ui;

QScriptEngine *m_engine;
};


Implementation:


QScriptValue qScriptValueFromProcess(QScriptEngine *engine, const QSharedPointer<MyProcess> &object)
{
return engine->newQObject(object.data());
}

void qScriptValueToProcess(const QScriptValue &, QSharedPointer<MyProcess> &)
{
}

Q_SCRIPT_DECLARE_QMETAOBJECT(MyProcess, QObject*)
Q_DECLARE_METATYPE(QSharedPointer<MyProcess>)

void MyProcess::startMyProcess(const QString &proc)
{
start(proc);
}

Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget),
m_engine(new QScriptEngine)
{
ui->setupUi(this);

QScriptValue processValue = m_engine->scriptValueFromQMetaObject<MyProcess>();
m_engine->globalObject().setProperty("Process", processValue);

qScriptRegisterMetaType(m_engine, qScriptValueFromProcess, qScriptValueToProcess);

connect(ui->buttonRun, SIGNAL(clicked()), this, SLOT(runScript()));
}

Widget::~Widget()
{
delete ui;
}

void Widget::runScript()
{
QScriptValue resultValue = m_engine->evaluate(ui->textScript->toPlainText(), "test");

if(resultValue.isError())
qDebug() << "Error: " << resultValue.toString();

}

chiiph
5th November 2010, 18:23
When I try something just like you did I get "QProcess: Destroyed while process is still running"... but with static functions it works... I'm running this on Linux, may be that's the problem, I don't know... I've created a test slot that just prints a string, and it works, so this isn't a problem of calling a non-static member function...

For now I can do with the static ones, we'll see how it goes with other classes...

Thanks again for everything!

tbscope
7th November 2010, 10:28
You might want to keep a private pointer to an existing QProcess in the wrapper.
I did something similar here:
http://gitorious.org/qt-creator-static-code-quality-plugin/qt-creator-static-code-quality-plugin/commit/d7d7a4be57134c6f468f2818a268de862ba7527b

And done a bit different:
http://doc.qt.nokia.com/4.7/script-customclass.html

chiiph
9th November 2010, 00:32
Thanks for the links...

Now, I've got to handle (for now) one type of scripts. What they need to do is a couple of file operations (create some empty dir, check some file for existence, etc... nothing fancy), and then start a new process with certain arguments. So, that's kind of worked out with all that's already discussed, but there's another issue: it'll be good to have each script to set up its own UI elements in a more general dialog from the application.

I've only worked with non-scripts plugins in qt... and they handle themselves quite differently, obviously. If I were using this kind of plugins, I'd probably just do something like the setupUi function from the qt designer generated files, and have a slot I can call from the main app to start the process that the plugin represents (all the other stuff would be handled from the script itself and the generated UI). So when the app starts, I load every plugin, execute setupUi(some_container), and later on trigger the execute slot; after that the plugin will handle all the UI signals that correspond to it.
How can I port this idea to a plugin implemented with qtscript?