- Gooey は Python 3 のコンソールアプリケーションを、エンドユーザーが使いやすい GUI アプリケーションに変換し、
argparse 宣言のある関数にデコレータを1行付けるだけで動作する
- 基本的なインストールは
pip install Gooey で可能で、@Gooey デコレータと ArgumentParser を一緒に使うと、Gooey が引数を読み取って WX コンポーネント ベースの UI を構成する
- 標準のウィジェット選択で不足する場合は、
GooeyParser を ArgumentParser の代替として使い、widget 引数を指定して FileChooser、DateChooser、PasswordField、Slider などの カスタムウィジェット を選べる
- 対象ユーザーは CLI を直接扱う開発者向けツールよりも、オフィス向けの「実行して完了」スクリプトや非プログラマ向けツールであり、高度/基本/設定なしの実行モード、メニュー、進捗表示、国際化、アイコン変更、パッケージ化をサポートする
- 動的バリデーションとライフサイクルイベントは実験的機能として扱われており、
optparse は現在サポートされず、ArgumentParser ベースのコードと任意のイベント購読を前提としている
コンソールプログラムを GUI に変える方法
- Gooey はコンソールアプリケーションを、エンドユーザーが扱いやすい GUI アプリケーションへ変換する
- 基本的な使い方は、
argparse 宣言を含む関数に @Gooey デコレータを付けること
from gooey import Gooey
@Gooey
def main():
parser = ArgumentParser(...)
# rest of code
- スタイルと動作はデコレータ引数で調整できる
advanced: 高度な設定画面を表示するかどうか
language: JSON ベースの翻訳を選択
auto_start: 設定画面をスキップして即時実行
target: サブプロセスとして実行するコマンドを指定
program_name, program_description: GUI に表示する名前と説明
default_size: GUI の初期サイズ
dump_build_config, load_build_config: Gooey ビルド設定 JSON の保存/読み込み
monospace_display: 出力画面で等幅フォントを使用
インストールと例
pip install Gooey
- リポジトリをクローンして
setup.py を実行してインストールすることもできる
git clone https://github.com/chriskiehl/Gooey.git
python setup.py install
- 用意されたサンプルスクリプトは Gooey Examples リポジトリから入手できる
- サンプルリポジトリは、Gooey のさまざまなレイアウト、ウィジェット、機能を確認するために提供されている
なぜ必要なのか
- Gooey は、コマンドプロンプトに不慣れなユーザーへそれを見せる問題を減らすために作られた
- プログラムが1つ以上の処理を行うにはオプションを受け取る必要があり、従来は GUI を自作するか、コンソールアプリケーションに引数を渡す方法を説明しなければならなかった
- Gooey は、開発者が慣れたやり方で構成可能なプログラムを作りつつ、表示と操作を GUI として提供できるようにする
向いているユーザーと向かないケース
- 自分自身や他のプログラマ向けのユーティリティ、他のコンソールアプリケーションへパイプする結果を生成するツールには Gooey は適していない場合がある
- オフィス向け実行スクリプト、拠点 A から B へデータを移すツール、非プログラマ向けプログラムには適している
- 複雑なアプリケーションを作りながら、GUI 側を追加コストなしで得ることが目標である
内部動作と argparse のマッピング
- Gooey は実行時に Python スクリプト内の
ArgumentParser 参照をパースする
optparse は現在サポートされない
- 抽出された引数は、指定された
action に応じて component type が割り当てられ、GUI 構成に使われる
ArgumentParser._actions は基本的に次の WX コンポーネントへマッピングされる
store: TextCtrl
store_const, store_true, store_False, version: CheckBox
append: TextCtrl
count: DropDown
Mutually Exclusive Group: RadioGroup
choice: DropDown
GooeyParser とカスタムウィジェット
- 標準のウィジェット選択で十分でない場合は、
ArgumentParser のドロップイン置換である GooeyParser を使用できる
GooeyParser は追加のキーワード引数 widget を提供し、表示するコンポーネント名を指定できる
from gooey import Gooey, GooeyParser
@Gooey
def main():
parser = GooeyParser(description="My Cool GUI Program!")
parser.add_argument('Filename', widget="FileChooser")
parser.add_argument('Date', widget="DateChooser")
- サポートされるカスタムウィジェットには次が含まれる
DirChooser, FileChooser, MultiFileChooser
FileSaver, MultiFileSaver
DateChooser, TimeChooser
PasswordField
Listbox
BlockCheckbox
ColourChooser
FilterableDropdown
IntegerField, DecimalField
Slider
DateChooser と TimeChooser がアプリケーションへ渡す値は常に ISO format であり、GUI の一部にはエンドユーザー設定に応じたローカライズ値が表示されることがある
BlockCheckbox は、長いヘルプテキストがある場合に標準のインラインチェックボックスより見やすくするため、テキストブロックを通常位置へ移し、コントロールの横には短い block_label を表示する
国際化
- Gooey はデコレータの
language 引数で表示言語を選択する
@Gooey(language='russian')
def main():
...
- すべてのプログラムテキストは外部の JSON ファイル に保存される
- 新しい言語サポートは
gooey/languages/ ディレクトリにキー/値を追加する方式
- 現在は 18 以上の翻訳が標準で用意されている
グローバル設定と UI 構成
- Gooey の全体的な見た目と動作はデコレータ引数で調整する
- 主な設定には次が含まれる
encoding: 表示文字エンコーディング、デフォルトは utf-8
advanced: 完全な設定画面または簡易画面を選択
auto_start: 設定なしで即時実行
target: Gooey が自身を再実行する方法を指定
suppress_gooey_flag: カスタム target 使用時に追加 CLI パラメータの注入を防ぐ
fullscreen: 全画面で開始
image_dir: カスタム画像/アイコンディレクトリ
language_dir: カスタム言語ファイルディレクトリ
disable_stop_button, show_stop_warning: 実行中の停止ボタンと警告を制御
show_success_modal, show_failure_modal: 成功/失敗サマリーモーダルを表示するかどうか
run_validators: プログラム呼び出し前にバリデーションを実行するかどうか
progress_regex, progress_expr: 進捗テキストの解析と変換
navigation: TABBED または SIDEBAR
body_bg_color, header_bg_color, footer_bg_color, sidebar_bg_color: 色設定
terminal_font_family, terminal_font_size, terminal_font_color: ターミナル表示フォント設定
menus: カスタムメニューグループと項目
clear_before_run: 再実行時に前回の出力を削除
レイアウトと実行モード
- Gooey は入力をデフォルトで
positional と optional の2グループに分ける
argparse の add_argument_group() を使うと、入力を論理グループにまとめ、各グループのレイアウトを調整できる
parser = ArgumentParser()
search_group = parser.add_argument_group(
"Search Options",
"Customize the search options"
)
search_group.add_argument(
'--query',
help='Base search string'
)
メニュー機能
- Gooey 1.0.2 以降、上部に Menu Bar を追加できる
- メニューは
@Gooey(menu=[{}, {}, ...]) 形式のマップ一覧で指定する
- 各メニューグループは
name と items を持つ
- 各メニュー項目は常に
type と menuTitle を含む
- サポートされるメニュー項目タイプは次のとおり
AboutDialog: 名前、バージョン、ライセンスなどのプログラム情報を表示
MessageDialog: ユーザーに情報メッセージを表示
Link: 指定 URL をデフォルトブラウザで開く
HtmlDialog: 制限付き HTML サブセットを使ったダイアログを表示
動的バリデーション
- Dynamic Validation は実験的機能であり、API が変更または削除される可能性がある
- Gooey はユーザー入力をプログラムへ渡す前に、任意で事前バリデーションを実行できる
- この機能は、ほとんどの Argparse 引数型で使える
type パラメータを活用する
def must_be_exactly_ten(value):
number = int(value)
if number == 10:
return number
else:
raise TypeError("Hey! you need to provide exactly the number 10!")
- バリデーションはデフォルトでは実行されない
- 有効化するには
VALIDATE_FORM イベントを購読する必要がある
from gooey import Gooey, Events
@Gooey(use_events=[Events.VALIDATE_FORM])
def main():
...
- この機能は内部で強い Monkey Patching を使うため、現在はオプトイン方式になっている
ライフサイクルイベントと UI 制御
- Gooey 1.2.0 以降、プログラムに ライフサイクルフック を公開している
- この機能も実験的であり、API が変更または削除される可能性がある
- 現在の主なフックは2つ
- どちらのフックもプロセス完了後に実行される
- ハンドラはパース済みの Argparse オブジェクトと現在の Gooey UI 状態を受け取り、更新された状態を返すと UI に反映できる
- ハンドラは
GooeyParser 作成時に接続する
parser = GooeyParser(
on_success=my_success_handler,
on_failure=my_failure_handler)
from gooey import Gooey, Events
@Gooey(use_events=[Events.ON_SUCCESS, Events.ON_ERROR])
def main():
...
進捗と時間表示
- Gooey は既存のテキスト進捗出力を読み取って Progress Bar を駆動できる
- 単純なケースでは
Progress 83% のような文字列を正規表現でマッチさせて進捗へ変換する
@Gooey(progress_regex=r"^progress: (\d+)%$")
- より複雑な出力は
progress_expr で正規表現マッチ結果を変換できる
@Gooey(progress_regex=r"^progress: (?P<current>\d+)/(?P<total>\d+)$",
progress_expr="current / total * 100")
hide_progress_msg=True を指定すると、進捗にマッチしたテキスト出力をコンソールから隠せる
timing_options を使うと経過時間と残り時間を表示できる
- 時間表示は
progress_regex と progress_expr を使う場合にのみ動作する
アイコンとパッケージ化
- Gooey は標準で6つのアイコンを提供し、
image_dir 引数でカスタム画像ディレクトリを指定して上書きできる
- 画像はファイル名ベースで検索され、上書き可能なファイル名は次のとおり
program_icon.png
success_icon.png
running_icon.png
loading_icon.gif
config_icon.png
error_icon.png
- 実行ファイルのパッケージ化では PyInstaller の使用例が提供されている
- 概要手順は、アプリケーションルートに
build.spec を置き、APPPNAME、name、pathex の値をプロジェクトに合わせて修正してから実行すること
pyinstaller -F --windowed build.spec
1件のコメント
Hacker News のコメント
あ、これ自分のプロジェクトなんだけど ^_^ なぜ HN のトップに上がっているんだろう?
argparseに関するコメントに短く答えると、Gooey はもうかなり古いプロジェクトで、開始当時はargparseが堅実な選択肢だった最近の Gooey 自体は JSON で動作し、
argparseからは切り離されているが、別のインターフェースを作った人がほとんどいないので、argparseが今も主な「公式」インターフェースとして残っている面白い点としては、Python だけでなく 任意の実行ファイル も呼び出せるのでかなり便利: https://chriskiehl.com/article/gooey-as-a-universal-frontend
最後のコミットが 2 年前だという話については、年を取り優先順位が変わると、ニッチなソフトウェアを無料で維持し続ける理由を見つけるのが難しくなる :( 慰めになるかは分からないけど、いつも罪悪感は感じている
不思議なことに、住んでいる街のあちこちに「GOOEY」のグラフィティがあって、放置された issue トラッカーを思い出させる常時通知みたいに見える haha
argparseは今でもかなり良い選択肢。非常に広く使われていて、ドキュメントもよく整っており、比較的簡単Typer と Click も使い勝手は良いが、Typer の「チュートリアル」式のドキュメントは、
Typer.App()がどんな引数を受け取るのか、といった答えを探すにはかなり検索しづらいと感じる今作業しているプログラムは、ユーザーが自分で引数解析を始める構造なので
argparseがよく合う。ユーザーにテキストベース UI を選択肢として提供したいが、Textualize は Click が必要だった気がするし、Textual をコードに直接つないで UI を作ろうとしても数時間使ったのに始めることすらできなかった最近はアプリケーションをプログラムから制御できないことで生じる苛立ちが多すぎる。正直、すべてのアプリケーション機能には API があるべきだという法律があればいいのにと思う
sys.argvを現在の作業ディレクトリのローカルファイルにダンプしているの? https://github.com/chriskiehl/Gooey/blob/be4b11b8f27f500e732...tmp.txtは一意な名前とはほど遠いので、何か見落としているのか気になっているargparseからは切り離されている」という部分が素晴らしいタイトルだけ見てクリックする前は「きっと
argparseに強く依存しているんだろう」と思っていたので、良い意味で驚いた私は熱心な
getoptファンで、開発者の都合よりユーザーの都合の方がはるかに重要だと考えているから。ユーザーは、開発者がどのプログラミング言語やオプション解析ライブラリを選んだかを気にする必要があってはならないargparse、Click、Go のflag/X11 スタイルなど、多くのライブラリは、半世紀にわたって積み上がった集団的な筋肉記憶に刻まれた慣習を壊している。だが中間レイヤーがあれば、二兎を得ることができそうに見えるさらに悪いのは、それを置き換えられる実行可能な代替案も出ておらず、2 つのフォークのどちらも traction を得られなかったこと
これは「
argparseベース」と限定すべきでは?argparseは単純な用途には良いが、Click ベースの CLI も多く、人気のある CLI ライブラリのかなりの数が Click の上に構築されている。argparseや Click ではない Python CLI ツールもあるが、この 2 つがおそらく最も人気のある部類ではあるClick でも動作することは確認されているのか? 今は
argparse関連の内容しか見えない。答えがノーなら、「ほぼすべて」という表現は明らかに誤りで、より正確なタイトルは「ほぼすべてのargparseベースの Python コマンドラインプログラムを完全な GUI アプリケーションに変える」になる最後の意味のあるコミットが 2 年以上前という点もある。それ自体が悪いわけではないが、未解決 issue の数を見ると、プロジェクトへの信頼はあまり湧かない
それでも素晴らしいプロジェクト。こういうものをもっと見たい。README で述べているように力を倍増させるツールなので、誰かが CLI で自動化を作り、技術職ではないオフィスの人たちに簡単に共有できる。Access がデータベースに対して果たした役割に似ている
Access と同じように、規模や複雑さが増すと限界にぶつかるだろうが、小規模なオフィス用途には十分に「良い程度」になり得る
argparseにどんな不足を見ているのか気になる。自分の好みは外部依存を最小化する方向argparseでできないことって何があるの?関連記事:
Gooey: ほぼすべての Python コマンドラインプログラムを GUI アプリケーションに変える - https://news.ycombinator.com/item?id=27490291 - 2021年6月、コメント 115 件
Gooey: コマンドラインプログラムを GUI アプリケーションに変える - https://news.ycombinator.com/item?id=8218785 - 2014年8月、コメント 74 件
OSやシェルが特定のパースライブラリに依存しなくても、プログラムの実行方法をもっとよく理解できるといいのに。
プログラム同士が型付きのJSON/proto形式で通信し、期待する入力・出力の型を覗くだけで、シェルコマンドの構造化・自動補完や完全なGUIを得られるといい。
今のところは、慎重にストローを差し込んで作った程度のものが最善に見える。プログラムごとに複数のシェル用補完ファイルを出力しなければならず、プログラムやパースライブラリごとにフラグのスタイルが大きく異なり、当然GUIもない。
ただ、gRPCの主な問題は、多くの開発者――自分も含めて――が、言語の中で自然に使う解決策に比べると少し扱いが面倒だと感じる点だと思う。
PowerShellが中核にある静的型を完全に活用できるのかはよく分からないが、コマンドラインでオブジェクトをやり取りする能力はほぼ唯一無二だ。
Linuxでは
jc(https://github.com/kellyjonbrazil/jc)とjq(https://jqlang.github.io/jq/)を組み合わせてコマンドラインをつなげられるが、PowerShellの組み込み機能に比べると、まだ余計に2段階の処理が必要になる。GUIは、ときどきコンフォートフードのように感じる。めったに使わないCLIで生じがちな認知負荷なしに、GUIインターフェースをただ見て回れるからだ。
少し話は違うが、数か月前にここでCUDATEXTエディタを知った。単一ファイルのPython APIで、MENUやINPUTSのような任意のGUI要素を使えるようにしてくれる。これらの要素はエディタ自身も使っているものだ。
今はそうした単純なGUI要素で設定を行い、エディタ内からそのままブログを生成している。
併せて見るとよさそうなもの:
“Textual: Python向けの軽量アプリケーションフレームワーク。シンプルなPython APIで洗練されたユーザーインターフェースを作り、アプリをターミナルとWebブラウザで実行” https://github.com/textualize/textual/
camply: https://juftin.com/camply/
この素晴らしいソフトウェアを作ってくれてありがとうと言いたい。Gooeyが好きで、いくつものプログラムでよく使ってきた。
自分にとっては、Pythonスクリプトが重要な仕事をしていて、それを今度は非プログラマーにも使えるようにしなければならない、まさにその領域にぴったり合う。
これはnaked objectsを思い出させる。Javaクラスにいくつかのアノテーションとテーマを定義するだけで、GUI全体、あるいはWebフロントエンドアプリケーションが生成される、というアイデアだった。
とてもクールなアイデアだったが、私の知る限り、結局うまくはいかなかった。
私が思うに、問題はプレゼンテーション、ドメイン、インフラといった垂直方向の関心事が互いに結合してしまう点だ。ドメインモデルの画面上の表現とデータベース上の表現が単純なうちは問題ない。
だが便利機能の多い複雑なインターフェースを作りたいなら、その要件がドメイン層へ漏れ出さざるを得ない。逆に複雑なドメインモデルを作りたいなら、言語レベルで不変条件を強制するのは非現実的だ。
Model参照を持つどんなコードでも任意に触れてしまえるからだ。逆方向にやってくれるものもあるといい。
argparseから得た情報をもとにパラメータスイープを設定し、その結果を適切に実行・保存するシェルスクリプトへ変換し直せるといい。昔のMacintosh Programmer's Workshop、つまり80〜90年代のMac OS向けテキストベースシェルであるMPWには、ほぼすべてのコマンドに対してCommandoという似た機能があった。
Commandoの情報は
cmdoリソースに保存されていた。拡張属性のサポートがまちまちでなければ、今日でも似たようなことができたはずだ。調べてみると、A/UXにもCommandoがあった。FinderでターミナルコマンドをダブルクリックするとCLIオプションを選ぶCommandoボックスが開き、その後シェルウィンドウで実行された。最近のFinderでターミナルコマンドを実行すると、引数なしでシェルウィンドウ内でただ実行されるだけだ。
https://cohost.org/boredzo/post/804893-i-still-want-a-moder
http://toastytech.com/guis/aux3.html