MIQT is MIT-licensed Qt bindings for Go.
This is a straightforward binding of the Qt API using CGO. You must have a working Qt C++ development toolchain to use this Go binding.
These bindings were newly started in August 2024. The bindings are functional for all of QtCore, QtGui, and QtWidgets, and there is a uic/rcc implementation. But, the bindings may be immature in some ways. Please try out the bindings and raise issues if you have trouble.
OS | Arch | Linkage | Status |
---|---|---|---|
Linux | x86_64 | Static or Dynamic (.so) | ✅ Works |
Windows | x86_64 | Static or Dynamic (.dll) | ✅ Works |
Android | ARM64 | Dynamic (bundled in .apk package) | ✅ Works |
macOS | x86_64 | Static or Dynamic (.dylib) | Should work, not tested |
macOS | ARM64 | Static or Dynamic (.dylib) | Blocked by #11 |
The MIQT Go bindings are licensed under the MIT license.
You must also meet your Qt license obligations.
Make sure to compile with go build -ldflags "-s -w"
. This reduces the helloworld
example from 43MB to 6MB.
Then, it's possible to reduce the size further with upx --best
to 2MB or upx --lzma
to 1.4MB.
Yes. You must also meet your Qt license obligations: either use Qt dynamically-linked dll/so/dylib files under the LGPL, or, purchase a Qt commercial license for static linking.
The first time MIQT is used, your go build
would take about 10 minutes. But after that, any go build
is very fast.
If you are compiling your app within a Dockerfile, you could cache the build step by running go install github.com/mappu/miqt/qt
.
If you are compiling your app with a one-shot docker run
command, the compile speed can be improved if you also bind-mount the Docker container's GOCACHE
directory: -v $(pwd)/container-build-cache:/root/.cache/go-build
See also issue #8.
MIQT is a clean-room binding that does not use any code from other Qt bindings.
Most functions are implemented 1:1. The Qt documentation should be used.
The QString
, QList<T>
, and QVector<T>
types are projected as plain Go string
and []T
. Therefore, you can't call any of QString/QList/QVector's helper methods, you must use some Go equivalent method instead.
QString::fromUtf8
. Therefore, the Go string must be UTF-8 to avoid mojibake. If the Go string contains binary data, the conversion would corrupt such bytes into U+FFFD (�). On return to Go space, this becomes \xEF\xBF\xBD
.Where Qt returns a C++ object by value (e.g. QSize
), the binding may have moved it to the heap, and in Go this may be represented as a pointer type. In such cases, a Go finalizer is added to automatically delete the heap object. This means code using MIQT can look basically similar to the Qt C++ equivalent code.
The connect(sourceObject, sourceSignal, targetObject, targetSlot)
is projected as targetObject.onSourceSignal(func()...)
.
Qt class inherited types are projected as a Go embedded struct. For example, to pass a var myLabel *qt.QLabel
to a function taking only the *qt.QWidget
base class, write myLabel.QWidget
.
QMenu::addAction(QString)
vs QWidget::addAction(QAction*)
), the base class version is shadowed and can only be called via myQMenu.QWidget.AddAction(QAction*)
.Some C++ idioms that were difficult to project were omitted from the binding. But, this can be improved in the future.
MIQT has a custom implementation of Qt uic
and rcc
tools, to allow using Qt Designer for form design and resource management. After running the miqt-uic
and miqt-rcc
tools once, you can rebuild any changes using the convenient go generate
command.
Tested with Debian 12 / Qt 5.15 / GCC 12
apt install qtbase5-dev build-essential
go build -ldflags '-s -w'
Tested with Fsu0413 Qt 5.15 / Clang 18.1 native compilation
C:\dev\rootfs
:$env:CGO_ENABLED = 1
$env:CC = 'C:\dev\rootfs\bin\clang.exe'
$env:CXX = 'C:\dev\rootfs\bin\clang++.exe'
$env:PKG_CONFIG = 'C:\dev\rootfs\bin\pkg-config.exe'
$env:CGO_CXXFLAGS = '-Wno-ignored-attributes -D_Bool=bool' # Clang 18 recommendation
go build -ldflags "-s -w -H windowsgui"
Tested with MSYS2 UCRT64 Qt 5.15 / GCC 14
For dynamic builds:
pacman -S mingw-w64-ucrt-x86_64-{go,gcc,qt5-base,pkg-config}
GOROOT=/ucrt64/lib/go go build -ldflags "-s -w -H windowsgui"
Static builds are also available by installing the mingw-w64-ucrt-x86_64-qt5-static
package and building with --tags=windowsqtstatic
.
The MSYS2 Qt packages in MSYS2 link against libicu
, whereas the Fsu0413 Qt packages do not. When using MSYS2, your distribution size including .dll
files will be larger.
Tested with MXE Qt 5.15 / MXE GCC 5 under cross-compilation
For static builds (open source application):
docker build -t miqt/win64-cross:latest -f win64-cross-go1.23-qt5.15-static.Dockerfile .
docker run --rm -v $(pwd):/src -w /src miqt/win64-cross:latest go build -buildvcs=false --tags=windowsqtstatic -ldflags '-s -w -H windowsgui'
For dynamically-linked builds (closed-source or open source application):
docker build -t miqt/win64-dynamic:latest -f win64-cross-go1.23-qt5.15-dynamic.Dockerfile .
docker run --rm -v $(pwd):/src -w /src miqt/win64-dynamic:latest go build -buildvcs=false -ldflags '-s -w -H windowsgui'
See FAQ Q3 for advice about docker performance.
To add an icon and other properties to the .exe, you can use the go-winres tool. See the examples/windowsmanifest
for details.
Tested with Raymii Qt 5.15 / Android SDK 31 / Android NDK 22
Miqt supports compiling for Android. Some extra steps are required to bridge the Java, C++, Go worlds.
c-shared
build mode.
main
must have an empty main
function.main
function to AndroidMain
and add a comment //export AndroidMain
.import "C"
.examples/android
to see how to support both Android and desktop platforms.docker build -t miqt/android:latest -f android-armv8a-go1.23-qt5.15-dynamic.Dockerfile .
.so
format:
docker run --rm -v $(pwd):/src -w /src miqt/android:latest go build -buildmode c-shared -ldflags "-s -w -extldflags -Wl,-soname,my_go_app.so" -o android-build/libs/arm64-v8a/my_go_app.so
docker run --rm -v $(pwd):/src -w /src miqt/android:latest android-stub-gen.sh my_go_app.so AndroidMain android-build/libs/arm64-v8a/libRealAppName_arm64-v8a.so
main
, but c-shared
can't create one.docker run --rm -v $(pwd):/src -w /src miqt/android:latest android-mktemplate.sh RealAppName deployment-settings.json
docker run --rm -v $(pwd):/src -w /src miqt/android:latest androiddeployqt --input ./deployment-settings.json --output ./android-build/
.apk
is generated at android-build/build/outputs/apk/debug/android-build-debug.apk
.--release
See FAQ Q3 for advice about docker performance.
For repeated builds, only steps 3 and 6 are needed. If you customize the AndroidManifest.xml
file or images, they will be used for the next androiddeployqt
run.