Sailfish OSの開発:文学用語を記憶するためのサンプルアプリケーションを使用したQMLのFLUXアーキテクチャ

すべての人に良い一日を! この記事では、Sailfish OSプラットフォーム向けの最初のアプリケーションをどのように開発したかを説明したいと思います(開発に関する記事はすでに多数ありました)。







タスクは、文学用語を研究し、記憶することができるアプリケーションを書くことでした。 単語の解釈を伴う通常の辞書を実装するのは簡単すぎて退屈なので、ユーザーの対話を通じて学習プロセスを整理するという決定が下されました。 ユーザーとの対話を構築するために利用可能なすべてのオプションを検討したため、テストの形でトレーニングを行うことが決定されました。



辞書はそれぞれの定義が与えられている用語のリストであるため、テスト用の3種類の質問を識別することが決定されました。 最初のタイプでは、ユーザーには用語自体と、適切な用語を選択する必要がある用語の定義の4つのバリエーションが表示されます。 2番目のタイプの質問は1番目の質問と同等ですが、唯一の違いはユーザーに1つの定義と4つの用語が既に表示されていることです。 ユーザーは、この定義に一致する用語を選択する必要があります。 最後に、最後のタイプの質問は公開回答です。 ユーザーには定義が表示され、この定義に対応する単語を自分で書く必要があります。



上記の学習プロセスに加えて、ユーザーが彼から何を尋ねられるかを知ることができるように、私たちが教える用語のリストの表示を実装する必要がありました。 また、ユーザーに単語の学習でどのような成功を達成したかを示すために、学習の進捗状況を追跡および維持することも計画されていました。



アプリケーション機能



実際、辞書自体は私たちの大学の外国語学部のスタッフ(P. G. Demidovにちなんで名付けられたヤロスラヴリ州立大学)から親切に提供されました。 これはプレーンテキストであったため、使いやすくするために、xml形式に移動しました。 結果は、次の形式の要素で構成されるxmlドキュメントです。



<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>
      
      





このような辞書のダウンロードは非常に簡単です-標準のXmlListModelコンポーネントを使用します。



Facebook独自のFluxアーキテクチャがアプリケーションアーキテクチャとして選択されました。 アーキテクチャ自体については、すでに多くの記事が執筆されています。 非常に興味深く、理解可能な翻訳がHabréで利用可能です: hereおよびhere 。 また、開発中に、 QMLアプリケーションを作成する際にFluxを使用することに関する記事にガイドされました。 QMLでアプリケーションを作成するすべての人(必ずしもモバイルである必要はありません)にこの記事をお勧めします。 すべての情報は上記のリンクで入手可能であり、非常によく説明されているため、ここでこれらのすべてのポイントを説明する必要はありません。 したがって、アプリケーションでFluxアーキテクチャがどのように使用されたかだけを記述します。



ビューを使用すると、すべてが明確になります-アプリケーションの各ページはビューの一部です。 ページ間の遷移は、アクションを使用して実行されます。 この場合、アクションnavigateToが遷移を担当します。



 AppListener { filter: ActionTypes.navigateTo onDispatched: { pageStack.push(Qt.resolvedUrl("../sailfish-only/views/pages/" + message.url)); } }
      
      





2つのストアを使用して、値を格納し、関数を実装します。 1つ(これをTermInformationStoreと呼びます )は、別の現在の用語を担当します。 この用語に関する情報が含まれています:単語自体、その転写、意味、使用例、同義語。 同じストアで、上記の情報を含むプロパティが入力されます。



2番目のストアであるTestStoreは、テストプロセスと単語学習の進捗を担当します。 現在のテスト問題に関する情報が含まれています。 したがって、これらの質問はここでまとめられ、ここで進捗状況が計算されます。



データとアプリケーションの部分間の関係の編成を分離するために、Viewから信号を受け取り、Storeから関数を正しい順序で呼び出す責任を負うScript要素が作成されました。これにより、古いアクションがまだ完了していないときに新しいアクションを呼び出す問題が解決されます。 また、この要素には、アプリケーションの異なる画面間を移動するためのすべてのロジックが含まれています。



実装された機能



これはこのプラットフォームの最初のアプリケーションであり、QML全般では、最初はもちろん最も単純な用語リストを取り上げました。 リスト自体は、 SilicaListViewを使用して実装されます。このリストには、 XmlListModelから用語のリストがロードされます (上記で説明したとおり)。 一般に、これは最も一般的なリストであり、リストの作成はQML全般、特にSailfish OSの最も基本的で一般的な例の1つであるため、この時点ではこの点に焦点を合わせません。



