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

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

ReadableWidgetsの紹介 (技育展2022)

みなさん、こんにちは!

今回、私は「簡易GUI設計支援ライブラリ ReadableWidgets」という作品名で「開発・スキル支援」部門に作品を出しています。

技育展とは、株式会社サポーターズが主催する学生向け技術ピッチコンテストです。今年は、事前審査を通過した15テーマ・全180作品(チーム)の作品が展示・発表されます。

人数・規模も然る事ながら提出・提案される作品のクオリティも非常に高いものが見られるため、学生登壇の場としては国内最大規模と言っても差し支えないでしょう。


選考を通った学生は、2週間の仕上げ期間を経て3分間のLTに臨みます。

優秀な発表・展示には賞金もあるそうで、総額600万円❗️

一般審査枠もあるそうです。ぜひ以下のリンクから詳細を確認してみてください。

talent.supporterz.jp


私は昨年登壇していたので、今年は(やる気的にも)どうしようかなーと思っていたのですが、

「参加賞 1万円/チーム」というのはやはり大きいのか、あるいは寂しがり屋なのか、いつの間にか作品を応募していました。


私は特に発表が苦手なので、失敗する前にあらかじめ内容を頭に入れてもらおうと思います。

ここには載せていないものもあるので、ぜひプレゼン資料をご覧いただければと思います。


ReadableWidgetsの紹介

ここから本題、ReadableWidgetsの紹介を行います。

ReadableWidgetsはGUI開発ツールですが、どちらかといえば、組み込み・IoT向けのGUI設計ツールになります。


GUI作成って手間かかりますよね?(簡単なものほど)

みなさんは、GUI作成にどれくらいの手間をかけていますか?

GUI前提(Webアプリとかスマホアプリ)であればともかく、元々便利ツール的に作っていたものだと意外とハードルは高いと思います。


「せっかくだし」…や「軽く」…なんて言われても実際は、軽いどころではなく時間がかかるものです。

そのため、他の人に使ってもらうにはGUI化したいけどできない…ということがしばしばあります。


そこで、流用しやすく修正・変更が効きやすいGUIの設計図があると素早く開発が進むのではないでしょうか?

私は、YAMLによるGUI表現があってもいいのではないかと考え、このツールを作成しました。


YAMLとは、タブとハイフンでキーと内容を表記するテキスト形式で、人間でも読みやすい形式に落とし込んであります。

ほとんどの言語で解読ライブラリが開発されているのも特徴です。


ReadableWidgetsでは、そのYAMLファイルをライブラリに通すことで初期化時点で自動的にGUIを生成します。

ライブラリ内では、解読したウィジェットの定義をキーと一緒に辞書型に格納し、GUIを構成していきます。

これで、初期化時点で既に完成されたGUIを実現します。


GUIへ機能を追加

しかしながら、生成されたGUIにはまだ機能が伴っていないため、先ほど生成された辞書を元に機能を追加していきます。

生成された辞書はクラスオブジェクトと同様に扱うことができ、これでボタン押下時の挙動やテキスト設定などが可能になります。


この機能により、初期化時点で完成されたGUIを提示することができるため、わずか20行程度でも「一応動く」GUIアプリが完成します。

このくらい短くできれば、本体のプログラムを読みにくくすることなく手軽に開発・流用できるのではないかと考えています。


中間表現としての活用の模索

また、現在、GUI自体の設計ツールについても言語の壁を越えられるYAMLならではの活用法を模索しています。

ここでは、DrawioからYAMLを生成してGUIに反映しています。

これなら、(私のような)デザインセンス皆無な人でもスムーズに開発ができそうです。


インストール手順

ここまで聴いたみなさんは、おそらく「使ってみたい!!!」と思ったでしょう(?)

ReadableWidgetsはGitHubから入手可能です。

github.com

以下にインストール用コマンドを示します。PySide2が動くPythonが必要です。

git clone https://github.com/Ar-Ray-code/ReadableWidgets
python –m pip install –v ReadableWidgets/PySide2/

Raspberry Pi OSの場合は、aptでPySide2を入手する必要があります。


まとめ

読みやすく汎用的なフォーマットでGUIデザインができれば、開発速度も格段に上がりそうです。

このライブラリはあまりGUI開発を行わない人を対象にしていますが、さまざまなライブラリのパーサーとして機能し、ちょっとしたシステムの別プラットフォームへの移植ができるようにしたらいいなーとか思っています。

例:高機能Linuxコンピュータ→軽量LinuxLinuxコンピュータ→Arduino GFXライブラリ


まだまだ実用というには使いづらい状態なので、サポートいただけるとありがたいです🙏🙏🙏


