Developing for Sailfish OS: architecture in FLUX QML application as an example for memorizing literary terms
All kind time of day! In this article I would like to tell you how we have developed our first app for the platform Sailfish OS (the development of which was a series of articles).

The task was to write an app using which we could study and memorize the literary terms. So as to realize a conventional dictionary interpretation of the words too simple and boring, it was decided: to organize the learning process through interaction with the user. Having considered all the available options for building the user interaction, it was decided to make training in tests.
Since the dictionary is a list of terms, each of which the definition, it was decided to allocate three types of questions for tests. In the first type the user will be shown the term and four possible definitions from which he must choose one is correct. The second type of questions is equivalent to the first, with the only difference that the user has already displayed one definition, and four of the term. The user must select the term that matches the definition. Finally, the last type of questions — open response. The user will be shown a definition and it is necessary to write the word corresponding to this definition.
In addition to the above-described learning process, it was necessary to implement a list view of terms, which we will learn to let the user know what to ask. It was also planned to track and maintain progress in study, to show the user how much progress he achieved in a study.
the
Actually the dictionary was kindly provided to us by employees of the Department of foreign languages of our University (YSU them. P. G. Demidov). He was in plain text, so for ease of use we moved it to xml format. It turned out an xml document consisting of elements of the form:
the
Loaded this dictionary is very easy — using the standard component XmlListModel.
As the application architecture was chosen promoted by the Corporation is Facebook architecture "Flux". About architecture there have been many written articles. Quite interesting and understandable translation available in Habra: here and here. As we were guided by article on the use of Flux when writing QML applications. Recommend article to anyone who writes applications in QML (not necessarily mobile). To describe all these aspects here too, since all information is available at the above links and it is described there very well. Therefore write only as a Flux architecture used in our application.
With View all is clear – each application page is part of View. The transition between pages is done using Actions. In our case, the transition is responsible for the Action navigateTo.
the
To store values and also to implement functions using two Store. One (we called it TermInformationStore) is responsible for the individual's current term. It contains information about the term: the word, its transcription, meaning, example of usage and synonyms to it. In the same Store are filled with properties that contain the above information.
The second Store is TestStore — responsible for testing process and progress in learning words. It contains information about the current test question. So, here these questions are compiled and calculated progress.
To separate the data and the organization of the relationship of the parts of the application were created by the Script, which is responsible for receiving signals from the View and calling functions from Store in the correct order, which solves the problem with calling new actions, when the old has not yet ended. Also, this element contains all the logic for moving between different screens of the app.
the
Since this was our first application for this platform, and QML in General, first we of course took the most simple — a list of terms. The list itself is implemented using SilicaListView, which loaded the list of terms of XmlListModel (as described just above). Actually, it's the usual list, and since making lists is one of the most basic and common example for QML in General, and for Sailfish OS in particular, and the focus we have at this moment won't.
Clicking on a list item opens a page with a detailed description of the term. Since for the application we decided to use the Flux architecture, the process of opening page is a bit unusual compared to MVC or MVVM. Clicking on a list item creates the Action with information about the index of the clicked element. This Action provokes TermInformationStore to change information about the current term, depending on the selected index of the list item, and then open the page with the description. It looks quite simple:

Testing could begin from the home screen. Just a test, 20 questions in a non-repeating terms randomly. The question type (as described at the beginning — we have three of them) and wrong answers (if they need to be in this type of question) are also chosen randomly. As mentioned above, all of the logic to formulate questions answers TestStore. Question is created as follows:
the
The function receives the index of the term in the dictionary and the type of question. Depending on these parameters are properties of the TestStore responsible for the current question (question, options, rightAnswer and others). They will then be used by view to display the question to the user. For each type of question has its own page:
the
Here is an example of code for a question page, where the user needs to choose the term by definition:
the
As you can see, the information on the page is very easy simply appealing to the properties of TestStore.
After each issue that was found during testing, the app displays the correct answer and the word itself, its meaning and application. This allows more time to consolidate the knowledge of the user or, if you were given an incorrect answer, gives you the ability to learn and remember correct:

