30 Mar 2018

Using FontAwesome 5 in QML

FontAwesome is a set of icon fonts making it easy to beef up your user interfaces. It is mostly used in web applications (aka HTML/CSS), however, leveraging it to your Qt applications is not difficult either.

First, download the latest Desktop release from their website. You unzip it and find the contained *.otf files in otfs folder. You should have the following files there:

  • Font Awesome 5 Brands-Regular-400.otf
  • Font Awesome 5 Free-Regular-400.otf
  • Font Awesome 5 Free-Solid-900.otf

Copy these into your application’s source directory.

Note: Make sure you use a recent version of FontAwesome 5. Earlier releases had an issue which prevented you to use both the solid and regular variants. This issue is meanwhile gone for the OTF files. Also, you still will encounter the issue when using the TTF files, but as Qt/QML can deal with both, just make sure you pick the OTF version from the Desktop release.

In QML, we can now easily access these fonts by writing a (singleton) component which makes them available:

// Fonts.qml

pragma Singleton

import QtQuick 2.0

Item {
    id: fonts

    readonly property FontLoader fontAwesomeRegular: FontLoader {
        source: "./Font Awesome 5 Free-Regular-400.otf"
    }
    readonly property FontLoader fontAwesomeSolid: FontLoader {
        source: "./Font Awesome 5 Free-Solid-900.otf"
    }
    readonly property FontLoader fontAwesomeBrands: FontLoader {
        source: "./Font Awesome 5 Brands-Regular-400.otf"
    }

    readonly property string icons: fonts.fontAwesomeRegular.name
    readonly property string brands: fonts.fontAwesomeBrands.name
}

For the singleton to work, you must create a qmldir file next to it as well:

// qmldir
singleton Fonts 1.0 Fonts.qml

With this, you should be able to display some icons using e.g. a ToolButton:

// FontAwesomeToolButton.qml

import QtQuick 2.9
import QtQuick.Controls 2.2

// This must point to the directory where you placed
// the 'Fonts.qml' file. If all your sources are in the
// same directory, this can be '.' as well.
import "../path/to/Fonts"

ToolButton {
    id: button

    property alias symbol: button.text

    // This can be set to either Fonts.icons or Fonts.brands
    // to switch between the two icon sets.
    font.family: Fonts.solidIcons

    // This controls the style of the regular icons which is
    // used. The default value (Regular) will map to the regular
    // icon set. Use either Font.Bold or Font.Light to switch
    // to the other styles.
    font.weight: Font.Regular
}

And finally we can use our tool button implementation:

// demo.qml

import QtQuick 2.9

// Adjust the paths accordingly. The latter import is where
// our tool button implementation is stored:
import "./path/to/Fonts"
import "./path/to/Components"

Grid {
    columns: 3

    FontAwesomeToolButton {
        symbol: "\uf2bb"
    }
    FontAwesomeToolButton {
        symbol: "\uf556"
    }
    FontAwesomeToolButton {
        symbol: "\uf35b"
    }
    FontAwesomeToolButton {
        symbol: "\uf2bb"
        font.weight: Font.Bold
    }
    FontAwesomeToolButton {
        symbol: "\uf556"
        font.weight: Font.Bold
    }
    FontAwesomeToolButton {
        symbol: "\uf35b"
        font.weight: Font.Bold
    }
    FontAwesomeToolButton {
        symbol: "\uf270"
    }
    FontAwesomeToolButton {
        symbol: "\uf294"
    }
    FontAwesomeToolButton {
        symbol: "\uf268"
    }
}

Run this e.g. via qmlscene:

qmlscene ./demo.qml

You should get something like this: FontAwesome Tool Buttons

While this approach works, you will have notices one bad thing: In order to select which icon to display, we inserted the unicode character codes as label names. You can get them easily from the FontAwesome Gallery, however, after inserting into the QML source code you do not quickly see which icon a code refers to. In HTML, you would use something like

<i class="fas fa-anchor"></i>
<i class="fas fa-angle-double-right"></i>
<i class="fas fa-asterisk"></i>

which is far more readable. To achieve something similar in QML, we can extract some meta information from the FontAwesome download package and generate another QML component from that one.