その他リンク

前身となったライブラリ(PyamlQt)

zenn.dev

LinuxのDesktopショートカットを自動生成するテンプレートを作った

最近ずっと「そこそこ作りやすくて使いやすいアプリケーション」という課題に対していくつか個人的にツールを作っていたりします。


ここでは、Linuxアプリケーションにおけるデスクトップショートカットの作成についてより手続きを単純化するツールを作ったので紹介します。


Linuxアプリのショートカットの作り方

Linuxアプリケーションのショートカットを作成するためには、.desktop ファイルを作成して .local/share/applications/~/Desktop に配置して実行権限を与えるだけでOKです。

.desktop には実行先のパスなどのアプリケーション情報を記載していきます。

ただ、慣れないうちはこの .desktop を正しく作るのは意外と面倒で、正しくパスを通しているはずなのにどこかでコケていることが多々あります。

そこで、エントリーポイントとなるbashファイル情報とアイコンを入力として勝手にインストールするスクリプトを作りました。

github.com

近いうちにReadableWidgetsの機能の一つに入れる予定です。


ショートカットの作り方

以下の画像にインストールの流れを示します。用意すべきものは緑枠の2つのファイルだけです。

アイコンはオプション指定しなかった場合は自動的にダウンロードするようにテンプレートを組んでいるので、実行可能なbashファイルさえあればなんとかなります。


別にインストールしたいアプリがないなら以下のコードで試せます。

インストールしたアプリのリンク先は entrypoint.bash になっており、ターミナル上に hello world を表示して5秒待機するシェルスクリプトです。

インストール時に --terminal (-t) をつけると、実行時にターミナルを開きます。実行中の出力をみたい場合や管理者権限確認ウィンドウを開かない場合は付けておいた方がいいです。

# git clone https://github.com/Ar-Ray-code/icon2entry-Linux.git
cd icon2entry-Linux/
sudo bash icon2entry_linux.bash --entry_bash ./entrypoint.bash -t

インストールするとデスクトップに entrypoint という名前の白紙アイコン(いらすとや)が貼り付けられます。

バツマークが付いて実行できなかったら、右クリック→ "Allow launching" を選択して有効化してください。

次のルートから実行可能です。


アンインストール

--entry_bash (-e) <.bash>--uninstall (-u) オプションでアンインストール可能です。

sudo bash icon2entry_linux.bash --entry_bash ./entrypoint.bash -u


注意

SCRIPT_DIR=$(cd $(dirname $0); pwd) でパス通しができなくなったり、ターミナルのinputが効かなくなったりする場合があります。

パス通しについては、テンプレートに環境変数登録スクリプトを埋め込み→環境変数を登録などで対応しておくといいかもしれません。

コマンドライン引数を少しずつ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: ファイル・フォルダを選択するウィジェット。フォルダモードや拡張子指定などのオプションあり。


飛行機の機材故障による欠航率は0.1%以下

8月の中旬に東京に滞在するために飛行機に乗りました。

九州から東京に行くためには飛行機で行く方が最も早く、保安検査場を通る時間や空港から主要駅への移動時間を考慮しても他の交通手段よりも早いです。

ただ、音・気圧・振動は他のどの移動手段と比べても最悪なので、個人的にはとても疲れます…(できることなら新幹線で移動したい)

つい一ヶ月前に購入したBluetoothイヤホンにはノイズキャンセリング機能がついているので、だいぶマシにはなりました。購入しておいてよかった。

飛行機ではBluetoothWi-Fiは常時使用可能らしいです。(機内モードONが前提となります)


滑走路の途中で?

ご存知の通り、飛行機は揚力を得るために加速を行うための滑走路が必要です。

そのため、実際に出発時刻になっても飛行機が即座に離陸するわけではなく、指定された滑走路に移動する時間(およそ10分〜20分)が発生します。

安全のための機内説明もその間に行われます。


この日は、いつも通り搭乗手続きを済ませていざ出発…だったはずなのですが、

発進しておよそ10分後、滑走路への移動の途中で突然飛行機が止まりました。

その5分後、機内放送で、機長が少しおどおどしたような申し訳ないような声で

「ブレーキ系統の警告が点灯したため、一度引き返して検査を行います。しばらくお待ちください。」

といったアナウンスを行いました。

(何のブレーキのことかは正直あまり覚えていませんが)ブレーキ関連の異常が滑走路の移動途中に通知されたそうです。

空港にいる整備士に診てもらうために15分かけて搭乗口付近に戻りました。


15分後、30分後に1回ずつ「もう少しお待ちください」と機長の方からアナウンスがありました。