When this occurs, the recalculation of user progress. The conversion is associated with the app settings and will be shown below.
The results of the user study are displayed for the vocabulary and for each term separately. For individual terms, the result is calculated when you select one of the options.
the
For the whole dictionary progress is displayed in a scale that reflects the total degree of "knowledge" all the present terms. The app also keeps statistics of how many words in the dictionary have already been successfully explored by the user. This progress is displayed on the home page of the application and on its cover:
the
Since the app is rated for continuous use, it was necessary to implement storage of the results of the user to the accumulated result is not lost between runs of the application. To save progress, it was decided to use Qt provided by class QSettings. It provides permanent storage of settings and application data. For Salifish OS all data is stored in ini file, respectively, the format of the stored data string. As QSettings yet the class from Qt, it was necessary to import it as a module in QML. This is done in the body of the main function as follows:
the
The progress in the file will be saved in the form of "a dictionary name/number of the term" — "degree of knowledge". The name of the dictionary is here not by chance, in the future we plan to add more dictionaries, and also, perhaps, to adding custom dictionaries. When you run the application, the degree of scrutiny of the terms read from a file and summed to calculate the total progress also reads the number of words that are "studied" by the user:
the
The entry/updating of the degree of scrutiny of the term occurs at the moment of its change, i.e. at the time of the choice response in the test. It happens thus:
the
the
In the end we managed to implement all planned features and our first application for Sailfish OS has been successfully created. Recently we published it Jolla Store where it is available to download and already has about 2 hundred users:

Authors: Maxim Kosterin, Nikita Romanov
Article based on information from habrahabr.ru

The task was to write an app using which we could study and memorize the literary terms. So as to realize a conventional dictionary interpretation of the words too simple and boring, it was decided: to organize the learning process through interaction with the user. Having considered all the available options for building the user interaction, it was decided to make training in tests.
Since the dictionary is a list of terms, each of which the definition, it was decided to allocate three types of questions for tests. In the first type the user will be shown the term and four possible definitions from which he must choose one is correct. The second type of questions is equivalent to the first, with the only difference that the user has already displayed one definition, and four of the term. The user must select the term that matches the definition. Finally, the last type of questions — open response. The user will be shown a definition and it is necessary to write the word corresponding to this definition.
In addition to the above-described learning process, it was necessary to implement a list view of terms, which we will learn to let the user know what to ask. It was also planned to track and maintain progress in study, to show the user how much progress he achieved in a study.
the
Features
Actually the dictionary was kindly provided to us by employees of the Department of foreign languages of our University (YSU them. P. G. Demidov). He was in plain text, so for ease of use we moved it to xml format. It turned out an xml document consisting of elements of the form:
the
<term>
<name>
<text>Epenalepsis</text>
</name>
<synonym>
<text>Polysyndeton</text>
<transcription>[ˌpɒlɪˈsɪndɪtən]</transcription>
</synonym>
<description>Use of several conjunctions</description>
<context>He thought, and thought, and thought...</context>
</term>
Loaded this dictionary is very easy — using the standard component XmlListModel.
As the application architecture was chosen promoted by the Corporation is Facebook architecture "Flux". About architecture there have been many written articles. Quite interesting and understandable translation available in Habra: here and here. As we were guided by article on the use of Flux when writing QML applications. Recommend article to anyone who writes applications in QML (not necessarily mobile). To describe all these aspects here too, since all information is available at the above links and it is described there very well. Therefore write only as a Flux architecture used in our application.
With View all is clear – each application page is part of View. The transition between pages is done using Actions. In our case, the transition is responsible for the Action navigateTo.
the
AppListener {
filter: ActionTypes.navigateTo
onDispatched: {
pageStack.push(Qt.resolvedUrl("../sailfish-only/views/pages/" + message.url));
}
}
To store values and also to implement functions using two Store. One (we called it TermInformationStore) is responsible for the individual's current term. It contains information about the term: the word, its transcription, meaning, example of usage and synonyms to it. In the same Store are filled with properties that contain the above information.
The second Store is TestStore — responsible for testing process and progress in learning words. It contains information about the current test question. So, here these questions are compiled and calculated progress.
To separate the data and the organization of the relationship of the parts of the application were created by the Script, which is responsible for receiving signals from the View and calling functions from Store in the correct order, which solves the problem with calling new actions, when the old has not yet ended. Also, this element contains all the logic for moving between different screens of the app.
the
Implemented features
Since this was our first application for this platform, and QML in General, first we of course took the most simple — a list of terms. The list itself is implemented using SilicaListView, which loaded the list of terms of XmlListModel (as described just above). Actually, it's the usual list, and since making lists is one of the most basic and common example for QML in General, and for Sailfish OS in particular, and the focus we have at this moment won't.
Clicking on a list item opens a page with a detailed description of the term. Since for the application we decided to use the Flux architecture, the process of opening page is a bit unusual compared to MVC or MVVM. Clicking on a list item creates the Action with information about the index of the clicked element. This Action provokes TermInformationStore to change information about the current term, depending on the selected index of the list item, and then open the page with the description. It looks quite simple:

