見出し画像

Retro-gaming and so on

PythonとスクリプトとGUIフロントエンド

このブログでは、「全てのソフトウェアの基本はREPL(Read-Eval-Print Loop)だ」と言うパースペクティヴで語ってる。
REPLはインタプリタの実装法、として有名だが、その実、あらゆるソフトウェアでこの「実装テクニック」を使えると言う主張だ。
しかし、人によっては、

「でもREPLを持たないソフトウェアもあるんじゃない?」

と思う人もいるだろう。その通りだ。

実はこのブログでは、今まで明言してなかったけど、ある種特殊な言い回し、っつーか、定義を採用してる。

  • ソフトウェア: REPL構造を持つプログラムの事
  • スクリプト: REPL構造を持たないプログラムの事
気をつけて欲しいのはこれらは一般的な定義ではない。しかし、同じ「プログラム」とは言っても「REPL構造を持つ」「REPL構造を持たない」2つを区別する言い回し、っつーか単語がないんだ。
従って、暫定的に「REPL構造を持つプログラム」をこのブログでは「ソフトウェア」ないしは「アプリ」と表現し(※1)、「REPL構造を持たないプログラム」を「スクリプト」と呼称してる。

そもそも「スクリプト」とはなにか(※2)、と言うと、「書き捨てのプログラム」的なニュアンスから出て来た一種バズワードだ。「プログラムと呼べる程大したモンじゃない」と言う言外のニュアンスがある。Windowsで言うトコのBATファイルなんつーのもスクリプトに大別される。
また英語話者にとっては「プログラム」と言う呼称は「何か難しそう」と言う印象があるらしく、一方「スクリプト」だと「何か簡単そう」と言う印象があるようだ。
よって、「新しくプログラミング言語を設計して流行らせたい」場合、技術的には全く同じなのにも関わらず「プログラミング言語」と名乗るより「スクリプト言語」と名乗らせた方がウケがいい模様だ(※3)。
要はこれはマーケティングの問題であって(※4)、プログラミング言語とスクリプト言語は全く同じで違いがない。単に「スクリプト」が英語話者にとっては「簡単そうに響く」ってだけの話らしい。日本語が母国語の僕らには良く分からん話だ(笑)。
まぁ、そういった「スクリプト」の背景を一応知ってもらって、このブログ上ではあくまで「REPL構造を持つプログラム」をソフトウェア/アプリと呼称し、「REPL構造を持たないプログラム」をスクリプト、と呼称してる。
繰り返すが、これらは単に「このブログ上の」ローカル定義で一般論じゃない。

表面的には、だ。Windows95以降だとGUIでのマルチタスクが基本になっててピンと来なくなってるだろうが、端末で起動した場合、OSのコマンドが使えなくなって「プログラム内に入っちゃう」ってのがREPLを持つプログラム、ソフトウェアの挙動だ。

Apple II の大ヒットソフトウェア「VisiCalc」。Microsoft Excel等の「表計算ソフトウェア」の遠い祖先。これも起動したら「ソフトウェアから出るまで」全てのコントロールが「VisiCalcの下にある」REPLの特徴を持っている。

ワードプロセッサ、表計算ソフト、データベース、ゲーム等、これらのソフトウェアは全て「REPLを持ってる」。
Microsoft WordなんかのGUIのワープロだと今日ではピンと来ないかもしれないが、昔のDOSベース時代では「WordをDOSから起動したら」Wordの中に入り、Wordの操作体系に支配されてしまう。いわゆる「Quit」をしないとDOSに戻る事が出来ない。
これがRead-Eval-Print Loopを持ったプログラムの特徴と言える。

Googleも使用してるオープンソースのデータベース「MariaDB」。これも起動するとMariaDBの「中に入り」、Quitするまでそこから脱出出来ない。


ゲームを「起動する」と言う事は、ゲームが持ってるREPLに「入る」事で、これも一旦ゲーム内に入ったら「Quit」しない以上その外に出る事が出来ない。

一方、Read-Eval-Print Loopを持たないプログラムの場合、端末で起動すると、「指定した処理」を行って、そのまま閉じてしまう。つまり、別にユーザーとのやり取りは生じないわけだ。
ちなみに、このブログの定義によると、

  • インタプリタはソフトウェアである
  • 一方、コンパイラはスクリプトである
と言う事だ。
一般的にはコンパイラは「ソフトウェア」なんで、こう書くと違和感があるだろうけど、あくまで「このブログ上の定義では」コンパイラはスクリプトなわけだ。インタプリタと違って特にユーザーとインタラクティヴなやり取りをするわけでもないし、コマンドライン引数に与えたファイルを覗いて「何らかのコードに変換して」そのまま終了する。


