例題12-5:建て増しで気配り¶
A: お、Bくんいいところに来た。
B: そのニヤニヤ顔、僕にとってとてもいい話のようには思えませんが。
A: いやいや。書きかけの実験プログラムの動作テストするから被験者やってくんない?
B: やっぱりそう来ましたか。今日はゼミの発表があって疲れてるので帰ります。
A: ほう。ここに出張土産の○○○屋のくるみゆべしがあるのだが。仕方ないなあ、じゃあ被験者はE君にでも…
B: やりましょう。何をすればいいんですか?
A: …その反応は予想通りだが、もう少し、こう、工夫はないのか。
B: 工夫? 工夫って言われても。
A: まあいい。ほれ。
B: へへへ、これ大好きなんですよ。いただきまあす。
A: ふむ。食べながらでもいいから見といてくれ。画面にこんな風に手の写真が出てくるんで、それが右手の写真家左手の写真か判断して左右のペダルを踏むのが課題だ。
B: 左右のペダル?
A: 机の下を見て。ペダルがあるだろ。
B: おお、なんですかこれ。
A: PCにUSB接続してマウスクリックやキー入力等のイベントを送れるとゆー代物だ。 それはともかく、ここからがポイントなんだが、答えるときに両手を太腿の上に、指を開いて手のひらを下にして置いて。
B: ええと、こうですか。
A: そうそう。それでいい。あとはB君も被験者慣れしてきただろうからやってみたらわかるでしょ。んじゃよろしく。
B: えー。そんないい加減な教示でいいんですか?
A: 動作確認だからな。
B: 教育的配慮がないなあ。ぶつぶつ。
A: B君ががんばってくれている間、皆さんはプログラムのソースをご覧ください。 注意点としては、まず左右のフットペダルを踏むとそれぞれキーボードの"f"と"j"が押されたというイベントが発生するようにペダルを設定しています。 あとは17行目と18行目でお馴染みのVisionEgg設定を表示せずダイアログフルスクリーンモードで実行するように設定しています。あとは今までの例題が理解できていれば問題ないはず。
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: 出来た。まあ手抜きだけどないよりマシだろ。
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: んじゃ、今回はこれにて。最後に手の写真を提供してくださった○○君に感謝します。ではでは。