リスト項目をクリックすると、用語の詳細な説明が記載されたページが開きます。 アプリケーションにFluxアーキテクチャを使用することを決定したため、このページを開くプロセスは、MVCまたはMVVMと比べてやや異常に見えます。 リスト項目をクリックすると、クリックされた項目のインデックスに関する情報を含むアクションが作成されます。 このアクションは、 TermInformationStore呼び出して、リストアイテムの選択されたインデックスに応じて現在の用語に関する情報を変更し、説明ページを開きます。 それは非常に簡単に見えます:









テストはメイン画面から開始できます。 ランダムに選択された非繰り返し用語のテストには20の質問があります。 質問自体のタイプ(冒頭で説明したように、そのうち3つあります)および間違った回答(このタイプの質問に該当する場合)もランダムに選択されます。 前述のように、 TestStoreは質問のコンパイルのロジック全体を担当します。 質問は次のように作成されます。



 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); } } } }
      
      





辞書内の用語のインデックスインデックスと質問のタイプが関数に渡されます。 これらのパラメーターに応じて、現在の質問を担当するTestStoreプロパティが入力されますquestionoptionsrightAnswerなど)。 これらは、ビューに使用され、質問をユーザーに表示します。 質問の種類ごとに独自のページがあります。









定義によって用語を選択する必要がある場所を尋ねるページのサンプルコードを次に示します。



 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(); } } } } }
      
      





ご覧のとおり、ページの情報はTestStoreのプロパティにアクセスするだけで非常に簡単に入力されます。



テスト中に発生した各質問の後に、アプリケーションは、この回答の正しさと、単語自体、その意味、およびアプリケーションを表示します。 これにより、ユーザーの知識を再度統合することができます。間違った答えが与えられた場合、正しいものを見つけて覚えることができます。









これにより、ユーザーの進行状況が再カウントされます。 再集計自体はアプリケーション設定に関連付けられており、以下に表示されます。



単語の調査におけるユーザーの結果は、辞書全体と各用語ごとに表示されます。 個々の用語については、回答オプションのいずれかを選択すると結果が計算されます。



 AppScript { runWhen: ActionTypes.submitAnswer script: { TestStore.checkResult(message.answer); TestStore.updateDictionaryProgress(TestStore.questionIndex); TermInformationStore.updateInfo(TestStore.questionIndex); AppActions.replacePage("QuestionResult.qml"); } }
      
      





辞書全体について、進行状況は、存在するすべての用語の「知識」の合計度合いを反映するスケールの形式で表示されます。 また、アプリケーションは、ユーザーが辞書からすでにいくつの単語を学習したかに関する統計を保持します。 この進行状況は、アプリケーションのメインページとその表紙の両方に表示されます。







アプリケーションは長期間使用するように設計されているため、蓄積された結果全体がアプリケーションの起動の間に失われないように、ユーザー結果の保存を実装する必要がありました。 進捗を保存するために、Qtが提供するQSettingsクラスを使用することにしました。 設定とアプリケーションデータを永続的に保存する機能を提供します。 Salifish OSの場合、すべてのデータはそれぞれiniファイルに保存されます。保存されるデータの形式は文字列です。 QSettingsはまだQtのクラスであるため、QMLのモジュールとしてインポートする必要がありました。 これは、メイン関数の本体で次のように行われます。



 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();
      
      





ファイルの学習の進捗は、「辞書名/用語番号」-「知識の程度」の形式で保存されます。 ここでの辞書の名前は偶発的なものではありません。将来、さらに辞書を追加する予定であり、カスタム辞書の追加を実装することも可能です。 アプリケーションが起動すると、用語の知識の程度がファイルから読み取られ、全体的な進捗を計算するために要約されます。ユーザーが「学習した」単語の数も読み取られます。



 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) } }
      
      





用語の知識の度合いの記録/更新は、その変更時、つまりテストで回答を選択するときに行われます。 このようになります:



 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); }
      
      





まとめ



その結果、計画されたすべての機能を実装でき、Sailfish OSの最初のアプリケーションが正常に作成されました。 さらに最近では、Jolla Storeを公開しました。JollaStoreはダウンロード可能で、すでに約200人のユーザーがいます。









著者:マキシムコステリン、ニキータロマノフ



All Articles