How to write clean QML code? Improve your QML project quality

Qt Qml development
2022-03-16
15 minutes
How to write clean QML code? Improve your QML project quality

There is no argue that QML is an amazing technology for developing an outstanding user interface matching today’s trends. The QML language has beautiful and easy-to-learn syntax, but the code in the QML file doesn’t structure itself that. By its nature, it can easily get messy. Nested object declarations are one of the reasons for that.

Therefore, you need to learn how to write clean QML code in order to keep your Qt software project easily maintainable. Ergo, to make savings on money and time.

First, if you do not know Qt framework and what features it contains, check out this blog post.

 

What makes QML code clean?

Let’s start by answering the question what makes QML code clean? Can the code actually be dirty? Well, yes. There are a lot of things you can do wrong in a QML document without realizing it.

 

QML code formatting

The code style is the way code is formatted. It is how your code looks. I find this definition pretty plain and simple. It’s true that it doesn’t affect the application’s execution in any way, but it significantly affects the readability of your code. So, it affects the application’s maintainability and therefore time future developers would need to make changes.

Code formatting is about indentation for properties, functions, and nested object declarations. It is about the usage of white spaces and new lines, code grouping, and QML object tree organization. Many of those things are personal preferences and there is no point in arguing about small things like where the opening bracket should be.

The goal is to have consistent code formatting across the project. I have seen software developers who couldn’t be persistent in their own code, so it’s even more difficult when more people work on the same codebase. That’s why you should try automating code formatting.

 

Tip #1 for better QML code formatting

QML code has a tendency to have many nesting levels and it quickly spreads too much to the right. Therefore, my first tip would be to decrease the indent size to 2 spaces to limit this effect.

In order to do that, enter Qt CreatorΒ OptionsΒ and navigate toΒ Qt QuickΒ settings. You should find a tab related to the code style. You cannot edit the default code style, so click on theΒ CopyΒ button in order to create your own style.

qml clean code

After giving your style a name click on theΒ EditΒ button in order to modify the details of the newly created style. I wish there were more options to adjust, but at least indentation settings can be changed here. You should see that bothΒ Tab sizeΒ and Indent size equal 4. Decrease them to 2 and you are done. The change should be reflected in the code editor and by tools likeΒ Reformat fileΒ tool.

qml clean code

Tip #2 for better QML code formatting

Another tip would be to useΒ QML/JSΒ Reformat fileΒ tool. How does it work? It takes your code and reformats it just to look nicer. Let’s have a look at the component below. It is jut random code to showcase the tool.

 

import QtQuick

Item
{
    id: root
    
    width:30;height:50;
    
    Text {
        id: textItem
        font.pixelSize: 48; wrapMode: Text.WordWrap
        lineHeight: 0.75
        Behavior on color { ColorAnimation { duration: 120; easing.type: Easing.OutElastic} }
        states: [State {
              name: "pressed"
                when: mouse.pressed
                PropertyChanges {
                  target:  textItem
                    color :Qt.lighter(root.color)
                }}
        ]
    }

    MouseArea {
        id: mouse
        anchors.fill: parent
        onClicked: {
            // ...
        }
    }

    function foo()
    {
        // ...
    }}

The above example is definitely not the most beautiful QML code that you have seen. There are several issues including indentations that are different from each other, wrong braces positions, and spaces that are sometimes missing and sometimes redundant. To fix it quickly and easily, navigate to the ToolsΒ option from Qt Creator’s top bar menu and findΒ Reformat fileΒ underΒ QML/JSΒ group. Running the tool can save a lot of your time.

 

import QtQuick

Item {
  id: root

  width: 30
  height: 50

  Text {
    id: textItem
    font.pixelSize: 48
    wrapMode: Text.WordWrap
    lineHeight: 0.75
    Behavior on color {
      ColorAnimation {
        duration: 120
        easing.type: Easing.OutElastic
      }
    }
    states: [
      State {
        name: "pressed"
        when: mouse.pressed
        PropertyChanges {
          target: textItem
          color: Qt.lighter(root.color)
        }
      }
    ]
  }

  MouseArea {
    id: mouse
    anchors.fill: parent
    onClicked: {
      // ...
    }
  }

 function foo() {// ...
  }
}

If you’re committed enough to give up Qt Creator some control over your QML code formatting, then try setting upΒ Reformat fileΒ tool to be run automatically on file save. However, this processing QML code is not always perfect.

qml clean code

There is also a tool called qmlformat that you can find next to the Qt framework’s installation. It is a command line tool that does basically the same. Just in case you want to have something to be used from the command line.

 

QML coding conventions

Coding conventions go beyond the remit of formatting. The QML code can be formatted nicely and look okay from the code style perspective, but it may need polishing anyway. In Scythe Studio we are pretty restrictive about our coding conventions that slightly differ from officialΒ QML coding conventions, but they also add a lot of other concepts like to declare child objects in a particular order. However, these official conventions are good for you to start with, so following them is suggested.

