Have you encountered a situation where a C++ application is already written, but you want to give the user more freedom—without recompilation and complex plugin systems? Every developer has at least once caught themselves thinking: “Let’s just run it—we’ll find out,” hoping that behavioral logic will turn out to be flexible after all.
This chapter carefully leads to a non-obvious but powerful solution. Here it’s revealed how Qt allows embedding JavaScript directly into a C++ application and transforming static code into a managed environment. You’ll discover why professional developers use scripts not for “toys,” but to speed up development, testing, and experimentation with interface logic.
The focus is on QJSEngine and QJSValue, binding signals and slots with JavaScript, extending Qt objects “on the fly,” and a practical example with “turtle” graphics. A few dozen lines of script—and the result appears instantly, without rebuilding the project.
If you want to stop writing “at random” and start consciously managing application behavior, this chapter is better not to postpone.
This chapter includes ready-to-use code examples.
Chapter Self-Check
Which Qt6 class represents the environment for executing JavaScript code and why is at least one of its objects needed?Answer
Correct answer: The QJSEngine class provides the JavaScript execution environment; without it, it’s impossible to interpret and run scripts, as it manages the execution context and provides the API for interaction.
Why does the QJSValue class provide isT() and toT() family methods?Answer
Correct answer: The isT() methods allow determining the type of JavaScript value, and toT() convert it to the needed C++ type, ensuring safe interaction between the two languages.
Why does the evaluate() method return QJSValue instead of just executing the code?Answer
Correct answer: The returned QJSValue contains the script execution result or error information, allowing C++ code to check execution success with the isError() method and handle the result.
Why is the newQObject() method needed when working with Qt objects from JavaScript?Answer
Correct answer: The method creates a JavaScript wrapper around a Qt object, making its properties, signals, and slots accessible to JavaScript code through Qt’s meta-object system.
Why is it necessary to call setObjectName() for all widgets in the turtle graphics example?Answer
Correct answer: The set object name allows access to it from JavaScript code by a clear identifier after registration via setProperty() in globalObject().
Why is the repaint() method called after each drawing command in the Turtle class?Answer
Correct answer: Calling repaint() immediately redraws the widget, showing the command result on screen; without it, changes would accumulate and display only after the entire script completes.
How can JavaScript functions connect to Qt signals and how does this differ from the regular C++ approach?Answer
Correct answer: JavaScript functions connect via the signal’s connect() method directly to functions or script object methods; this allows dynamically changing behavior without recompilation, unlike static C++ connections.
What happens if you don’t check the evaluate() result with the isError() method?Answer
Correct answer: Script execution errors will go unnoticed, the application may work incorrectly or not perform expected actions, and the user won’t receive information about the problem.
How can you extend a Qt object’s functionality with new methods directly from JavaScript?Answer
Correct answer: You can add new methods to the object by assigning functions to its properties (e.g., turtle.circle = function(){…}), as shown in the circle drawing example.
What are the four ways to connect a signal with JavaScript shown in the example and what are their differences?Answer
Correct answer: A signal can be connected to: (1) a script function, (2) a JavaScript class object method, (3) a Qt widget slot directly, (4) an anonymous function right in connect(). The choice depends on the need to preserve state and code reuse.
Why create a separate JSTools class with functions instead of directly using the Qt API?Answer
Correct answer: JSTools provides a convenient high-level interface for JavaScript, hiding the complexity of the Qt API and creating a familiar environment for web developers (alert, print, etc.).
Why are all functions in JSTools declared as slots for working with scripting language?Answer
Correct answer: Only Qt slots are accessible through the meta-object system and can be called from JavaScript after registering the object via newQObject(); regular C++ methods are not accessible to scripts.
What does the globalObject() method do and why set properties in it?Answer
Correct answer: globalObject() returns the global JavaScript environment context; setting properties in it makes objects and variables accessible throughout the script program without additional parameter passing.
How does the turtle graphics example use the let operator instead of var and why is this important?Answer
Correct answer: The let operator creates variables with block scope and conforms to modern ECMAScript standards, preventing errors due to variable leakage beyond loops and conditions.
Practical Exercises
Easy Level
JavaScript Calculator
Create a Qt application with two text fields for entering numbers, a dropdown list for selecting an operation (+, -, *, /), and a “Calculate” button. When the button is pressed, a JavaScript script should execute that takes values from the fields, performs the operation, and outputs the result to a label (QLabel). Use QJSEngine for calculations.
Hints: Create a QJSEngine and pass widgets to it via newQObject(). Form a string with a JavaScript expression (e.g., “10 + 5”) and execute via evaluate(). Convert the result to a string with the toString() method. Don’t forget to check for errors via isError().
Medium Level
Drawing Shapes via JavaScript
Extend the turtle graphics example by adding the ability to control pen color and line thickness. Create JavaScript functions setColor(r, g, b) and setWidth(width) that will change QPainter parameters. Implement ready scripts for drawing: a multicolored spiral, a rainbow of concentric circles, and an abstract pattern with changing line thickness. Add the ability to save the result to a PNG file via JavaScript.
Hints: Add setColor() and setWidth() slots to the Turtle class that change QPen via QPainter::setPen(). For saving a file, create a save(filename) slot using QPixmap::save(). In scripts, use loops with changing color and thickness parameters. The class name in JavaScript should be registered via setProperty().
Hard Level
Interactive Editor with JavaScript Plugins
Create a text editor with JavaScript plugin support. The editor should load .js files from the plugins folder and provide them with an API for working with text: getText(), setText(), insertText(), getSelection(), replaceSelection(). Implement three plugins: (1) text statistics counter (words, characters, sentences), (2) automatic code formatting (adding indentation), (3) search and replace with regular expression support. Plugins should have their own buttons in the interface and be registered dynamically at startup.
Hints: Create a TextEditorAPI class with slots for working with QTextEdit text. Scan the plugins folder via QDir::entryList(). Each plugin should return an object with an execute() method and name, description properties. Use QJSValue::property() to extract plugin information. Dynamically create buttons for each plugin and connect their clicked to calling the corresponding function via evaluate(). Handle plugin loading errors.
💬 Join the Discussion!
Managed to launch turtle graphics? Curious what patterns you created while experimenting with angles?
Share your JavaScript plugins for Qt, discuss the features of integrating QJSEngine in real projects, or ask questions about when to use scripts instead of C++!