Releases: rochus-keller/LeanQt
Widgets, item and graphic views - GUI feature complete
I'm pleased to announce that the migration of the QtWidgets module to LeanQt is complete. The new LeanQt modules are widgets, itemviews and graphics, which can be optionally added to the build.
All widgets (including item and graphics views) work on Linux, Windows and Mac. LeanQt and its examples have been successfully tested with different configurations on Windows x86 & x86_64, on macOS x86_64 and M1, and on Linux x86, ARMv7 (Raspi) and x86_64.
With this release the LeanQt GUI is feature complete, and also my original goals are met in that I can now build my Oberon+ IDE with LeanQt, and am no longer dependent on original Qt for almost any of my projects; as expected I can just recompile my projects with LeanQt without any modifications to the source code.
The number of lines of code is now close to one million SLOC; the uncompressed source tree requires ~55 MB, and the ZIP is 12.5 MB. As described in the last release notes the size of the source tree can be reduced by removing the subdirectories not required.
How to build
LeanQt with item and graphic views can be built easily. By default, the sample applications that match the selected options are also built. Proceed as follows.
- Create a new directory; let's call it new_build (the name doesn't matter).
- Download https://github.com/rochus-keller/BUSY/archive/refs/heads/master.zip and unpack it to the new_build directory; rename the new subdirectory to e.g. BUSY (the name doesn't matter, but must correspond to the following commands).
- Download https://github.com/rochus-keller/LeanQt/archive/refs/heads/master.zip and unpack it to the new_build directory; rename the new subdirectory to e.g. LeanQt (the name doesn't matter, but must correspond to the following commands).
- Open a command line in the BUSY subdirectory and type
cc *.c -O2 -lm -O2 -o lua
orcl /O2 /MD /Fe:lua.exe *.c
depending on whether you are on a Unix or Windows machine; wait a few seconds until the lua executable is built. - Now type
./lua build.lua ../LeanQt -P HAVE_ITEMVIEWS -P HAVE_GRAPHICS
(orlua build.lua ../LeanQt -P HAVE_ITEMVIEWS -P HAVE_GRAPHICS
on Windows); wait until the build finishes; you find the example executables in the BUSY/output subdirectory; if you don't need optimizations but a faster build add the-P HAVE_NOOPT
option to the command line.
Note that both -P HAVE_ITEMVIEWS
and -P HAVE_GRAPHICS
automatically enable HAVE_WIDGETS. If you only need the basic widgets (including the rich text widgets) just use the -P HAVE_WIDGETS
option. If you prefer a shared library version of LeanQt just add the -P HAVE_SHARED
option. All options are declared and described in the root BUSY file.
When you start e.g. the chart application (e.g. via typing output/chart
or by double clicking on the executable), you should see something like this:
Note that it's easy to put all LeanQt stuff into just one shared or static library, and even to statically link the application with LeanQt; the Oberon+ project demonstrates how to do this. Essentially you just have to add libqt to the deps (instead of libqtcore, libqtgui and libqtwidgets) and the qt_client_config to the configs (instead of core_client_config, gui_client_config and widgets_client_config).
How to generate a qmake project
If you don't want to directly build the libraries and executables using the BUSY tool (as demonstrated in the previous section), but instead want to use qmake and Qt Creator to build and/or debug an application, BUSY lets you generate a qmake project.
To do so, just add -G qmake
to the command in step 5 of the procedure in the previous section, so it e.g. looks like ./lua build.lua ../LeanQt -P HAVE_ITEMVIEWS -P HAVE_GRAPHICS -G qmake
. In the BUSY/outputs directory (or any directory you choose with the -B
option) there is now a Project.pro file with a lot of subdirectories with yet other .pro files:
Now e.g. open the Project.pro file in Qt Creator. There you first have to select a Qt kit in the "Configure Project" dialog; BUSY generates qmake files compatible with Qt 5, so select an appropriate kit with a toolchain compatible with your target (it should also work with Qt 6, but I haven't tried). Here is how the project looks in Qt Creator:
It looks and behaves like any other Qt Creator project, but it's not using the Qt version selected with the kit, but LeanQt.
Limitations
See the readme for what is not supported in LeanQt.
The "macintosh" style doesn't fully work yet; there are some display errors; but the "fusion" style works and is automatically enabled also on Macintosh; the "macintosh" style can still be manually enabled e.g. by addign -style macintosh
to the application command line. Personally I'm not much interested in the macintosh style, so if anyone else wants to try their luck with debugging, help is greatly appreciated.
❇️❇️❇️ Merry Christmas and happy coding! ❇️❇️❇️
Gui, Image and Mime module release
This is an important release on the way to a full GUI toolkit. Due to the great demand, I have advanced this. The QtGui module, most of which is now migrated to LeanQt, offers cross-platform windows, 2D graphics, fonts, rich text, image handling and format conversion. The next step is the migration of widgets and some other GUI features.
In the original Qt, the platform backends are in separate plugins. I have never understood the advantage of this, but it makes the deployment more complicated. LeanQt puts both the front and the backend in the same library. It is even easy to merge all parts of LeanQt into just one static or shared library.
The external dependencies were kept very minimal. To compile the LeanQt gui module on Linux, you only need the standard libraries and libxcb, which you can install on Ubuntu, for example, with the package libxcb1-dev. On Mac and Windows, LeanQt only needs standard system libraries installed with the operating system.
The mime module is required by the gui module when compiled on macOS; that's why I also had to migrate it for this release.
The release has been successfully tested on Linux x86 & x86_64, Win x86 & AMD64, and Mac x86_64 & M1. There is the clock application in the examples folder, which displays a digital and analog clock in a window that you can use to try out the gui module.
Update 2022-11-13: meanwhile I was able to run the HAVE_GUI build on a Raspberry Pi Model 3B, BCM2835 ARMv7, 4 cores, Raspbian Linux 4.9.59-v7+ armv7l; time
resulted in 65 minutes (rounded).
How to build
LeanQt can be built easily. By default, the sample applications that match the selected options are also built. Proceed as follows.
- Create a new directory; let's call it new_build (the name doesn't matter).
- Download https://github.com/rochus-keller/BUSY/archive/refs/heads/master.zip and unpack it to the new_build directory; rename the new subdirectory to e.g. BUSY (the name doesn't matter, but must correspond to the following commands).
- Download https://github.com/rochus-keller/LeanQt/archive/refs/heads/master.zip and unpack it to the new_build directory; rename the new subdirectory to e.g. LeanQt (the name doesn't matter, but must correspond to the following commands).
- Open a command line in the BUSY subdirectory and type
cc *.c -O2 -lm -O2 -o lua
orcl /O2 /MD /Fe:lua.exe *.c
depending on whether you are on a Unix or Windows machine; wait a few seconds until thelua
executable is built. - Now type
./lua build.lua ../LeanQt -P HAVE_GUI
(orlua build.lua ../LeanQt -P HAVE_GUI
on Windows); wait a few minutes until the build finishes; you find the clock executable in the BUSY/output subdirectory.
When you start the clock application (e.g. via output/clock
or by double clicking), you should see something like this:
Reducing the size of the source tree
With the new gui, image and thirdparty modules, the uncompressed size of the source tree has grown to ~43 MB (10 MB zip compressed), with close to 3000 C/C++ files and more than 700 kSLOC.
LeanQt is designed to build only the modules you actually need. Unlike original Qt, the QImage and QImageReader/Writer classes can be built separately, independently of the gui module.
For the modules that are not needed, the source code doesn't have to be there either. The BUSY build system offers the possibility to simply remove the subdirectories that are not needed (see here for how this works). If you remove a BUSY module too much, the build will notify you with an according message.
For example, my Oberon+ OBXMC tool only requires the core module of LeanQt. If I therefore want to deploy a minimal LeanQt source tree together with my OBXMC source code, I can just delete the concurrent, gui, image, mime, net, and xml subdirectories, as well as the freetype, harfbuzz, libjpeg, libpng, openssl, xcb, xkbcommon and zlib subdirectories of the thirdparty subdirectory. It is also legal to delete the QtConcurrent, QtGui, QtNetwork and QtXml subdirectories from the includes subdirectory.
The resulting source tree has less than 800 files and requires only ~11 MB (2.6 MB zip compressed), and perfectly works as a substitute of the full source tree in this guide; see the attached LeanQt_core_only.zip file.
Network and Concurrent module release, planned feature set reached
With this official release, LeanQt has reached the feature set planned to date.
The QtNetwork and QtConcurrent modules are fully migrated to LeanQt with new configuration options. The basic configuration only builds QUdpSocket, QTcpSocket, QTcpServer, QDnsLookup and QNetworkProxy; in contrast to original Qt also QFtp is available in the public API. Like before SSL is a separate option; only dynamic loading of the OpenSSL shared library is supported (I will look for an alternative implementation based on mbedTLS). Local sockets, the QNetworkAccessManager with helper classes, as well as bearing and authentication are options that can be enabled via parameters; see the comments for the HAVE_* parameters in https://github.com/rochus-keller/LeanQt/blob/main/BUSY. The code has been modified to reduce mutual dependencies so most options can be individually switched on or off.
With this release LeanQt is ready for non-GUI network applications (like webservers or clients). There are bearer plugins in a separate directory of the original Qt source tree though, which I've never used for the last twenty years, and thus didn't migrate; if you urgently need some of these, please post an issue.
The core module has the new HAVE_ZLIB option to enable qCompress and qUncompress. The net module has been modified so it works with or without ZLIB. In contrast to original Qt ZLIB is an implementation detail of the core module and not directly used by the net module anymore.
This release of LeanQt also uses the most recent features of the BUSY build system; see below for an example.
Copy Example
Instances of the BUSY Copy class can be used at the top level of the build (and also on other levels of course) to both initiate the build for all dependent parts and to copy the resulting libraries or executables to a dedicated directory.
Here is an excerpt of the LeanQt root BUSY file which builds all libraries and examples according to the selected options:
submod examples
let build_examples ! : Copy {
.use_deps += `executable
.deps += examples.hello
if HAVE_OBJECT {
.deps += examples.objects
}
if HAVE_THREADS {
.deps += examples.threads
}
if HAVE_PROCESS {
.deps += examples.process
}
if HAVE_NET_MINIMUM && HAVE_CMDLINE {
.deps += examples.dnslookup
}
if HAVE_CONCURRENT {
.deps += examples.concurrent
}
if HAVE_NETACCESS && HAVE_FILEIO {
.deps += examples.download
}
.outputs += ./{{source_file_part}}
}
Due to the !
behind the instance name build_examples
is automatically selected by BUSY to be built by default (see here for more information how to select build targets and set options via BUSY command line).
The code of the executables lives in the examples
subdirectory which is also a BUSY module. Here is an excerpt of the examples BUSY file which builds the dnslookup executable:
let dnslookup_moc : Moc {
.sources += ./dnslookup.h
}
let dnslookup* : Executable {
.sources += ./dnslookup.cpp
.deps += [ ^libqtcore ^libqtnetwork dnslookup_moc ]
.configs += [ ^core_client_config ^net_client_config ]
}
The example is only built if HAVE_NET_MINIMUM && HAVE_CMDLINE
is true, and if so, it first takes care that the core and net libraries are built and moc is run before compiling dnslookup.cpp
and linking. The resulting dnslookup
executable is copied to the BUSY root build directory.
LeanQt Core and Xml Module Release
I'm proud to present the first official release of LeanQt. It includes most features of the QtCore module (including the moc and rcc tools) and all features of the QtXml module.
The BUSY build system used by LeanQt has been extended to directly support the Qt moc and rcc tools; see below and here for an example.
The following features have been omitted (some may be added later if need be):
- animation, timeline
- big and iconv codecs, icu binding
- other hashes than sha1 and md5
- bytearray gzip compression
- item models
- storage info
- mime data and types
- signalmapper and cleanuphandler
- statemachine
A real-world example in practical use is the Oberon+ OBXMC compiler, see the repository and the build instructions for more information.
Moc Example
Here is an example demonstrating a QObject subclass and inter-object communication using Qt signals and slots. The file with the class declaration marked with Q_OBJECT
has to be processed by the Qt meta-object compiler (moc) to generate the required code. Usually such declarations are in header files and the generated .cpp file is automatically added to the build by BUSY (see here for an example). In this example the declaration is in the .cpp file and the generated file has to be included at the bottom of the .cpp file.
The declaration let moc : Moc { ... }
in the BUSY file (see below) declares an immutable variable of the predeclared class Moc
(see here for the specification). Since the variable moc
is added to the dependency (deps
) list of the executable, it is run for the build; note that the order is significant; the library referenced by qt.libqtcore
has to be built before moc can be called.
The example assumes the following directory layout:
├─ build
│ └─ output
├─ LeanQt
└─ Example
├─ Example.cpp
└─ BUSY
The build
directory includes the BUSY source files (download from here and see here how to build it).
Here is the Example.cpp file:
#include <QCoreApplication>
#include <QTimer>
#include <QObject>
#include <QtDebug>
class Example : public QObject
{
Q_OBJECT
public:
Example(QObject* p = 0):QObject(p){}
~Example() { qDebug() << "done"; emit done(); }
signals:
void done();
public slots:
void hello() { qDebug() << "Hello from Example"; }
};
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
qDebug() << "started";
Example* example = new Example();
QTimer t1;
t1.setSingleShot(true);
QObject::connect(&t1,SIGNAL(timeout()),example,SLOT(deleteLater()));
t1.start(2000);
QTimer t2;
t2.setSingleShot(true);
QObject::connect(&t2,SIGNAL(timeout()),example,SLOT(hello()));
t2.start(1000);
QObject::connect(example,SIGNAL(done()),&app,SLOT(quit()));
return app.exec();
}
#include "Example.moc"
And here is the corresponding BUSY file:
submod qt = ../LeanQt (HAVE_OBJECT)
let file = ./Example.cpp
let moc : Moc {
.sources += file
}
let example ! : Executable {
.sources += file;
.deps += [ qt.libqtcore moc ]
.configs += qt.core_client_config;
.include_dirs += build_dir()
}
The example can be built by running ./lua build.lua ../Example
(or lua build.lua ../Example
on Windows) in the build directory. The resulting executable is in the output
directory.
If you would like to link LeanQt dynamically instead of statically, just add the HAVE_SHARED
identifier to the submodule parameter list (see here for the specification of submodules and parameter lists). Delete the output directory before you rebuild with a different configuration.