Below you will find a few of the conventions that we follow in Scythe Studio company in order to provide high-qualityΒ Qt QML software developmentΒ services and have clean QML code. We enforce following conventions even on job candidates while working on their technical assignments during screening.

 

1. Name the top item in QML file “root”

In order to improve readability and traceability, setting an id of a top-level item in a file toΒ rootΒ is suggested. Then, in every single QML file, you know that the reference toΒ the rootΒ is about the top-level item.

import QtQuick

Item {
  id: root
}

This is exactly what was done in the above example. You can actually name it differently than root, but that’s how we do it at Scythe Studio, and many others do it the same way. The benefit of that is that even besides having many nested object declarations, you always know that the child object using root, refers to the top item in QML document.

 

2. Use unified strings translations format in every QML file

If the string is visible to the user always wrap it inΒ qsTr()Β function. Also instead of using end translation as strings displayed by default useΒ THIS_NOTATION. For example instead of writingΒ text:Β qsTr(β€žThis is displayed”) useΒ text: qsTr(β€žTHIS_DISPLAYED”).Β Thanks to this approach if any string is missing a translation we can instantly see that in UI.

 

3. Have a private object in your QML document

Encapsulation and proper scoping should matter when you code in QML. Just like in C++, you do not want all members of your class to be public, so the same should apply to the QML code. Not all properties and functions should be visible from the outside.

There is often a need for helping properties and functions, but they have no value or can even cause some unexpected harm when used from outside. Therefore wrapping them into an internal QtObject is suggested. ID property of such QML object I often set to just underscore.

 

QtObject {
  id: _ // or id: internal
    
  readonly property real timeout: 1000
  property bool isExpanded: false

function addDestination() {
    // ...
  }
}

 

4. Order of various attributes

Official QML conventions cover the order of attributes and proposed look for QML object tree, but we just define it a bit differently and in more detail. It simply pays off for us in big projects. So following is our proposal on how to declare child objects and attributes in a QML file.

 

import QtQuick
import QtQuick.Controls // Qt modules first
import com.scythestudio.ownmodule 1.0 // then own modules
import "./controls/" // then local directories

// A hypothetical custom button item.
Item {
  id: root

  /// The text to be placed on the button
  property string title: "title"                           // properties declaration
  /// The button's background color
  property alias color: button.color

  signal buttonClicked()                                   // signal declaration

  Button {                                                // child items
    id: button

    width: parent.width                                   // properties derived from Item
    height: parent.height

    icon.color: "black"                                   // Button's own properties

    onClicked: {
      root.buttonClicked()
 }
  }

  states: State {                                         // states
    name: "selected"
    PropertyChanges { target: button; icon.color: "red" }
  }

  transitions: Transition {                               // transitions
    from: ""
    to: "selected"
    ColorAnimation { target: button; duration: 200 }
  }

  PropertyAnimation {                                     // special items
    id: closeDelayAnimation
    // ...
  }

  Timer {                                                 // special items

  }

  Connections {                                           // special items
    target: someCppController

    function onProcessFailed() {
      // ...
    }
  }

  QtObject {                                               // private item
    id: _

    readonly property real timeout: 1000
    property bool isExpanded: false

    function addDestination() {
        // ...
    }
  }

  onWidthChanged: {                                       // slots

  }

  function close() {                                      // public functions
    isExpanded = false
    closeDelayAnimation.start()
  }
}

From the code snippet above you can deduce a lot of rules about QML object tree and attributes order. It should give you some hints on how to write QML code and define child objects. Let’s highlight some of the rules.

  • The order of each import statement is not random. Built-in Qt modules import statement should be above own modules;

  • Functions definitions are preferred to be on the bottom of the file in order to see the component’s children’s QML objects first and quickly understand how the item is built visually. After all, QML code defines firstly look and then the flow of the application;

  • States and transitions tend to grow fast, so that’s why they should be placed below child objects though they are also properties;

  • The component’s public API (properties and functions) should be documented;

 

Good QML practices

In the documentation of the Qt framework, you can find a list ofΒ best practices for QML and Qt Quick. I find all of them quite useful and some of them principal to have a clean QML code. Probably the most crucial practice described on that page is a rule toΒ separate UI from logic.Β When you code using QML, you may be tempted to use QML and JavaScript for many things including application logic and network communication.

QML code good practices

Do not put too much logic in the QML file

Try to move such things to C++ as then you have a clear separation of the frontend written in QML and let’s say the backend written in C++. Therefore, your application’s architecture offers more scalability. One of the things that should almost always be written in C++ is data models. You can go with QML models only if your list is static. Otherwise, you will finally either lose your time trying to perform advanced operations using QML models or rewriting them to C++ models.

In this blog post about Improving Performance and Optimizing QML apps, you will find a lot of other reasons to use C++ to implement logic. There are many benchmarks there to prove the point. And let’s put it this way. Clean QML code defines UI, not the application’s critical features.

