こんにちは。
今回は新しくなったWriter Framework(旧Streamsync)を使って簡単なToDoアプリを爆速で作ってみましたので、その作り方についてご紹介します。
本記事の趣旨として
- Writer Frameworkの使い方について、入門から一歩進んだ使い方を紹介する
- Writer Frameworkが最近バージョンアップしたので、新しい機能を紹介する
- Writer Frameworkならこんなに簡単に爆速でアプリが作れることを紹介する
上記3点をポイントとして紹介しようと思います。
過去に入門用の記事も出していますので、Writer Frameworkの基本的な概要についてはこちらをご覧ください。
Writer Frameworkをより実践的に使えるようになりたいという方々の参考になれば幸いです。
それではよろしくお願いします。
Writer Frameworkをインストールする
前回の入門では、streamsyncをインストールしていましたが、今回からはWriter Frameworkをインストールしましょう。
お使いの仮想環境やコンテナ上で以下のコマンドでインストールできます。
pip install writer
これをインストールするだけで、numpyやpandasなど必要なライブラリもインストールできます。
私のバージョンは0.7.2(2024年9月2日時点)になりますので、完全に同じ動作にしたい場合は
pip install writer==0.7.2
でインストールしてください。これで準備は完了です。
Writer Frameworkを作成・起動する
作成・起動方法はstreamsyncと同様です。
まずはhelloプロジェクトを試しに作成・起動してみましょう。
writerをインストールした環境で以下のコマンドを入力します。
writer hello
前回同様helloフォルダが生成され、ターミナル(ちょっとバグったような表示が出ますが問題なし)にURLが表示されるのでアクセスしましょう。

URLにアクセスすると、以下のような画面が表示されます。

全体的に雰囲気が変わりましたね!
特にハトのイラストが新しくなったようです。
ToDoプロジェクトを作成する
ではhelloプロジェクトはCtrl+Cで止めて、必要なければ削除してください。
今からToDoアプリを作るためのプロジェクトを作成します。
writer create todo_app
これでtodo_appフォルダが自動で作成されます。

ここで注目したいのが、pyproject.toml(poetry)が追加されているんですよね。
poetryはPythonのパッケージマネージャーの一つで、いわゆる仮想環境の作成・管理ができるツールです。
今後検証・調査してみますが、おそらくプロジェクト毎にライブラリを指定できるようになると思うんですよね。
先ほどのフロントエンドのデザインだけでなく、新しい機能が少しずつ増えていることは今後も注目です。
では編集画面を起動します。以下のコマンドで編集画面を立ち上げます。
writer edit todo_app

おなじみの画面ですね。やはりデザインの雰囲気が変わりました。
そしてやっぱりこのページはいらないので、前回同様消しましょう。
ページ全体のところをクリックして、青く選択できたら赤いゴミ箱マークをクリックして削除です。

そしてmain.pyの方もinitial_stateのみ残して他は全部消しましょう。

これでプロジェクトの基本準備はOKです。
ToDOアプリの要件定義
では今からどんなToDOアプリを作るか、簡易的な要件を定義します。
- 日付とタスクがリストで見られること
- タスクを追加したり削除したりできること
最低限の機能としてはこんなところでしょうか。
もちろんそれなりに実用性を考えれば様々な要件や機能が必要になりますので、ぜひご自身で色々試してみてください。
実装
Writer Frameworkはデータアプリの側面で強みを持つため、今回はDataFrameを使って作ってみようと思います。
では真っ白なWEB画面に戻って、左下の「+ Add Page」を押してページを追加します。

HeaderとSectionを入れましょう。タイトルも好きなものを入力してください。

続けてSectionの中にDataFrameとHorizontal Stackを追加します。

Horizontal Stackの中にDate inputとText inputを入れます。
Horizontalなので、縦に並ばず横に並んでくれます。
ついでにラベルも書いておきましょう。

Date inputはカレンダーのアイコンを押すと、普段アプリでよく使うような日付のカレンダーで選べます。

こんな便利な機能がドラッグ&ドロップ一発で実装できるのはお手軽ですよね。
ではSectionの外に、Horizontal Stackを置き、その中にButtonを2つ置きましょう。
このButtonのラベルも「追加」「削除」としておきましょう。

これでもボタンとしては十分なのですが、右側のicon: Text欄にMaterial Symbols idを入力するとアイコンを追加することができます。
追加ボタンには「add」と打つと+マークが、削除ボタンには「delete」と打つとゴミ箱のマークが追加されます。

