psychopy ratingScale with numeric keypad - python

In my task, I've been allowing responses using the numeric keypad (e.g 'num_1') as well as the regular numbers at the top of the keyboard (e.g. '1'). When I later ask for ratings using ratingScale, I'd like both options to be available as well, but I don't know how to achieve this.
As is, ratingScale does not accept responses with the numeric keypad. I can change this with respKeys, but I would have to provide "A list of keys to use for selecting choices, in the desired order". This means I can't allow both '1' and 'num_1' to select the first rating (e.g. with respKeys = ['1','num_1, '2', 'num_2', ...] '1' would select the first rating, 'num_1' the second, etc.).
Am I really stuck with either respKeys = ['1','2','3','4','5'] or respKeys = ['num_1','num_2','num_3','num_4','num_5']?
Thanks for any help!

I don't think that there's any built-in ways for visual.RatingScale to take multiple keyboard keys for the same scale location.
If you're using coder, a hack could be to use event.getKeys() and visual.RatingScale.setMarkerPos(). So for a simple case with a rating scale with three positions:
# Initiate psychopy stuff
from psychopy import visual, event
win = visual.Window()
scale = visual.RatingScale(win, low=1, high=3)
# A list of lists, each sublist being the keys which represents this location
keysLocation = [['1', 'num_1'], ['2', 'num_2'], ['3', 'num_3']]
respKeys = [key for keysLoc in keysLocation for key in keysLoc] # looks complicated but it simply flattens the list to be non-nested
# Present the ratingscale and wait for a response
currentPos = 1
while scale.noResponse:
response = event.getKeys(keyList=respKeys) # only accept respKeys
if response and response[0] in respKeys:
# Then loop through sublists and update currentPos to where the match is
for i, loc in enumerate(keysLocation):
if response[0] in loc:
currentPos = i
# Set the marker position and draw
scale.setMarkerPos(currentPos)
scale.draw()
win.flip()
My solution looks complicated but most of it is just about handling the keysLocationlist and searching it for matches. Sorry for the bad/ambiguous variable names but I couldn't come up with anything better right now.
This solution could probably also work in Builder. Just remove the "initiate psychopy" stuff, change scale to the name of your RatingScale, and paste the code into a code component just above the RatingScale.

Related

Extracting multiple data from a single list

I working on a text file that contains multiple information. I converted it into a list in python and right now I'm trying to separate the different data into different lists. The data is presented as following:
CODE/ DESCRIPTION/ Unity/ Value1/ Value2/ Value3/ Value4 and then repeat, an example would be:
P03133 Auxiliar helper un 203.02 417.54 437.22 675.80
My approach to it until now has been:
Creating lists to storage each information:
codes = []
description = []
unity = []
cost = []
Through loops finding a code, based on the code's structure, and using the code's index as base to find the remaining values.
Finding a code's easy, it's a distinct type of information amongst the other data.
For the remaining values I made a loop to find the next value that is numeric after a code. That way I can delimitate the rest of the indexes:
The unity would be the code's index + index until isnumeric - 1, hence it's the first information prior to the first numeric value in each line.
The cost would be the code's index + index until isnumeric + 2, the third value is the only one I need to store.
The description is a little harder, the number of elements that compose it varies across the list. So I used slicing starting at code's index + 1 and ending at index until isnumeric - 2.
for i, carc in enumerate(txtl):
if carc[0] == "P" and carc[1].isnumeric():
codes.append(carc)
j = 0
while not txtl[i+j].isnumeric():
j = j + 1
description.append(" ".join(txtl[i+1:i+j-2]))
unity.append(txtl[i+j-1])
cost.append(txtl[i+j])
I'm facing some problems with this approach, although there will always be more elements to the list after a code I'm getting the error:
while not txtl[i+j].isnumeric():
txtl[i+j] list index out of range.
Accepting any solution to debug my code or even new solutions to problem.
OBS: I'm also going to have to do this to a really similar data font, but the code would be just a sequence of 7 numbers, thus harder to find amongst the other data. Any solution that includes this facet is also appreciated!
A slight addition to your code should resolve this:
while i+j < len(txtl) and not txtl[i+j].isnumeric():
j += 1
The first condition fails when out of bounds, so the second one doesn't get checked.
Also, please use a list of dict items instead of 4 different lists, fe:
thelist = []
thelist.append({'codes': 69, 'description': 'random text', 'unity': 'whatever', 'cost': 'your life'})
In this way you always have the correct values together in the list, and you don't need to keep track of where you are with indexes or other black magic...
EDIT after comment interactions:
Ok, so in this case you split the line you are processing on the space character, and then process the words in the line.
from pprint import pprint # just for pretty printing
textl = 'P03133 Auxiliar helper un 203.02 417.54 437.22 675.80'
the_list = []
def handle_line(textl: str):
description = ''
unity = None
values = []
for word in textl.split()[1:]:
# it splits on space characters by default
# you can ignore the first item in the list, as this will always be the code
# str.isnumeric() doesn't work with floats, only integers. See https://stackoverflow.com/a/23639915/9267296
if not word.replace(',', '').replace('.', '').isnumeric():
if len(description) == 0:
description = word
else:
description = f'{description} {word}' # I like f-strings
elif not unity:
# if unity is still None, that means it has not been set yet
unity = word
else:
values.append(word)
return {'code': textl.split()[0], 'description': description, 'unity': unity, 'values': values}
the_list.append(handle_line(textl))
pprint(the_list)
str.isnumeric() doesn't work with floats, only integers. See https://stackoverflow.com/a/23639915/9267296

