Kitaya lab

オリジナル課題の追加2




Operant Houseのコードを検索機能を使って"def Task90()"を検索してください。
出てきたコードが課題90の本体のコードです。


def Task90():   # Test task
   global Phase
   Phase0_Init = 0
   Phase1_Init = 0
   Phase2_Init = 0
   while EndFlag == 0:
       OperantHouseUpdate()    # Run house keeping function (This must be executed once per frame)

       if GetBackButtonStat()==1:    # If "Back" button is clicked
           break   # Back to the task select phase
       if Phase == 0:  # Parameter inputs
           if Phase0_Init == 0:    # Make GUI for the setting of the parameters for this task
               RemoveMainRightWidget() # Remove task buttons
               PutStartBackButton()    # Put "start" and "back" buttons

               ColumnWidth = 14    # Width of input columns
               
               mMaxCorrectNum = ttk.Label(MainWindowRightFrame, text='MaxCorrectNum', width=ColumnWidth).grid(row=0, column=0, sticky=W)    # Put label
               MaxCorrectNumVar = IntVar(MainWindowRoot)  # Declare a variable for input column
               iMaxCorrectNum = ttk.Entry(MainWindowRightFrame, textvariable=MaxCorrectNumVar, width=ColumnWidth).grid(row=1,column=0)  # Create input column and link it with the variable
               
               mTimeLimit = ttk.Label(MainWindowRightFrame, text='TimeLimit(min)', width=ColumnWidth).grid(row=0, column=1, sticky=W)
               TimeLimitVar = IntVar(MainWindowRoot)
               iTimeLimit = ttk.Entry(MainWindowRightFrame, textvariable=TimeLimitVar, width=ColumnWidth).grid(row=1, column=1)
               
               mLickDur = ttk.Label(MainWindowRightFrame, text='LickDur(s)', width=ColumnWidth).grid(row=0, column=2, sticky=W)
               LickDurVar = IntVar(MainWindowRoot)
               iLickDur = ttk.Entry(MainWindowRightFrame, textvariable=LickDurVar, width=ColumnWidth).grid(row=1, column=2)


               Str = "ParametersForTask" + str(GetTaskID())
               if os.path.exists(Str + '/MaxCorrectNum.dat') == True:  # If save file named "MaxCorrectNum.dat" exist:
                   with open(Str + '/MaxCorrectNum.dat', 'rb') as PickleInst[GetTaskID()]:
                       MaxCorrectNumVar.set(pickle.load(PickleInst[GetTaskID()]))  # Substitute loaded data into the variable of thisprogram
               else:  # If save file doesn't exist
                   MaxCorrectNumVar.set(80)  # Substitute 80 into the variable

               if os.path.exists(Str + '/TimeLimit.dat') == True:
                   with open(Str + '/TimeLimit.dat', 'rb') as PickleInst[GetTaskID()]:
                       TimeLimitVar.set(pickle.load(PickleInst[GetTaskID()]))
               else:
                   TimeLimitVar.set(600)

               if os.path.exists(Str + '/LickDur.dat') == True:
                   with open(Str + '/LickDur.dat', 'rb') as PickleInst[GetTaskID()]:
                       LickDurVar.set(pickle.load(PickleInst[GetTaskID()]))
               else:
                   LickDurVar.set(2)


               PutRoiGui(0, 1, 1, 0)  # Put setting GUI of the indicated ROI on ROI window (ROI number, Detection mode, Thresholddirection, ShowSymbol or not)
               PutRoiGui(19, 0, 0, 1)

               RemoveAllDigitalOutGui()    # Remove all GUIs on the Digital out window
               PutDigitalOutGui(10)    # Put the digital output GUI for channel 10 on Digital ouput window
               PutDigitalOutGui(12)
               PutDigitalOutGui(13)

               RemoveAllServoGui() # Remove all GUI on the Servo GUI
               PutServoGui(3)  # Put a servo GUI for channel 3 on the Servo GUI

               Phase0_Init = 1 # Set a flag which indicating phase0 initialization has done

       if GetSaveTrgStat() == 1:    # If save trigger is activated
           # Parameters will be saved into a folder named "ParametersForTask90"
           Str="ParametersForTask"+str(GetTaskID())   # Substitute save folder path
           if os.path.exists(Str) == False:    # If save folder doesn't exist
               os.mkdir(Str)   # Create the folder
           with open(Str+'/MaxCorrectNum.dat', 'wb') as PickleInst[GetTaskID()]:
               pickle.dump(MaxCorrectNumVar.get(),PickleInst[GetTaskID()])     # Save a value of "MaxCorrectNumVar" as "MaxCorrectNum.dat" file

           with open(Str+'/TimeLimit.dat', 'wb') as PickleInst[GetTaskID()]:
               pickle.dump(TimeLimitVar.get(),PickleInst[GetTaskID()])

           with open(Str+'/LickDur.dat', 'wb') as PickleInst[GetTaskID()]:
               pickle.dump(LickDurVar.get(), PickleInst[GetTaskID()])


       if Phase == 1:  # Waiting phase (Task will start when the set time arrives)
           if Phase1_Init == 0: # If the initialization for phase1 has not done
               PutPreTaskButton()  # Put "StartNow" and "Back" button on Main window
               mStatusVar = StringVar(MainWindowRoot)  # Create a variable for status display
               mStatus = ttk.Label(MainWindowRightFrame, textvariable=mStatusVar)  # Create label object and link it with Mainwindow
               mStatus.place(x=10, y=0)    # Place label object on the Main window
               mOngoingResultVar = StringVar(MainWindowRoot)   # Create a variable for progress display
               Phase1_Init = 1 # Flat that phase1 has done
           mStatusVar.set('Test task (' + str(GetTaskID()) + ')    Waiting...')    # Show current status of the Operant House
           if IsStartTime() == 1:  # Check whether task start time arrives
               StartNow()  # Start task (Phase number will be "2")

       if Phase == 2:  # If it is during task
           if Phase2_Init == 0: # Initialization of the task
               PutEndTaskNowButton()   # Put "TaskEnd" button on Main window
               mStatusVar.set('Test task (' + str(GetTaskID()) + ')    Start time ' + str(TaskStartedMonth) + '/' + str(TaskStartedDay)+ ' ' + str(TaskStartedHour) + ':' + str(TaskStartedMinute) + '    Running')    # Substitute latest information about current task into"mStatusVar"
               mOngoingResult = ttk.Label(MainWindowRightFrame, textvariable=mOngoingResultVar)
               mOngoingResult.place(x=10, y=18)

               # Substitute task parameter values in StringVars into integer or string variable (to make the cord easeir to read)
               MaxCorrectNum=int(MaxCorrectNumVar.get())   # Get the value of "MaxCorrectNumVar" and convert it from string tointeger and substitute into variable named "MaxCorrectNum"
               TimeLimit = int(TimeLimitVar.get())
               LickDur = int(LickDurVar.get())

               # Declar local variables for this task
               CorrectNum = 0
               TaskDur = 0  # This will keep the elapsed time during of task
               NowDrinking = 0

               StartRecording()  # Start camera capture / TTL signal output
               StartLickRecording()  # Start an entry of lick log
               LightCycleControlOff()  # Deactivate automatic Light/Dark cycle illumination
               RoofLightOff()  # Turn off the lights on roof (Digital output Ch13)
               InfraredLightOn()   # Turn on the infrared LED illumination (Digital output Ch12)
               DigitalOutOn(10)        # Turn on cue LED connected to Ch10
               ServoPosInside(3)   # Change the angle of water arm servo connected to Ch3 to inside position
               CreateNormalPanel(0)  # Create a white-filled square panel as panel #0

               Writer_TouchEventTxt = open(Path + "/" + str(TimeNow.year) + "_" + str(TimeNow.month) + "_" + str(TimeNow.day) +" " + str(TimeNow.hour) + "h" + str(TimeNow.minute) + "m Task" + str(GetTaskID()) + " Touch.txt", 'w')  # Initialize the textexporter for a result file
               Writer_TouchEventTxt.write('TrialNum\tResult\t\t\tyyyy/mm/dd\th:m\ts\n')  # Write item name on the result file
               Writer_TouchEventCsv = open(Path + "/" + str(TimeNow.year) + "_" + str(TimeNow.month) + "_" + str(TimeNow.day) +" " + str(TimeNow.hour) + "h" + str(TimeNow.minute) + "m Task" + str(GetTaskID()) + " Touch.csv", 'w')
               print("Task #" + str(GetTaskID()) + " is started at " + str(GetTaskStartedMonth()) + '/' + str(GetTaskStartedDay()) + ' '+ str(GetTaskStartedHour()) + ':' + str(GetTaskStartedMinute()) + ':' + str(GetTaskStartedSecond()))  # Enter the start time in theconsole window of Pycharm

               Timer_Start(5)  # Start a timer #5 to measure the duration of the task
               Phase2 = 2  # Start task from the reward phase
               Phase2_Init = 1 # Flag indicating that initialization of Phase2 has done

           if Phase2 == 0:   # Initiation of new trial
               TaskDur = Timer_GetSec(5)
               ShowPanel(0)    # Display panel #0
               Phase2 = 1

           if Phase2 == 1:   # Panel presentation
               TouchedPanelID = DetectRoiNosepoke()  # Examine which panel is touched (return panel ID. If none of the panelstouched, return -1)
               if TouchedPanelID == 0: # If mouse touches the panel
                   ServoPosInside(3)    # Move the water nozzle into inside position
                   DigitalOutOn(10)    # Onset cue light
                   HidePanel(0)
                   NowDrinking = 0
                   CorrectNum += 1     # Increase the number of correct response
                   mOngoingResultVar.set('CorrectNum:' + str(CorrectNum))
                   Writer_TouchEventTxt.write(str(CorrectNum)+'\tPanelTouched(Correct)\t'+ str(TimeNow.year)+"/"+str(TimeNow.month)+"/"+str(TimeNow.day)+"\t"+str(TimeNow.hour)+":"+str(TimeNow.minute)+"\t"+str(TimeNow.second)+"."+str(TimeNow.microsecond//1000)+"\n") # Write the response on the text file
                   Writer_TouchEventCsv.write(str(CorrectNum) + ',1,' + str(TimeNow.year) + "," + str(TimeNow.month) + "," + str(TimeNow.day) + "," + str(TimeNow.hour) + "," + str(TimeNow.minute) + "," + str(TimeNow.second) + "." + str(TimeNow.microsecond//1000)+"\n")  # Write the response on the csv file
                   Phase2 = 2  # Start reward phase

               if Timer_GetSec(5) >= TimeLimit * 60:   # If time limit of the task comes
                   TaskDur = Timer_GetSec(5)
                   print("Time limit elapsed")
                   Timer_End(5)    # Stop the timer
                   Phase2 = -1

           if Phase2 == 2:  # Reward phase
               if DetectRoiNosepoke() == 19 and NowDrinking == 0:    # If the mouse initiates nose poking
                   NowDrinking = 1
                   Timer_Start(0)  # Start lick timer
               if NowDrinking == 1:    # If the nose poke has begun
                   if Timer_GetSec(0) >= LickDur:   # If the nosepoke duration exceeds the lick duration
                       Timer_End(0) # End timer for measuring lick duration
                       ServoPosMiddle(3)   # Move water nozzle back to the middle position
                       DigitalOutOff(10)   # Turn off the cue LED
                       NowDrinking = 0
                       if CorrectNum < MaxCorrectNum:  # If the touch number doesn't exceed the maximum number
                           Phase2 = 0
                       if CorrectNum >= MaxCorrectNum:   # If the touch number exceeds the maximum number
                           TaskDur = Timer_GetSec(5)  # Keep the task time
                           Phase2 = -1 # Go to the task finalizing phase

           if Phase2 == -1 or GetEndTaskNowButtonStat() == 1:  # If the flag is set to finish the task
               LightCycleControlOn()   # Activate automatic light/dark cycle
               ServoPosMiddle(3)   # Moze servo nozzle into middle position
               DeleteAllPanel()    # Remove a panel on touch screen
               InfraredLightOff()  # Turn off the infrared LED
               if GetRecordingStat() == 1: # If camera is capturing
                   SetEndRecordingTimer(60)    # Onset a timer to finish video recording after 60 frames (correspond about 2sec)from now
               # Add summary of results into the result file
               Writer_TouchEventTxt.write('CorrectNum:'+str(CorrectNum)+"\n")
               Writer_TouchEventTxt.write('SessionStartTime: ' + str(GetTaskStartedMonth()) + '/' + str(GetTaskStartedDay()) + ' ' +str(GetTaskStartedHour()) + ':' + str(GetTaskStartedMinute()) + ':' + str(GetTaskStartedSecond()) + "\n")
               Writer_TouchEventTxt.write('SessionEndTime: ' + str(TimeNow.month) + '/' + str(TimeNow.day) + ' ' + str(TimeNow.hour) + ':' + str(TimeNow.minute) + ':' + str(TimeNow.second + (TimeNow.microsecond // 1000) / 1000) + "\n")
               Writer_TouchEventTxt.write('TaskDuration(sec): ' + str(TaskDur) + "\n")
               # Add experimental conditions into the result file
               Writer_TouchEventTxt.write('MaxCorrectNum: '+str(MaxCorrectNum)+"\n")
               Writer_TouchEventTxt.write('TimeLimit: ' + str(TimeLimit) + "\n")
               Writer_TouchEventTxt.write('LickDur: ' + str(LickDur)+"\n")
               Writer_TouchEventTxt.write('RecordFPS: ' + str(GetRecordFps()) + "\n")  # Recorded frame number per second
               Writer_TouchEventTxt.write(GetRoiSensitivity() + "\n")  # Settings of each ROI
               Writer_TouchEventTxt.write(GetServoAngle() + "\n")  # Set angles of each servo

               Writer_TouchEventTxt.close()    # Close the text exporter for the result file
               Writer_TouchEventCsv.close()
               EndLickRecording()  # End lick log recording

               SendMail(DeviceNameVar.get()+' finished task '+str(GetTaskID())+'. Correct:'+str(CorrectNum)+' Dur:'+str(round(Timer_GetSec(5) / 60,1))+' min','The task is finished.') # Send a email (correct number and task duration are added to email title)

               Phase = 1  # Go back to the task-waiting phase
               Phase2 = 0
               Phase2_Init = 0

           SetDispVariable(0, 'Phase2', str(Phase2))  # Display "Phase2" value on the bottom of main window
   return



オペラントハウスのプログラムでは課題ボタンを押すと上記の関数(Task90)が呼び出されます。
そして6行目にあるループ構文(while)に行きつきます。

while EndFlag == 0:

そのためここより下のコードがメインループとなり、パラメータ設定、課題の待機、課題の実行中のいずれにおいても繰り返し実行されます。
ただこのコードには課題に直接関わる命令しか含まれていないため、カメラやArduinoの制御、GUIの表示やタイマーなどの基本動作が実行されません。

そこで次の行にある関数を実行しています。
この関数を毎フレームに1回実行する事でこれらの基本動作を維持させる事ができます。

OperantHouseUpdate()    # Run house keeping function (This must be executed once per frame)

この関数を毎フレームに1回実行する事でオペラントハウスの補助機能を維持する事ができます。


次の命令はメインウィンドウの左側にあるBackボタンを押されたらループを抜けて課題選択画面に戻る処理です。

if GetBackButtonStat()==1:    # If "Back" button is clicked
   break   # Back to the task select phase

ちなみに課題選択画面ではこのプログラムの最後尾にある以下のコードをループしています。

while EndFlag<=4: # Main roop of non-task period 3
   OperantHouseUpdate()

   if OHCnt==30:    # If it takes a while after running (Serial connection doesn't establish at the 1st frame)
       RoofLightOn()    #Turn on roof illumination
       InfraredLightOn()    # Turn on IR illumination
       ServoPosMiddle(3)  # Move water nozzle to the intermediate position
   if ButtonAWS_On==1: # If button activated AWS is turned on
       if AWS_Stat==1:
           ServoPosMiddle(3)
           DigitalOutOff(10)
           RoofLightOn()
           IsWaterCueBlink = 0
           ButtonAWS_On=0
   if EndFlag==3:  # If LEDs are turned off and water nozzle move to neutral position, turn off the switch of servo
       Angle = 127  # Means make servo is turned off (ValueRange=0-127(0-180 degree))
       ServoOutputBook = 1


では次に進みましょう。
この課題はPhaseが0,1,2に分けられています。
Phase0のコードは課題条件の入力画面、Phase1は課題待機画面、Phase2は課題中に実行されます。
このPhase変数はグローバル変数であり、オペラントハウスのボタン操作で値が変わります。

課題ボタンを押すとまずPhase0が実行されるので次の章でPhase0がどのようなコードか見て行きましょう。