QtScript
QtScript (Qt Script) is the ECMAScript-based application-scripting language and engine that Trolltech shipped inside Qt 4.3 in May 2007 to replace Qt Script for Applications (QSA): JavaScript with Qt signal-and-slot connections and automatic QObject bindings, re-based on WebKit's JavaScriptCore in Qt 4.6, used to run the first QML, deprecated in Qt 5.5 and dropped from Qt 6.0
Created by Trolltech ASA of Oslo, Norway (from 2008 Qt Software at Nokia, from 2012 Digia and from 2014 The Qt Company). Kent Hansen was the Trolltech/Nokia engineer who announced QtScript on the Qt Labs blog on 5 January 2007, maintained the Qt Script Generator bindings from 2008 to 2012 and blogged the JavaScriptCore and V8 back-end work of 2009-2011; QtScript's immediate predecessor QSA (2003) was Trolltech's, and the JavaScriptCore engine underneath QtScript from Qt 4.6 descends from KDE's KJS via Apple's WebKit
QtScript - “Qt Script” in the documentation, the QtScript module and QScript* classes in code - is the JavaScript that lived inside the Qt application framework from 2007 to 2020. It is ECMAScript 3 with Qt bolted on: a C++ program creates a QScriptEngine, hands it its QObjects, and the objects’ properties, signals, slots and children appear in script, where a signal can be wired to a function with button.clicked.connect(f). Trolltech built it to replace a separately sold product, Qt Script for Applications, then rebuilt it on WebKit’s JavaScriptCore, used it to run the first QML, tried to move it to V8, and finally let it go when Qt 6 arrived. Along the way it was the scripting language of Amarok, Plasma, Stellarium, MuseScore and Qt’s own build tool.
History and Origins
Qt Script for Applications (2003-2008)
Trolltech’s first scripting product was announced from Oslo on 2 July 2003. Qt Script for Applications (QSA) 1.0 “takes static Qt/C++ applications, and makes them dynamic”, the press release said; Trolltech’s president Eirik Eng promised that “all developers need to do is add a few lines of code to their Qt applications, and then the apps are enabled for scripting and extending.” QSA was a separate library, dual-licensed under the GPL and a commercial licence, and it was more than an interpreter: it came with QSA Workbench, an editor with completion and project management, and C++ classes (QSInterpreter, QSProject, QSEditor) for embedding them.
The language QSA interpreted was already called “Qt Script”, and it was an odd hybrid. Qt’s later porting guide describes it as “derived from ECMAScript 3.0 and 4.0”: the built-in library followed the third edition of ECMA-262, but “most of the run-time logic, such as classes and scoping rules, is based on the ECMAScript 4.0 proposal”, so QSA programmers wrote class Car { var regNumber; function Car(n) { ... } } with static members and C++-style inheritance. The ECMAScript 4 proposal was eventually abandoned, which left QSA speaking a dialect no other engine spoke. The last version, QSA 1.2.2, has documentation copyrighted 2006; Trolltech’s notice that “QSA for Qt 4 will reach end of life status December 1st, 2008” and that “a new scripting engine will be available with Qt 4.3 conforming to ECMA Standard 262” was quoted by projects that had built on it.
QtScript in Qt 4.3 (2007)
The new engine was previewed by Kent Hansen on the Qt Labs blog on 5 January 2007 under the title “Say hello to QtScript!” and shipped in Qt 4.3.0 on 30 May 2007. KDE.news summarised the release’s headline features as “QtScript, an ECMAscript standard application scripting engine, replacing QSA; SSL support; improved OpenGL engine”. Unlike QSA it was part of Qt itself - “the QtScript module is part of the Qt Desktop Edition and the Qt Open Source Edition”, linked with QT += script - and unlike QSA it was, in the porting guide’s words, “only an interpreter, it does not provide an editor, completion or script project management”, but it “provides almost full compliance with the ECMAScript standard and performs significantly better”. The 4.3 module had six public classes: QScriptEngine, QScriptValue, QScriptValueIterator, QScriptContext, QScriptable and QScriptExtensionPlugin.
Qt 4.4 (May 2008) filled in the API - qScriptConnect() for connecting a signal to a script function from C++, abortEvaluation(), automatic handling of QObject-derived slot arguments - and Qt 4.5 (March 2009), the release that also took Qt to the LGPL, added a second module, QtScriptTools, whose QScriptEngineDebugger gave applications a drop-in graphical debugger, along with checkSyntax(), setGlobalObject() and multi-line string literals.
The generator and “scripting in the large” (2008-2009)
Two projects widened QtScript’s ambitions. The Qt Script Generator, imported from Trolltech’s Labs Subversion into git on 3 July 2008 and maintained by Kent Hansen into 2012, parsed Qt’s own headers and produced binding plugins for the whole Qt API, so that a script could qs.script.importExtension("qt.gui") and build a complete GUI without any C++. Amarok 2 shipped those bindings with its scripts; MuseScore did the same and later counted the cost - more than 1,100 generated files and 297,000 lines.
The second was the change of engine. During 2009 Nokia’s Qt developers replaced the interpreter written for 4.3 with JavaScriptCore, the engine of Apple’s WebKit, which itself descends from KJS, the JavaScript engine KDE wrote for Konqueror. Qt 4.6 (1 December 2009) recorded the switch tersely: “The QtScript module is now based on the 3rdparty JavaScriptCore library. It is only available under the LGPL or a compatible license.” The changelog promised “many optimizations” and “improved ECMA script compliance”, and warned of two behaviour changes: Date no longer went through QDateTime, and RegExp objects were “no longer thin wrappers around QRegExp; they are now ‘proper’ JavaScript RegExp objects.” Hansen’s Qt Labs post of 23 November 2009, “QtScript in 4.6”, put numbers on it with SunSpider run under Qt 4.5 and 4.6 on his Linux machine (the charts show speed-ups relative to 4.5 per SunSpider test; no absolute times were published), and read them as a change of scope: pre-4.6 QtScript was “sufficient for ‘scripting in the small/medium’, but with 4.6 it gains the ability to better tackle ‘scripting in the large’”. He also cautioned that “it’s not always going to be faster”: JavaScriptCore always JIT-compiled (there was no hot-spot detection), so for a tiny script such as 1 + 2 compiling could cost more than the old stack-based VM’s interpretation - the reason QScriptProgram, which caches the compiled form, was slipped into 4.6 as an internal class and made public in 4.7. The SunSpider suite remains in the module’s benchmarks and third-party attribution pages.
QML, V8 and the split (2010-2012)
Qt 4.7 (21 September 2010) added QML and Qt Quick, “a declarative language oriented on JavaScript”, and the QtDeclarative engine that ran QML bindings was built directly on QScriptEngine - the private engine header includes QtScript/qscriptengine.h, QScriptClass and QScriptString. QtScript thereby became the runtime of Nokia’s mobile user-interface strategy. The same release “significantly improved performance of the Qt/C++<–>JavaScript bridge” and made QScriptProgram, for pre-compiling scripts, a public class.
That success exposed the module’s weakness. On 20 January 2011 Hansen wrote that “we want Qt to have the best possible JavaScript technology. To make this happen, we need the ability to make drastic changes to the implementation - even replacing it - underneath our public APIs. If an API exposes implementation details, that will cause us to limp.” A month later, on 24 February, “Qt people, our JavaScript platform is burning rubber” announced open work on a V8 back-end: “the status of the V8-based back-end is that we still have some QtScript API that’s not implemented, and there are autotests failing, but several of the QtScript and QML examples and demos run.” The wip/v8 branch of the module was worked on until July 2011. The outcome was not a V8-backed QtScript but a fork in the road: QML got V8 (and, from Qt 5.2, Qt’s own V4 engine) through a new, smaller class, QJSEngine, while QtScript kept JavaScriptCore. On 27 April 2011 the module was cut out of the monolithic tree (“Initial import from the monolithic Qt”) as the qtscript repository, and Qt 5.0 (19 December 2012) shipped it as a compatibility module still sitting on a JavaScriptCore snapshot dated 27 January 2011 - the snapshot it carries to this day.
Deprecation and removal (2015-2020)
Qt 5.5 (July 2015) made the position official: “With Qt 5.5 the following modules are deprecated: Qt WebKit, Qt Declarative (Qt Quick 1), Qt Script. These modules are still included in the Qt 5.5 release, but considered for removal in the future releases of Qt.” The module page acquired the warning that has headed it since: “This module is not actively developed. It is provided for backwards compatibility with Qt 4 only. For new code, use QJSEngine and related classes in the Qt QML module instead.” Qt 5.15 LTS (26 May 2020) was the last feature release to carry it, and the Qt 6.0 release notes of 8 December 2020 list Qt Script and Qt Script Tools among the “Removed Modules in Qt 6.0”, with the boilerplate that “they may be re-introduced in a future release of Qt 6”. They have not been. The 5.15 branch lived on as commercial long-term support - v5.15.3-lts on 3 March 2021, with matching -lts-lgpl tags following about a year behind - but Qt Script’s commits in it are version bumps; the last substantive change, a sanitizer suppression in the JavaScriptCore heap code, is dated 26 October 2021, and the final tags are v5.15.19-lts (19 May 2025), v5.15.18-lts-lgpl (31 October 2025) and v5.15.19-vxworks-lts (18 December 2025).
Design Philosophy
Standard JavaScript, plus Qt
QSA’s lesson was that a private dialect is a liability. QtScript’s documentation therefore opens by placing it firmly in the standard: “Qt Script is based on the ECMAScript scripting language, as defined in standard ECMA-262. Microsoft’s JScript, and Netscape’s JavaScript are also based on the ECMAScript standard”, and it refers readers who do not know the language to JavaScript: The Definitive Guide rather than to a manual of its own. The “ECMAScript Compatibility” section claims the whole of ECMA-262’s built-in objects and then lists the extensions exhaustively, and the list is short: __proto__, __defineGetter__/__defineSetter__, Function.prototype.connect and disconnect, QObject.prototype.findChild, findChildren and toString, and a gc() function. Everything else Qt-specific arrives as objects the host application chooses to expose.
The QObject is the binding
The core idea, inherited from QSA and Qt’s meta-object system, is that a C++ class does not need a hand-written binding if it is a QObject. engine.newQObject(&button) wraps a widget; in script button.text, button.styleSheet and button.show() work because Qt’s meta-object already knows the properties and slots, and button.clicked.connect(function() { ... }) works because it knows the signals. The wrap options let the host decide how much to expose - whether child objects appear as properties, whether deleteLater() is reachable (Qt 4.5 added the option to hide it “so that scripts can’t delete application objects”), whether superclass members are included. QScriptable lets a C++ slot find out which engine and which this invoked it; QScriptClass lets C++ define objects with entirely custom property behaviour; QScriptEngineAgent lets a debugger or profiler watch execution. The design goal, stated in the Qt 4.3 module page, was simply “making Qt applications scriptable”.
Scripts as a product feature, not a language product
QtScript never had a standalone interpreter beyond the qscript example, no package ecosystem and no standard library beyond ECMAScript’s; qsTr() and QT_TR_NOOP() for translation were the only global functions Qt added, and even those needed installTranslatorFunctions(). It was designed as the scripting layer of something else, which is why its heaviest users were applications with plug-in cultures - a music player, a desktop shell, a planetarium, a notation editor - and why the loss of the module fell on them rather than on any community of “QtScript programmers”.
The Language
Hello, World
Because QtScript is ECMAScript, the plainest program is the plainest JavaScript. The qscript example shipped with the module is a small interactive shell that provides print():
| |
The Qt-flavoured Hello World is the helloscript example, which is two files. The C++ side creates the engine and exposes a button:
| |
and the script, helloscript.js, drives the widget through its ordinary Qt properties and slots:
| |
Signals and slots
Qt’s callback mechanism appears in script as methods on the signal:
| |
A connection may name a receiver object as well as a function, so that this inside the slot is bound; if a signal is overloaded, Qt 4.5 onward requires the full signature (obj["valueChanged(int)"].connect(f)) rather than the bare name. Script functions can also be invoked as slots from C++ with qScriptConnect(), and a script can emit a signal simply by calling it. The QObject.prototype.findChild() extension mirrors QObject::findChild(), so a script given only a top-level window can navigate to the widgets it wants.
Objects, prototypes and the QSA migration
The porting guide’s worked example shows what changed between QSA’s classes and QtScript’s prototypes. QSA:
| |
QtScript, which has no class:
| |
The same prototype mechanism is how C++ extends script-side behaviour of value types: engine.setDefaultPrototype(qMetaTypeId<QPoint>(), proto) gives every QPoint returned from a slot a script prototype, and qScriptRegisterMetaType() supplies conversion functions for custom types.
The C++ API
| Class | Introduced | Purpose |
|---|---|---|
QScriptEngine | Qt 4.3 | Evaluate code, hold the global object, wrap QObjects and QMetaObjects, convert values |
QScriptValue | Qt 4.3 | Container for any script value; toNumber(), toString(), property(), call(), construct() |
QScriptContext | Qt 4.3 | A function invocation: thisObject(), argument(i), throwError() |
QScriptable | Qt 4.3 | Lets a C++ slot access the calling engine and context |
QScriptExtensionPlugin | Qt 4.3 | Loadable extensions for importExtension() (the Script Generator’s output) |
QScriptClass, QScriptClassPropertyIterator | Qt 4.4 | Custom object classes with programmatic properties |
QScriptEngineAgent, QScriptContextInfo, QScriptString | Qt 4.4 | Execution tracing, backtraces, interned strings |
QScriptEngineDebugger (QtScriptTools) | Qt 4.5 | Graphical debugger with console, breakpoints, locals and stack views |
QScriptSyntaxCheckResult | Qt 4.5 | Result of checkSyntax() for interactive shells |
QScriptProgram | Qt 4.6 (internal), public in Qt 4.7 | Pre-parsed script for repeated evaluation |
The rule of thumb in the documentation was “if you can express it as a QObject, you don’t need to write a binding” - and it is the part of the API that survived, in reduced form, as QJSEngine::newQObject() in Qt 5 and 6.
Evolution
| Qt release | Date | QtScript |
|---|---|---|
| QSA 1.0 | 2 July 2003 | Separate add-on; ES3/ES4-hybrid “Qt Script”; QSA Workbench |
| Qt 4.3 | 30 May 2007 | QtScript module debuts with its own interpreter; QSA declared replaced |
| Qt 4.4 | 6 May 2008 | qScriptConnect(), abortEvaluation(), QScriptClass, QScriptEngineAgent |
| Qt 4.5 | 3 March 2009 | QtScriptTools debugger; checkSyntax(); LGPL licensing |
| Qt 4.6 | 1 December 2009 | Engine replaced by JavaScriptCore (LGPL-only); JIT; proper RegExp and Date |
| Qt 4.7 | 21 September 2010 | Runs QML/Qt Quick 1; QScriptProgram; faster C++ bridge |
| Qt 5.0 | 19 December 2012 | Separate qtscript repository; QJSEngine appears as the successor |
| Qt 5.5 | July 2015 | Deprecated |
| Qt 5.15 | 26 May 2020 | Last feature release with the module |
| Qt 6.0 | 8 December 2020 | Removed |
The module’s own source history since the 2011 split is compact: 33 commits by Kent Hansen and a long tail of build-system and licence-header maintenance by the Qt Project’s release engineers. The JavaScriptCore snapshot was refreshed in 4.7 and once more in January 2011 and then frozen, which is why Qt Script’s language level stopped where a January 2011 JavaScriptCore stopped: it never gained let, arrow functions, class or modules, the ECMAScript 2015 features that QJSEngine’s V4 engine later acquired.
Current Relevance
Running QtScript today
There is no Docker image and no standalone interpreter to download, but the module is still easy to build against on a 2025-era Linux system: Debian 12 “bookworm” (5.15.8), Ubuntu 24.04 (5.15.13) and Debian 13 “trixie” (5.15.15) all package qtscript5-dev, libqt5script5 and libqt5scripttools5. Building the qscript example from the module’s examples/script/qscript directory yields an interactive shell that evaluates typed-in ECMAScript and runs script files given on the command line, which is enough to run the language examples above. Applications that still depend on the module - anything built on Qt 5 that has not been ported - continue to work as long as Qt 5.15 does, and The Qt Company’s commercial 5.15 support, extended to five years, was scheduled to run to 26 May 2025 - although -lts tags kept appearing into December 2025 - and for open-source users the KDE project’s community patch collection is the only ongoing maintenance.
Migration
Most of QtScript’s larger users have moved. The Qt documentation’s advice is to use QJSEngine, whose evaluate(), newQObject(), globalObject() and QJSValue are near-copies of the QtScript originals; what is missing is QScriptClass, QScriptEngineAgent, the debugger and QScriptable. The projects that made the move recorded how uneven it was: Stellarium found the old module “more flexible and easier to fine-tune” and lists the lost isEvaluating() and pause() in its migration notes; Amarok warned that “previous scripts are often not compatible”; qbs chose QuickJS over QJSEngine altogether; MuseScore had already decided in 2012 that “the bindings are dead” and rebuilt on QML.
Why It Matters
QtScript was the mainstream way to script a C++ desktop application for the better part of a decade, and it did it with an idea that has outlived it: that a reflective object model - Qt’s meta-object system - can stand in for hand-written language bindings. A QObject handed to the engine was scriptable without another line of code, which is why a music player, a desktop shell and a planetarium could each grow a plug-in culture in the same language their users already knew from the web.
It is also a case study in what an embedded JavaScript engine costs its host. Trolltech wrote one interpreter, Nokia swapped it for JavaScriptCore, researched V8, and concluded that an API which exposed engine internals “will cause us to limp”; the successor, QJSEngine, exposes almost nothing of the engine underneath, and QML has since changed engines twice without breaking its users. QtScript’s deprecation notice - “provided for backwards compatibility with Qt 4 only” - is the epitaph of a module that was, for Qt 4.7 and 4.8, the runtime of QML itself.
Sources
- Qt 4.3 documentation, QtScript Module (doc.qt.io/archives/4.3/qtscript.html); Qt 4.8 documentation, Making Applications Scriptable, ECMAScript Reference and Moving from QSA to Qt Script (doc.qt.io/archives/qt-4.8/)
- Qt 5.15 documentation, Qt Script module page and third-party attribution (JavaScriptCore snapshot 2011-01-27, SunSpider benchmarks)
- Qt 6.0 documentation, What’s New in Qt 6.0, “Removed Modules in Qt 6.0”
- Qt wiki: New Features in Qt 5.5 (Deprecated Functionality), Qt version history, Qt History
dist/changes-4.3.0throughchanges-4.7.0in the qt/qt repository (GitHub mirror)- qt/qtscript repository (code.qt.io / GitHub): tags v5.0.0 to v5.15.19-vxworks-lts, branches 5.15 and wip/v8,
examples/script/helloscriptandexamples/script/qscript - qt-labs/qtscriptgenerator repository (code.qt.io), initial import 3 July 2008
- QSA 1.2.2 documentation archive (doc.qt.io/archives/qsa-1.2.2/); Trolltech press release “Trolltech Introduces Qt Script for Applications, (QSA) Version 1.0”, LWN.net, 2 July 2003
- KDE.news, “Trolltech Releases Qt and Qtopia Core 4.3”, 4 June 2007
- Kent Hansen, Qt Labs / Qt Blog: “QtScript Bindings for Animations & States Classes” (4 June 2009), “Resolving QtScript’s legacy APIs” (20 January 2011), “Qt people, our JavaScript platform is burning rubber” (24 February 2011), “Say Hello to Envjs for QtScript” (10 March 2011); “QtScript in 4.6” (23 November 2009, read from the Wayback Machine capture of labs.qt.nokia.com/2009/11/23/qtscript-in-46/ dated 19 January 2011); “Say hello to QtScript!” (5 January 2007) as cited by Wikipedia and search indexes
- Lars Knoll, “Qt 5.15 LTS Released” (26 May 2020) and “Qt 6.0 Released” (8 December 2020), Qt Blog
- Amarok ChangeLog (invent.kde.org/multimedia/amarok); Wikipedia, Amarok (software)
- KDE TechBase, Development/Tutorials/Plasma4/JavaScript/API; Wikipedia, KDE Software Compilation 4
- Stellarium ChangeLog, Scripting Engine (0.22 documentation) and StelScriptMgr Class Reference (1.x documentation)
- MuseScore, “Introducing the new QML based plugin framework”, 10 July 2012 (SourceForge news mirror)
- qbs repository changelog
changes-2.0.0.mdand tags - Daz 3D, Daz Studio 4 Reference Guide: Scripting
- sources.debian.org and packages.ubuntu.com:
qtscript-opensource-src5.15.8 (Debian 12 bookworm), 5.15.15 (Debian 13 trixie) and 5.15.13+dfsg-1 (Ubuntu 24.04 noble) - Wikipedia, QtScript, Qt (software), WebKit
Timeline
Notable Uses & Legacy
QML and Qt Quick 1 (Qt 4.7-4.8)
The most consequential use was Qt's own: the QtDeclarative module that Qt 4.7 introduced in September 2010 evaluated QML's property bindings and JavaScript functions with a QScriptEngine on the JavaScriptCore back-end (the Qt 4.8 engine header includes QtScript/qscriptengine.h). Every Qt Quick 1 application - including those written for Nokia's Symbian and MeeGo phones - therefore ran its logic on QtScript until Qt 5 gave QML the V8 and then V4 engines
Amarok 2 (KDE music player)
Amarok 2.0 (December 2008) rebuilt its scripting on QtScript with the Qt Script Generator bindings ('QtBindings support for the scripts', its ChangeLog records), so lyrics fetchers, internet-radio services and, from Amarok 2.2, context-view applets were written in JavaScript against Amarok.Playlist, Amarok.Engine and similar objects. Wikipedia's QtScript article uses it as the canonical example. The engine 'has been ported from QtScript to QJSEngine' in the Amarok 3.0 cycle (2.9.82 beta, April 2024), with the ChangeLog admitting that 'previous scripts are often not compatible'
KDE Plasma 4 desktop
Plasma's 'Simplified JavaScript' plasmoid API, which KDE TechBase documents as 'powered by Qt's QtScript system', let desktop widgets be written in JavaScript without a compiler; the engine moved into kdebase/runtime in KDE 4.3 (August 2009) and KDE SC 4.4 (February 2010) opened plasmoid development to QtScript, with Aaron Seigo's 'JavaScript Jam' contest to promote it. Later Plasma 4 releases also used the engine for desktop-layout scripts. Plasma 5 replaced this with QML and QJSEngine
Stellarium planetarium
Stellarium 0.10.1 (February 2009) replaced its home-grown StratoScript with a scripting feature 'based on the Qt Scripting Engine', in which ECMAScript scripts drive the sky view through slots on the StelMainScriptAPI object - the show-and-tell scripts bundled with every release are written in it. The QtScript module (under Qt 4, then Qt 5) was used from 0.10.1 to 0.22 (2022); the Qt 6-based Stellarium 1.0 of October 2022 ported StelScriptMgr to QJSEngine, and its class documentation keeps 'Notes on migration from QtScript to QJSEngine'
MuseScore 1.x plugins and qbs
MuseScore's first plugin system (in the 0.9.x and 1.x releases, up to 1.3 in 2013) was QtScript with generator-made Qt bindings - '> 1100 files with > 297000 lines of code' of bindings, as the project's July 2012 announcement of the QML replacement complained. Qt's own build tool qbs (first commit February 2012, 1.0 in May 2013) executed the JavaScript in its project files on QtScript for eleven years, until qbs 2.0 (30 March 2023) 'switched JavaScript engine from QtScript to QuickJS'
Daz Studio
Daz 3D's scripting reference records the two generations of Qt scripting in one commercial product: Daz Script 1 was based on QSA, and when Trolltech ended QSA, Daz Script 2 was rebuilt on Qt Script's ECMA-262 compliance; Daz Studio 4.0.x was built on Qt 4.7 and 4.5+ on Qt 4.8, and the company points its script authors at Qt's 'Moving from QSA to Qt Script' document