例題12-5:建て増しで気配り

A: お、Bくんいいところに来た。

B: そのニヤニヤ顔、僕にとってとてもいい話のようには思えませんが。

A: いやいや。書きかけの実験プログラムの動作テストするから被験者やってくんない?

B: やっぱりそう来ましたか。今日はゼミの発表があって疲れてるので帰ります。

A: ほう。ここに出張土産の○○○屋のくるみゆべしがあるのだが。仕方ないなあ、じゃあ被験者はE君にでも…

B: やりましょう。何をすればいいんですか?

A: …その反応は予想通りだが、もう少し、こう、工夫はないのか。

B: 工夫? 工夫って言われても。

A: まあいい。ほれ。

B: へへへ、これ大好きなんですよ。いただきまあす。

A: ふむ。食べながらでもいいから見といてくれ。画面にこんな風に手の写真が出てくるんで、それが右手の写真家左手の写真か判断して左右のペダルを踏むのが課題だ。

../_images/12-5-01.png

B: 左右のペダル?

A: 机の下を見て。ペダルがあるだろ。

B: おお、なんですかこれ。

A: PCにUSB接続してマウスクリックやキー入力等のイベントを送れるとゆー代物だ。 それはともかく、ここからがポイントなんだが、答えるときに両手を太腿の上に、指を開いて手のひらを下にして置いて。

B: ええと、こうですか。

A: そうそう。それでいい。あとはB君も被験者慣れしてきただろうからやってみたらわかるでしょ。んじゃよろしく。

B: えー。そんないい加減な教示でいいんですか?

A: 動作確認だからな。

B: 教育的配慮がないなあ。ぶつぶつ。

A: B君ががんばってくれている間、皆さんはプログラムのソースをご覧ください。 注意点としては、まず左右のフットペダルを踏むとそれぞれキーボードの"f"と"j"が押されたというイベントが発生するようにペダルを設定しています。 あとは17行目と18行目でお馴染みのVisionEgg設定を表示せずダイアログフルスクリーンモードで実行するように設定しています。あとは今までの例題が理解できていれば問題ないはず。

  • 行番号なしのソースファイルをダウンロード→ 12-4a.py

  • プログラム中で使用している画像ファイル(ZIP形式)→ 12-4.zip

  1# -*- coding: shift-jis -*-
  2
  3import VisionEgg
  4import VisionEgg.Core
  5import pygame
  6import VisionEgg.Textures
  7import VisionEgg.Text
  8import VisionEgg.MoreStimuli
  9
 10import random
 11import pygame.locals
 12
 13import codecs
 14import sys
 15import Tkinter
 16
 17VisionEgg.config.VISIONEGG_FULLSCREEN = 1
 18VisionEgg.config.VISIONEGG_GUI_INIT = 0
 19
 20########################################
 21# データファイルの設定
 22
 23class FileWindow(Tkinter.Frame):
 24    def __init__(self,master=None):
 25        Tkinter.Frame.__init__(self,master)
 26        self.FileNameEntry = Tkinter.StringVar()
 27        self.FileNameEntry.set('s00.txt')
 28        Tkinter.Label(self,text=u'ファイル名',
 29                      font=('Helvetica', '12')).grid(row=0,column=0,padx=5,pady=5)
 30        Tkinter.Entry(self,textvariable=self.FileNameEntry,
 31                      font=('Helvetica', '12')).grid(row=0,column=1,padx=5,pady=5)
 32        Tkinter.Button(self,text=u'OK',command=self.quit,
 33                      font=('Helvetica', '12')).grid(row=1,columnspan=2,ipadx=15,pady=5)
 34        self.pack()
 35
 36wf = FileWindow()
 37wf.mainloop()
 38
 39fDataFile = codecs.open(wf.FileNameEntry.get(),'w','shift-jis')
 40fDataFile.write(u'#%s\n' % unicode(sys.argv[0],'shift-jis'))
 41fDataFile.flush()
 42wf.winfo_toplevel().destroy()
 43
 44
 45texLeftPalm0 = VisionEgg.Textures.Texture('LeftPalm0.jpg')
 46texLeftBack0 = VisionEgg.Textures.Texture('LeftBack0.jpg')
 47texRightPalm0 = VisionEgg.Textures.Texture('RightPalm0.jpg')
 48texRightBack0 = VisionEgg.Textures.Texture('RightBack0.jpg')
 49texLeftPalm5 = VisionEgg.Textures.Texture('LeftPalm5.jpg')
 50texLeftBack5 = VisionEgg.Textures.Texture('LeftBack5.jpg')
 51texRightPalm5 = VisionEgg.Textures.Texture('RightPalm5.jpg')
 52texRightBack5 = VisionEgg.Textures.Texture('RightBack5.jpg')
 53
 54cnd = []
 55for lr in ['L','R']:
 56    for palmback in ['P','B']:
 57        for orientation in [0,90,180,270]:
 58                for rep in range(6):
 59                    cnd.append([lr,palmback,orientation])
 60
 61random.shuffle(cnd)
 62
 63screen = VisionEgg.Core.get_default_screen()
 64SX,SY= screen.size
 65
 66stim = VisionEgg.Textures.TextureStimulus(position=(SX/2,SY/2),anchor='center')
 67mesg = VisionEgg.Text.Text(position=(SX/2,SY/2),anchor='center',
 68                           font_name=r'C:\Windows\Fonts\MSGOTHIC.TTC')
 69
 70viewport = VisionEgg.Core.Viewport(screen=screen,stimuli=[stim,mesg])
 71
 72
 73for tn in range(len(cnd)):
 74    if cnd[tn][0] == 'L':
 75        if cnd[tn][1] == 'P':
 76            stim.parameters.texture = texLeftPalm5
 77        else: #B
 78            stim.parameters.texture = texLeftBack5
 79    else: #R
 80        if cnd[tn][1] == 'P':
 81            stim.parameters.texture = texRightPalm5
 82        else: #B
 83            stim.parameters.texture = texRightBack5
 84    
 85    stim.parameters.angle = cnd[tn][2]
 86    
 87    if tn == len(cnd)/2:
 88        mesg.parameters.text = u'残り半分です。ペダルを踏むと問題が表示されます。'
 89    else:
 90        mesg.parameters.text = u'ペダルを踏むと問題が表示されます。'
 91    mesg.parameters.on = True
 92    stim.parameters.on = False
 93    waitkeypress = True
 94    while waitkeypress:
 95        for e in pygame.event.get():
 96            if e.type == pygame.locals.KEYDOWN:
 97                if e.key == pygame.locals.K_f:
 98                    waitkeypress = False
 99                elif e.key == pygame.locals.K_j:
