.. _chapter-gui: グラフィカルインターフェースを活用しよう ================================================================ 本章では、本書の第2版執筆以降に追加された「クリッカブルオブジェクト」、「Sliderコンポーネント」、「Formコンポーネント」について解説します。全ての追加機能を使う実験を作るのが難しそうでしたので、ひとつの実験を作り上げるのを目標とはせずに新機能を体験するデモを作ることを目標とします。 クリックした視覚刺激オブジェクトを記録しよう --------------------------------------------------------------- PsychoPy 1.90.0から、Polygonなどの視覚刺激オブジェクトをクリックするという方法で反応を記録することが出来るようになりました。従来でも :numref:`第%s章 ` で解説したテクニックを使って同様のことは実現できたのですが、より簡単になりました。さっそくこの機能を確かめてみましょう。 - 実験設定ダイアログ - **[単位]** をheightにする。 - trialルーチン - Polygonコンポーネントを1つ配置し、以下の通り設定する。 - **[名前]** をbutton_yesにする。 - **[開始]** を「時刻 (秒)」で0.5に、**[終了]** を空白にする。 - **[形状]** を長方形にする。 - **[サイズ [w, h] $]** を[0.3, 0.1]にする。 - **[位置 [x, y] $]** を[-0.2, 0]にする。 - button_yesをコピーして **[名前]** をbutton_noにする。 **[位置 [x, y] $]** を[0.2, 0]にする。 - Textコンポーネントを1つ配置し、以下の通り設定する。 - **[開始]** を「時刻 (秒)」で0.5に、**[終了]** を空白にする。 - **[色]** をblackにする。 - **[位置 [x, y] $]** を[-0.2, 0]にし、**[文字列]** をYesにする。 - 上記のTextコンポーネントをコピーし(名前は何でもよい)、 **[位置 [x, y] $]** を[0.2, 0]、 **[文字列]** をNoにする。 - Mouseコンポーネントを1つ配置し、以下の通り設定する。 - **[開始]** を「時刻 (秒)」で0.5に、**[終了]** を空白にする。 - **[ボタン押しでRoutineを終了]** を有効なクリックにする。 - **[クリック可能な視覚刺激 $]** に button_yes, button_no と入力する。 - trialルーチンを繰り返すループを作成する。ループのプロパティはそのままで構わない。 完成したら実行してみましょう。 :numref:`fig-yes-no-buttons` のような画面が表示されるはずです。マウスを操作して、灰色の背景のどこでクリックしてもルーチンが終了しないこと、白い長方形(=ボタン)のどちらかをクリックするとルーチンが終了することを確認してください。左クリック、右クリックのどちらでもルーチンが終了することも確認しておくとよいでしょう。ループは初期値で5回となっているので、白いボタンを5回クリックしたら終了します。 終了したら、trial-by-trial記録ファイルの内容を確認しましょう。 :numref:`fig-yes-no-buttons` の下に示すように、mouse.clicked_nameという列が存在していて、クリックした方のボタンに対応するPolygonオブジェクトの **[名前]** の値が保存されているはずです。 .. _fig-yes-no-buttons: .. figure:: fig09/yes-no-buttons.png :width: 80% Yes、Noのボタンを押してルーチンを終了します。どちらのボタンを押したかは記録ファイルで確認できます。 以上でおおよそ使い方はおわかりいただけたのではないかと思いますが、補足しておきましょう。Mouseコンポーネントの **[ボタン押しでRoutineを終了]** を「有効なクリック」に設定して **[クリック可能な視覚刺激 $]** にカンマ区切りで視覚刺激の **[名前]** を列挙すると、列挙された刺激上にマウスカーソルを重ねた状態でボタンをクリックした時のみルーチンが終了します。この機能を使うと、画面上に描画したボタンをマウスでクリックするという方法で参加者の反応を記録することが出来ます。 いくつか注意点を挙げておきますと、まず **[クリック可能な視覚刺激 $]** に挙げられた視覚刺激が重なり合っていた場合、重なっている位置をクリックしてもいずれかひとつの名前しかtrial-by-trial記録ファイルには保存されません。Polygonコンポーネントで描いた図形の場合は(十字などでも)見た目通り、その図形上にカーソルの先端がなければclickと判定されませんが、Textコンポーネントの場合は文字の周辺に設定された透明な領域もクリックの対象となります。Imageコンポーネントで透明な背景を持つ画像を使った場合も、見た目とは異なり透明な部分もクリックの対象となります。 とりあえずユーザーインターフェースとしては :numref:`fig-yes-no-buttons` のようなボタンでも十分かと思いますが、もっと凝ったデザインにしたければボタンを画像で用意してImageオブジェクトで表示すると良いでしょう。また、マウスカーソルを重ねただけ(ホバー)で視覚刺激の色が変わるなどの効果を付けたい場合は、:numref:`第%s章 ` で解説したCodeコンポーネントと組み合わせる方法を使う必要があります。反応時間を記録したい場合も :numref:`第%s章 ` の方法のほうが楽かもしれません。 **[クリック時に保存するパラメータ $]** は、刺激の色や回転角度などのパラメータが変化する実験において、クリック時の値を記録したい場合に便利です。これも簡単なデモを作ってみましょう。 - 実験設定ダイアログ - **[単位]** をheightにする。 - trialルーチン - Polygonコンポーネントを1つ配置し、以下の通り設定する。 - **[名前]** をstimにする。 - **[開始]** を「時刻 (秒)」で0.5に、**[終了]** を空白にする。 - **[形状]** を三角形にする(初期値)。 - **[サイズ [w, h] $]** を[0.1, 0.3]にする。 - **[位置 [x, y] $]** を[0.2*cos(t), 0.2*sin(t)]にし、「フレーム毎に更新」にする。 - **[回転角度 $]** を 45*t にし、「フレーム毎に更新」にする。 - **[塗りつぶしの色]** を $[sin(t), 1, 1] にし、「フレーム毎に更新」にする。 - Mouseコンポーネントを1つ配置し、以下の通り設定する。 - **[開始]** を「時刻 (秒)」で0.5に、**[終了]** を空白にする。 - **[ボタン押しでRoutineを終了]** を有効なクリックにする。 - **[クリック可能な視覚刺激 $]** に stim と入力する。 - **[クリック時に保存するパラメータ $]** に ori, pos, fillColor と入力する。 - trialルーチンを繰り返すループを作成する。ループのプロパティはそのままで構わない。 完成したら実行してみましょう。三角形が色を変化させつつ回転しながら円軌道を移動していきます。適当な時点で三角形をクリックしてルーチンを終了させてください。5回繰り返して終了させたらtrial-by-trial記録ファイルを確認しましょう。mouse.clicked_ori, mouse.clicked_pos, mouse.clicked_fillColorという列が存在していて、それぞれ回転角度、位置、塗りつぶしの色が出力されているはずです。便利な機能ですが、 **[クリック時に保存するパラメータ $]** には **[回転角度 $]** のようなプロパティ設定ウィンドウに表示されているプロパティ名ではなく、Builder内部で使用されているオブジェクトのデータ属性の名前を書かなければならないことが難点です。 :numref:`tbl-clickable-object-parameters` に示すパラメータ名は多くの視覚刺激で使用できるので、覚えておくと便利です。 .. tabularcolumns:: |p{6zw}|p{24zw}| .. _tbl-clickable-object-parameters: .. csv-table:: **[クリック時に保存するパラメータ $]** で使える主なデータ属性 :delim: ; :widths: 24, 64 name; **[名前]** に対応 opacity; **[不透明度 $]** に対応 ori; **[回転角度 $]** に対応 pos; **[位置 [x, y] $]** に対応 size; **[サイズ [w, h] $]** に対応 color; **[色]** に対応(Polygonコンポーネント以外) fillColor; **[塗りつぶしの色]** に対応(Polygonコンポーネント) lineColor; **[輪郭線の色]** に対応(Polygonコンポーネント) 最後にちょっとした応用例を示しておきましょう。写真などの画像において特定の領域を参加者にクリックさせて反応を記録したいとします。このような領域はしばしばRegion of Interest, ROIと呼ばれるのでROIと表記することにしましょう。このような実験を作りたいとき、画像をImageコンポーネントで表示して、ROIをPolygonコンポーネントで作成して **[クリック可能な視覚刺激 $]** に列挙するという方法をとることが出来ます。 :numref:`fig-image-roi` の右側は写真の例で、中央やや左寄りに野鳥が写っています。 :numref:`fig-image-roi` 上段はROIを写真の上に描画した状態で、野鳥がいるところに赤い枠が描かれています。下段は写真をROIの上に描画した状態で、実験参加者にはROIは見えません。しかし **[クリック可能な視覚刺激 $]** が別の視覚刺激の下に隠れていてもクリック判定されるので、クリックを検出できるというわけです。 .. _fig-image-roi: .. figure:: fig09/image-roi.png :width: 80% 画像の特定の領域をクリックさせる課題を作成するときには、Imageコンポーネントの下にクリック可能な刺激を隠すという方法があります。 実際にこの実験を作成するときには、写真のファイル名とそれに対応するROIの位置とサイズを記入した条件ファイルを作成する必要がありますが、条件ファイルだけを眺めても適切にROIが設定されているか判断するのは難しいので、Builder上でコンポーネントの順番をひとつ変更するだけでROIの描画をON/OFFできるというのはきっと便利なはずです。 ROIを隠すには他にも **[不透明度 $]** を0にするなどの方法もありますが、ひとつの画面にROIが複数個ある場合はすべてのROIのパラメータを変更しないといけませんので、コンポーネントの順番を変更する方が手間がかからないでしょう。 チェックリスト - 視覚刺激をクリックしてルーチンを終了するようにMouseコンポーネントを設定できる。 - trial-by-trial記録ファイルからクリックされた刺激の名前を読み取ることが出来る。 - trial-by-trial記録ファイルにクリックした刺激の不透明度、回転角度、位置、サイズ、色を出力するように設定することが出来る。 - Imageコンポーネントの下にクリック可能な視覚刺激を配置して、画像上の特定の領域をクリックするとルーチンが終了するようにすることが出来る。 Sliderコンポーネントを使ってみよう ---------------------------------------------------------------- SliderコンポーネントはPsychoPy 3.0.0で追加された新しいユーザーインターフェースで、RatingScaleコンポーネントと非常によく似ています。本節では両者の違いに注目しながら紹介しましょう。 SliderコンポーネントはRatingScalesと同じ「カスタム」カテゴリにあります(:numref:`fig-slider-properties`)。ルーチンに配置してプロパティ設定ダイアログを開くと、:numref:`fig-slider-properties` のように「基本」、「体裁」、「データ」の3つのタブがあります。 .. _fig-slider-properties: .. figure:: fig09/slider-properties.png :width: 80% Sliderコンポーネントのアイコンとプロパティ まず「基本」タブから見ていきましょう。毎度おなじみの **[名前]** 、 **[開始]** 、 **[終了]** は他のコンポーネントと同様です。 **[Routineを終了]** もKeyboardコンポーネントやRatingScaleコンポーネントと同じなので、特に問題はないでしょう。 一方、少なくともバージョン3.0.5で確認した限りは、 **[単位]** 、 **[不透明度 $]** と **[回転角度 $]** は項目は値を設定しても効果がありません。**[位置 [x,y] $]** はSlider **[単位]** で指定した単位ではなく実験設定ダイアログで指定した単位に従うようです。 **[サイズ $]** は幅、高さの順で2つの値を指定しますが、幅の値が高さの値より大きい場合は横向き、幅の値が高さの値以下ならば縦向きの尺度が描かれます。「より大きければ横/以下ならば縦」なので、幅と高さの値が同一ならば(実用的かどうかは別として)縦向きとなります。縦向きの尺度が簡単に描けるのはRatingScaleコンポーネントとの大きな違いです(:numref:`fig-slider-size`)。なお、 **[サイズ $]** の単位も実験ダイアログで指定した単位に従うようです。 .. _fig-slider-size: .. figure:: fig09/slider-size.png :width: 80% **[サイズ $]** の幅と高さの設定で縦向き、横向きが自動的に切り替わります。 **[目盛]** と **[ラベル]** はSliderコンポーネントの使いこなしのカギになるプロパティです。まず、 **[目盛]** を指定すると尺度で記録される値は数値となります。目盛をつける位置を「小さい値から順番に」カンマ区切りで指定します。ばらばらな順番で並べると正常に動かないので注意してください。:numref:`fig-slider-numerical` 下の例が示すように、数値は等間隔である必要はありません。数値の最小値と最大値がそのまま尺度で記録できる値の最小値と最大値になります。 .. _fig-slider-numerical: .. figure:: fig09/slider-numerical.png :width: 80% **[目盛 $]** に数値をカンマ区切りで並べると数値が記録される尺度となります。ラベルに文字列を指定しても、記録される値は数値です。 **[ラベル]** は目盛に付けるラベルをカンマ区切りで並べます。'あてはまらない', 'どちらでもない', 'あてはまる'のように各項目を文字列として(つまりシングルクォーテーションかダブルクォーテーションで囲んで)記述しますが、数値の場合は-2, 1, 0, 1, 2のように書くこともできます。 **[目盛]** と **[ラベル]** で食い違う値を設定することもできますが、反応として記録されるのは **[目盛]** で定義した値です。例えば、 **[目盛]** が1, 2, 3で **[ラベル]** が-1, 0, 1なら、画面上には **[ラベル]** で定義した-1, 0, 1が表示されますが、-1のところをクリックしたときに記録される値はそれに対応する **[目盛]** の値である1です。まあこれは極端な例でしたが、「まったくあてはまらない」を0、「あまりあてはまらない」を1、…という具合に数値を割り当てて分析するような場合には意味があるでしょう。 なお、**[目盛]** と **[ラベル]** の項目数は同一にそろえるか、あるいは **[ラベル]** の項目数が2つでなければいけません。 **[ラベル]** の項目数が2つの場合は **[目盛]** の最小値と最大値に割り振られます。 **[ラベル]** の項目数が3つ以上で、なおかつ **[目盛]** の項目数と一致しない場合は正常に表示されません。 **[ラベル]** の項目が1つしかない場合はエラーとなり実行できません。 .. _fig-slider-categorical: .. figure:: fig09/slider-categorical.png :width: 80% **[目盛 $]** を空欄にするとカテゴリカルな尺度になります。 カテゴリカルな値を記録する尺度を作りたい場合は、 **[目盛]** を空欄にして **[ラベル]** に値を列挙します(:numref:`fig-slider-categorical`)。見た目は :numref:`fig-slider-numerical` 下の例とよく似ていますが、 :numref:`fig-slider-numerical` 下の例で「全然」を選択すると 1 が記録される一方、:numref:`fig-slider-categorical` の例では「全然」をクリックすると「全然」という文字列が記録されます。分析の方法に応じて使い分けるとよいでしょう。 **[精度 $]** は数値を記録する尺度でのみ意味を持ち、マーカーの位置が取りうる値を指定します。例えばこの値が1ならば、マーカーの位置は1の倍数、つまり整数となります。ただし、例外的に 0 はシステムで可能な最小の値に対応します。わざわざこんなややこしい設定をする方はいないと思いますが一応補足しておきますと、 **[目盛]** に3, 15、 **[精度 $]** に4を指定した場合、マーカーの位置は4の倍数となるのですから尺度の両端の値である3や15には設定できるかどうかは保証されません。 続いて「体裁」のタブに進みましょう。 **[フォント]** と **[色]** は尺度の描画に用いるフォントと色の指定です。 **[反転]** をチェックすると、ラベルが反対側(横向きなら上、縦向きなら右)に描かれます。 **[スタイル]** は尺度の見た目を変更するオプションで、最初の3つ(slider, rating, radio)は全体的な見た目を指定します。残りのオプションは最初の3つと組み合わせて使うもので、labels45はラベルを45度傾けて描きます。whiteOnBlackはモノクロで尺度を描画します。ただし、whiteOnBlackを指定した場合でもラベルの色は **[色]** での指定に従います。triangleMarkerはratingとradioの時に有効で、三角形のマーカーを描きます。:numref:`fig-slider-styles` に例を示します。 .. _fig-slider-styles: .. figure:: fig09/slider-styles.png :width: 80% Sliderのスタイルの例 最後の「データ」タブには **[履歴を記録]** 、 **[評定を記録]** 、 **[反応時間を記録]** という項目があります。これらはRatingScaleコンポーネントの「高度」タブにあったものと同じなので、改めて解説する必要はないでしょう。以上でSliderコンポーネントのプロパティの解説はおしまいです。 ここまでで解説したほかにRatingScaleコンポーネントと異なる点として、Sliderコンポーネントにはacceptボタンを表示したり、尺度の説明を表示したりする機能がないことが挙げられます。尺度の説明はTextコンポーネントで描画できますし、acceptボタンは前節のクリッカブルオブジェクトを使えば実現できるでしょう。RatingScaleコンポーネントとSliderコンポーネントのどちらを使うかは好みの問題ですが、Sliderコンポーネントの方がいろいろと柔軟な運用ができそうです。 チェックリスト - Sliderコンポーネントで縦向き、横向きの尺度を描画できる。 - Sliderコンポーネントで数値を記録する尺度とカテゴリカルな値を記録する尺度を使い分けることが出来る。 - Sliderコンポーネントでslider、rating、radioのスタイルを使い分けられる。 - Sliderコンポーネントでマーカーを三角にする、ラベルを45度傾ける、モノクロで描画するといったスタイルの変更ができる。 Sliderコンポーネントで刺激をリアルタイムに調整しよう ------------------------------------------------------------------- Sliderコンポーネントは反応の記録のほかにも、実験中の刺激パラメータの調整にも使うことが出来ます。本節では、:numref:`fig-slider-dynamic` に示すような画面で、スライダー(本節の使い方では尺度と呼ぶのもおかしいのでスライダーと表記します)を動かしてリアルタイムに刺激の回転角度、不透明度、色を変更してみましょう。 .. _fig-slider-dynamic: .. figure:: fig09/slider-dynamic.png :width: 80% Sliderで刺激のパラメータをリアルタイムに調整することが出来ます。 さっそくですが、Builderで実験を新規作成して以下のように作業してください。 - 実験設定ダイアログ - **[単位]** を height にする。 - trialルーチン - Sliderコンポーネント1つ配置して以下の通りに設定する。 - **[名前]** を slider_color にする。 - **[サイズ $]** を (0.04, 0.4) にする。 - **[位置 [x,y] $]** を (-0.4, 0) にする。 - **[目盛]** を空欄にして、 **[ラベル]** を 'white', 'blue', 'orange' にする。 - **[Routineを終了]** のチェックを外す。 - slider_colorをコピーして slider_oriの名前で貼り付け、以下の通りに作業する。 - **[目盛]** と **[ラベル]** を 0,90,180 にする。 - **[サイズ $]** を (0.5, 0.03) にする。 - **[位置 [x,y] $]** を (0, -0.3) にする。 - slider_oriをコピーして slider_opacの名前で貼り付け、以下の通りに設定する。 - **[目盛]** と **[ラベル]** を 0, 1 にし、 **[精度 $]** を 0.1にする。 - **[位置 [x,y] $]** を (0, -0.4) にする。 - Codeコンポーネントをひとつ配置する。 - Polygonコンポーネントをひとつ配置して以下の通り設定する。 - **[名前]** を stim にする。 - **[終了]** を 空欄にする。 - **[不透明度 $]** を slider_opac.markerPos として「フレーム毎に更新」にする。 - **[回転角度 $]** を slider_ori.markerPos として「フレーム毎に更新」にする。 - **[塗りつぶしの色]** を $polygon_color として「フレーム毎に更新」にする。 - Polygonコンポーネントをひとつ配置して以下の通り設定する。 - **[名前]** を button にする。 - **[終了]** を 空欄にする。 - **[サイズ $]** を (0.4, 0) にする。 - **[位置 [x,y] $]** を (0.15, 0.05) にする。 - Textコンポーネントをひとつ配置して以下の通り設定する。 - **[終了]** を 空欄にする。 - **[色]** を black にする。 - **[文字の高さ]** を0.05にする。 - **[位置 [x,y] $]** を (0.4, 0) にする。 - **[文字列]** を OK にする。 - Mouseコンポーネントをひとつ配置して以下の通り設定する。 - **[終了]** を 空欄にする。 - **[ボタン押しでRoutineを終了]** を「有効なクリック」にする。 - **[マウスの状態を保存]** を「なし」にする。 - **[クリック可能な視覚刺激 $]** に button と入力する。 最後に、Codeコンポーネントにコードを入力する。まず、 **[Routine開始時]** のタブに以下のように入力する。 .. code-block:: python slider_ori.markerPos = 0 slider_opac.markerPos = 1 slider_color.markerPos = 0 polygon_color = 'white' 続いて **[フレーム毎]** に以下のように入力する。 .. code-block:: python color_choice = slider_color.getRating() if color_choice is not None: polygon_color = color_choice 以上で作業は終了です。完成したら実行してみましょう。:numref:`fig-slider-dynamic` のような画面が表示され、スライダーを操作すると三角形の色や回転角度、不透明度が変化するはずです。特に回転角度がなめらかに変化するのに対して、不透明度はとびとびにしか変化しない点に注意してください。これはslider_opacの **[精度 $]** に0.1を指定しているからです。また、回転角度や不透明度はスライダーをドラッグ(マウスのボタンを押したまま動かす)するとリアルタイムに変化するのに対して、色の指定だけはマウスのボタンを離した時点で変化することも確認しておいてください。 ひととおり操作したら右側のOKと書かれたボタンをクリックして終了してください。そしてtrial-by-trial記録ファイルを開いて、OKをクリックしたときのスライダーの値が保存されていることを確認しましょう。回転角度はslider_ori.response、不透明度はslider_opac.response、色はslider_color.responseといった具合に各Sliderコンポーネントの **[名前]** に.responseと付けた列があって、そこに値が出力されているはずです。ちなみに反応時間は. **[名前]** にrtと付けた列に出力されます。OKをクリックした時刻ではなく各スライダーを最後に値を変更した時刻が出力されるので、それぞれで値が異なるはずです。 .. tabularcolumns:: |p{6zw}|p{30zw}| .. _tbl-slider-attributes-methods: .. csv-table:: Sliderオブジェクトの主な属性とメソッド :widths: 24, 64 markerPos, マーカーの現在の位置を保持するデータ属性。初期状態では値はNoneで、マーカーは表示されていない(未選択の状態)。Codeコンポーネントを使って値を代入することでマーカーの位置を変更できる。カテゴリカルな尺度の場合、最初の項目が0、二番目の項目が1といった具合にインデックスで表現される。 getRating(), 現在マーカーがある位置の値を得る。markerPosと異なり、カテゴリカルな尺度ではインデックスではなくその項目の値が得られる。 動作を確認し終えたところで、このデモの要点を確認しましょう。:numref:`tbl-slider-attributes-methods` はこのデモで使用したSliderオブジェクトのデータ属性とメソッドを示しています。markerPosというデータ属性で現在のマーカー位置が得られるので、stimの **[不透明度 $]** にといったプロパティに slider_opac.markerPos 書いて「フレーム毎に更新」にするだけで反映させることができるわけです。 ただ、この方法にはひとつ問題点があって、Sliderコンポーネントは初期状態で未選択(マーカーが表示されない)となります。未選択の時にはmarkerPosの値はNoneとなるので、何も対策せずに視覚刺激のプロパティに書くとルーチン開始直後にエラーとなってしまいます。この「対策」というのがCodeコンポーネントの **[Routine開始時]** のコードの以下の部分です。markerPosには値を代入することもできるので、このように書いておくとルーチン開始時のエラーを回避できます。 .. code-block:: python slider_ori.markerPos = 0 slider_opac.markerPos = 1 これで一件落着…と言いたいところですが、まだ問題が残っています。それは、slider_colorのようなカテゴリカルなスライダーの扱いです。カテゴリカルなスライダーの場合、markerPosには最初の項目が0、二番目の項目が1…といった具合にインデックスで表現されています。ですので、ルーチン開始時に最初の項目であるwhiteが選ばれているようにするためには、Codeコンポーネントの **[Routine開始時]** に以下のように書けばよいはずです。 .. code-block:: python slider_color.markerPos = 0 ここまでは正しいのですが、問題は値を得るときです。カテゴリカルなスライダーにおいて、選択された項目の(インデックスではなく)値を得るためにはgetRating()メソッドを用いる必要があります。ただ、getRating()は実験参加者が実際にスライダーを操作してマウスのボタンを離すまではNoneを返すので、markerPosの値をコードでいじるだけでは解決しません。そこで、本節のデモではif文を使ってgetRating()の値がNoneの場合とそうでない場合で処理を分けています。まず、polygon_colorという刺激の色を保持する変数を用意して、 **[Routine開始時]** に以下のように初期値を代入しておきます。 .. code-block:: python polygon_color = 'white' そして、 **[フレーム毎]** のコードで以下のように変数color_choiceにgetRating()の結果を代入します。そして、color_choiceの値がNoneでない場合に、polygon_colorへcolor_choiceの値を代入します。あとはこのpolygon_colorを刺激の **[塗りつぶしの色]** に指定すれば問題解決というわけです。 .. code-block:: python color_choice = slider_color.getRating() if color_choice is not None: polygon_color = color_choice ちなみに、getRating()をフレーム毎に2回実行するのをいとわなければcolor_choiceという変数は省略して以下のように書くこともできます。 .. code-block:: python if slider_color.getRating() is not None: polygon_color = slider_color.getRating() あとは特に新しいテクニックは使っていないはずなので、問題はないと思います。なお、このデモはひとつのルーチンに複数の尺度を設置してすべて反応させるパターンの例にもなっています。複数のRatingScaleコンポーネントをひとつのルーチンに配置するとルーチンの終了条件の扱いが少々面倒でしたが、Sliderコンポーネントとクリッカブルオブジェクトを併用すると比較的簡単に実現できるので、ぜひ活用してください。 チェックリスト - Sliderコンポーネントで刺激の不透明度、回転角度などの数値をリアルタイムに調整することが出来る。 - Sliderコンポーネントで刺激の色名などのカテゴリカルな値をリアルタイムに調整することが出来る。 Formコンポーネントで複数の質問を画面上に配置しよう ------------------------------------------------------------------- Sliderコンポーネントの用途のひとつとして、 :numref:`fig-form-example` のように質問文とスライダーを整然と並べて反応を計測するというものがあります。SliderコンポーネントとTextコンポーネントを使えばこのようなレイアウトを実現することは難しくありませんが、少々面倒ではあります。そこで、いくつかの制限の代わりにこのような複数の質問文とスライダーを並べたレイアウトを作成するFormコンポーネントというものがPsychoPyには用意されています。 .. _fig-form-example: .. figure:: fig09/form-example.png :width: 80% Formコンポーネントで作成できるレイアウト :numref:`fig-form-properties` にFormコンポーネントのアイコンとプロパティを示します。 **[テキストの高さ $]** 、 **[サイズ $]** 、**[位置 $]** 、 **[項目間の余白 $]** は見た目を調整するプロパティです。 **[サイズ $]** はFormコンポーネントによって作成される質問項目全体を囲む枠の大きさに対応していると考えてください。 **[サイズ $] に対して項目数が多い場合は、枠の右端にスクロールバーが自動的に設けられます** 。 **[データフォーマット]** は .. _fig-form-properties: .. figure:: fig09/form-properties.png :width: 80% Formコンポーネントのプロパティ **[項目]** がこのコンポーネントの鍵となるプロパティで、質問文やスライダーのパラメータを定義したCSVファイルまたはxlsxファイルを指定します。項目ファイルで定義できる内容を :numref:`tbl-form-items-file` に示します。1行目に見出しを書き、2行目以降に1行につき1項目の形で質問項目を定義していきます。後で具体的な例を挙げながら解説します。スクリーン上では項目ファイルに記入した順番に質問項目が表示されますが、 **[無作為化]** にチェックを入れておくと実行時にPsychoPyが項目の順序を無作為に並べ替えてくれます。 .. tabularcolumns:: |p{6zw}|p{30zw}| .. _tbl-form-items-file: .. csv-table:: Formコンポーネントの項目ファイルで指定する内容 :widths: 24, 64 :escape: ' index,項目の順序を整数で指定します。省略すると自動的に番号が割り振られます。 questionText,質問文を指定します。省略すると"Default question"という文字列となります。 questionWidth,Formコンポーネントの **[サイズ$]** で指定した幅のうち、質問文が占める幅の割合を0.0から1.0指定します。省略すると0.7となります。 type,スライダーの種類をradio、rating、sliderのいずれかで指定します。省略するとratingとなります。 responseWidth,Formコンポーネントの **[サイズ$]** で指定した幅のうち、スライダーが占める幅の割合を0.0から1.0指定します。省略すると0.3となります options,スライダーのラベルをカンマ区切りで指定します。省略するとYes',Noとなります。 layout,スライダーの方向をhoriz、vertのいずれかで指定します。horizなら水平方向、vertなら垂直方向です。省略するとhorizになります。 questionColor,質問文のフォントの色を指定します。省略するとwhiteになります。 responseColor,スライダーの色を指定します。省略するとwhiteになります。 最後に残った **[データフォーマット]** は、データを出力する際の形式を指定します。質問項目ひとつがrowの場合はデータファイルの1行に、columnの場合は1列に出力されます。 それでは実際に作業しながら確認しましょう。 - 実験設定ダイアログ - **[単位]** をheightにする。 - trialルーチン - Formコンポーネント1つ配置して以下の通りに設定する。 - **[名前]** を form にする(初期値のまま)。 - **[項目]** を items.xlsx にする。 - **[テキストの高さ $]** を 0.025 にする。 以上の作業を行った実験を保存し(名前は何でも良いですがform_test.psyexpとでもしましょうか)、 :numref:`fig-form-sample-xlsx` の内容のxlsxファイルを作成して同じ場所にitems.xlsxという名前で保存してください。なお、 :numref:`tbl-form-items-file` にあるように各列には標準の値が設定されていて、列を省略した場合は自動的に標準の値が用いられます。この機能は列が丸ごと省略されている場合のみ有効で、一部の行だけ空欄のまま残しても標準の値は用いられずエラーとなります。 .. _fig-form-sample-xlsx: .. figure:: fig09/form-sample-xlsx.png :width: 100% 項目ファイルの内容。横に長いので途中で分割して2段にしているのでご注意ください。 .. .. _tbl-form-sample-xlsx: .. .. .. csv-table:: 項目ファイルの内容 .. :escape: ' .. .. index,questionText,questionWidth,type,responseWidth,options,layout,questionColor,responseColor .. 0,質問文その1,0.7,rating,0.3,あてはまらない',あてはまる,horiz,white,white .. 1,質問文その2,0.7,slider,0.3,1',2',3',4',5,horiz,[-1',-1',1],[1',1',-1] .. 2,複数行にわたる\n質問の例,0.7,radio,0.3,選択肢1',選択肢2,vert,-0.5,-0.5 これで準備は完了です。Builderに戻って実験を実行してみましょう。 :numref:`fig-form-sample-screen` のように表示されれば成功です。マウスでスライダーを操作できますが、すべて選択しても終了しません。ESCキーを押して実験を中断してください。 .. _fig-form-sample-screen: .. figure:: fig09/form-sample-screen.png :width: 80% サンプルの実行例 それでは :numref:`tbl-form-items-file` 、 :numref:`fig-form-sample-xlsx` 、:numref:`fig-form-sample-screen` を見比べながら項目の指定方法を確認しましょう。まず、questionTextの列が質問文として表示されていることがわかります。英語などではFormコンポーネントの幅に対して質問文が長すぎるときに自動的に改行されるのですが、日本語では自動改行されません。どうしても改行させたい場合はこの例の3つ目の項目のように \\n を入れれば実現可能です。ただし、出力されるデータファイル内でも改行されてしまいますので、データ処理の際に注意が必要です(Excelで開く場合は問題ないようです)。自動改行は半角スペースの位置で生じますので、一度改行なしで表示させてみてから「ここで改行してほしい」という位置に手作業で半角スペースを入れる方法もあります。 続いてスライダーですが、typeの列がスライダーの種類の指定です。指定できる値はrating、slider、radioですが、これはSliderコンポーネントと同じなのでそちらを参照してください。スライダー上のラベルはoptionsの列で指定します。Sliderコンポーネントでは **[ラベル]** と **[目盛]** のプロパティが独立していたので「5段階の尺度だけどラベルは両端のみ」といった指定が可能でしたが、Formコンポーネントではoptionsの列のみで指定しないといけないので、ラベルと目盛の数は必ず等しくなります。これがFormコンポーネントの制限のひとつです。 layout、questionWidth、responseWidth、questionColor、responseColorは見た目を調整するパラメータです。layoutはスライダーの向きをhoriz(水平)かvert(垂直)で指定します。:numref:`fig-form-sample-screen` の3つ目の項目がvertの例です。questionWidthとresponseWidthはFormコンポーネントの枠の幅に対して質問文とスライダーの幅が占める割合を0.0から1.0で指定します。これらの値の合計が1.0を超えてしまうと、項目とスライダーが重なってしまったり枠からはみ出たりしてしまって正常に表示されません。合計が1.0に満たない場合は質問文とスライダーの間に空白ができます。 questionColorとresponseColorは質問文とスライダーの色の指定です。他のコンポーネントと同様にRGBの値を-1.0から1.0で表したリストや色名が利用できます。 :numref:`fig-form-sample-xlsx` の3行目のようにひとつの数値を指定した場合、グレースケールとして解釈されます。つまり、-0.5とだけ書いてあれば[-0.5, -0.5, -0.5]と書いてあるのと同じだということです。 以上で **[項目]** の指定方法はひととおり解説しました。まだindexの列が残っていますが、これは出力されたデータファイルについて説明する際に触れます。それではさっそくデータファイルの解説を…と言いたいところですが、先ほど作成した実験はすべての質問項目を選択しても終了しないので仕方なくESCキーで中断したのでした。きちんとデータを保存して終了するようにする必要があります。 Formコンポーネントを置いてあるルーチンを終了させる方法はいろいろ考えられますが、実用的なのはKeyboardコンポーネントを置いてキー押しで終了させるか、Mouseコンポーネントを置いてクリッカブルオブジェクトをクリックして終了させるかのどちらかでしょう。ここではMouseコンポーネントを使う方法を使います。先ほどの実験をBuilderで開いて、以下のように作業しましょう。 - trialルーチン - Mouseコンポーネントを1つ配置して以下の通りに設定する。 - **[ボタン押しでRoutineを終了]** を 有効なクリック にする。 - **[マウスの状態を保存]** を なし にする。 - **[クリック可能な視覚刺激 $]** に button と入力する。 - Polygonコンポーネントを1つ配置して以下の通りに設定する。 - **[名前]** を button にする。 - **[終了]** を空欄にする。 - **[形状]** を 長方形 にする。 - **[位置 [x,y] $]** を (0.45, -0.4) にする。 - **[サイズ [w,h] $]** を (0.2, 0.05) にする。 - 「高度」タブの **[塗りつぶしの色]** を darkgray にする。 - 「高度」タブの **[輪郭線の色]** を black にする。 - Textコンポーネントを1つ配置して以下の通りに設定する。 - **[名前]** を text_button にする。 - **[終了]** を空欄にする。 - **[文字の高さ]** を 0.03 にする。 - **[位置 [x,y] $]** を (0.45, -0.4) にする。 - **[文字列]** に OK と入力する。 これでスクリーンの右下に OK と書かれたボタンが表示され、これをクリックすると終了できるようになりました。ただ、このままでは質問に全く答えずにいきなり OK をクリックしても終了してしまいます。すべての質問に答えるまで終了できないようにするには、:numref:`第%s章 ` で「とりあえず無視」した機能を利用します。各コンポーネントの **[開始]** および **[終了]** のプルダウンメニューには 「条件式」 という項目があります。「条件式」を選択すると、この欄に記入した式の値が True になった時点でコンポーネントを開始したり終了したりすることができます。:numref:`第%s章 ` 以後、if文などを通じて条件式を学んできた今なら、この機能を活用できるはずです。 問題はFormコンポーネントの質問にすべて答えた時に True になる式をどのように書くかですが、これは難問です。Builderのコンポーネントにはそれに対応するPsychoPyのオブジェクトがあるという話を :numref:`第%s章 ` でしましたが、Formコンポーネントの場合はpsychopy.visual.Formというオブジェクト(以下Formオブジェクト)が対応しています。このFormオブジェクトには formComplete() というメソッドがあり、すべての質問に回答済みの場合にTrue、未回答の質問があればFalseが返されます。まさに今回の目的にぴったりです。今回の実験ではFormコンポーネントの名前を form としていますので、 .. code-block:: python form.formComplete() という式を書けばよいことがわかります。実験を以下のように修正しましょう。 - trialルーチン - buttonという名前のPolygonコンポーネントの **[開始]** を 条件式 に変更し、form.formComplete()と入力する。 - text_buttonという名前のTextコンポーネントの **[開始]** を 条件式 に変更し、form.formComplete()と入力する。 修正が終わったら実行してください。最初は右下の OK ボタンは表示されていなくて、Formコンポーネントの質問をすべて回答したらボタンが出現するはずです。**[開始]** および **[終了]** で条件式を使う方法は応用範囲が広く、使い回こなすといろいろと面白いことができます。 :numref:`第%s章 ` でも出てきますので、ぜひ参考にしてください。 さて、ここまで作成した実験を実行し、すべての質問に回答して終了すると、いつも通りCSV形式のデータファイルが保存されているはずです。データファイルの例を :numref:`fig-form-datafile` に例を示します。通常のコンポーネントはループでの繰り返し1回につき1行の形式でデータを出力しますが、Formコンポーネントは複数行にわたってデータを出力します。**[データフォーマット]** がrowsであれば質問1項目につき1行、columnsであれば1項目につき1列の形式で、項目のインデックス(項目ファイルのindexの列で指定した値)、質問文、反応、反応時間が出力されます。項目のインデックスは **[無作為化]** で項目の順序が変更されてしまっている場合に、データを並び替えたりプログラムで処理したりするのに役に立ちます。 .. _fig-form-datafile: .. figure:: fig09/form-datafile.png :width: 80% Formコンポーネントの出力 最後にFormコンポーネントのもうひとつの、重要な制約について触れておきます。このような特殊な形式でデータを出力するせいか、**Formコンポーネントはループで繰り返されるRoutineでは正常に動作しません** 。本節の例のように、ループがないフローで利用してください。ループさえしていなければ、Formコンポーネントを配置したルーチンを複数実行することができます(Builderの公式デモのBigFiveInentory参照)。ループ内のルーチンで複数のスライダーを使用する場合は、Sliderコンポーネントを使用しましょう。 チェックリスト - Formコンポーネントで複数の質問項目とスライダーのペアをルーチンに配置することができる。