Pythonは起動すると端末上で支配的になり、これもQuitしないとずーっとPythonと言う「ソフトウェア」の中にいる事になる。PythonインタプリタはREPLを持ってるから、だ。


一方、例えばRustのビルドツールは、コンパイルを行って生成したプログラムが「走った」のを確認したらそのまま閉じてしまう。これはREPLがないプログラムの特徴だ。

もちろん、突き詰めればこのブログで言う「ソフトウェア」と「スクリプト」の間に差はない。と言うか、REPLがあるプログラムの場合、仮にRead、Eval、Printが別々のファイルに書かれてたら、Read、Eval、Printと言う「別々のスクリプト」が単独で存在してる、って捉える事が可能だ。特にEvalは実際は「何らかの計算をしたら閉じてしまう」スクリプトそのものが本体、って事になってる。それはこの記事を読んだ人には明らかだろう。Loopしなければスクリプトと大差がない、って事なんだ。
逆に言うと、REPLとは、Read、Eval、Printと言う「単体のスクリプト群」をLoopを使って「組み合わせてる」に過ぎないんだ。

さて。
実のトコ、歴史的に見ると、「REPLを持ったプログラム」が多いのは黎明期のPCに始まった、民生機の方じゃないか、って思う。
と言うのも、民生機の「OS」と言うのは長らくシングルタスク・・・つまり、何らかのプログラムを「起動」したら、他の事が出来なかったから、って事に由来してると思う。つまり、民生機の「OS」上では「ソフトウェアの中に入る」って事は凄く自然な事だったんだ(※5)。
一方、UNIX系OSの場合、マルチタスクベースな為、端末が「なにか」に専有される事を好まない。従って、圧倒的に「スクリプト」の存在率が高いと思う。出てくるツールも圧倒的に「スクリプト」だ。
まぁ、単なる印象論っていえば印象論だけどな。
また、このブログの定義で言うと「OSのコマンド」の正体ってのは基本的に全て独立したスクリプトの事に過ぎない。逆に言うとOSのカーネル(※6)と直接やり取りするスクリプト群が「OS組み込みのコマンド」って事になり、また、それらと見分けが付かないような「スクリプト」がUNIX系プログラムとして流通している。


UNIX系OSでもっともお世話になるコマンドls。これもこのブログで言うトコの「スクリプト」で、フォルダ内にある全フォルダやファイルを「リストアップして表示する」コマンドだ。
なお、WindowsのDOS窓でのコマンドでこれに当たるのがdirだ。

今回はこのテのスクリプト、要するにUNIX系のコマンドラインツールのお話、だ。

実のトコ、このテのCLIのプログラムは使いづらいだろ(笑)?

「あれ、以前、プログラマにとっては使いやすいって言ってなかった?」

とお叱りを受けるかもしんない。うん、言った言った(笑)。でも俺ってテキトーなんだわ(笑)。
いや、「プログラマにとっては使いやすい」ってのは事実なんだよ。大枠はな。
ただし、「プログラマにとっては使いやすい」ってのは「プログラムに組み込む上では扱いやすい」って意味なんだ。
元々、プログラマが「UNIXすげぇ!」ってなったのは(それにも時間がかかったが・※7)、UNIXが「単体では大した事がない」スクリプトを大量に用意してくれて、自在にそれを呼び出してプログラミングする事を推奨してたから、だ。
要はUNIXは今で言う「APIの塊」で、API指向のOSだった、ってのが特徴で、それまでの「ソフトウェアは自分で一から全て作り上げる」と言うスタイルに反旗を翻してたんだ。
UNIXは、ソフトウェア作成とは「既存のモノを再利用しつつ作り上げていく」モデルだ、とハッキリと提示した最初の例と言えるだろう。
一方、「端末相手に直に叩いて」となるとさすがのプログラマも「ちょっとな・・・」って思う人が多いと思う。いや、コマンドライン信奉者ってのもいるけどよ。
ただよ、UNIX登場時ならいざ知らず、実際問題コマンドラインってのが相当「ややこしくなった」ってのは言えるたぁ思うんだ。1つは設計上の問題、もう1つは環境変化、って事だよな。
ちと説明していってみよう。

元々UNIXは本当に「単体の小さなスクリプト」をOSのコマンドとしてたくさん抱えていた。下手すれば「ユーザーの望むような」出力さえ出来ないくらい「小さな」プログラム群だ。
UNIXの方策では、ちょっと複雑な事をやるだけ、でも端末上でコマンドを組み合わせないとならない。UNIXはそのシステムさえも「関数は1つの事だけを行う」と言う哲学を貫いていたんだ。
ところが、ここに変化が起きる。GNU/Linuxの登場だ。

余談だけど、FSFは通称「Linux」を「GNU/Linux」と呼称してくれ、と言っている。と言うのもLinuxとは実はOSのカーネルの事で(作者はフィンランド人のリーナス・トーバルズ)、いわゆるOSの「コマンド」群はFSFが作ったコマンド群が流用されていて、作者が違うんだ。
FSFは元々、「フリーなOSを作ろう」として、先にコマンド群の開発を終了、そしてカーネルが後回しとなったが、このカーネルの作成が難航したんだ。そのうち、Linuxと言うカーネルが登場し、FSFが作ったGNUコマンド群を利用して「UNIX代替OS」となった。見方を変えるとLinuxがGNUコマンド群を「乗っ取った」ように見える(笑)。
いずれにせよ、通称「Linux」と言うOSはカーネルとコマンド群は全く別の作成者に作られたモノが統合されたものを指している(※8)。

話を戻そう。
FSFはMITの研究者であったリチャード・ストールマンと言うLisperが立ち上げた組織で、当初は「フリーで使えるLisp OS」を企画してたらしい。
ところが、フツーのコンピュータアーキテクチャだとLispマシンOSを動かすのが効率的じゃない事。そして大学の中古ミニコンでUNIXが流行りだした事を受けて

「フリーソフトウェアとしてOSを流通させてフリーソフトウェア運動を推進するなら流行りのUNIXのクローンOSにした方がエエんちゃう?」

と方針を変更する。
言っちゃえば、UNIXと言うOSのコンセプトを良く知らずにUNIXのコマンド群のクローンを作り出したわけだ。
そしてその途中で、「このコマンドとこのコマンドは組み合わせて1つにした方がエエんちゃう?」を何過程にも渡って行った。
結果、コマンドラインオプション(※9)と言うモノが複雑怪奇化した、と言う背景がある。

ここでもう一個余談を入れよう。
以前どっかで書いたが、「ソフトウェア作成」に於いては二大潮流がある。1つはUNIXを擁するAT&T陣営の方式。「プログラム一個は1つの事しかしない」を徹底している。元々C言語自体も「小さなプログラムを書く」為に開発された言語で、こいつの出番は本来、「UNIXが提供するユーティリティでは足りない時」、小さな機能を追加する時だけ、なんだ。「デカいソフトをフルスクラッチで作る為のプログラミング言語」じゃないんだ。
もちろんC言語「だけ」で作られたデカいプログラムは山ほどある。しかし、その殆どは「UNIXと全く関係ないプラットフォーム」であるMS-DOS上で、言わば「想定外の使われ方をした」結果なんだ。
そしてUNIX陣営は何でも短縮形で書く。関数名なんて見ても省略し過ぎて意味不明だ。
これがUNIXを擁するAT&Tの「プログラミングスタイル」だ。
対するMITは真逆の事をする。1つのプログラムに何でも詰め込もうとする。そして関数名は誰が見ても分かりやすい名前をつける。長くても構わないんだ。
結果、MIT作成のソフトウェアは一体何のソフトウェアだか分からなくなる(笑)。こういうMIT方式をキッチンシンク・アプローチと呼ぶ。
キッチンシンク・アプローチで出来た代表格のソフトウェアが、ご存知GNU Emacsだ。一応テキストエディタ、って事になってる。しかし、Webブラウジングも出来るしゲームも出来るしTwitterも出来る。
要は何でも突っ込みすぎていて良く分からないモノになってる原因、ってのがまさしくキッチンシンク・アプローチなんだ。


GNU Emacsのブラウザでisamさんのブログを開いた例。

もう分かったろ?UNIXは「小さいプログラムの集合体」だったんだけど、Linuxが出てきてGNUのツール群と組み合わさった、って事は、MITのキッチンシンクアプローチが混ざった、って事だ(※10)。
結果、Linuxが広まるに連れて、キッチンシンクアプローチのコマンドラインが増えたんだ。それらは元々のUNIX思想に反して複雑なコマンドラインオプションを持つものばっかになった。UNIX思想の「小さなプログラム」は主流じゃなくなったんだ(※11)。

もう1つ理由を挙げよう。要はコンピューティング環境の変化だ。
ミニコン文化は殆どはテキスト文化なんだよ。そしてテキストを一括処理するならコマンドラインが強い。
一方、民生機は「ゲーム機代わり」として作られたトコもあり、最初から「グラフィック」は重要な要素だったんだ。
ところが、グラフィックを扱う場合、コマンドラインじゃどうにもならん、って現象が一般化してきたんだよな。
当たり前、だ。
そしてユーザーの要求度はどんどん上昇する。昔は「研究所」で「学者相手に」テキスト処理だけしときゃ良かったんだけど、PCはそんな環境を上回るモノを要求された。音楽をやったり絵を描いたり動画を編集したり・・・・・・。
そう、明らかにコマンドラインじゃ対処出来ない要求と現象が、むしろ「民生機側」でたくさん起こったんだ。

ここで言ってるのは、例えばコマンドラインで動画編集が出来ない、って事ではない。
むしろWindowsも使える有名なコマンドラインでのプログラム、ffmpegなんつーのがある。フリーソフトのコマンドラインプログラムなんで、色んなソフトの動画編集のエンジンとしても利用されている。
gifアニメなんかも作れるし、僕がちょくちょく作ってるgifアニメはこのffmpegで作ってる。



しかし、ffmegで動画から一部を切り取ってgifアニメを作るのは、今回のケースだと例えばこんなんだ。

ffmpeg -ss 19:45 -i midv-400.mp4 -t 11.28 -an -r 10 -s 320x180 -f gif ~/ピクチャ/buzz.gif

コマンドラインオプションだらけでクソ長い
いや、ホント、こんなの一々打つのはさすがにアレなんだよ。
言い換えると、やっぱffmpegなんかは他のプログラムから動画編集エンジンとして使うのが正解なんだよな。どんなに長いコマンドラインになってもプログラムから操るんなら障害にはならない。
一方、こんなにコマンドラインが長ければ、もはや人が使うモノではない、と言えるだろう。
いずれにせよ、PCの発展で、コマンドライン「だけ」だと対処しきれない状態になってるのは間違いないんだ。
そしてコマンドラインオプションが複雑になればなるほど、マニュアルを読むのがクッソめんどくさくなる。

さて、ここまでコマンドラインの「弱点」を見てきたが、「現代」の通常の「プログラムの使用環境」だとGUIが台頭してしかるべき、ってのはその通りなんだ。
繰り返すが、CLIでプログラムを書く際に、他のプログラムを「再利用」しようとする。ただ、その対象がGUIだと扱いが厄介になるし、どうせ利用するのなら「コマンドラインオプションが豊富」なプログラムを重宝するのは間違いない。
ただし、その「コマンドラインオプションが豊富」なプログラムは、人間が直接使うには向かない。
そこで、Linuxなんかで良くあるのは、コマンドラインのスクリプトに「GUIを被せて」、通常人間が使う際にはそっちのGUI版を使う、ってテだ。
そうすればGUIデザインにそこまで凝らなくてもいいし、「豊富なコマンドラインオプション」を直打ちしないで済む。
例えば前出のffmpegにもGUI版のWinFFってのが存在する。これも本体はCLIのスクリプトであるffmpegなんだけど、それにGUIのガワを被せて配布してるわけだ。


WinFF。GUIのガワはFreePascalで書かれている模様。いくつかのffmpegでのコマンドラインオプションの「組み合わせ」をプリセットとして提供して色々な面倒をGUIで「選ぶ」事によって解決する。

こういうGUIのガワは当然本体じゃないんだけど、こういうのをGUIフロントエンドと呼ぶ。実際は本体とは(プログラム上)関係なく独立してて、GUIで受け取った情報を端末に送り込んで目的のプログラム(本体)を実行する、ってのがその仕組みだ。
Linuxではこういう「フロントエンド」が、知名度が低い割には多い(※12)。そして、GUIフロントエンドはユーザーが作って別に配布する、とか言うケースがかなり見受けられる。
言い換えると、他人が作ったコマンドラインのプログラムにGUIフロントエンドを付けるだけで「何か立派なソフトを作った」気になれるし(笑)、そして場合によっては人に有難がられるわけだ(笑)。
ここでは、「写真をPDFに変換する」と言うお題でGUIフロントエンドを作ってみよう。そして(Python以外でも可能だが)Pythonはこういう事にも向いている。
一時期、「グルー言語」(※13)ともてはやされたPythonの使い方を見てみよう。

今回はコマンドラインツールとしてimagemagickを用いる(※14)。これはある種有名なツールで、かなりの種類の画像フォーマットを自在に変換出来る強力なツールだ。
このブログの例を挙げると、例えばPC-9801/8801のゲーム画面をエミュレータのスクショ機能で撮ると、大体がbmpフォーマットとなる。ところが、最近は改良されたんだけど、goo blogはちょっと前までbmpフォーマット画像を受け付けなかった(今は受けつけるようになったが)。



そういう時がimagemagickの出番で、bmpフォーマットを例えばpngとかjpgとかに簡単に変換してくれる。当然pdfにも変換出来るわけだ。
imagemagickはLinuxでのデファクトスタンダードな「画像フォーマット変換ソフト」なんだけど、Windowsでも使えるし有用だ。
この機会にダウンロード/インストールしてみよう。

imagemagickの使い方は簡単で、

convert 元画像ファイル 変換画像ファイル

で画像を変換出来る。
例えばこういう写真(名称を取り敢えず00001.jpgとする)があって、



端末上で

convert 00001.jpg 00001.pdf 

と打てばpdfに変換される。



問題は「端末で」なんだけど、じゃあPythonで操れるのか。
答えは「操れる」。と言うかマトモなプログラミング言語だと大体、プログラミング言語側から端末を呼び出して使える機能が搭載されてるんだ。
ホントの事を言うと、「プログラミング言語を学びたい」って思ってるプログラミング初心者に「真っ先に」教えなアカン機能だとは思ってるんだ。と言うのも下手すると「プログラムを書く必要さえなくなる」(笑)。
ところが、こういう機能を初学者に教えないんだよ。これが大問題だと思っていて、少なくとも「UNIX系プログラミングの哲学」を教える方が分かってないか、知らない、って事だ。
結果、「おまけの知識」的なトコへ追いやられてる。
実は

「プログラミング、と言う面倒臭い行為は出来るなら避ける事」

ってのがプログラミングの極意その一、なんだ(笑)。はい、これホント。極意がいくつあるかは知らんがな(笑)。
ただ、UNIXは「フリーソフトウェアの世界」だからこういう哲学が出てきたんだけど、一方MS-DOSから始まるWindowsの世界だと「商用利用」だ何だのライセンス形態があって、結果こういう「哲学」が普及しなかった、って事なんだ。「ソフトを売る」世界じゃこういったUNIXの考え方はちと馴染まないんだよな(※15)。

さて、Pythonで端末のコマンドを使うにはsubprocess.runを使う。
例えばこういう写真(00032.jpegとする)があって、



import subprocessしたPythonで

subprocess.run(['convert', '00032.jpeg', '00032.pdf'])

を走らせるとまたもやpdfが生成されるだろう。


os.chdirは実行するフォルダを移動するコマンド。写真が保存されているフォルダに移動してる。



subprocess.runに対する注意点は、端末ならガーッと打ち込むコマンドラインを要素毎に文字列にして、それをリストにする事だ。

convert 00032.jpeg 00032.pdf -> ['convert', '00032.jpeg', '00032.pdf']

ただし、「リストにしなきゃいけない」と言う事は文字列そのものを操作するより俄然ラクになる。つまり、Python自体から操りやすい。
勘のいい人は既に気づいたろう。REPLでのEvalに当たるトコがこの部分で、GUIだったらMVCのModelにあたる。つまり、既存のコマンドラインスクリプトがあれば、プログラムの実行部分をラップするだけ、で何も書かなくて良いわけだ。

wxGladeで作るGUIのヒエラルキーをこうしてみる。



これはこういうGUIの見た目を想定している。



この辺はいいだろう。毎度毎度言ってるが、GUI自体のコードを書くのはクソメンド臭いので、全部アッサリとwxGladeに任せる。
wxGladeに生成させたコードはここここに置いておく。
さぁ、imagemagickのGUIフロントエンドを作っていこう。
ところで、今まではwxGladeに生成させたPythonファイル(デフォルト名はwxGlade_out.py)をコピーしたファイルを改造してGUIアプリを作っていたが、ここでは新しいテクニックを導入する。オブジェクト指向の華形、継承オーバーライドだ。
繰り返すが、オブジェクト指向の真の目的は、同じような機能を持った関数を同名で定義したい、と言う名前空間の抱える問題の解決だ。そこで「データ型」が形成する名前空間にそれを操作する関数を所属させる事によって、「同名」の関数同士がぶつからないようにする、と言うのが一般に見られる解決策だ。
この時の、名前空間を形成する「データ型」をクラスと呼び、その「データ型に所属する関数」を言語によってメンバ関数メソッドと呼ぶ。
オブジェクト指向の真の目的はそれだけ、なんだけど、オブジェクト指向の人気は傍流である継承とオーバーライドに支えられている。今まで、wxGlade_out.pyをコピーしたファイルを「改造」して目的のGUIのプログラムを作っていたが、言っちゃえばこの「コピーして改造」をプログラミング言語レベルで行うのが継承とオーバーライドだ。大事な事なんで二度言う。プログラミング言語レベルで「コピー」を行うのが継承で、「改造」を行うのがオーバーライドだ。
新しく作るプログラム、pdf_converter.pyではwxGlade_out.pyからまずはMyFrameimportして、MyFrameを継承してViewを作る。

