例題5-5:ここらでひとつ系列位置効果でも¶
A: このところ心理実験でもなんでもない短いサンプルばかりが続いたので、 例題5の締めくくりにひとつ系列位置効果の実験プログラムを掲載しておこう。これは昔実験実習用に書いたもので、 受講生が解答用紙と筆記用具を持ってPCの前に座って実行し、各自で答え合わせすることを想定している。
B: 答え合わせも受講生にやらせるんですね。手抜きだなあ。
A: いや、受講生の数にもよるが、そんなの教員がやってたら講義時間中に終わらんぞ。 プログラムとしては、実験の本体はキーが押されたら次の刺激を表示するか一定時間経過したら表示するかの違いだけで、 リーディングスパンテストの実験と大して変わらない。今回、このプログラムを引っ張り出してきたのは、Tkinterで フォントを指定したり、エディットボックスに文字列の初期値をセットしたり、チェックボタンを使ったり、 ラベルの文字列を引数で与えたりといったテクニックのサンプルになると思ったからだ。 敢えて詳しい解説はつけないので、例題1から例題5のまとめとして各自で読んでみてほしい。
B: とかいって、単に解説を書くのが面倒くさいんじゃないんですか?
A: 面倒くさいというか、なかなか時間がないんだよ。ホント。 いつか時間ができたらきちんと書きなおしたいね。
B: その日は永遠に来ないに3000点。
A: むっ。でも正直自分でもそうかも知れんと思う。一部に例題1から5で 全く紹介していないクラスなどを使っているので、それだけ簡単に解説しておこう。それでは検討を祈る。
Tkinter.StringVar().set() |
Tkinter.StringVar()にあらかじめ値を設定しておく方法のサンプルです。 |
tkMessageBox |
簡単なメッセージダイアログを表示するためのモジュールです。 ここでは指定したファイルをオープンできなかったときのエラーメッセージ表示に使用しています(31行目)。 |
sys.exit() |
プログラムをその場で終了させるメソッドです(32行目)。 |
datetime |
日付や時間のデータを操作するためのモジュール。 35行目ではdatetimeモジュールに含まれるdatetimeクラス(ややこしい…)のnowメソッド()で現在の日時を取り出し、strftime()メソッドで 文字列に変換しています。詳しくはPythonライブラリリファレンスを参照してください。 |
CheckWindowクラス |
Tkinter.Frameを継承して系列位置効果の答え合わせ ダイアログを作成しています(140行目から)。フォントの大きさや種類を指定したり、Tkinter.IntVarとTkinter.CheckButtonを使って チェックボタンを作成したり、ラベルの文字列を引数で与えたりするなど、例題5-3や5-4よりもう少し踏み込んだTkinterの使い方のサンプルです。 |
unicode.encode() |
テキストファイルに日本語を出力するとき、文字コードが原因の トラブルが起こることがあります。234行目では普通にr[2]とすると文字列を出力できないと言って怒られるので、encode()というメソッドを 用いて文字コードを指定しています。Shift JIS以外の環境で使用されている方は、それに合わせて変更する必要があります。 |
break |
現在のforやwhile文によるループを強制的に中断します。 例題4-2で出てきたcontinueと似ていますが、中断された後はループの次の行へ処理が進みます。このサンプルではESCキーで強制的に先の画面に進むために使用しています。 |
補足(2012/10/31)¶
05-5.pyではTkinter.Labelなどのウィジェットに毎回font=('Helvetica', '12')という引数を与えてフォントを指定していますが、ダイアログ内のすべての要素に対して同じ文字を設定する時にはoption_addというメソッドが使えます。 以下05-5.pyの11行目から22行目をopion_addを使って書き直した例を示します。
class FileWindow(Tkinter.Frame):
def __init__(self,master=None):
Tkinter.Frame.__init__(self,master)
#self.masterのoption_addというメソッドですべてのフォントをHelveticaの12ポイントに
self.master.option_add('*font', ('Helvetica', 12))
self.FileNameEntry = Tkinter.StringVar()
self.FileNameEntry.set('.txt')
#個々のウィジェットでfont=...を設定する必要がない
Tkinter.Label(self,text=u'ファイル名').grid(row=0,column=0,padx=5,pady=5)
Tkinter.Entry(self,textvariable=self.FileNameEntry).grid(row=0,column=1,padx=5,pady=5)
Tkinter.Button(self,text=u'OK',command=self.quit).grid(row=1,columnspan=2,ipadx=15,pady=5)
self.pack()
行番号なしのソースファイルをダウンロード→ 05-5.py
1#!/usr/bin/env python
2# -*- coding: shift-jis -*-
3
4########################################
5# データファイルの設定
6import Tkinter
7import tkMessageBox
8import datetime
9import sys
10
11class FileWindow(Tkinter.Frame):
12 def __init__(self,master=None):
13 Tkinter.Frame.__init__(self,master)
14 self.FileNameEntry = Tkinter.StringVar()
15 self.FileNameEntry.set('.txt')
16 Tkinter.Label(self,text=u'ファイル名',
17 font=('Helvetica', '12')).grid(row=0,column=0,padx=5,pady=5)
18 Tkinter.Entry(self,textvariable=self.FileNameEntry,
19 font=('Helvetica', '12')).grid(row=0,column=1,padx=5,pady=5)
20 Tkinter.Button(self,text=u'OK',command=self.quit,
21 font=('Helvetica', '12')).grid(row=1,columnspan=2,ipadx=15,pady=5)
22 self.pack()
23
24wf = FileWindow()
25wf.mainloop()
26
27try:
28 fDataFile = open(wf.FileNameEntry.get(),'w')
29except:
30
31 tkMessageBox.showerror('Error',u'ファイルを開けませんでした。終了します。')
32 sys.exit()
33
34wf.winfo_toplevel().destroy()
35fDataFile.write('#%s\n'%datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S'))
36
37############################
38# VisionEggの初期化
39############################
40import VisionEgg
41from VisionEgg.Core import *
42from VisionEgg.Text import *
43
44from random import shuffle
45
46
47import pygame.locals
48
49VisionEgg.start_default_logging()
50VisionEgg.watch_exceptions()
51
52VisionEgg.config.VISIONEGG_GUI_INIT = 0
53VisionEgg.config.VISIONEGG_FULLSCREEN = 0
54VisionEgg.config.VISIONEGG_SCREEN_W = 800
55VisionEgg.config.VISIONEGG_SCREEN_H = 600
56
57screen = get_default_screen()
58SX = screen.size[0]
59SY = screen.size[1]
60
61nTrials = 8
62nWordsInTrial = 15
63StimulusDuration = 1.0
64InterStimulusInterval = 1.0
65answerCheck = 4 #4試行毎に答え合わせ
66
67fontSize = 32
68fontName = r'C:\Windows\Fonts\MSGOTHIC.TTC'
69fontSizeStim = 40
70
71######################################
72# 単語リストの作成
73######################################
74
75wList = [u'要素',u'整理',u'管理',u'資金',u'声明',u'孝行',u'輸送',u'精神',
76 u'呼吸',u'事情',u'対策',u'実際',u'乱用',u'俳句',u'征服',u'意味',
77 u'圧迫',u'価値',u'事業',u'紹介',u'記者',u'漁業',u'質問',u'経済',
78 u'報告',u'適当',u'宣伝',u'実験',u'無実',u'停車',u'配当',u'結果',
79 u'設備',u'機会',u'技術',u'攻撃',u'電波',u'状態',u'過労',u'事実',
80 u'基礎',u'原価',u'留学',u'主義',u'選挙',u'最後',u'思想',u'目標',
81 u'強気',u'当時',u'委員',u'屋上',u'関係',u'警察',u'検査',u'必要',
82 u'程度',u'美術',u'世論',u'権利',u'帽子',u'部分',u'税金',u'世間',
83 u'試験',u'姉妹',u'訪問',u'知識',u'注意',u'宗教',u'種類',u'植物',
84 u'職場',u'教育',u'免許',u'練習',u'注目',u'設計',u'企業',u'笑顔',
85 u'資本',u'感情',u'団体',u'周囲',u'推理',u'経営',u'故郷',u'建物',
86 u'想像',u'意志',u'輸出',u'予防',u'利益',u'勝負',u'大体',u'午後',
87 u'電池',u'場合',u'発展',u'秘密',u'価格',u'記録',u'研究',u'能力',
88 u'用意',u'常識',u'生産',u'賛成',u'事件',u'自動',u'労働',u'国際',
89 u'期間',u'貿易',u'有料',u'最近',u'満員',u'表情',u'普通',u'役所']
90
91
92#単語をランダムに並べ替えておく
93shuffle(wList)
94
95cnd = []
96for i in range(nTrials):
97 cnd.append(wList[i*nWordsInTrial:(i+1)*nWordsInTrial])
98
99stim = Text(anchor='center',position=(SX/2,SY/2),
100 font_name=fontName,font_size=fontSizeStim)
101msg1 = Text(anchor='center',position=(SX/2,SY/2),
102 font_name=fontName,font_size=fontSize)
103msg2 = Text(anchor='center',position=(SX/2,SY/2-fontSize*1.2),
104 font_name=fontName,font_size=fontSize)
105
106viewport = Viewport( screen=screen, stimuli=[stim,msg1,msg2])
107
108######################################
109# タイムアウト付きキー入力待ち関数
110
111def waitKey(exitkey=pygame.locals.K_SPACE,
112 escapekey=pygame.locals.K_ESCAPE, timeout=2.0):
113 wf = True
114 flgEscape = False
115 t = VisionEgg.time_func()
116 while wf:
117 if VisionEgg.time_func()-t > timeout:
118 wf = False
119 for event in pygame.event.get():
120 if event.type==pygame.locals.KEYDOWN and event.key==escapekey:
121 flgEscape = True
122 wf = False
123 elif event.type==pygame.locals.KEYDOWN and event.key==exitkey:
124 wf = False
125 return flgEscape
126
127######################################
128# テキストの表示オンオフをセットして画面を描画する関数
129
130def drawText(stimlist,screen,viewport):
131 for s in stimlist:
132 s[0].parameters.on = s[1]
133 screen.clear()
134 viewport.draw()
135 swap_buffers()
136
137########################################
138# 答え合わせダイアログ (15単語4トライアルに固定)
139def checkanswer(answerlist):
140 class CheckWindow(Tkinter.Frame):
141 def __init__(self,master=None,al=answerlist):
142 Tkinter.Frame.__init__(self,master)
143 row = 0
144 Tkinter.Label(self,text=u'思い出せた単語にチェックを付けてください',
145 font=('Helvetica', '12')).grid(row=row,columnspan=4,padx=5,pady=5)
146 row = 1
147 for col in range(4):
148 Tkinter.Label(self,text='Trial'+str(col+1),
149 font=('Helvetica', '12')).grid(row=row,column=col)
150
151 self.cl = []
152 for i in range(len(al)):
153 tmp = []
154 for j in range(len(al[0])):
155 tmp.append(Tkinter.IntVar())
156 self.cl.append(tmp)
157
158 for row in range(2,17):
159 for col in range(4):
160 Tkinter.Checkbutton(self,text=al[col][row-2],
161 variable=self.cl[col][row-2],
162 font=('Helvetica', '12')).grid(row=row,column=col)
163
164 row = 17
165 Tkinter.Button(self,text='OK',command=self.quitfunc,
166 font=('Helvetica', '12')).grid(row=row,columnspan=4,
167 ipadx=15,pady=5)
168 self.pack()
169
170 def quitfunc(self):
171 self.winfo_toplevel().destroy()
172 self.quit()
173
174 w = CheckWindow(None,answerlist)
175 w.mainloop()
176 checklist = []
177 for i in range(len(w.cl)):
178 for j in range(len(w.cl[0])):
179 checklist.append([i+1,j+1,answerlist[i][j],w.cl[i][j].get()])
180 return(checklist)
181
182########################################
183# メインループ
184
185results = []
186
187for tn in range(nTrials):
188 #開始を促すメッセージを表示
189 msg1.parameters.text = u'準備が出来たらスペースキーを押してください。'
190 drawText([[stim,False],[msg1,True],[msg2,False]],screen,viewport)
191
192 #キー押しを待つ
193 waitKey(timeout=3600.0)
194
195 #カウントダウン
196 for i in range(3):
197 msg1.parameters.text=str(3-i)
198 drawText([[stim,False],[msg1,True],[msg2,False]],screen,viewport)
199 flgEsc = waitKey(exitkey=pygame.locals.K_ESCAPE, timeout=1.0)
200 if flgEsc:
201 break
202
203 msg1.parameters.on = False
204 stim.parameters.on = True
205
206 for w in range(nWordsInTrial):
207 drawText([[stim,False],[msg1,False],[msg2,False]],screen,viewport)
208 flgEsc = waitKey(exitkey=pygame.locals.K_ESCAPE, timeout=1.0)
209 if flgEsc:
210 break
211
212 stim.parameters.text = cnd[tn][w]
213 drawText([[stim,True],[msg1,False],[msg2,False]],screen,viewport)
214 flgEsc = waitKey(exitkey=pygame.locals.K_ESCAPE, timeout=1.0)
215 if flgEsc:
216 break
217
218
219 #刺激を再生する間待つ
220 msg1.parameters.text = u'覚えている単語を回答用紙に記入してください。'
221 for i in range(60):
222 msg2.parameters.text = u'(残り' + str(60-i) + u'秒)'
223 drawText([[stim,False],[msg1,True],[msg2,True]],screen,viewport)
224
225 flgEsc = waitKey(exitkey=pygame.locals.K_ESCAPE, timeout=1.0)
226 if flgEsc:
227 break
228
229 if (tn+1) % answerCheck == 0:
230 results += checkanswer(cnd[(tn-answerCheck+1):(tn+1)])
231
232##実験結果を出力
233for r in results:
234 fDataFile.write('%d,%d,%s,%d\n' % (r[0],r[1],r[2].encode('shift-jis'),r[3]))
235fDataFile.close()
236
237#終了メッセージの表示
238msg1.parameters.text = u'実験終了です。ありがとうございました。'
239drawText([[stim,False],[msg1,True],[msg2,False]],screen,viewport)
240
241#10秒間経つかエスケープキーが押されたら終了
242waitKey(exitkey=pygame.locals.K_ESCAPE, timeout=10.0)