LinuxでTUI

Linux(今回はUbuntu20.04)でTUIする方法を紹介します。LinuxでGUIでなく端末操作する場合、CLIと言ったりしますが、今回はTUIです。

  • GUI: Graphical User Interface
  • CLI: Command Line Interface(和製英語だとCUIと呼ばれる)
  • TUI: Text-based User Interface

ニュアンス的にはテキストでグラフィカルなウィジェットを模したインターフェースを表現し、キーボードだけで操作していく、簡易GUI的なテキストベースのUIをTUIと呼ぶ感じです。作り的にはncursesなどを利用したアプリケーションになります。

目的

Linuxで簡単なTUIアプリケーションを作成すること

補足

リモートの本番サーバーなどでは、セキュリティリスクやメンテナンスコストが上がるので一般的にはGUIを使用しません。そこでGUIではなくTUIとすることで、リモートでの実行可能性を高めつつ、難しいと言われがちなCLIの操作性向上を狙うという意図です。

背景

Linuxでは主にインストールやコンフィギュレーション時にTUIを使用したアプリケーションが使用されます。インストールの際には必ずしもGUIが使えるとは限りませんが、Linuxに不慣れなユーザーでもインストーラーを使用可能にしたいからです。

これらの用途において、Linuxでは古来、curses系ライブラリを用いたdialogというコマンド及びライブラリが使われてきました。UbuntuなどDebian系ではnewt由来のwhiptailという似たようなコマンドが採用されています。今回はwhiptailとdialogを使用してごく簡単なUIを作ってみたいと思います。

whiptailを使う

まずはdebian系のディストリビューションで使用されているwhiptailです。

shell script編

$ whiptail --menu '選択して下さい' 13 40 5 '1)' A '2)' B '3)' C '4)' Other 2>selected.txt

上記を実行すると以下のような画面を表示することができます。

この画面では矢印キーやTABキーやEnterキーによって項目を選択し、リストボックスのGUIと似たような操作が可能になっています。選択が終わると選択されたものをエラー出力に出力するので、今回の場合だとselected.txtに入ります。

他にもウィジェットやダイアログは複数あるので、Shell Scriptから使用すると容易に簡単なTUIを実現できることが分かると思います。詳細はman whiptailに載っています。

C/C++編

C/C++の場合は、whiptailを含むnewtの開発ライブラリlibnewt-devパッケージのインストールが必要です。

$ sudo apt install libnewt-dev

ドキュメンテーションはあまりないですが、C++だとこんなコードになります。

#include <iostream>
#include <newt.h>

int main() {
    newtInit();
    newtCls();

    newtOpenWindow(1, 1, 40, 13, nullptr);
    newtSetColor(NEWT_COLORSET_LABEL, "black", "lightgray");
    newtComponent l = newtLabel(1, 1, "選択して下さい");
    newtComponent list = newtListbox(15, 3, 6, 0);
    newtListboxAppendEntry(list, "1) A", "1)");
    newtListboxAppendEntry(list, "2) B", "2)");
    newtListboxAppendEntry(list, "3) C", "3)");
    newtListboxAppendEntry(list, "4) Other", "4)");
    newtComponent bOk = newtCompactButton(10, 11, "了解");
    newtComponent bCancel = newtCompactButton(22, 11, "取消");
    newtComponent form = newtForm(nullptr, nullptr, 0);
    newtFormAddComponents(form, l, list, bOk, bCancel, nullptr);

    struct newtExitStruct es;
    newtFormRun(form, &es);
    const char* result = (es.u.co == bOk) ? static_cast<const char*>(newtListboxGetCurrent(list)) : nullptr;
    newtFormDestroy(form);

    newtFinished();

    if (result) {
        std::cerr << result;
    }
    return result ? 0 : 1;
}

コンパイルは以下のとおりです。

$ g++ sample.cpp -lnewt -o sample

ウィジェットも各種あるので、whiptailのテンプレートにないダイアログも記述できるし、何度もウィンドウを開けます。debian系では標準で使えるライブラリだと思いますが、コードもドキュメンテーションもあまりないのが欠点です。

dialogを使う

こちらの方が古く、より定番なようです。ただしdebian系ではデフォルトでは入っていません。

shell script編

menuだとwhiptailと全く同じオプションで使えます。

$ dialog --menu '選択して下さい' 13 40 5 '1)' A '2)' B '3)' C '4)' Other 2>selected.txt

見た目は多少異なります。

操作感やI/Fも同じです。

C/C++編

libdialogとヘッダはアプリと一緒にインストールされているのですが、内部で使用しているncursesのヘッダがないため、まずは開発版のncursesライブラリをインストールします。

$ sudo apt install libncurses-dev

libdialogのUbuntuのドキュメンテーションもそれほどはないのですが、歴史が古くそれなりに使用実績があるので、他OSのものやサンプルコードが割とすぐ見つかるので実装にはさほど困りません。

#include <iostream>
#include <dialog.h>

int main() {
    char *modes[] = {
        "1)", "A",
        "2)", "B",
        "3)", "C",
        "4)", "Other"
    };

    init_dialog(stdin, stdout);
    int result =  dialog_menu(nullptr, "選択して下さい", 0, 0, 0, sizeof(modes)/sizeof(modes[0])/ 2,  modes);
    end_dialog();

    if (result == 0) {
        std::cout << dialog_vars.input_result;
    }
    return result;
}

ちょっと使う分にはこちらの方が楽な印象です。

まとめ

Linuxでnewt/dialogを使って簡単なメニュー選択TUIアプリを作成しました。
なぜか先発のdialogの方が若干簡単な印象を持ちました。

未分類C++,linux

Posted by first_user