これでフロントエンドの最初の準備はOKです。
続いてmain.pyに戻りまして、以下のようにします。
initial_state = wf.init_state(
{
"my_app": {"title": "MY APP"},
"df": pd.DataFrame(
{
"日付": ["2024-9-1", "2024-9-2", "2024-9-3"],
"タスク": ["買い物", "掃除", "勉強"],
},
),
"get_date": "",
"get_task": "",
}
)
今から管理するDataframeの初期値を定義しました。
ではこれを先ほどのフロントエンド側のDataframeに反映させるためにはどうすれば良いか。
WEB画面に戻り、DataframeのData欄に「@{df}」と入力しましょう。
はい、ここで新しい機能に注目です!

なんと入力途中で候補を表示してくれるようになりました。
以前は何も出なかったので、作っていてうまく反映されるか不安でしたが、これはすごく安心ができますね。
しかも型まで教えてくれるので、開発者にとっては嬉しい機能となります。
「@{df}」と入力すると、初期値がアプリに反映することが確認できると思います。

では次にこのDataframeに追加する機能を実装しましょう。
追加ボタンを押したら日付とタスクが追加されるようにします。
その際、日付とタスクどちらか入力がないと追加できない仕様とします。
では入力した日付をバックエンドで受け取れるようにするためにはどうすれば良いか。
Date inputのEventハンドラを使う必要があります。
Eventの横に?マークが○で囲んであるアイコンがあるので、それをクリックします。
すると以下のようになります。

このコードをコピーして、main.pyに貼り付けます。
def onchange_handler(state, payload):
# Set the state variable "new_date" to the new value, provided as a YYYY-MM-DD string.
state["new_date"] = payload
これを次のように編集します。
def get_date(state, payload):
state["get_date"] = payload
state[“get_date”]に入力した値が格納される関数です。
Text inputも同様なので、タスク側も受け取れるようにします。
def get_task(state, payload):
state["get_task"] = payload
ここまでできたらmain.pyを上書き保存します。
そしたら先ほどのEventハンドラに戻り、wf-date-changeを見ると、get_dateとget_taskが選べるようになっているので、get_dateを選びます。

タスク側も同様にwf-changeにget_taskを選択します。

これでデータが入力されたら、入力値をバックエンド側で受け取ることができるようになりました。
ではその入力値を使ってDataframeに追加していきましょう。
main.pyに以下の関数を追加します。
def add_df_loc(state):
if state["get_date"] == "" or state["get_task"] == "":
state.add_notification(
"warning",
"追加情報に記入漏れがあります.",
"日付とタスクを入力してください.",
)
return
state["df"] = pd.concat(
[
state["df"],
pd.DataFrame({"日付": [state["get_date"]], "タスク": [state["get_task"]]}),
]
)
日付とタスクに入力がない場合にはwarningを出して追加できないようにしています。
あとはpandasのconcatを使って行を追加します。

では追加機能が上手く動くか確認してみましょう。
このままでも確認できますが、左上のタブの「preview」で確認してみましょう。

では空白だとどうなるでしょうか?

追加できないことを通知してくれますね。
後は削除機能の実装ですが、これも追加と似たようなものです。
今回は簡単に行うため、最後の行を削除する仕様とします。
main.pyに以下の関数を追加します。
def delete_df_loc(state):
state["df"] = state["df"].iloc[:-1]
delete_df_locを削除ボタンのEventハンドラに選択すれば、一番下の行が削除できます。
これでToDoアプリは完成となります。
補足:Dataframeの機能について
Dataframeを眺めると、インデックス番号がうっすらと見えるかと思います。
これを非表示にすることが可能で、Show Indexをnoにすると非表示にできます。

その下にEnable searchとEnable downloadがあるので、これらもyesとすると検索機能やDataframeのダウンロード機能が追加できます。

yesかnoを入力するだけで実装できるのはとても簡単で便利ですね!
まとめ
今回は新しくなったWriter Frameworkで簡単なToDoアプリを作成してみました。
今回作成したコードは以下のGitにアップしていますので、こちらもご参考ください。
Writer Frameworkも少しずつ新しい便利な機能が追加されてるようです。
また本記事では伝わりにくいですが、UIのオブジェクトの選択が以前よりもしやすくなっていたりと地味にユーザビリティの向上・改善が見られるのも印象的でした。
直感的に扱えるようになるには、ある程度慣れが必要なツールですが、コツを掴めば時間も手間も少なくアプリが作れる便利なフレームワークです。
今後も定期的に更新されたタイミングで触ってみますので、また面白い情報が分かりましたら紹介します。
ここまでご覧いただきありがとうございました。