100                    waitkeypress = False
101        screen.clear()
102        viewport.draw()
103        VisionEgg.Core.swap_buffers()
104    
105    
106    mesg.parameters.on = False
107    stim.parameters.on = True
108    waitkeypress = True
109    startTime = VisionEgg.time_func()
110    while waitkeypress:
111        for e in pygame.event.get():
112            if e.type == pygame.locals.KEYDOWN:
113                if e.key == pygame.locals.K_f:
114                    response = [VisionEgg.time_func()-startTime, 'L']
115                    waitkeypress = False
116                elif e.key == pygame.locals.K_j:
117                    response = [VisionEgg.time_func()-startTime, 'R']
118                    waitkeypress = False
119        
120        if VisionEgg.time_func()-startTime > 3.00: #timeout
121            response = [3.00, '-']
122        
123        screen.clear()
124        viewport.draw()
125        VisionEgg.Core.swap_buffers()
126    
127    fDataFile.write('%c,%c,%d,%f,%c\n' % (cnd[tn][0],cnd[tn][1],cnd[tn][2],
128                                          response[0],response[1]))
129    fDataFile.flush()
130
131
132fDataFile.close()
133

B: …っと。終わりました。案外短いですね。

A: うむ。96試行しかないからな。プログラムの動作は問題なさそうだが…。

B: だが?

A: いや、この実験、察しがつくと思うんだけど、自分の手の姿勢と刺激写真の手の姿勢の組み合わせによって課題の難易度がどう変わるのかを見たいのね。 んで、96試行を1セットとして、セット毎に被験者と刺激写真の手の姿勢の組み合わせを変えながら実験を行う。

B: ふむふむ。

A: 自分の手の姿勢を間違えると実験として成り立たないんだよな。

B: はあ、そりゃそうですね。

A: 一応手の姿勢と刺激写真の手の姿勢の組み合わせごとに別のファイルにして、組み合わせがわかるようなファイル名をつける予定なのだが…。 やっぱり実行前にイラストで手の姿勢の説明を入れたりしたほうが被験者にもわかりやすいだろうし、教示を間違える危険性が小さい。 今回の実験は私自身ではなくてFさんにやってもらう予定だから、そのあたりは気配りをしておいたほうがいい。

B: ようするにFさんを信用してないんですな、そりゃ。Fさんに言ってやろ。

A: 何を言うか。自分が実験者でも不安はあるわい。 人間は間違うもの 、間違う可能性があることを前提に間違いによる損害を最小限に食い止める工夫をするのは基本中の基本だろ。

B: でも、普段のAさんの実験ってとてもそんな気遣いがされているようには思えませんが。しょっちゅう「あ、間違えてESC押しちゃった」とか言って実験が中断してるじゃないですか。

