\chapter How to Make Qt Applications Scriptable

\section1 Overview

This chapter demonstrates how to write a C++ Qt application which
integrates \QSA to make the application extensible and customizable
through scripting. The goal is to write a simple spreadsheet
application which can be extended by the user. To do this, the
spreadsheet will provide interfaces to its sheets. The script
code can access the sheets, so the user can write \QS code that
presents dialogs to accept user preferences, and which can access and
manipulate the spreadsheet data. The code for this example can be
found in \c examples/spreadsheet.

Additional examples that demonstrate other \QSA usage are also
included in the \c examples directory.

\section1 How to make a Qt/C++ Application Scriptable

\section2 1) Include the Qt Script for Applications Library

To make a Qt/C++ application scriptable, you need the \c libqsa
library that is included in the \QSA package. To use \c libqsa and get
other QSA specific build options, add the following line to your .pro
file:

\code
CONFIG += qsa
\endcode

The \c libqsa library provides the QSInterpreter class. A default
instance of QSInterpreter is available, for convenience, by using
the function QSInterpreter::defaultInterpreter().

\section2 2) Add the Application Objects to the Scripting Engine

To make application functionality available to scripts, the
application must provide \c{QObject}s or \c{QObject} subclasses which
implement the application's functionality. By passing an object to the
scripting engine (using QSInterpreter::addTransientObject() or
QSProject::addObject()), this object and all its signals, slots,
properties, and child objects are made available to scripts. Because
\QSA uses \QT's meta object system, there is no need to implement any
additional wrappers or bindings.

If no parent object of the object that is passed into the interpreter has
been made available yet, the new object is added as a toplevel object
to the scripting engine. This means that it is available through \c
Application.object_name to the script writer.

If a parent object of the object has been previously added via a
\c{addObject()}, the new object is not added as a toplevel object,
and is available through \c Application.parent1.parent2.object_name
(given that \c parent1 has been added previously via \c{addObject()}). The
reason for doing that is because the object can be used as a
namespace or context later, and code can be added in the context of
that object.

In most cases we do not pass \c{QObject}s which are directly used in
the C++ application to the scripting engine because this would expose too
many slots. Instead we implement interface \c{QObject}s which offer the
signals, slots, and properties that we want to offer to the scripts and
which will be simply implemented as forward function calls.

In our spreadsheet example we will add interface objects for the
sheets. The interface objects implement slots and properties to query and set
selection ranges, retrieve and set cell data, etc.

In other cases it might be possible to use an application's existing
\c{QObject}s as application objects in the scripting language. An example
of this approach is shown in the \c examples/textedit example, which is
a slightly modified, scriptable, version of the Qt textedit example.

To read about how to design and implement application objects, see the
\l{How to Design and Implement Application Objects} chapter.

\section2 3) Open a Scripting Project

\QSA always works with one current scripting project that contains all
the forms and files in which all the functions and classes are
implemented.

An instance of \c{QSInterpreter} can be used on its own, but to get
full access to the functionality of \QSA, use \c{QSProject}. To use a
stand alone \c{QSInterpreter} use QSInterpreter::defaultInterpreter()
or QSInterpreter::QSInterpreter(). To create an interpreter that runs
with a project, create the project using QSProject::QSProject() and
access its interpreter using QSProject::interpreter().

If you work with a project, you can either choose to use
functionality in \QSA to take care of everything (saving, loading, etc.)
or you can decide to take care of most functionality yourself, giving you more
flexibility.

If you choose to have \QSA take care of everything for you, \QSA then loads and saves the whole project from
one file or one data block which you can specify. In this case all
project data is compressed in the data block or file and extracted
temporarily when loading. If you choose to take care of the
functionality yourself, then open an empty
project and use \c{QSProject}'s API to add scripts manually and then
retrieve them and save or store them however you'd like.

If you choose the first option, do the following:

Create a new \c{QSProject} and load in a scripting project 
using QSProject::load().

Loading a project can be called in one of two ways:
\list 1

\o Call QSProject::load() with a filename (including the path). \QSA
will open the specified project file.

\o Call QSProject::loadFromData() with a byte array that contains the
scripting project's data. This is useful if you don't want to save the
project as an individual file on disk; for example, you might prefer
to keep the project data embedded in the document. If this approach is
taken, the data block which contains the project is passed to
\c{load()}. If an empty byte-array is passed, a new project will be
created. Use QSProject::saveToData() to retrieve a project as a
byte-array suitable for use with the \c QSProject::loadFromData()
overload.  
\endlist

