Drawing Matplotlib plot into Psychopy loop

379 views Asked by At

Using Psychopy and Matplotlib I am displaying 100 rectangles of random orientations (0,360), sizes, and positions, on the left hand side of the screen. I am assigning a unique colour to each quadrant of orientation (i.e. red for all orientations < 90, green for ori's >90 and < 180, blue for all >180 and <270, and grey for all > 270). I'm keeping a count of how many of each rectangle fall within those ranges, and then plotting them using Matplotlib. I'm displaying that graph on the right hand side of the screen by saving the graph, and then calling it back in using Psychopy Imagestim. I'm keeping this script in a function, and then running the function n number of times. The idea is, on each keypress, I get a new display of rectangles (left) and a new accompanying graph (right side).

My issue is, whilst the rectangles and rectangle counts are displaying fine, the graph is not updating properly. If for example, on the first trial there are 24 reds, the red bar will show this. However, if on the next trial there are 22 reds, the red part of the graph will not reduce to 23, it stays on 24. It basically only increases. This is the case for the whole graph (all colors), resulting in a graph that only increases each bar on every trial (no reductions). I cannot see why this is happening. I assumed it was a problem with how I am passing my 4 (color) variables to Matplotlib, but they are being reset to 0 at the beginning of each trial; for this reason I can't figure out the problem.

import random
import psychopy.visual
import psychopy.event
import psychopy.core
import numpy as np
import matplotlib.pyplot as plt

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

def one():
    win.flip()
    rect=psychopy.visual.Rect(win=win,units="pix") 

    n_rect=100

    performance = []

    red   = 0
    green = 0
    blue  = 0
    grey  = 0

    for i_rect in range(n_rect):                               
        rect.width = random.uniform(10,100)
        rect.height = random.uniform(10,100)
        rect.ori = random.uniform(0,360)

        rect.pos = [
            random.uniform(-300,-10),
            random.uniform(-300,300)
        ]

        if rect.ori < 90:
            rect.fillColor = (1,-1,-1)
            red += 1

        elif rect.ori >90 and rect.ori <180:
            rect.fillColor = (-1,1,-1)
            green += 1

        elif rect.ori > 180 and rect.ori < 270:
            rect.fillColor = (-1,-1,1)
            blue += 1

        else:
            rect.fillColor = (0,0,0)
            grey += 1

        rect.draw()

    print ("red:   %i")%(red)
    print ("green: %i")%(green)
    print ("blue:  %i")%(blue)
    print ("grey:  %i")%(grey)

    red_text   =  psychopy.visual.TextStim(win,text =  red,  pos =  (165,-180))
    green_text =  psychopy.visual.TextStim(win,text =  green,pos = (295,-180))
    blue_text  =  psychopy.visual.TextStim(win,text =  blue, pos = (425,-180))
    grey_text  =  psychopy.visual.TextStim(win,text =  grey, pos = (555,-180)) 

    color_text_list = [red_text,green_text,blue_text,grey_text]

    for color in color_text_list:
        color.draw()

    # Here I pass the value of my colour variables to Matplotlib for plotting

    plt.style.use('ggplot')
    colors = ('red','green','blue','grey')
    y_pos = np.arange(len(colors))
    performance = [red,green,blue,grey]

    x = plt.bar(y_pos, performance, align='center')
    x[0].set_color('red')
    x[1].set_color('green')
    x[2].set_color('blue')
    x[3].set_color('grey')
    plt.xticks(y_pos, colors)
    plt.ylabel('count')

    plt.savefig('image.png',dpi=80,transparent=True)

    img = psychopy.visual.ImageStim(
        win=win,
        image="image.png",
        units="pix",
        pos = (350,50)
    )

    rect.draw()
    img.draw()
    win.flip()

    psychopy.event.waitKeys()

for i in range(20):
    one()

win.close()

The code can be run on it's own if anyone wants to try it. The code is relatively simple, but this is the first time I have used Matplotlib, perhaps I am missing something obvious.

Thanks, Steve

1

There are 1 answers

0
Andrew Guy On BEST ANSWER

I can't verify as I don't have psychopy installed currently, but I think your issue is that you aren't clearing your pyplot figure after each iteration.

def one():
    ...
    ...
    performance = [red,green,blue,grey]
    #Clears the current plot figure, leaves the window open.
    plt.clf()
    x = plt.bar(y_pos, performance, align='center')
    ...
    ...

pyplot keeps track of the currently active figure, and will keep on drawing onto that figure (without clearing previous data) until you tell it otherwise. You can also call plt.figure() to open a new figure, but make sure that you close the previous figure to clear up memory.

See this question for a more detailed explanation of the different ways of clearing your plot - When to use cla(), clf() or close() for clearing a plot in matplotlib?