[Rust] 11 FLTK-RS : DebugとReleaseのビルド時間比較

[M1 Mac, Big Sur 11.6.7, Rust 1.62.0]

DebugとReleaseのビルド時間を比較してみました。

Releaseモードで最適化レベルが最高の3(デフォルト)ならば、ファイルサイズが1/3削減になります。

Releaseモードの最適化なしで最もビルド時間が短いですが、コマンドが長いのでこの程度の差であれば使わなくてもいいでしょう。

# Debugモード
cargo build

# Releaseモード
cargo build --release
[profile.dev]
opt-level = 0

[profile.release]
opt-level = 3
測定対象のアプリ

[Rust] 10 FLTK-RS : FrameType

[M1 Mac, Big Sur 11.6.7, Rust 1.62.0]

Buttonの形を変えてみました。FrameTypeは60種類近くあります。fltk-rsのGitHubで形状を確認できます。

use fltk::{app, prelude::*, enums::*, button::*, window::Window, group::{Group}};

let mut _btn = Button::new(300, 15, 50, 50, "実行");
    _btn.set_frame(FrameType::GleamUpBox);
    _btn.set_color(Color::from_u32(0x4D5AAF));
    _btn.set_label_color(Color::from_u32(0xFFFFFF));

[Rust] 09 FLTK-RS : 色設定, ツールチップ

[M1 Mac, Big Sur 11.6.7, Rust 1.62.0]

ランチャーアプリに色をつけて、さらにTooltipが表示されるようにしました。色設定にはenums::Colorを使います。

use fltk::{app, prelude::*, enums::*, button::*, window::Window, group::{Group}};

fn main() {
    let app = app::App::default();
    let mut window = Window::new(100, 100, 360, 190, "Tool Launcher");
    window.set_color(Color::from_u32(0x00A381));
    
    let  grp = Group::new(15, 15, 285 , 160, "");
    let mut _btn1= RadioRoundButton::new(15, 15, 140, 20, "OutingCosts");
    _btn1.set(true);
    _btn1.set_tooltip("OutingCosts.outingcosts");
    _btn1.set_label_color(Color::from_u32(0xFFFFFF));
    let mut _btn2= RadioRoundButton::new(15, 50, 140, 20, "PhotoSorting");
    _btn2.set_label_color(Color::from_u32(0xFFFFFF));
    let mut _btn3= RadioRoundButton::new(15, 85, 140, 20, "Scraping");
    _btn3.set_label_color(Color::from_u32(0xFFFFFF));
    let mut _btn4= RadioRoundButton::new(15, 120, 140, 20, "ScrapingJP");
    _btn4.set_label_color(Color::from_u32(0xFFFFFF));
    let mut _btn5= RadioRoundButton::new(15, 155, 140, 20, "");
    let mut _btn6= RadioRoundButton::new(160, 15, 140, 20, "");
    let mut _btn7= RadioRoundButton::new(160, 50, 140, 20, "");
    let mut _btn8= RadioRoundButton::new(160, 85, 140, 20, "");
    let mut _btn9= RadioRoundButton::new(160, 120, 140, 20, "");
    let mut _btn10= RadioRoundButton::new(160, 155, 140, 20, "");
    grp.end();

    let mut _btn = Button::new(300, 15, 50, 25, "実行");
    _btn.set_color(Color::from_u32(0x4D5AAF));
    _btn.set_label_color(Color::from_u32(0xFFFFFF));
    _btn.set_callback(move |_| {
        let _onoff1 = _btn1.value();let _onoff2 = _btn2.value();let _onoff3 = _btn3.value();
        let _onoff4 = _btn4.value();let _onoff5 = _btn5.value();let _onoff6 = _btn6.value();
        let _onoff7 = _btn7.value();let _onoff8 = _btn8.value();let _onoff9 = _btn9.value();
        let _onoff10 = _btn10.value();

        if _onoff1 == true{
            pythonmodule1();
        } else if _onoff2 == true{
            pythonmodule2();
        } else if _onoff3 == true{
            pythonmodule3();
        } else if _onoff4 == true{
            pythonmodule4();
        }
    });

    window.end();
    window.show();
    app.run().unwrap();
}

[Rust] 08 FLTK-RS : Python自製ライブラリのimport 