If you choose the second option, simply create a new QSProject and
use its functions to add scripts and save the project at your convenience.

Of course it is possible to use a combination of both approaches,
e.g. using the first approach, but later adding script code programmatically.

Note that when saving a project, the functions in \c{QSInterpreter}
will add transient content to the interpreter, while the functions in
the project will add persistent content to the interpreter. This
means that content that is added to the project can be saved and will
remain even if the interpreter state is cleared, while the content added using the
\c{QSInterpreter} will not be saved and will be cleared along with the
interpreter state.

In the spreadsheet example we use one scripting project for the whole
application, and we let \QSA save this to disk as an individual file
that is opened on startup. The following code is used to initialize
\c{QSProject} (adding application objects and opening the
project):

\quotefromfile spreadsheet/spreadsheet.ui.h
\skipto void SpreadSheet::init()
\printuntil }

\section2 4) Allow the User to Create and Edit Scripts

In some cases it might be sufficient to offer a basic editor widget
which allows the user to write code. For example, you might want to
embed the code editor directly into your application's user interface,
and you don't want to open another toplevel window (such as \QSAD) for
the user. In other cases, you want the users to be able to add and
remove scripts, to have intelligent completion and hints in the
editor, and to use GUI controls in the script. For these cases, including \QSAD
is the best option.

There are two ways for the end user to use \QSAD.
\list 1

\o Open \QSAD and create or edit a script.
\o Define a \link Macros macro\endlink.
\endlist

There are different ways to provide the scripting functionality to the
end user depending on the type of application. For a typical end-user
application you can offer one or both of the approaches to scripting
mentioned above. For example you can provide a menu option and a
toolbar button to launch \QSAD, and an editable combobox which lists
all the global functions. If the user enters a function name that
doesn't exist they are given the opportunity to create a new
function of that name. If the user chooses an existing function, 
\QSAD is launched with the cursor at that function, ready for
editing. A 'Run' toolbar button can be placed beside the combobox,
so that the user can choose a function and click 'Run' to execute
it.

Other approaches include enabling a user to: define functions to validate
data of data entry forms, to customize the functionality of an editor,
to customize the user interface of a complex 3D graphics application or
to provide scripting modules for an image processing application.

The usage of application scripting can greatly vary depending on the
type of application. The spreadsheet application described in this
chapter is an example of a typical end-user application. This example
will make you familiar with most of the important scripting concepts.
Following this example will teach you how to use \QSA to make your
applications scriptable, even if the way your end users will use
application scripting might be very different from what we describe
here.

\section3 Macros

We define a macro as a stand-alone global function. Create a QSScript
using QSProject::createScript() to create a script in global
context. Call QSScript::addFunction() to add a new macro to the
script. You can then open the editor and edit the newly created
function.

\quotefromfile spreadsheet/addscriptdialog.ui.h
\skipto void AddScriptDialog::addScript()
\printuntil }
\printuntil }
\printuntil }

\section3 Launching \QSAD to Create or Edit a Script

In the spreadsheet example the following slot is called to open \QSAD:

\quotefromfile spreadsheet/spreadsheet.ui.h
\skipto void SpreadSheet::openIDE()
\printuntil }

\section3 Adding and Editing Macros

In our spreadsheet example we want to enable the user to add macros as
actions to the toolbar and menu which they can associate with a
function that will be called when they activate the action. To add macros,
we provide a dialog through which the user can either choose an existing
global function to edit, or add a new function (as described in \l
Macros). If the user adds a new function, a new action and icon are created
along with a menu option and toolbar button.

The following code is used in the macro dialog to initialize the
combo box which lets the user choose a script function:

\quotefromfile spreadsheet/addscriptdialog.ui.h
\skipto void AddScriptDialog::init()
\printuntil }

When the user clicks \Button OK in this dialog, the following slot is
executed. If the function the user specified doesn't exist, the user
is asked if this function should be added to the project, in which case,
\c{addFunction()} is called:

\quotefromfile spreadsheet/addscriptdialog.ui.h
\skipto void AddScriptDialog::addScript()
\printuntil }
\printuntil }
\printuntil }

