普段使うツールをRustで作る場合、コマンドラインベースのものが多いと思います。それでも、Rustを使ってデスクトップアプリを作ることもできます。今回は、実績のあるGUIツールキットの「Tcl/Tk」をラップしたクレートを使って簡単なGUIを作成してみましょう。

  • Rustで単位変換ツールを作ったところ

    Rustで単位変換ツールを作ったところ

GUIツールキット「Tcl/Tk」のインストール

Rustのライブラリを集約したcrates.ioを見ると、多くのGUIライブラリが存在します。Rustはさまざまな環境で動作することを想定していることもあり、「これこそGUIの定番」というものはありません。多くの選択肢があり、用途に応じて使い分けるのが正しい作法です。

WebブラウザベースのTauri、ゲーム開発と相性の良いegui、GTK+を利用するGTK-rs、Tcl/Tkをラップしたライブラリのtcl/tkなど、いろいろなクレートがあります。今回は、この中からTcl/TkをラップしたGUIライブラリの「tcl」と「tk」クレートを利用してみましょう。

このクレートは、伝統的なGUIツールキット「Tcl/Tk」のラッパーです。そのため、別途Tcl/Tkのインストールが必要となります。OSごとにTcl/Tkをインストールしましょう。

なお、Tcl/Tkについては、姉妹連載のこちらで詳しく紹介しています。

【WindowsでTcl/Tkをインストールする場合】

Windowsであれば、Rustに加えて、ActiveTclのインストールが必要になります。なお、ActiveTclをダウンロードする際には、ActiveStateのアカウントを作成する必要がありますが、無料でダウンロードできます。インストーラーをダウンロードしたら、ダブルクリックでActiveTclをインストールします。

【macOSでTcl/Tkをインストールする場合】

macOSの場合は、Homebrewを使ってTcl/Tkをインストールします。Homebrewをインストールした上で、ターミナルにて下記のコマンドを実行します。

brew install tcl-tk

簡単なテストプロジェクトを作ろう

最初に、簡単なテストプログラムを作って動作を確認してみましょう。ターミナル(WindowsならPowerShell、macOSならターミナル.app)を起動して、次のコマンドを実行しましょう。

# ディレクトリを作成してプロジェクトを初期化
mkdir hello_tk
cd hello_tk
cargo init
# クレートを追加
cargo add tcl tk

次にメインプログラム「src/main.rs」を記述しましょう。プログラムを以下のように書き換えましょう。こちらからもコピーすることができます。

use tk::*;
use tk::cmd::*;

fn main() -> TkResult<()> {
    // Tkの初期化 --- (*1)
    let tk = make_tk!()?;
    let root = tk.root();
    // ウィンドウ上にフレームを作成 --- (*2)
    let content = root
        .add_ttk_frame(())?
        .pack(())?;
    content.configure( -padding(12) )?; // 余白を設定
    // ラベルを配置 --- (*3)
    content
        .add_label("lbl1" -text("Tkのテスト"))?
        .pack(())?;
    // ボタンを配置 --- (*4)
    content
        .add_button("btn1" -text("閉じる") -command("destroy ."))?
        .pack( -side("bottom") )?;
    // メインループ --- (*5)
    Ok( main_loop() )
}

プログラムを実行するために、ターミナルで下記のコマンドを実行しましょう。

cargo run

なお、Windowsでエラーが出て失敗する場合、下記の点を確認してください。
(1)ActiveTclをデフォルト設定でインストールし、PATHにwish.exeのパスを追加する
(2)libclangが見つからないというエラー『 thread 'main' panicked at 'Unable to find libclang: "couldn't find any valid shared libraries matching: ['clang.dll', 'libclang.dll']』が出る場合には、Chocolateyをインストールした上で管理者権限でPowerShellを起動して以下のコマンドを実行し、最新版のLLVMをインストールしてください。

choco install llvm

(3)stdio.hがないというエラーが出る場合、Visual Studioをインストールして、「C++によるデスクトップ開発」にチェックを付けてインストールする必要があります。
それでも、エラーが解決しない場合、こちらにフォロー記事を投稿しましたのでご確認ください。