A: げふっ、げふっ。それはプログラムの開発にかけられる時間とのバランスを考えてだな…

B: まったく、口だけは偉そうなんだから。

A: とにかく。このプログラムの最初に「こういう風に手を構えてください」というイラスト入りの教示を入れることにする。 イラストは…描くの 面倒くさい な。写真があるんだから使いまわすか。ちょちょいのちょい。

B: …(黙々とくるみゆべしを食べている)

A: 出来た。まあ手抜きだけどないよりマシだろ。

../_images/12-5-02.png

B: むは、さすがに早いですね。

A: 数行コピペして書きなおしただけだからな。こんな感じ。 えーと、読者の皆様、12-4a.pyの71行目と72行目の間に以下のコードが入ります。

  • 行番号なしのソースファイル(全体)をダウンロード→ 12-4b.py

72instR = VisionEgg.Textures.TextureStimulus(position=(SX/2+160,SY/2),
73                                           anchor='center',size=(320,320),
74                                           texture=texRightBack5)
75instL = VisionEgg.Textures.TextureStimulus(position=(SX/2-160,SY/2),
76                                           anchor='center',size=(320,320),
77                                           texture=texLeftBack5)
78instMesg = VisionEgg.Text.Text(text=u'手をこのように太腿の上に置いてください。',
79                               position=(SX/2,SY/2-200),anchor='center',
80                               font_name=r'C:\Windows\Fonts\MSGOTHIC.TTC')
81
82instViewport = VisionEgg.Core.Viewport(screen=screen,stimuli=[instR,instL,instMesg])
83
84screen.clear()
85instViewport.draw()
86VisionEgg.Core.swap_buffers()
87
88waitkeypress = True
89while waitkeypress:
90    for e in pygame.event.get():
91        if e.type == pygame.locals.KEYDOWN:
92            if e.key == pygame.locals.K_f or e.key == pygame.locals.K_j:
93                waitkeypress = False

B: ??? Viewportが二つある? Viewportって二つ作れるんですか?

A: ああ、教えていなかったかな。VisionEgg.Core.Viewportの解説をしたのは…と、 例題1-4 か。 一応「Viewportは複数用意して「被験者へのメッセージ文字列は平面的に投影するけど、刺激は3D画像として投影する」なんてことも出来る」って書いてるな。 今気づいたけど例題1-4も気配りがテーマか。因縁を感じるな。

B: すごくあっさり書いてるからすっかり忘れてましたよ。しかもそのすぐ後に「Viewportのインスタンスはひとつ作れば十分」とか書いてあるし。

A: ふむ。こりゃすまんかったな。まあじゃあ今回は複数のViewportを使う実例ということで。 もし教示画面の追加をひとつのViewportで済まそうとすると、刺激のparameters.onをTrueにしたりFalseにしたりする処理を追加しないといけない。これがとても面倒くさい。 まあ今回は最初に一回表示するだけなのでViewportひとつでもそんなに面倒じゃないんだが、10試行に1回表示するとか、そのたびにメッセージが変わるとか、そういう実験だととても面倒くさい。 そういう時にViewportをうまく使い分けるととても楽だ。

B: はあ、そういうもんですか。

A: 一度このプログラムをViewportひとつで済ませるように書きなおしてごらん。 そうしたら言わんとしていることがわかると思うから。これは練習問題…にするほどのものでもないな。

B: ふは、ちょっとどきっとしました。

A: さて、そんなこんなで、この例題はそろそろお開き。

B: ずいぶんあっさり終了しましたね。本当にこれだけ?

A: うむ。実はな、そろそろ3Dオブジェクトの表示を取り上げようと思ったんだが、そうするとこの複数Viewportがさらに活躍するんだな。 で、サンプルプログラムを書いていたら、そういえば複数Viewportの例題出してないなぁと思って、そこへ今回の実験プログラムを書かなきやいけない事情が発生してだな、…

B: 要するに毎度おなじみの行き当たりばったりなわけですね。

A: まあ、そういうこった。

B: それにしても次は3Dオブジェクトの表示なんだ。ちょっと楽しみ。

A: ふふふ。それはどうかな?

B: へ? 他に何か企んでいるんですか?

A: もしかしたら以前挫折したアレのリベンジをするかも知れない。 まあ例によって予告はぜーんぜんアテにならないので、予告しないことにしておこう。…ってコラ、いつのまにくるみゆべし3個も食べたんだ!

B: ◎△$♪×¥●!!

A: 喰っちまったもんはしょうがないんで、Fさんの実験の本番の被験者もしてあげること。いいな?

B: は、はーい。

A: んじゃ、今回はこれにて。最後に手の写真を提供してくださった○○君に感謝します。ではでは。