DevDays lessons: how to use a state machine to drive a QML UI

One of the more interesting presentations at DevDays 2011 was given by Marius from Cutehacks. After hearing this presentation I had a bit of an epiphany when it comes to state machines. I had been using them in Qt for quite a while, but I was never happy with how they meshed with the rest of the components. Indeed, there was a simpler and clearer way to do it, and that’s what I’m going to talk about in this article – the Cutehacks state machine design and how I used it in a real app.

The theory

The actual presentation slides are here and they have some sample code if you’re interested, but off the top of my head, here’s how Cutehacks does it:

  1. First of all, they noticed that implementing a state machine in pure QML was not the best idea. To tell you the truth, I was also surprised when first encountering QML states – sure they were very flexible, but the result wasn’t really a formal state machine. In QML you can switch from any state to any other state and even to ones that don’t exist, because the current state is just a name stored in a string property.
  2. One solution to the above problem is implementing the state machine in C++. Cutehacks inherited their state machine from the QStateMachine class and declared all the states as private members. That state machine had a signal corresponding to each state that shared that state’s name. e.g: state mFoo matched signal foo() inside the state machine.
  3. Each state had an isActive property that was exposed together with the state to QML. In order to make isActive work, they inherited from QState and reimplemented the onEntry and onExit events to toggle the property.

So far we have our own state machine with states and transitions neatly defined in C++ in the constructor, we have signals that can be used to transition between states and each state has an isActive property. How is this connected to QML? Each C++ state was matched with a QML state having the “when” property set to “cppState.isActive”.

It turns out that in QML you can call the signals of any C++ object that has been exposed to QML! So in the UI files when pressing a button you could say something like cppStateMachine.mainState() and then the mainState() signal of the cppStateMachine C++ object would be called. If the current state had a transition to mainState, then the transition would happen, mainState.isActive would become true and the corresponding QML state for mainState would be entered.

Finally, the entire point is that in the QML state you could then change some random QML properties on entry or even run some JavaScript code.

The practice

When coming back from DevDays, I attempted to use this state machine design in a C++/QML app that I was working on. Previously I was using only QML states, but it was reasonably easy to refactor the code to use a C++ state machine in the background. The implementation felt more robust, as I could just take a look in the C++ state machine implementation to see how the app behaved, instead of hunting for the elusive assignments to the “state” property. It was also easier to make changes, since the UI changes were well defined in one place now.

Soon after that, I ran into quite a big issue though… In the app I was working on, transitions were important – for instance when going from A -> B something was supposed to happen, but when going from C -> B, or even B -> A something different had to happen. My first thought was to use the Transition QML element. After adding a few transitions for testing, I noticed that the Transition animations and code were not being executed.

Immediately I suspected that “when” was the problem. If you’ll remember, state transitions were made in a specific way: each QML state had “when” set to the C++ state’s isActive property. In a C++ transition from A -> B, the QML transitions were A -> “”-> B instead of A -> B. The empty string is the default QML state. Since A.isActive became false, the state machine transitioned to the default state automatically. Then it transitioned to B when B.isActive became true.

As one can see QML state machines are quite peculiar…

