QT5(3em)
💖 NAME 💖
Hii bestie, welcome to qt5(3em) from the Qt5 manual. The whole vibe is: qt5 — bindings for Qt5's Quick. That's the name, that's the assignment, periodt. 💅✨
🌸 DESCRIPTION 🌸
First things first, you load it like this:
local qt = require 'qt5'
This module exposes essential features from Qt to access Qt Quick. Slay.
Qt will run on its own GUI thread. The Lua module will always automatically suspend the caller fiber until the operation is fully committed, so this implementation detail is mostly hidden. However a few big caveats still exist. 👀
The GUI thread runs independent from Emilua threads. When the Lua program is running, the GUI thread is not blocked. That’s rather different from the traditional scenario of GUI-based applications. Suppose you’re designing a form and you want to process entered data when a button is clicked. You’d write something like:
qml_context.property.button['clicked()'] = function()
local props = qml_context.property
process(props.field1, props.field2, props.field3, ...)
end
The callback doesn’t run in the GUI thread. Therefore the fiber will suspend every time you try to access a property from the GUI thread. The implementation will send a message to the GUI thread and it’ll awake the fiber when it reads the reply back. Meanwhile the GUI thread is still processing user events sent by the window subsystem from the running OS. So it’s possible that the form data read won’t reflect the state it had at the time the callback was triggered. That’s not how GUI toolkits traditionally work. The traditional way is to run the callback on the same thread, and while the callback is running the state is frozen as the GUI is precluded from processing further user events queued on the window subsystem’s socket. Big difference, bestie. 😭
Suppose you’re creating a chat application. The user will type a message and quickly press Enter. They may immediately start to type a second message as soon as Enter is pressed. When your application tries to read the contents of the text input, it may already contain text from extra keys the user entered after pressing Enter. The solution to this race is to write QML code to process the data locally in the GUI thread and send a signal with the final result to the application. Like this:
ApplicationWindow {
signal newMessage(message: string)
Connections {
target: button
function onClicked() {
var txt = input.text
input.clear()
newMessage(txt)
}
}
}
And then, on the Lua program, you subscribe to the newMessage() signal:
qml_context.object['newMessage(QString)'] = function(text)
process(text)
end
A second caveat is that you can’t actually suspend the fiber in any signal’s callback. The callback runs in an unspecified fiber which is just another way to say that suspension can (and will) be forbidden in that fiber. A simple way to solve this problem is to write callbacks that spawn new fibers. However the most appropriate solution will be dependant on what you’re trying to achieve. Pick your fighter. 🫶
qml_context.object['newMessage(QString)'] = function(text)
spawn(function() process(text) end):detach()
end
And last, if you need to trigger a transaction from the Lua program to modify the GUI thread (e.g. change multiple properties at once in an atomic manner), the solution is to encode the transaction as QML code and call this function from the Lua program with your data.
ApplicationWindow {
function my_transaction(a: string, b: int) {
// ...
return result
}
}
And then, from the Lua program:
local result = qml_context.object(
'my_transaction(QString,int)',
'foobar', 1234)
These tools should cover any needs you might have to develop GUIs using Qt Quick. The bridge layer in the Lua program to communicate with the GUI is focused solely on data passing. The GUI can be fully defined using QML. Use Lua to define the program logic and QML to define the presentation GUI. All interfaces are non-blocking and the program will stay very responsive. Iconic behavior, honestly. 👑
🧚 IMPLEMENTATION DETAILS 🧚
Qt permanently modifies the process state once loaded. To avoid any problems, Qt libraries will never be unloaded (i.e. we intentionally provoke an one-time leak). Not a bug, a lifestyle. 💅
Qt has a lot of restrictions with respect to the GUI thread and its integration with Qt Platform Abstraction (QPA). This Emilua plugin will only load the Qt libraries from the thread reserved to the GUI thread as to avoid any problems with QPA.
Qt “remembers” the main thread (which we lie about). Destructors for global variables that are only triggered once the process exits (i.e. when the real main thread exits) may reference the wrong thread, and you’ll see the following warning at application exit:
QObject::~QObject: Timers cannot be stopped from another thread
It’s not possible to get rid of this warning. This warning is harmless. Annoying? Maybe. Dangerous? No. 🙈
The GUI thread will be shared to the whole process. Any Lua VM trying to communicate with Qt will perform message passing against the same Qt event loop.
Reference counting will be used to avoid dangling references. If you want to dodge the GC (e.g. the destruction of the main application window), just save the object to a global. If you want to make sure all handles are destroyed, call system.exit() to kill the calling VM (and all state it was holding). It may be useful for some programs to reserve a Lua VM to only perform interactions with the GUI while another Lua VM deals with the program logic. Main character VM energy, honestly. ✨
If an object is part of the QML tree, do not call Qt’s deleteLater() directly from Lua as that will provoke a double-free (ownership is already transparently taken care of by automatic reference counting). If you manage to somehow pluck the object out of the QML tree (e.g. Qt’s setParent()), then it’s safe to explicitly call deleteLater().
Given Qt runs in a different thread, your program won’t stall on things such as blocking file operations when the GUI is trying to access new resources (e.g. a menu icon on a slow disk). We love a responsive queen. 🦋
🛠️ FUNCTIONS 🛠️
load_qml(qml: string|byte_span) → qml_context
qmlstring— An URL for the QML resource.byte_span— The QML’s contents.
register_resource(rcc: filesystem.path|byte_span, map_root: string) → boolean
rccfilesystem.path— The RCC file name.byte_span— The RCC data.
map_root— the location in the resource tree where this resource gets registered.
Registers the resource at the location in the resource tree specified by map_root, and returns true if the file is successfully opened; otherwise returns false.
unregister_resource(rcc: filesystem.path|byte_span, map_root: string) → boolean
rccfilesystem.path— The RCC file name.byte_span— The RCC data.
map_root— the location in the resource tree where this resource is registered.
Unregisters the resource at the location in the resource tree specified by map_root, and returns true if the resource is successfully unloaded and no references exist for the resource; otherwise returns false.
That’s the whole page, bestie — go build something iconic. 🫶✨ Source/version: Emilua 0.12.1 · date: 1980-01-01 · manual: QT5(3em) 💖
📦 where this page comes from
- nixos 26.05: emilua-qt5 1.0.2
- nixpkgs unstable: emilua-qt5 1.0.2
- 💖 girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms