Hatena::ブログ(Diary)

ザリガニが見ていた...。 このページをアンテナに追加 RSSフィード

2013-07-04

標準入力からプログレスバーを利用する仕組み

cocoaDialogというアプリケーションがあって、インストールするとコマンド呼び出しから、GUIのダイアログ環境を提供してくれる。

例:

f:id:zariganitosh:20130704110556p:image:w450 msgbox(メッセージボックス)

f:id:zariganitosh:20130704110555p:image:w450 inputbox(インプットボックス)

f:id:zariganitosh:20130704110554p:image:w450 secure-inputbox(セキュア インプットボックス)

f:id:zariganitosh:20130704110551p:image:w450 dropdown(ドロップダウン)

f:id:zariganitosh:20130704110553p:image:w450 fileselect(ファイルセレクト)

f:id:zariganitosh:20130704110552p:image:w450 textbox(テキストボックス)


f:id:zariganitosh:20130704110550p:image:w450 progressbar(プログレスバー)

f:id:zariganitosh:20130704110549p:image:w450 progressbar(プログレスバー)

ひと通りのGUIが揃っていて、たいへん便利である。その中でも気になるのがプログレスバーの存在である。時間のかかる処理の進捗状況を教えてくれるあの棒グラフみたいなGUI。今まで単機能のコマンドツールを作っても、進捗状況を的確にフィードバックする方法がなくて困っていた。窮余の策として、Growlや通知センターを使って、「開始しました」「終了しました」なんてメッセージを出力して、しのいでいた...。

そんな時はCocoaDoalogのプログレスバーを利用すれば、快適な待ち時間を演出できそうである!

作業環境

インストール

  • 安定板と開発版がある
    • バージョン 2.1.1(stable=安定板)
    • バージョン 3.0.0-beta7(development=開発版)
  • ここではバージョン 3.0.0-beta7の開発版を試してみた。

f:id:zariganitosh:20130704110557p:image:w450

  • ダウンロードしたCocoaDialogは、アプリケーションフォルダにコピーしてみた。

プログレスバーの特殊性

  • プログレスバーは、その他のダイアログと違って少々特殊な性格である。
  • プログレスバー以外のダイアログは...
    • ダイアログが表示されている間は、処理を中断して、ユーザーの入力を待っている。
    • ユーザーの入力が完了したらダイアログを閉じて、入力値を受け取って、処理を再開する。
  • 一方、プログレスバーの場合は...
    • ダイアログが表示されている間も、平行して処理を継続している。
    • 処理の進捗状況に応じて、バーの長さやメッセージを随時更新する。
    • 処理が完了したら、連動してプログレスバーも閉じる。
  • つまり、プログレスバーはメインの処理とマルチタクスで実行される処理なのである。
  • しかも、メインの処理と連動して、プログレスバーの表示を随時更新する必要もある。

CocoaDialogのプログレスバーの仕組み

  • このマルチタスクと随時更新をどのように実現させているのか?
  • CocoaDialogのプログレスバーでは、更新に必要なデータを標準入力から受け取る仕様になっていた。
  • 更新に必要なデータとは、プログレスバーの割合(0-100の数値)とメッセージとしてのテキスト。
  • 半角スペースで区切って、以下のように指定するのだ。
$ echo 10 running... | /Applications/CocoaDialog.app/Contents/MacOS/CocoaDialog progressbar
  • しかーし、上記コマンドを実行しても、そのプログレスバーを確認する間もなく一瞬にして終了してしまう...。
  • なぜか?それはechoコマンドがすぐ終了してしまうので、それに連携したプログレスバーも終了しているのだ。
  • そこで、5秒ほど時間を稼いでみる。
$ { echo 10 running...; sleep 5; } | /Applications/CocoaDialog.app/Contents/MacOS/CocoaDialog progressbar
あるいは...
$ ( echo 10 running...; sleep 5; ) | /Applications/CocoaDialog.app/Contents/MacOS/CocoaDialog progressbar
  • 括弧で囲いsleepコマンドとグループ化して、それをパイプでCocoaDialogに渡してみた。

f:id:zariganitosh:20130704161158p:image:w450

見えた!

  • さらに、処理に応じてプログレスバーを更新してみる。
{ 
  echo 10 running...; sleep 2; 
  echo 50 running...; sleep 3; 
  echo 100 running...; sleep 1; 
} | /Applications/CocoaDialog.app/Contents/MacOS/CocoaDialog progressbar
  • もうちょっと工夫して、forループで繰り返してみる。
for (( i = 1; i <= 100; i++ )); do 
  echo $i $i/100 running...; sleep .05; 
done | /Applications/CocoaDialog.app/Contents/MacOS/CocoaDialog progressbar

f:id:zariganitosh:20130704165622p:image:w450


  • 終了までの時間が予測できない場合は、--indeterminateオプションでこんな表示にできる。
$ sleep 5 | /Applications/CocoaDialog.app/Contents/MacOS/CocoaDialog progressbar --indeterminate

f:id:zariganitosh:20130704165623p:image:w450


ひとまずこれだけ知っていれば、プログレスバーを便利に使えそう!

まめ知識 ()と{}の違い

  • ちなみに、{ コマンド; ...; }と( コマンド; ...; )の違いは...
    • どちらも括弧内のコマンドをグループ化するが、(上例では括弧内のコマンドの標準出力は、すべてCocoaDialogに送信される)
    • ()では、サブシェルを起動して、サブシェルの環境でコマンドを実行する。
    • {}では、現在のシェル環境で、括弧内のコマンドを実行する。
    • ちなみにサブシェルとは、現在のシェルの子プロセスとして起動したシェルのこと。
$ ( sleep 5; )

$ { sleep 5; }
  • 上記を実行して、アクティビティモニタで観察してみると...
    • ( sleep 5; ) では、bashとsleepが起動した。
    • { sleep 5; } では、sleepのみ起動した。
  • つまり、()でグループ化して実行する場合には、現在のシェル環境に影響を与えない、という利点がある。
    • ()内でcdコマンドを実行するとか、
    • ()内外で同じ名前の変数を利用しているとか、
    • そんな場合でも、()外のカレントディレクトリや変数の内容に影響を与えないのだ。
  • 一方、{}でグループ化して実行した場合は、現在のシェル環境にそのまま影響してしまう...。
    • 但し、自分のbash環境 version 3.2.48(1)-release (x86_64-apple-darwin12) では...
    • パイプを利用した場合には、たとえ{}でグループ化してもサブシェル環境で実行された。
    • つまり、`{コマンド} | コマンド` == `(コマンド) | コマンド`なのである。

まめ知識 パイプのマルチタスク性

  • パイプとは、あるコマンドの標準出力を、次のコマンドの標準入力に接続する方法である。
  • つまり、あるコマンドの処理結果を、次のコマンドでさらに加工できるのだ。
  • 単純なコマンドもパイプで組み合わせることによって、複雑な処理をこなせるようになる。

素晴らしい仕組みである!

  • ところで、コマンドA | コマンドB というパイプ処理があった場合、
  • まずコマンドAを実行して、その処理が終了したらコマンドBを起動して処理を引き継ぐ、ようなイメージを抱いてしまう。

しかし、現実は違う。

  • 現実は、コマンドAの終了を待たずに、コマンドBも起動する。
  • 順序としてはコマンドA、コマンドBの順だが、ほぼ同時に起動するのだ。

  • アクティビティモニタで観察しながら以下コマンドを実行すると、sleep、cat、grepが同時に起動していることがわかる。
$ sleep 10 | cat | grep .

  • そして、以下のようなコマンドを実行すると、毎秒ごとに1から10までカウントが進行する。
  • つまり、forブロック、cat、grepがすべて起動した状態でパイプライン接続され、マルチタスク的に処理されているのだ。
$ for (( i=1; i<=10; i++ )); do echo $i; sleep 1; done | cat | grep .
1
2
3
4
5
6
7
8
9
10

中川 啓至中川 啓至 2013/07/17 11:17 初めまして、いつも楽しく拝見させて頂いております。
cocoadialog の記述で質問があります。
dropdown ダイアログでアイコンが付いた画像を表示しておられますが、一体どのように書いておられるのでしょうか?開発者HPのリファレンスで見ていてもdropdownにはアイコンを付けることはできなさそうで困っています。もしご存知でしたらご教授頂ければ幸いです。

zariganitoshzariganitosh 2013/07/17 18:24 コメントありがとうございます!

CocoaDialogのバージョン 3.0.0-beta7(development=開発版)をダウンロードして、以下のコマンドを試してみてください。
(cocoadialogの部分は、ご自分の環境のCocoaDialogへのパスでお願いします。)
cocoadialog dropdown --items "Mac OS X" "GNU/Linux" "Windows" --button1 'That one!' --icon info

iconオプションについては、以下のURLに説明が載っていました。
http://mstratman.github.io/cocoadialog/#documentation3.0/icons

ちなみに、CocoaDialogのバージョン 2.1.1(stable=安定板)で試したところ、ドロップダウンでアイコン表示はできませんでした。

中川 啓至中川 啓至 2013/07/18 08:41 早速のご返信ありがとうございます☆
昨日バージョン 3.0.0-beta7で試して、うまくアイコンを表示することができました。3.0のアイコンのオプションページにつきましても大変感謝しております。Applescriptをかじる程度の自分からしてみればザリガニさんはかなり突っ込んだことをされているのでいつも勉強になります☆更新楽しみにしていますのでこれからもサイト運営頑張って下さい☆

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証

トラックバック - http://d.hatena.ne.jp/zariganitosh/20130704/cocoa_dialog_progressbar_stdin
リンク元