WPFで画面遷移したい

WPF+C#で同一ウィンドウ内で画面遷移したくて色々調べていたのですが、メインウィンドウにframe要素を配置してそこのプロパティを変更するという手法でいけそうでした。

で、親ウィンドウのframe.sourceプロパティを編集すれば変わりそう・・・だったので、まずstringを渡しましたがエラー。

エラーを見るとframe.sourceに渡せるのはuri、ということで、string→uriの変換方法を探してみます。

Uri.parce()を使えばいいと書かれていましたがどうも現在は使われていないらしい…

https://docs.microsoft.com/ja-jp/dotnet/api/system.uri.parse?view=netframework-4.8

普通に検索してると代替がなかなか見つからなくて困りましたが・・・リファレンスを見ると、

https://docs.microsoft.com/ja-jp/dotnet/api/system.uri?view=netframework-4.8

単純にUri(string,UriKind)で変換できました。リファレンスをまず確認すべきだった。

 

次に突き当たった問題は親ウィンドウ→子ページと参照していると、親のコントロールのプロパティを子が変更できない、というもの。

子ページ内のボタンをクリックすると親ウィンドウのframeのプロパティが書き換えられる、という動作を作りたい

 

子が新しく親のオブジェクトをnewか何かで宣言すると、その変更はできるんですがそれは親自体には何の影響も及ぼさないわけです。

(親→(子→(孫)))という構造で、括弧の外にでることができないわけです。

 

結局frame内に外部から参照可能な変数を定義し、その変数の変更を親ウィンドウが検知したときにframe.sourceを書き換えるという処理を行うという方法をとることに。

 

絶対もっとスマートな方法があるはずですが…とりあえず動く!

 

アルゴリズムを考える

 

次に具体的なアルゴリズムを考えていきます。SRSを(大まかに!)利用する点、スケジュールを自動設定する点は基本として、いろいろな要素を付け加えていきたいのですが・・・。まずは基本的なところから順番にやっていこうと思います。

①カード作成時

カード作成時、まず、ユーザーはフィールドを入力します。

(ここでリストをコピペして、複数枚のカードの同時入力ができれば便利ではないでしょうか・・・?)

ここでの入力内容をcards/fieldsに格納。同時にid,nid,didも自動的に入力されます。カードのlearningの初期状態は0,学習中だと1,期限がある程度を超えれば2とします。(将来的にはデッキ内の2の割合、あるいは絶対枚数が一定を超えるなどでメダルを獲得できるようにしたいです。するとメダルの情報を保存したテーブルも必要に)

続いて

②学習時

学習時に、ユーザーは記憶/忘却情報を入力します。試験などでは一定期間内に思い出すことも必要となりそうなので、タイマーも導入したいところです。フィールドの1つとしてlimit timeを設定し、その時間内の正答のみを正答と扱うのではどうでしょうか。英単語を一つ思い出すのに1分以上かかるようでは、英文は読めないので。

また、ankiの場合は自己判定で行いますが、フィールド入力も導入することはできないか、考える余地がありそう。例えばりんご→appleといったレベルの学習では実際に入力したほうが記憶に定着しそうですし、また正解時にピンポーン、といったSEが鳴ったほうが楽しそうです。

また、漢字などを覚える際にもフィールドへの書き込み(ペンタブなど使用)を導入したいですが、自動判定は難しいので自己判定でできないでしょうか…。また、失敗時の画像を保存しておいて、前回失敗時の画像を表示するといったことも記憶の補助になると思います。

としても、これはサブ的な用途なので後回しで、基本はフィールド内になんらかの入力フィールドの用意を可能にする、という方向でいきたいです。

③学習後

入力した記憶/忘却情報に基づいて、いくつかの処理を行います。すなわち、失敗したカード→翌日復習、成功したカード→むずかしさに応じて次の期間を設定、また、正答、誤答履歴を書き込み、期限のカード枚数を減らし、次回復習期日が1か月以上ならばLearningを2にする、といったようなもろもろの処理を行っていきます。

④保存はどのタイミングで行うか?

10、多くとも50個復習くらいのタイミングでセーブしておかないと、さすがにやる気が失せるのでそれ位は必要だと思います。

 

 

 

 

テーブルの形式を画定する

今回はテーブル形式をどうするべきか考えてみます。

とりあえずUnixTimeを使用してIDを定めるとして、このIDもやはりAnkiを参考にして、

①カードID

②ノートID

③デッキID

は必須のものとして、更にノートID毎に④テンプレートIDを対応付けるという形式になりそうです。

またankiではnotesのfldsに、すべてのフィールドを特殊文字で区切って配置しているのですが、これにはフィールドが増えても対応しやすいというメリットがありそうです。

同期関連のデータはとりあえずは必要がなさそうですが、ivlやlapseといったスケジュール計算に必要になりそうです。

今のところは正答率と誤答率からインターバルを算出し、期限が到来したカードで早いものから表示していく(復習し忘れたカードを優先する)というアルゴリズムを考えています。