# wxglade_out.MyFrame を継承して作る    
class View(MyFrame):
 def __init__(self, *args, **kwds):
  # 親クラス(wxglade_out.MyFrame) を初期化
  super().__init__(*args, **kwds)
  # View 独自のプロパティ
  pub.subscribe(self.setProperties, "View")
 def setProperties(self, x):
  self.text_ctrl_1.Clear()
  self.button_2.Enable(True)
 # wxglade_out.MyFrame の chooseFile をオーバーライド
 def chooseFile(self, event):
  with OpenFile(self, wildcard = "BMP, GIF, PNG, JPEG files (*.bmp;*.gif;*.png;*.jpeg;*.jpg)|*.bmp;*.gif;*.png;*.jpeg;*.jpg") as fileDialog:
   if fileDialog.ShowModal() == wx.ID_CANCEL:
    return
   d = fileDialog.GetDirectory()
   self.text_ctrl_1.write(",".join([f"{d}/{i}" for i in
                fileDialog.GetFilenames()]))
 # wxglade_out.MyFrame の convert をオーバーライド
 def convert(self, event):
  self.button_2.Enable(False)
  pub.sendMessage("Controller.read",
  x = {"files": self.text_ctrl_1.GetLineText(0),
    "resize": self.combo_box_1.GetStringSelection(),
    "pdf": self.text_ctrl_2.GetLineText(0)})

冒頭のClass View(MyFrame):ViewMyFrameを継承、つまり全機能を「コピー」する。
その後、Viewの初期化過程を記述するが(__init__)、super().__init__(*args, **kwds)がやってるのは、継承元のMyFrame、親クラスとかスーパークラス、と呼ぶが、の初期化だ。もっと正確に言うと、ViewMyFrameからコピーした全機能を初期化してる。
一方、PyPubSubに付いての記述はスーパークラスのMyFrameには存在しない。よってこれはView独自のモノで、言い換えるとViewMyFrameの機能を受け継ぎながら拡張してるわけだ。
chooseFileconvertMyFrameにも存在したが、事実上「何もしない」空のメソッドだった。ここでは実際に「身のある」機能を同名で再実装してる。これをオーバーライドと言うわけだ。
chooseFileの方は後で定義されてる、wx.FileDialogを継承したOpenFileを利用して、「PDFに変換したい」画像ファイルを選択する。複数ファイルを選択可能にする為、OpenFileではwx.FD_MULTIPLEを利用してるが、この辺はマニュアルを見た方がいいだろう。wx.FD_MULTIPLEを使わないとファイルは1つしか選択出来ない。


wx.FileDialog。styleにwx.FD_MULTIPLEを組み込まないと、ファイルの複数選択が出来ない。

また、メソッドGetFilenamesはマウスで複数選択されたファイル名を取得するが、残念ながら「どのフォルダにあるか」と言う情報は取れない。そのため、GetDirectoryで取得したフォルダ階層情報と合わせて、文字列に変換した後、self.text_ctrl_1に書き込んでいる。
convertは「変換」ボタンがクリックされると実行されるメソッドだ。一回クリックされれば取り敢えず「クリックできない」状態になった後、Controllerへと情報を送る。必要な情報はself.text_ctrl_1に記述された情報、そしてサイズ変更の情報、あとはself.text_ctrl_2に書かれた出力ファイル名(デフォルト名はout.pdfになる)だ。
3つの情報が後にimagemagickに渡されるコマンドとして再構成されるわけだが、どの部分が何なのか、を明示する為、リストを使わず敢えて辞書にして送信するようにした。
ちなみに、imagemagickでの「画像のサイズ変更」はコマンドラインオプションの-resizeで行う。
例えば元画像を二倍の大きさにしたい場合、

convert 元画像 -resize 200% 変換画像

ってのがその書式で、コマンドラインオプションの-resizeで自在に画像の拡大・縮小が可能だ。
拡大・縮小の数値は、今回新しく導入したwx.ComboBoxのテキスト情報から取ってきてる。

wxComboBox。良くある「選択肢」を提供するウィジェットの1つ。

