psychopy.org | Reference | Downloads | Github

Keyboard input to move selecter on rating scale not detected

I have a likert-scale, where participants move the selecter rightwards and leftwards by clicking the ‘L’ key and ‘A’ key.
My issue is that the input is registered in the coder window(i.e. the ‘a’ or ‘l’ is added to my code), not in the experiment window, unless I click the experiment window with the mouse to set focus.
The scale appears within a loop, and it is only the first trial that presents the issue. After that, keyboard is registered and used to move the selecter on the scale without issue. I intend to run the experiment without the use of a mouse, so any help on how to solve this issue is greatly appreciated.
Is there a way I can force my system to set focus on the experiment-window when it opens?

Some info:
PsychoPy v. 3.1.0 (coder)
The issue is the same if using left/right arrow keys.
I believe I am using PyGame v. 1.9.4 for graphics (it seems to be my default)
Windows

Hi @Linea, so you have to manually focus the window using the mouse? What happens when you set the window to fullscreen using the fullscr window param? By the way, the same thing can be achieved using the new Slider component. E.g., the following should work in Coder.

from __future__ import absolute_import, division
from psychopy import locale_setup, visual, core, event

win = visual.Window(
    size=(1024, 768), fullscr=False, screen=0, 
    winType='pyglet', allowGUI=False, allowStencil=False,
    monitor='testMonitor', color=[0,0,0], colorSpace='rgb',
    blendMode='avg', useFBO=True, 
    units='height')

slider = visual.Slider(win=win, name='radio',
    size=(1.0, 0.1), pos=(0, -0.1),
    labels=['a','b','c','d','e'], ticks=(1, 2, 3, 4, 5),
    granularity=0, style=['rating'],
    color='LightGray', font='HelveticaBold',
    flip=False)

slider.reset()
slider.markerPos = 2
while True:
    keys = event.getKeys()
    if keys:
        if 'left' in keys:
            slider.markerPos -= 1
        if 'right' in keys:
            slider.markerPos += 1
    
    # check for quit (typically the Esc key)
    if "escape" in keys:
        core.quit()
    slider.draw()
    win.flip()


# make sure everything is closed down
thisExp.abort()  # or data files will save again on exit
win.close()
core.quit()

Thank you for your answer.
Ideally I would like to stick with the RatingScale, rather than switching to the slider, since it makes it easy for me to record the reaction time.

I have already set my window to fullscreen (fullscr = true).

I am further puzzled about this issue.
I have added a screen displaying instructions to the participant, and to move on from the instruction, the participant needs to press any key. If a letter key is pressed it is registered, and program continues, but the letter they types will also be added to the code in the psychopy coder window (at the location I was last writing).
The same thing happens when the participant click ‘A’ or ‘L’ (which are used to move the ratingsscale), except they only occur in the code, but does not work on the rating-scale, untill the window has been clicked with the mouse (still only for first ratings-scale out of the loop).

Anybody recognize this issue?
Linea

I am having this issue as well. If anyone has any more recommendations to fix it, that would be super helpful! I have tried both ratingScale and Slider using Psychopy3 and it remains an issue…
UPDATE: issue only occurs when in fullscreen mode

from psychopy import visual 
from psychopy import gui, data, core,event
import csv
import time
from time import localtime, strftime, gmtime
from datetime import datetime
import os.path
import pyglet 

def close():
    """Quits experiment if user presses 'escape' key."""
    if 'escape' in event.getKeys():
        print ("User exited")
        win.close()
        outputfile.close()
        core.quit()

# User should set cwd to the experiment directory 
os.chdir('\\\\storage.its.york.ac.uk\\pshome\\blam500\\Downloads\\movie_study_0.8')
# User should set directory for output files to be stored 
save_path= '\\\\storage.its.york.ac.uk\\pshome\\blam500\\Downloads\\movie_study_0.8\\output\\questionnaire'

# Create a dictionary to store information from the dialogue box
inputbox = {'expdate': datetime.now().strftime('%Y%m%d_%H%M'),'part_number':'','videoCondition':''}

# Create dialogue box
# user enters participant number + video condition (i.e. the Header of the column of video lists in the film csvFile)
dlg=gui.DlgFromDict(inputbox, title = 'Input participation info',
                  	fixed='expdate',
                  	order=['expdate', 'part_number','videoCondition'])