(あまり声の調子が変わっていませんでした。元からそうだったのでしょう)


さらに15分後(引き返しから45分後)、以下のような趣旨のアナウンスが、

  • 原因を確認したところ、この空港では対応できないとこのこと
  • この便を欠航として別の便への振替の案内を行う


1時間近く座っていた身からするとなかなか辛いアナウンスでした…

到着後のスケジュールもなんとなく考えていたのにまた飛行機の取り直しです。

欠航時は特急料金の払い戻しシステムはありませんが、次の便に無料で振り替えることができます。

次の便を取り、東京に無事着くことができました。(次の便は、振替のせいか座席がワンランクアップしていました)


機材故障による欠航はレア

飛行機の遅延率・欠航率は国土交通省が4半期ごとに公開しています。

欠航・遅延は季節・空港環境に左右されるため、同じ時期の割合を比較してみました。


令和2年7〜9月

https://www.mlit.go.jp/report/press/content/001384714.pdf

令和3年7〜9月

https://www.mlit.go.jp/report/press/content/001461399.pdf


グラフを眺めると、いずれの航空会社も概ね2%以下の欠航率で、50回に1回くらいは欠航に当たるそうです。遅延率は3%くらいです。

しかし、その欠航の多くが天候によるものです。

JALANAであれば、令和3年夏の欠航内訳は以下の通りです。

JAL ANA
天候 1.77 1.59
機材故障 0.07 0.05
機材繰り 0.25 0.09
その他 0.07 0.16

機材繰りとは、前便の欠航が原因となって欠航する場合のことを指す

なんと、機材故障による欠航は0.1%に満たないらしい。

なんて運が良いんだ(悪い)😇


安全運航に感謝!

離陸する前に警告を示すシステムや警告を元に適切な処置を行うスタッフはさすがプロだなぁと思いました。

完全に自動で安全であればそれに越したことはないですが、便利な機材を扱う場合であっても、理解して判断・操作できる知識と心構えが必要だと感じさせられる良い機会でした。

PlatformIOでプラットフォームのキーワードを取得する(PIO, Arduino)

VSCodeでPlatformIOを使用するとき、ボードでコンパイル分岐させることがあります。

とくにArduinoなどのもはやアーキテクチャすら違うボードではライブラリを同様に扱えるようにしておきたいところです。

分岐を行う場合は #if defined()#endif で挟んでその間に記述を行います。条件に当てはまらなかったときは、そもそもコンパイル対象になりません。

// 使用例(STM32L4xxである場合は、"This is STM32L4xx"と表示)

#if defined(STM32L4xx)
Serial.println("This is STM32L4xx");
#endif

キーワードの取得自体はそれほど難しくありませんが、PIOの仕様が分からず少し戸惑ったところもあったので、備忘録として残しておきます。


キーワードの取得方法

キーワードはボードごとに異なり、それぞれ探す必要があります。最も簡単な方法がプロジェクトのルートにある .vscode/c_cpp_properties.json[configurations][defines] 欄の確認となります。

ファイル内では PLATFORMIO=60103, STM32L432xx, STM32L4xx, ARDUINO=10808, ARDUINO_ARCH_STM32, ARDUINO_NUCLEO_L432KC, BOARD_NAME="NUCLEO_L432KC", HAL_UART_MODULE_ENABLED, USE_FULL_LL_DRIVER, VARIANT_H="variant_NUCLEO_L432KC.h"が定義されています。

=で定義されているものは #if ARDUINO==10808 のように記述して条件分岐させます。

ボード上のデバイスによって左右されやすいものは BOARD_NAME="NUCLEO_L432KC" などの定義を行い、あまり左右されない場合は ARDUINO_ARCH_STM32 などのゆるい定義を行うと良いでしょう。


いつ c_cpp_properties.jsonが生成されるのか

PlatformIOは非常によくできているIDEで、パス通しがArduinoの仕様を残しつつVSCodeの使い勝手の良さを実現しています。

この使い勝手の良さは .vscode/ 内の設定によって実現していますが、これは以下の条件のときに生成されます。

  1. プロジェクトルートに platformio.ini があるとき
  2. 1の状態で新しくVSCodeのウィンドウを開いたとき

自動生成されたあとに削除などを行わなければ、platformio.ini を編集するたびに自動更新されます。


仮に設定のリセットを行いたい場合は .vscode/ を削除し、VSCodeを再度開きます。

VSCodeを再度開く場合:ターミナル以外の場所をクリック後に「Ctrl + Shift + c」を同時押してターミナルを開いた上でVSCodeを閉じて code ./VSCodeを開き直せます。