I. Qui est Ken Hansen ?▲
Kent Hansen est ingénieur logiciel chez Nokia dans le département de développement du framework Qt à Oslo. Il est titulaire d'un master d'informatique de l'université NTNU (Norwegian University of Science and Technology). Il travaille depuis 2005 sur Qt Script, l'intégration de Javascript dans Qt et sur le framework de la machine à états.
II. Pourquoi Qt Script ?▲
L'objectif de Qt Script est, comme son nom l'indique, d'embarquer des scripts dans une application Qt. La base est réalisée en C++ mais avec une interface à l'environnement de script. On est sur une approche plug-in avec de l'interactivité (console). L'exemple type est le navigateur web avec un script chargé comme une page web et le navigateur qui exporte l'arbre DOM dans la page. Il s'agit d'un langage basé sur le standard ECMA-262, comparable à JavaScript sans la partie DOM. Il utilise une syntaxe de type Java. Qt Script a été créé dans trois grands buts :
- Personnaliser vos applications à l'aide de scripts ;
- Automatiser des tâches de votre logiciel ;
- Étendre votre application en permettant un nouveau modèle de déploiement.
III. Utilisation basique de Qt Script▲
Objectif : écrire une application qui utilise Qt Script pour calculer le sens de la vie.
Créer un projet Qt Core Application et ajouter le module Qt Script. Vous aurez alors un fichier de projet semblable à celui-ci :
QT += script
QT -= gui
TARGET = QtScript
CONFIG += console
CONFIG -= app_bundle
TEMPLATE = app
SOURCES += main.cpp
RESOURCES += ressources.qrc
III-A. Premières lignes en Qt Script▲
#include
<QtCore/QCoreApplication>
#include
<QtScript>
int
main(int
argc, char
*
argv[])
{
QCoreApplication
a(argc, argv);
QScriptEngine
engine;
QScriptValue
result =
engine.evaluate("41+1"
);
qDebug
() <<
result.toNumber(); // 42
return
a.exec();
}
L'environnement minimum de Qt Script réside dans QtScriptEngine. QScriptValue permet, entre autres, de :
- Tester les types et effectuer des conversions ;
- Accéder aux propriétés ;
- Appeler des fonctions à partir du C++.
III-B. Tester les types et effectuer des conversions▲
QScriptEngine
engine;
QScriptValue
result =
engine.evaluate("41+1"
);
//Test du type
if
(result.isNumber())
{
//Conversion en chaîne de caractère
qDebug
() <<
result.toString(); // "42"
}
III-C. Utilisation d'un fichier Javascript▲
Dans la suite des exemples, la partie script sera dans un fichier script.js, ajouté en ressource au projet et exécuté par le code suivant :
QScriptValue
evaluateFile(QScriptEngine
&
engine, QString
const
&
filename)
{
QFile
file(filename);
file.open(QIODevice
::
ReadOnly);
return
engine.evaluate(file.readAll(),filename);
}
int
main(int
argc, char
*
argv[])
{
QCoreApplication
a(argc, argv);
QScriptEngine
engine;
QScriptValue
result =
evaluateFile(engine, ":/script.js"
);
qDebug
() <<
result.toNumber(); // 42
return
a.exec();
}
41
+
1
III-D. Accéder aux propriétés▲
QScriptEngine
engine;
QScriptValue
result =
evaluateFile(engine, ":/script.js"
);
qDebug
() <<
result.property("food"
).toString(); // "spaghetti"
({
food
:
"spaghetti"
}
)
III-E. Appeler des fonctions Javascript à partir du C++▲
QScriptEngine
engine;
QScriptValue
result =
evaluateFile(engine, ":/script.js"
);
QScriptValue
function =
result.property("funkyFunction"
);
qDebug
() <<
function.call(result).toString(); // "I love spaghetti"
({
food
:
"spaghetti"
,
funkyFunction
:
function(
){
return "I love "
+
this.
food;
}
}
)
Et maintenant, le même exemple, mais en ajoutant comme argument le verbe de la phrase :
QScriptEngine
engine;
QScriptValue
result =
evaluateFile(engine, ":/script.js"
);
QScriptValue
function =
result.property("funkyFunction"
);
qDebug
() <<
function.call(result,QScriptValueList
() <<
"dislike "
).toString(); // "I dislike meatballs"
III-F. Lister les propriétés▲
QScriptEngine
engine;
QScriptValue
result =
evaluateFile(engine, ":/script.js"
);
result.setProperty("Bar"
, 123
);
QScriptValueIterator
it(result);
while
(it.hasNext()){
it.next();
qDebug
() <<
it.name() <<
it.value().toString();
}
"food"
"meatballs"
"funkyFunction"
"function(verb) {
return "
I " + verb + this.food;
}"
"Bar"
"123"
III-G. Manipulation des erreurs▲
Les exceptions sont des objets, ce qui permet de manipuler facilement les erreurs, comme dans l'exemple suivant :
QScriptEngine
engine;
QScriptValue
result =
evaluateFile(engine, ":/script.js"
);
if
(result.isError())
{
qDebug
() <<
result.property("message"
).toString(); // "noSuchVariable is not defined"
}
noSuchVariable
III-H. Objets globaux▲
Qt script donne également la possibilité de définir des objets globaux permettant d'accéder aux propriétés de ces objets n'importe où :
QScriptEngine
engine;
QScriptValue
global =
engine.globalObject();
global.setProperty("MeaningOfLife"
,42
,QScriptValue
::
ReadOnly);
QScriptValue
result =
evaluateFile(engine, ":/script.js"
);
qDebug
() <<
result.toNumber(); // 6.48074
Math.sqrt
(
MeaningOfLife);
IV. Utilisation avancée de Qt Script▲
Dans cette seconde partie, nous allons travailler sur une interface graphique. Votre fichier projet ressemblera à un projet Qt Application :
QT += script
TARGET = QtScript
TEMPLATE = app
SOURCES += main.cpp
RESOURCES += ressources.qrc
Votre fichier main.cpp ressembe maintenant au suivant (ajout d'un bouton de taille 200x200) :
#include
<QtCore/QCoreApplication>
#include
<QtGui>
#include
<QtScript>
QScriptValue
evaluateFile(QScriptEngine
&
engine, QString
const
&
filename)
{
QFile
file(filename);
file.open(QIODevice
::
ReadOnly);
return
engine.evaluate(file.readAll(),filename);
}
int
main(int
argc, char
*
argv[])
{
QApplication
a(argc, argv);
QPushButton
button;
button.resize(200
,200
);
return
a.exec();
}
La création d'un objet avec Qt Script se fait de la manière suivante :
QScriptValue
objet =
engine.newObject();
L'avantage de ce type d'objet, c'est qu'il peut être modifié en cours d'exécution via les propriétés et les signaux/slots.
IV-A. Modification des propriétés▲
La modification des propriétés d'un QObject passe par la création d'un newQObject prenant en paramètre le QObject en question :
QScriptEngine
engine;
QScriptValue
global =
engine.globalObject();
QScriptValue
scriptButton =
engine.newQObject(&
button);
global.setProperty("button"
,scriptButton);
QScriptValue
result =
evaluateFile(engine, ":/script.js"
);
button
.
text
=
"Hello Qt Script!"
;
button
.
styleSheet =
"background : red"
;
button
.show
(
);
On obtient alors un bouton avec le texte "Hello Qt Script!" sur fond rouge.
IV-B. Signaux et Slots▲
Il est tout à fait possible de connecter un signal et un slot avec Qt SCript en utilisant la fonction qScriptConnect. Ajouter les lignes suivantes aux codes C++ et javascript :
QScriptValue
handler =
result.property("buttonClickHandler"
);
qScriptConnect
(&
button, SIGNAL
(clicked()), scriptButton, handler);
buttonClickHandler =
function(
){
button
.
styleSheet =
"background : green"
;
};
button
.
clicked.connect
(
buttonClickHandler);
En cliquant sur le bouton, le fond change de couleur, du rouge au vert.
IV-C. QtScriptDebugger▲
Pour utiliser le débogueur de Qt Script, il est nécessaire d'ajouter l'utilisation du module scripttools dans le fichier projet :
QT += scripttools
Ainsi que l'en-tête du module au début du fichier C++. Il suffit alors de créer un objet de type QScriptEngineDebugger et d'attacher l'objet QScriptEngine dessus.
#include
<QtScriptTools>
...
QScriptEngineDebugger
debugger;
debugger.attachTo(&
engine);
Il est alors possible d'écrire et d'exécuter des scripts directement dans la console du débogueur.
V. Conclusion▲
Qt Script permet facilement de scripter une application Qt. Avec l'aide du débogueur, il est alors facile de maîtriser rapidement cette API. Pour plus d'informations : QtScript/Generator sur Qt Labs.
VI. Questions▲
Est-il possible d'ajouter des propriétés à des classes C++ ?
Oui, par le proxy, cela arrivera sous peu.
Est-il possible d'utiliser QtScriptDebugger dans Visual Studio ?
Non.
Est-il possible d'utiliser Qt Script dans des threads ?
Non, il n'est pas thread safe.
Est-il possible de limiter la mémoire utilisée par Javascript, et éviter ainsi tout crash ?
Non, ce n'est pas possible en dehors de la portée de Qt Script.