125
I Use This!
Very High Activity

News

Analyzed 1 day ago. based on code collected 3 days ago.
Posted almost 5 years ago
Posted almost 5 years ago
Posted almost 5 years ago
One thing that comes up occasionally in support is that when you have multiple chart views that it would be good to align them up underneath each other so that the actual plotted size is the same for both charts. If you have similar sizes for the ... [More] axes then this is easily achieved as they will take up the same width and it will align for you. However, if this is not the case as it so often is, then we can use the margins to force it to have the sizes that we want. So let’s say that our chart currently looks like: If you are using C++ for your chart code then this can be achieved with the aid of a single slot, that can be connected to the QChart::plotAreaChanged() signal. To start off we need to prevent recursion from happening as we will be changing margins and so on but we want to let the internals still do their thing so we don’t want to block signals. So to do that we will just have a static boolean which we will set to true to indicate that we are in the middle of our calculations: void updatePlotArea(const QRectF &area) { static bool fixing = false; if (fixing) return; fixing = true; The next thing we need to do is work out what chart is the best one to be used for aligning the others against, this means picking the one that has the largest left value for the plot area (i.e. the one that has the widest axis). We use the currently changed one as a starting point for this and if any are in fact better we change the margins for those as it will ensure we have the most size available for the plot area at any given point for the largest axis. QChart *bestChart = (QChart *)sender(); QRectF bestRect = area; foreach(QChart *chart, charts) { if (chart->plotArea().left() > bestRect.left()) { bestChart = chart; bestRect = chart->plotArea(); chart->setMargins(QMargins(20, 0, 20, 0)); } } Then, with the exception of the one that ends up being the best one we adjust the margins to ensure that it matches the best one so that they are aligned correctly by setting the margins to be the existing margin plus the difference between the best chart’s plot area and this chart’s plot area. Finally we send any posted events to ensure it is updated right away for us. foreach(QChart *chart, charts) { if (bestChart != chart) { const int left = chart->margins().left() + (bestRect.left() - chart->plotArea().left()); const int right = chart->margins().right() + (chart->plotArea().right() - bestRect.right()); chart->setMargins(QMargins(left, 0, right, 0)); } } QApplication::sendPostedEvents(); fixing = false; This will give us the two charts aligned to look like: As for doing this in QML, we can do something similar in a function which is called via onPlotAreaChanged. property bool fixing: false property var chartViews: [chartview, chartview_b] function updatePlotArea(chart, area) { if (fixing) return fixing = true var tmpChart var bestRect = chart.plotArea var bestChart = chart for (var i = 0; i Math.ceil(bestRect.left) || (Math.ceil(tmpChart.plotArea.left) === Math.ceil(bestRect.left) && Math.floor(tmpChart.plotArea.right) < Math.floor(bestRect.right))) { bestChart = tmpChart; bestRect = tmpChart.plotArea; } } bestRect.left = Math.ceil(bestRect.left) bestRect.right = Math.floor(bestRect.right) for (i = 0; i < chartViews.length; i++) { tmpChart = chartViews[i] if (tmpChart !== bestChart) { var newLeft = 20 + bestRect.left - Math.floor(tmpChart.plotArea.left); var newRight = 20 + Math.ceil(tmpChart.plotArea.right) - bestRect.right; tmpChart.margins.left = newLeft tmpChart.margins.right = newRight } } fixing = false; } The only difference being is that we account for the fact that the plot area is using real values and margins are still integer based so we do some extra accounting for that as a result. In other news from the support desk, to reiterate the value of reporting bugs directly to the support team as a customer. As a customer indicated the issue reported in https://bugreports.qt.io/browse/QTBUG-74523 we were able to quickly find a solution and also get that integrated in time for the next release. It had been reported earlier this year, but was given a lower priority because Qt Quick Controls 1 is deprecated and therefore no one had scheduled time to investigate it. As always, when a bug is reported to us from a customer it will increase the priority and in cases like this we are able to solve it much quicker as a result as the support team can spend time on it too. The post Qt Support – Aligning chart views underneath each other appeared first on Qt Blog. [Less]
Posted almost 5 years ago
One thing that comes up occasionally in support is that when you have multiple chart views that it would be good to align them up underneath each other so that the actual plotted size is the same for both charts. If you have similar sizes for the ... [More] axes then this is easily achieved as they will take up the same width and it will align for you. However, if this is not the case as it so often is, then we can use the margins to force it to have the sizes that we want. So let’s say that our chart currently looks like: If you are using C++ for your chart code then this can be achieved with the aid of a single slot, that can be connected to the QChart::plotAreaChanged() signal. To start off we need to prevent recursion from happening as we will be changing margins and so on but we want to let the internals still do their thing so we don’t want to block signals. So to do that we will just have a static boolean which we will set to true to indicate that we are in the middle of our calculations: void updatePlotArea(const QRectF &area) { static bool fixing = false; if (fixing) return; fixing = true; The next thing we need to do is work out what chart is the best one to be used for aligning the others against, this means picking the one that has the largest left value for the plot area (i.e. the one that has the widest axis). We use the currently changed one as a starting point for this and if any are in fact better we change the margins for those as it will ensure we have the most size available for the plot area at any given point for the largest axis. QChart *bestChart = (QChart *)sender(); QRectF bestRect = area; foreach(QChart *chart, charts) { if (chart->plotArea().left() > bestRect.left()) { bestChart = chart; bestRect = chart->plotArea(); chart->setMargins(QMargins(20, 0, 20, 0)); } } Then, with the exception of the one that ends up being the best one we adjust the margins to ensure that it matches the best one so that they are aligned correctly by setting the margins to be the existing margin plus the difference between the best chart’s plot area and this chart’s plot area. Finally we send any posted events to ensure it is updated right away for us. foreach(QChart *chart, charts) { if (bestChart != chart) { const int left = chart->margins().left() + (bestRect.left() - chart->plotArea().left()); const int right = chart->margins().right() + (chart->plotArea().right() - bestRect.right()); chart->setMargins(QMargins(left, 0, right, 0)); } } QApplication::sendPostedEvents(); fixing = false; This will give us the two charts aligned to look like: As for doing this in QML, we can do something similar in a function which is called via onPlotAreaChanged. property bool fixing: false property var chartViews: [chartview, chartview_b] function updatePlotArea(chart, area) { if (fixing) return fixing = true var tmpChart var bestRect = chart.plotArea var bestChart = chart for (var i = 0; i < chartViews.length; i++) { tmpChart = chartViews[i] if (tmpChart.plotArea.left > Math.ceil(bestRect.left) || (Math.ceil(tmpChart.plotArea.left) === Math.ceil(bestRect.left) && Math.floor(tmpChart.plotArea.right) < Math.floor(bestRect.right))) { bestChart = tmpChart; bestRect = tmpChart.plotArea; } } bestRect.left = Math.ceil(bestRect.left) bestRect.right = Math.floor(bestRect.right) for (i = 0; i < chartViews.length; i++) { tmpChart = chartViews[i] if (tmpChart !== bestChart) { var newLeft = 20 + bestRect.left - Math.floor(tmpChart.plotArea.left); var newRight = 20 + Math.ceil(tmpChart.plotArea.right) - bestRect.right; tmpChart.margins.left = newLeft tmpChart.margins.right = newRight } } fixing = false; } The only difference being is that we account for the fact that the plot area is using real values and margins are still integer based so we do some extra accounting for that as a result. In other news from the support desk, to reiterate the value of reporting bugs directly to the support team as a customer. As a customer indicated the issue reported in https://bugreports.qt.io/browse/QTBUG-74523 we were able to quickly find a solution and also get that integrated in time for the next release. It had been reported earlier this year, but was given a lower priority because Qt Quick Controls 1 is deprecated and therefore no one had scheduled time to investigate it. As always, when a bug is reported to us from a customer it will increase the priority and in cases like this we are able to solve it much quicker as a result as the support team can spend time on it too. The post Qt Support – Aligning chart views underneath each other appeared first on Qt Blog. [Less]
Posted almost 5 years ago
Posted almost 5 years ago
I have built Qt and various apps using emscripten so many times over the last couple of years, it isn't even funny. One detractor with building Qt applications for the web using Qt for WebAssembly, is the time it takes to build the client ... [More] application. Especially during linking, it takes a huge amount of time to produce a binary for Qt WebAssembly. This is caused by all the magic that the linker in emscripten is doing. Luckily, llvm can now produce wasm binaries directly so emscripten does not have to go through any additional steps to output wasm. Emscripten and Qt can utilize this feature to reduce the length of time it takes to link and emit a .wasm binary. YAY! \0/ I did some very non scientific measurements on build time. Using my development machine with make -j8: Qt (not including configure) textedit default configure Qt for WebAssembly user 20m3.706s user 2m46.678s using -device-option WASM_OBJECT_FILES=1 user 23m30.230s user 0m29.435s The result is that building Qt takes a tad longer, but building the client application is significantly faster. Which means development iterations will be much faster. In this instance, I used the textedit example in Qt. The build time went from 2 minutes and 46 seconds for a default non object files build, to 29 seconds! Zip! Zoom! Zang! Dang! that's fast (for wasm) Unfortunately, this is not currently done by default by either Qt, emscripten or llvm. There is a way to do this which involves building llvm from upstream and telling emscripten to use that. For the emscripten and binaryen version, I tested the current 1.38.32 From this bug report https://bugreports.qt.io/browse/QTBUG-72537 Morten has fleshed out a method for doing this. ------------------------------------------------------------ Getting started: (Note: this is "how-I-did-it", not necessarily "how-it-should-be-done".) 1. Clone the following repositories: emscripten : https://github.com/kripken/emscripten.git binaryen: https://github.com/WebAssembly/binaryen.git llvm, https://github.com/llvm/llvm-project.git 2. Check out the version you want to use  emscripten and binaryen have matching emsdk version tags, for example "1.38.23". llvm has its own version numbers, and must be synced up manually. Currently: emscripten <= 1.38.23 : llvm 8.0 (8.0.0-rc2 branch) emscripten > 1.38.23 : llvm 9.0 (master branch) (em++/emcc will complain if you have the incorrect llvm version) 3. Build emscripten is all python, no build needed. binaryen: "cmake -GNinja && ninja" llvm: cmake -GNinja../path/to/llvmcheckout-DLLVM_ENABLE_PROJECTS="clang;libcxx;libcxxabi;lld"-DCMAKE_BUILD_TYPE=Release-DLLVM_TARGETS_TO_BUILD=WebAssembly-DLLVM_EXPERIMENTAL_TARGETS_TO_BUILD=WebAssembly (one of the WebAssembly targets may be redundant) 4. Configure Environment I use the following: export EMSDK="/Users/msorvig/dev/emsdks/emscripten-1.38.23"export PATH="$EMSDK:$PATH"export LLVM="/Users/msorvig/dev/emsdks/llvm-8.0.0-build/bin"export BINARYEN="/Users/msorvig/dev/emsdks/binaryen-1.38.23"export EM_CONFIG="/Users/msorvig/dev/emsdks/.emscripten-vanillallvm-1.38.23"export EM_CACHE="/Users/msorvig/dev/emsdks/.emscripten-vanillallvm-cache-1.38.23" Where .emscripten-vanillallvm-1.38.23 is a copy of the .emscripten config file that emsdk generates. ------------------------------------------------------------ You will need to adjust the various paths to your system, of course.  Then to configure and build Qt, add -device-option WASM_OBJECT_FILES=1 to the normal Qt for WebAssembly configure line. The one I normally use is: configure -xplatform wasm-emscripten -developer-build -nomake tests -nomake examples -opensource -confirm-license -verbose -compile-examples -no-warnings-are-errors -release  -device-option WASM_OBJECT_FILES=1 Works like a charm for the Qt 5.13 and the 5.13.0 branches of the Qt source code repo. I tried this with the 5.13.0 beta4 WebAssembly binaries, but got: wasm-ld: warning: function signature mismatch: so a complete rebuild is required. There is a chapter regarding Qt for WebAssembly in the book, Hands-On Mobile and Embedded Development with Qt 5 [Less]
Posted almost 5 years ago
I am happy to introduce the new QtCoAP library! It is the client-side implementation of the Constrained Application Protocol (CoAP) for the Internet of Things. The library provides quick and easy way of using the CoAP protocol in your cross-platform ... [More] Qt applications. As it has been announced earlier, QtCoAP will be available in the new Qt 5.13 release as a part of our Qt for Automation offering, together with other IoT protocol implementations, such as MQTT, KNX and OPC UA. What is CoAP? CoAP was designed as a lightweight machine-to-machine (M2M) communication protocol that can run on devices with scarce memory and computing resources. It is based on the concept of RESTful APIs and is very similar to HTTP. CoAP has a client-server architecture and uses GET, POST, PUT and DELETE requests for interaction with the data. But unlike HTTP, it uses the lightweight UDP for the transport instead of TCP. Additionally, it supports some interesting features like multicast requests, resource discovery and observation. Thanks to the low overhead and simplicity, CoAP  has become one of the popular IoT protocols to be used on the embedded devices. It acts as a sort of HTTP for the embedded world. Overview of Qt CoAP Implementation QtCoAP supports the following functionality: Resource observation Resource discovery Group communication (multicast) Blockwise transfers Security The library is really simple to use. You just need to create an instance of QCoapClient and connect its signals: QCoapClient client; connect(&client, &QCoapClient::finished, this, &CoapHandler::onFinished); connect(&client, &QCoapClient::error, this, &CoapHandler::onError); Now you are ready to send requests and receive replies: // GET requests client.get(QUrl("coap://127.0.0.1:5683/resource")); // or simply client.get(QUrl("127.0.0.1/resource")); // PUT/POST requests QFile file("data.json"); // ... client.post(QUrl("127.0.0.1/resource"), file.readAll()); client.put(QUrl("127.0.0.1/resource"), file.readAll()); // DELETE requests client.deleteResource(QUrl("127.0.0.1/resource")); Using the QCoapRequest class you can pass options and customize your requests. For example: QCoapRequest request; request.setUrl(QUrl("127.0.0.1")); request.setPayload(file.readAll()); request.addOption(QCoapOption::UriPath, "resource"); client.put(request); CoAP also provides a publish-subscribe mechanism achieved via “observe” requests: QCoapReply *observeReply = client.observe(QUrl("127.0.0.1/temperature")); connect(observeReply, &QCoapReply::notified, this, &CoapHandler::onNotified); Now your application will get notified whenever the “/temperature” resource changes. What makes CoAP even more interesting, is the ability to find and discover CoAP resources. You can discover resources on the given host: QCoapResourceDiscoveryReply *discoverReply = client.discover(QUrl("127.0.0.1")); Or in the entire network: QCoapResourceDiscoveryReply *discoverReply = client.discover(); This will send a multicast discovery request to the IPv4 CoAP multicast group. You can also run the discovery for the IPv6 nodes: discoverReply = client.discover(QtCoap::MulticastGroup::AllCoapNodesIPv6LinkLocal); // or discoverReply = client.discover(QtCoap::MulticastGroup::AllCoapNodesIPv6SiteLocal); ... connect(discoverReply, &QCoapResourceDiscoveryReply::discovered, this, &CoapHandler::onDiscovered); You will get several discovery replies from each CoAP device in your network. For example: Host 1: RES: 2.05 Content ;rt="temperature-c";if="sensor";obs, ;rt="firmware";sz=262144 Host 2: RES: 2.05 Content ;rt="temperature-c";if="sensor";obs, This will indicate, that in your network you have 2 devices running CoAP servers: one of them is connected to temperature and light sensors and the other has only a temperature sensor. Security Last, but not least is security. QtCoAP library supports the following security modes: Authentication via pre-shared keys. Using X.509 certificates. For securing the CoAP connection, you need to pass one of these modes when creating the client and configure it accordingly. For example: QCoapClient secureClient(QtCoap::SecurityMode::PreSharedKey); QCoapSecurityConfiguration config; config.setIdentity("Client_identity"); config.setPreSharedKey("secretPSK"); secureClient.setSecurityConfiguration(config); Please give us your feedback if you find this post interesting! The post Introducing QtCoAP appeared first on Qt Blog. [Less]
Posted almost 5 years ago
I am happy to introduce the new QtCoAP library! It is the client-side implementation of the Constrained Application Protocol (CoAP) for the Internet of Things. The library provides quick and easy way of using the CoAP protocol in your cross-platform ... [More] Qt applications. Big thanks to our partners at Witekio for development and contribution of the main functionality! As it has been announced earlier, QtCoAP will be available in the new Qt 5.13 release as a part of our Qt for Automation offering, together with other IoT protocol implementations, such as MQTT, KNX and OPC UA. What is CoAP? CoAP was designed as a lightweight machine-to-machine (M2M) communication protocol that can run on devices with scarce memory and computing resources. It is based on the concept of RESTful APIs and is very similar to HTTP. CoAP has a client-server architecture and uses GET, POST, PUT and DELETE requests for interaction with the data. But unlike HTTP, it uses the lightweight UDP for the transport instead of TCP. Additionally, it supports some interesting features like multicast requests, resource discovery and observation. Thanks to the low overhead and simplicity, CoAP  has become one of the popular IoT protocols to be used on the embedded devices. It acts as a sort of HTTP for the embedded world. Overview of Qt CoAP Implementation QtCoAP supports the following functionality: Resource observation Resource discovery Group communication (multicast) Blockwise transfers Security The library is really simple to use. You just need to create an instance of QCoapClient and connect its signals: QCoapClient client; connect(&client, &QCoapClient::finished, this, &CoapHandler::onFinished); connect(&client, &QCoapClient::error, this, &CoapHandler::onError); Now you are ready to send requests and receive replies: // GET requests client.get(QUrl("coap://127.0.0.1:5683/resource")); // or simply client.get(QUrl("127.0.0.1/resource")); // PUT/POST requests QFile file("data.json"); // ... client.post(QUrl("127.0.0.1/resource"), file.readAll()); client.put(QUrl("127.0.0.1/resource"), file.readAll()); // DELETE requests client.deleteResource(QUrl("127.0.0.1/resource")); Using the QCoapRequest class you can pass options and customize your requests. For example: QCoapRequest request; request.setUrl(QUrl("127.0.0.1")); request.setPayload(file.readAll()); request.addOption(QCoapOption::UriPath, "resource"); client.put(request); CoAP also provides a publish-subscribe mechanism achieved via “observe” requests: QCoapReply *observeReply = client.observe(QUrl("127.0.0.1/temperature")); connect(observeReply, &QCoapReply::notified, this, &CoapHandler::onNotified); Now your application will get notified whenever the “/temperature” resource changes. What makes CoAP even more interesting, is the ability to find and discover CoAP resources. You can discover resources on the given host: QCoapResourceDiscoveryReply *discoverReply = client.discover(QUrl("127.0.0.1")); Or in the entire network: QCoapResourceDiscoveryReply *discoverReply = client.discover(); This will send a multicast discovery request to the IPv4 CoAP multicast group. You can also run the discovery for the IPv6 nodes: discoverReply = client.discover(QtCoap::MulticastGroup::AllCoapNodesIPv6LinkLocal); // or discoverReply = client.discover(QtCoap::MulticastGroup::AllCoapNodesIPv6SiteLocal); ... connect(discoverReply, &QCoapResourceDiscoveryReply::discovered, this, &CoapHandler::onDiscovered); You will get several discovery replies from each CoAP device in your network. For example: Host 1: RES: 2.05 Content ;rt="temperature-c";if="sensor";obs, ;rt="firmware";sz=262144 Host 2: RES: 2.05 Content ;rt="temperature-c";if="sensor";obs, This will indicate, that in your network you have 2 devices running CoAP servers: one of them is connected to temperature and light sensors and the other has only a temperature sensor. Security Last, but not least is security. QtCoAP library supports the following security modes: Authentication via pre-shared keys. Using X.509 certificates. For securing the CoAP connection, you need to pass one of these modes when creating the client and configure it accordingly. For example: QCoapClient secureClient(QtCoap::SecurityMode::PreSharedKey); QCoapSecurityConfiguration config; config.setIdentity("Client_identity"); config.setPreSharedKey("secretPSK"); secureClient.setSecurityConfiguration(config); Please give us your feedback if you find this post interesting! The post Introducing QtCoAP appeared first on Qt Blog. [Less]
Posted almost 5 years ago
Posted almost 5 years ago
When working on Qt, we need to write code that builds and runs on multiple platforms, with various compiler versions and platform SDKs, all the time. Building code, running tests, reproducing reported bugs, or testing packages is at best cumbersome ... [More] and time consuming without easy access to the various machines locally. Keeping actual hardware around is an option that doesn’t scale particularly well. Maintaining a bunch of virtual machines is often a better option – but we still need to set those machines up, and find an efficient way to build and run our local code on them. Building my local Qt 5 clone on different platforms to see if my latest local changes work (or at least compile) should be as simple as running “make”, perhaps with a few more options needed. Something like qt5 $ minicoin run windows10 macos1014 ubuntu1804 build-qt should bring up three machines, configure them using the same steps that we ask Qt developers to follow when they set up their local machines (or that we use in our CI system Coin – hence the name), and then run the build job for the code in the local directory. This (and a few other things) is possible now with minicoin. We can define virtual machines in code that we can share with each other like any other piece of source code. Setting up a well-defined virtual machine within which we can build our code takes just a few minutes. minicoin is a set of scripts and conventions on top of Vagrant, with the goal to make building and testing cross-platform code easy. It is now available under the MIT license at https://git.qt.io/vohilshe/minicoin. A small detour through engineering of large-scale and distributed systems While working with large-scale (thousands of hosts), distributed (globally) systems, one of my favourite, albeit somewhat gruesome, metaphors was that of “servers as cattle” vs “servers as pets”. Pet-servers are those we groom manually, we keep them alive, and we give them nice names by which to remember and call (ie ssh into) them. However, once you are dealing with hundreds of machines, manually managing their configuration is no longer an option. And once you have thousands of machines, something will break all the time, and you need to be able to provision new machines quickly, and automatically, without having to manually follow a list of complicated instructions. When working with such systems, we use configuration management systems such as CFEngine, Chef, Puppet, or Ansible, to automate the provisioning and configuration of machines. When working in the cloud, the entire machine definition becomes “infrastructure as code”. With these tools, servers become cattle which – so the rather unvegetarian idea – is simply “taken behind the barn and shot” when it doesn’t behave like it should. We can simply bring a new machine, or an entire environment, up by running the code that defines it. We can use the same code to bring production, development, and testing environments up, and we can look at the code to see exactly what the differences between those environments are. The tooling in this space is fairly complex, but even so there is little focus on developers writing native code targeting multiple platforms. For us as developers, the machine we write our code on is most likely a pet. Our primary workstation dying is the stuff for nightmares, and setting up a new machine will probably keep us busy for many days. But this amount of love and care is perhaps not required for those machines that we only need for checking whether our code builds and runs correctly. We don’t need our test machines to be around for a long time, and we want to know exactly how they are set up so that we can compare things. Applying the concepts from cloud computing and systems engineering to this problem lead me (back) to Vagrant, which is a popular tool to manage virtual machines locally and to share development environments. Vagrant basics Vagrant gives us all the mechanisms to define and manage virtual machines. It knows how to talk to a local hypervisor (such as VirtualBox or VMware) to manage the life-cycle of a machine, and how to apply machine-specific configurations. Vagrant is written in Ruby, and the way to define a virtual machine is to write a Vagrantfile, using Ruby code in a pseudo-declarative way: Vagrant.configure("2") do |config| config.vm.box = "generic/ubuntu1804" config.vm.provision "shell", inline: "echo Hello, World!" end Running “vagrant up” in a directory with that Vagrantfile will launch a new machine based on Ubuntu 18.04 (downloading the machine image from the vagrantcloud first), and then run “echo Hello, World!” within that machine. Once the machine is up, you can ssh into it and mess it up; when done, just kill it with “vagrant destroy”, leaving no traces. For provisioning, Vagrant can run scripts on the guest, execute configuration management tools to apply policies and run playbooks, upload files, build and run docker containers, etc. Other configurations, such as network, file sharing, or machine parameters such as RAM, can be defined as well, in a more or less hypervisor-independent format. A single Vagrantfile can define multiple machines, and each machine can be based on a different OS image. However, Vagrant works on a fairly low level and each platform requires different provisioning steps, which makes it cumbersome and repetitive to do essentially the same thing in several different ways. Also, each guest OS has slightly different behaviours (for instance, where uploaded files end up, or where shared folders are located). Some OS’es don’t fully support all the capabilities (hello macOS), and of course running actual tasks is done different on each OS. Finally, Vagrant assumes that the current working directory is where the Vagrantfile lives, which is not practical for developing native code. minicoin status minicoin provides various abstractions that try to hide many of the various platform specific details, works around some of the guest OS limitations, and makes the definition of virtual machines fully declarative (using a YAML file; I’m by no means the first one with that idea, so shout-out to Scott Lowe). It defines a structure for providing standard provisioning steps (which I call “roles”) for configuring machines, and for jobs that can be executed on a machine. I hope the documentation gets you going, and I’d definitely like to hear your feedback. Implementing roles and jobs to support multiple platforms and distributions is sometimes just as complicated as writing cross-platform C++ code, but it’s still a bit less complex than hacking on Qt. We can’t give access to our ready-made machine images for Windows and macOS, but there are some scripts in “basebox” that I collected while setting up the various base boxes, and I’m happy to share my experiences if you want to set up your own (it’s mostly about following the general Vagrant instructions about how to set up base boxes). Of course, this is far from done. Building Qt and Qt applications with the various compilers and toolchains works quite well, and saves me a fair bit of time when touching platform specific code. However, working within the machines is still somewhat clunky, but it should become easier with more jobs defined. On the provisioning side, there is still a fair bit of work to be done before we can run our auto-tests reliably within a minicoin machine. I’ve experimented with different ways of setting up the build environments; from a simple shell script to install things, to “insert CD with installed software”, and using docker images (for example for setting up a box that builds a web-assembly, using Maurice’s excellent work with Using Docker to test WebAssembly). Given the amount of discussions we have on the mailing list about “how to build things” (including documentation, where my journey into this rabbit hole started), perhaps this provides a mechanism for us to share our environments with each other. Ultimately, I’d like coin and minicoin to converge, at least for the definition of the environments – there are already “coin nodes” defined as boxes, but I’m not sure if this is the right approach. In the end, anyone that wants to work with or contribute to Qt should be able to build and run their code in a way that is fairly close to how the CI system does things. The post Building and testing on multiple platforms – introducing minicoin appeared first on Qt Blog. [Less]