概要
Anvil は Python でコーディングする、Web ベースの統合開発環境です。
1個のプロジェクトが以下のような構成になっています。
- Anvil プロジェクト
- Client Code (UI のデザインとコード)
- Server Code (サーバサイドのコード)
- Services (連携するサービス (DB など))
この構成を理解するには、DB の基本操作 (CRUD) を実装するチュートリアル News Aggregator Tutorial を学ぶのが手っ取り早いです。
このチュートリアルのプロジェクトを少し改造して、クイズ集アプリのようにしましたので、その時のメモをご紹介します。
前提
News Aggregator Tutorial をやった前提で説明します (プロジェクトを作るには Anvil へのサインアップが必要です)。
News Aggregator Tutorial ではニュースデータを扱いますが、この構造をそのまま使ってクイズ集アプリとしました。
変更点
リネーム
扱うデータが (構造は同じですが) ニュースからクイズに変わりますので、分かりやすくリネームしました。
- プロジェクト名
- 何でも良いですが、'Quiz' としました。
- フォーム名
- 'ArticleEdit' から 'QuestionEdit' に変更
- 'ArticleView' から 'QuestionView' に変更
- データテーブル名
- 'articles' から 'questions' に変更
- カラム名 (および関連するデータバインド)
- 'title' を 'question' に変更
- 'content' を 'answer' に変更
- 他にもフォームのコンポーネントやイベントハンドラ名などを以下のように変更
- 'articles' → 'questions'
- 'title' → 'question'
- 'content' → 'answer'
身も蓋もないことを言うと、あとは入力するデータの内容を「質問」と「答え」にしさえすれば、クイズ集アプリになります。
とはいえ、気になるところもあるので、もう少し改造してみました。
QuestionView
QuestionView フォームに、以下の変更をしました。
「問題」「答え」ラベルを追加
「タイトル」と「記事本文」を「問題」と「答え」として使うので、それぞれ「問題」と「答え」であることを示した方が良いと思い、ラベルを追加しました。
やり方は Toolbox から Label をドラッグ & ドロップするだけです。
Column Panel や Flow Panel を使ってレイアウトの調整もします (なかなか思い通りに行きませんが…)。
答えの表示/非表示を切り替える
クイズなのに答えが丸見えだったので、答えの表示/非表示を切り替えるようにしました。
Toolbox から CheckBox をドラッグ & ドロップし、名前を answer_check
としました。
初期状態はチェックがはずれた状態とし、change
イベントを追加しました。
イベントハンドラを以下のように書いて、クリックするたびに答えの表示/非表示が切り替わるようにしました。
def answer_check_change(self, **event_args):
"""This method is called when this checkbox is checked or unchecked"""
self.answer_label.visible = self.answer_check.checked
答えの方も初期状態を非表示にしました。
カテゴリの初期値
チュートリアルをなぞっただけでは、新規レコード作成時に何もせずに 'SAVE' を押すとエラーになります。
これは、カテゴリが外部参照なのに値が入っていないからです。
None を許容する方法や、フォームを閉じる前にバリデーションする方法が分からなかったので、初期値を与えることで (強引に) 解決しました。
初期値を与えるには、問題を作成するボタンのイベントハンドラ (Homepage
フォーム) を以下のようにしました。
変更前
def add_question_button_click(self, **event_args):
"""This method is called when the button is clicked"""
new_question = {}
save_clicked = alert(
...
変更後
def add_question_button_click(self, **event_args):
"""This method is called when the button is clicked"""
default_category = anvil.server.call('default_category') # 追加
new_question = {'category': default_category} # 変更
save_clicked = alert(
...
サーバ側のコードにカテゴリの初期値を取得する関数を追加 ('Python' というカテゴリが存在する場合)
@anvil.server.callable
def default_category():
return app_tables.categories.get(name='Python')
シャッフルボタン
できれば、問題が1個出て答えると次の問題が出る、というフローにしたいところですが、改造の域を超えそうなので、シャッフルボタンをつけてお茶を濁しました。
仕様としては、以下のようにしました。
- シャッフルボタンを押すとカードの並びがランダムになる。
- 同時にシャッフル解除ボタンが有効になる。
- シャッフル解除ボタンを押すと、DB から取得したままの順になる。
- 同時にシャッフル解除ボタンが無効になる。
- 編集をした時などは、DB から取得した順になる。
- シャッフル解除ボタンは無効になる。
ボタンを追加
シャッフルボタンとシャッフル解除ボタンを追加して、それぞれ shuffle_button
、unshuffle_button
としました。
コードの追加・変更
シャッフルボタンのクリックハンドラ
def shuffle_button_click(self, **event_args):
questions = self.questions_panel.items
self.questions_panel.items = random.sample(questions, len(questions))
self.unshuffle_button.enabled = True
シャッフル解除ボタンのクリックハンドラ
def unshuffle_button_click(self, **event_args):
self.refresh_questions()
refresh_questions()
メソッド (元々 refresh_articles() だったもの) も変更
def refresh_questions(self):
self.questions_panel.items = anvil.server.call('get_questions')
self.unshuffle_button.enabled = False # 追加
デモ
以上の変更によりできたクイズ集アプリを、以下の URL で公開しています。
https://Z7NSR2ZWKFYP6CJK.anvil.app/OGKX5IPZ2SLK3TKVBPVJLZGH
所感
まだなかなか、思ったことがすぐ出来る (すぐやり方が見つかる) という感じではないです。
Anvil の UI を思い通りにデザインしようとすると道は険しそうです。
UI を極めるよりは、次は Anvil Uplink という仕組みを試してみたいです。
Anvil Uplink は、ローカルで動いているモデル等を Web アプリから (API サーバみたいな感じに?) 使える仕組みのようです。