In Scythe Studio we conduct online and offline workshops dedicated to various aspects of Qt software development. One of the topics of the workshops isΒ β€žAdvanced Qt QML”. See our training offer to learn more.

 

Groom your QML files with qmllint

Another good practice is to useΒ qmllint toolΒ which would help you easier spot the issues in your Qml code. It’s a pretty useful tool warning about many issues including unused imports and unqualified access to properties. Since Qt 6 it is even nicely integrated with Qt Creator IDE .

You can even make qmllint return a JSON output. Then after processing QML code you have all the issues and errors in a format that you can then integrate with GitHub, GitLab, or other CI/CD tools.

 

Structure your QML objects using Qt modules and CMake methods

During Qt World Summit 2022, I had a talk named CMake and Qt. qt_add_qml_module() in practice. I recommend you watch this video on YouTube. The purpose of this talk was to explain what are modules in QML code and how you can use them to structure your QML objects in a reasonable way.

To keep it simple. Modules are the way to organize your QML files and JavaScript code in packages similar to C++ namespaces. This way you can put items like UI controls or basic helping items (Constants, Theme, etc) that are used across source code in separated modules named in a meaningful way.

 

Reasons to use QML modules

I will not go into technical details, because it was explained during that presentation, but the benefits of using the modules are:

  • Possibility to separate features and items;

  • Having a modularized project structure that helps using particular modules across many subprojects;

  • Sharing sets of QML source code in form of libraries

  • Keeping the codebase clean and maintainable;

  • Possibility to introduce versioning;

 

Why is it important to write clean QML code?

At this point, you should know what clean code means and what you can do to ensure that the code you write follows these rules. So now let’s answer the question why is it so important to write clean QML code? It is one of the factors thatΒ make your code high quality.

Clean QML code benefits

 

Reducing the effort during a review

When you have a set of conventions and rules to follow, it’s easier to spot errors and therefore you immediately start to make time savings. Moreover, as clean code concepts include good practices, the developers who follow these directives write code according to the art.Β It doesn’t require then that much effort during the reviewing process. Also, your senior developers do not need to then point out a lot of tiny visual syntax issues.

 

Focusing on more important aspects

During coding, there are plenty of tiny decisions to make. Looking at the very same code after some time, you rather have no idea why you wrote the code in a particular way. On the other hand, when you follow clean QML code rules, you outsource those little decisions to the code. It frees up your thinking resources.

 

Improved maintainability of the project

Finally, of course, the most important reason to write clean QML code is the fact that itΒ significantly improves the maintainability of the project.Β Someone said that you write the code once, but it’s read multiple times. Therefore, you need to care about the readability of your code as well as about all the good practices. Then it’s easier for you and other developers to jump into the project, understand a particular piece and make changes.

Now you may ask how strict about following clean QML code rules your team should be. Well, it all depends on the length of the project that you’re currently working on. It is also important how many people participate or will participate in the future. In my point of view, it’s hard to overdo it when it comes to ensuring high-quality code.

Even if you prepare a small demo of which code will be public, it’s good to follow all these rules to be finally content with the results of your work. After a while, it gets into the blood, and developers stop even thinking about it as an additional effort.

If you maintain a long-life project such as the Qt framework, you have many reasons to be extremely restrictive about the code quality. I described my experience of contributing toΒ Qt Charts in Qt 6.2Β module in a blog post. There are a lot of conventions and rules to follow and it’s actually quite difficult at the beginning to get used to them.

Thankfully, there are CI/CD mechanisms that save reviewers time by, for example, looking for typos. In the beginning, it was a struggle to make my code match all those requirements. Nevertheless, I know that it’s a must-have in this project and I can say that it was even enjoyable. Basic CI/CD setup for Qt QML project was explained in the blog post Cross-platform Qt CI/CD setup – make it easy.

 

Key takeaways and general rules

Let’s summarize the blog post to again remind the most important rules for writing clean QML code. Many of those rules should be applied to your project, and to your source code regardless of the used technology.

  • Maintaining a clean QML codebase is an important aspect of a healthy Qt project;

  • KISS – keep it short and simple. Not for you, but for the reader. Always choose the simpler implementation option over the more complicated one;

  • Document public interfaces (API) of created items;

  • Do not add comments describing how something is achieved. If the reader can’t deduce that from your code, then the code needs refactoring. Comment on not obvious solutions that may look surprising;

  • Do not reinvent the wheel;

  • Adapt the code to the existing codebase. If you have improvement ideas, first discuss them with other developers;

  • Follow the coding conventions and good QML practices;

Scythe-Studio - Chief Executive Officer

Łukasz KosiΕ„ski Chief Executive Officer

NeedΒ QtΒ QML developmentΒ services?

service partner

Let's face it? It is a challenge to get top Qt QML developers on board. Help yourself and start the collaboration with Scythe Studio - real experts in Qt C++ framework.

Discover our capabilities

Latest posts

[ 134 ]