Qt Quick with PyQt5 on Raspberry Pi

QtLogoThe prime motivation for me to go through Qt licensing documentation and installing Qt Creator IDE was to explore the new UI infrastructure introduced in Qt 5 under the umbrella of “Qt Quick“. As far as I can tell, this is an entirely different system for creating user interface of a Qt application. Built with modern ideas such as OpenGL graphics acceleration for animation effects and UI layout declared with a text-based markup language QML (probably stands for Qt Markup Language.)

Up to this point my experience with building graphics user interface in Qt was with the QWidget-based infrastructure, which has a long lineage in past editions of Qt. Qt Quick is new for Qt5 and seem to share nothing in common with QWidget other than both a part of Qt5. Now that I’ve had a bit of QWidget UI work under my belt I wanted to see what Qt Quick has to offer. And this starts with a smoke test to make sure I could run Qt Quick in the environments I care about: Python and Raspberry Pi.

Step 1: Qt Creator IDE Default Boilerplate.

Once the Qt Creator IDE was up and running, I followed the Qt Quick tutorial to create a bare bones boilerplate Qt Quick application. Even without any changes to the startup boilerplate, it reported error messages complaining of missing modules. Reading the error message, I looked at the output of apt list qml-module-qtquick* and installed the ones that sound right. (From memory:qml-module-qtquick2qml-module-qtquick-controls2, qml-module-qtquick-templates2, and qml-module-qtquick-layouts)

QML CPP

Once the boilerplate successfully launched, I switched languages…

Step 2: PyQt5

The next goal is to get it up and running on Python via PyQt5. The PyQt5 documentation claimed support for QML but the example on the introductory page doesn’t quite line up with the Qt Creator boilerplate code. Looking at the Qt Creator boilerplate main.cpp for reference, I translated the application launch code into main.py. This required sudo apt install python3-pyqt5.qtquick in addition to the python3-pyqt5 I already had. (If there are additional dependencies I forgot about, look for them in the output of apt list python3-pyqt5*)

QML PyQt

Once that was done, the application launched successfully on my Ubuntu desktop machine, albeit with visual appearance very different from the C++ version. That’s good enough for now, so I pushed these changes up to Github and switched platforms…

Step 3: Raspberry Pi (Ubuntu mate)

I pulled the project git repository to my Raspberry Pi running Ubuntu Mate and tried to run the project. After installing the required packages, I got stuck. My QML’s import QtQuick 2.7 failed with error module "QtQuick" version 2.7 is not installed The obvious implication is that the version of QtQuick in qml-module-qtquick2 was too old, but I couldn’t figure out how to verify version number is indeed the problem or if it’s a configuration issue elsewhere in the system.

Searching on the web, I found somebody on stackoverflow.com stuck in the same place. As of this writing, no solution had been posted. I wish I was good enough to figure out what’s going on and contribute intelligently to the discussion!

I don’t have a full grasp of what goes on in the world of repositories ran by various Debian-based distributions, but I could see URLs flying by on-screen and I remembered that Ubuntu Mate pulled from different repositories than Raspbian. I switched to Raspbian to give that a shot…

Step 4: Raspberry Pi (Raspbian Stretch)

After repeating the process on the latest Raspbian, the Qt Quick QML test application launches. Hooray! Whether it was some configuration issue or out of date binaries we don’t know yet for sure, but it does run.

That’s the good news. Now the bad news: it launches with the error:

JIT is disabled for QML. Property bindings and animations will be very slow. Visit https://wiki.qt.io/V4 to learn about possible solutions for your platform.

And indeed, the transition between “First” and “Second” tabs were slow. Looking on the page that it pointed to, it looks like the V4 JavaScript engine used by Qt for QML applications does not have JIT compilation for Raspberry Pi’s ARM chip. That’s a shame.

For now, this excludes Qt Quick as a candidate for writing modern responsive user interfaces for Raspberry Pi applications. If I want to stick with Qt and Python, I’m better off writing Qt interfaces in the old school QWidget style. We’ll keep an eye on this – maybe they’ll add JIT support for Raspberry Pi in the future.


(The source code related to this blog post are publicly available on Github.)

Qt Licensing Means Reading Big Walls of Text

QtLogoFrom a legal perspective, Qt is an interesting beast. There are two sets of licenses for application developers who wish to use Qt: a commercial license, or an open-source license. I’m sure this “dual-license” approach is intended to let people have the best of both worlds. In practice, it means people who try to do their legal due diligence will have to wade through a lot of legal language. It’s a very challenging barrier – most of the Qt Frequently Asked Questions page is devoted to licensing.

