えいあーるれいの技術日記

ROS2やM5 Stack、Ubuntuについて書いています

コマンドライン引数を少しずつGUIに置き換えていくためのツールを作った

研究・趣味開発のプログラムのエントリーポイントに引数を渡すことがしばしばあると思います。

これらは「コマンドライン引数」と呼ばれ、ターミナルの実行ファイルにつづけて空白区切りで値を指定していくもので、コマンドライン引数は全て文字列の配列で渡されるため、文字列解析を自力で行って適切に解釈させる必要があります。

Pythonはargparseというパッケージを使用することで比較的簡単にコマンドライン引数を解析できるのでかなり便利です。


bashファイルの例:a.bash b cde fgh

# a.bash.     #結果

echo $0      # a.bash
echo $1.      # b
echo $2.     # cde
echo $3      # fgh
echo $4      #       (なし)


一方で、コマンドライン引数は(仮に自分が作成したとしても)指定するのが面倒に感じることがあります。

特に私は物覚えも良くないので、どの引数がどの役割かも忘れてしまい、結局ソースコードを開いていちいち役割を確認しにいく羽目になります。(ヘルプオプションをつければ全く問題ない話ではあります)

また、仮に気を利かせてプログラムの実行例をREADMEに貼り付けたとしても、ファイル位置が変更された場合に書き直しが発生してやっぱり使用感は微妙になっていきます。


GUIにしたい

私は開発も使用も頭を使わずに行いたいと思っていたので、「引数指定もできるけど、できなかったときはGUIで補完できるインタフェースを作りたい」とずっと考えていました。

イメージ図は以下の通りです。

GUIで補完できるのであれば、わざわざコマンドライン引数を確認する必要もないですし、コマンドライン引数を使わなくていいのであれば、引数順に悩む必要もありません。

そこで、私はGUIを呼び出して返り値をもらって閉じるだけのシンプルなアプリケーションで代替しようと考えました。

考え方は珍しいわけでもないですが、開発者側からもコントローラのような使い勝手なGUIを目指しました。


単純なPySide2ウィンドウを使って解決

bashファイルでPythonプログラムを実行して返り値を取得する一番簡単な方法を以下に示します。

# ./hello.py
print("hello")
# ./hello.bash
A=`python ./hello.py`
echo 'A='$A

bash ./hello.bashの結果はA=helloとなります。

これをファイル選択ウィジェットに埋め込むとこうなります。GUIはPySide2を使っています。

#!/bin/python3
# ===========================
# question_select.py
# Ar-Ray-code 2022
# ===========================

from PySide2.QtWidgets import QApplication, QWidget, QMessageBox
import sys
import argparse

class QuestionWidget(QWidget):
    def __init__(self):
        super().__init__()

        parser = argparse.ArgumentParser(description='Select folder')
        parser.add_argument('-t', '--title', help='title of dialog', default='Choose your answer')
        parser.add_argument('-al', '--answer-list', help='list of answers', nargs='+', default=['Connect', 'Abort'])
        
        args = parser.parse_args()

        self.title = args.title
        self.answer_list = args.answer_list

        self.create_widgets()

    def create_widgets(self):
        msgBox = QMessageBox()
        msgBox.setText(self.title)

        ans_get = []
        for ans in self.answer_list:
            ans_get.append(msgBox.addButton(ans, QMessageBox.ActionRole))

        msgBox.exec_()

        for ans in ans_get:
            if msgBox.clickedButton() == ans:
                print(ans.text())
                sys.exit(0)

        sys.exit(1)        

def entry_point():
    app = QApplication(sys.argv)
    ex = QuestionWidget()

これを実行すると以下の通りになります。(argparseを使っているため、-hオプションで使い方を確認できます)

question_select.py -t "select your answer" -al ans1 "ans 2" ans3 ans4
# 例えば "ans3"を選んだ場合
> ans3


表示(M1 Mac上にx86_64のpyenv:python3.10を入れています。)


pythonから使用

Pythonモジュールなので、Pythonから単体で起動可能です。

ドキュメントをまだ整えていないですが、ソースコードのリンク先を貼っておきます。

github.com


ウィジェット一覧

今のところ、次のウィジェットを用意しています。PyamlSide2のversion0.5.0 (次回リリース予定) よりメインブランチから使用可能になります。

ReadableWidgetsは開発者側からも読みやすく流用が効くGUI開発ツールセットを目指しています。

詳細(GitHub)は以下のリンクから。

github.com


  • messagebox: メッセージボックスを表示するウィジェットquestionモードのみYes/Noの返り値がある。
  • question_lineedit: 文字列を入力するウィジェット。デフォルト値も指定可能。
  • question_select: 選択肢から選ぶウィジェット
  • select_folder_file_dialog: ファイル・フォルダを選択するウィジェット。フォルダモードや拡張子指定などのオプションあり。