それを考えると必要になりそうなのは、

⑤インターバル(マイナス符号は分、プラス符号は日付)

⑥フィールド(問題文の本文)

⑦factor→ankiでは、「普通」をクリックしたときのインターバルの伸びを格納しています。例えばインターバルが10日の時に「普通」を選び、その時のfactorが2500なら、次の期日は25日後になるという具合です。

チェックサム//ankiではこれでカードの重複判定を行っています

あたりでしょうか。

⑨カード参照//やっと出てきたオリジナル要素ですが、これは次の記事で説明します。

 

 

ankiのデータを見ていく

はじめに

このブログは個人的なプログラミング学習の備忘録として使っていきたいと思います。

プログラミングは1週間ほど前に始めたばかりなので手探りです。

当面の目標としてankiみたいなソフトを作ってみることを目指してみます。

ankiというのは記憶補助アプリケーションで、いわゆる暗記カードを、忘却曲線に従って出題することで、効率的な学習を可能としています。

導入はこちら:

http://rs.luminousspice.com/how-to-anki/

僕もこのアプリケーションを愛用しており、英単語や雑学などをデッキに入れているのですが、より自分用にカスタマイズしたいと考えてみました

データの保存場所等

まずデータはユーザー名/AppData/Roaming/Anki2/(Ankiのユーザー名)に保存されています。以下のようなファイル構造になっています。

f:id:xrshosho:20190803090533p:plain

collection.mediaは画像・音声などのメディアが、backupsはそのとおりバックアップが保存されているフォルダです。

データベースが保存されているのがcollection.anki2です。

collection.anki2はSQLite3形式のデータベースファイルなので、適切なソフト(pupsqliteなど)で内部を閲覧することができます。

(また、backupsの中にあるファイルはzip圧縮されたanki2ファイルです。backupsにおけるデータベースはcolpkg形式、ankiのエクスポート機能を用いて生成されたデータベースはapkg形式ですが、どちらもzip解凍が可能です。)

データベースの内容

で、データベースを見ていくわけですが、以下のようなテーブルがあると思います。

f:id:xrshosho:20190803091300p:plain

で、テーブルを開いていくと、どうやらnotesにノートがあることはわかりますが、それ以外はさっぱりなのでgithubを参照します。

https://github.com/ankidroid/Anki-Android/wiki/Database-Structure

ここのdatabase schemaにデータベースの説明が書かれています。

とりあえず上から見ていくと、cardsの上3つ、id,nid,didはカード作成時の時間から生成されたid,ノートID、デッキIDのようです。

ankiのシステムについて

このIDを説明する前にankiのシステムについて説明しておきます。

まず、ユーザーは「デッキ」を選択し、表示された「カード」に対して覚えている/いないの選択をします。それにより、そのカードの次回出現までの間隔がソフトによって自動で計算されます。

一方、ユーザーはカードを作成することもできます。この場合、まずデッキを選択(または作成)し、「ノートタイプ」を選択し、フィールドにパラメータを入力し、追加することでカードが作成されます。

そして選択したノートタイプによっては、一つのフィールド追加で2枚以上のカードが作成されるものもあります。例えば、漢字の読みを覚えたい場合、

表面:(漢字) 裏面:読み

というカードを作成しますが、そのあとで漢字の書きを覚えたい場合、いちいち

表面:読み 裏面:(漢字)

のカードを作成するのは面倒です。この場合、ankiはそのカードが使用しているカードのノートタイプを編集し、表面と裏面のフィールドを入れ替えたノートタイプを追加することで一気に作成することができます。

id,nid,did...

以上の説明より、個別のカードに対応するのがid,のちに出てくるnotesテーブルのidに対応するのがnid(つまり個別のfieldのセットに対応するものです),そのカードが所属するデッキに対応するのがdidということになります。

つづいてordはノートタイプ編集画面の

f:id:xrshosho:20190803093629p:plain

カードタイプが複数存在する場合、その順番に割り振られた番号を表しています。

modはカードが編集された時間、usnは同期時に更新する必要があるか、等等。

以上のように、cardsテーブルはカードの属性を格納しているようです。

 

colテーブルは、コレクションに関するデータを格納しています。

gravesは削除カードを格納しているようです。

 

notesはid,syncの時に使うグローバルID、モデルID(mid)、編集時刻modなどに続いて存在する、fldsがフィールドを保存しています。複数のフィールドが存在する場合は、

0x1f (31)

で区切っているようです。

revlogはレビューログ、つまり復習記録です。すべてのレビューデータを保存していることがankiの長所です。

 

 

ここまでで、カードに関する大体のデータとその保存場所はわかりましたが、ノートタイプはどこに…と探していくと、どうやらcolテーブルのmodelに格納されているようです。

modelid(cardsテーブルのmid)やtmpls(ノートタイプのテンプレート)が確認できます。

まだどのデータを使うかは決めてないので、とりあえず今回はここまで。