はじめに
スリントアドベントカレンダーも無事に埋まって15日の日曜日を迎えました。すでに月曜日だろって…ごめんなさい。いやぁ、結局金曜日から数えで3日連続忘年会になってしまって…今日は空いてた予定だったのですけどねぇ。
昨日は、@task_jpさんによる「Linux で Slint にカメラの映像を表示する」でした。gstreamer-rsと組み合わせてカメラ入力をslintに渡すという処理ですね。Rustはクレートがどんどん増えてきており、様々なクレートを組み合わせてアプリを作れるのが素敵ですよね。
本日のお題
さて、Slintを流行らせたいなぁと技術同人誌のためにと称してアドベントカレンダー記事を書いてるもののいまいちViewも伸び悩んでいてどうしたものかねと再度友人に相談したら、マイコンでいきなりSlintのタグで追いかけ始める人もマイコンでどうこうって言われて食いつく人も人数的にはレアだろって怒られました…あ、はい。凹みました。
ちょっと書きかけてた別の記事もあったのですが、先に書くことがあるだろうということで、本日はRustを始めよう、ついでにUIのためslintも一緒に触ってみようかなと言う人が開発環境構築を構築できるように開発何橋構築回とします。あと、同人誌もSlint言語入門でやってましたが、おそらく方向転換してRustでSlintをやれるようにする方向で頑張ってみようかなと思っています。
カレンダーの方は埋まらなさそうだし、Slint言語入門編も続き書きますよ。組込みエレメントとかウィジェットも紹介しておかなくてはですし。同人誌にするときは全部まとめるかと思いますけど。多分先にこちらの環境構築、Rustで簡単なUI作成解説、Slint言語入門、簡単なテーマ決めて実際にアプリ開発な方向ですかねぇ。
爆死しそうなテーマなのにページ数が増えそうなんだけど。でも、技術同人誌書くならせっかくなら紙も刷りたいんだよねぇ…本業でもっと稼がなきゃ。
RustでUI - Slint開発環境構築
Slintとは
SlintはRustで実装されたGUIフレームワークです。軽量コンパクトが売りで、UI記述用の独自の宣言型言語を提供しています。より詳しくは、@task_jpさんの「GUI フレームワーク Slint の紹介」を読んでみてください。Rustで実装されているというだけで、Rustの他、C++、NodeJS(ベータ版)、Python(アルファ版)で利用できます。
Rustとは
Rustは、安全性とパフォーマンスを両立したモダンなシステムプログラミング言語です。メモリ管理について今までの言語とは異なる新しいアプローチを取っており、言語的に強い制約を設けることで、コンパイル時にメモリの使用を追跡し、不正な利用を自動的に検知できるよう設計されています。
またメモリの使用に関するチェックをコンパイラが行うことでスレッドセーフなコードを効率的に書くことにもつながり並列性のサポートに優れています。
モジュール管理、エラー処理、クロージャ、パターンマッチ、イテレータ、高機能なマクロ等モダンな機能が多く取り入れられており、またビルドシステムとパッケージマネージャーを兼ねるcargoのような優れたツールが用意されています。まだ若い言語であるためライブラリやツールチェーンが未成熟ながらも急速にエコシステムの成長が続いています。
コンパイラによる追跡を可能とする厳しい言語的な制約のため学習コストが高いというデメリットはあるものの、安全性とパフォーマンスを両立する言語としての注目度は高く、Linuxカーネルドライバの開発言語としても採用され、信頼性が求められる開発においてこれから重要な役割を果たしていく次世代のシステムプログラミング言語として高い注目を集めています。
環境構築
本ドキュメントでは、RustとVisual Studio Codeを使いSlintでUI開発を行う環境を構築します。
とりあえず今回はLinuxでKubuntu24.04環境についてだけ記載します(執筆準備で環境を追加したら加筆・修正するかもですが)。
PRETTY_NAME="Ubuntu 24.04.1 LTS"
NAME="Ubuntu"
VERSION_ID="24.04"
VERSION="24.04.1 LTS (Noble Numbat)"
VERSION_CODENAME=noble
ID=ubuntu
ID_LIKE=debian
HOME_URL="https://www.ubuntu.com/"
SUPPORT_URL="https://help.ubuntu.com/"
BUG_REPORT_URL="https://bugs.launchpad.net/ubuntu/"
PRIVACY_POLICY_URL="https://www.ubuntu.com/legal/terms-and-policies/privacy-policy"
UBUNTU_CODENAME=noble
LOGO=ubuntu-logo
OS 追加パッケージ
いらないものも含まれているかもしれませんが
sudo apt install build-essential libfontconfig-dev libxcb-shape0-dev \
libxcb-xfixes0-dev libxkbcommon-dev libglu1-mesa-dev libudev-dev \
cmake ninja-build lldb openocd gdb-multiarch curl
Rust環境構築
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
途中のプロンプトはデフォルト(そのままenter)後、bashrcを読み直しておきます。
source ~/.bashrc
Rust コンポーネント追加
rustup component add rust-analysis rust-src rust-analyzer
Cargoで追加のアプリインストール
cargo install cargo-generate cargo-binutils slint-viewer slint-lsp slint-updater
Visual Studio Code環境構築
インストール
wget -q -O - https://packages.microsoft.com/keys/microsoft.asc | gpg --dearmor > microsoft.gpg
sudo install -o root -g root -m 644 microsoft.gpg /etc/apt/trusted.gpg.d/
sudo sh -c 'echo "deb [arch=amd64] https://packages.microsoft.com/repos/vscode stable main" > /etc/apt/sources.list.d/vscode.list'
sudo apt update
sudo apt install code
日本語化
- Ctrl + Shift + P
- displayの候補からConfigure Display Languageを選択
- 日本語を選択
- ダイアログでrestart実行
拡張のインストール
Ctrl + Shift + X で拡張機能を選択。何を入れるか個人の自由なんですがRust+Slint開発で便利なものだけ簡単に紹介しておきます。
CodeLLDB
デバッグサポート用の拡張機能
rust-analyzer
公式のLanguage Server Protocol拡張です。コード補完や定義などへのジャンプ、アシスト機能などが有効になります。rust-analyzerを使ってみたら思いのほか素敵だったので紹介してみるという紹介記事があったので、詳しくはそちらを見たほうが良いかと思います。
Rust Syntax
Rustのセマンティックハイライト機能です。rust-analyzerにもセマンティックハイライトは入っていますが、そちらより優れているそうです。最初から入れて使っているので違いを把握していないのですけどね。
Rust Doc Viewer
cargo docのドキュメントをローカルで生成してVisual Studio Codeのタブで表示できるようになります。
Even Better TOML
Tomlとかを書くときに便利
Dependi
プロジェクト依存関係管理ツール。Cargo.tomlなどのdependenciesのバージョン周りの確認やリスト表示が行えます。
Markdown All in one
Readme.mdのためには必要だよね。
Markdown Preview Enhanced
slint
今回の本命。Slintのライブプレビューやシンタックスハイライト
拡張インストールすると英語ですがチュートリアルが出るかと思います。画面的にはこんな感じでハイライトしてくれて、エディタ中に挿入されている"▶Show Preview"を押すとプレビューウィンドウが開きます。
Styleを変更してプレビューしたり、エディットモードである程度デザイナとして利用することも可能です。
まずはslint templateを使ってみる
- Ctrl+Shift+` でターミナルを開く
- 任意のフォルダへ移動する
- 以下のコマンドを実施する
cargo generate --git https://github.com/slint-ui/slint-rust-template Project Name: hello code -r hello
いつもこのようにコマンドプロンプト経由で生成してるのですが、もっとイケてる方法知っている方は教えてください。ともあれ、これで再起動されてhelloをプロジェクトとして開いてくれます。
エディタ中に挿入されている"▶Run"の方を押すとビルド後に実行されます。
デモとしては、increaseボタンを押すと画面上のカウンタが増加するというシンプルなものです。
#Templateの中身を見て見る
あらかじめUI用の.slintファイルが分割されたプロジェクトとなっています。slint compilerにビルドすべきファイルを通知するようになっています。
fn main() {
slint_build::compile("ui/app-window.slint").unwrap();
}
これは、slint言語ファイルをビルドするためのコードです。ファイル名を変えたい場合はここも修正が必要です。
import { Button, VerticalBox } from "std-widgets.slint";
export component AppWindow inherits Window {
in-out property<int> counter: 42;
callback request-increase-value();
VerticalBox {
Text {
text: "Counter: \{root.counter}";
}
Button {
text: "Increase value";
clicked => {
root.request-increase-value();
}
}
}
}
実行時に表示されるダイアログと見比べて見てください。ものすごく単純化して画面パーツだけ抜き出すと
Window {
Text {
"Counter: \{root.counter}"
}
Button {
"Increase value"
}
}
のような構成になっているのがわかるでしょうか。GUIの構成をそのまま宣言していくので、Slint言語自体に精通していなくても雰囲気でこんな画面かなというのが見えますよね。
たとえばQtのuiファイルだとXMLなのでデザイナーを使わないと画面の構成がわからなくなります。ビルドしたソースコードはさらに命令文で羅列されるされるためどんな画面なのか動かしてみないと想像できなくなります。
それに比べるとSlintのような宣言型UI言語はシンプルなのでデザイナツールに頼らなくても編集できますし、ある程度画面を構成するパーツも理解することができます(まぁ、この規模だからというのはありますが)。
slint::include_modules!();
fn main() -> Result<(), slint::PlatformError> {
let ui = AppWindow::new()?;
ui.on_request_increase_value({
let ui_handle = ui.as_weak();
move || {
let ui = ui_handle.unwrap();
ui.set_counter(ui.get_counter() + 1);
}
});
ui.run()
}
このテンプレートは、Hellow Worldより一歩進んで、RustとSlint間のやり取りも含んだテンプレートになっています。callbackはon_callbacknameという形で、in-outのプロパティはgetとsetが利用できるようになります。これらをRust側で実装してUI側とやり取りするのが見て取れるかと思います。
ちなみにmain.rsのui.set_counterの部分にブレークポイントをつけて、エディタ上に表示されているDebugという部分をクリックすれば、デバッグ状態で起動されます。この状態で表示されたウィンドウのIncrease Valueを押せばブレークポイントで停止することが確認できます。
このプロジェクトはapp-window.slintをbuild.rsで事前にコンパイルしてRustコードに変換をするのでstep inするとbuild/.../out/app-window.rsファイルに入ります。まぁ、コンパイル生成されたコードですので通常は追いかける必要がないと思いますけど。
まとめ
今回は、色々大事なものを飛ばし過ぎなんだよ、流行らせる気あるのかよって怒られが発生したので、ちょっと反省して開発環境構築と、最初のプロジェクト作成を書きました。
いや、途中の大半は備忘録として以前書いたPico用開発環境とあまり代わり映えしない気もしますが、多少拡張周りの変更もありますし、Slint使わなくても最低限必要かなというものを入れているので、入れておいて見てください。
次回はなにかこの環境で動くものを用意してみるか、Slint言語入門側をはじめるか、どちらにするか検討中です。明日取れる時間次第かなぁ。また遅刻しそうな予感。
なお、明日の記事というか今日の記事は、すでに @task_jpさんが「Slint で車載用のメーターを作ってみました」を上げてくださっています。入門も、開発環境構築も終わってるので、もっと実践的なことが学びたいんだよって人は、@task_jpさんの記事の方を追いかけてみてください。