先にも書いたし、上のWinFFでも見たが、大体、この「コマンドラインオプション」がメンド臭いんだ。今回は簡単な例なんで画像のサイズを変更するimagemagickの-resizeだけを扱ったが、大体、GUIフロントエンドでは、こう言った「複雑なコマンドラインオプションの組み合わせ」の代表的なトコだけを選んで、「プリセット」として提供する。そしてユーザー側は、こういった「コンボボックス」からその「プリセット」から使いたいモノを選んで使う、と言う事だ。
こういう方式だと細かい設定に対してはお手上げにならざるを得ないんだけど、逆に言うと、今回の場合だと「画像の33%の大きさが欲しい」とか、あんま無いだろう。レアケースに関しては「無視して構わん」っつー事だ。
まぁ、GUIフロントエンドを作る側からすれば、複雑なコマンドラインオプションをマニュアルを見て調べないとならないが、一旦GUIフロントエンドを作成してしまえば見返りはそこそこ大きいとは思う。
使いやすいソフトウェアの第一歩、だ。

あとはネタ的にはいつもと同じだ。ControllerViewから送られてきた情報からコマンドライン引数を組み立てた後、Modelへと送る。

class Controller(object):
 def __init__(self):
  pub.subscribe(self.read, "Controller")
 def read(self, x):
  # print(x) # デバッグ用
  pub.sendMessage("Model.eval",
           x = [i.strip() for i in x["files"].split(",")]
           + ["-resize", x["resize"]]
           + [x["pdf"] + ".pdf"])

正確には、subprocess.runが受け取れるような(コマンドライン引数の)「文字列のリスト」をここで生成してるわけだ。
繰り返すが、ControllerはREPLで言うRead(読み込み部)に当たり、Lispでは特にピンと来ないが、実はこの部分で構文解析を行う。
今回の「コマンドライン引数(のリスト)生成」ってのは構文解析の代替だ。もちろんアレコレ「一気」に書いてもいいけど、そうじゃなくってMVCと言うモデル(翻ってREPLと言うモデル)、ではパーツ毎の「役割」と言うのをキチンと把握しよう。Controllerの役割から言うと「コマンドライン引数の組み立て」こそがまさにここでやるべき事なんだ。

Modelは今回はそう、基本的にコマンドライン引数を受け取って、subprocess.runでimagemagick(convertコマンド)を走らせるだけ、だ。

class Model(object):
 def __init__(self):
  pub.subscribe(self.eval, "Model")
 def eval(self, x):
  # print(x) # デバッグ用
  pub.sendMessage("View.setProperties", x = subprocess.run(["convert"] + x))

これで終了、だ。
Viewにメッセージ送信してるが、それは画像をPDFに変換してる間、「変換ボタン」を使えなくしてるわけだが、画像の変換が終わると、Viewにメッセージが飛ばされる。そのタイミングでView.setPropertiesで表示されたファイル名を全消去し、「変換ボタン」を使えるように戻す。
そうすればユーザーも「あ、変換が終わったのね」と分かるだろう。
そのためだけに(いや、それが重要なんだけど)Viewにメッセージを送信している。
言わば「変換終わったよ!」メッセージをViewに投げてるんだ。

と言うわけで、今回はオシマイ。
こうやって「他人が作った」CLIのプログラムもGUI化出来る、ってわけだ。
また、自分で作ったPythonスクリプトも当然GUI化出来る事になる。
簡単な例だけど、重要なのはGUIそのものよりもsubprocess.runだ。「マトモなプログラミング言語」は端末で他のプログラムを走らせるような機能を持ってる、と言う事。それを利用すれば「自分でプログラムを書かなくてもプログラムを作れる」と言う事。OSの複雑な機能やコマンドラインを組み合わせて望みの結果を得る事。それもGUIのプログラムの心臓部になり得る、と言う事。
是非ともそういう「UNIX的プログラム作成」のアイディアに馴染んで欲しい。
以上。


なお、今回のソースコードはここに置いておく。

※1: 「アプリ」とは元々「アプリケーション・ソフトウェア(Application Software)」の略称だ。

※2: ScriptScribe(記述する)から来てる。「記述されたモノ」をScriptと言うわけだが、プログラミングではScripting、とまた新しい動詞(っつーか動名詞)が出て来てる。
正確なトコはもとこんぐさんにでも訊いてくれ(笑)。

※3: この代表的な例が、Javaと全く関係ないのに「JavaScript」と言う名称を付けられた、正式名称「EcmaScript」と言うプログラミング言語だ。

※4: 人によっては「スクリプト言語」は「インタプリタ」で、「プログラミング言語」は「コンパイラ」だ、って思い込んでるが、それは全く正確な定義じゃない。
っつーかコンピュータ関連の用語はこのようにマーケティングな意図によるバズワードが元になってて、考えすぎると損するようになっている。

