Hi all,
Question
I have coded a task containing a slider controlled by the keys “up” and “down”, using the method KeyStateHandler() (mentioned here) .
The movement of the rating worked perfectly smooth on my own laptop, but when I switched to another laptop the movement among the slider became a bit choppy. I am wondering if this is because of the inefficiency of my codes, or it’s more related to the hardware? (codes and description of the task will follow)
Any comments or suggestions on codes would be helpful!
Flow of the task
Here is a simplified flow chart of the timeline in case that I don’t do a good job explaining the flow:
After the first set of image and text (img1 + txt1) appears, the participant is allowed to make response by pressing “↑” or “↓” to control the slider.
If he or she has made any response (hasRespond = True) and if there is no keyboard input for some specific time (= timeConfidence), it’s assumed that the participant has made their decision. The slider rating and the reaction time, and the keyboard history during the picture will be recorded.
Then the next set (img2+txt2) will be set to start.
After the same decision period for img2+txt2, img3+txt3 will be started. But for these two, the slider is set to read-only and we ignore the inputs from keyboard.
During the entire time, the mouse is disabled (by fixate it at the corner). All three pictures appear by fading in.
Codes
(I only paste the critical section of trial here, if need more info please let me know)
for thisRepeat in Repeat:
event.clearEvents(eventType='keyboard')
currentLoop = Repeat
# abbreviate parameter names if possible (e.g. rgb = thisRepeat.rgb)
if thisRepeat != None:
for paramName in thisRepeat:
exec('{} = thisRepeat[paramName]'.format(paramName))
#--------------------#
#-------Trials-------#
#--------------------#
# update component parameters for each repeat
slider.reset()
text1.setText(txt_1)
text2.setText(txt_2)
text3.setText(txt_3)
image1.setImage(img_1)
image2.setImage(img_2)
image3.setImage(img_3)
# ------Prepare to start Routine "Trials"-------
sliderHistorySecond = [] # store here the rating and the time at point wanted
TrialsComponents = [slider, text1, text2, text3, image1, image2, image3] # keep track of which components have finished
for thisComponent in TrialsComponents:
initiateComponent(thisComponent) # set the values to None
core.wait(timeBuffering) #blank screen for time buffering
hasRespond = False # check if the participant has responded or not
continueRoutine = True # check if continue routine or not
frameN = -1
t = 0
entering = 0 # for each repeat, entering start from 0, therefore need to be initialized outside of the loop
# initialize some clocks
TrialsClock = core.Clock() # Create some handy timers
countDown = core.CountdownTimer() # Used to detect whether we reached the timeConfidence
# create a keyboard to record all the keys pressed during one repeat
trialKeyboard = keyboard.Keyboard()
event.clearEvents() # clean up buffer before entering to the loop (not sure why it couldn't remove the keys with negative reaction time, and this is why I added getKeys() to remove)
trialKeyboard.getKeys() # to remove the keys that was pressed during the buffering periods, or we will get negative reaction time for those keys
# reset the clocks to starting time
TrialsClock.reset() # clock of the trial
countDown.reset(t=timeConfidence) # clock for timeConfidence
# -------Start Routine "Trials"-------
while continueRoutine:
# get current time
t = TrialsClock.getTime() # current time
frameN = frameN + 1 # number of completed frames (so 0 is the first frame)
if myMouse.getPos()[0]!= 1 or myMouse.getPos()[1] != 1:# Fixate the mouse
myMouse.setPos(newPos = (1,1))
# notice: I added this step because on my computer the setVisible of mouse event didn't work
# may be due to the mouse initialization in the slider class
# Therefore I just fixiated it s.t. it won't be used
# update/draw components on each frame
#---------------#
#---------------#
#-slider-update-#
#---------------#
#---------------#
# Movement and record updates
# changed to DOWN and UP according since it's a verticle slider
# only detect during the first two pictures
if entering < 2 :
if keyState[key.DOWN]:
newRat = slider.rating - sliderSpeed
slider.rating = newRat
countDown.reset(t = timeConfidence)
hasRespond = True
if keyState[key.UP]:
newRat = slider.rating + sliderSpeed
slider.rating = newRat
countDown.reset (t = timeConfidence)
hasRespond = True
if t >= timeStartOne:
#record starting position, starting slider
if slider.status == NOT_STARTED:
slider.recordRating(rating = sliderStartP)
slider.setAutoDraw(True)
#update slider
componentUpdate(slider, t, frameN, False)
#-------------------------#
#-------------------------#
#-switch-between-pictures-#
#-------------------------#
#-------------------------#
# see whether the decision is made, more specifically:
# Not entering unles the participant has responded
# when non of the keys are pressed for the confidence time, record the rating, redo clock
# add count for entering this, set the True/False value accordingly for image and text
# note that when setAutoDraw = True, status changes to STARTED automatically
# when entering = 1, image1 & txt1 finished, image2 & txt2 starts
# when entering = 2, image2 & txt2 finished, image3 & txt3 starts, and set the slider to read-only
# when entering = 3, image3 & txt3 finished, stop the entire trial
if countDown.getTime() <= 0 and hasRespond: # countDown is a count-down timer, with parameter "timeConfidence"
entering += 1 #implement by 1
command = "pass" #place holder
#entering = 1, 2
if entering < 3:
hasRespond = False # flip it back to false
command = "text{}.setAutoDraw(False)\nimage{}.setAutoDraw(True)\ntext{}.setAutoDraw(True)".format(str(entering), str(entering+1), str(entering+1)) # starts the next image
command +="\nkeyRecord{} = trialKeyboard.getKeys(['up','down'])".format(str(entering))# get the keys here (getKeys get the respond starting from the last call of getKeys)
if entering == 2: # after the end of the second image
command += "\nslider.readOnly = True" # set slider to read only for the third picture
hasRespond = True # When we are at the second image, set it True for the third image
# Note: since the keyboards seems to be using the same buffer, if stop recording the keyboard input here, the defaultKeyboard won't be able to catch escape and space for exiting the program later
exec(command)
exec("tmp = image{}.tStart".format(str(entering)))
sliderHistorySecond.append([slider.getRating(), (t-timeConfidence-tmp)]) # record rating and the time of decision, append it to the list
# This is the rating and time for making the decision, should get two pairs since there shouldn't be any rating for the third picture
#entering = 3
if entering == 3:
continueRoutine = False # stop the routine
countDown.reset(t = timeConfidence) # reset the clock at the end s.t can count down again
#------------------------#
#------------------------#
#-image-and-text-updates-#
#------------------------#
#------------------------#
# Notice that this can't be moved before the switching, because
# text1 + image 1 updates after the "timeStartedOne"
if t >= timeStartOne:
componentUpdate(text1, t, frameN, True)
componentUpdate(image1, t, frameN, True)
# update opacity of img1
imgFadeIn(image1, frameN, timeFadePara)
# text2 + image2 updates
componentUpdate(text2, t, frameN, False)
componentUpdate(image2, t, frameN, False)
# update opacity of img2
imgFadeIn(image2, frameN, timeFadePara)
# text3 + image3 updates
componentUpdate(text3, t, frameN, False)
componentUpdate(image3, t, frameN, False)
# update opacity of img3
imgFadeIn(image3, frameN, timeFadePara)
Thanks in advanced!