How to manipulate 2d lists

I need help with working in 2d lists for my computing GCSE NEA(non examined assessment) and what I need to do is show the first letter of each song title on one line(One single song) along with the artists name for example AC/DC 'Back in Black' would be 'B i B' AC/DC
I am generally stuck on how to manipulate the array to show what I need and I've used several websites such as snakify and a few others
this is my program so far:
stageone=[['another one bites the dust','Queen',
'smoke on the water','Deep Purple',
'Stairway to heaven','Led Zeppelin',
'Carry on wayward son','Kansas',
'Don't stop believin','Journey']]
mainmenu=input("Welcome to the game please select an option-s for start the game,ts for top scores,ys for your scores,n for new game")
if mainmenu=='s':
play=input("do you want to continue you last game-c or do you want to start
a new game-n?")
if play=='s':
difficulty=input("What difficulty do you want to play? you can choose
from; easy,medium,difficult and legend")
if difficulty=='easy':
print("The easy mode of this game contains five stages,each with five
questions")
sure=input("Are you sure you want to play this gamemode?")
if sure=='y':
print(stageone)
I need the song to be on its own,not the whole array. And each song needs the first letter of each word not the whole word. I cannot figure out how to code this part of my program and help would greatly be appreciated. The song artist name however needs to be whole not single,first letter like the song title
when you define stageone, make it a list of lists like this:
stageone = [[title, band], [title, band], [title, band]]
then, instead of print(stageone) in the last line do:
for entry in stageone:
shortTitle = ' '.join([word[0] for word in entry[0].split(' ')])
print(shortTitle, entry[1])
Update: To get one hint at a time you'll need some method to select an entry out of stage one (for a game, I imagine this might be a random index). Then you just remove the for loop I gave before and use your selected entry like so
i = #some code to select an entry in stageone
entry = stageone[i]
shortTitle = ' '.join([word[0] for word in entry[0].split(' ')])
print(shortTitle, entry[1])
Note that there are a bunch of ways to make this more compact, but the original question makes me think a more verbose answer is better than a minimal solution. For instance, #calestini commented a one-liner to replace the for loop in my first response,
res = [[' '.join([x[0] for x in i[0].split()]), i[1]] for i in stageone ]
That's a fine solution - res will be a list like [[song hint, band name], [song hint, band name]] and then you print hints however you want. I'm not changing my original answer because I prefer not to use two list comprehensions in one line (I have a hard time reading the code).

How to get the list of root parents created in a scene - Autodesk Maya / Python?

I am a bit new to python and I am trying to get a list containing all root parent existing in a scene of type joint.
for example, my scene outliner is something like that:
group1>>group2>>joint1>>joint2>>joint3
group3>>joint4>>joint5
joint16>>joint17>>joint18
I want a script that travels through the outliner and returns a list, in my example:
[joint1, joint4, joint16]
Any tips would be really appreciated. thank you so much.
Im not sure if it is any of use has Haggi Krey solution works fine but
You can use also the flag : -long from cmds.ls
# list all the joints from the scene
mjoints = cmds.ls(type='joint', l=True)
# list of the top joints from chain
output = []
# list to optimise the loop counter
exclusion = []
# lets iterate joints
for jnt in mjoints:
# convert all hierarchy into a list
pars = jnt.split('|')[1:]
# lets see if our hierarchy is in the exclusion list
# we put [1:] because maya root is represented by ''
if not set(pars) & set(exclusion):
# we parse the hierarchy until we reach the top joint
# then we add it to the output
# we add everything else to the exclusion list to avoid
for p in pars:
if cmds.nodeType(p) == 'joint':
output.append(p)
exclusion+=pars
break
print(output)
I just put this because there is not one way to go. I hope the construction of this code could help your python skills. It is exactly the same, just the way to find the parent nodes is different !
I've used DrWeeny's idea before where you traverse the hierarchy by the object's long name. The difference in this answer is that the script won't crash if there's objects with duplicate names in the scene. What I mean by that is let's say you have a situation where you have 2 hierachies:
group1>>joint1>>joint2>>group2>>joint3
and
group3>>joint1>>joint2>>group2>>joint3
Maya easily allows this, like when duplicating a top node, so we need to prevent the script from crashing in this case. When there's multiple objects with duplicate names Maya will crash if you try to access the object's short name (it doesn't know what one you're referring to!), so instead we must always use its long name:
import maya.cmds as cmds
jnts = cmds.ls(type="joint", l=True) # Collect all joints in the scene by their long names.
output = set() # Use a set to avoid adding the same joint.
for jnt in jnts:
pars = jnt.split("|") # Split long name so we can traverse its hierarchy.
root_jnt = None
while pars:
obj = "|".join(pars)
del pars[-1] # Remove last word to "traverse" up hierarchy on next loop.
# If this is a joint, mark it as the new root joint.
if obj and cmds.nodeType(obj) == "joint":
root_jnt = obj
# If a root joint was found, append it to our final list.
if root_jnt is not None:
output.add(root_jnt)
print(list(output))
Using this script on the hierarchies above would return
[u'|group1|joint1', u'|group3|joint1']
I'd suggest to list all joints and for every joint you can check if it's parent is not a joint. In your definition, these joints should be your root joints.
I use this method to get a joint hierarchy. I have given up on trying to figure out a sexier way to do this.
myItems = cmds.ls(selection = True, type='joint')
theParentJnt = cmds.listRelatives(myItems, parent = True)
jntRel = cmds.listRelatives(myItems, allDescendents = True)
allJnt = jntRel + myItems
#Green Cell
Your method worked once, and never worked again. Restarted maya 2020 more then 5 times and only displays to the top node joint, never again does it return the all the joints in one list.

Create random, unique variable names for objects

I'm playing around with Python and any programming language for the first time, so please bear with me. I started an online class two weeks ago, but try to develop a small game at the side to learn faster (and have fun). It's a text adventure, but is shall have random encounters and fights with enemies that have random equipment that the players can then loot.
This is my problem: If I create random objects/weapons for my random encounter, I need to make sure that the object has a unique name. The way the level is designed there could in theory be an infinite number of objects (it can be open ended, with encounters just popping up).
This is my approach so far
class Item_NPCs: #create objects for NPCs
def__init__(self, item_type, item_number):
# e.g. item_type 1 = weapons, item_type2 = potions, etc.
if item_type == 1 and item number == 1:
self.property1 = 5
self.property2 = 4
if item_type == 1 and item_number ==2:
# etc. :)
def prepare_encounter():
inventory_NPC = [] # empty list for stuff the NPC carries around
XXX = Class_Item(item_type, item_number) # I might randomize the arguments.
What is important is that I want "XXX" to be unique and random, so that no object exists more than once and can later be put into the player's inventory.
How to do that?
Joe
Why do you need it to be random ? You could simply use a list, and append every new object to the list, with its index being its unique identifier :
items = []
items.append( Class_Item(item_type, item_number) )
But if you really need random identifier, maybe you can use a dictionary :
items = dict()
items[random_id] = Class_Item(item_type, item_number)
This requires random_id to be hashable (but it should be if it is a number or a string).
I don't know why others haven't thought of this:
yourvariableName = randint(0,1000)
exec("var_%s = 'value'" % yourVariableName)
I just thought of it myself. I hope it helps you.
A downside is you can't just do this:
print(var_yourVariableName)
you can only do this:
exec("print(var_%s)" % yourVariableName)
But you can probably circumvent this one way or another. Leave a comment if you manage to figure it out.
One more downside — if used in certain ways, it could be very insecure (as we are using exec), so make sure to cover any holes!

Trialhandler and time measuring in psychopy

For a go-NoGo Task I want to organize pictures with the data.TrialHandler class from psychopy:
trials = data.TrialHandler(ImageList, nReps=1, method='random')
Now I want to code a loop in which psychopy is going into the dictionary, is presenting the first set of pictures (e.g. A_n) and afterwards is going to the second set until the sixth set. I tried the following:
import glob, os, random, sys, time
import numpy.random as rnd
from psychopy import visual, core, event, monitors, gui, logging, data
im_a = glob.glob('./a*') # upload pictures of the a-type, gives out a List of .jpg-files
im_n = glob.glob('./n*') # n-type
im_e = glob.glob('./e*') # e-type
# combining Lists of Pictures
A_n = im_a + im_n
N_a = im_n + im_a
A_e = im_a + im_e
E_a = im_e + im_a
E_n = im_e + im_n
N_e = im_n + im_e
# making a Dictionary of Pictures and Conditions
PicList = [A_n, N_a, A_e, E_a, E_n, N_e] # just the six Combinations
CondList = [im_a,im_n,im_a,im_e,im_e,im_n] # images that are in the GO-Condition
ImageList = []
for imagelist, condition in zip(PicList, CondList):
ImageList.append({'imagelist':imagelist,'condition':condition}) # to associate the picturelist with the GO Conditionlist
for the header I ask an extra question: Combining and associating multiple dictionaries
# Set Experiment
win = visual.Window(color='white',units='pix', fullscr=False)
fixCross=visual.TextStim(win,text='+',color='black',pos=(0.0,0.0), height=40)
corrFb = visual.TextStim(win,text='O',height=40,color='green',pos=[0,0])
incorrFb = visual.TextStim(win,text='X',height=40, color='red',pos=[0,0])
# Start Experiement
trials = data.TrialHandler(ImageList, nReps=1, method='random')
rt_clock = core.Clock()
bitmap = visual.ImageStim(win)
for liste in ImageList[0:5]: # to loop through all 6 conditions
keys = []
for i,Pictures in enumerate(liste): # to loop through all pictures in each condition
bitmap.setImage(Pictures) # attribute error occurs, not if I use Pictures[0][0], even though in this case every pictures is the same
bitmap.draw()
win.flip()
rt_clock.reset()
resp = False
while rt_clock.getTime() < 2.0: # timelimit is defined 2 s
if not resp:
resp = event.getKeys(keyList=['space'])
rt = rt_clock.getTime()
if bool(resp) is (Pictures in CondList): # at this point python should have access to the Dictionary in which the associated GO Pictures are saved
corrFb.draw()
accu=1 # doesn't work yet
else:
incorrFb.draw()
accu=0
win.flip()
core.wait(0.5)
trials.addData('rt_'+str(i), rt) # is working well when loop: if trial in trials: ...; in this case trialHAndler is not used, therefor trials.addData is not working
trials.addData('accu_'+str(i), accu)
trials.saveAsExcel(datanames)
core.quit()
There are a few problems in this code: first it only presents one pictuere for six times, but not six different pictures [1]
and secondly a totally different problem [2] ist the time measuring and the saving of the accuracy which the trialhandler is doing, but for each trial. So it adds up all the RT's for each trial. I want to get the RT's for each image. I tried a few things like an extra stimulus.trialhandler for the stimuli and an extraloop in the end which gives me the last RT but not each. --> is answered below!!!
for stimuli in stimulus: stimulus.addData('rt', rt)
I know these four questions are a lot for one question, but maybe somebody can give me some good ideas of how I can solve these... Thanks everybody!
The reason for your problem labelled [1] is that you set the image to PicList[0][0] which never changes. As Mike is suggesting above you need::
for i,thisPic in enumerate(PicList):
bitmap.setImage(thisPic) #not PicList[0][0]
But maybe you need to go back to basics so that you actually use the trial handler to handle your trials ;-)
Create a single list of dictionaries where one dictionary represents one trial, and then run through those in order (tell the TrialHandler to use the list 'sequential' rather than 'random'). So the loops that you're currently using should just be to create your list of condition dicts, not to run the trials. Then pass that one list to the trial handler::
trials = TrialHandler(trialTypes = myCondsListInOrder, nReps=1, method='sequential')
for thisTrial in trials:
pic = thisTrial['pic']
stim.setImage(pic)
...
trials.addData('rt', rt)
trials.addData('acc',acc)
Also, I would output your data not using the excel format, but the 'long wide' format::
trials.saveAsWideText('mydataFile.csv')
best wishes,
Jon
(A) This isn't relevant to your question but will improve performance.
The line:
bitmap = visual.ImageStim(win)
Shouldn't occur within the loop. i.e. you should initialise each stimulus only once, then within a loop you just update that the properties of that stimulus, e.g. with bitmap.setImage(…). So shift this initialisation line to the top, where you create the TextStims.
(B) [deleted: I hadn't paid attention to the first code block.]
(C)
bitmap.draw(pictures)
This line doesn't take any arguments. It should just be bitmap.draw(). And anyway, it isn't clear what 'pictures' refers to. Remember that Python is case sensitive. This isn't the same thing as 'Pictures' defined in the loop above. I'm guessing that you want to update what picture is being shown? In that case, then you need to be doing the bitmap.setImage(…) line within this loop, not above, where you will always be drawing a fixed picture as that is the only one that gets set on each trial.
(D) Re the RTs, you are saving this only once per trial (check the indentation). If you want to save one per image, you need to indent these lines again. Also, you only get one line per trial in the data output. If you want to record multiple RTs per trial, you will need to give them unique names, e.g. rt_1, rt_2, …, rt_6 so they each appear in a separate column. e.g. you could use an enumerator for this loop:
for i, picture in enumerate(Piclist)
# lots of code
# then save data:
trials.addData('rt_'+str(i), rt)

Categories