First, create a file fa_iconsjson2qml.py and put the following content in it:

#!/bin/env python3

"""
Convert FontAwesome's `icons.json` to QML.

This script creates a QML component which defines
constants for all FontAwesome icons listed in the file
`icons.json` in the FontAwesome package.
"""

import sys


def _dash2cap(name):
    try:
        while True:
            idx = name.index("-")
            pre = name[0:idx]
            suf = name[idx + 2:]
            cap = name[idx + 1:idx + 2].capitalize()
            name = pre + cap + suf
    except ValueError:
        pass
    return name


def _main(argv):
    import json
    if len(argv) != 3:
        print("Usage:")
        msg = "    {} path/to/icons.json path/to/Icons.qml"
        msg = msg.format(argv[0])
        print(msg)
        sys.exit(1)
    with open(argv[1], "r") as file:
        icons = json.load(file)
    lines = []
    lines.append("pragma Singleton")
    lines.append("import QtQuick 2.0")
    lines.append("")
    lines.append("QtObject {")
    prop = '    readonly property string {}: "{}"'
    for key in icons:
        name = "fa-" + key
        name = _dash2cap(name)
        code = "\\u" + icons[key]["unicode"]
        line = prop.format(name, code)
        lines.append(line)
    lines.append("}")
    with open(argv[2], "w") as file:
        file.write("\n".join(lines))


if __name__ == '__main__':
    _main(sys.argv)

Make the file executable and call it on the file icons.json which is contained in the FontAwesome zip:

chmod +x ./fa_iconsjson2qml.py
./fa_iconsjson2qml.py \
    ./path/to/FontAwesome/metadata/icons.json \
    ./path/to/qml/sources/Fonts/Icons.qml

This script basically extracts the available icons from the JSON file and creates another QML singleton component which looks like this:

pragma Singleton
import QtQuick 2.0

QtObject {
    readonly property string fa500px: "\uf26e"
    readonly property string faAccessibleIcon: "\uf368"
    readonly property string faAccusoft: "\uf369"
    readonly property string faAddressBook: "\uf2b9"
    readonly property string faAddressCard: "\uf2bb"
    // several hundred similar lines following...
}

Ideally, put the file next to the Fonts.qml file and edit the qmldir file to look like this:

singleton Icons 1.0 Icons.qml
singleton Fonts 1.0 Fonts.qml

Now, you can modify the demo from above to:

// demo.qml

import QtQuick 2.9

// Adjust the paths accordingly. The latter import is where
// our tool button implementation is stored:
import "./path/to/Fonts"
import "./path/to/Components"

Grid {
    columns: 3

    FontAwesomeToolButton {
        symbol: Icons.faAddressCard
    }
    FontAwesomeToolButton {
        symbol: Icons.faAngry
    }
    FontAwesomeToolButton {
        symbol: Icons.faArrowAltCircleUp
    }
    FontAwesomeToolButton {
        symbol: Icons.faAddressCard
        font.weight: Font.Bold
    }
    FontAwesomeToolButton {
        symbol: Icons.faAngry
        font.weight: Font.Bold
    }
    FontAwesomeToolButton {
        symbol: Icons.faArrowAltCircleUp
        font.weight: Font.Bold
    }
    FontAwesomeToolButton {
        symbol: Icons.faAmazon
    }
    FontAwesomeToolButton {
        symbol: Icons.faBluetoothB
    }
    FontAwesomeToolButton {
        symbol: Icons.faChrome
    }
}

… which is way more readable.

Thank You For Reading
Martin Hoeher

I am a software/firmware developer, working in Dresden, Germany. In my free time, I spent some time on developing apps, presenting some interesting findings here in my blog.

Comments
Your comment has been filed

We'll review it and it will appear here as soon as we're done.

Sorry, something went wrong...

Your comment could not be posted.

Regarding your personal information...
  • The name you enter will be shown next to your comment. You may enter your real name or whatever you like.
  • Your e-mail will be used to show an image representing you next to your comment. We do not store nor show your address somewhere. Instead, an ID will be calculated from it and stored. This ID is then used to retrieve an image from Gravatar.