Error in printing to file

I have some additional code in my experiment that allows it to write to an excel file. This works fine on some of my testing laptops, but not others and I can’t work out why. They have the same versions of psychopy (1.9) and excel (2016). The bit of code it has a problem with is this:

ws.cell(“C”+str(q)).value= str(a)

Where q is a variable that increases by one at each trial (such that it prints outputs on moving down the worksheet)

As I say, on most of my computers, this works fine. However on two of them, it appears not to recognise “c” - it outputs the following error:

########### Running: C:\Python26\Kids\WWW_KID1_scaffolded_lastrun.py ###########
pyo version 0.9.0 (uses single precision)
C:\Python26\Kids\WWW_KID1_scaffolded_lastrun.py:127: DeprecationWarning: Call to deprecated function get_active_sheet (Use the .active property).
ws = wb.get_active_sheet()
C:\Program Files (x86)\PsychoPy2_PY3\lib\site-packages\pyglet\font\text.py:320: DeprecationWarning: Use pyglet.text.Label instead
warnings.warn(‘Use pyglet.text.Label instead’, DeprecationWarning)
C:\Program Files (x86)\PsychoPy2_PY3\lib\site-packages\pyglet\text\layout.py:1055: DeprecationWarning: generator ‘ZipRunIterator.ranges’ raised StopIteration
for start, end, (font, element) in runs.ranges(0, len(text)):
C:\Program Files (x86)\PsychoPy2_PY3\lib\site-packages\pyglet\text\layout.py:388: DeprecationWarning: generator ‘ZipRunIterator.ranges’ raised StopIteration
for start, end, decoration in context.decoration_iter.ranges(i, i + n_glyphs):
Traceback (most recent call last):
File “C:\Python26\Kids\WWW_KID1_scaffolded_lastrun.py”, line 695, in <module>
ws.cell(“C”+str(q)).value= str(a)
TypeError: cell() missing 1 required positional argument: ‘column’

Is there any reason why there would be a difference between computers in how psychopy interprets this kind of code? Or something else I’m missing? I’ve tried removing the speech marks around C, but it then said it didn’t recognise “C”

Thanks!

It’s not the 'C' that is the issue here, but the q. It seems that you are iterating over something and I guess q is the index for that operation, but somehow the iteration fails.

You probably need to show us the code that the ws.cell(“C”+str(q)).value= str(a) is embedded in.

But perhaps you could explain why you need to use such a laborious scheme to create your Excel file. i.e. is there a reason why you can’t export a dataset in one go, without the need to refer to individual cell coordinates? This seems remarkably sophisticated, and would usually only be something people would do if they needed to fit within some sort of template file, rather than simply export tabular data.

Thanks Michael!

To answer your second question first - its part of a spatial memory task which involves using the coordinates that the participants put things during encoding as the locations where (some) of the stimuli appear during recognition. Thus the code updates the coordinates in the excel as it goes so that the later part of the task can use those coordinates.

The full code for this routine is as follows:

Begin Experiment:
trialClock = core.Clock()
nameoffile=str(expInfo['participant'])+".xls"
file=open(nameoffile,"w")
from random import randint
from openpyxl.reader.excel import load_workbook
wb=load_workbook(r'kidwhere.xlsx')
ws = wb.get_active_sheet()
q=2

Begin Routine:
file.write('object1')
file.write('\t')
file.write('object2')
file.write('\t')
file.write('object3')
file.write('\t')
file.write('object4')
file.write('\n')
a=[0,0]
def moveup(c):
    if a[1]<0.85:
       a[1]=a[1]+0.12
       c.setPos(a)
def movedown(c):
    if a[1]>-0.85:
       a[1]=a[1]-0.12
       c.setPos(a)
def moveleft(c):
    if a[0]>-0.85:
       a[0]=a[0]-0.12
       c.setPos(a)
def moveright(c):
    if a[0]<0.85:
       a[0]=a[0]+0.12
       c.setPos(a)
object1=visual.ImageStim(win=win, name='object1',
    image='sin', mask=None,
    ori=0, pos=a, size=[0.2, 0.2],
    color=[1,1,1], colorSpace=u'rgb', opacity=1,
    texRes=128, interpolate=False, depth=-1.0)
object2=visual.ImageStim(win=win, name='object2',
    image='sin', mask=None,
    ori=0, pos=a, size=[0.2, 0.2],
    color=[1,1,1], colorSpace=u'rgb', opacity=1,
    texRes=128, interpolate=False, depth=-2.0)
object3=visual.ImageStim(win=win, name='object3',
    image='sin', mask=None,
    ori=0, pos=a, size=[0.2, 0.2],
    color=[1,1,1], colorSpace=u'rgb', opacity=1,
    texRes=128, interpolate=False, depth=-1.0)
object4=visual.ImageStim(win=win, name='object4',
    image='sin', mask=None,
    ori=0, pos=a, size=[0.2, 0.2],
    color=[1,1,1], colorSpace=u'rgb', opacity=1,
    texRes=128, interpolate=False, depth=-2.0)
