Hello, I am trying to add a joystick component that continues to move until the button is no longer pressed. I have been using the builder, but I clearly need a custom code to make this work. Currently when I plug in my USB joystick and press a direction, it moves to that direction to the far side of the screen, and then when you let go, it re-centers. I have changed the joystick.xFactor and joystick.yFactor so it only moves a small amount instead of to the edge of the screen. I have really been struggling for several weeks now to figure out how to get continuous movement out of the joystick. Below is the code I have for just my test program to see if I can get the joystick to work. I have used pygame in the past without using psychopy and have been successful, but have had a ton of issues with pygame in the psychopy builder so I have been sticking to the default pyglet option, which I am less familiar with. Any advice would be greatly appreciated! This would get my dissertation up and running!
#!/usr/bin/env python
-- coding: utf-8 --
ââ"
This experiment was created using PsychoPy3 Experiment Builder (v2022.1.3),
on Mon May 2 19:03:28 2022
If you publish work using this script the most relevant publication is:
Peirce J, Gray JR, Simpson S, MacAskill M, Höchenberger R, Sogo H, Kastman E, LindelÞv JK. (2019)
PsychoPy2: Experiments in behavior made easy Behav Res 51: 195.
https://doi.org/10.3758/s13428-018-01193-y
ââ"
from psychopy import locale_setup
from psychopy import prefs
from psychopy import sound, gui, visual, core, data, event, logging, clock, colors, layout
from psychopy.constants import (NOT_STARTED, STARTED, PLAYING, PAUSED,
STOPPED, FINISHED, PRESSED, RELEASED, FOREVER)
import numpy as np # whole numpy lib is available, prepend ânp.â
from numpy import (sin, cos, tan, log, log10, pi, average,
sqrt, std, deg2rad, rad2deg, linspace, asarray)
from numpy.random import random, randint, normal, shuffle, choice as randchoice
import os # handy system and path functions
import sys # to get file system encoding
from psychopy.hardware import keyboard
Ensure that relative paths start from the same directory as this script
_thisDir = os.path.dirname(os.path.abspath(file))
os.chdir(_thisDir)
Store info about the experiment session
psychopyVersion = â2022.1.3â
expName = âtriangleâ # from the Builder filename that created this script
expInfo = {âparticipantâ: ââ}
dlg = gui.DlgFromDict(dictionary=expInfo, sortKeys=False, title=expName)
if dlg.OK == False:
core.quit() # user pressed cancel
expInfo[âdateâ] = data.getDateStr() # add a simple timestamp
expInfo[âexpNameâ] = expName
expInfo[âpsychopyVersionâ] = psychopyVersion
Data file name stem = absolute path + name; later add .psyexp, .csv, .log, etc
filename = thisDir + os.sep + uâdata/%s%s_%sâ % (expInfo[âparticipantâ], expName, expInfo[âdateâ])
An ExperimentHandler isnât essential but helps with data saving
thisExp = data.ExperimentHandler(name=expName, version=ââ,
extraInfo=expInfo, runtimeInfo=None,
originPath=â/Users/brookejac55/Desktop/PsychoPy Programs/old/Psychopy_Joystick/triangle.pyâ,
savePickle=True, saveWideText=True,
dataFileName=filename)
save a log file for detail verbose info
logFile = logging.LogFile(filename+â.logâ, level=logging.EXP)
logging.console.setLevel(logging.WARNING) # this outputs to the screen, not a file
endExpNow = False # flag for âescapeâ or other condition => quit the exp
frameTolerance = 0.001 # how close to onset before âsameâ frame
Start Code - component code to be run after the window creation
from psychopy.hardware import joystick as joysticklib # joystick/gamepad accsss
from psychopy.experiment.components.joystick import virtualJoystick as virtualjoysticklib
Setup the Window
win = visual.Window(
size=[1440, 900], fullscr=True, screen=0,
winType=âpygletâ, allowGUI=False, allowStencil=False,
monitor=âtestMonitorâ, color=âwhiteâ, colorSpace=ârgbâ,
blendMode=âavgâ, useFBO=True,
units=âheightâ)
store frame rate of monitor if we can measure it
expInfo[âframeRateâ] = win.getActualFrameRate()
if expInfo[âframeRateâ] != None:
frameDur = 1.0 / round(expInfo[âframeRateâ])
else:
frameDur = 1.0 / 60.0 # could not measure, so guess
Setup ioHub
ioConfig = {}
ioSession = ioServer = eyetracker = None
create a default keyboard (e.g. to check for escape)
defaultKeyboard = keyboard.Keyboard(backend=âeventâ)
Initialize components for Routine âtrialâ
trialClock = core.Clock()
polygon = visual.ShapeStim(
win=win, name=âpolygonâ,
size=(0.2, 0.2), vertices=âtriangleâ,
ori=0.0, pos=(0.0, 0.3), anchor=âcenterâ,
lineWidth=1.0, colorSpace=ârgbâ, lineColor=âwhiteâ, fillColor=âblueâ,
opacity=None, depth=0.0, interpolate=True)
polygon_2 = visual.ShapeStim(
win=win, name=âpolygon_2â,
size=(0.04, 0.04), vertices=âcircleâ,
ori=1.0, pos=[0,0], anchor=âcenterâ,
lineWidth=1.0, colorSpace=ârgbâ, lineColor=âwhiteâ, fillColor=âredâ,
opacity=None, depth=-1.0, interpolate=True)
x, y = [None, None]
joystick = type(ââ, (), {})() # Create an object to use as a name space
joystick.device = None
joystick.device_number = 0
joystick.joystickClock = core.Clock()
joystick.xFactor = 1
joystick.yFactor = 1
try:
numJoysticks = joysticklib.getNumJoysticks()
if numJoysticks > 0:
try:
joystickCache
except NameError:
joystickCache={}
if not 0 in joystickCache:
joystickCache[0] = joysticklib.Joystick(0)
joystick.device = joystickCache[0]
if win.units == âheightâ:
joystick.xFactor = 0.5 * win.size[0]/win.size[1]
joystick.yFactor = 0.5
else:
joystick.device = virtualjoysticklib.VirtualJoystick(0)
logging.warning(âjoystick_{}: Using keyboard+mouse emulation âctrlâ + âAltâ + digit.â.format(joystick.device_number))
except Exception:
pass
if not joystick.device:
logging.error(âNo joystick/gamepad device found.â)
core.quit()
joystick.status = None
joystick.clock = core.Clock()
joystick.numButtons = joystick.device.getNumButtons()
joystick.getNumButtons = joystick.device.getNumButtons
joystick.getAllButtons = joystick.device.getAllButtons
joystick.getX = lambda: joystick.xFactor * joystick.device.getX()
joystick.getY = lambda: joystick.yFactor * joystick.device.getY()
joystick.xFactor = .1 * joystick.getX()
joystick.yFactor = .1 * joystick.getY()
Create some handy timers
globalClock = core.Clock() # to track the time since experiment started
routineTimer = core.CountdownTimer() # to track time remaining of each (non-slip) routine
------Prepare to start Routine âtrialâ-------
continueRoutine = True
update component parameters for each repeat
joystick.oldButtonState = joystick.device.getAllButtons()[:]
joystick.activeButtons=[i for i in range(joystick.numButtons)]
setup some python lists for storing info about the joystick
joystick.clicked_name =
gotValidClick = False # until a click is received
while joystick.status == STARTED:
joystick.getX() * joystick.xFactor
joystick.getY() * joystick.YFactor
keep track of which components have finished
trialComponents = [polygon, polygon_2, joystick]
for thisComponent in trialComponents:
thisComponent.tStart = None
thisComponent.tStop = None
thisComponent.tStartRefresh = None
thisComponent.tStopRefresh = None
if hasattr(thisComponent, âstatusâ):
thisComponent.status = NOT_STARTED
reset timers
t = 0
_timeToFirstFrame = win.getFutureFlipTime(clock=ânowâ)
trialClock.reset(-_timeToFirstFrame) # t0 is time of first possible flip
frameN = -1
-------Run Routine âtrialâ-------
while continueRoutine:
# get current time
t = trialClock.getTime()
tThisFlip = win.getFutureFlipTime(clock=trialClock)
tThisFlipGlobal = win.getFutureFlipTime(clock=None)
frameN = frameN + 1 # number of completed frames (so 0 is the first frame)
# update/draw components on each frame
# *polygon* updates
if polygon.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
# keep track of start time/frame for later
polygon.frameNStart = frameN # exact frame index
polygon.tStart = t # local t and not account for scr refresh
polygon.tStartRefresh = tThisFlipGlobal # on global time
win.timeOnFlip(polygon, 'tStartRefresh') # time at next scr refresh
polygon.setAutoDraw(True)
# *polygon_2* updates
if polygon_2.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
# keep track of start time/frame for later
polygon_2.frameNStart = frameN # exact frame index
polygon_2.tStart = t # local t and not account for scr refresh
polygon_2.tStartRefresh = tThisFlipGlobal # on global time
win.timeOnFlip(polygon_2, 'tStartRefresh') # time at next scr refresh
polygon_2.setAutoDraw(True)
if polygon_2.status == STARTED: # only update if drawing
polygon_2.setPos((joystick.getX(), joystick.getY()*-1), log=False)
polygon_2.setOri(0.0, log=False)
# *joystick* updates
if joystick.status == NOT_STARTED and tThisFlip >= 0.0-frameTolerance:
# keep track of start time/frame for later
joystick.frameNStart = frameN # exact frame index
joystick.tStart = t # local t and not account for scr refresh
joystick.tStartRefresh = tThisFlipGlobal # on global time
win.timeOnFlip(joystick, 'tStartRefresh') # time at next scr refresh
joystick.status = STARTED
joystick.joystickClock.reset()
if joystick.status == STARTED: # only update if started and not finished!
joystick.newButtonState = joystick.getAllButtons()[:]
if joystick.newButtonState != joystick.oldButtonState: # New button press
joystick.pressedButtons = [i for i in range(joystick.numButtons) if joystick.newButtonState[i] and not joystick.oldButtonState[i]]
joystick.releasedButtons = [i for i in range(joystick.numButtons) if not joystick.newButtonState[i] and joystick.oldButtonState[i]]
joystick.newPressedButtons = [i for i in joystick.activeButtons if i in joystick.pressedButtons]
joystick.oldButtonState = joystick.newButtonState
joystick.buttons = joystick.newPressedButtons
[logging.data("joystick_{}_button: {}, pos=({:1.4f},{:1.4f})".format(joystick.device_number, i, joystick.getX(), joystick.getY())) for i in joystick.pressedButtons]
if len(joystick.buttons) > 0: # state changed to a new click
# check if the joystick was inside our 'clickable' objects
gotValidClick = False;
for obj in [polygon]:
if obj.contains(joystick.getX(), joystick.getY()):
gotValidClick = True
joystick.clicked_name.append(obj.name)
# abort routine on response
continueRoutine = False
# check for quit (typically the Esc key)
if endExpNow or defaultKeyboard.getKeys(keyList=["escape"]):
core.quit()
# check if all components have finished
if not continueRoutine: # a component has requested a forced-end of Routine
break
continueRoutine = False # will revert to True if at least one component still running
for thisComponent in trialComponents:
if hasattr(thisComponent, "status") and thisComponent.status != FINISHED:
continueRoutine = True
break # at least one component has not yet finished
# refresh the screen
if continueRoutine: # don't flip if this routine is over or we'll get a blank screen
win.flip()
-------Ending Routine âtrialâ-------
for thisComponent in trialComponents:
if hasattr(thisComponent, âsetAutoDrawâ):
thisComponent.setAutoDraw(False)
thisExp.addData(âpolygon.startedâ, polygon.tStartRefresh)
thisExp.addData(âpolygon.stoppedâ, polygon.tStopRefresh)
thisExp.addData(âpolygon_2.startedâ, polygon_2.tStartRefresh)
thisExp.addData(âpolygon_2.stoppedâ, polygon_2.tStopRefresh)
store data for thisExp (ExperimentHandler)
store data for thisExp (ExperimentHandler)
if len(joystick.buttons) > 0:
# check if the joystick was inside our âclickableâ objects
gotValidClick = False;
for obj in [polygon]:
if obj.contains(joystick.getX(), joystick.getY()):
gotValidClick = True
joystick.clicked_name.append(obj.name)
x, y = joystick.getX(), joystick.getY()
joystick.newButtonState = joystick.getAllButtons()[:]
joystick.pressedState = [joystick.newButtonState[i] for i in range(joystick.numButtons)]
joystick.time = joystick.joystickClock.getTime()
thisExp.addData(âjoystick.xâ, x)
thisExp.addData(âjoystick.yâ, y)
[thisExp.addData(âjoystick.button_{0}â.format(i), int(joystick.pressedState[i])) for i in joystick.activeButtons]
thisExp.addData(âjoystick.timeâ, joystick.time)
if len(joystick.clicked_name):
thisExp.addData(âjoystick.clicked_nameâ, joystick.clicked_name[0])
thisExp.addData(âjoystick.startedâ, joystick.tStartRefresh)
thisExp.addData(âjoystick.stoppedâ, joystick.tStopRefresh)
thisExp.nextEntry()
the Routine âtrialâ was not non-slip safe, so reset the non-slip timer
routineTimer.reset()
Flip one final time so any remaining win.callOnFlip()
and win.timeOnFlip() tasks get executed before quitting
win.flip()
these shouldnât be strictly necessary (should auto-save)
thisExp.saveAsWideText(filename+â.csvâ, delim=âautoâ)
thisExp.saveAsPickle(filename)
logging.flush()
make sure everything is closed down
if eyetracker:
eyetracker.setConnectionState(False)
thisExp.abort() # or data files will save again on exit
win.close()
core.quit()