雑食性雑感雑記

知識の整理場。ため込んだ知識をブログ記事として再構築します。

Ubuntu で動作する .NET 系な GUI フレームワークお試し (Mono、AvaloniaUI)

仕事で、Ubuntu で動く GUI フレームワークについてアレコレ調べたのですが、思いの外深掘りになったので、改めて私的にまとめてみることにしました。

経緯

以前、Ubuntu を使って Qt を用いた GUI アプリケーションを作ったのですが、データのやり取りが絡んでくると結構複雑な構成となり、ちょっとトラウマ。。

改めて今回、同じく Ubuntu で動く GUI アプリケーションとなったので、使えるものを調査することにしました。
最近 WPF を使うことが増えてきたので、その互換のものがあればスキル的にも楽かと――

作るもの

実際作るものを簡易的にした構成の『動画ビューア』を作ります。
動画の読み込みは OpenCV を使った動的ライブラリ (.so ファイル) で行い、それを GUI から呼び出すようにします。

完成イメージはこんな感じ。
動画の読み込みダイアログ使ったファイルパス指定と、動画のスタート/ストップボタン。

f:id:kazuki_nagasawa:20201021121106p:plain
完成イメージ

作ったもの

今回作ったものは github に置いたので、興味ある方はどうぞ。
github.com

環境

Ubuntu 環境は WSL で試してます。

  • Windows 10
    • Visual Studio 2017
    • Ubuntu 18.04 (on WSL)
      • OpenCV 4.2.0
      • .NET Core SDK 2.1

以下、作ったものの説明です。

動的ライブラリ

これは普通に Linux C++ で動くものを作るだけだから特記事項はないですね。
忘れがちなのは 「extern "C"」 でエントリポイント作らないと外部呼出しで関数が見えないことですね。

extern "C" {
    int initialize(char* videoPath, int width, int height);
    int getFrame(byte* data);
}

使い方としては、
・initialize で読み込む動画パス、映像表示ウィンドウの幅、高さを指定。
・getFrame で 1 フレーム分の動画データを映像表示ウィンドウに合わせてリサイズ後、byte 配列として取得。
というのを想定しています。

Mono で Windows Forms アプリケーションを Linux 化

調べていてまず初めに出てきたのが Mono。オープンソースで .NET Framework ベースの GUI を使えるらしい。( そのうち .NET Framework に吸収される? )
https://www.mono-project.com/

いくつか使える中の Windows Forms アプリケーションを試してみます。

Visual Studio で GUI 構築

( どこまで新しいバージョンに対応できているか不明ですが、)
作成はそのまま Visual Studio でできるので、開発は楽で良いですね。

so ライブラリ読み込み部は Linux 側じゃないと確認できないので、表示・操作部分のみ VS 側で実装してしまいます。

f:id:kazuki_nagasawa:20201021123056p:plain
Visual Studio で表示部作成中

Linux 側に移してビルド

表示部が出来たら、C# ファイル (.cs) だけを Linux 側にコピー。今回は
・Program.cs ... コード全体のエントリポイント
・MainWindow.cs ... ウィンドウの処理実装部
・MainWindow.Designer.cs ... ウィンドウのコントロールやデザイン。大抵自動生成。
の3ファイル。
MainWindow.cs には so ライブラリとの結合処理も追加で作ります。

