Today I was wondering if I could use Font Awesome with Qt’s QML to add some nice looking icons to my buttons. A quick google search brought up an article “Using Fonts Awesome in QML” by markg85 at Mark’s KDE Blog. Mark came up with a nice solution that uses a javascript dictionary to store the icon names with their corresponding unicode character:
var Icon = {
Glass : "\uf000",
Music : "\uf001",
...
};
However, this has two problems.
- The names in the javascript file do not match the definitions on fontawesome.github.io.
- The defintions are outdated and do neither include the latest icons nor alias names.
Funny Fact: It’s actually fortawesome.github.io not fontawesome.github.io.
To fix both issues, I wrote a small python script, which will visit https://fontawesome.com/icons, extract all icon names and the corresponding icon pages (for example https://fontawesome.com/icons/cogs) and load the unicode associated with this icon name from the icon page:
#!/usr/bin/python3
import re
import urllib.request
import http.server
def fetchSite(url):
req = urllib.request.Request(url)
try:
with urllib.request.urlopen(req) as response:
html = response.read()
except urllib.error.HTTPError as e:
errorMsg = http.server.BaseHTTPRequestHandler.responses[e.code][0]
print("Cannot retrieve URL: {} : {}".format(str(e.code), errorMsg))
except urllib.error.URLError as e:
print("Cannot retrieve URL: {}".format(e.reason))
except:
print("Cannot retrieve URL: unknown error")
return html
def extractIconLinks(html):
links = {}
pattern = r'<div class="fa-hover col-md-3 col-sm-4"><a href="../icon/(.*?)"><i class="fa (.*?)"></i> (.*?)</a></div>'
for match in re.finditer(pattern, str(html)):
iconName = match.group(2).partition(' ')[0]
iconLink = match.group(1)
if iconName[:3] == "fa-":
links[iconName] = "{}{}".format("https://fontawesome.com/icons/", iconLink)
return links
def extractUnicode(html):
match = re.search(r'Unicode: <span class="upper">(.*?)</span>', str(html))
if match:
return "\\u{}".format(match.group(1))
return None
def extractIconData(html):
icons = []
# Extract the icon urls from the given site, ...
links = extractIconLinks(html)
# ... fetch for each icon the unicode char.
for key, value in links.items():
try:
iconHtml = fetchSite(value)
iconUnicode = extractUnicode(iconHtml)
icons.append({'name': key, 'link': value, 'unicode': iconUnicode})
except:
print("Failed to fetch icon {} from {}".format(key, value))
return icons
def main():
# Prepare the url of the main site ...
fawesomeUrl = "https://fontawesome.com/icons/"
# ... fetch its' content ...
fawesomeHtml = fetchSite(fawesomeUrl)
# ... and extract the icon data.
icons = extractIconData(fawesomeHtml)
# Write the result to fontawesome.js.
with open('fontawesome.js', 'w') as fp:
line = '\t'
fp.write("var Icon = {\n")
for icon in icons:
escapedName = icon['name'].replace('-', '_')
iconEntry = '{}: "{}", '.format(escapedName, icon['unicode'])
if len('{}{}'.format(line, iconEntry)) <= 80:
line = '{}{}'.format(line, iconEntry)
else:
fp.write('{}\n'.format(line))
line = '\t{}'.format(iconEntry)
fp.write('{}\n}};'.format(line))
if __name__ == "__main__":
main()
This will generate a file in the current working directory called fontawesome.js with the following content (shorted):
var Icon = {
fa_copyright: "\uf1f9", fa_connectdevelop: "\uf20e", fa_archive: "\uf187",
fa_taxi: "\uf1ba", fa_file_sound_o: "\uf1c7",
...
fa_link: "\uf0c1",
};
The resulting javascript file could be loaded and accessed within the *.qml file as Mark described in his Blog Post:
import QtQuick 2.5
import "fontawesome.js" as FontAwesome
ApplicationWindow {
visible: true
width: 300
height: 300
title: qsTr("Font Awesome - Test")
FontLoader {
source: "fontawesome-webfont.ttf"
}
Rectangle {
id: main
anchors.fill: parent
Text {
anchors.horizontalCenter: main.horizontalCenter
anchors.verticalCenter: main.verticalCenter
font.pointSize: 100
font.family: "FontAwesome"
text: FontAwesome.Icon.fa_optin_monster
}
}
}
The above QML code should produce the following output:
That’s it. Enjoy and until next time: Keep coding!