Testing could begin from the home screen. Just a test, 20 questions in a non-repeating terms randomly. The question type (as described at the beginning — we have three of them) and wrong answers (if they need to be in this type of question) are also chosen randomly. As mentioned above, all of the logic to formulate questions answers TestStore. Question is created as follows:
the
function makeQuestion(index, type) {
options = [];
var element = dictionary.get(index);
question = (type === 0) ? element.name : element.description;
questionIndex = index;
rightAnswer = (type === 0) ? element.description : element.name;
alternativeRightAnswer = (element.synonym !== "") ? element.synonym : element.name;
if(type !== 2) {
var rightVariantNumber = Math.floor(Math.random() * 4);
for(var i = 0; i < 4; i++) {
if(i !== rightVariantNumber) {
options.push(getWrongOption(index, type));
} else {
options.push((type === 0) ? element.description : element.name);
}
}
}
}
The function receives the index of the term in the dictionary and the type of question. Depending on these parameters are properties of the TestStore responsible for the current question (question, options, rightAnswer and others). They will then be used by view to display the question to the user. For each type of question has its own page:
![]() |
![]() |
![]() |
Here is an example of code for a question page, where the user needs to choose the term by definition:
the
Page {
SilicaFlickable {
anchors.fill: parent
contentHeight: column.height + Theme.paddingLarge
VerticalScrollDecorator {}
Column {
id: column
width: parent.width
spacing: Theme.paddingLarge
PageHeader { title: qsTr("Question ") + TestStore.questionNumber }
Label {
text: TestStore.question
font.pixelSize: Theme.fontSizeMedium
wrapMode: Text.Wrap
anchors {
left: parent.left
right: parent.right
margins: Theme.paddingLarge
}
}
Button {
id: option0
height: Theme.itemSizeMedium
anchors {
left: parent.left
right: parent.right
margins: Theme.paddingLarge
}
text: TestStore.options[0]
onClicked: {
AppActions.submitAnswer(option0.text);
}
}
Button {
id: option1
height: Theme.itemSizeMedium
anchors {
left: parent.left
right: parent.right
margins: Theme.paddingLarge
}
text: TestStore.options[1]
onClicked: {
AppActions.submitAnswer(option1.text);
}
Button {
id: option2
height: Theme.itemSizeMedium
anchors {
left: parent.left
right: parent.right
margins: Theme.paddingLarge
}
text: TestStore.options[2]
onClicked: {
AppActions.submitAnswer(option2.text);
}
}
Button {
id: option3
height: Theme.itemSizeMedium
anchors {
left: parent.left
right: parent.right
margins: Theme.paddingLarge
}
text: TestStore.options[3]
onClicked: {
AppActions.submitAnswer(option3.text);
}
}
Button {
height: Theme.itemSizeLarge
anchors {
left: parent.left
right: parent.right
margins: Theme.paddingLarge
}
text: qsTr("Skip question")
onClicked: {
AppActions.skipQuestion();
}
}
}
}
}
As you can see, the information on the page is very easy simply appealing to the properties of TestStore.
After each issue that was found during testing, the app displays the correct answer and the word itself, its meaning and application. This allows more time to consolidate the knowledge of the user or, if you were given an incorrect answer, gives you the ability to learn and remember correct:

When this occurs, the recalculation of user progress. The conversion is associated with the app settings and will be shown below.
The results of the user study are displayed for the vocabulary and for each term separately. For individual terms, the result is calculated when you select one of the options.
the
AppScript {
runWhen: ActionTypes.submitAnswer
script: {
TestStore.checkResult(message.answer);
TestStore.updateDictionaryProgress(TestStore.questionIndex);
TermInformationStore.updateInfo(TestStore.questionIndex);
AppActions.replacePage("QuestionResult.qml");
}
}
For the whole dictionary progress is displayed in a scale that reflects the total degree of "knowledge" all the present terms. The app also keeps statistics of how many words in the dictionary have already been successfully explored by the user. This progress is displayed on the home page of the application and on its cover:
![]() |
![]() |
Since the app is rated for continuous use, it was necessary to implement storage of the results of the user to the accumulated result is not lost between runs of the application. To save progress, it was decided to use Qt provided by class QSettings. It provides permanent storage of settings and application data. For Salifish OS all data is stored in ini file, respectively, the format of the stored data string. As QSettings yet the class from Qt, it was necessary to import it as a module in QML. This is done in the body of the main function as follows:
the
qmlRegisterType<Settings>("harbour.dictionary.trainer.settings", 1, 0, "Settings");
QQuickView* view = SailfishApp::createView();
QSettings data("FRUCT", "Dictionary Trainer");
data.setPath(QSettings::NativeFormat, QSettings::UserScope,
QStandardPaths::writableLocation(QStandardPaths::DataLocation));
qmlEngine- > rootContext()->setContextProperty("data", &data);
QQmlComponent dataComponent(qmlEngine, QUrl("TestStore"));
dataComponent.create();
The progress in the file will be saved in the form of "a dictionary name/number of the term" — "degree of knowledge". The name of the dictionary is here not by chance, in the future we plan to add more dictionaries, and also, perhaps, to adding custom dictionaries. When you run the application, the degree of scrutiny of the terms read from a file and summed to calculate the total progress also reads the number of words that are "studied" by the user:
the
function fillProgress() {
progress = 0;
learnedWords = 0;
if(data.childGroups().indexOf("dictionary") !== -1) {
for (var i = 0; i < dictionary.count; i++){
progress += data.valueAsInt("dictionary/" + i.toString());
}
learnedWords = data.value("dictionary/learnedWords", 0);
} else {
for (var i = 0; i < dictionary.count; i++){
data.setValue("dictionary/" + i.toString(), 0);
}
data.setValue("dictionary/learnedWords", 0)
}
The entry/updating of the degree of scrutiny of the term occurs at the moment of its change, i.e. at the time of the choice response in the test. It happens thus:
the
function updateDictionaryProgress(index) {
var currentStatus = data.valueAsInt("dictionary/" + index);
var newStatus;
if (result === "correct") {
newStatus = getWordStatus(currentStatus + 1);
} else {
newStatus = getWordStatus(currentStatus - 2);
}
var statusChange = newStatus - currentStatus;
calculateLearnedWords(currentStatus, newStatus);
progress += statusChange;
data.setValue("dictionary/" + index.toString(), newStatus);
}
the
Summary
In the end we managed to implement all planned features and our first application for Sailfish OS has been successfully created. Recently we published it Jolla Store where it is available to download and already has about 2 hundred users:

Authors: Maxim Kosterin, Nikita Romanov
Комментарии
Отправить комментарий