psychopy.org | Reference | Downloads | Github

Are functions in Psychopy Pythonic?

def function(random):
        stim.setText(random)
        stim.setColor('blue')
        stim.draw()
        win.flip()

function(introduction)
function(end)
etc.

If you have to present stimulus at beginning of experiment, end of experiment and throughout the experiment - would creating a function like this be considered pythonic/correct?

It works it my experiment, and saves about 10 lines or so of code in total, but is it frowned upon to do this?

Also, is it okay to set one TextStim for the entire experiment (for introductory paragraph, ending paragraph, trials, etc?).

I am not entirely certain on the ‘correct’ layout for pythonic code and/or psychopy.

‘Pythonic’ is a matter of judgment and interpretation. And you won’t find much judgmentalism here about ‘correct’ programming style. Do whatever works for you.
A lot of the PsychoPy coder demos and examples are perhaps not very ‘Pythonic’, in that they are simply a list of instructions in order: create this object, change this attribute, draw it to the screen, collect this response, etc. This is probably a deliberate approach to make sense to experimenters learning to program rather than to programmers learning to make experiments. i.e. the code simply describes the list of operations in the experiment. People coming from a programming background might want their code to look more beautiful, to minimise repetition by using functions and iterators, and encapsulate functions and state in custom objects, etc, and are free to do so.

Basically, adopt whatever style works for you. But the one thing that trumps everything in the PsychoPy world is performance. You want stimuli to appear on time, with the desired attributes, and for responses to be collected with fidelity. Those are the issues you’ll get best feedback from in this forum.

So in answer to that specific aspect of your question, yes it is (usually) perfectly fine to have just one TextStim for the entire experiment. In fact, it is usually desirable to minimise the creation of stimuli (which is time-expensive), and wherever possible just update the attributes of existing ones.Meanwhile, purely for clarity in the code, some people might want to create separate text stimuli named, say, instruction_text and feedback_text. It can make the experiment code more readable, and doesn’t consume any meaningful extra amount of memory. But you could certainly have a generic_text stimulus, and alter its contents as needed.

But note that altering text stimulus content is a slower process than updating most other stimuli. So when performance is critical (e.g. showing different text stimuli on alternating screen refreshes), then it would definitely be better to do this:

# set the content of two stimuli just once:
text_1.text = 'hello'
text_2.text = 'goodbye'

while True: # so can very quickly alternate stimuli
    text_1.draw()
    win.flip()
    text_2.draw()
    win.flip()

rather than this:

# needlessly update content of a single stimulus repetitively:
while True: 
    text_1.text = 'hello'
    text_1.draw()
    win.flip()

    text_1.text = 'goodbye'
    text_1.draw()
    win.flip()

i.e. if you update enough text stimuli, eventually you might find that the code takes longer than one screen refresh to execute (and has caused memory leaks in underlying frameworks before). But if you are just going to present instruction text for the duration of a trial, then updating it is fine. (Note that in your function example above, you are re-settting the text content on every single refresh, even though that content is not changing. PsychoPy is clever enough to test for that, and only update text content if the string has actually altered, but you should be wary of needless updating).

Lastly, my only other comment on your proposed structure is that embedding the win.flip() within small functions can become limiting. Think more about the structure of your code. Will you also be collecting responses? Then that code needs to be within the function too. But it would also need to be within the other functions, so you might be getting a lot of duplication. i.e. the stimuli might change from one portion to the next, but response collection might not. So it might be best to have your functions just draw stimuli (without flipping the window), and then do other tasks outside those functions.

Before thinking about specific functions, think first of the wider cyclic nature of the experiment. Generally you are passing through phases of an experiment, and then looping over trials within one or phases, and lastly looping over screen refreshes within portions of those trials. How you might divide that process up into functions, or embed functions within those loops, is up to you. But the ExperimentHandler and TrialHandler classes do a lot of the housework for providing a structure for your experiment, saving data, and so on, and it can be good to start with those and try to incorporate them within whatever coding approach you like.

1 Like

This is a great question. I come down on your side: I like to define functions for as much as possible. But I also like those functions to be long if necessary, so I’d rather define a few functions which do more things rather than just defining a function that presents a stimulus.

I would agree with Michael here that it may be better to think about the specific points at which you present text. Is it as part of instructions? Maybe you should define an instruct function which then also waits for the participant to press space to continue.

Is it part of your experimental trials? Maybe you should make a trial function that presents the text and collects the response. Within those, you can then of course re-use the same TextStim.

I think this makes code much more flexible!

1 Like

Thanks for the answers!

Sorry been busy this past month so completely forgot about this.

Within my experiment, I am presenting introduction stimulus, end of experiment stimulus, and break stimulus (breaks will appear three times throughout the experiment). Would updating the textStim be a bad idea to present all this, or should I just create three separate textStim? I am not entirely sure what would be considered too much.

My current code seems like a bit of a mess, so I am trying to simplify it a bit. I would try and use TrialHandler, but I’m not entirely sure how it works, or how to implement it into my code.

I will be collecting responses, but I won’t be calling the function for that part of the code. I would only be using it to present the stimulus I mentioned above.