And that’s the condensed version. The legal Terms and Conditions section has multiple pages dedicated to different aspects of using Qt. As a non-lawyer, it’s very hard to not let my eyes gloss over the thick language. Even if we just focus on just the open-source side of the licensing, there is an array of licenses to worry about. There are the strong GPL , the more permissive BSD licenses, and the LGPL that attempts to strike a balance between them. I didn’t even know about LGPL until today. All of them are in play for various parts of Qt.

Fortunately, since my work so far is not for profit, and I intend to put everything up in public on Github, most of the terms of the open-source license should be easy to meet. I would definitely need to consult a lawyer before embarking on a commercial project, though.

With this basic due diligence complete, I went looking for the open-source edition of the Qt tools. They don’t exactly make it easy to find on the official Qt downloads page. It takes some persistence to navigate through the menu structures, looking for the de-emphasized texts and links, to find the download areas for the free open-source edition.

Before I found that compiled binaries repository, I found the information to build Qt5 and Qt Creator from source code. It seemed to be confused by the Qt (both 4 and 5) that appeared to already exist on my Ubuntu installation and I never got the home-built Qt5 up and running. After I stumbled across the binaries repository, I decided to come back to building Qt5 from source after I’ve gained a bit more experience with Linux build systems.

First Use of Python Threads is Quickly Followed By First Crash… in Qt

QtLogoBased on experience, I fully expected my first venture into Python multi-threading to run into problems. I just didn’t know exactly what that problem would look like. I had hoped that my first Python threading bug would be friendlier to understand and debug, just as the Python programming language has been friendlier for a beginner to write.

Sadly, no such luck. After I started using my GPIO class, with the debounce routine powered by threading.Timer, the crash is a very unfriendly Segmentation Fault. Surprisingly, it’s not the full “Segmentation Fault (core dumped)” so there was no core for me to debug with. No matter, it’s my program and I can relaunch it under gdb and try to dissect each instance of the crash.

