何某日和

「カメラ」のち「ハンダゴテ」ところにより「プログラミング」 ── そんな私“かめきち”のウェブサイト

2012.10.31:色相・彩度・明度

概要

 GUIシリーズ第5弾の「色相・彩度・明度」。先週と先々週に引き続き、色生成のアプリケーションを拵えます。 以前にも述べましたが、今回を含めた3回分の内容は、次回のペアプログラミング「リファクタリング」を執り行うための序章に過ぎません。 アプリケーションの完成に満足するのではなく、そこに見出だせる「モノ無き関係論」を垣間見ることが肝要です。 ちなみにテキストはこちら(http://www.cc.kyoto-su.ac.jp/~atsushi/Programs/ColorHSB/index-j.html

準備

マシンメンテナンス

 そろそろ「日課」になっているでしょうか。日々励行!(日々のマシンメンテナンス

今日の目標

 本日の目標物はこちらです。

完成したアプリケーション(画面例1) 完成したアプリケーション(画面例2) 完成したアプリケーション(画面例3)

「RGB」「CMY」の次は「HSB」です。色相(Hue)・彩度(Saturation)・明度(Brightness)を調整することによって色を生み出します。

準備プログラムの実行

 以下のプログラムを実行して、準備を整えましょう。

| aCollection |
(aCollection := OrderedCollection new)
    add: #url: -> 'http://www.cc.kyoto-su.ac.jp/~atsushi/Programs/ColorHSB/ColorHSB.st';
    add: #comment: -> 'Copyright 2008-2011 KSU (Kyoto Sangyo University). All Right Reserved.';
    add: #bundle: -> #KSU;
    add: #package: -> 'KSU-Template';
    add: #nameSpace: -> #KSU;
    add: #category: -> 'KSU-Template';
    add: #class: -> #{KSU.ColorHSB};
    add: #protocol: -> #examples;
    add: #selector: -> #example1;
    add: #execute: -> [#{KSU.ColorHSB} value example1];
    yourself.
JunSystem
    perform: ((aCollection collect: [:each | each key]) inject: String new
            into: [:selector :key | selector , key]) asSymbol
    withArguments: (aCollection collect: [:each | each value]) asArray
準備完了

今回のGUI部品:Label, View Holder

  • GUI部品の設置(ラベル、ビューホルダー)
  • ちゃんと動きますか?

GUI部品の設置(ラベル、ビューホルダー)

 次の画像を参考にGUI部品を貼り付けてゆきましょう。

Palette Window

今回は、ラベル(Label)とビューホルダー(View Holder)を貼り付けます。MVCのビュー(View)の貼り付け場所を用意したのだと思ってください。

MainWindow hueLabel saturationLabel brightnessLabel hueView saturationView brightnessView

編集後はインストールを。

ちゃんと動きますか?

Do it 完成したウィンドウ

さて、正常動作を確認できたでしょうか。

コードリーディング

  • 【ColorHSB】color, {hue,saturation,brightness}Gauge, initialize, postOpenWith:
  • 【ColorHSB】updateColor, updateColor{Hue,Saturation,Brightness}:
  • 【ColorHSB】hueView, saturationView, brightnessView
  • 【ColorHSBView】aspectArea
  • 【ColorHSBView】defaultControllerClass
  • 【ColorHSBView】displayOn:, displayAspectAreaOn:, displayAspectAreasOutsideOn:, displayMarksOn:
  • 【ColorHSBController】mouseMovedEvent:
  • 【ColorHSBController】redButtonPressedEvent:
  • 先週までとの違い
  • 復習「ValueHolder」

【ColorHSB】color, {hue,saturation,brightness}Gauge, initialize, postOpenWith:

 さあ、息をするようにプログラムを説明してみてください。3週目ですから、きっとできるはず。

【ColorHSB】updateColor, updateColor{Hue,Saturation,Brightness}:

 これもまた同様にすらすらと説明ができるはずですね。

【ColorHSB】hueView, saturationView, brightnessView

 先週までには見られなかった構造が現れました。ColorHSBViewクラスのオブジェクトを生み出して、互いに自己紹介していますね。

【ColorHSBView】aspectArea

 この領域が何を表すか理解できますか。insetとは何を指すのでしょう。defaultInsetの値を変更するとどうなるのでしょう。

【ColorHSBView】defaultControllerClass

 何故このようなメソッドが必要なのかを考えてみてください。(クラスへのアクセサですよね。つまり...)

【ColorHSBView】displayOn:, displayAspectAreaOn:, displayAspectAreasOutsideOn:, displayMarksOn:

 面白いテクニックが散りばめられているので、覗いておいて損はないでしょう。「areasOutside」や「insetBy:」などなど。

【ColorHSBController】mouseMovedEvent:

 「crossHair」「normal」といったカーソルが登場しますが、それ以外にも様々に用意されています。見つけてみてください。

【ColorHSBController】redButtonPressedEvent:

 「Processor yield」だとか「Delay」だとか。時間を守るプログラムというか、エルゴノミック(人間工学的)なプログラムになっていますね。 あまり頻繁に更新したところで、人間の視覚能力では認知することができませんので、250000マイクロ秒(0.25秒)間隔で再描写します。 そして、これは他のプロセスへの配慮も兼ねていて...ということを次節で簡単に述べておきます。

先週までとの違い

 前回と前々回は、すでに用意されていたビュー・コントローラ(スピンボタンやスライダーなど)を用いて拵えていましたが、 今回は、MVC構造のモデル・ビュー・コントローラを全て明示的に用意しました。 つまり、GUI部品も必要に応じて独自に開発することができるということです。

復習「ValueHolder」

 少ししつこいかもしれませんが、ValueHolderについて説明してみてください。

他のプロセスへの配慮 ── 並行プログラミングへの導入

(追記:2012.11.02)

生きているプロセスたち

 時間を守り(Delay)、CPUの利用権を譲り(Processor yield)、つまり他に生きているプロセスに配慮している様子が伺えると思います。 では、その「他に生きているプロセス」に会ってみましょう。

| aTable aCollection |
aTable := Dictionary new.
ObjectMemory globalGarbageCollect.
Process allInstances do: 
    [:aProcess |
    aCollection := aTable at: aProcess priority
        ifAbsentPut: [OrderedCollection new].
    aCollection add: aProcess].
^aTable
生きているプロセスたち

単に「Process allInstances」をインスペクトすれば得られるのですが、敢えて各プロセスのpriority(優先度)ごとに仕分けして捉えてみました。 10だの50だの100だの様々な優先度があり、そこにまた様々なプロセスが入っていると思います。 (ちなみに「globalGarbageCollect」は、不要になったオブジェクトを成仏させることに相当します。)

優先度

 今日拵えたプログラムで言えば、ColorHSBControllerのredButtonPressedEvent:が良い例となりましょう。 「250000」と指定している部分があると思いますが、それを「0」に変更してみてください。 挿入されているDelayでは一切待機せず、どんどん繰り返し処理を行います。 しかし「Processor yield」を挿入しているのですから、他のプロセスを妨げたりはしないはず。 ちょっと試してみましょう。

明度のゲージの描写が追いつかない例(1) 明度のゲージの描写が追いつかない例(2)

 描画を終える前にまた更に描画を行うとなると、描画処理の後半(例えば明度のゲージ)は不完全な状態で表現されることが頻繁にあります。 ただ、アプリケーションの動きとしてはそれほど問題もなく、正常に動いているように見えますよね。

 さて、では3Dの例題プログラムを実行して、同時に動きを与えましょう。 Jun LauncherのExamplesメニューから「Cube」を実行して、ウィンドウ左上の手のアイコンを選択した状態で、3Dオブジェクトをドラッグ&ドロップしてみてください。 一定方向に回転し始めるはずです。

Jun Launcherの「Cube」 Cube

 3Dオブジェクトが回転している状態で、先のColorHSBのゲージを動かしてみてください。「Processor yield」が入っているわけですから、3DオブジェクトもColorHSBも問題なく動くはず...。

止まる3Dオブジェクトと動くColorHSB

 いかがでしょう。3Dオブジェクトの回転は止まり、ColorHSBのゲージは正しく動いているのではないでしょうか。(実行環境にも依存するのですが。) 「Processor yield」を入れて他のプロセスに配慮しているにも関わらず、何故このような不公平(CPU利用権の独占的使用)が起こるのでしょうか。 実を言うと、先に触れた「優先度」という言葉がポイントになります。

JunOpenGLDisplayController createMovementProcess

 JunOpenGLDisplayControllerクラスのインスタンスメソッドにcreateMovementProcessというものがあります。(上図のもの。) 3Dオブジェクトを回転させる際に呼び出されて、回転処理を行うプロセスを生成してくれます。 ここで注目したいのが次のフレーズ...

movementProcess priority: Processor activeProcess priority - 1.

「Processor activeProcess」とは、このフレーズが書かれたプログラムを実行しているプロセス自身を指しますが、そのプロセスにpriorityというメッセージを投げると、 そのプロセスの優先度が数値として得られます。つまり、現在のプロセス優先度よりも1だけ低い優先度をmovementProcess(回転処理のプロセス)に設定しているのです。

プログラムでも「ひとりじゃないって」

 たかだかCubeの回転処理のためにCPUの利用権を独占してはならない...と自粛しているかのような構造を目の当たりにしたと思います。 この節の冒頭で、生きているプロセスを集めてみましたが、その数は100を超えていて、実に様々なプロセスが同時に共存しているのだということが理解できたはずです。 講義「ソフトウェア工学II」にて再三耳にしたであろう「ひとりじゃないって」という言葉は、何もソフトウェアプロジェクトに限定した話ではなく、 ソフトウェアプログラムにも言えることなのです。

ProcessorScheduler initialize

 ユーザが実行するプログラムの処理よりも、補助記憶装置への処理が優先され、それよりもオブジェクトメモリへの処理が優先され、 それよりも主記憶装置への処理が優先され...と、プロセス(仕事)に優先順位を付けているのです。 意識はしていないかもしれませんが、普段の生活の中でも物事の優先順位を判断した上で行動しているではありませんか。

 振り返ってみてください。これまでに拵えたプログラムには、優先順位を調整するような機能を付していたでしょうか。 まさかCPUの利用権を独占するような「傲慢なプログラム」を書いてきたのでは無いですか?!

 他のプロセスに気を配るようなプログラムを書こうとすれば、相応の力量が必要となるでしょうが、実際に私達が用いているソフトウェアはこの気遣いをしっかりこなして動いているのです。 難しくとも「ソフトウェア技術者」としては避けて通れない道。これから自分でプログラムを拵える際には、少しは「他のプロセス」を意識してみてください。

リファクタリングに向けて

 今日はリーディングに時間をかけることになるので、ライティングの時間を取れないはずです。(もし取れたらば、前回・前々回の機能拡張を同様に付してみるとか。) そこで、次週「リファクタリング」への導入だけ済ませておきましょう。

抽象化(モノ無き関係論)

 簡単な例題で「抽象化」を説明します。また研究の話か、とは思わないでくださいね。。。

  • そば
  • うどん
  • スパゲッティ

上に掲げた3つは「麺」の具体例で、そのいずれも「粉を練って細長く成形した食べ物」を指しています。 さて、これらをクラス図にしてみるとどのように描き表せるでしょうか

麺のクラス図

「そば」「うどん」「スパゲッティ」といった具体例を抽象化して「麺」という抽象クラスを生み出しました。 「麺」として持ち合わせている性質は麺クラスにて定義しておけば、その具体例である「そば」「うどん」「スパゲッティ」それぞれは「麺」の定義を継承していますから、重複する定義は必要ありません。 「そば」は風味を持ち、「うどん」はコシを持ち、「スパゲッティ」は若干の芯を持ち、それぞれ特有の性質のみを定義すればよいのです。 自分のすべき仕事のみに注力するという、まさに“餅は餅屋”精神ですね。 ここで「新しい麺の具体例を追加する」ことを考えてみると、抽象クラスによる恩恵を理解できるはず。。。

さてさて、

  • ColorRGB
  • ColorCMY
  • ColorHSB

この3つも「麺」と同じように抽象化できないでしょうか。

色生成アプリケーションのクラス図

来週は、抽象化によるColorAbstractクラスの作成と、それに伴うColor{RGB,CMY,HSB}の改造を、つまり「リファクタリング」を行います。

さいごに:保存を忘れずに

 くれぐれもデータの保存を忘れないようにしましょう。