background=visual.ImageStim(win=win, name='background',
    image='sin', mask=None,
    ori=0, pos=a, size=[2, 2],
    color=[1,1,1], colorSpace=u'rgb', opacity=1,
    texRes=128, interpolate=False, depth=-1.0)
HidingControl= event.BuilderKeyResponse() 
HidingControl.status=NOT_STARTED
trialComponents=[]
trialComponents.append(HidingControl)

Each Frame:
background.setImage(landscape)
object1.setImage(item1)
object2.setImage(item2)
background.setAutoDraw(True)
object1.setAutoDraw(True)
win.flip

if t>=0.0 and HidingControl.status==NOT_STARTED:
            #keep track of start time/frame for later
            HidingControl.tStart=t#underestimates by a little under one frame
            HidingControl.frameNStart=frameN#exact frame index
            HidingControl.status=STARTED
x=0
while x==0:
    object1.setPos(a)
    object1.setAutoDraw(True)
    win.flip
    for keys in event.getKeys(timeStamped=True):
        for key in keys:
            if key=='up':
                moveup(object1)
            elif key=='down':
                movedown(object1)
            elif key=='left':
                moveleft(object1)
            elif key=='right':
                moveright(object1)
            elif key=='return':
                coordinates=str(a)
                file.write(str(a))
                file.write('\t')
                ws.cell("C"+str(q)).value= str(a)
                ws.cell("D"+str(q)).value= str(a)
		obj3loc=str(a)
                q=q+1
                object1.setAutoDraw(False)
                a[0]=randint(-6,6)*0.12
                a[1]=randint(-6,6)*0.12
                object2.setPos(a)
                object2.setAutoDraw(True)
                object2.draw() 
                x=x+1
                win.flip
            elif key in ['escape','q']:
                core.quit()
        object1.draw()
        win.flip()
        
  
while x==1:
    for keys in event.getKeys(timeStamped=True):
        for key in keys:
            if key=='up':
                moveup(object2)
            elif key=='down':
                movedown(object2)
            elif key=='left':
                moveleft(object2)
            elif key=='right':
                moveright(object2)
            elif key=='return':
                coordinates=str(a)
                file.write(str(a))
                file.write('\n')
                ws.cell("C"+str(q)).value= str(a)
                ws.cell("D"+str(q)).value= str(a)
		obj4loc=str(a)
                q=q+1
                object2.setAutoDraw(False)
                win.flip()
                x=x+1
            elif key in ['escape','q']:
                core.quit()
        object2.draw()
        win.flip()
if x==2:
    continueRoutine=False

End Routine:
background.setAutoDraw(False)

OK, what is likely causing your error is using the openpyxl API incorrectly. As documented here: https://openpyxl.readthedocs.io/en/stable/tutorial.html#accessing-one-cell

You can refer to a cell directly like this:

ws['A1'] = value

or like this:

ws.cell(row=1, column=1, value=value)

You seem to be mixing the two notations.

But there are other issues with your code which mean that even if it runs without an explicit error, the performance is likely to be very poor.

Firstly, you can’t use win.flip() within Builder code components. Builder-generated code is inherently structured around a drawing event loop, with one win.flip() that Builder itself inserts. If you insert other ones, you will really be mucking up all of the timing. i.e. several screen refreshes will have occurred when Builder is just counting one, so all of the experiment timeline will be dramatically slowed down.

So take all of those out. You need to be thinking about what happens within each tab of a code component. For example, at the moment you are updating the image properties of several stimuli on every screen refresh, but don’t actually seem to be updating the variables that specify those images at the same rate (i.e. guess they get updated once per routine?) This will also be dramatically slowing down your experiment: you are needlessly updating something that doesn’t change, but that update takes non-trivial time (i.e. the image file needs to be read from disk, decompressed, etc) which with multiple images will easily exceed the time available within one screen refresh. This will lead to dropped frames, again slowing your experiment. And one should avoid file operations (like using an external library to update Excel cell contents) within that limited time period too (perhaps save all of this until the end of the routine or even the whole loop).

So you should pay a lot of attention to performance testing here. Try logging the time of each screen refresh to check that they are actually happening at the expected screen refresh rate.

On top of that, the code does seem more than a little over-engineered. e.g. is there any reason to be writing to specific cells in a spreadsheet, when you could be saving a simple tabular data structure to a .csv file for the same purpose?

And you could capitalise more on what Builder can provide for you via the GUI. e.g. you could create your stimuli via stimulus components rather than via code, and have them automatically update their image property on the appropriate schedule. Then all you have to use your code for is to move the stimuli in response to keypresses, and control their visibility as required.

Thankyou so much Michael!

Writing it as ws[“c” + str(q)]=str(a) worked :slight_smile:

Yes I’m aware the code is bodged to high heaven - I wrote it in 2013 (with no background in coding) and have only tweaked it since (as I keep applying for money to completely re-do all my tasks as online things, but keep not getting it!) , the thing that had me so confused on this one was why half my computers are fine with this code (and always have been) and the other computers, which do not differ in any way I can work out, couldn’t cope with it!

Thanks again

Lucy