[M1 Mac, Big Sur 11.6.7, Rust 1.62.0]

Rustコードに導入するPythonスクリプトが自製ライブラリをimportする場合は、.bash_profileにPYTHONPATHとしてライブラリのあるディレクトリを設定します。

これでランチャーアプリに登録するPythonスクリプトが4つに増えました。

# 導入するPythonスクリプトのあるディレクトリ設定
export PYTHONPATH="/code/Python/library/python_module"

# importする自製ライブラリのあるディレクトリ設定
export PYTHONPATH="/code/Python/library"
use fltk::{app, prelude::*, button::*, window::Window, group::{Group}};
use pyo3::prelude::*;

fn pythonmodule1() -> PyResult<()> {
    Python::with_gil(|py| {
        println!("module1 start");
        let mymodule = PyModule::import(py, "OutingCosts")?;
        let response:i32 = mymodule.getattr("outingcosts")?.call0()?.extract()?;
        println!("module1 {}", response);
        Ok(())
    })
}

fn pythonmodule2() -> PyResult<()> {
    Python::with_gil(|py| {
        println!("module2 start");
        let mymodule = PyModule::import(py, "PhotoSorting")?;
        let response:i32 = mymodule.getattr("photosorting")?.call0()?.extract()?;
        println!("module2 {}", response);
        Ok(())
    })
}

fn pythonmodule3() -> PyResult<()> {
    Python::with_gil(|py| {
        println!("module3 start");
        let mymodule = PyModule::import(py, "Scraping")?;
        let response:i32 = mymodule.getattr("scraping")?.call0()?.extract()?;
        println!("module3 {}", response);
        Ok(())
    })
}

fn pythonmodule4() -> PyResult<()> {
    Python::with_gil(|py| {
        println!("module4 start");
        let mymodule = PyModule::import(py, "ScrapingJP")?;
        let response:i32 = mymodule.getattr("scrapingjp")?.call0()?.extract()?;
        println!("module4 {}", response);
        Ok(())
    })
}

fn main() {
    let app = app::App::default();
    let mut window = Window::new(100, 100, 360, 190, "Tool Launcher");
    
    let  grp = Group::new(15, 15, 285 , 160, "");
    let mut _btn1= RadioRoundButton::new(15, 15, 140, 20, "OutingCosts");
    _btn1.set(true);
    let mut _btn2= RadioRoundButton::new(15, 50, 140, 20, "PhotoSorting");
    let mut _btn3= RadioRoundButton::new(15, 85, 140, 20, "Scraping");
    let mut _btn4= RadioRoundButton::new(15, 120, 140, 20, "ScrapingJP");
    let mut _btn5= RadioRoundButton::new(15, 155, 140, 20, "");
    let mut _btn6= RadioRoundButton::new(160, 15, 140, 20, "");
    let mut _btn7= RadioRoundButton::new(160, 50, 140, 20, "");
    let mut _btn8= RadioRoundButton::new(160, 85, 140, 20, "");
    let mut _btn9= RadioRoundButton::new(160, 120, 140, 20, "");
    let mut _btn10= RadioRoundButton::new(160, 155, 140, 20, "");
    grp.end();

    let mut _btn = Button::new(300, 15, 50, 25, "実行");
    _btn.set_callback(move |_| {
        let _onoff1 = _btn1.value();let _onoff2 = _btn2.value();let _onoff3 = _btn3.value();
        let _onoff4 = _btn4.value();let _onoff5 = _btn5.value();let _onoff6 = _btn6.value();
        let _onoff7 = _btn7.value();let _onoff8 = _btn8.value();let _onoff9 = _btn9.value();
        let _onoff10 = _btn10.value();

        if _onoff1 == true{
            pythonmodule1();
        } else if _onoff2 == true{
            pythonmodule2();
        } else if _onoff3 == true{
            pythonmodule3();
        } else if _onoff4 == true{
            pythonmodule4();
        }
    });

    window.end();
    window.show();
    app.run().unwrap();
}

[Rust] 07 FLTK-RS : アプリおよびプロジェクトのサイズ

[M1 Mac, Big Sur 11.6.7, Rust 1.62.0]

ランチャーアプリに2つのPythonスクリプトを登録しました。

アプリ(実行ファイル)とプロジェクトのサイズは以下の通りです。

