例題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)