The symptoms are consistent with a multi-thread timing issue:

  • It is unpredictable. I could use my program for several minutes without a problem, or the program might die within a few seconds of launch.
  • When something does go wrong, the actual point of failure that crashed in gdb is deep in the bowels of the system far from the actual problem.
    • In one case, a Python thread worker without any of my code on the stack.
    • In another case, internal system corruption error “Error in `python3': corrupted size vs. prev_size
  • The state of the rest of the system is also unpredictable. Using gdb’s “info threads” command I could get an overlook of the rest of the program, and it looks different every time.

I thought my timer-related code was simple: it checks the state of the input pin, and if the pin level has changed (after the debounce wait) also change the appearance of the visual indicator. The visual indication change was done by changing the stylesheet on the QWidget on screen. One of the error messages gave me my first clue that was my bug.

Could not parse stylesheet of object 0x21f6f58

Going back to look at the state of the other threads with this in mind, I saw they all had something in common: the main UI thread at the point of the crash is working on some part of visual rendering.

So we have a candidate explanation: I had been updating the QWidget stylesheet in the timer callback, which executes on a thread that is not the main Qt UI thread. So when this occurs when the UI thread is in the middle or rendering the QWidget, it would see the data structure change out from under it by the timer thread. This is not good and could explain the crash.

To test the hypothesis, I modified my program to perform stylesheet updates from the timer callback thread and do it frequently. Tens of times per second versus once every few seconds in my normal execution. The program now crashes consistently within a few seconds of launch which I saw as good confidence builder for my hypothesis.

The next thing we need is a fix. I need to make sure the stylesheet update occurs on the UI thread, and it seems simplest to post a message to the UI thread event queue. I can accomplish this by emitting a signal from the timer thread and have that go to a slot on the UI thread via a Queued Connection. The slot executes on the UI thread and can safely update the stylesheet there.

After this modification, the test program is now only emitting a signal from the timer callback thread to queue a stylesheet update, instead of performing the actual update itself on the timer thread. And now the test program no longer crashes.

I started debugging this believing I ran into a problem deep in the bowels of Python implementation internals. It turns out my clumsy use of threads had confused Qt.

Lesson learned, moving on to the next lesson…

(The project discussed in this blog post is publicly available on GitHub. The threading fix is in this commit.)

 

Learning Timers: Qt QTimer and Python threading.Timer

QtLogoWhen I interfaced my PyQt application to the Raspberry Pi GPIO pins, I ran into a classic problem: the need to perform input debouncing. The classic solution is to have the software wait a bit before deciding whether the input change is noise to be ignored or not. A simple concept, but “wait a bit” can get complicated in the world of GUI programming. When writing simple programs, we can probably get away with a literal wait by “going to sleep” for a little bit. But we don’t have that luxury in the world of GUI programming – going to sleep would freeze everything in the program. In general, users do not appreciate their UI becoming frozen and unresponsive.

Python LogoThe solution: a timer. In a Windows application, the programmer can use the operating system timer and do their “after waiting a bit” tasks in response to the WM_TIMER message. I went looking for the Qt equivalent and found several timer-related features. QTimer, QBasicTimer, and QObject::startTimer(). Thankfully, the documentation also provided an overview describing how they differ. For debounce purposes, the most fitting mechanism is a single-shot timer.

Unfortunately, when I tried to use it, I received an error message telling me I could only use Qt timer objects from code launched with QThread. Which apparently wasn’t the case with code running under the context of a QWidget within a QApplication.

I had hoped the Qt timers, working off of the QApplication event queue, would stay on the UI thread. But it appears they have to have their own QThread. I could put in more time to figure out how to get Qt timers to work, but I decided to turn to Python library instead. If I have to deal with multi-threading issues anyway, there’s no reason to avoid Python’s Timer object in the threading library.

It does mean I had to review my code to make sure it would be correct even if called from multiple threads. Since the important state are the status of the GPIO pins, they are handled by the pigpio library and the code in my app should be fairly safe. I do set a flag to avoid creating multiple Timer objects in the case of input bounce. If I cared about making sure I only ever create a single Timer, this flag should be protected against cross-thread access. But reviewing the code I decided it was OK if a few Timer ends up being created – the final result of reading the GPIO pin should still be the same even in the case of multiple Timers doing duplicate work.

(The project discussed in this blog post is publicly available on Github.)

Notes on “ZetCode’s PyQt5 Tutorial” From a Windows Developer.

QtLogoOnce I decided to learn how to create a GUI application using the PyQt5 Python binding for the Qt framework, I looked online for some resources to get started. The reference guides for PyQt5 and Qt version 5 itself seems to be fairly robust, but I needed a little push to get over the initial learning curve to understand where to look for what I need.

The Python Foundation’s wiki for PyQt tutorials has a fairly long list. But when I looked, only one explicitly states it is for PyQt5. So off I go to Zetcode’s PyQt5 Tutorial. It was a very bare-bones tutorial that might be a bit too bare for a complete beginner trying to learn GUI programming. But for somebody who already knows the general concepts of a windowing graphics interface system, it is a quick primer to learn the vocabulary.Python Logo

Since I’ve worked with various flavors of Windows frameworks (from raw Win32, to MFC, to WPF) I just needed a tutorial to help me connect concepts in my head to the terminology used in Qt. For example: in Qt, when something happens, a notification called a “signal” occurs. A signal can be connected to a “slot” which can then respond to whatever just happened. Once I learned this, I was able to translate the concepts into my head: a “signal” is an event that could be raised, and a “slot” is an event handler. Once I got this and a few other basic “Rosetta Stone” translations in my head I could switch to the reference documentation to find answers.

One important note: Even though it is labelled as a PyQt5 tutorial, Zetcode’s tutorial is actually pretty much the PyQt4 tutorial updated with the changes to syntax needed for PyQt5. It doesn’t actually cover anything that is new in Qt5. For me this is fine, because the “old way” covered in the tutorial is probably what I’ll end up using when I go even further back in time for the ROS flavor of Qt.

But know there’s no coverage of the Qt5 advancements in declarative interface construction, hardware-accelerated rendering, etc. Anybody who wants to learn the new toys of Qt 5 would have to look elsewhere.

Qt + Python = GUI for Raspberry Pi Project

Since the mission of the Raspberry Pi foundation is to “put the power of digital making into the hands of people all over the world” there is no shortage of options for programming the Pi. We have at our disposal many choices in programming languages, each with multiple application frameworks, and a large community of Raspberry Pi users for support.

QtLogoFeeling overwhelmed with options, I chose the one that best lines up with my long-term goal of getting up and running on ROS. The ROS plug-in architecture for operator GUI is rqt, based on Qt. And like much of ROS, the user has the option of working with rqt in either C++ or Python. Since I had started dabbling with ROS in Python before getting distracted, I thought the combination of Qt and Python would be a good direction to go.Python Logo

The Qt framework itself is aimed at C++ developers, and its documentation is written accordingly. Fortunately there are translation layers (language bindings) for Python. The one that seems to be the most mature is PyQt with a long list of resources, books, and online tutorials.

The next decision to make is which version to start learning. Browsing through the resources, it looks like Qt 4 is the mainstream version and Qt 5 is the new shiny. Since ROS is still in the midst of transitioning from Python 2 to Python 3, I assume rqt would be relatively old school as well. No matter which one I choose, there’ll be differences I have to tackle whenever I get around to diving deep into ROS. On the assumption that the latest and greatest versions are also the most polished (an assumption based on how Python 3 cleaned up a lot of architectural messiness of Python 2) I thought I’d start learning with the latest releases and make adjustments later as I need to.

So: Qt 5 and Python 3 it is, with the help of PyQt5 binding. Which is easily installed on a Raspbian Stretch system by installing the packages “pyqt5-dev” and “pyqt5-dev-tools”.