.. _chapter-gui: グラフィカルインターフェースを活用しよう ================================================================ 本章では、本書の第2版執筆以降に追加された「Buttonコンポーネント」、「クリッカブルオブジェクト」、「Sliderコンポーネント」、「Formコンポーネント」について解説します。全ての追加機能を使う実験を作るのが難しそうでしたので、ひとつの実験を作り上げるのではなく各機能を体験するデモを作ることを目標とします。 ButtonコンポーネントとVariableコンポーネントで刺激を操作してみよう ---------------------------------------------------------------------- PsychoPy 2021.1.0から、マウスでクリックして反応を測定したり刺激を操作したりできるボタンを画面上に配置するButtonコンポーネントが登場しました。 :numref:`第%s章 ` で解説したテクニックを使って同様のことは実現できますが、こちらの方が使用するコンポーネントが少ない分実験の作成が少し楽になります。本節ではVariableコンポーネントの紹介も兼ねて、簡単なデモを作ってみましょう。 .. _fig-button-variable-icons: .. figure:: fig09/button-variable-icons.png :width: 80% ButtonコンポーネントとVariableコンポーネントのアイコン。 Buttonコンポーネントは「反応」、Variableコンポーネントは「カスタム」カテゴリの中にあります(:numref:`fig-button-variable-icons`)。 Buttonコンポーネントの「基本」タブにはお馴染みの **[名前]** 、 **[開始]** 、 **[終了]** 、 **[Routineを終了]** に続いて、 :numref:`tbl-button-properties` に示すプロパティがあります。 **[ボタンのテキスト]** は解説の必要はないかと思いますが、 **[コールバック関数 $]** という用語は聞いたことがない方が多いでしょう。コールバック関数とは「関数から呼び出すために引数として渡しておく関数」のことで、ここではButtonコンポーネントに「もしクリックされたらその時はこれを実行してほしい」と渡しておくコードを意味しています。Codeコンポーネントで記入するコードは「Routine開始時」や「フレーム毎」といったスケジュールに沿って実行されますが、コールバック関数はButtonコンポーネントが適切なタイミングで実行してくれるわけです( :numref:`fig-callback-func` )。ちょっとプログラミングに詳しい方向けに書いておくと(意味が分からない人は読み飛ばしていただいて結構です)、「コールバック関数」といってもここでは関数の宣言を書く必要はありません。関数内で実行する処理だけを書けばよいです。 .. tabularcolumns:: |p{6zw}|p{30zw}| .. _tbl-button-properties: .. csv-table:: Buttonコンポーネントの主要なプロパティ。 :widths: 24, 64 **[ボタンのテキスト]**,ボタン内に表示するテキストです。 **[コールバック関数 $]**,ボタンをクリックしたときに実行させたいコードを記入します。「ボタンをクリックしたことを記録してルーチンを終了する」だけなら何も記入する必要はありません。 **[クリック毎に1回実行]**,**[コールバック関数 $]** に記入したコードをクリックされたときに1度だけ実行するか、押されたボタンが離されるまでフレーム毎に実行するかを指定します。 **[Routineを終了]** がチェックされているとこの項目自体無効になります(クリックした途端に終了するので意味がない)。 .. _fig-callback-func: .. figure:: fig09/callback-func.png :width: 80% コールバック関数の仕組み。 **[クリック毎に1回実行]** は、ボタン上にマウスカーソルを移動させてボタンをやや長めに押した時に意味を持ってきます。この項目がチェックされていないと、Builderはフレーム毎に「ボタンが押されているか」をチェックしてコールバック関数を実行します。チェックされていれば、一度ボタンを押した後、いったん指を離して押し直すまでコールバック関数が実行されません。これは恐らく実際に確認した方がわかりやすいと思いますので、デモを作成してから改めて解説することにしましょう。 Buttonコンポーネントには「基本」タブの他に「レイアウト」、「外観」、「書式」、「データ」、「テスト」のタブがあります。「レイアウト」、「外観」、「書式」タブにはボタンの枠とテキストの色や大きさ、フォント等を指定するプロパティが含まれています。TextコンポーネントやPolygonコンポーネントと大部分が共通していますので、解説の必要はないでしょう。唯一気をつけないといけないのが **[フォント]** で、Textコンポーネントでは日本語の文字が正しく表示できるにも関わらず、Buttonコンポーネントで同じ設定にしても日本語の文字が表示されない場合があります。内部で日本語の字体の取得に失敗しているのが原因なので、日本語の字体を含むフォントを明示的に **[フォント]** で指定する必要があります。例えばWindowsではOpen SansやArialなどではなくMeiryoやYu Gothicなどを指定してください。 「データ」タブには他と共通のプロパティに加えて **[クリックを記録]** というものがあり、「全てのクリック」、「最初のクリック」、「最後のクリック」、「なし」のいずれかを選ぶことができます。Keyboardコンポーネントの **[記録]** に対応するものだと考えてください。 **[Routineを終了]** をチェックしている場合は「全てのクリック」、「最初のクリック」、「最後のクリック」の動画が実質同じになる点や、単に次のルーチンへ進むために参加者に押させたいだけの場合は「なし」を選ぶと余計な出力を抑えられる点も同じです。 続いてVariableコンポーネントですが、これは :numref:`第%s章 ` で学んだ「プローブの長さを保持する独自の変数を用意して記録ファイルに値を出力する」といった動作をCodeコンポーネントを使わずに実現するコンポーネントです。 :numref:`第%s章 ` の方法とVariableコンポーネントのそれぞれに長所、短所がありますので、本章のデモを見て好みの方を使うと良いと思います。 Variableコンポーネントの「基本」タブには毎度おなじみの **[名前]** 、 **[開始]** 、 **[終了]** があります。 **[名前]** はそのまま変数名として解釈されます。続いて **[実験開始時の値]** 、 **[Routine開始時の値]** 、 **[フレーム開始時の値]** という項目がありますが、それぞれのタイミングで変数の値を設定するために使います。設定する必要がない場合は空欄で構いません。例えば :numref:`第%s章 ` でprobeLenという独自の変数をCodeコンポネントで使用してルーチンの開始時にprobeLen = initProbeLenを実行しましたが、同じことをVariableコンポーネントで実現するなら、以下のように設定すればよいわけです。**[実験開始時の値]** や **[フレーム開始時の値]** は空欄のままにしてください。 - **[名前]** を probeLen にする。 - **[Routine開始時の値]** を initProbeLen にする。 続いて「データ」タブですが、ここではどの時点の値を記録ファイルに出力するかを指定します。 :numref:`第%s章 ` の例なら、trialルーチンが終わる毎に値を保存する必要があるので **[Routine終了時の値を保存]** をチェックすればよいでしょう。これでCodeコンポーネントを使わなくても記録ファイルに値が出力されます。 Variableコンポーネントの長所は、addData( )などのメソッドを覚えておかなくても使えること、そしてルーチンペインを見たらどのような独自変数を使っているのか一目でわかることの2点です。Codeコンポーネントで変数を定義した場合、どのような変数を独自に定義したかを把握するにはプロパティの内容を確認しなければなりません。実験を作成した直後は使用した変数のことなどはよく覚えているものですが、数か月後に論文執筆や次の実験の作成のために久しぶりに実験ファイルを開くと、自分で作ったものなのに「あれ、これはどうだったかな」とわからなくなることは珍しくありません。「わかりやすい」というのはとても大切です。 一方、Variableコンポーネントの短所は、凝った処理を行おうとすると結局Codeコンポーネントが必要となりがちなことと、複数のルーチンにまたがって使用する変数で問題が生じる場合があることの2点が挙げられるでしょう。ひとつめの問題については、Variableコンポーネントを使って変数の存在をアピールしつつCodeコンポーネントを併用して処理を行うということも可能です。しかし、その場合はルーチン内でVariableコンポーネントとCodeコンポーネントの順番に注意する必要があります(処理内容によってはひとつのCodeコンポーネントでは不可能)。ふたつめの問題は、例えばtrialというルーチンにresponseという名前のVariableコンポーネントを置いたら、practiceというルーチンに同じresponseという名前のVariableコンポーネントを置くことはできないということです。Builderの実験においてコンポーネントの名前はグローバルなものなので、どちらか一方のルーチンに配置しておけばもう一方でもその変数にアクセスできますが、 **[Routine開始時の値]** などのプロパティを使って値を設定したり、 **[Routine終了時の値を保存]** を使って値を保存したりできるのはコンポーネントが置かれているルーチンのみです。 前置きが長くなりました。コンポーネントの解説はこのくらいにしておいて、デモを作成してみましょう。 - 実験設定ダイアログ - PsychoPyの設定でheight以外の単位を標準に設定している場合は、実験設定ダイアログを開いて **[単位]** をheightにしておく。 - trialルーチン - Variableコンポーネントを1つ配置し、以下の通り設定する。 - **[名前]** に angle と入力する。 - **[Routine開始時の値]** に 0 と入力する。 - 「データ」タブの **[Routine終了時の値を保存]** をチェックして、他のチェックをすべて解除する。 - Buttonコンポーネントを1つ配置し、以下の通り設定する。 - **[名前]** に button_end と入力する。 - **[終了]** を空白にする。 - **[ボタンのテキスト]** に end と入力する。 - 「レイアウト」タブの **[サイズ [w,h] $]** を (0.15,0.05) に、 **[位置 [x,y] $]** を (0, -0.45) にする - 「書式」タブの **[文字の高さ $]** を0.02にする。 - 「データ」タブの **[クリックを記録]** が「全てのクリック」になっていることを確認する(初期値でそうなっているはず)。 - 「データ」タブの **[開始・終了時刻を保存]** のチェックを外す。 - button_endをコピーしてbutton_step, button_cntの **[名前]** で貼り付けて、以下の通り設定する。 - button_stepの **[ボタンのテキスト]** に step 、 **[コールバック関数 $]** に angle+=5 と設定する。 **[クリック毎に1回実行]** がチェックされていることを確認する。 - button_stepの「レイアウト」タブの **[位置 [x,y] $]** を (-0.4, -0.4) にする。 - button_cntの **[ボタンのテキスト]** に continuous 、 **[コールバック関数 $]** に angle+=5 と設定する。 **[クリック毎に1回実行]** のチェックを外す。 - button_cntの「レイアウト」タブの **[位置 [x,y] $]** を (0.4, -0.4) にする。 - Polygonコンポーネントを1つ配置し、以下の通り設定する。 - **[終了]** を空白にする。 - **[回転角度]** に angle と入力し、「フレーム毎に更新」にする。 - 「データ」タブの **[開始・終了時刻を保存]** のチェックを外す。 以上でデモは完成です。鋭い人は「マウスを使うデモなのにMouseコンポネントがない…?」と疑問を抱いているのではないかと思いますが、 **Buttonコンポーネントを使用すると自動的にマウスが準備されます。** ですからMouseコンポーネントを置く必要はありません。 :numref:`第%s章 ` のようにマウスカーソルの座標を用いた処理も並行しておこなうのなら、Mouseコンポーネントを置く必要があります。自分で置いたMouseコンポーネントとButtonコンポーネントが自動的に準備するマウスは互いに影響を与えませんので、両方のコンポーネントを置いても問題は生じません。 デモを実行すると :numref:`fig-button-demo` のように中央に大きな三角形が描かれ、下の方にStep、end、Continuousの3つのボタンが表示されます。Step、Continuousのボタンはいずれも同じコールバック関数angle=+=5が設定されていて、違いは(外見上の違いを除けば) **[クリック毎に1回実行]** のチェックの有無だけです。マウスを操作して、それぞれのボタンを少し長めに押してみましょう。Stepのボタンはマウスのボタンを押した瞬間に三角形が少し時計回りに回転して、その後マウスのボタンをいったん離してもう一度押し直すまで回転しないはずです。一方、Continuousのボタンはマウスのボタンを押し続けている間ずっと回り続けるはずです。Stepボタンは **[クリック毎に1回実行]** がチェックされているので1回のボタン押しに対してコールバック関数が1回しか呼ばれず、Continuousボタンは **[クリック毎に1回実行]** がチェックされていないのでボタンを押している間コールバック関数が呼ばれ続けるというわけです。違いがおわかりいただけたでしょうか。 .. _fig-button-demo: .. figure:: fig09/button-demo.png :width: 80% Step、Continuousのボタンを長押しして動作の違いを確認しましょう。endをクリックすると終了します。 StepボタンとContinuousボタンの違いを確認したら、endボタンを押して実験を終了して記録ファイルを確認しましょう。Buttonコンポーネントひとつにつきボタン名+.numClicks、ボタン名+.timesOn、ボタン名+.timesOffという3つの列が出力されていて、それぞれボタンがクリックされた回数、ボタン押しが始まった時刻、ボタン押しが終了した時刻が出力されています(時刻の単位は秒)。timesOnとtimesOffに並んでいる時刻の個数はnumClicksと一致して、timesOffのn番目の数値からtimesOnのn番目の数値を引き算すればn回目のボタン押しで何秒間ボタンが押され続けていたかを計算することができます。 以上はButtonコンポーネントによる出力でしたが、Variableコンポーネントによる出力も確認しておきましょう。angle.routineEndValという列も出力されていて、ルーチン終了時のangleの値が出力されているはずです。このように、変数名+.保存タイミングを表す文字列の列名で値が出力されます。なお、実は筆者も最初ハマったのですが、 **[Routine開始時の値]が設定されていなければ、[Routine終了時の値を保存]をチェックしていても値は出力されません** (2021.1.2で確認)。 これは実験製作者がうっかり「値が一度も設定されないまま値が保存」しようとしてしまった時に生じうるエラーを考えると仕方がないのですが、エラーメッセージや警告メッセージが何も表示されないので見落としやすいです。ご注意ください。 以上でButtonコンポーネントとVariableコンポーネントのデモはおしまいです。マウスを反応に使用するのならButtonコンポーネントは覚えておく価値があるでしょう。腕に自信がある人は、:numref:`第%s章 ` の実験をボタン押しでプローブ長を調節するように変更してみるとよい練習になると思います。Variableコンポーネントは好みがわかれるところだと思いますが、無視できない長所もあるので一度使ってみることをお勧めします。 チェックリスト - Buttonコンポーネントを使ってルーチンを終了させることができる。 - Buttonコンポーネントを使ってボタンがクリックされたときにコードを実行させることができる。 - Buttonコンポーネントの **[クリック毎に1回実行]** のチェックの有無による動作の違いを説明できる。 - Variableコンポーネントを使って変数を設定し、ルーチン開始時に特定の値を設定することができる。 - Variableコンポーネントを使って変数の値を記録ファイルに出力することができる。 クリックした視覚刺激オブジェクトを記録しよう --------------------------------------------------------------- Buttonコンポーネントとても便利ですが、ボタンのデザインを自由に変更したい場合や、ボタンが押されたときに自分自身以外のコンポーネントの情報を保存したい場合などには使用できません。 そういった複雑な処理を行うにはCodeコンポーネントが必要ですが、Mouseコンポーネントの **[クリック可能な視覚刺激 $]** というプロパティを使用すると、Buttonコンポーネントよりは少し複雑な処理をCodeコンポーネントに頼らずに実現できます。本節では **[クリック可能な視覚刺激 $]** の使用例を紹介しましょう。まずは、Buttonコンポーネントのようにクリックするとルーチンを終了する処理を作成してみます。 - 実験設定ダイアログ - PsychoPyの設定でheight以外の単位を標準に設定している場合は、実験設定ダイアログを開いて **[単位]** をheightにしておく。 - trialルーチン - Polygonコンポーネントを1つ配置し、以下の通り設定する。 - **[名前]** をbutton_yesにする。 - **[開始]** を「時刻 (秒)」で0.5に、**[終了]** を空白にする。 - **[形状]** を長方形にする。 - **[塗りつぶしの色]** をwhiteにする。 - **[サイズ [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コンポーネントで透明な背景を持つ画像を使った場合も、見た目とは異なり透明な部分もクリックの対象となります。 **[クリック時に保存するパラメータ $]** は、刺激の色や回転角度などのパラメータが変化する実験において、クリック時の値を記録したい場合に便利です。これも簡単なデモを作ってみましょう。 - 実験設定ダイアログ - PsychoPyの設定でheight以外の単位を標準に設定している場合は、実験設定ダイアログを開いて **[単位]** を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; **[前景色]** に対応 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コンポーネントは、定規のような「尺度」をスクリーン上に提示して、尺度上の位置を参加者に選択させることによって反応を記録するためのコンポーネントです。本書の第4版まではこのSliderコンポーネントに似た機能を持つRatingScaleコンポーネントを紹介していたのですが、現在のバージョン(本節の執筆時点で2022.2.4)でRatingScaleコンポーネントは廃止されてしまったので、今後はSliderコンポーネントを使用しなければいけません。すでにRatingScaleを含む実験を持っている方向けに補足しておくと、バージョン2022.2.4の時点ではRatingScaleはBuilderのコンポーネントペインに表示されないだけで、RatingScaleを含む実験を実行することができます。将来RatingScaleが完全に削除してしまった場合は旧バージョンのPsychoPyで実行する必要があるでしょう。 .. _fig-slider-icon: .. figure:: fig09/slider-icon.png :width: 50% Sliderコンポーネントのアイコン。 そろそろ本題に入りましょう。Sliderコンポーネントは「反応」カテゴリにあります(:numref:`fig-slider-icon`)。まず「基本」タブから見ていきましょう。 **[名前]** 、 **[開始]** 、 **[終了]** は他のコンポーネントと同様です。 **[Routineを終了]** もKeyboardコンポーネントと同じなので、問題はないでしょう。 「レイアウト」タブの **[空間の単位]** と **[位置 [x,y] $]** は他のコンポーネントと同様ですが、 **[回転角度 $]** は変更すると尺度のサイズの計算がおかしくなる場合があるので変更しないでください。 **[サイズ $]** は幅、高さの順で2つの値を指定しますが、幅の値が高さの値より大きい場合は横向き、幅の値が高さの値以下ならば縦向きの尺度が描かれます。「より大きければ横/以下ならば縦」なので、幅と高さの値が同一ならば(実用的かどうかは別として)縦向きとなります(: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, 6, 9, 12, 15)、 **[精度 $]** に4を指定した場合)でもエラーにはなりませんが、混乱の元になりますので避けるべきでしょう。 **[初期値]** は最初から尺度上にマーカーが描かれている状態にしたいときに、その位置を指定します。カテゴリカル尺度の場合うまく機能しませんので(2022.2.4で確認)、 **[目盛]** と **[ラベル]** の両方を指定して数値で位置を指定するといいでしょう。 続いて「外観」のタブに進みましょう。 **[ラベルの色]** 、 **[マーカーの色]** 、 **[枠線の色]** はそれぞれの部分の色を指定します。 **[不透明度 $]** は2022.2.4で確認した限り、目盛にのみ効果があるようです(つまり主線やラベルに効果がない)。 **[スタイル]** は尺度の見た目を変更するオプションです。 **[スタイルの微調整]** はラベルを45度傾けたりやマーカーを三角形にしたりといった微調整をおこないますが、2022.2.4で確認したところ45度傾ける機能が無効になっているようです。旧バージョンでは正常に動作したので、一時的なバグの可能性もあります。 :numref:`fig-slider-styles` にスタイルの例を示します。 .. _fig-slider-styles: .. figure:: fig09/slider-styles.png :width: 80% Sliderのスタイルの例 「書式」タブは **[フォント]** と **[文字の高さ $]** のみで、Textコンポーネントなどと同じです。 「データ」タブは他のコンポーネントと共通のものに加えて **[読み込み専用]** 、 **[評定を記録]** 、 **[反応時間を記録]** 、 **[履歴を記録]** があります。 **[読み込み専用]** をチェックすると参加者が値を変更できなくなります。あまり使う機会はないでしょう。 **[評定を記録]** と **[反応時間を記録]** は初期値でチェックされていて、それぞれ参加者が選択した値と反応に要した時間(単位は秒)を記録するかどうかを決定します。反応時間はともかく、 **[評定を記録]** のチェックをオフにすることは通常の用途では無いと思います。 **[履歴を記録]** をチェックすると、反応を確定するまでに選択した値をすべてカンマ区切りで保存します。「基本」タブの **[Routineを終了]** にチェックが入っていると一回クリックしただけでルーチンが終了してしまうので、このチェックをオフにしないと意味がありません。ルーチンを終了させる他の手段と組み合わせて使う必要があります。 チェックリスト - Sliderコンポーネントで縦向き、横向きの尺度を描画できる。 - Sliderコンポーネントで数値を記録する尺度とカテゴリカルな値を記録する尺度を使い分けることができる。 - Sliderコンポーネントでスタイルを使い分けられる。 Sliderコンポーネントで刺激をリアルタイムに調整しよう ------------------------------------------------------------------- Sliderコンポーネントは反応の記録のほかにも、実験中の刺激パラメータの調整にも使うことができます。本節では、:numref:`fig-slider-dynamic` に示すような画面で、スライダー(本節の使い方では尺度と呼ぶのもおかしいのでスライダーと表記します)を動かしてリアルタイムに刺激の回転角度、不透明度、色を変更してみましょう。 .. _fig-slider-dynamic: .. figure:: fig09/slider-dynamic.png :width: 80% Sliderで刺激のパラメータをリアルタイムに調整することができます。 さっそくですが、Builderで実験を新規作成して以下のように作業してください。 - 実験設定ダイアログ - PsychoPyの設定でheight以外の単位を標準に設定している場合は、実験設定ダイアログを開いて **[単位]** をheightにしておく。 - trialルーチン - Sliderコンポーネント1つ配置して以下の通りに設定する。 - **[名前]** を slider_color にする。 - **[サイズ $]** を (0.04, 0.4) にする。 - **[位置 [x,y] $]** を (-0.4, 0) にする。 - **[目盛]** を空欄にして、 **[ラベル]** を 'white', 'blue', 'orange' にする。 - **[Routineを終了]** のチェックを外す。 - **[文字の高さ $]** を0.03にする。 - slider_colorをコピーして slider_oriの名前で貼り付け、以下の通りに作業する。 - **[目盛]** と **[ラベル]** を 0,90,180 にする。 - **[サイズ $]** を (0.5, 0.03) にする。 - **[位置 [x,y] $]** を (0, -0.3) にする。 - slider_oriをコピーして slider_opacの名前で貼り付け、以下の通りに設定する。 - **[目盛]** と **[ラベル]** を 0, 1 にし、 **[精度 $]** を 0.2にする。 - **[位置 [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.2を指定しているからです。また、回転角度や不透明度はスライダーをドラッグ(マウスのボタンを押したまま動かす)するとリアルタイムに変化するのに対して、色の指定だけはマウスのボタンを離した時点で変化することも確認しておいてください。色の変更がうまくいかない場合は「 :numref:`{number}:{name} ` 」を参考にしてください。 ひととおり操作したら右側の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コンポーネントで刺激の色名などのカテゴリカルな値をリアルタイムに調整することができる。 .. _section-textbox: TextBoxコンポーネントで文字を入力してみよう ---------------------------------------------------------------------- TextBoxコンポーネントはバージョン2020.2で追加された新しい機能で、従来のTextコンポーネントに加えて周囲に枠を描いたり、ボールド体や斜体を指定したり、行のアライメント(中央揃え、左揃えなど)を指定したりできます。これだけでも便利なのですが、さらにキーボードから文字を入力したり削除したりできるようにすることも可能なのです。実験の内容によっては参加者に自由記述をしてもらいとことがありますが、そういった実験もBuilderで作ることができるようになります。 きっと「この機能を待っていました!」という人も多いであろう注目機能のひとつですが、2020.2.0のリリース時に「まだ開発版なので実験に使う時は念入りに動作を確認しましょう(certainly in beta and should be tested carefully in your study)」と書かれていたように、まだまだ開発途上の段階です。英語のように語の間をスペースで区切る言語で、なおかつ入力にIME(Input Method Editor)を必要としない言語であればそこそこ実用的な段階に到達しているのですが、残念ながら日本語はどちらにも該当しません。まあ、前置きはこのくらいにしてどのような感じになるのか試してみましょう。 - 実験設定ダイアログ - PsychoPyの設定でheight以外の単位を標準に設定している場合は、実験設定ダイアログを開いて **[単位]** をheightにしておく。 - trialルーチン - TextBoxコンポーネント1つ配置して以下の通りに設定する。 - **[名前]** を textbox にする(初期値のまま)。 - **[終了]** を空欄にする。 - **[編集可能]** をチェックする。 - **[サイズ [w, h] $]** を(0.6, 0.4)にする。 - **[枠線の色]** をwhiteにする。 - **[フォント]** に日本語に対応したフォントを指定する(WindowsならMeiryo、MacならHiragino Sansなど)。 - **[行揃え]** を「左上」にする。 - Buttonコンポーネントを1つ配置して、以下の通りに設定する。 - **[終了]** を空欄にする。 - **[Routineを終了]** がチェックされていることを確認する。 - **[ボタンのテキスト]** に OK と入力する。 - **[サイズ [w, h] $]** を(0.05, 0.03)にする。 - **[位置 [x, y] $]** を(0.4, -0.4)にする。 - **[文字の高さ $]** を0.02にする。 - **[クリックを記録]** を「なし」にする。 ここまで作業したら保存して実行してみましょう。 :numref:`fig-textbox-default-sample` 左上のように、四角い枠が描かれてその内側に **[文字列]**に入力されていた文字列が表示されているはずです。文章の各行が左寄せになっていることに注目してください。これはもちろん **[行揃え]** を「左上」にした効果ですが、こういったレイアウトは従来のTextコンポーネントでは面倒です。これだけを目当てにTextBoxコンポーネントを使う価値があります。 **[塗りつぶしの色]** を指定すれば枠内に背景色をつけることもできます(もちろん **[枠線の色]** をNoneにして枠なしで背景色だけつけることもできます)。 .. _fig-textbox-default-sample: .. figure:: fig09/textbox-default-sample.png :width: 80% TextBoxの例。左上は最初の状態、右上は英文を入力した状態、左下は長い英文を入力した状態、右下は日本語の文を入力した状態。 それでは続いてマウスを操作して枠内をクリックして、キーボードのBackSpaceキーを押してください。表示されている文字列を削除できるはずです。すべて削除したら、適当な英文を入力してみましょう。内容はなんでもよいのですが、枠の幅より長い文を入力してください。行が枠からはみ出る長さになると自動的に :numref:`fig-textbox-default-sample` 右上のように改行されるはずです。行末に半角スペースや改行文字を入力した時のカーソル(文字入力位置を示す縦線)の位置が通常とは異なるので少々戸惑いますが、おおむね自然に入力と削除ができるのではないかと思います。このように **[編集可能]** をチェックしておけば文字列を編集することができます。この例では **[文字列]** に初期値が入ったままにしましたが、 **[文字列]** を空欄にしておけば空っぽの枠内に文字を入力していくことができます。 最初に「TextBoxはまだまだ開発途上の段階」と書きましたが、どのあたりが「まだまだ」なのか確認しておきましょう。まず、 :numref:`fig-textbox-default-sample` 左下のように枠内にどんどん英文を入力してみてください。行数が多くなって枠の高さを超えると、一般的なアプリのようにスクロールバーが表示されたりせずに枠からはみ出してしまいます。はみ出してしまっても正しく入力内容を保存できるのですが、あらかじめ参加者に断っておかないと参加者が不安になってしまいそうな挙動です。続いていったん英文を削除して、日本語で入力を行ってみましょう。まず、日本語入力をONにしてキーを少し叩くと、入力中の文字が表示されないことに気づくと思います。Enterキーを押すなどして確定すれば表示されますが、かなり入力しづらいです。特に、漢字変換を行おうとしたときに変換候補が見えないので、思い通りに変換することは非常に困難です。さらに、枠の幅を超える長さの文を入力すると、 :numref:`fig-textbox-default-sample` 右下のように自動改行されずに右へはみ出してしまうことがわかります。 こういった状況を考えると、少なくとも現時点(バージョン2022.2.4)では日本語の文章入力にTextBoxコンポーネントを使うのは難しそうです。TextBoxコンポーネントはオンライン実験にも対応しているのですが、オンライン実験は通常のインターネットブラウザで動作するため日本語入力の問題がすべて解決します(入力中の文字や変換候補もすべて見えるし、枠に入りきらない長さの文字列を入力すると自動的にスクロールバーがつく)。ですので、 **どうしても参加者に日本語入力をしてもらいたい実験があるのなら、オンライン実験として出力するという選択肢もあります。** Formコンポーネントで複数の質問を画面上に配置しよう ------------------------------------------------------------------- 心理学実験においては、 :numref:`fig-form-example` のように質問文と尺度を複数並べて回答してもらいたいという場面にしばしば出くわします。 SliderコンポーネントとTextコンポーネントを使えばこのようなレイアウトを実現することは可能ですが、見栄えよくなラベルには位置やサイズの調整などがかなり面倒です。そこで、いくつかの制限の代わりにこのような複数の質問文とスライダーを並べたレイアウトを作成するFormコンポーネントというものがPsychoPyには用意されています。 .. _fig-form-example: .. figure:: fig09/form-example.png :width: 80% Formコンポーネントで作成できるレイアウト :numref:`fig-form-icon` にFormコンポーネントのアイコンを示します。「基本」タブにはおなじみの **[名前]** 、 **[開始]** 、 **[終了]** があり、続いて **[項目]** があります。 この **[項目]** がFormコンポーネントの鍵となるプロパティで、質問文やスライダーのパラメータを定義したCSVファイルまたはxlsxファイルを指定します。 .. _fig-form-icon: .. figure:: fig09/form-icon.png :width: 50% Formコンポーネントのアイコン 項目ファイルで定義できる内容を :numref:`tbl-form-items-file` に示します。1行目に見出しを書き、2行目以降に1行につき1項目の形で質問項目を定義していきます。後で具体的な例を挙げながら解説します。スクリーン上では項目ファイルに記入した順番に質問項目が表示されますが、 **[無作為化]** にチェックを入れておくと実行時にPsychoPyが項目の順序を無作為に並べ替えてくれます。 **[データフォーマット]** は、データを出力する際の形式を指定します。質問項目ひとつが「行」の場合はデータファイルの1行に、「列」の場合は1列に出力されます。 .. tabularcolumns:: |p{6zw}|p{30zw}| .. _tbl-form-items-file: .. csv-table:: Formコンポーネントの項目ファイルで指定する内容 :widths: 24, 64 :delim: ; :class: longtable inde;項目の順序を整数で指定します。省略すると自動的に番号が割り振られます。刺激の描画には影響しませんが、 **[無作為化]** した時にデータファイルの出力を項目順に並び替えるときに役に立ちます。 itemText;表示したい文を指定します。 type;項目の種類を指定します。rating,slider,radioはSliderコンポーネントと同じです。radioはchoiceと書くことも出来ます。heading,descriptionは教示などの文だけを表示したいときに使います(headingはやや大き目でボールド体になる)。heading, descriptionを選んだ場合はoptions, ticks, ticklabels, layout, responseColor, responseWidth, granularityは意味を持ちません。ほかに文字列を入力できるfree textを指定できますが、TextBoxコンポーネントと同じで現状では日本語入力には使い物になりません(オンライン実験なら実用的)。 options;スライダーのラベルをカンマ区切りで指定します。ラベルに対応する値は自動的に決まりますが、両者を別々に設定したい場合はticksとtickLabelsを使います。 ticks;スライダーの目盛をカンマ区切りで指定します。 tickLabels;スライダーの目盛をカンマ区切りで指定します。 layout;スライダーの方向をhoriz、vertのいずれかで指定します。horizなら水平方向、vertなら垂直方向です。省略するとhorizになります。 itemColor;質問文の文字色を指定します。省略するとwhiteになります。スタイル(後述)がcustom...の場合のみ有効です。 itemWidth;Formコンポーネントの **[サイズ$]** で指定した幅のうち、質問文が占める幅の割合を0.0から1.0指定します。 responseColor;スライダーの色を指定します。省略するとwhiteになります。スタイル(後述)がcustom...の場合のみ有効です。 responseWidth;Formコンポーネントの **[サイズ$]** で指定した幅のうち、スライダーが占める幅の割合を0.0から1.0指定します。 granularity;Sliderコンポーネントの **[精度]** に対応しますが、typeがsliderの時のみ有効です。rating, radio, choiceの時は目盛の位置しか選択できません。 font;使用するフォントはFormコンポーネントのプロパティダイアログの **[フォント]** で指定しますが、特定の項目だけ別のフォントを指定したい場合はここでフォント名を指定します。 「外観」タブの **[スタイル]** は全体的な配色を選択します。 darkとlightという配色が用意されているほか、custom...という項目を選択して細かく指定することもできます( **[塗りつぶしの色]** などの項目はcustom...にしないと有効になりません)。「レイアウト」タブの **[サイズ $]** はFormコンポーネントによって作成される質問項目全体を囲む枠の大きさに対応していると考えてください。 **[サイズ $] に対して項目数が多い場合は、枠の右端にスクロールバーが自動的に設けられます** 。 **[位置 [x, y] $]** は全体を囲む枠の中心の座標、 **[項目間の余白 $]** 文字通り項目間の余白を指定します。フォントの大きさは「書式」タブの **[テキストの高さ $]** で指定します。 それでは実際に作業しながら確認しましょう。 - 実験設定ダイアログ - PsychoPyの設定でheight以外の単位を標準に設定している場合は、実験設定ダイアログを開いて **[単位]** をheightにしておく。 - trialルーチン - Formコンポーネント1つ配置して以下の通りに設定する。 - **[名前]** を form にする(初期値のまま)。 - **[項目]** を items.csv にする。 - **[テキストの高さ $]** を 0.02 にする。 - **[フォント]** に日本語に対応したフォントを指定する(WindowsならMeiryo、MacならHiragino Sansなど)。 以上の作業を行った実験を保存し(名前は何でも良いですがform_test.psyexpとでもしましょうか)、 :numref:`fig-form-sample-xlsx` の内容のxlsxファイルを作成して同じ場所にitems.csvという名前で保存してください。 **xlsx形式にも対応しているのですが、xlsx形式は読み込みに失敗することが多いのでcsv形式で保存することを強くお勧めします。** .. _fig-form-sample-xlsx: .. figure:: fig09/form-sample-xlsx.png :width: 100% 項目ファイルの内容。横に長いので途中で分割して2段にしているのでご注意ください。 .. .. _tbl-form-sample-xlsx: .. .. .. csv-table:: 項目ファイルの内容 .. :escape: ' .. .. index,itemText,type,options,ticks,tickLabels,layout,itemColor,itemWidth,responseColor,responseWidth,granularity,font .. ,見出し,heading,,,,,lightyellow,,,,, .. ,説明文の例です,description,,,,,,,,,, .. ,ratingの例,rating,"1,2,3,4,5",,,horiz,lightblue,0.3,lightblue,0.5,, .. ,sliderの例,slider,,"1,2,3,4,5","1,2,3,4,5",horiz,,0.3,,0.5,0.1, .. ,radioの例\n(choiceと書いても同じ),radio,"あてはまらない,あてはまる",,,vert,,0.3,,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` を見比べながら項目の指定方法を確認しましょう。まず、実行結果の上から1段目がheading、2段目がdescriptionの出力例です。フォントの大きさや字体が異なることがわかります。 3段目から5段目がrating、slider、radioの例です。いずれもitemTextの列が質問文として表示されていることがわかります。英語などではFormコンポーネントの幅に対して質問文が長すぎるときに自動的に改行されるのですが、日本語では自動改行されません。どうしても改行させたい場合はこの例の最後の項目のように \\n を使って改行します。ただし、出力されるデータファイル内でも改行されてしまいますので、データ処理の際に注意が必要です(Excelで開く場合は問題ないようです)。自動改行は半角スペースの位置で生じますので、一度改行なしで表示させてみてから「ここで改行してほしい」という位置に手作業で半角スペースを入れる方法もあります。 続いて質問文の右側に表示されているスライダーですが、Sliderコンポーネントのrating、slider、radioと対応していることがおわかりいただけると思います。options, ticks, tickLabelsは機能が重複していてややこしいのですが、おそらくこれれは旧バージョンとの互換性を保つためだと思います。旧バージョンではoptionsのみで項目を指定する必要があったため、Sliderコンポーネントのように **[ラベル]** と **[目盛]** を独立に指定することができませんでした。そこで **[ラベル]** に対応するtickLabels、 **[目盛]** に対応するticksが追加されたのですが、旧バージョンのoptionsも残されているのでしょう。optionsのみを使うか、ticksとtickLabelsを組み合わせて使うのがよいと思います。使わない方はitems.csvのように空欄にしておいて問題ありません。 layout、itemWidth、responseWidth、itemColor、responseColorは見た目を調整するパラメータです。layoutはスライダーの向きをhoriz(水平)かvert(垂直)で指定します。:numref:`fig-form-sample-screen` の最後の項目がvertの例です。このようにradioとvert組み合わせると、ラベルが少し長めの文字列の場合でもうまく枠内に収めることができます。 itemWidthとresponseWidthはFormコンポーネントの枠の幅に対して質問文とスライダーの幅が占める割合を0.0から1.0で指定します。これらの値の合計が1.0を超えてしまうと、項目とスライダーが重なってしまったり枠からはみ出たりしてしまって正常に表示されません。合計が1.0に満たない場合は質問文とスライダーの間に空白ができます。 itemColorとresponseColorは質問文とスライダーの色の指定です。他のコンポーネントと同様にRGBの値を-1.0から1.0で表したリストや色名が利用できます。 **[スタイル]** でdarkまたはlightが選択されている場合はそちらの色設定が優先されるので、サンプルを最初に実行した時には指定した色が反映されていないはずです。Builderで配置済みのFormコンポーネントのプロパティダイアログを開いて、 **[スタイル]** をcustom...に変更して実行してみてください。 items.csvに新たな行を追加して項目を増やしてみたり、itemWidth, itemColor, responseWidth, responseColor, granularity, fontなどを自由に編集して、効果を確かめてみてください。特に行数が多くなって枠内に全項目が入りきらなくなった時に、枠の右側にスクロールバーが出現してスクロールさせて全項目を操作できるようになることを確認してください。 一通り確認を終えたら続いてデータファイルの確認をしたいのですが、現状ではESCキーを押して中断するしかないため正常に終了できるようにしないといけません。Formコンポーネントを置いてあるルーチンを終了させる方法はいろいろ考えられますが、Formの操作でマウスを使用しますので、OKボタンを用意してクリックして終了するようにするのがよいでしょう。ここではButtonコンポーネントを使います。 - trialルーチン - Buttonコンポーネントを1つ配置して、以下の通りに設定する。 - **[終了]** を空欄にする。 - **[Routineを終了]** がチェックされていることを確認する。 - **[ボタンのテキスト]** に OK と入力する。 - **[サイズ [w, h] $]** を(0.05, 0.03)にする。 - **[位置 [x, y] $]** を(0.4, -0.4)にする。 - **[文字の高さ $]** を0.02にする。 - **[クリックを記録]** を「なし」にする。 これでスクリーンの右下に 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コンポーネントの **[開始]** を「条件式」に変更し、form.formComplete()と入力する。 修正が終わったら実行してください。最初は右下の OK ボタンは表示されていなくて、Formコンポーネントの質問をすべて回答したらボタンが出現するはずです。**[開始]** および **[終了]** で条件式を使う方法は応用範囲が広く、使い回こなすといろいろと面白いことができます。 :numref:`第%s章 ` でも出てきますので、ぜひ参考にしてください。 さて、ここまで作成した実験を実行し、すべての質問に回答して終了すると、いつも通りCSV形式のデータファイルが保存されているはずです。データファイルの例を :numref:`fig-form-datafile` に例を示します。通常のコンポーネントはループでの繰り返し1回につき1行の形式でデータを出力しますが、Formコンポーネントは複数行にわたってデータを出力します。 **[データフォーマット]** が「行」の場合は質問1項目につき1行の形式で項目のインデックス(項目ファイルのindexの列で指定した値)、質問文、反応、反応時間などが出力されます。 **[データフォーマット]** が「列」の場合は「行」形式の出力内容を1行に展開した形式となり、一見1行に収まっているように見えるのですが、ルーチン内に他のコンポーネントが存在してデータを出力した場合、Formコンポーネントの出力は他のコンポーネントと別の行になります。この意味で、1回の繰り返しにつき1行の形式が守られていません。人が目視で確認する場合は「行」の方が見やすいと思いますし、プログラムで処理する場合はどちらも「繰り返し1回=1行」のルールが破られてしまうので工夫が必要です。使いやすいと思う方を選択していただければと思います。 .. _fig-form-datafile: .. figure:: fig09/form-datafile.png :width: 80% Formコンポーネントの出力 最後にひとつ注意点を挙げておきます。ループの中でFormコンポーネントを使用した場合、2回目以降の実行時には前回の反応が残っています。その方が便利な場合もあるかもしれませんが、繰り返しのたび初期化したいケースが多いのではないでしょうか。2回目以降の実行時に未反応の状態に戻すには、Formオブジェクトのreset()というメソッドを使用します。Codeコンポーネントを用いてRoutine開始時に以下のコードを実行するとよいでしょう。 .. code-block:: python form.reset() チェックリスト - Formコンポーネントで複数の質問項目とスライダーのペアをルーチンに配置することができる。 - Formコンポーネントのすべての項目に回答したらルーチン終了のボタンを表示させることができる。 - ループの中でFormコンポーネントを使う時に繰り返しのたびに反応を初期化することができる。 この章のトピックス --------------------------------------------------- .. _topic-slider-getRating: Sliderによる色の変更が動作しない原因と解決例 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 本書を第5版に改定するにあたって本章の内容を確認していたところ、バージョン2022.2.4においてSliderによるPolygonコンポーネントの色の変更が動作しないことに気づきました。 2021系の最終バージョンである2021.2.3では動作すること、特に仕様変更に関するアナウンスがないことから一時的なバグではないかと考えていますが、サンプルが動かないのは困りますので原因と解決例を記しておきます。 単刀直入に言うと、この問題の原因は、2022.2.4ではgetRating()がカテゴリカルな尺度でもマーカーの位置を浮動小数点数で返すことです。ということは、解決するにはマーカー位置から項目の値を得ればよいでしょう。 **[ラベル]** に'white', 'blue', 'orange'と3項目を入力していて **[目盛]** が空欄ですから、getRating()の戻り値はwhiteを選択したとき0.0、blueなら1.0、orangeなら2.0です。したがって、Codeコンポーネントの **[フレーム毎]** のコードを以下のように変更すれば期待通りの動作となります(3行目のみが異なる)。 .. code-block:: python color_choice = slider_color.getRating() if color_choice is not None: polygon_color = ['white', 'blue', 'orange'][int(color_choice)]