The way I solved this was by integrating the C++ state machine more tightly with the QML state machine:

  1. I removed the isActive property for the C++ states and the “when” property for all QML states.
  2. For each qmlState I set its name to cppState.objectName, which is just the the QState’s QObject name.
    State {
               name: cppStateMachine.initState.objectName [...]
  3. In MyState::onEntered I emitted a signal called enteredWithObjectName(QString) and passed the objectName() to it.
    void MyState::onEntry(QEvent *event)
    {
        [...]
        emit enteredWithObjectName(objectName());
  4. When setting up the state machine, I connected the enteredWithObjectName for each state to the C++ state machine’s stateChanged(QString) signal.
    connect(mInit, SIGNAL(enteredWithObjectName(QString)), SIGNAL(stateChanged(QString)));
  5. In QML I wrote a signal handler called onStateChanged and in there I set the QML “state” property to the name parameter passed to the signal.
    Connections {
            target: cppStateMachine
            onStateChanged: {
                console.debug("changing state to " + objectName);
                rootWindow.state = objectName;
            }
        }

The end result was that now transitions were done directly from on state to another without going through the default “” state. When a QML object called cppStateMachine.mainState(), a C++ state transition happened, the C++ state mainState was entered and it emitted its QObject.objectName. The state machine then passed that on to QML and the QML set the state property to that name, effectively triggering a QML state transition. Since each QML state had the same name as the C++ QState object, it worked.

This implementation allowed for a lot of flexibility with state changes. If some transitions were similar, they could be grouped in a QML Transition with from set to ‘*’(star/any state). If there was a special case, from could be set to specialState.objectName, and the QML transition would only execute for that state, while ‘*’ would work for the rest.

The advantage with C++ is that the state machine API is already implemented, but my assumption is that a similar state machine API can be written in JavaScript, so that you could have a pure QML/JS project if that’s what you want. The important ideas when building such an API are to respect the definition of a FSM in your design and to encapsulate operations such as state and transition set up and transition triggers.

Learning from your users

A couple of months ago, I conducted usability tests and did customer training for a software project.

I didn’t reach the ideal number of users for a usability study (only four in this case), but I got plenty of interesting information from just watching and asking questions. Looking at people while they use your software is very enlightening for a software engineer.

What surprised me the most was that even though I took special care in designing a user friendly interface and I had a pretty good guess about the users’ computer know-how, I still overestimated their ability to work with the software and missed opportunities to make things simpler. A lot of software engineers are just like me I believe, they don’t have a good grasp about what happens when someone sits down and starts working with their software.

The number one recommendation that I can make is: do usability testing. Odds are that you’ll have an epiphany and find many ways to improve your project. In the mean time, maybe you’ll find the following thoughts useful:

  • In order to do usability testing you need at least one user, preferably three or more. The trick is that it has to be a real user, someone that will use/is already using/is likely to use your software. You can’t just go to Bob the tester and ask him to allow you to watch him. Sure, watching Bob will help, but you won’t get any mind-blowing insights from him; he probably has a much too similar way of thinking to you.
  • Explain to your test partners that you’re not testing them, you are testing the software and they can’t do anything wrong or somehow break the computer. This should make them feel more comfortable and allow them to be as natural as possible.
  • Tell them why they’re there, what your software does and what you hope to achieve with your test. Ask them what they think about the software at first sight.
  • Try to be general with your instructions: for instance I wouldn’t say “Let’s open the invoice window and add three items by clicking the ‘Add item button’.”, I would say “Let’s try to create an invoice” or “Let’s do an invoice for three widgets. How do you think we could do that?”.
  • Watch carefully. Having the users think out loud helps, but I managed fine just by telling them to stop whenever they’d like to ask me questions or point out something that grabbed their attention.   Steve Krug recommends that the session should be recorded. Pen and paper are acceptable in my experience, even if you have to stop for a few moments while you’re writing.

Having done more than ten usability sessions so far, I’ve made some general notes about what works and what doesn’t when it comes to UI. I’ll publish  those in a following post.

The Firefox password manager is leaking information

It’s very annoying to have the master password dialog pop up every time you visit a site that has its password stored by Firefox. Let’s say that I want to browse reddit: when I open a reddit page, the master password dialog pops up. I don’t want to login, so I cancel the dialog.

Then I see an interesting story and open it in a new tab. Here comes the master password dialog and it gets killed again. Now I use the new tab to surf to /r/cpp. The master password dialog cheerfully strolls into view.

Popping a dialog when the user selects a user name filed would be better, as it avoids leaking membership information when visiting a web site. I would also like to see this dialog redesigned – right now it’s a simple application-modal message box with an editor widget in the middle. If you have ten tabs open and you open another tab to a site that has its password stored, you won’t be able to interact with any of the tabs until you either enter the master password or dismiss the master password dialog.

The “Large-Scale C++ Software Design” rules in practice

One of the most interesting books that I have read on the C++ programming language is John Lakos’ Large-Scale C++ Software Design. It was published in 1996 and unfortunately it is the only C++ book that talks about physical design in C++ and scaling physical design to large systems.

How have the guidelines stood the test of time after more than 10 years? Here are my brief comments on some of the more important major guidelines after experimenting with them on real-world projects:

A few definitions

  1. Physical design is concerned with the physical entities of a software system (files, directories, libraries).
  2. A declaration introduces a name into a program; a definition provides a unique description of an entity (e.g. type, instance, function) within a program.
  3. A name has internal linkage if it is local to its translation unit and cannot collide with an identical name defined in another translation unit at link-time.
  4. A name has external linkage if, in a multi-file program, that name can interact with other translation units at link time.
  5. A component is the smallest unit of physical design. This is typically a header + source file pair.
  6. A package is a group of components.
  7. A subsystem is a group of packages.

The guidelines

  • Keep class data private

This is one of the basic guidelines of both object-oriented design and physical design. It’s a good idea because it hides some of a component’s complexity.

Merely declaring member variables as private won’t have any physical design effect , but going a step further and using a compiler firewall (PIMPL/Cheshire cat) is known to reduce compile time dependencies and decrease compile times.

Verdict: thumbs up

  • Avoid data with external linkage at file scope

Very easy to do (just add “static”) and helps avoid linker errors and linker bugs.  For instance: I have encountered an issue with two external linkage functions sharing the same name and having parameters that were convertible to each other. The wrong function was being called at runtime with no warnings at compile time whatsoever.

The only caveat here is that most C++ compilers don’t support making symbols internal by including them in an anonymous namespace even though this is the standard recommended method and the static method is officially deprecated.

Verdict: thumbs up

  • Avoid free functions (except operator functions) at file scope in .h files; avoid free functions with external linkage (including operator functions) in .cpp files.

The basic idea is that of avoiding name clashes and strange interactions between translation units. I always do this one.

Verdict: thumbs up

  • Avoid enumerations, typedefs, and constants at file scope in .h files.

Same idea as before. Enumerations are especially tricky, because the enumeration name is not a namespace and each enumeration value is published in the global namespace.

Verdict: thumbs up

  • Avoid using preprocessor macros in header files except as include guards.

Macros in header files can lead to very hard to track bugs. The classic example is the badly named macro that unintentionally changes code that includes it, but it can be more subtle than that: a former colleague of mine once had a memory corruption issue where the program crashed in the debugger when deleting a certain object. To make things more interesting, it only crashed on certain deletes and not others. It seemed that all deletes from package A crashed, and all deletes from package B worked!

To make a not so long story even shorter, he had #ifdef-ed some of the member variables of the object. Package A crashed on delete because it got size X objects from package B and tried to delete them with size X – sizeof(#ifdefed member variables).

Verdict: thumbs up

  • Only classes, structures, unions, and free operator functions should be declared at files scope in a .h file; only classes, structures, unions, and inline (member or free operator) functions should be defined at file scope in a .h file.

This one is a consequence of the former rules. The idea is that classes, structures and unions create a kind of namespace when they are declared and this helps minimize name clashes. Operator functions mustn’t necessarily be declared and defined at file scope, but some operators can’t be made member functions so there is no choice.

Verdict: thumbs up

  • Place a unique and predictable (internal) include guard around the contents of each header file.

This is actually obligatory for most projects (even small ones) because you’ll get compilation errors if a header file gets included multiple times. It’s worth stressing that the include guard should have a single predictable name; I’ve worked on projects where multiple naming conventions were used and it was confusing.

I like to use something based on the file name, such as INC_FILENAME_H and I’ve created a little macro in my IDE/code editor that can generate an include guard for the selected text.

Verdict: thumbs up

  • Logical entities declared within a component should not be defined outside that component.

I have never encountered a situation where this rule was broken, but it must have been at some point, because otherwise why would it even be mentioned? C++ is probably one of the few programming languages where you can actually get away with this. There is no reason whatsoever that you would want to do it though…

Verdict: should be pretty obvious

  • The .c file of every component should include its own .h file as the first substantive line of code.

The purpose of this rule is avoiding successful compilation of header files that are not complete (i.e missing includes). If such a file is included first, it will certainly fail to compile, if it is included after other header files those files might include the files that our header is missing and the compilation would succeed.

This guideline is subverted by precompiled headers which must usually be the first file included (e.g: MSVC stdafx.h). My approach is to include the precompiled header first, include the component header next, the project headers, the external library headers (e.g: boost, wxWidgets, etc) and finally the STL/CRT headers. I also include the files that are part of the precompiled header explicitly, since compilers are smart enough to skip them and this way I can easily compile without precompiled headers should I need to.

Verdict: thumbs up

  • Avoid definitions with external linkage in the .c file of a component that are not declared explicitly in the corresponding .h file.
  • Avoid accessing a definition with external linkage in another component via a local declaration;
    instead, include the .h file for that component.

These guidelines are related. By importing names properly, you have only one point of change and when those names change in a breaking way you will get a compile error. Covert imports can and do break silently and cause hard to track bugs.

Verdict: thumbs up

  • Prepend every global identifier with its package prefix.
  • Prepend every source file name with its package prefix.

A bit of an overkill, no? You’ll have to remember that the book IS about large-scale software though… When your project contains thousands of files, you probably can’t keep all the file names in your head and anything that helps to tell you “what goes where” is welcomed.

I’ve found this practice to be especially helpful when:

  • doing code reviews on printed paper (or a really bad IDE/editor)
  • filtering packages when trying to navigate to a file
  • filtering packages when trying to navigate to an identifier

Verdict: thumbs up

  • Avoid cyclic dependencies among packages.

Cyclic dependencies are bad mmkay? Dependency management is very important in large-scale projects, because if you’re not careful you’ll end up with a monolith.

The problems that you’re likely to encounter with cyclic dependencies in packages are:

  • the inability to test packages separately, which in turn hinders automated unit testing
  • bigger compile times
  • changes that propagate through the entire program source code

Verdict: a big thumbs up

  • Provide a mechanism for freeing any dynamic memory allocated to static constructs within a component.

One reason why you’d want to free such memory is that it allows you to track memory leaks easier. Most memory verification tools work by taking snapshots of the program’s memory at different points in time. Non freed memory allocated by static constructs will show up as leaks.

Furthermore, if the memory is used by an object, the destructor won’t be called unless the memory is freed explicitly by the programmer, even if the OS can free the memory on program exit.

The easiest way to do this is probably by using a smart pointer like auto_ptr or shared_ptr.

Verdict: thumbs up

Conclusions: As you can see, most of the guidelines are still useful after all this time. Lakos’ book has had a big impact on the way that I approach large-scale C++ programming and I’ve successfuly used the guidelines in several projects that I’ve worked on. In a later post I’ll write about the minor guidelines and some of the general guidelines. While the major guidelines should most of the times be respected, the minor and general ones are quite open to discussion as you’ll see.