示例#1
0
    def __init__(self,
                 win,
                 borderThickness=.003,
                 labelSize=0.03,
                 pos=(0, 0),
                 labelText="text for button",
                 textColor='blue',
                 borderColor='blue',
                 buttonColor='white',
                 buttonEnabled=False,
                 ):

        # local variables
        super(ButtonStim, self).__init__(win)
        button_width = len(labelText) * .025
        button_x_inner_margin = .02
        button_x_outer_margin = button_x_inner_margin + borderThickness
        button_y_inner_margin = labelSize
        button_y_outer_margin = labelSize + borderThickness
        button_x_range = (0 - button_width / 2 + pos[0], 0 + button_width / 2 + pos[0])

        self.win = win
        self.borderThickness = borderThickness
        self.labelSize = labelSize
        self.pos = pos
        self.labelText = labelText
        self.textColor = textColor
        self.borderColor = borderColor
        self.buttonColor = buttonColor
        self.buttonEnabled = buttonEnabled

        self._dragging = False
        self.mouse = event.Mouse()
        self.buttonSelected = False
        self.buttonItems = []

        self.buttonBorder = BaseShapeStim(self.win, fillColor=self.borderColor, vertices=(
            (button_x_range[0] - button_x_outer_margin, -button_y_outer_margin + self.pos[1]),
            (button_x_range[0] - button_x_outer_margin, button_y_outer_margin + self.pos[1]),
            (button_x_range[1] + button_x_outer_margin, button_y_outer_margin + self.pos[1]),
            (button_x_range[1] + button_x_outer_margin, -button_y_outer_margin + self.pos[1])))
        self.buttonInner = BaseShapeStim(self.win, fillColor=self.buttonColor, vertices=(
            (button_x_range[0] - button_x_inner_margin, -button_y_inner_margin + self.pos[1]),
            (button_x_range[0] - button_x_inner_margin, button_y_inner_margin + self.pos[1]),
            (button_x_range[1] + button_x_inner_margin, button_y_inner_margin + self.pos[1]),
            (button_x_range[1] + button_x_inner_margin, -button_y_inner_margin + self.pos[1])))
        self.buttonInnerText = TextStim(self.win, text=self.labelText, color=self.textColor, pos=self.pos,
                                               height=self.labelSize)
        self.buttonItems.append(self.buttonBorder)
        self.buttonItems.append(self.buttonInner)
        self.buttonItems.append(self.buttonInnerText)