Rustアプリ:2.4MB
Rustプロジェクト:430MB
比較例(同じような機能数のFLTKアプリ)
C++アプリ:237KB
C++プロジェクト:4MB

アプリは10倍、プロジェクトにおいては100倍以上です。

Gitの他にプロジェクトを丸ごとコピーしてコードを管理していますが、Rustの場合は安易にコピーできないですね。コピー3つで1.3GBですから。

なかなか優れもののRustではありますが、サイズの大きさ、そしてコンパイルの遅さを考えるとまだまだ主言語にはできないな、というのが率直な感想です。

Rust修得に関する今後の課題は以下の通りです。

1.appファイルの作成方法
2.Pythonスクリプトに自製ライブラリのimportがある場合の対応

[Rust] 06 FLTK-RS : ランチャーアプリ製作 PyO3によるPythonモジュール導入

[M1 Mac, Big Sur 11.6.7, Rust 1.62.0]

Rustコードから引数なしのPythonスクリプトを実行できるようにしました。Python/C API経験者であればそんなに難しくないでしょう。

RustではPyO3ライブラリを使います。PyO3公式サイトを読めば、スクリプト埋め込みや複数スクリプトなど様々なPythonの使い方が分かります。

with_gilのgilはGlobal interpreter lockの略であり、Pythonインタプリタへのアクセスができるようになります。

C++やObjective-Cよりも導入方法が簡単で驚きです。これならPythonを使うのであればC言語系ではなくRustにしますね。以前はセキュリティ面で課題を抱えていたと聞きますが、PyO3の開発陣はすごいなと素直に思いました。

Python→C/C++の流れからPython→RustのようにC/C++をスルーするユーザーが今後増えていくのではないでしょうか。またRustの記事を書く方の中にはC/C++を知らない方もいるということを認識しておく必要があります。

いつも思うのですが、プログラミングに関する記事を書く場合はWindowsなのかLinuxなのかMacOSなのか、バージョンと合わせて明記していただきたいものです。

use fltk::{app, prelude::*, button::*, window::Window, group::{Group}};
use pyo3::prelude::*;

// PhotoSorting.py内のphotosorting関数を使用
fn pythonmodule() -> PyResult<()> {
    Python::with_gil(|py| {
        let mymodule = PyModule::import(py, "PhotoSorting")?;
        let response:i32 = mymodule.getattr("photosorting")?.call0()?.extract()?;
        println!("{}", response);
        Ok(())
    })
}

fn main() {
    let app = app::App::default();
    let mut window = Window::new(100, 100, 360, 190, "Tool Launcher");
    
    let  grp = Group::new(15, 15, 285 , 160, "");
    let mut _btn1= RadioRoundButton::new(15, 15, 140, 20, "PhotoSorting");
    _btn1.set(true);
    let mut _btn2= RadioRoundButton::new(15, 50, 140, 20, "");
    let mut _btn3= RadioRoundButton::new(15, 85, 140, 20, "");
    let mut _btn4= RadioRoundButton::new(15, 120, 140, 20, "");
    let mut _btn5= RadioRoundButton::new(15, 155, 140, 20, "");
    let mut _btn6= RadioRoundButton::new(160, 15, 140, 20, "");
    let mut _btn7= RadioRoundButton::new(160, 50, 140, 20, "");
    let mut _btn8= RadioRoundButton::new(160, 85, 140, 20, "");
    let mut _btn9= RadioRoundButton::new(160, 120, 140, 20, "");
    let mut _btn10= RadioRoundButton::new(160, 155, 140, 20, "");
    grp.end();

    let mut _btn = Button::new(300, 15, 50, 25, "実行");
    _btn.set_callback(move |_| {
        let _onoff1 = _btn1.value();let _onoff2 = _btn2.value();let _onoff3 = _btn3.value();
        let _onoff4 = _btn4.value();let _onoff5 = _btn5.value();let _onoff6 = _btn6.value();
        let _onoff7 = _btn7.value();let _onoff8 = _btn8.value();let _onoff9 = _btn9.value();
        let _onoff10 = _btn10.value();

        if _onoff1 == true{
            println!("onoff1");
            pythonmodule();
        }
    });

    window.end();
    window.show();
    app.run().unwrap();
}
[dependencies.pyo3]
version = "0.16.5"
features = ["auto-initialize"]