事前に mcs (Mono C# Compiler) をインストールした上でビルドすれば、アプリケーション (exe) がビルドできます。

実行

あとは mono コマンドで実行すれば OK 。
( Windows 上で実行する場合は X Window System 対応アプリケーションを事前に起動しておくこと。)

f:id:kazuki_nagasawa:20201022083215p:plain
WSL 上から起動した Mono 版アプリケーション

ファイル選択ウィンドウのフィルタ部の日本語は文字化けしてますね。。
この辺り正しく使うなら追加設定必要そう。

f:id:kazuki_nagasawa:20201021123835p:plain
ファイル選択ウィンドウ (Mono)

AvaloniaUI で WPF (に近い) アプリケーションを Linux 化

Windows Forms + Mono を使えばとりあえず Linux で作りやすい GUI ができることが分かったのですが、
・Windows Forms はアンカー設定等、画面サイズがコロコロ変わるものを作りにくい。
・配置には WPF の Grid layout が便利だったなー。
ということでやっぱり WPF 使いたい!!

「Mono で WPF も対応してくれないのー?」→ 「AvaloniaUI なるものがあるよ。」
https://www.mono-project.com/docs/gui/wpf/

ということだったので、それならと AvaloniaUI でも同じものを作って試してみることにしました。

Visual Studio に AvaloniaUI 適用

AvaloniaUI を Visual Studio で使うには、プラグインが配布されているのでそれを入れれば OK 。
https://avaloniaui.net/docs/quickstart/create-new-project

↑のチュートリアルの通り、Avalonia Application を作って進めていけば作れます。

axaml ? xaml ?

現状の AvaloniaUI では、xaml も対応しているらしいけど、axaml が標準らしい。( Avalonia の a がついている xaml ? )

Visual Studio の Solution Explorer で axaml を認識してくれなかったので、全部 xaml にリネームしたらビルド通らなくなった orz..
結局、フォルダ形式で閲覧することで axaml 見えるので、このままで開発を進めました。

コントロール

大体使うコントロールは docs に載ってます
https://avaloniaui.net/docs/controls/
が、やっぱり足りなくなると検索かける必要が。ドキュメントは全体的に少な目。。

また、厳密に WPF と互換性があるわけではなく、結構いろんなところに違いがあります。
例えば、OpenFileDialog だと、WPF 標準は

private void ButtonOpenVideo_Click(object sender, RoutedEventArgs e)
{
    Microsoft.Win32.OpenFileDialog dialog = new Microsoft.Win32.OpenFileDialog();
    dialog.Filter = "動画ファイル (*.avi,*.mov,*.mp4)|*.avi;*.mov;*.mp4|全てのファイル (*.*)|*.*";
    if (this.TextboxVideoFilePath.Text.Length > 0 && System.IO.File.Exists(this.TextboxVideoFilePath.Text))
    {
        dialog.InitialDirectory = System.IO.Path.GetDirectoryName(this.TextboxVideoFilePath.Text);
        dialog.FileName = System.IO.Path.GetFileName(this.TextboxVideoFilePath.Text);
    }
    else
    {
        dialog.InitialDirectory = System.AppDomain.CurrentDomain.BaseDirectory;
    }
    if (dialog.ShowDialog() == true)
    {
        this.TextboxVideoFilePath.Text = dialog.FileName;
    }
}

なのに対して、AvaloniaUI だと

private async void ButtonOpenVideo_Clicked(object sender, RoutedEventArgs e)
{
    OpenFileDialog dialog = new OpenFileDialog();
    FileDialogFilter f1 = new FileDialogFilter();
    f1.Name = "動画ファイル (*.avi,*.mov,*.mp4)";
    f1.Extensions = new System.Collections.Generic.List<string>();
    f1.Extensions.Add("avi");
    f1.Extensions.Add("mov");
    f1.Extensions.Add("mp4");
    FileDialogFilter f2 = new FileDialogFilter();
    f2.Name = "すべてのファイル (*.*)";
    f2.Extensions.Add("*.*");
    dialog.Filters.Add(f1);
    dialog.Filters.Add(f2);
    try {
        string[] pathes = await dialog.ShowAsync(this);
        if (pathes.Length > 0) {
           TextBoxVideoPath.Text = pathes[0];
        }
    } catch {};
}

な感じ。非同期になってますね。

また、WriteableBitmap も Avalonia.Media.Imaging にあるものを。

Linux 側に移してビルド

表示部が出来たら Linux 側に移してビルド。AvaloniaUI の場合、.NET Core でビルドするので、ソリューションファイル (.sln) 以外すべて必要です。
dotnet build でビルド。

実行

dotnet run で実行。

f:id:kazuki_nagasawa:20201022083324p:plain
WSL 上から起動した AvaloniaUI 版アプリケーション

こっちはファイル選択ウィンドウのフィルタ部の日本語も大丈夫。

f:id:kazuki_nagasawa:20201021125832p:plain
ファイル選択ウィンドウ (AvaloniaUI)

まとめ

.NET 系な GUI フレームワークである Mono と AvaloniaUI それぞれで動画再生 GUI を作ってみました。
ドキュメントが少ないですが、自分としては WPF に近く、レイアウトしやすい AvaloniaUI にとりあえず軍配でしょうか。

開発する側としては、早く OS を垣根を越えて簡単に作れるものが出てくれると嬉しいのですが。。