Practical QML
Burkhard Stubert
Chief Engineer, Embedded Use
www.embeddeduse.com
Contents

 Key Navigation
 Dynamic Language Change
 Themes
Key Navigation in Cars

Navigation clusters for controlling
in-vehicle infotainment systems
Key Navigation in Harvesters

Driver terminals for
Harvesters and tractors
Active Focus
 QML item needs active focus to receive key

events
 Only single item has active focus
 Property Item.acti...
Focus Scopes
 Component FocusScope
 Controls which child item gets active focus
 Needed for introducing new components ...
Who gains active focus?
FocusScope A
FocusScope B1
Rectangle C1

Rectangle C2

focus: true

FocusScope B2

focus: true

Re...
Recap: KeyNavigation
Attached Property
Tab
Backtab

FlagButton {
id: france
KeyNavigation.backtab: spain
KeyNavigation.tab...
Crossing FocusScopes with
KeyNavigation
focus: true
focus: true

 Enclose flag rows with FocusScope as preliminary

for F...
Crossing FocusScopes with
KeyNavigation (2)

 KeyNavigation stops when crossing to other

FocusScope
 Reason: FocusScope...
Crossing Focus Scopes
with KeyNavigation (3)
 Solution:

FlagButton {
id: italy
KeyNavigation.backtab: france
KeyNavigati...
Introducing a Generic
Cursor Component
 Forces guiding the solution
 Write code for state machine, visual items, key and...
Moving Active Focus in Item
Hierarchy
FlagRow.row0

FlagRow.row1

FlagButton.france

FlagButton.italy

FlagButton.uk

Curs...
Introducing New Attached
Property KeyNav
 KeyNav
 tabUp : Item
 backtabUp: Item

tabDown: Item
backtabDown: Item

 Att...
Handling the Return Key in
Cursor
signal released()
Keys.onPressed: {
if (event.key === Qt.Key_Return) {
root.state = “pre...
Key Navigation in ListViews
 Forces guiding the solution
 ListView item has no way to find out previous and
next item
• ...
Key Navigation in ListViews
(2)
 Extract doTab() and doBacktab() from Cursor

into ButtonCursor and ListViewItemCursor

C...
Key Navigation in ListViews
(3)
 Every ListView inherits from BaseListView

 BaseListView provides tabbing and backtabbi...
Adding Mouse Handling to
Cursor Components
MouseArea {
anchors.fill: parent
onPressed: {
root.doMousePress()
root.state = ...
Adding Mouse Handling to
Cursor Components (2)
In ButtonCursor:
function doMousePress() {
root.forceActiveFocus()
}
index ...
Contents

 Key Navigation
 Dynamic Language Change
 Themes
Dynamic Language Change
Dynamic Language Change
for QWidgets
 QCoreApplication::installTranslator() sends

LanguageChange event to application ob...
Problems in QML
 Not a single QWidget in QML applications
 Not even QQuickView derives from QWidget
 QApplication not u...
Dynamic Language Change
in QML
 TranslationManager emits signal

languageChanged()
 Qt/C++ classes (e.g., list models) c...
Changing the Language
 TranslationManager::setLanguage(language)
 Load translation file for language in QTranslator
 Re...
Retranslating Qt/C++
Models
 Equivalent to reimplementing changeEvent()

and calling retranslateUi()
 In constructor of ...
Retranslating Qt/C++
Models (2)
void BiggestCitiesModel::retranslate(const QString &language)
{
emit titleChange();
CityDa...
Retranslating Qt/C++
Models (3)
const char *CityDatabase::m_strings[][2] = {
{ QT_TR_NOOP(“Munich”), QT_TR_NOOP(“Bavaria”)...
Reevaluating qsTr on
Language Change
Text {
text: qsTr(“City:”) + g_tr.languageChanged
…
}

 Use Property Binding:
 When...
Reevaluating qsTr on
Language Change (2)
In TranslationManager:
Q_PROPERTY(QString languageChanged
READ emptyString
NOTIFY...
Reevaluating qsTr on
Language Change (3)
On instance of QQuickView:
view->rootContext()->setContextProperty(
“g_tr”, Trans...
Contents

 Key Navigation
 Dynamic Language Change
 Themes
Dynamic Theme Change
Theming QML Code
Rectangle {
color: index % 2 === 0 ?
“#1E90FF” :
“#00BFFF”

Row {
Text {
text: city.name
color: “#191970”...
Implementing the Themes
QtObject {
property QtObject listViewItem : QtObject {
property color backgroundColor: “#1E90FF”
p...
Changing Themes
In top-level QML item (main.qml)

Global variable accessible from
everywhere in QML

property alias g_them...
The End

Thank you!
Upcoming SlideShare
Loading in …5
×

Practical QML - Key Navigation, Dynamic Language and Theme Change

8,267 views

Published on

This is the presentation "Practical QML" that I gave at the Qt Developer Days 2013 in Berlin and San Francisco.

What topics entail the liveliest discussions when you talk to the makers of in-vehicle infotainment systems, agricultural driver terminals or set-top boxes about building QML GUIs? No, it is not about "fluid" multi-touch UIs with graphical "wow" effects, but about mundane things like key navigation, theming, and internationalisation. I'll address these three problems in my talk.

First, I’ll discuss how to navigate QML GUIs solely with a rotary knob. By turning or pushing the knob, a "cursor" is moved from one QML component or one screen to another.

Then, I'll show how to switch between different languages and between different themes at run-time.

Published in: Technology
0 Comments
5 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
8,267
On SlideShare
0
From Embeds
0
Number of Embeds
1,001
Actions
Shares
0
Downloads
93
Comments
0
Likes
5
Embeds 0
No embeds

No notes for slide

Practical QML - Key Navigation, Dynamic Language and Theme Change

  1. 1. Practical QML Burkhard Stubert Chief Engineer, Embedded Use www.embeddeduse.com
  2. 2. Contents  Key Navigation  Dynamic Language Change  Themes
  3. 3. Key Navigation in Cars Navigation clusters for controlling in-vehicle infotainment systems
  4. 4. Key Navigation in Harvesters Driver terminals for Harvesters and tractors
  5. 5. Active Focus  QML item needs active focus to receive key events  Only single item has active focus  Property Item.activeFocus (read-only)  True if item has active focus  Function Item.forceActiveFocus()  Forces item to have active focus  Property Item.focus  Requests active focus when set to true
  6. 6. Focus Scopes  Component FocusScope  Controls which child item gets active focus  Needed for introducing new components with key handling  When FocusScope receives active focus:  Last item to request focus gains active focus  When last item is FocusScope, active focus is forwarded to FocusScope
  7. 7. Who gains active focus? FocusScope A FocusScope B1 Rectangle C1 Rectangle C2 focus: true FocusScope B2 focus: true Rectangle D1 Rectangle D1 focus: true
  8. 8. Recap: KeyNavigation Attached Property Tab Backtab FlagButton { id: france KeyNavigation.backtab: spain KeyNavigation.tab: italy
  9. 9. Crossing FocusScopes with KeyNavigation focus: true focus: true  Enclose flag rows with FocusScope as preliminary for FlagRow component  What happens when crossing to other flag row?
  10. 10. Crossing FocusScopes with KeyNavigation (2)  KeyNavigation stops when crossing to other FocusScope  Reason: FocusScope changes focus instead of activeFocus
  11. 11. Crossing Focus Scopes with KeyNavigation (3)  Solution: FlagButton { id: italy KeyNavigation.backtab: france KeyNavigation.tab: uk Keys.onTabPressed: uk.forceActiveFocus()  KeyNavigation not suited for components  Reason: top item of component always a FocusScope  KeyNavigation forces monolithic code
  12. 12. Introducing a Generic Cursor Component  Forces guiding the solution  Write code for state machine, visual items, key and mouse handling only once  Use only one way to move active focus: forceActiveFocus()  Tab and backtab chains must take component structures into account
  13. 13. Moving Active Focus in Item Hierarchy FlagRow.row0 FlagRow.row1 FlagButton.france FlagButton.italy FlagButton.uk Cursor.france Cursor.italy Cursor.italy Tab Tab  KeyNavigation structure needs four properties: tabUp/tabDown and backtabUp/backtabDow
  14. 14. Introducing New Attached Property KeyNav  KeyNav  tabUp : Item  backtabUp: Item tabDown: Item backtabDown: Item  Attached properties ≈ multipe inheritance  Save us from declaring four properties in each QML component  Example use in middle FlagButton FlagButton { id: flag1 KeyNav.backtabUp: flag0.KeyNav.backtabDown KeyNav.tabUp: flag2.KeyNav.tabDown }
  15. 15. Handling the Return Key in Cursor signal released() Keys.onPressed: { if (event.key === Qt.Key_Return) { root.state = “pressed” event.accepted = true } } Keys.onReleased: { if (event.key === Qt.Key_Return) { root.state = “focused” root.released() event.accepted = true } } Make key and mouse handling look the same for clients Also add “pressed” State to states property Move out of if-clause to stop default key handling of ListView (Up and Down) Forward in Cursor instance of FlagButton: onReleased: root.release()
  16. 16. Key Navigation in ListViews  Forces guiding the solution  ListView item has no way to find out previous and next item • Cannot use forceActiveFocus()  Changing currentIndex changes focus • Reimplement doTab() and doBacktab() for Cursor  Special cases for moving the active focus into the ListView with Tab and Backtab • Implement doTab() and doBacktab() for ListView
  17. 17. Key Navigation in ListViews (2)  Extract doTab() and doBacktab() from Cursor into ButtonCursor and ListViewItemCursor Cursor ButtonCursor ListViewItemCursor doTab() and doBacktab() use forceActiveFocus() to move active focus doTab() and doBacktab() change currentIndex to move active focus
  18. 18. Key Navigation in ListViews (3)  Every ListView inherits from BaseListView  BaseListView provides tabbing and backtabbing into list view In BaseListView: function doTab() { root.positionViewAtIndex(0, ListView.Beginning) root.currentIndex = 0 root.forceActiveFocus() } Ensure that first item will be visible Request focus for first item Forces active focus on ListView, which passes it to first item
  19. 19. Adding Mouse Handling to Cursor Components MouseArea { anchors.fill: parent onPressed: { root.doMousePress() root.state = “pressed” mouse.accepted = true } onReleased: { if (root.activeFocus) { root.state = “focused” root.released() } mouse.accepted = true } } Active focus on item pressed, no dereferencing of tab chain needed Mouse press different for buttons and list view items Do not execute “release” when item lost focus, e.g., when error dialog opened
  20. 20. Adding Mouse Handling to Cursor Components (2) In ButtonCursor: function doMousePress() { root.forceActiveFocus() } index provided by delegate in ListView In ListViewItemCursor: function doMousePress() { delegateRoot.ListView.view.currentIndex = index delegateRoot.ListView.view.forceActiveFocus() } For the case when the flag row has active focus and the user clicks in list view. Avoids multiple cursors.
  21. 21. Contents  Key Navigation  Dynamic Language Change  Themes
  22. 22. Dynamic Language Change
  23. 23. Dynamic Language Change for QWidgets  QCoreApplication::installTranslator() sends LanguageChange event to application object  QApplication::event() posts LanguageChange event to every top-level widget (QWidget*)  QWidget::event() calls changeEvent() on the widget and sends LanguageChange event to all its children  changeEvent() is called on every widget in the widget tree rooted at a top-level widget
  24. 24. Problems in QML  Not a single QWidget in QML applications  Not even QQuickView derives from QWidget  QApplication not used in QML applications  Note: QApplication derives from QGuiApplication Need to rebuild LanguageChange infrastructure in QML
  25. 25. Dynamic Language Change in QML  TranslationManager emits signal languageChanged()  Qt/C++ classes (e.g., list models) connect signal with their retranslate() slot  Every qsTr() call in QML must be reevaluated when signal emitted
  26. 26. Changing the Language  TranslationManager::setLanguage(language)  Load translation file for language in QTranslator  Remove old translator from application  Install new translator in application  emit languageChanged(language)  Call setLanguage() before main view of application is created  Call setLanguage() when user changes language
  27. 27. Retranslating Qt/C++ Models  Equivalent to reimplementing changeEvent() and calling retranslateUi()  In constructor of model class: connect(TranslationManager::instance(), SIGNAL(languageChanged(QString)), this, SLOT(retranslate(QString)));
  28. 28. Retranslating Qt/C++ Models (2) void BiggestCitiesModel::retranslate(const QString &language) { emit titleChange(); CityDatabase::instance()->retranslate(language); emit dataChanged(index(0), index(m_cities.count() - 1)); } Notify QML ListView that all its items have changed and need reloading Notify QML code that title property has changed QML calls title(), which returns tr(rawTitle()) Delegate retranslation, as model is “view” on database
  29. 29. Retranslating Qt/C++ Models (3) const char *CityDatabase::m_strings[][2] = { { QT_TR_NOOP(“Munich”), QT_TR_NOOP(“Bavaria”) }, … void CityDatabase::retranslate(const QString &language) { if (m_currentLanguage != language) { for (int i = 0; i < m_cities.count(); ++i) { m_cities[i]->setName(tr(m_strings[i][0])); … } m_currentLanguage = language; } Reset visible members } Guard against multiple “views” (e.g., German cities, British cities) requesting retranslation to same language (e.g., city name, state) with new translation of raw string
  30. 30. Reevaluating qsTr on Language Change Text { text: qsTr(“City:”) + g_tr.languageChanged … }  Use Property Binding:  Whenever g_tr.languageChanged changes, text must be reevaluated:  qsTr() is called and returns translation for new language
  31. 31. Reevaluating qsTr on Language Change (2) In TranslationManager: Q_PROPERTY(QString languageChanged READ emptyString NOTIFY languageChanged) QString emptyString() const { return “”; } Empty string can be appended to translated string without changing anything Emitting this signal forces QML to call emptyString(), the READ method of languageChanged property
  32. 32. Reevaluating qsTr on Language Change (3) On instance of QQuickView: view->rootContext()->setContextProperty( “g_tr”, TranslationManager::instance()); Makes pointer to TranslationManager globally available in QML under name g_tr
  33. 33. Contents  Key Navigation  Dynamic Language Change  Themes
  34. 34. Dynamic Theme Change
  35. 35. Theming QML Code Rectangle { color: index % 2 === 0 ? “#1E90FF” : “#00BFFF” Row { Text { text: city.name color: “#191970” Unthemed Rectangle { color: index % 2 === 0 ? g_theme.listViewItem. backgroundColor : g_theme.listViewItem. backgroundColorAlt Row { Text { text: city.name color: g_theme.listViewItem. textColor Themed
  36. 36. Implementing the Themes QtObject { property QtObject listViewItem : QtObject { property color backgroundColor: “#1E90FF” property color backgroundColorAlt: “#00BFFF” property color textColor: “#191970” } QtObject { property QtObject listViewItem : QtObject { property color backgroundColor: “#A5A5A5” property color backgroundColorAlt: “#818181” property color textColor: “#1E1E1E” }
  37. 37. Changing Themes In top-level QML item (main.qml) Global variable accessible from everywhere in QML property alias g_theme: loader.item Loader { id: loader } Set theme on start-up Component.onCompleted: { loader.source = Qt.resolveUrl(“BlueTheme.qml”) } QQuickView forwards signal Connections { themeChanged(QString theme) target: g_viewer onThemeChanged: { loader.source = Qt.resolvedUrl(theme + “Theme.qml”) } }
  38. 38. The End Thank you!

×