参考サイト

[Rust] 05 FLTK_RS : ランチャーアプリ製作 ButtonのBool値

[M1 Mac, Big Sur 11.6.7, Rust 1.62.0]

ButtonのON/OFF状態は本家FLTKと同様にvalue()関数で取得できます。ボタン群を波カッコで囲むとスコープが閉ざされてしまうので削除しました。end()関数で閉じていれば問題ありません。

関数外での変数の設定が簡単にはできず、データの改ざんなどがしにくいようスコープを狭くする傾向があるようです。

あとはPythonスクリプトを実行できるようにして仮完成です。

今後FLTKアプリを配布することがあればRustで書こうかと考えています。

まあ重大な不具合を発生させるほど複雑なアプリをC++で作っているわけではないですが、RustがこれからもC/C++の代替言語として影響力を拡げていく可能性もありますし、今から慣れておくのも悪くないでしょう。

コンパイラから細かいところで指導が入るのがうっとうしいものの、C++の影響をかなり受けていて頻出するletとmut以外そんなに違和感はないです。

use fltk::{app, prelude::*, button::*, window::Window, group::{Group}};

fn main() {
    let app = app::App::default();
    let mut window = Window::new(100, 100, 360, 190, "Tool Launcher");
 
    let grp = Group::new(15, 15, 285 , 160, "");
    let mut _btn1= RadioRoundButton::new(15, 15, 140, 20, "PhotoSorting");
    _btn1.set(true);
    let mut _btn2= RadioRoundButton::new(15, 50, 140, 20, "");
    let mut _btn3= RadioRoundButton::new(15, 85, 140, 20, "");
    let mut _btn4= RadioRoundButton::new(15, 120, 140, 20, "");
    let mut _btn5= RadioRoundButton::new(15, 155, 140, 20, "");
    let mut _btn6= RadioRoundButton::new(160, 15, 140, 20, "");
    let mut _btn7= RadioRoundButton::new(160, 50, 140, 20, "");
    let mut _btn8= RadioRoundButton::new(160, 85, 140, 20, "");
    let mut _btn9= RadioRoundButton::new(160, 120, 140, 20, "");
    let mut _btn10= RadioRoundButton::new(160, 155, 140, 20, "");
    grp.end();

    let mut _btn = Button::new(300, 15, 50, 25, "実行");
    _btn.set_callback(move |_| {
        let _onoff1 = _btn1.value();let _onoff2 = _btn2.value();let _onoff3 = _btn3.value();
        let _onoff4 = _btn4.value();let _onoff5 = _btn5.value();let _onoff6 = _btn6.value();
        let _onoff7 = _btn7.value();let _onoff8 = _btn8.value();let _onoff9 = _btn9.value();
        let _onoff10 = _btn10.value();

        if _onoff1 == true{
            println!("onoff1");
        }
    });

    window.end();
    window.show();
    app.run().unwrap();
}

[Rust] 04 FLTK_RS : ランチャーアプリ製作 set_callback

[M1 Mac, Big Sur 11.6.7, Rust 1.62.0]

ボタンの動作設定は本家FLTKとは異なる方法でした。

.cargoディレクトリにあるfltk-1.3.10のexamples内custom_widgets.rsでようやく分かりました。なぜそうなるのかについてはこれから調べます。move |_|とはいったい何なんでしょう。DOCS.RSを読んでもset_callback関数の存在が分かるだけでした。

use fltk::{app, prelude::*, button::*, window::Window, group::{Group}};

fn main() {
    let app = app::App::default();
    let mut window = Window::new(100, 100, 360, 190, "Tool Launcher");
    
    let mut _btn = Button::new(300, 15, 50, 25, "実行");
    _btn.set_callback(move |_| {
        println!("test");
    });
 
    let  grp = Group::new(15, 15, 285 , 160, "");{
        let mut _btn1= RadioRoundButton::new(15, 15, 140, 20, "PhotoSorting");
        _btn1.set(true);
        let mut _btn2= RadioRoundButton::new(15, 50, 140, 20, "");
        let mut _btn3= RadioRoundButton::new(15, 85, 140, 20, "");
        let mut _btn4= RadioRoundButton::new(15, 120, 140, 20, "");
        let mut _btn5= RadioRoundButton::new(15, 155, 140, 20, "");
        let mut _btn6= RadioRoundButton::new(160, 15, 140, 20, "");
        let mut _btn7= RadioRoundButton::new(160, 50, 140, 20, "");
        let mut _btn8= RadioRoundButton::new(160, 85, 140, 20, "");
        let mut _btn9= RadioRoundButton::new(160, 120, 140, 20, "");
        let mut _btn10= RadioRoundButton::new(160, 155, 140, 20, "");
    }
    grp.end();

    window.end();
    window.show();
    app.run().unwrap();
}

