Drawing to a window

Objectives

  • Understand the process of drawing stimuli to the window.
  • Be aware of the implications of window “flipping”.

In this short lesson, we are going to cover a simple but important concept—the process by which we can draw and present visual stimuli.

Most, if not all, of the psychopy stimulus types that we will be using are drawn to the screen using the same procedure. They each have a .draw() function—this tells psychopy to take the information that has been provided about the current stimulus and render it to the window. However, a very important point is that such a rendering is not immediately visible in the window.

To illustrate this, we can take our example from the previous lesson but remove the mysterious win.flip() command (note that the text.draw() command remains):

import psychopy.visual
import psychopy.event

win = psychopy.visual.Window(
    size=[400, 400],
    units="pix",
    fullscr=False,
    color=[1, 1, 1]
)

text = psychopy.visual.TextStim(
    win=win,
    text="Hello, world!",
    color=[-1, -1, -1]
)

text.draw()

#win.flip()

psychopy.event.waitKeys()

win.close()

Tip

Recall that everything to the right of a # character in a line of Python code is treated as a “comment”, and is not interpreted by Python.

You can see that even though we have “drawn” the stimulus to the window, it does not appear. To make it appear, we have to reinstate the win.flip() command. So what does this command do and why does it make what we have drawn visible? A useful way to think about it is as if the window has two layers, one in front of the other. When we execute the draw command, we are “painting” the second layer of the window which, because it is behind the first layer, we cannot see. Once we have done all the drawing we want to do, and we are ready to see the results, we “flip” the layers around; what was at the back is now at the front, and hence visible.

Importantly, in this process of “flipping” the window, we wipe what was previously drawn to the front window. That means that each time we flip the window, we need to draw something again in order to see it. For example, we can add in another section where we flip the window and wait for another keypress, but this time without drawing our text.

import psychopy.visual
import psychopy.event

win = psychopy.visual.Window(
    size=[400, 400],
    units="pix",
    fullscr=False,
    color=[1, 1, 1]
)

text = psychopy.visual.TextStim(
    win=win,
    text="Hello, world!",
    color=[-1, -1, -1]
)

text.draw()

win.flip()

psychopy.event.waitKeys()

win.flip()

psychopy.event.waitKeys()

win.close()

As you can see, the second time around does not show our text message—because we had not drawn anything to the window after we flipped it.

A final point about drawing stimuli is that the order of drawing matters, with more recent draw commands having to potential to overwrite what was drawn by previous commands. For example, we could draw the text in both black and green.

import psychopy.visual
import psychopy.event

win = psychopy.visual.Window(
    size=[400, 400],
    units="pix",
    fullscr=False,
    color=[1, 1, 1]
)

text = psychopy.visual.TextStim(
    win=win,
    text="Hello, world!",
    color=[-1, -1, -1]
)

# text is black
text.draw()

# change text to green
text.color = [-1, 0, -1]

# draw text again
text.draw()

win.flip()

psychopy.event.waitKeys()

win.close()

As you can see, there is no sign of our black text—it has been overwritten by the green. We can see this even more clearly if we move the green text horizontally and vertically by a small amount, creating a neat shadow effect:

import psychopy.visual
import psychopy.event

win = psychopy.visual.Window(
    size=[400, 400],
    units="pix",
    fullscr=False,
    color=[1, 1, 1]
)

text = psychopy.visual.TextStim(
    win=win,
    text="Hello, world!",
    color=[-1, -1, -1]
)

# text is black
text.draw()

# change text to green
text.color = [-1, 0, -1]

# shift the green text one pixel to the right and one pixel up
text.pos = [1, 1]

# draw text again
text.draw()

win.flip()

psychopy.event.waitKeys()

win.close()

Tip

Position values are specified in psychopy via a two-item list of coordinates (horizontal and vertical). The pair [0, 0] is at the centre of the screen. Negative horizontal coordinates are offset to the left of centre and positive to the right of centre. Negative vertical coordinates are offset below centre and positive above centre.