Beispiel #1
0
    def addRoutine(self, routineName, routine=None):
        """Add a Routine to the current list of them.

        Can take a Routine object directly or will create
        an empty one if none is given.
        """
        if routine is None:
            # create a deafult routine with this name
            self.routines[routineName] = Routine(routineName, exp=self)
        else:
            self.routines[routineName] = routine
        return self.routines[routineName]
Beispiel #2
0
    def loadFromXML(self, filename):
        """Loads an xml file and parses the builder Experiment from it
        """
        self._doc.parse(filename)
        root = self._doc.getroot()

        # some error checking on the version (and report that this isn't valid
        # .psyexp)?
        filenameBase = os.path.basename(filename)

        if root.tag != "PsychoPy2experiment":
            logging.error('%s is not a valid .psyexp file, "%s"' %
                          (filenameBase, root.tag))
            # the current exp is already vaporized at this point, oops
            return
        self.psychopyVersion = root.get('version')
        # If running an experiment from a future version, send alert to change "Use Version"
        if Version(psychopy.__version__) < Version(self.psychopyVersion):
            alert(code=4051, strFields={'version': self.psychopyVersion})
        # If versions are either side of 2021, send alert
        if Version(psychopy.__version__) >= Version("2021.1.0") > Version(
                self.psychopyVersion):
            alert(code=4052, strFields={'version': self.psychopyVersion})

        # Parse document nodes
        # first make sure we're empty
        self.flow = Flow(exp=self)  # every exp has exactly one flow
        self.routines = {}
        self.namespace = NameSpace(self)  # start fresh
        modifiedNames = []
        duplicateNames = []

        # fetch exp settings
        settingsNode = root.find('Settings')
        for child in settingsNode:
            self._getXMLparam(params=self.settings.params,
                              paramNode=child,
                              componentNode=settingsNode)
        # name should be saved as a settings parameter (only from 1.74.00)
        if self.settings.params['expName'].val in ['', None, 'None']:
            shortName = os.path.splitext(filenameBase)[0]
            self.setExpName(shortName)
        # fetch routines
        routinesNode = root.find('Routines')
        allCompons = getAllComponents(self.prefsBuilder['componentsFolders'],
                                      fetchIcons=False)
        allRoutines = getAllStandaloneRoutines(fetchIcons=False)
        # get each routine node from the list of routines
        for routineNode in routinesNode:
            if routineNode.tag == "Routine":
                routineGoodName = self.namespace.makeValid(
                    routineNode.get('name'))
                if routineGoodName != routineNode.get('name'):
                    modifiedNames.append(routineNode.get('name'))
                self.namespace.user.append(routineGoodName)
                routine = Routine(name=routineGoodName, exp=self)
                # self._getXMLparam(params=routine.params, paramNode=routineNode)
                self.routines[routineNode.get('name')] = routine
                for componentNode in routineNode:

                    componentType = componentNode.tag
                    if componentType in allCompons:
                        # create an actual component of that type
                        component = allCompons[componentType](
                            name=componentNode.get('name'),
                            parentName=routineNode.get('name'),
                            exp=self)
                    else:
                        # create UnknownComponent instead
                        component = allCompons['UnknownComponent'](
                            name=componentNode.get('name'),
                            parentName=routineNode.get('name'),
                            exp=self)
                    # check for components that were absent in older versions of
                    # the builder and change the default behavior
                    # (currently only the new behavior of choices for RatingScale,
                    # HS, November 2012)
                    # HS's modification superceded Jan 2014, removing several
                    # RatingScale options
                    if componentType == 'RatingScaleComponent':
                        if (componentNode.get('choiceLabelsAboveLine')
                                or componentNode.get('lowAnchorText')
                                or componentNode.get('highAnchorText')):
                            pass
                        # if not componentNode.get('choiceLabelsAboveLine'):
                        #    # this rating scale was created using older version
                        #    component.params['choiceLabelsAboveLine'].val=True
                    # populate the component with its various params
                    for paramNode in componentNode:
                        self._getXMLparam(params=component.params,
                                          paramNode=paramNode,
                                          componentNode=componentNode)
                    compGoodName = self.namespace.makeValid(
                        componentNode.get('name'))
                    if compGoodName != componentNode.get('name'):
                        modifiedNames.append(componentNode.get('name'))
                    self.namespace.add(compGoodName)
                    component.params['name'].val = compGoodName
                    routine.append(component)
            else:
                if routineNode.tag in allRoutines:
                    # If not a routine, may be a standalone routine
                    routine = allRoutines[routineNode.tag](
                        exp=self, name=routineNode.get('name'))
                else:
                    # Otherwise treat as unknown
                    routine = allRoutines['UnknownRoutine'](
                        exp=self, name=routineNode.get('name'))
                # Apply all params
                for paramNode in routineNode:
                    if paramNode.tag == "Param":
                        for key, val in paramNode.items():
                            setattr(routine.params[paramNode.get("name")], key,
                                    val)
                # Add routine to experiment
                self.addStandaloneRoutine(routine.name, routine)
        # for each component that uses a Static for updates, we need to set
        # that
        for thisRoutine in list(self.routines.values()):
            for thisComp in thisRoutine:
                for thisParamName in thisComp.params:
                    thisParam = thisComp.params[thisParamName]
                    if thisParamName == 'advancedParams':
                        continue  # advanced isn't a normal param
                    elif thisParam.updates and "during:" in thisParam.updates:
                        # remove the part that says 'during'
                        updates = thisParam.updates.split(': ')[1]
                        routine, static = updates.split('.')
                        if routine not in self.routines:
                            msg = ("%s was set to update during %s Static "
                                   "Component, but that component no longer "
                                   "exists")
                            logging.warning(msg % (thisParamName, static))
                        else:
                            self.routines[routine].getComponentFromName(
                                static).addComponentUpdate(
                                    thisRoutine.params['name'],
                                    thisComp.params['name'], thisParamName)
        # fetch flow settings
        flowNode = root.find('Flow')
        loops = {}
        for elementNode in flowNode:
            if elementNode.tag == "LoopInitiator":
                loopType = elementNode.get('loopType')
                loopName = self.namespace.makeValid(elementNode.get('name'))
                if loopName != elementNode.get('name'):
                    modifiedNames.append(elementNode.get('name'))
                self.namespace.add(loopName)
                loop = eval('%s(exp=self,name="%s")' % (loopType, loopName))
                loops[loopName] = loop
                for paramNode in elementNode:
                    self._getXMLparam(paramNode=paramNode, params=loop.params)
                    # for conditions convert string rep to list of dicts
                    if paramNode.get('name') == 'conditions':
                        param = loop.params['conditions']
                        # e.g. param.val=[{'ori':0},{'ori':3}]
                        try:
                            param.val = eval('%s' % (param.val))
                        except SyntaxError:
                            # This can occur if Python2.7 conditions string
                            # contained long ints (e.g. 8L) and these can't be
                            # parsed by Py3. But allow the file to carry on
                            # loading and the conditions will still be loaded
                            # from the xlsx file
                            pass
                # get condition names from within conditionsFile, if any:
                try:
                    # psychophysicsstaircase demo has no such param
                    conditionsFile = loop.params['conditionsFile'].val
                except Exception:
                    conditionsFile = None
                if conditionsFile in ['None', '']:
                    conditionsFile = None
                if conditionsFile:
                    try:
                        trialList, fieldNames = data.importConditions(
                            conditionsFile, returnFieldNames=True)
                        for fname in fieldNames:
                            if fname != self.namespace.makeValid(fname):
                                duplicateNames.append(fname)
                            else:
                                self.namespace.add(fname)
                    except Exception:
                        pass  # couldn't load the conditions file for now
                self.flow.append(LoopInitiator(loop=loops[loopName]))
            elif elementNode.tag == "LoopTerminator":
                self.flow.append(
                    LoopTerminator(loop=loops[elementNode.get('name')]))
            else:
                if elementNode.get('name') in self.routines:
                    self.flow.append(self.routines[elementNode.get('name')])
                else:
                    logging.error("A Routine called '{}' was on the Flow but "
                                  "could not be found (failed rename?). You "
                                  "may need to re-insert it".format(
                                      elementNode.get('name')))
                    logging.flush()

        if modifiedNames:
            msg = 'duplicate variable name(s) changed in loadFromXML: %s\n'
            logging.warning(msg % ', '.join(list(set(modifiedNames))))
        if duplicateNames:
            msg = 'duplicate variable names: %s'
            logging.warning(msg % ', '.join(list(set(duplicateNames))))
        # if we succeeded then save current filename to self
        self.filename = filename