[Rust] 03 FLTK_RS : ランチャーアプリ製作 RadioRoundButton

[M1 Mac, Big Sur 11.6.7, Rust 1.62.0]

RoundButtonがただの丸いボタンであることを忘れていました。今回必要なのはRadioRoundButtonです。

本家FLTKとほぼ同じ書き方でOKです。

use fltk::{app, prelude::*, button::*, window::Window, group::{Group, Pack}};

fn main() {
    let app = app::App::default();
    let mut window = Window::new(100, 100, 360, 190, "Tool Launcher");
    
    let _btn = Button::new(300, 15, 50, 25, "実行");

    let grp = Group::new(15, 15, 285 , 160, "");{
        let mut _btn1= RadioRoundButton::new(15, 15, 140, 20, "PhotoSorting");
        _btn1.set(true);
        let mut _btn2= RadioRoundButton::new(15, 50, 140, 20, "");
        let mut _btn3= RadioRoundButton::new(15, 85, 140, 20, "");
        let mut _btn4= RadioRoundButton::new(15, 120, 140, 20, "");
        let mut _btn5= RadioRoundButton::new(15, 155, 140, 20, "");
        let mut _btn6= RadioRoundButton::new(160, 15, 140, 20, "");
        let mut _btn7= RadioRoundButton::new(160, 50, 140, 20, "");
        let mut _btn8= RadioRoundButton::new(160, 85, 140, 20, "");
        let mut _btn9= RadioRoundButton::new(160, 120, 140, 20, "");
        let mut _btn10= RadioRoundButton::new(160, 155, 140, 20, "");
    }
    grp.end();

    window.end();
    window.show();
    app.run().unwrap();
}

[Rust] 02 FLTK_RS : ランチャーアプリ製作 RoundButton

[M1 Mac, Big Sur 11.6.7, Rust 1.62.0]

各種Pythonスクリプトを実行するランチャーアプリをRustで製作しています。

RoundButtonを10個並べたものの、まだ一つだけ選べるようにはなっていません。

FLTK_RSライブラリの中に一応作例はありますが、本家FLTKほど充実はしていないのでさらに情報を集める必要があります。

Rustはプロジェクト名をスネークケース(小文字の単語を_でつなぐ命名規則)にしろだとか、オブジェクト名には先頭に_を付けろ等々、色々細かい注文をつけてきますね。

Makefileではなくcargoコマンドでコンパイルしますが、ブラックボックスになっていてライブラリのバージョンをこちらで指定できないのが不満です。

use fltk::{app, prelude::*, button::*, window::Window, group::Group};

fn main() {
    let app = app::App::default();
    let mut window = Window::new(100, 100, 360, 190, "Tool Launcher");

    let grp1 = Group::new(15, 15, 285 , 160, "");

    let _btn = Button::new(300, 15, 50, 25, "実行");

    let _btn1= RoundButton::new(15, 15, 140, 20, "PhotoSorting");
    let _btn2= RoundButton::new(15, 50, 140, 20, "");
    let _btn3= RoundButton::new(15, 85, 140, 20, "");
    let _btn4= RoundButton::new(15, 120, 140, 20, "");
    let _btn5= RoundButton::new(15, 155, 140, 20, "");
    let _btn6= RoundButton::new(160, 15, 140, 20, "");
    let _btn7= RoundButton::new(160, 50, 140, 20, "");
    let _btn8= RoundButton::new(160, 85, 140, 20, "");
    let _btn9= RoundButton::new(160, 120, 140, 20, "");
    let _btn10= RoundButton::new(160, 155, 140, 20, "");
    
    grp1.end();

    window.end();
    window.show();
    app.run().unwrap();
}