# If the user doesn't press ok, the programme will quit and inform user.
if not dlg.OK:
	print ("User exited")
	core.quit()

# Function to present thought probes, store responses and present break screen in between videos 
# If it is the last trial, presents end screen instead of break screen 
def thought_probes (video_name, participant_number, last=0):

    # Use trialhandler to present thought probes from csv file in random order 
    Questionnaire = data.TrialHandler(nReps = 1, method = 'random', trialList = data.importConditions('references\\questions.csv'), name = 'Questionnaire')
     
    # Create rating scale for user to rate thought probes
    ratingScale = visual.RatingScale(win, low=1, high=10, markerStart=4.5,
                precision=10,
                leftKeys='1', rightKeys='2', acceptKeys='4', scale = None, labels = None1)
    
    
    # Create text stimulus for thought probe presentation 
    QuestionText = visual.TextStim(win, color = [-1,-1,-1], alignHoriz = 'center', alignVert= 'top', pos =(0.0, 0.3))
    # Create text stimuli for low and high scale responses 
    Scale_low = visual.TextStim(win, pos= (-0.5,-0.5), color ='black')
    Scale_high = visual.TextStim(win, pos =(0.6, -0.5), color ='black')

    # Make thisRunDict global so that it can be accessed outside of function to write to outputfile 
    global thisRunDict
    # Store participant number and video name in thisRunDict to write to outputfile  
    thisRunDict= {'Participant_number': str(participant_number),'videoName': video_name }
    
    # Loop through each thought probe in the Questionnaire created above using trialhandler
    for question in Questionnaire:
        ratingScale.noResponse = True
        
        # added section for keyboard handling 
        key = pyglet.window.key
        keyState = key.KeyStateHandler()
        win.winHandle.push_handlers(keyState)
        
        pos = ratingScale.markerStart
        inc = 0.1 

        # While there is no response from user, present probe and scal
        while ratingScale.noResponse:
            close()
            if keyState[key._1] is True:
                pos -= inc
            elif keyState[key._2] is True:
                pos += inc
            if pos > 9:
                pos = 9
            elif pos < 0:
                pos = 0
            ratingScale.setMarkerPos(pos)
            
            # Set text of probe and responses 
            QuestionText.setText(question['Questions'])
            Scale_low.setText(question['Scale_low'])
            Scale_high.setText(question['Scale_high'])
            # draw text stimuli and rating scale
            QuestionText.draw()
            ratingScale.draw() 
            Scale_low.draw()
            Scale_high.draw()
            # Store response using getRating function
            responded = ratingScale.getRating()
            win.flip()

        # reset marker to middle of scale 
        ratingScale.setMarkerPos((0.5))
        # For each probe, store probe label and response in thisRunDict 
        thisRunDict[ str(question['Label'] )] = str(responded)

    # Create text stimuli to be updated for breaks and end screen 
    stim = visual.TextStim(win, "", color = [-1,-1,-1], wrapWidth = 800) 

    # Present break screen at the end of each set of questions
    if last==0:
        stim.setText("""You are welcome to take a break if you need to.
        \nPress enter when you are ready to continue.""")
        stim.draw()
        win.flip()
        
        # Wait for user to press Return to continue 
        key = event.waitKeys(keyList=(['return']), timeStamped = True)
        close ()

    else:
        # Presents end screen at the end of task 
        stim.setText("""You have reached the end of the experiment. 
        \nPlease let the experimenter know you have finished. 
        \nThank you for your participation.""")
        stim.draw()
        win.flip()
        # Waits for user to press escape to exit experiment 
        key = event.waitKeys(keyList=(['return']), timeStamped = True)
        close ()

# Store participant number, video condition and experiment date provided by user as variables for later use 
part_number = inputbox['part_number']
videoCondition = inputbox['videoCondition']
expdate = inputbox['expdate']

# create filename based on user input 
filename = '{}_{}_{}.csv'.format(inputbox['part_number'], inputbox['expdate'],inputbox['videoCondition'])
# update filename to include absolute path so that it is stored in output directory 
completeName = os.path.join(save_path, filename)
# open file for writing 
outputfile = open(completeName, "w", newline = '')