示例#2
0
    def __init__(
        self, 
        no_demographics = False, 
        task = 'test'):        
        
        if task=='main':
            self.sub = self.solicit_subid()
            demographics = self.solicit_demographics(no_demographics)            
            iohub_config = {
                'experiment_code': 'color',
                'datastore_name': node(),
                'session_code': task,
                'experiment_info': {
                    'version': str(git.repo.fun.rev_parse(git.Repo(), 'HEAD'))[0:6]},
                'session_info': {
                'user_variables': {
                    'date': datetime.now().strftime("%d-%m-%Y_%H-%M-%S"),
                    'sub': self.sub,
                    'sex': demographics[0],
                    'ethnicity': demographics[1],
                    'race': demographics[2],
                    'age': demographics[3]}}}
        else:
            iohub_config = {}
            self.sub = 0

        self.trialdf = self.__prep_df(os.path.join('stimuli','design.csv'))

        self.io = iohub_config

        # Inform the ioHub server about the TrialHandler
        # randomization already done (hence 'sequential')
        self.io.createTrialHandlerRecordTable(data.TrialHandler(
            [x for x in (self.trialdf.T.to_dict()).values()], 
            nReps=1,
            method='sequential')) 

        self.mon = monitors.Monitor("default", distance=60.96)

        # create a window to draw in
        self.win = visual.Window(
            size=(1920, 1080),
            fullscr=True,
            allowGUI=False,
            winType='pyglet',
            blendMode='avg', 
            useFBO=True,
            units="deg",
            monitor=self.mon,
            # gamma = [r.gamma, g.gamma, b.gamma],
            color='black')

        if task == 'main':
            runinfo = RunTimeInfo(verbose=True, userProcsDetailed=True, win=self.win, refreshTest=True)
            with open(os.path.join('data-raw', f'sub-{self.sub}_task-{task}_runinfo.pkl'), 'xb') as f:pickle.dump(runinfo, f)

        self.fix = visual.Circle(win=self.win, radius=self.fix_radius, size=1, fillColor="white")

        # cues for color
        self.triangle = visual.Polygon(win=self.win, radius=self.radius, lineColor="gray", lineWidth=self.linewidth)
        self.circle = visual.Circle(win=self.win, radius=self.radius, lineColor="gray", lineWidth=self.linewidth)

        # cues for direction
        self.line1 = visual.Line(win=self.win, lineWidth=self.linewidth, start=(-math.sqrt(3)/2, math.sqrt(3)/2), 
            end=(math.sqrt(3)/2, -math.sqrt(3)/2), lineColor="gray", size=self.radius)
        self.line2 = visual.Line(win=self.win, lineWidth=self.linewidth, start=(-math.sqrt(3)/2, -math.sqrt(3)/2), 
            end=(math.sqrt(3)/2, math.sqrt(3)/2), lineColor="gray", size=self.radius)
        self.fleur = visual.ShapeStim(win=self.win, size=self.radius, lineColor="gray", lineWidth=self.linewidth, vertices=self.__make_vertices())

        self.dots = WrappedDot(
            win=self.win,
            units='deg',
            fieldShape="circle",
            dotSize=deg2pix(self.dotsize_deg, self.mon),
            dotLife=-1,
            coherence=self.dot_coherence,
            nDots=self.ndots,
            fieldSize=self.dotfield_diameter_deg,
            speed=self.dot_speed_deg_per_sec / self.refresh_rate)

        self.correct = TextStim(self.win, text = "CORRECT", color="green")
        self.incorrect = TextStim(self.win, text = "INCORRECT", color="red")
        self.welcome = TextStim(self.win, text=
        '''
        welcome to the experiment

        Press "c" to continue
        ''',
        wrapWidth=50,
        alignText="left")
        self.rest = TextStim(
            self.win,
            pos=(0, 6),
            alignText="left",
            wrapWidth=50)

        self.waiter = clock.StaticPeriod(screenHz=self.refresh_rate)
