Every developer has encountered the moment when even the most well-thought-out application suddenly turns out to be “too rigid”. The user lacks one function, testing requires logic changes, and for a minor modification, you have to rebuild the entire project and go through the release cycle again.
This chapter carefully leads to an unexpected solution: you’ll discover how an embedded scripting language can turn a C++ Qt application into a flexible platform that can be extended and customized without recompilation. Here the secret is revealed why professional developers increasingly separate the “core” from extension logic and how this directly saves time on support and product development.
You’ll see how, using Qt’s meta-object model, a few macros, and the built-in JavaScript engine, you can manage application objects, automate lengthy operations, and create hybrid solutions.
If the idea of an extensible application without pain has already hooked you—it only gets more interesting from here.
This chapter includes ready-to-use code examples.
Chapter Self-Check
Why do slots and properties of a class inherited from QObject automatically become accessible in JavaScript without writing additional code?Answer
Correct answer: Thanks to Qt’s meta-object model and the MOC preprocessor, which generates meta-information about the class, including data about its slots, signals, and properties, making them visible to the scripting language.
What does the evaluate() method of QJSEngine class return and what data types can the returned value contain?Answer
Correct answer: The method returns a QJSValue object, which can contain values of different types: strings, numbers, boolean values, arrays, QVariant objects, or an error in case of unsuccessful interpretation.
Why is it necessary to create a wrapper class for regular C++ classes not inherited from QObject?Answer
Correct answer: A wrapper class inherited from QObject aggregates an object of the needed class and provides delegating methods as slots and properties, making the original class functionality accessible to JavaScript.
Why is it recommended to implement performance-critical application components in C++ rather than JavaScript?Answer
Correct answer: Interpreted JavaScript code executes significantly slower than compiled C++ code, so performance-critical operations are better implemented in a compiled language.
What is the Q_INVOKABLE macro used for when declaring class methods?Answer
Correct answer: The macro makes a regular class method (not a slot) visible and callable from JavaScript scripting language without needing to declare it as a slot.
Which Qt6 module is used for JavaScript support and why can’t you use QtScript?Answer
Correct answer: Qt6 uses the QtQml module, as the QtScript module was completely removed from the library. QtQml provides modern JavaScript support based on the ECMAScript 2017 standard.
How does scripting language support simplify providing technical support to program users?Answer
Correct answer: You can implement bug workaround code and add specific features for particular users on-site through scripts, without changing or recompiling the main application source code.
What is the advantage of using properties (Q_PROPERTY) instead of direct slot calls when working with scripts?Answer
Correct answer: Properties provide a more natural and familiar syntax for scripting language developers (for example, object.property = value instead of object.setProperty(value)), making the API more intuitive.
How do you check that a script execution result contains an error and get a description of that error?Answer
Correct answer: You need to call the isError() method on the QJSValue object returned by evaluate(), then get the error text with the toString() method.
Why is it impossible to get access to all Qt or application functionality from a JavaScript script at once?Answer
Correct answer: Because scripts only get access to specific application objects that the developer explicitly made accessible via newQObject() and setProperty(), which is a conscious limitation for control and security.
What will happen if you pass a string with a JavaScript syntax error to the evaluate() method?Answer
Correct answer: The method will return a QJSValue object containing an error, which can be detected by checking isError() and getting the description via toString().
What are the three main steps needed to add JavaScript support to a Qt project?Answer
Correct answer: Add the QtQml module to the project file (QT += qml), include the QJSEngine header file, create a QJSEngine object, and register needed objects via newQObject().
Practical Exercises
Easy Level
Calculator via JavaScript
Create a Qt application with an input field (QLineEdit) for mathematical expressions and a “Calculate” button. When the button is pressed, the application should use QJSEngine to evaluate the entered expression and display the result in a QLabel. Handle syntax error cases.
Hints: Use QLineEdit::text() to get the expression. Pass the string to scriptEngine.evaluate(). Check the result with the isError() method. If there’s no error—use toNumber() or toString() to display the result in the label.
Medium Level
Configurable Widget via Properties
Create a ConfigurableWidget class inherited from QWidget with several properties (Q_PROPERTY): background color (backgroundColor), font size (fontSize), text (text), and visibility (visible). Implement the ability to load configuration from a JavaScript file that sets the values of these properties. Provide a slot for applying the configuration and a signal notifying of changes.
Hints: Declare Q_PROPERTY for each property with READ, WRITE, and NOTIFY. Create a loadConfig(QString filename) method that reads the JS file and executes it via evaluate(). Register the widget in the engine via newQObject() under the name “widget”. In the JS file, use syntax: widget.backgroundColor = “#ff0000”.
Hard Level
JavaScript Plugin System with API Wrapper
Develop a mini-plugin system where the main C++ application provides an API through a wrapper class for a third-party library (for example, a class for working with a task list). Create a TaskManager class (non-Qt class) with methods for adding, deleting, and getting tasks. Implement a TaskManagerWrapper wrapper class inherited from QObject with corresponding slots, signals, and properties. Load JavaScript plugins from the plugins/ folder, each plugin must have an initialize() function and have access to the global taskManager object.
Hints: In TaskManagerWrapper, aggregate a TaskManager object. Create delegating slots for all operations. Use QDir to scan the plugins/ folder. For each .js file, create a separate QJSEngine or use one engine. Register taskManager via scriptEngine.globalObject().setProperty(). In plugins, call taskManager.addTask(“name”) and other methods. Handle plugin loading and execution errors.
💬 Join the Discussion!
Figured out JavaScript integration in Qt applications? Have questions about creating wrappers or using the meta-object model?
Share your experience implementing plugin systems, talk about the difficulties you encountered, or help other readers master using JavaScript in Qt applications!