# Create list of headers for output csv file 
fieldnames = ['Participant_number', 'videoName','Video_startTime','Video_endTime','Questionnaire_startTime','Questionnaire_endTime',
'TrialDuration','Focus','Future','Past','Self','Other','Emotion','Modality','Detailed','Deliberate','Problem','Diversity','Intrusive','Source']

# create variable which calls DictWriter to write to outputfile and specifies fieldnames 
writer = csv.DictWriter(outputfile, fieldnames)
# writes headers using fieldnames as specified above when creating writer variable
writer.writeheader()

# Use trialhandler to sequentially present films listed in filmlist csv file 
filmDict = data.TrialHandler(nReps = 1, method = 'sequential', trialList = data.importConditions('references\\filmList.csv'), name = 'filmList') 

# Create white window for stimuli to be presented on throughout task 
#win = visual.Window(size =[1024, 768], fullscr = True, allowGUI = False, units = "pix", color = (1,1,1), monitor="testMonitor")
win = visual.Window(size=[1024, 768], color=[1,1,1,], monitor="testMonitor", fullscr= True, allowGUI = False)

# Create text stimuli to be updated for start screen instructions
stim = visual.TextStim(win, "", color = [-1,-1,-1], wrapWidth = 1300, units = "pix", height=40)

# Update text to include instructions for task 
stim.setText("""You will be presented with several videos.
\nThese videos will contain dangerous behaviour, strong language, disturbing images,threat and violence.If you find these types of videos distressing ,please do not participate and let the experimenter know.
\nIf at any point, you become distressed and would like to stop the task, please inform the experimenter.
You will not be penalised for withdrawing from the study. 
\nAt the end of each video, you will be asked to rate several statements regarding the nature of your ongoing thoughts on a sliding scale.
Use the mouse to move the marker and press enter when you are happy with your selection. 
\nPress Enter to begin.""")
# draw text stimuli 
stim.draw()
# flip window to show text 
win.flip()
close()

# Wait for user to press Return to continue 
key = event.waitKeys(keyList=(['return']), timeStamped = True)
close()

# Start a clock right before the experiment starts
tasktime = core.Clock()
tasktime.reset()

# Loop through each film stored in filmDict created above using trialhandler 
for film in filmDict:
    # store trial start time for later use in calculating trial duration 
    start =time.time()
    # store when the video started to later store in outputfile, this videoStart uses clock created at start of experiment 
    videoStart = tasktime.getTime()
    # present movie using moviestim2
    mov = visual.MovieStim3 (win, film[videoCondition], size=(1920, 1080), flipVert=False, flipHoriz=False, loop=False)

    while mov.status != visual.FINISHED:
        mov.draw()
        win.flip()
        close()

    # store when the video ends to later store in outputfile, this videoEnd uses clock created at start of experiment 
    videoEnd = tasktime.getTime()
    # If statement to either present break screen or end screen
    nextTrial = filmDict.getFutureTrial(n=1) #fixes error for end screen 
    if nextTrial is None or nextTrial[videoCondition] != None:
        # when the video has ended, call thought_probes function to present probes and rating scale
        thought_probes(film[videoCondition], part_number) 
    else:
        thought_probes(film[videoCondition], part_number,1)

    # store when the questions end to later store in outputfile, this qEnd uses clock created at start of experiment 
    qEnd = tasktime.getTime()
    # store trial end time for later use in calculating trial duration 
    end =time.time()
    # calculate trial duration to store in outputfile 
    trial_duration = (end-start)

    # add timings to global thisRunDict to write to outputfile below
    thisRunDict['Video_startTime']= str(videoStart)
    thisRunDict['Video_endTime']= str(videoEnd)
    thisRunDict['Questionnaire_startTime']= str(videoEnd)
    thisRunDict['Questionnaire_endTime']= str(qEnd)
    thisRunDict['TrialDuration'] = str(trial_duration)
    
    # write responses and timings stored in thisRunDict to outputfile 
    writer.writerows([thisRunDict])
    outputfile.flush()

Hi @BronteMckeown, you could try adding win.winHandle.activate() after your dialog has been closed and your window has been created. Alternatively, try the new keyboard component using the PsychToolBox backend, which should also fix this issue because the new keyboard is globally accessible once created, rather than to the specific window, as with the event module.