※5: 今の人は驚くかもしれんが、昔は元々、例えば「エクセルで作ったデータ」をコピーして「ワードに貼り付ける」って事が出来なかった。後に「疑似マルチタスク」と言うのが出てきて可能になったが、そもそもシングルタスクOSは「2つのソフトウェアを同時に立ち上げて」そのソフト間で「データのやり取りを行う」なんつー使い方を想定していなかった。
基本的に「1つのソフトウェアを立ち上げ」ると、そのソフトウェアが「支配的」になる事が当たり前だったんだ。この当時の民生機では「ソフトウェアを動かす」と言う事はコンピュータの全リソースをそのソフトウェアに集中させる、事を意味してた。

※6: 「核」。実はこれこそ、がOS「そのもの」であり、文字通り中核で、メモリ管理や何やらやってる場所だ。コマンドラインの「コマンド」は平たく言うとカーネルとやり取りするスクリプト群で、それらは実は「OSの一部」ではない。
なお、このカーネルの実装法に「モノリシックカーネル」方式と「マイクロカーネル」方式と言うのがあって、比較して新しい技術なのは「マイクロカーネル方式」だ。
平たく言うとカーネルを更にその中核部分と「その他」のサーバー群に分けよう、としてるが、ぶっちゃけて言うとこの「マイクロカーネル方式」を実装して上手く行ってる「実例」はいまだねぇんじゃねぇか、って思う(FSFが挑戦してるが結果何十年かかってて開発は上手く行ってない)。
LinuxやWindowsはモノリシックカーネル方式、でMacもなんだかんだ言って基本いまだそれだ、と思う。
ちなみに、DOS窓等の端末エミュレータをShell(シェル: 貝殻)と呼称するのは、カーネルを「貝の身」として見立てて、それを包んでる物体、と言うメタファ故だ。

※7: 以前書いたが、任天堂がスーファミを出した当時、開発環境でUNIXが指定された際、ゲームプログラマ側のブーイングは物凄かったらしい。DOSで慣れてた人たちがUNIXに「慣れる」には時間がかかったんだ。
どんなプログラマでさえ、いきなり「UNIX」を使わされた際に「すげぇ!」とは残念ながらならず、結局これも「慣れの問題」になるわけ。

※8: 一方、同じUNIX系OSであるMac OS Xの場合、カーネルはMachと呼ばれる米国カーネギーメロン大学が作ったモノをベースとしていて、コマンド類はFreeBSDをベースとしたモノを採用している。同じUNIX系でもツール的にはLinuxとは全く違う。
そんなわけで、Mac OS XはWindowsとは違って、多くがLinuxと同様にフリーソフトベースになってて(とは言ってもかなり改造されている模様)、Appleの技術者は言っちゃえば、「GUI部分」に全力投球しているわけだ。

※9: 端末で走らせる「コマンド」に対してのオプショナル引数。デフォルト値を持ってるが、ユーザーが特定の値を指定する事が出来る。

※10: 考えてみればUNIXは失敗したMULTICSと言うOSの反省で生まれた、と言う側面がある。そしてMULTICSはMITとAT&Tのコラボだった。
何が起きたのか言わずと知れるだろう(笑)。

※11: だからこそ、古来からのUNIXスタイルを愛好する人々はキッチンシンク・アプローチを嫌って、Linuxを選ぶ代わりにFreeBSDを選ぶ。
あるいは現在ではMac OS Xを選ぶんだ。

※12: 何故に「知名度が低い」のか、と言うとLinuxではコマンドライン信者が多いから、だ。この辺頑なな人が多いのに比べると、パソコンの「使いこなし」自体は劣ってるが、Windowsユーザーの方が柔軟だ。

※13: グルーとは「糊」の事。1つのプログラムと別のプログラムを「貼り合わせる」のに向いてる言語、の意。少なくとも日本では、Python2.x時代、そういう使い方で人気があった(日本語処理が怪しかったんで普及に限界があったが)。

※14: 実はPythonでは有志によりimagemagickへのバインディングが提供されているが、今回の趣旨によりそれは用いない。

※15: インターネットの普及でこういった「フリーソフトウェア」のアイディア自体が広まって、「オープンソース」が盛り上がったのは、こういった「ソフトの再利用を妨げない」方が結果ラクだ、ってプログラマが実感したから、だ。
一方、Windowsで言う「フリーウェア」はソフトの再利用や、ソースコードの改変は許さない事が多く、FSFが言う「フリーソフトウェア」とは意味が違ったりする。こっちのは単なる「タダで配布してるソフトウェア」と言う意味以上ではないので、混同しない事。
  • Xでシェアする
  • Facebookでシェアする
  • はてなブックマークに追加する
  • LINEでシェアする

最近の「プログラミング」カテゴリーもっと見る

最近の記事
バックナンバー
人気記事