プログラムが正しくコンパイルされると、次のようなウィンドウが表示されます。ウィンドウに、ラベルとボタンが配置され、「閉じる」ボタンを押すとプログラムを終了します。

  • 一番簡単なプログラム

    一番簡単なプログラム

プログラムを確認してみましょう。(*1)ではTkを初期化します。(*2)ではウィンドウ上にフレームを作成して配置します。configureメソッドを使ってフレームに余白を設定します。

(*3)では、add_labelメソッドでラベルを作成して、packメソッドで配置します。(*4)では、add_buttonメソッドでボタンを作成してpackメソッドで配置します。

tkクレートのadd_labelやadd_buttonのメソッドには、ウィジェットのIDに加えて、初期パラメータを指定できます。パラメータには、-padding(…)や-text(…)などのように値を指定します。

(*5)ではメインループを実行します。これによって、ウィンドウが動作します。

単位変換プログラムを作ろう

次に、単位変換を行うプログラムを作ってみましょう。分かりやすく、単位変換プログラムの定番である、インチをセンチに変換するプログラムを作ってみましょう。

プロジェクトを初期化するために、ターミナルで下記のコマンドを実行しましょう。

mkdir unit_converter
cd unit_converter
cargo init
cargo add tcl tk

そして、メインプログラム「src/main.rs」に下記のプログラムを記述しましょう。こちらからコピーすることもできます。

use tk::*;
use tk::cmd::*;
use tcl::*;
fn main() -> TkResult<()> {
    // Tkの初期化
    let tk = make_tk!()?;
    let root = tk.root();
    root.set_wm_title("インチからセンチへの変換")?;
    // ウィンドウ上にフレームを作成
    let c = root
        .add_ttk_frame(())?
        .pack(())?;
    c.configure( -padding(12) )?; // 余白を設定
    // Gridレイアウトを利用してウィジェットを配置 --- (*1)
    c.add_ttk_label( "inch_lbl" -text("インチ") )?
        .grid( -column(1) -row(1) -sticky("ne") )?;
    c.add_entry( "inch_entry" -textvariable("inch_var") )?
        .grid( -column(2) -row(1) -sticky("nw") )?;
    c.add_button( "calc" -text("計算") -command("calculate") )?
        .grid( -column(3) -row(1) -sticky("w") )?;
    c.add_ttk_label( "cm_lbl" -text("センチ") )?
        .grid( -column(1) -row(2) -sticky("ne") )?;
    c.add_entry( "cm_entry" -textvariable("cm_var") )?
        .grid( -column(2) -row(2) -sticky("nw") )?;
    // Entryに初期値を設定 --- (*2)
    tk.run(("set", "inch_var", "30.0"))?;
    // 計算関数を定義 --- (*3)
    #[proc] fn calculate() -> TkResult<()> {
        let interp = tcl_interp!();
        let inch: f64 = interp.get_double("inch_var")?;
        let cm = inch * 2.54;
        interp.set_double("cm_var", cm);
        Ok(())
    }
    // 定義した関数を登録 --- (*4)
    unsafe{ tk.def_proc( "calculate", calculate ); }
    // メインループ
    Ok( main_loop() )
}

プログラムを実行するには、ターミナルで下記のコマンドを実行します。

cargo run

すると、次のようなウィンドウが表示されます。インチの右横にあるテキストボックスに数値を入力して「計算」ボタンを押すと、インチをセンチに変換してテキストボックスに表示します。

  • インチからセンチへの変換プログラムを実行したところ

    インチからセンチへの変換プログラムを実行したところ

プログラムを確認してみましょう。(*1)ではGridレイアウトを利用して、各種ウィジェットを配置します。(*2)では、Entryに初期値を配置します。(*3)では、計算関数を定義します。インチの値を取得して、センチに変換して値を設定します。(*4)では、定義した関数を登録します。

この記事は
Members+会員の方のみ御覧いただけます

ログイン/無料会員登録

会員サービスの詳細はこちら