示例#3
0
class Experiment(object):
    
    linewidth = 3
    radius = 0.75
    fix_radius = 0.1
    n_fleur_vertex = 10
    refresh_rate = 60
    cue_sec = 1
    dot_sec = 3
    n_flips_per_dots = dot_sec * refresh_rate
    fix_sec = 0.5
    dot_speed_deg_per_sec = 1.67
    dotsize_deg = 0.08
    ndots = 400
    dotfield_diameter_deg = 3.2
    dot_coherence = 1
    feedback_sec = 0.5

    initial_pause_sec = 0.5

    def __init__(
        self, 
        no_demographics = False, 
        task = 'test'):        
        
        if task=='main':
            self.sub = self.solicit_subid()
            demographics = self.solicit_demographics(no_demographics)            
            iohub_config = {
                'experiment_code': 'color',
                'datastore_name': node(),
                'session_code': task,
                'experiment_info': {
                    'version': str(git.repo.fun.rev_parse(git.Repo(), 'HEAD'))[0:6]},
                'session_info': {
                'user_variables': {
                    'date': datetime.now().strftime("%d-%m-%Y_%H-%M-%S"),
                    'sub': self.sub,
                    'sex': demographics[0],
                    'ethnicity': demographics[1],
                    'race': demographics[2],
                    'age': demographics[3]}}}
        else:
            iohub_config = {}
            self.sub = 0

        self.trialdf = self.__prep_df(os.path.join('stimuli','design.csv'))

        self.io = iohub_config

        # Inform the ioHub server about the TrialHandler
        # randomization already done (hence 'sequential')
        self.io.createTrialHandlerRecordTable(data.TrialHandler(
            [x for x in (self.trialdf.T.to_dict()).values()], 
            nReps=1,
            method='sequential')) 

        self.mon = monitors.Monitor("default", distance=60.96)

        # create a window to draw in
        self.win = visual.Window(
            size=(1920, 1080),
            fullscr=True,
            allowGUI=False,
            winType='pyglet',
            blendMode='avg', 
            useFBO=True,
            units="deg",
            monitor=self.mon,
            # gamma = [r.gamma, g.gamma, b.gamma],
            color='black')

        if task == 'main':
            runinfo = RunTimeInfo(verbose=True, userProcsDetailed=True, win=self.win, refreshTest=True)
            with open(os.path.join('data-raw', f'sub-{self.sub}_task-{task}_runinfo.pkl'), 'xb') as f:pickle.dump(runinfo, f)

        self.fix = visual.Circle(win=self.win, radius=self.fix_radius, size=1, fillColor="white")

        # cues for color
        self.triangle = visual.Polygon(win=self.win, radius=self.radius, lineColor="gray", lineWidth=self.linewidth)
        self.circle = visual.Circle(win=self.win, radius=self.radius, lineColor="gray", lineWidth=self.linewidth)

        # cues for direction
        self.line1 = visual.Line(win=self.win, lineWidth=self.linewidth, start=(-math.sqrt(3)/2, math.sqrt(3)/2), 
            end=(math.sqrt(3)/2, -math.sqrt(3)/2), lineColor="gray", size=self.radius)
        self.line2 = visual.Line(win=self.win, lineWidth=self.linewidth, start=(-math.sqrt(3)/2, -math.sqrt(3)/2), 
            end=(math.sqrt(3)/2, math.sqrt(3)/2), lineColor="gray", size=self.radius)
        self.fleur = visual.ShapeStim(win=self.win, size=self.radius, lineColor="gray", lineWidth=self.linewidth, vertices=self.__make_vertices())

        self.dots = WrappedDot(
            win=self.win,
            units='deg',
            fieldShape="circle",
            dotSize=deg2pix(self.dotsize_deg, self.mon),
            dotLife=-1,
            coherence=self.dot_coherence,
            nDots=self.ndots,
            fieldSize=self.dotfield_diameter_deg,
            speed=self.dot_speed_deg_per_sec / self.refresh_rate)

        self.correct = TextStim(self.win, text = "CORRECT", color="green")
        self.incorrect = TextStim(self.win, text = "INCORRECT", color="red")
        self.welcome = TextStim(self.win, text=
        '''
        welcome to the experiment

        Press "c" to continue
        ''',
        wrapWidth=50,
        alignText="left")
        self.rest = TextStim(
            self.win,
            pos=(0, 6),
            alignText="left",
            wrapWidth=50)

        self.waiter = clock.StaticPeriod(screenHz=self.refresh_rate)

    
    @property
    def win(self) -> visual.Window:
        return self.__win

    
    @win.setter
    def win(self, win: visual.Window) -> None:
        self.__win = win

    
    @win.deleter
    def win(self) -> None:
        self.__win.close()
        del self.__win


    def __prep_df(self, design: TextIO) -> pd.DataFrame:
        d = pd.read_csv(design)
                
        d_sub = (d.loc[d['sub']==self.sub]
            .sort_values(by=['block','trial'])
            .assign(
                fix_start = float('NaN'),
                cue_start = float('NaN'),
                dots_start = float('NaN'),
                dots_end = float('NaN'),
                response_time = float('NaN'),
                feedback_start = float('NaN'),
                correct = None, 
                response_key = '',
                a = lambda x: x.chroma * np.cos(np.radians(x.hue)),
                b = lambda x: x.chroma * np.sin(np.radians(x.hue)),
                rgb = lambda x: cst.cielab2rgb(
                    x.loc[:,['lightness','a','b']],
                    # whiteXYZ=[0.9642, 1.0, 0.8521],
                    # conversionMatrix=rgb2xyz, 
                    clip=True).tolist()))
        d_sub['R'] = [x[0] for x in d_sub['rgb']]
        d_sub['G'] = [x[1] for x in d_sub['rgb']]
        d_sub['B'] = [x[2] for x in d_sub['rgb']]
        return d_sub.drop('rgb', axis=1)


    @property
    def trialdf(self) -> pd.DataFrame:
        return self.__trialdf


    @trialdf.setter
    def trialdf(self, df: pd.DataFrame) -> None:
        self.__trialdf = df


    @property
    def io(self) -> ioHubConnection:
        return self.__io


    @io.setter
    def io(self, iohub_config: dict) -> None:
        # Start the ioHub process. 'io' can now be used during the
        # # experiment to access iohub devices and read iohub device events.
        io = launchHubServer(**iohub_config)
        self.__io = io


    @io.deleter
    def io(self) -> None:
        self.__io.quit()
        del self.__io


    @staticmethod
    def solicit_demographics(no_demographics) -> Tuple[str, str, str, str]:

        dlg = Dlg(title="Demographics")
        dlg.addText('''The National Institute of Health requests basic demographic information (sex, ethnicity, race, and age)
        for clinical or behavioral studies, to the extent that this information is provided by research participants.
    
        You are under no obligation to provide this information. If you would rather not answer these questions, 
        you will still receive full compensation for your participation in this study and the data you provide will still be useful for our research.
        ''')
        dlg.addField('sex at birth:', choices=['Female', 'Male', 'Other', 'Rather not say'])
        dlg.addField('ethnicity:', choices= ['Hispanic or Latino', 'Not Hispanic or Latino', 'Rather not say'])
        dlg.addField('race:', choices= ['American Indian/Alaska Native', 'Asian', 'Black or African American', 'Native Hawaiian or Other Pacific Islander', 'White', 'Rather not say'])
        dlg.addField('age:', choices=[x for x in range(18, 51)] + ['Rather not say'])
        if not no_demographics:
            demographics = dlg.show()
            if dlg.OK == False: # user pressed cancel
                # fine to quit here, since nothing important has been opened
                core.quit()  
        else:
            demographics = ['na', 'na', 'na', 'na']

        return demographics
        

    @staticmethod
    def solicit_subid() -> int:

        dlg = Dlg(title="Participant")
        dlg.addField('ID:', choices=[x for x in range(0, 31)])
        ID = dlg.show()        
        if dlg.OK == False: # user pressed cancel
            # fine to quit here, since nothing important has been opened
            core.quit()  

        return ID[0]

    @staticmethod
    def __make_vertex(angle: float, a: float = radius, b: float = radius/3) -> Tuple[float, float]:
        theta = math.atan2(a*math.tan(angle), b)
        return (a*math.cos(theta), b*math.sin(theta))


    def __make_vertices(self) -> List[float]:
        vertices = []
        vertices.extend([self.__make_vertex(angle) for angle in 
            np.linspace(-math.pi/4, math.pi/4, num=self.n_fleur_vertex, endpoint=False)])
        vertices.extend([self.__make_vertex(angle, a=self.radius/3, b=self.radius) for angle in 
            np.linspace(math.pi/4, math.pi/2, num=self.n_fleur_vertex//2, endpoint=False)])
        vertices.extend([self.__make_vertex(angle, a=self.radius/3, b=-self.radius) for angle in 
            np.linspace(-math.pi/2, -math.pi/4, num=self.n_fleur_vertex//2, endpoint=False)])
        vertices.extend([self.__make_vertex(angle, a=-self.radius) for angle in 
            np.linspace(-math.pi/4, math.pi/4, num=self.n_fleur_vertex)])
        vertices.extend([self.__make_vertex(angle, a=self.radius/3, b=-self.radius) for angle in 
            np.linspace(math.pi/4, math.pi/2, num=self.n_fleur_vertex//2, endpoint=False)])
        vertices.extend([self.__make_vertex(angle, a=self.radius/3, b=self.radius) for angle in 
            np.linspace(-math.pi/2, -math.pi/4, num=self.n_fleur_vertex//2, endpoint=False)])
        return vertices


    def __drawcue(self, shape) -> None:
        if shape == 'triangle':
            self.triangle.draw()
        elif shape == 'cross':
            self.line1.draw()
            self.line2.draw()
        elif shape == 'circle':
            self.circle.draw()
        else: 
            self.fleur.draw()


    def __prep_dots(self, direction: float, rgb: Tuple[float, float, float]) -> None:
        self.dots.dir = direction
        self.dots.color = rgb


    @property
    def presses(self) -> dict:
        return self.__presses

    
    @presses.setter
    def presses(self, presses: Optional[dict]) -> None:

        if presses and presses[0].key == 'escape':
            sys.exit()
        else:    
            self.__presses = presses
    

    # run the experiment
    def run(self) -> None:
        self.win.setMouseVisible(False)

        self.welcome.draw()
        self.win.flip()
        self.io.clearEvents()
        self.io.devices.keyboard.waitForPresses(keys=['c'])

        for block in self.trialdf.block.unique():
            self.rest.text = f'''
            you are about to start part {block+1} of {self.trialdf.block.max()+1} in the experiment                         

            remember:
            cross and flower shapes mean categorize direction
            triangle and circle mean categorize color

            when you are categorizing direction, press "left" for upwards and "right" for downwards
            when you are categorizing color, press "left" for redder and "right" for greener

            when you are ready to begin this part, press "c"
            '''
            self.rest.draw()
            self.win.flip()
            self.io.clearEvents()
            self.io.devices.keyboard.waitForPresses(keys=['c'])

            self.fix.draw()
            self.win.flip()
            self.waiter.start(self.initial_pause_sec)
            for __t in self.trialdf.query(f'block == {block}').itertuples():
                trial = __t._asdict()
                # fixation
                self.fix.draw()
                self.waiter.complete()
                trial['fix_start'] = self.win.flip()
                self.waiter.start(self.fix_sec)
                self.__prep_dots(trial['direction'], (trial['R'], trial['G'], trial['B']))
            
                # cue
                self.__drawcue(trial['shape'])
                self.fix.draw()
                self.waiter.complete()
                trial['cue_start'] = self.win.flip()
                self.waiter.start(self.cue_sec)

                # stim
                for flip in range(0, self.n_flips_per_dots):
                    self.draw_list([self.dots, self.fix])

                    if flip == 0: 
                        self.waiter.complete()
                        self.io.clearEvents()
                
                    now = self.win.flip()
                    self.io.sendMessageEvent(f'trial-{trial["trial"]}_flip-{flip}', category="flip", sec_time=now)

                    if flip == 0:
                        trial['dots_start'] = now
            
                    self.presses = self.io.devices.keyboard.getPresses(keys=['left','right','escape'])
                    if self.presses:
                        break                    
                                        
                # record trial results (if any) and prepare for next trial            
                trial['dots_end'] = now
                if self.presses:                    
                    trial['response_time'] = self.presses[0].time
                    trial['response_key'] = self.presses[0].key

                trial['correct'] = (
                    (trial['response_key'] == 'left' and 
                      ((trial['shape'] in ['triangle', 'circle'] and trial['hue'] <= 90) 
                      or (trial['shape'] in ['cross', 'fleur'] and trial['direction'] >= 0)))
                or (trial['response_key'] == 'right' and 
                    ((trial['shape'] in ['triangle', 'circle'] and trial['hue'] >= 90) 
                    or (trial['shape'] in ['cross', 'fleur'] and trial['direction'] <= 0))))
                if trial['correct']:
                    self.correct.draw()
                else:
                    self.incorrect.draw()

                trial['feedback_start'] = self.win.flip()
                self.waiter.start(self.feedback_sec)            
            
                # At the end of each trial, before getting
                # the next trial handler row, send the trial
                # variable states to iohub so they can be stored for future
                # reference.
                self.io.addTrialHandlerRecord(trial)
            
            if block == self.trialdf.block.max():
                msg = '''
                you have reached the end of the experiment!

                please go find the researcher and let them know you finished
                '''
            else:
                msg = f'''
                congrats! you have just finished that part
                the time is currently {datetime.now().strftime("%H:%M")}

                please take a brief break

                when you are ready to continue, press "c"
                '''

            self.rest.text = msg
            self.rest.draw()
            self.waiter.complete()
            self.win.flip()
            self.io.clearEvents()
            self.io.devices.keyboard.waitForPresses(keys=['c'])



    @staticmethod
    def draw_list(stims) -> None:
        for x in stims:
            x.draw()


    def instruct(self) -> None:
        self.win.setMouseVisible(False)

        text = visual.TextStim(
            win=self.win,
            text='''
            in this experiment you will see a bunch of colored dots move together
            you will be asked to categorize either the color or the direction of motion
            
            press "c" to continue
            ''',
            pos=(0, 6),
            alignText="left",
            wrapWidth = 50)

        text.draw()
        self.win.flip()
        self.io.clearEvents()
        self.io.devices.keyboard.waitForPresses(keys=['c'])

        text = visual.TextStim(
            win=self.win,
            text='''
            when you see these cues, categorize the motion
            when the dots are moving upwards, press "left"
            when the dots are moving downwards, press "right"
            
            press "c" to continue
            ''',
            pos=(0, 6),
            alignText="left",
            wrapWidth = 50)

        # motion cues
        self.line1.pos = (-1,0)
        self.line2.pos = (-1,0)
        self.fleur.pos = (1,0)
        self.dots.dir = 30
        self.dots.color = cst.cielch2rgb([90,15,120], clip=True)
        text.draw()
        self.__drawcue('cross')
        self.__drawcue('fluer')
        self.win.flip()        
        self.line1.pos = (0,0)
        self.line2.pos = (0,0)
        self.fleur.pos = (0,0)

        self.io.clearEvents()
        self.io.devices.keyboard.waitForPresses(keys=['c'])

        text.text = '''
        here, the dots are moving upwards
        so you want to press "left"
        '''

        self.io.clearEvents()
        while 1:
            self.draw_list([text, self.dots, self.fix])
            self.win.flip()    
            self.presses = self.io.devices.keyboard.getPresses(keys=['left','escape'])
            if self.presses:
                break

        # color cues
        text.text = '''
        when you see these cues, categorize the color
        when the dots are more red, press "left"
        when the dots are more green, press "right
            
        press "c" to continue"
        '''

        self.triangle.pos = (-1,0)
        self.circle.pos = (1,0)
        text.draw()
        self.__drawcue('triangle')
        self.__drawcue('circle')
        self.win.flip()
        self.io.clearEvents()
        self.triangle.pos = (0,0)
        self.circle.pos = (0,0)

        self.io.devices.keyboard.waitForPresses(keys=['c'])
        text.text = '''
        here, the dots are more green
        so you want to press "right"
        '''

        self.io.clearEvents()
        while 1:
            self.draw_list([text, self.dots, self.fix])
            self.win.flip()    
            self.presses = self.io.devices.keyboard.getPresses(keys=['right','escape'])
            if self.presses:
                break

        text.text = '''
        in summary: 
        cross and flower shapes mean categorize direction
        triangle and circle mean categorize color

        when you are categorizing direction, press "left" for upwards and "right" for downwards
        when you are categorizing color, press "left" for redder and "right" for greener

        in the actual experiment, everything will happen more quickly
            
        press "c" to continue to a regular trial
        '''

        text.draw()
        self.win.flip()
        self.io.clearEvents()
        self.io.devices.keyboard.waitForPresses(keys=['c'])

        # fixation
        self.fix.draw()
        self.win.flip()
        self.waiter.start(self.fix_sec)

        # cue
        self.draw_list([self.triangle, self.fix])
        self.waiter.complete()
        self.win.flip()
        self.waiter.start(self.cue_sec)

        # stim
        for flip in range(0, self.n_flips_per_dots):
            self.draw_list([self.dots, self.fix])

            if flip == 0: 
                self.waiter.complete()
                self.io.clearEvents()
                
            self.win.flip()
            self.presses = self.io.devices.keyboard.getPresses(keys=['right', 'escape'])
            if self.presses:
                break

    
        text.text = '''
        lastly, a white dot will be visible throughout the experiment

        when it is visible, please keep your eyes focused on that dot

        press 'c' to conclude the instructions
        '''

        self.io.clearEvents()
        self.draw_list([text, self.fix])
        self.win.flip()
        self.io.clearEvents()
        self.io.devices.keyboard.waitForPresses(keys=['c'])
示例#4
0
    def _gen_orientation_labels(
        self
    ) -> typing.Tuple[visual.ImageStim, visual.ImageStim, visual.ImageStim,
                      visual.ImageStim, visual.ImageStim]:
        """Draws and returns orientation labels, with position depending on
        outlines of body image and spacing depending on the font size.
        """
        # To orient the קדימה/אחורה labels at the bottom of the images
        lowest_y = self.dotting_outlines[:, 1].min()
        # To orient the right-hand 'R' label to the right of the 'back' image
        rightmost_x = self.dotting_outlines[:, 0].max()
        # The matching 'y' value
        rightmost_y = self.dotting_outlines[np.argmax(
            self.dotting_outlines[:, 0] == rightmost_x), 1]
        # To orient the left-hand 'R' label to the left of the 'front' image
        leftmost_x = self.dotting_outlines[:, 0].min()
        # The matching 'y' value
        leftmost_y = self.dotting_outlines[np.argmax(
            self.dotting_outlines[:, 0] == leftmost_x), 1]
        # Controlling height of letters, wrap length and spacing
        height = params.TEXT_LABELS_SIZE * self.win.size[1]
        # center_label
        front_right_label = TextStim(
            win=self.win,
            height=height,
            units='pix',
            text='ימין',
            languageStyle='RTL',
            color='black',
            alignHoriz='left',
            pos=[
                float(rightmost_x + leftmost_x) / 2 +
                1.5 * params.TEXT_LABELS_SIZE * self.win.size[0], rightmost_y
            ],
            wrapWidth=height)
        # center_right_label
        front_left_label = TextStim(
            win=self.win,
            height=height,
            units='pix',
            pos=[
                rightmost_x + 0.5 * params.TEXT_LABELS_SIZE * self.win.size[0],
                rightmost_y
            ],
            text='שמאל',
            languageStyle='RTL',
            alignHoriz='left',
            color='black',
            wrapWidth=height)
        # center_left_label
        back_left_label = TextStim(
            win=self.win,
            height=height,
            units='pix',
            pos=[
                leftmost_x - 1.5 * params.TEXT_LABELS_SIZE * self.win.size[0],
                leftmost_y
            ],
            text='שמאל',
            languageStyle='RTL',
            color='black',
            wrapWidth=height)
        # new - back_right
        back_right_label = TextStim(
            win=self.win,
            height=height,
            units='pix',
            pos=[
                float(rightmost_x + leftmost_x) / 2 -
                2 * params.TEXT_LABELS_SIZE * self.win.size[0], rightmost_y
            ],
            text='ימין',
            languageStyle='RTL',
            color='black',
            wrapWidth=height)

        bottom_left_label = TextStim(
            win=self.win,
            height=height,
            units='pix',
            alignHoriz='center',
            pos=[(-self.win.size[0] * params.IMG_OFFSET[0]) - 12,
                 lowest_y - height],
            text='אחורה',
            languageStyle='RTL',
            color='black',
            wrapWidth=height)
        bottom_right_label = TextStim(
            win=self.win,
            height=height,
            units='pix',
            alignHoriz='center',
            pos=[(self.win.size[0] * params.IMG_OFFSET[0]) - 12,
                 lowest_y - height],
            text='קדימה',
            languageStyle='RTL',
            color='black',
            wrapWidth=height)

        return (front_right_label, front_left_label, back_right_label,
                back_left_label, bottom_left_label, bottom_right_label)
示例#5
0
    def __init__(
        self,
        win,
        text='Hello World',
        pos=(0.0, 0.0),
        width=None,
        height=None,
        padx=2,
        pady=2,
        units="",
        checked=False,
        name=None,
        autoLog=None,
        autoDraw=False,
    ):
        """
        Button accepts all input parameters, that
        `~psychopy.visual.BaseVisualStim` accept, except for vertices
        and closeShape.

        :Parameters:

            width : int or float
                Width of the Rectangle (in its respective units, if specified)

            height : int or float
                Height of the Rectangle (in its respective units, if specified)

        """
        # what local vars are defined (these are the init params) for use by
        # __repr__
        self._initParams = dir()
        self._initParams.remove('self')

        super(Button, self).__init__(win,
                                     units=units,
                                     name=name,
                                     autoLog=False)

        self.__dict__['pos'] = pos
        self.__dict__['text'] = text

        self.textStim = TextStim(win,
                                 text=text,
                                 pos=self.pos,
                                 wrapWidth=width,
                                 color='Black',
                                 units=units,
                                 autoLog=autoLog)

        # TODO: expose content_width via TextStim
        autoWidth = (self.textStim._pygletTextObj._layout.content_width +
                     2 * padx)
        autoHeight = self.textStim.height + 2 * pady

        if width is not None and width > autoWidth:
            self.__dict__['width'] = width
        else:
            self.__dict__['width'] = autoWidth

        if height is not None and height > autoHeight:
            self.__dict__['height'] = height
        else:
            self.__dict__['height'] = autoHeight

        self.rectStim = Rect(win,
                             pos=self.pos,
                             width=self.width,
                             height=self.height,
                             units=units,
                             autoLog=autoLog)

        self.normalStyle = ButtonStyle()
        self.checkedStyle = ButtonStyle(fillColor='#2E2EFE',
                                        borderColor='White',
                                        textColor='White')

        self.checked = checked  # this will set style

        self.autoDraw = autoDraw

        self.__dict__['autoLog'] = (autoLog
                                    or autoLog is None and self.win.autoLog)
        if self.autoLog:
            logging.exp("Created %s = %s" % (self.name, str(self)))
示例#6
0
class Button(BaseVisualStim):
    """Creates a button of given width and height, by combining a
    TextStim and a Rect

    (New in version 1.80.99 FIXME)
    """
    def __init__(
        self,
        win,
        text='Hello World',
        pos=(0.0, 0.0),
        width=None,
        height=None,
        padx=2,
        pady=2,
        units="",
        checked=False,
        name=None,
        autoLog=None,
        autoDraw=False,
    ):
        """
        Button accepts all input parameters, that
        `~psychopy.visual.BaseVisualStim` accept, except for vertices
        and closeShape.

        :Parameters:

            width : int or float
                Width of the Rectangle (in its respective units, if specified)

            height : int or float
                Height of the Rectangle (in its respective units, if specified)

        """
        # what local vars are defined (these are the init params) for use by
        # __repr__
        self._initParams = dir()
        self._initParams.remove('self')

        super(Button, self).__init__(win,
                                     units=units,
                                     name=name,
                                     autoLog=False)

        self.__dict__['pos'] = pos
        self.__dict__['text'] = text

        self.textStim = TextStim(win,
                                 text=text,
                                 pos=self.pos,
                                 wrapWidth=width,
                                 color='Black',
                                 units=units,
                                 autoLog=autoLog)

        # TODO: expose content_width via TextStim
        autoWidth = (self.textStim._pygletTextObj._layout.content_width +
                     2 * padx)
        autoHeight = self.textStim.height + 2 * pady

        if width is not None and width > autoWidth:
            self.__dict__['width'] = width
        else:
            self.__dict__['width'] = autoWidth

        if height is not None and height > autoHeight:
            self.__dict__['height'] = height
        else:
            self.__dict__['height'] = autoHeight

        self.rectStim = Rect(win,
                             pos=self.pos,
                             width=self.width,
                             height=self.height,
                             units=units,
                             autoLog=autoLog)

        self.normalStyle = ButtonStyle()
        self.checkedStyle = ButtonStyle(fillColor='#2E2EFE',
                                        borderColor='White',
                                        textColor='White')

        self.checked = checked  # this will set style

        self.autoDraw = autoDraw

        self.__dict__['autoLog'] = (autoLog
                                    or autoLog is None and self.win.autoLog)
        if self.autoLog:
            logging.exp("Created %s = %s" % (self.name, str(self)))

    def _setStyle(self, style):
        self.rectStim.lineColor = style.borderColor
        self.rectStim.fillColor = style.fillColor
        self.textStim.color = style.textColor

    @attributeSetter
    def text(self, value):
        """Changes the text of the Button"""
        self.__dict__['text'] = value
        # TODO: change this to attributeSetters with 1.80.99
        self.textStim.setText(value)

    @attributeSetter
    def pos(self, value):
        """Changes the position of the Button"""
        self.__dict__['pos'] = value
        self.rectStim.pos = value
        self.textStim.pos = value

    @attributeSetter
    def width(self, value):
        """Changes the width of the Button"""
        self.__dict__['width'] = value
        # TODO: change this to attributeSetters with 1.80.99
        self.rectStim.setWidth(value)
        # this won't work, need to construct new or wait for 1.80.99
        self.textStim.wrapWidth = value

    @attributeSetter
    def height(self, value):
        """Changes the height of the Button"""
        self.__dict__['height'] = value
        # TODO: change this to attributeSetters with 1.80.99
        self.rectStim.setHeight(value)

        # don't set height of text as it will change font size
        # self.textStim.setHeight(value)

    @attributeSetter
    def checked(self, value):
        self.__dict__['checked'] = value
        if self.checked:
            self._setStyle(self.checkedStyle)
        else:
            self._setStyle(self.normalStyle)

    def setColor(self, color, colorSpace=None, operation=''):
        """For Button use :meth:`~Button.fillColor` or
        :meth:`~Button.borderColor` or :meth:`~Button.textColor`
        """
        raise AttributeError('Button does not support setColor method.'
                             'Please use fillColor, borderColor, or textColor')

    def contains(self, x, y=None, units=None):
        return self.rectStim.contains(x, y, units)

    def draw(self, win=None):
        """
        Draw the stimulus in its relevant window. You must call
        this method after every MyWin.flip() if you want the
        stimulus to appear on that frame and then update the screen
        again.

        If win is specified then override the normal window of this stimulus.
        """
        if win is None:
            win = self.win

        self.rectStim.draw(win)
        self.textStim.draw(win)