At the end of the function, the \c{newScript()} signal which is
connected to the \c{addScript()} slot in the spreadsheet is emitted.
The \c{addScript()} function creates an action for the macro and adds
a menu option and toolbar button for the macro. In addition, the
action's \c{triggered()} signal is connected to \c{runScript()}. To
find out which function this macro (action) will call, the action and
its associated function are inserted into the scripts map:

\quotefromfile spreadsheet/spreadsheet.ui.h
\skipto void SpreadSheet::addScript( const QString &function, const
\printuntil }

\section2 5) Allow the User to Run Scripts Directly from the Application

It would be tedious for users if they had to launch \QSAD and click
\Toolbutton Run every time they want to execute a script. For this
reason it is normal practice to provide a means by which the user can
execute a function from within the application itself. How this is
acheived depends to some extent on the application and on the
functionality of the script.

One approach to providing the user with access to their script
functions is to provide a list, e.g. a popup list, from
which they can pick the function they wish to execute. (This approach
is taken in the \c{textedit} example.) A list of existing global
functions in the current project is obtained by calling 
QSInterpreter::functions(). To call a script
function, use QSInterpreter::call().

In the \c{spreadsheet} example we have seen that each macro (global
function) is associated with an action and has a corresponding menu
option and toolbar button. Now we'll see how clicking a macro menu
option or toolbar button will cause the macro to be executed.

When the user invokes an action, the \c{runScript()} slot is triggered
by the action, and we have to find which function should then be
executed. For every slot, we call \c{sender()} (implemented in
\c{QObject}), to find out what action triggered that slot. We cast the
\c{sender()} to a \c{QAction} pointer (since we know it is a
\c{QAction}) and then look up this pointer in the \c{scripts} map.
Each action is mapped to the name of the function that it is
associated with, so we can now call \c{QSInterpreter::call()} with the
action's associated function name to execute it:

\quotefromfile spreadsheet/spreadsheet.ui.h
\skipto void SpreadSheet::runScript()
\printuntil }

\section2 Establishing Connections to Signals and Running the Interpreter

It is possible to connect script functions to an application objects
signals by letting the user edit scripts in \QSAD. These connections
are established when the project is evaluated. When the user opens
\QSAD, the project is paused for as long as \QSAD is open, and
no connections are active during this time. When a scripting function
is executed while \QSAD is opened or when play is pressed, the
project is run again each time so that changes to the script become
active. When \QSAD is closed again, the project is re-run once
more and all connections are re-established.

\section2 Error Handling 

If an error occurs, the QSInterpreter emits a \l
QSInterpreter::error() signal.

\section1 Instantiating \c{QObjects} from Qt Script

We have shown that script programmers can easily access application
instances of \c{QObject} subclasses if the class is made available to
the interpreter.  This is sufficient for most situations, but
sometimes it may be desirable to allow script programmers to
instantiate their own object instances. One solution is to expose an
application object which has a slot that acts as a factory function,
returning new \c{QObject} instances.  Another solution is to allow the
script writer to directly instantiate their own objects from C++
classes, with script code like this: 

\code var a = new SomeCppObject( arg1, arg2 );
\endcode

To make a \c{QObject} subclass available as a constructable object in \QS,
use the \l QSObjectFactory class. This class makes it possible to
create new C++ data-types and make them available to \QS.

\section1 Wrapping Non-\c{QObject} C++ Datatypes

\QSA automatically wraps every \c{QObject} you pass to it. It also
wraps every \c{QObject} which is returned from a slot or passed into a
slot. But you often have non-\c{QObject} datatypes in C++ which you
want to make available to the script writer as well. One possibility
is to change your C++ API and convert all those datatypes to
\c{QObject} subclasses. From a design and efficiency point of view,
this is a bad way to go; imagine the effects of having every item of a
listview being a \c{QObject} subclass.

\QSA provides an innovative solution by offering the \l
QSWrapperFactory class. This class allows you to define non-\c{QObject}s
that you can wrap. A \c{QSWrapperFactory} basically offers a \c{QObject} which
can wrap a known C++ datatype. If Qt Script runs accross an unknown
C++ datatype it will ask all installed \c{QSWrapperFactories} if it knows
the type. If one of the \c{QSWrapperFactories} knows the datatype, a
wrapper for that datatype is instantiated and used.

We have demonstrated the flexibility that Qt Script offers for making
applications scriptable. In the next chapter, we will extend the
applications functionality to end users by teaching them to create
scripts with a simple, but complete example.
