1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SlintAdvent Calendar 2024

Day 22

Slint で書いたログイン画面を bash から表示する

Last updated at Posted at 2024-12-21

はじめに

この記事は Slint Advent Calendar 2024 22日目の記事です。

昨日は @hermit4 さんによる Slintのバックエンドとレンダラー でした。

公式ドキュメントの Backends & Renderers の内容が日本語でよくまとまっていてとてもありがたいです。

Slint をシェル(bash 等)から動かそう

Slint ではフロントエンドを .slint ファイルに記述し、Rust や C++、JavaScript、Python で書かれたバックエンドから利用するのが一般的なやり方です。

Slint には Slint Viewer と呼ばれる .slint ファイルのビューアーが用意されていて、それを利用することで、簡単なものであれば(もしくはすごく頑張れば)シェルスクリプトでも GUI アプリケーションの作成が可能です。

公式のサンプルアプリにも Slint Bash example というものがあり、今回はこれをアレンジして遊んでいます。

Slint Viewer の仕様

Slit Viewer は Rust のパッケージマネージャーを利用して、以下のコマンドでインストールが可能です。

$ cargo install slint-viewer

以下の引数が利用可能です。

  • --auto-reload: Automatically watch the file system, and reload when it changes
  • --save-data <file>: When exiting, write the value of public properties to a json file. Only property whose types can be serialized to json will be written. This option is incompatible with --auto-reload
  • --load-data <file>: Load the values of public properties from a json file.
  • -I <path>: Add an include path to look for imported .slint files or images.
  • -L <library=path>: Add a library path to look for @library imports.
  • --style <style>: Set the style. Defaults to native if the Qt backend is compiled, otherwise fluent
  • --backend <backend>: Override the Slint rendering backend
  • --on <callback> <handler>: Set a callback handler, see callback handler
  • --component <name>: Load the component with the given name. If not specified, load the last exported component

シェルスクリプトで GUI を表示したい場合の入出力は --load-data--save-data で行うことができます。

main.slint
export component MyApp inherits Window {
  callback open-url(string);
  //...
}

のようなコールバックが呼ばれた際の処理は以下のように行うことが可能です。

$ slint-viewer --on open-url 'xdg-open $1' main.slint

表示するエレメントが Dialog を継承している場合には、利用する StandardButton によって slint-viewer の終了コードが以下のように変わります。

  • okyesclose の場合は正常終了(=0)
  • cancelno の場合は異常終了(=1)

.slint で ログイン画面を作る

ユーザー名とパスワードの入力ダイアログを以下のように作りました。

login.slint
import { LineEdit, VerticalBox, GridBox, StandardButton } from "std-widgets.slint";

export component LoginForm inherits Dialog {
    title: "Login";
    preferred-width: 300px;
    forward-focus: username;
    GridBox {
        Row {
            Text { text: @tr("Username:"); }
            username := LineEdit {}
        }
        Row {
            Text { text: @tr("Password:"); }
            password := LineEdit { input-type: InputType.password; }
        }
    }
    StandardButton { kind: ok; enabled: username.text != "" && password.text != ""; }
    StandardButton { kind: cancel; }
}

SlintPad で動作確認することが可能です。

シェルスクリプトを作成する

上記の login.slint と同じディレクトリに以下の login.sh を作成します。

login.sh
#!/bin/sh

slint-viewer `dirname $0`/login.slint

実行してみましょう。

$ chmod +x login.sh
$ ./login.sh

image.png

ちゃんと画面が表示されましたね。

入力結果の取得

それでは次に、入力された値を取得してみましょう。

login.sh
#!/bin/sh

JSON=$(slint-viewer --save-data - `dirname $0`/login.slint)
if [ $? -ne 0 ]; then
    echo "Failed to login"
    exit 1
fi
echo $JSON

--save-data オプションに - を渡すことで、.slint からロードされた
コンポーネントのルート要素のパブリックなアウトプット対応のプロパティの情報が JSON 形式で標準出力に出力されます。

.slint 側に出力するプロパティを作成しましょう。

login.slint
export component LoginForm inherits Dialog {
    ...
    out property<string> username <=> username.text;
    out property<string> password <=> password.text;
    ...
}

プロパティの宣言に、出力用の out をつけています。

image.png

$ ./login.sh
{ "password": "p@ssword", "username": "user" }

コマンドラインの JSON のパーサー jq を利用して、スクリプトの変数に代入してみましょう。

login.sh
#!/bin/sh

JSON=$(slint-viewer --save-data - `dirname $0`/login.slint)
if [ $? -ne 0 ]; then
    echo "Failed to login"
    exit 1
fi
USERNAME=$(jq -r ".username" <<< "$JSON")
PASSWORD=$(jq -r ".password" <<< "$JSON")

echo "UserName: $USERNAME"
echo "Password: $PASSWORD"
$ ./login.sh
UserName: user
Password: p@ssword

初期値の設定

毎回ユーザー名を入力するのが面倒くさいので、whoami の結果をデフォルトとして設定しましょう。

login.sh
#!/bin/sh

USERNAME=$(whoami)
JSON=$(slint-viewer --load-data - --save-data - `dirname $0`/login.slint <<EOF
{
    "username": "$USERNAME",
    "password": ""
}
EOF
)

...

.slint 側も、入力を受け付けるように変更しましょう。

login.slint
export component LoginForm inherits Dialog {
    ...
    in-out property<string> username <=> username.text;
    in-out property<string> password <=> password.text;
    ...
}

image.png

おわりに

今回は Slint のバックエンドのロジックをシェルスクリプトで書いて遊んでみました。

簡単なものなら簡単に作れてとても便利な気がします。

明日は @rarirure さんによる RustとSlintで作る動的棒グラフ・ゲージチャート です。お楽しみに!

余談

今回使用した .slint のコードでは、ユーザー名とパスワードの入力状態に応じて OK ボタンの有効無効を切り替えています。

    StandardButton { kind: ok; enabled: username.text != "" && password.text != ""; }

.is_empty() と書けたらよかったんですが、そういう機能は無かったので、空文字列との比較で実現しています。

また、ユーザー名もパスワードも通常は文字数の制限(とくに下限)があるので、文字数に応じた処理を行いたかったのですが、len()length() のような機能もなく、今回は見送りました。

そういうのが用意されていない理由があるのか、ちょっと調べたけど分からなかったので、(バグレポ書いたり議論したりをすっとばしていますが)以下のプルリクエストを作成してみました。

メインの開発者は大体ドイツに住んでいるようで、もうクリスマス休暇で仕事はしてないっぽいので、年が明けたらレビューしてもらって、できれば本体に取り込んでもらいたいなと思っています。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?