def configure_controller(cls): c = Controller() c.add_trait('parameters', traits.List(traits.Str())) c.add_trait('concat_plug', traits.Str()) c.add_trait('outputs', traits.List(traits.Str())) c.add_trait('param_types', traits.List(traits.Str('Str'))) return c
def configure_controller(cls): c = Controller() c.add_trait('param_type', traits.Str('Str')) c.add_trait('is_output', traits.Bool(True)) c.add_trait('test_is_output', traits.Bool(True)) c.add_trait('has_index', traits.Bool(True)) return c
def get_node_instance(node_type, pipeline, conf_dict=None, **kwargs): """ Get a custom node instance from a module + class name (see :func:`get_node_class`) and a configuration dict or Controller. The configuration contains parameters needed to instantiate the node type. Each node class may specify its parameters via its class method `configure_node`. Parameters ---------- node_type: str or Node subclass or Node instance node type to be built. Either a class (Node subclass) or a Node instance (the node will be re-instantiated), or a string describing a module and class. pipeline: Pipeline pipeline in which the node will be inserted. conf_dict: dict or Controller configuration dict or Controller defining parameters needed to build the node. The controller should be obtained using the node class's `configure_node()` static method, then filled with the desired values. If not given the node is supposed to be built with no parameters, which will not work for every node type. kwargs: default values of the node instance parameters. """ cls_and_name = get_node_class(node_type) if cls_and_name is None: raise ValueError("Could not find node class %s" % node_type) name, cls = cls_and_name if isinstance(conf_dict, Controller): conf_controller = conf_dict elif conf_dict is not None: if hasattr(cls, 'configure_controller'): conf_controller = cls.configure_controller() if conf_controller is None: raise ValueError("node type %s has a configuration controller " "problem (see %s.configure_controller()" % (node_type, node_type)) conf_controller.import_from_dict(conf_dict) else: conf_controller = Controller() else: if hasattr(cls, 'configure_controller'): conf_controller = cls.configure_controller() else: conf_controller = Controller() if hasattr(cls, 'build_node'): node = cls.build_node(pipeline, name, conf_controller) else: # probably bound to fail... node = cls(pipeline, name, [], []) # Set the instance default parameters for name, value in six.iteritems(kwargs): setattr(node, name, value) return node
def configure_controller(cls): c = Controller() c.add_trait('input_types', traits.List(traits.Str)) c.add_trait('input_names', traits.List(traits.Str)) c.add_trait('output_names', traits.List(traits.Str)) c.input_names = ['inputs'] c.output_names = ['output_%d'] c.input_types = ['File'] return c
def __init__(self, study_name=None, init_config=None, modules=None, **override_config): """ Initilize the StudyConfig class Parameters ---------- study_name: Name of the study to configure. This name is used to identify specific configuration for a study. init_config: if not None, must contain a dictionary that will be used to configure this StudyConfig (instead of reading configuration from configuration files). modules: list of string (default self.default_modules). the names of configuration module classes that will be included in this study configuration. override_config: dictionary The content of these keyword parameters will be set on the configuration after it has been initialized from configuration files (or from init_config). """ # Inheritance super(StudyConfig, self).__init__() if study_name: self.study_name = study_name # Read the configuration for the given study if init_config is None: config = self.read_configuration() config.update(override_config) else: self.global_config_file = None self.study_config_file = None if override_config: config = init_config.copy() config.update(override_config) else: config = init_config # Create modules if modules is None: # Make it possible for a study to define its own set of modules modules = config.pop('config_modules', self.default_modules) # 'modules_data' is a container for modules-specific internal data # each module is encouraged to prefix its variables there by its # module name self.modules_data = Controller() self.modules = {} for module in modules: self.load_module(module, config) # Set self attributes according to configuration values for k, v in six.iteritems(config): setattr(self, k, v) self.initialize_modules()
def get_attribute_values(self): ''' Get attributes Controller associated to a process Returns ------- attributes: Controller ''' t = self.trait('capsul_attributes') if t is None: try: pattributes = ProcessCompletionEngine.get_completion_engine( self.process.process).get_attribute_values() except AttributeError: # ProcessCompletionEngine not implemented for this process: # no completion return schemas = self._get_schemas() attributes = ProcessAttributes(self.process, schemas) self.add_trait('capsul_attributes', ControllerTrait(Controller())) self.capsul_attributes = attributes iter_attrib = self.get_iterated_attributes() for attrib, trait in six.iteritems(pattributes.user_traits()): if attrib not in iter_attrib: attributes.add_trait(attrib, trait) for attrib in iter_attrib: trait = pattributes.trait(attrib) if trait is not None: attributes.add_trait( attrib, traits.List(trait, output=trait.output)) value = getattr(pattributes, attrib, None) if value is not None and value is not traits.Undefined: setattr(attributes, attrib, [value]) return self.capsul_attributes
def test_controller(self): c1 = Controller() c1.add_trait('gogo', traits.Str()) c1.add_trait('bozo', traits.Int(12)) self.assertEqual(c1.gogo, '') self.assertEqual(c1.bozo, 12) self.assertEqual(c1.user_traits().keys(), ['gogo', 'bozo']) c1.gogo = 'blop krok' self.assertEqual(c1.gogo, 'blop krok')
def edition_widget(engine, environment): ''' Edition GUI for AFNI config - see :class:`~capsul.qt_gui.widgets.settings_editor.SettingsEditor` ''' from soma.qt_gui.controller_widget import ScrollControllerWidget from soma.controller import Controller import types import traits.api as traits def validate_config(widget): controller = widget.controller_widget.controller with widget.engine.settings as session: conf = session.config('afni', widget.environment) values = {'config_id': 'afni'} for k in ['directory']: value = getattr(controller, k) if value is traits.Undefined: value = None values[k] = value if conf is None: session.new_config('afni', widget.environment, values) else: for k, value in values.items(): if k == 'config_id': continue setattr(conf, k, values[k]) controller = Controller() controller.add_trait( 'directory', traits.Directory(traits.Undefined, desc='Directory where AFNI is installed')) conf = engine.settings.select_configurations(environment, {'afni': 'any'}) if conf: fconf = conf.get('capsul.engine.module.afni', {}) controller.directory = fconf.get('directory', traits.Undefined) widget = ScrollControllerWidget(controller, live=True) widget.engine = engine widget.environment = environment widget.accept = types.MethodType(validate_config, widget) return widget
def connect_resource(self, resource_id=None, force_reconnect=False): ''' Connect a soma-workflow computing resource. Sets the current resource to the given resource_id (transformed by get_resource_id() if None or "localhost" is given, for instance). Parameters ---------- resource_id: str (optional) resource name, may be None or "localhost". If None, the current one (study_config.somaworkflow_computing_resource) is used, or the localhost if none is configured. force_reconnect: bool (optional) if True, if an existing workflow controller is already connected, it will be disconnected (deleted) and a new one will be connected. If False, an existing controller will be reused without reconnection. Returns ------- :somaworkflow:`WorkflowController <client_API.html>` object ''' import soma_workflow.client as swclient resource_id = self.get_resource_id(resource_id, True) if force_reconnect: self.disconnect_resource(resource_id) r = self.study_config.modules_data.somaworkflow.setdefault( resource_id, Controller()) if not force_reconnect: wc = self.get_workflow_controller(resource_id) if wc is not None: return wc conf_file = self.study_config.somaworkflow_config_file if conf_file in (None, Undefined): conf_file \ = swclient.configuration.Configuration.search_config_path() login = swclient.configuration.Configuration.get_logins(conf_file).get( resource_id) config = getattr(r, 'config', None) if config is None: config = swclient.configuration.Configuration.load_from_file( config_file_path=conf_file) r.config = config password = getattr(r, 'password', None) rsa_key_pass = getattr(r, 'rsa_key_password', None) wc = swclient.WorkflowController(resource_id=resource_id, login=login, password=password, rsa_key_pass=rsa_key_pass, config=config) r.workflow_controller = wc return wc
def get_workflow_controller(self, resource_id=None): ''' Get a connected :somaworkflow:`WorkflowController <client_API.html>` for the given resource ''' resource_id = self.get_resource_id(resource_id) r = self.study_config.modules_data.somaworkflow.setdefault( resource_id, Controller()) wc = getattr(r, 'workflow_controller', None) return wc
def edition_widget(engine, environment): ''' Edition GUI for matlab config - see :class:`~capsul.qt_gui.widgets.settings_editor.SettingsEditor` ''' from soma.qt_gui.controller_widget import ScrollControllerWidget from soma.controller import Controller import types import traits.api as traits def validate_config(widget): controller = widget.controller_widget.controller with widget.engine.settings as session: conf = session.config('matlab', widget.environment) values = {'config_id': 'matlab'} if controller.executable in (None, traits.Undefined, ''): values['executable'] = None else: values['executable'] = controller.executable if conf is None: session.new_config('matlab', widget.environment, values) else: for k in ('executable', ): setattr(conf, k, values[k]) controller = Controller() controller.add_trait('executable', traits.Str(desc='Full path of the matlab executable')) conf = engine.settings.select_configurations(environment, {'matlab': 'any'}) if conf: controller.executable = conf.get('capsul.engine.module.matlab', {}).get('executable', traits.Undefined) widget = ScrollControllerWidget(controller, live=True) widget.engine = engine widget.environment = environment widget.accept = types.MethodType(validate_config, widget) return widget
def edition_widget(engine, environment): ''' Edition GUI for axon config - see :class:`~capsul.qt_gui.widgets.settings_editor.SettingsEditor` ''' from soma.qt_gui.controller_widget import ScrollControllerWidget from soma.controller import Controller import types import traits.api as traits def validate_config(widget): controller = widget.controller_widget.controller with widget.engine.settings as session: conf = session.config('axon', widget.environment) values = {'config_id': 'axon', 'user_level': controller.user_level} if controller.shared_directory in (None, traits.Undefined, ''): values['shared_directory'] = None else: values['shared_directory'] = controller.shared_directory if conf is None: session.new_config('axon', widget.environment, values) else: for k in ('shared_directory', 'user_level'): setattr(conf, k, values[k]) controller = Controller() controller.add_trait( 'shared_directory', traits.Directory(desc='Directory where BrainVisa ' 'shared data is installed')) controller.add_trait( 'user_level', traits.Int(desc='0: basic, 1: advanced, 2: expert, or more. ' 'used to display or hide some advanced features or ' 'process parameters that would be confusing to a novice ' 'user')) conf = engine.settings.select_configurations(environment, {'axon': 'any'}) if conf: controller.shared_directory = conf.get('capsul.engine.module.axon', {}).get('shared_directory', traits.Undefined) controller.user_level = conf.get('capsul.engine.module.axon', {}).get('user_level', 0) widget = ScrollControllerWidget(controller, live=True) widget.engine = engine widget.environment = environment widget.accept = types.MethodType(validate_config, widget) return widget
def disable_runtime_steps_with_existing_outputs(pipeline): ''' Disable steps in a pipeline which outputs contain existing files. This disabling is the "runtime steps disabling" one (see :py:class:`capsul.pipeline.Pipeline`), not the node disabling with activation propagation, so it doesn't affect the actual pipeline state. The aim is to prevent overwriting files which have already been processed, and to allow downstream execution of the remaining steps of the pipeline. Parameters ---------- pipeline: Pipeline (mandatory) pipeline to disbale nodes in. ''' steps = getattr(pipeline, 'pipeline_steps', Controller()) for step, trait in six.iteritems(steps.user_traits()): if not getattr(steps, step): continue # already inactive for node_name in trait.nodes: node = pipeline.nodes[node_name] if not node.enabled or not node.activated: continue # node not active anyway process = node.process for param in node.plugs: trait = process.trait(param) if trait.output and (isinstance(trait.trait_type, traits.File) or isinstance(trait.trait_type, traits.Directory)): value = getattr(process, param) if value is not None and value is not traits.Undefined \ and os.path.exists(value): # check special case when the output is also an input # (of the same node) disable = True for n, t in six.iteritems(process.user_traits()): if not t.output and (isinstance(t.trait_type, traits.File) or isinstance(t.trait_type, traits.Directory)): v = getattr(process, n) if v == value: disable = False break # found in inputs if disable: # disable step print('disable step', step, 'because of:', node_name, '.', param) setattr(steps, step, False) # no need to iterate other nodes in same step break
def get_attribute_values(self): ''' Get attributes Controller associated to a process Returns ------- attributes: Controller ''' if self.trait('capsul_attributes') is not None \ and hasattr(self, 'capsul_attributes'): return self.capsul_attributes self.add_trait('capsul_attributes', ControllerTrait(Controller())) schemas = self._get_schemas() #schemas = self.process.get_study_config().modules_data.foms.keys() self.capsul_attributes = ProcessAttributes(self.process, schemas) self.create_attributes_with_fom() return self.capsul_attributes
def set_computing_resource_password(self, resource_id, password=None, rsa_key_password=None): ''' Set credentials for a given computinf resource. Such credentials are stored in the config object, but will not be written when the config is saved in a file. They are thus non- persistant. Parameters ---------- resource_id: str (optional) password: str (optional) rsa_key_password: str (optional) ''' resource_id = self.get_resource_id(resource_id) r = self.study_config.modules_data.somaworkflow.setdefault( resource_id, Controller()) if password: r.password = password if rsa_key_password: r.rsa_key_password = rsa_key_password
def get_attribute_values(self): ''' Get attributes Controller associated to a process Returns ------- attributes: ProcessAttributes instance The default implementation does nothing for a single Process instance, and merges attributes from its children if the process is a pipeline. ''' if not self._rebuild_attributes \ and self.trait('capsul_attributes') is not None \ and hasattr(self, 'capsul_attributes'): return self.capsul_attributes schemas = self._get_schemas() study_config = self.process.get_study_config() proc_attr_cls = ProcessAttributes if 'AttributesConfig' in study_config.modules: factory = study_config.modules_data.attributes_factory names = [self.process.name] if hasattr(self.process, 'context_name'): names.insert(0, self.process.context_name) for name in names: try: proc_attr_cls = factory.get('process_attributes', name) found = True break except ValueError: pass if not hasattr(self, 'capsul_attributes'): self.add_trait('capsul_attributes', ControllerTrait(Controller())) self.capsul_attributes = proc_attr_cls(self.process, schemas) self._rebuild_attributes = False # if no specialized attributes set and process is a pipeline, # try building from children nodes if proc_attr_cls is ProcessAttributes \ and isinstance(self.process, Pipeline): attributes = self.capsul_attributes name = getattr(self.process, 'context_name', self.process.name) for node_name, node in six.iteritems(self.process.nodes): if node_name == '': continue subprocess = None if hasattr(node, 'process'): subprocess = node.process elif isinstance(node, Switch): subprocess = node if subprocess is not None: pname = '.'.join([name, node_name]) subprocess_compl = \ ProcessCompletionEngine.get_completion_engine( subprocess, pname) try: sub_attributes \ = subprocess_compl.get_attribute_values() except: try: subprocess_compl = self.__class__(subprocess) sub_attributes \ = subprocess_compl.get_attribute_values() except: continue for attribute, trait \ in six.iteritems(sub_attributes.user_traits()): if attributes.trait(attribute) is None: attributes.add_trait(attribute, trait) setattr(attributes, attribute, getattr(sub_attributes, attribute)) self._get_linked_attributes() return self.capsul_attributes
def get_attribute_values(self): if self.trait('capsul_attributes') is not None \ and hasattr(self, 'capsul_attributes'): return self.capsul_attributes self.add_trait('capsul_attributes', ControllerTrait(Controller())) capsul_attributes = ProcessAttributes(self.process, {}) self.capsul_attributes = capsul_attributes outputs = self.process._outputs schema = 'switch' # FIXME name = getattr(self.process, 'context_name', self.name) pipeline_name = '.'.join(name.split('.')[:-1]) if pipeline_name == '': pipeline_name = [] else: pipeline_name = [pipeline_name] forbidden_attributes = set( ['generated_by_parameter', 'generated_by_process']) traits_types = { str: traits.Str, unicode: traits.Str, int: traits.Int, float: traits.Float, list: traits.List } for out_name in outputs: in_name = '_switch_'.join((self.process.switch, out_name)) found = False for output, name in ((False, in_name), (True, out_name)): plug = self.process.plugs.get(name) if plug is None: continue if output: links = plug.links_to else: links = plug.links_from for link in links: node = link[2] if isinstance(node, Switch): # FIXME: just for now continue if link[0] == '': # link to the parent pipeline: don't call it to avoid # an infinite loop. # Either it will provide attributes by its own, either # we must not take them into account, so skip it. continue if hasattr(node, 'process'): process = node.process else: process = node proc_name = '.'.join(pipeline_name + [link[0]]) completion_engine \ = ProcessCompletionEngine.get_completion_engine( process, name=proc_name) attributes = completion_engine.get_attribute_values() try: param_attributes \ = attributes.get_parameters_attributes()[link[1]] except: continue if len(param_attributes) != 0 \ and len([x for x in param_attributes.keys() if x not in forbidden_attributes]) != 0: ea = EditableAttributes() for attribute, value in six.iteritems( param_attributes): if attribute not in forbidden_attributes: ttype = traits_types.get(type(value)) if ttype is not None: trait = ttype() else: trait = value ea.add_trait(attribute, ttype) setattr(ea, attribute, value) capsul_attributes.set_parameter_attributes( name, schema, ea, {}) found = True break if found: break if found: # propagate from input/output to other side ea = EditableAttributes() for attribute, value in six.iteritems(param_attributes): ttype = traits_types.get(type(value)) if ttype is not None: trait = ttype() else: trait = value ea.add_trait(attribute, ttype) setattr(ea, attribute, value) if output: capsul_attributes.set_parameter_attributes( in_name, schema, ea, {}) else: capsul_attributes.set_parameter_attributes( out_name, schema, ea, {}) self.install_switch_observer() return capsul_attributes
def configure_controller(cls): c = Controller() c.add_trait('param_type', traits.Str('Any')) return c
def edition_widget(engine, environment): ''' Edition GUI for attributes config - see :class:`~capsul.qt_gui.widgets.settings_editor.SettingsEditor` ''' from soma.qt_gui.controller_widget import ScrollControllerWidget from soma.controller import Controller import types import traits.api as traits def validate_config(widget): controller = widget.controller_widget.controller with widget.engine.settings as session: conf = session.config('attributes', widget.environment) values = {'config_id': 'attributes'} values['attributes_schema_paths'] \ = controller.attributes_schema_paths values['attributes_schemas'] = controller.attributes_schemas values['process_completion'] = controller.process_completion if controller.process_completion is traits.Undefined: values['process_completion'] = None values['path_completion'] = controller.path_completion if controller.path_completion is traits.Undefined: values['path_completion'] = None if conf is None: session.new_config('attributes', widget.environment, values) else: for k in ('attributes_schema_paths', 'attributes_schemas', 'process_completion', 'path_completion'): setattr(conf, k, values[k]) controller = Controller() controller.add_trait( 'attributes_schema_paths', traits.List(traits.Str(), desc='attributes shchemas modules names')) controller.add_trait( 'attributes_schemas', traits.DictStrStr(desc='attributes shchemas names')) controller.add_trait( 'process_completion', traits.Str(desc='process completion model name')) controller.add_trait( 'path_completion', traits.Str(desc='path completion model name', optional=True)) conf = engine.settings.select_configurations( environment, {'attributes': 'any'}) if conf: aconf = conf.get( 'capsul.engine.module.attributes', {}) controller.attributes_schema_paths = aconf.get( 'attributes_schema_paths', []) controller.attributes_schemas = aconf.get( 'attributes_schemas', {}) controller.process_completion = aconf.get( 'process_completion', 'builtin') controller.path_completion = aconf.get( 'path_completion', traits.Undefined) widget = ScrollControllerWidget(controller, live=True) widget.engine = engine widget.environment = environment widget.accept = types.MethodType(validate_config, widget) return widget
def test_controller(self): c1 = Controller() c1.add_trait('gogo', traits.Str()) c1.add_trait('bozo', traits.Int(12)) self.assertEqual(c1.gogo, '') self.assertEqual(c1.bozo, 12) self.assertEqual(c1.user_traits().keys(), ['gogo', 'bozo']) c1.gogo = 'blop krok' self.assertEqual(c1.gogo, 'blop krok') d = c1.export_to_dict() self.assertEqual(d, {'gogo': 'blop krok', 'bozo': 12}) c1.reorder_traits(['bozo', 'gogo']) self.assertEqual(c1.user_traits().keys(), ['bozo', 'gogo']) c1.reorder_traits(['gogo', 'bozo']) self.assertEqual(c1.user_traits().keys(), ['gogo', 'bozo'])
def __init__(self, attributed_process, enable_attr_from_filename=False, enable_load_buttons=False, override_control_types=None, separate_outputs=True, user_data=None, userlevel=0, scroll=True): """ Parameters ---------- attributed_process: Process instance process with attributes to be displayed enable_attr_from_filename: bool (optional) if enabled, it will be possible to specify an input filename to build attributes from override_control_types: dict (optional) if given, this is a "factory" dict assigning new controller editor types to some traits types in the parameters controller. separate_outputs: bool if True, inputs and outputs (traits with output=True set) will be separated into two boxes. user_data: any type (optional) optional user data that can be accessed by individual control editors userlevel: int the current user level: some traits may be marked with a non-zero userlevel, and will only be visible if the ControllerWidget userlevel is more than (or equal) the trait level. scroll: bool if True, the widget includes scrollbars in the parameters and attributes sections when needed, otherwise it will be a fixed size widget. """ super(AttributedProcessWidget, self).__init__() self.setLayout(QtGui.QVBoxLayout()) self.layout().setContentsMargins(0, 0, 0, 0) self.attributed_process = attributed_process self._show_completion = False self.user_data = user_data self.separate_outputs = separate_outputs self._userlevel = userlevel process = attributed_process completion_engine = getattr(process, 'completion_engine', None) if completion_engine is not None: splitter = QtGui.QSplitter(QtCore.Qt.Vertical) self.layout().addWidget(splitter) spl_up = QtGui.QWidget() spl_up.setLayout(QtGui.QVBoxLayout()) splitter.addWidget(spl_up) spl_down = QtGui.QWidget() spl_down.setLayout(QtGui.QVBoxLayout()) splitter.addWidget(spl_down) else: spl_up = self spl_down = self filename_widget = None if enable_attr_from_filename and completion_engine is not None: c = Controller() c.add_trait('attributes_from_input_filename', File(optional=True)) filename_widget = ControllerWidget(c, live=True, user_data=user_data) spl_up.layout().addWidget(filename_widget) self.input_filename_controller = c c.on_trait_change(self.on_input_filename_changed, 'attributes_from_input_filename', dispatch='ui') filename_widget.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) # groupbox area to show attributes attrib_widget = QtGui.QGroupBox('Attributes:') attrib_widget.setFlat(True) attrib_widget.setAlignment(QtCore.Qt.AlignLeft) attrib_widget.setLayout(QtGui.QVBoxLayout()) self.attrib_widget = attrib_widget spl_up.layout().addWidget(attrib_widget) attrib_widget.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred) hlay = QtGui.QHBoxLayout() spl_up.layout().addLayout(hlay) # CheckBox to completion rules or not self.checkbox_fom = QtGui.QCheckBox('Follow completion rules') self.checkbox_fom.setChecked(True) self.checkbox_fom.stateChanged.connect(self.on_use_fom_change) hlay.addWidget(self.checkbox_fom) # Button Show/Hide completion self.btn_show_completion = QtGui.QCheckBox('Show completion') self.btn_show_completion.setSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) hlay.addWidget(self.btn_show_completion) self.btn_show_completion.stateChanged.connect(self.on_show_completion) params = QtGui.QWidget() playout = QtGui.QVBoxLayout() params.setLayout(playout) if scroll: scroll_a = QtGui.QScrollArea() scroll_a.setWidgetResizable(True) scroll_a.setWidget(params) spl_up.layout().addWidget(scroll_a) scroll_a.setSizePolicy(QtGui.QSizePolicy.Preferred, QtGui.QSizePolicy.Preferred) params.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred) CWidgetClass = ScrollControllerWidget else: spl_up.layout().addWidget(params) CWidgetClass = ControllerWidget # groupbox area to show completion if separate_outputs: param_widget = QtGui.QGroupBox('Inputs:') else: param_widget = QtGui.QGroupBox('Parameters:') param_widget.setFlat(True) param_widget.setAlignment(QtCore.Qt.AlignLeft) playout.addWidget(param_widget) param_widget.setLayout(QtGui.QVBoxLayout()) param_widget.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) if separate_outputs: out_widget = QtGui.QGroupBox('Outputs:') out_widget.setFlat(True) out_widget.setAlignment(QtCore.Qt.AlignLeft) playout.addWidget(out_widget) out_widget.setLayout(QtGui.QVBoxLayout()) out_widget.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) # use concise shape for lists GUI from soma.qt_gui.controls import OffscreenListControlWidget control_types_a = {'List': OffscreenListControlWidget} control_types_p = {'List': OffscreenListControlWidget} if override_control_types: control_types_p.update(override_control_types) #ControllerWidget._defined_controls['List'] = OffscreenListControlWidget # Create controller widget for process and object_attribute sel = None if separate_outputs: sel = 'inputs' self.controller_widget = ControllerWidget(process, live=True, parent=param_widget, override_control_types=control_types_p, user_data=user_data, userlevel=userlevel, select_controls=sel) if separate_outputs: self.outputs_cwidget = ControllerWidget(process, live=True, parent=out_widget, override_control_types=control_types_p, user_data=user_data, userlevel=userlevel, select_controls='outputs') show_ce = (completion_engine is not None and len( completion_engine.get_attribute_values().user_traits()) != 0) if completion_engine is not None: self.controller_widget2 = CWidgetClass( completion_engine.get_attribute_values(), live=True, parent=attrib_widget, override_control_types=control_types_a, user_data=user_data, userlevel=userlevel) completion_engine.get_attribute_values().on_trait_change( completion_engine.attributes_changed, 'anytrait') else: self.controller_widget2 = CWidgetClass( Controller(), override_control_types=control_types_a, user_data=user_data, userlevel=userlevel) # Set controller of attributes and controller of process for each # corresponding area param_widget.layout().addWidget(self.controller_widget) if separate_outputs: out_widget.layout().addWidget(self.outputs_cwidget) attrib_widget.layout().addWidget(self.controller_widget2) if enable_load_buttons and completion_engine is not None: io_lay = QtGui.QHBoxLayout() self.layout().addLayout(io_lay) self.btn_load_json = QtGui.QPushButton('Load attributes') io_lay.addWidget(self.btn_load_json) self.btn_load_json.clicked.connect(self.on_btn_load_json) self.btn_save_json = QtGui.QPushButton('Save attributes') io_lay.addWidget(self.btn_save_json) self.btn_save_json.clicked.connect(self.on_btn_save_json) if not show_ce: if filename_widget: filename_widget.hide() attrib_widget.hide() self.checkbox_fom.hide() self.btn_show_completion.hide() if hasattr(self, 'btn_load_json'): self.btn_load_json.hide() self.btn_save_json.hide() self.show_completion(True) # hide file parts else: self.show_completion(False) # hide file parts if completion_engine is not None: completion_engine.on_trait_change( self._completion_progress_changed, 'completion_progress', dispatch='ui')
def nodes_with_existing_outputs(pipeline, exclude_inactive=True, recursive=False, exclude_inputs=True): ''' Checks nodes in a pipeline which outputs contain existing files on the filesystem. Such nodes, maybe, should not run again. Only nodes which actually produce outputs are selected this way (switches are skipped). Parameters ---------- pipeline: Pipeline (mandatory) pipeline to disbale nodes in. exclude_inactive: bool (optional) if this option is set, inactive nodes will not be checked nor returned in the list. Inactive means disabled, not active, or in a disabled runtime step. Default: True recursive: bool (optional) if this option is set, sub-pipelines will not be returned as a whole but will be parsed recursively to select individual leaf nodes. Default: False exclude_inputs: bool (optional, default: True) Some processes or pipelines have input/output files: files taken as inputs which are re-written as outputs, or may carry an input file to the outputs through a switch selection (in the case of preprocessing steps, for instance). If this option is set, such outputs which also appear in the same node inputs will not be listed in the existing outputs, so that they will not be erased by a cleaning operation, and will not prevent execution of these nodes. Returns ------- selected_nodes: dict keys: node names values: list of pairs (param_name, file_name) ''' selected_nodes = {} if exclude_inactive: steps = getattr(pipeline, 'pipeline_steps', Controller()) disabled_nodes = set() for step, trait in six.iteritems(steps.user_traits()): if not getattr(steps, step): disabled_nodes.update(trait.nodes) nodes = pipeline.nodes.items() while nodes: node_name, node = nodes.pop(0) if node_name == '' or not hasattr(node, 'process'): # main pipeline node, switch... continue if not node.enabled or not node.activated \ or (exclude_inactive and node_name in disabled_nodes): continue process = node.process if recursive and isinstance(process, Pipeline): nodes += [('%s.%s' % (node_name, new_name), new_node) for new_name, new_node in six.iteritems(process.nodes) if new_name != ''] continue plug_list = [] input_files_list = set() for plug_name, plug in six.iteritems(node.plugs): trait = process.trait(plug_name) if isinstance(trait.trait_type, traits.File) \ or isinstance(trait.trait_type, traits.Directory) \ or isinstance(trait.trait_type, traits.Any): value = getattr(process, plug_name) if isinstance(value, basestring) \ and os.path.exists(value) \ and value not in input_files_list: if plug.output: plug_list.append((plug_name, value)) elif exclude_inputs: input_files_list.add(value) if exclude_inputs: new_plug_list = [item for item in plug_list if item[1] not in input_files_list] plug_list = new_plug_list if plug_list: selected_nodes[node_name] = plug_list return selected_nodes
def __init__(self, process_with_fom, enable_attr_from_filename=False, enable_load_buttons=False): """ Parameters ---------- process_with_fom: ProcessWithFom instance process with FOM to be displayed enable_attr_from_filename: bool (optional) if enabled, it will be possible to specify an input filename to build FOM attributes from """ super(ProcessWithFomWidget, self).__init__() self.setLayout(QtGui.QVBoxLayout()) self.process_with_fom = process_with_fom if enable_attr_from_filename: c = Controller() c.add_trait('attributes_from_input_filename', File(optional=True)) cw = ControllerWidget(c, live=True) self.layout().addWidget(cw) self.input_filename_controller = c c.on_trait_change(self.on_input_filename_changed, 'attributes_from_input_filename') cw.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) #if self.process_with_fom.study_config.input_fom \ #!= self.process_with_fom.study_config.output_fom: #c.add_trait('attributes_from_output_filename', File()) #c.on_trait_change(self.on_input_filename_changed, #'attributes_from_output_filename') # groupbox area to show attributs attrib_widget = QtGui.QGroupBox('Attributes:') attrib_widget.setAlignment(QtCore.Qt.AlignLeft) attrib_widget.setLayout(QtGui.QVBoxLayout()) self.attrib_widget = attrib_widget self.layout().addWidget(attrib_widget) attrib_widget.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred) hlay = QtGui.QHBoxLayout() self.layout().addLayout(hlay) # CheckBox to foms rules or not self.checkbox_fom = QtGui.QCheckBox('Follow FOM rules') self.checkbox_fom.setChecked(True) self.checkbox_fom.stateChanged.connect(self.on_use_fom_change) hlay.addWidget(self.checkbox_fom) # Button Show/Hide completion self.btn_show_completion = QtGui.QCheckBox('Show completion') self.btn_show_completion.setSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) hlay.addWidget(self.btn_show_completion) self.btn_show_completion.stateChanged.connect(self.on_show_completion) # groupbox area to show completion param_widget = QtGui.QGroupBox('Parameters:') param_widget.setAlignment(QtCore.Qt.AlignLeft) self.layout().addWidget(param_widget) param_widget.setLayout(QtGui.QVBoxLayout()) param_widget.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) # Create controller widget for process and object_attribute process = process_with_fom.process self.controller_widget = ScrollControllerWidget(process, live=True, parent=param_widget) self.controller_widget2 = ScrollControllerWidget(self.process_with_fom, live=True, parent=attrib_widget) self.process_with_fom.on_trait_change( self.process_with_fom.attributes_changed, 'anytrait') # Set controller of attributs and controller of process for each # corresponding area param_widget.layout().addWidget(self.controller_widget) attrib_widget.layout().addWidget(self.controller_widget2) if enable_load_buttons: io_lay = QtGui.QHBoxLayout() self.layout().addLayout(io_lay) self.btn_load_json = QtGui.QPushButton('Load attributes') io_lay.addWidget(self.btn_load_json) self.btn_load_json.clicked.connect(self.on_btn_load_json) self.btn_save_json = QtGui.QPushButton('Save attributes') io_lay.addWidget(self.btn_save_json) self.btn_save_json.clicked.connect(self.on_btn_save_json) self.show_completion(False) # hide file parts
def edition_widget(engine, environment): ''' Edition GUI for SPM config - see :class:`~capsul.qt_gui.widgets.settings_editor.SettingsEditor` ''' from soma.qt_gui.controller_widget import ScrollControllerWidget from soma.controller import Controller import types import traits.api as traits def validate_config(widget): controller = widget.controller_widget.controller with widget.engine.settings as session: values = {} if controller.directory in (None, traits.Undefined, ''): values['directory'] = None else: values['directory'] = controller.directory values['standalone'] = controller.standalone values['version'] = controller.version id = 'spm%s%s' % (controller.version, '-standalone' if controller.standalone else '') values['config_id'] = id query = 'config_id == "%s"' % id conf = session.config('spm', 'global', selection=query) if conf is None: session.new_config('spm', widget.environment, values) else: for k in ('directory', 'standalone', 'version'): setattr(conf, k, values[k]) controller = Controller() controller.add_trait( "directory", traits.Directory(traits.Undefined, output=False, desc="Directory containing SPM.")) controller.add_trait( "standalone", traits.Bool(True, desc="If True, use the standalone version of SPM.")) controller.add_trait( 'version', traits.Str(traits.Undefined, output=False, desc='Version string for SPM: "12", "8", etc.')) conf = engine.settings.select_configurations(environment, {'spm': 'any'}) if conf: controller.directory = conf.get('capsul.engine.module.spm', {}).get('directory', traits.Undefined) controller.standalone = conf.get('capsul.engine.module.spm', {}).get('standalone', True) controller.version = conf.get('capsul.engine.module.spm', {}).get('version', '12') # TODO handle several configs widget = ScrollControllerWidget(controller, live=True) widget.engine = engine widget.environment = environment widget.accept = types.MethodType(validate_config, widget) return widget
def __init__(self, attributed_process, enable_attr_from_filename=False, enable_load_buttons=False): """ Parameters ---------- attributed_process: Process instance process with attributes to be displayed enable_attr_from_filename: bool (optional) if enabled, it will be possible to specify an input filename to build attributes from """ super(AttributedProcessWidget, self).__init__() self.setLayout(QtGui.QVBoxLayout()) self.attributed_process = attributed_process self._show_completion = False process = attributed_process completion_engine = getattr(process, 'completion_engine', None) if completion_engine is not None: splitter = QtGui.QSplitter(QtCore.Qt.Vertical) self.layout().addWidget(splitter) spl_up = QtGui.QWidget() spl_up.setLayout(QtGui.QVBoxLayout()) splitter.addWidget(spl_up) spl_down = QtGui.QWidget() spl_down.setLayout(QtGui.QVBoxLayout()) splitter.addWidget(spl_down) else: spl_up = self spl_down = self if enable_attr_from_filename and completion_engine is not None: c = Controller() c.add_trait('attributes_from_input_filename', File(optional=True)) cw = ControllerWidget(c, live=True) spl_up.layout().addWidget(cw) self.input_filename_controller = c c.on_trait_change(self.on_input_filename_changed, 'attributes_from_input_filename', dispatch='ui') cw.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Fixed) # groupbox area to show attributs attrib_widget = QtGui.QGroupBox('Attributes:') attrib_widget.setAlignment(QtCore.Qt.AlignLeft) attrib_widget.setLayout(QtGui.QVBoxLayout()) self.attrib_widget = attrib_widget spl_up.layout().addWidget(attrib_widget) attrib_widget.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Preferred) hlay = QtGui.QHBoxLayout() spl_up.layout().addLayout(hlay) # CheckBox to completion rules or not self.checkbox_fom = QtGui.QCheckBox('Follow completion rules') self.checkbox_fom.setChecked(True) self.checkbox_fom.stateChanged.connect(self.on_use_fom_change) hlay.addWidget(self.checkbox_fom) # Button Show/Hide completion self.btn_show_completion = QtGui.QCheckBox('Show completion') self.btn_show_completion.setSizePolicy(QtGui.QSizePolicy.Fixed, QtGui.QSizePolicy.Fixed) hlay.addWidget(self.btn_show_completion) self.btn_show_completion.stateChanged.connect(self.on_show_completion) # groupbox area to show completion param_widget = QtGui.QGroupBox('Parameters:') param_widget.setAlignment(QtCore.Qt.AlignLeft) spl_down.layout().addWidget(param_widget) param_widget.setLayout(QtGui.QVBoxLayout()) param_widget.setSizePolicy(QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) # Create controller widget for process and object_attribute self.controller_widget = ScrollControllerWidget(process, live=True, parent=param_widget) if completion_engine is not None: self.controller_widget2 = ScrollControllerWidget( completion_engine.get_attribute_values(), live=True, parent=attrib_widget) completion_engine.get_attribute_values().on_trait_change( completion_engine.attributes_changed, 'anytrait') else: self.controller_widget2 = ScrollControllerWidget(Controller()) # Set controller of attributs and controller of process for each # corresponding area param_widget.layout().addWidget(self.controller_widget) attrib_widget.layout().addWidget(self.controller_widget2) if enable_load_buttons and completion_engine is not None: io_lay = QtGui.QHBoxLayout() self.layout().addLayout(io_lay) self.btn_load_json = QtGui.QPushButton('Load attributes') io_lay.addWidget(self.btn_load_json) self.btn_load_json.clicked.connect(self.on_btn_load_json) self.btn_save_json = QtGui.QPushButton('Save attributes') io_lay.addWidget(self.btn_save_json) self.btn_save_json.clicked.connect(self.on_btn_save_json) if completion_engine is None: attrib_widget.hide() self.checkbox_fom.hide() self.btn_show_completion.hide() self.show_completion(True) # hide file parts else: self.show_completion(False) # hide file parts if completion_engine is not None: completion_engine.on_trait_change( self._completion_progress_changed, 'completion_progress', dispatch='ui')
def edition_widget(engine, environment): ''' Edition GUI for FSL config - see :class:`~capsul.qt_gui.widgets.settings_editor.SettingsEditor` ''' from soma.qt_gui.controller_widget import ScrollControllerWidget from soma.controller import Controller import types import traits.api as traits def validate_config(widget): controller = widget.controller_widget.controller with widget.engine.settings as session: conf = session.config('fsl', widget.environment) values = {'config_id': 'fsl'} for k in ('directory', 'config', 'prefix'): value = getattr(controller, k) if value is traits.Undefined: value = None values[k] = value if conf is None: session.new_config('fsl', widget.environment, values) else: for k, value in values.items(): if k == 'config_id': continue setattr(conf, k, values[k]) controller = Controller() controller.add_trait( 'directory', traits.Directory(traits.Undefined, desc='Directory where FSL is installed')) controller.add_trait( 'config', traits.File(traits.Undefined, output=False, desc='Parameter to specify the fsl.sh path')) controller.add_trait( 'prefix', traits.String(traits.Undefined, desc='Prefix to add to FSL commands')) conf = engine.settings.select_configurations(environment, {'fsl': 'any'}) if conf: fconf = conf.get('capsul.engine.module.fsl', {}) controller.directory = fconf.get('directory', traits.Undefined) controller.config = fconf.get('config', traits.Undefined) controller.prefix = fconf.get('prefix', traits.Undefined) widget = ScrollControllerWidget(controller, live=True) widget.engine = engine widget.environment = environment widget.accept = types.MethodType(validate_config, widget) return widget
def nodes_with_missing_inputs(pipeline, recursive=True): ''' Checks nodes in a pipeline which inputs contain invalid inputs. Inputs which are files non-existing on the filesystem (so, which cannot run), or have missing mandatory inputs, or take as input a temporary file which should be the output from another disabled node, are recorded. Parameters ---------- pipeline: Pipeline (mandatory) pipeline to disbale nodes in. recursive: bool (optional) if this option is set, sub-pipelines will not be returned as a whole but will be parsed recursively to select individual leaf nodes. Note that if not set, a pipeline is regarded as a process, but pipelines may not use all their inputs/outputs so the result might be inaccurate. Default: True Returns ------- selected_nodes: dict keys: node names values: list of pairs (param_name, file_name) ''' selected_nodes = {} steps = getattr(pipeline, 'pipeline_steps', Controller()) disabled_nodes = set() for step, trait in six.iteritems(steps.user_traits()): if not getattr(steps, step): disabled_nodes.update( [pipeline.nodes[node_name] for node_name in trait.nodes]) nodes = pipeline.nodes.items() while nodes: node_name, node = nodes.pop(0) if node_name == '' or not hasattr(node, 'process'): # main pipeline node, switch... continue if not node.enabled or not node.activated or node in disabled_nodes: continue process = node.process if recursive and isinstance(process, Pipeline): nodes += [('%s.%s' % (node_name, new_name), new_node) for new_name, new_node in six.iteritems(process.nodes) if new_name != ''] continue for plug_name, plug in six.iteritems(node.plugs): if not plug.output: trait = process.trait(plug_name) if isinstance(trait.trait_type, traits.File) \ or isinstance(trait.trait_type, traits.Directory): value = getattr(process, plug_name) keep_me = False if value is None or value is traits.Undefined \ or value == '' or not os.path.exists(value): # check where this file comes from origin_node, origin_param, origin_parent \ = where_is_plug_value_from(plug, recursive) if origin_node is not None \ and (origin_node in disabled_nodes or origin_parent in disabled_nodes): # file coming from another disabled node #if not value or value is traits.Undefined: ## temporary one #print('TEMP: %s.%s' % (node_name, plug_name)) #value = None keep_me = True elif origin_node is None: # unplugged: does not come from anywhere else if (value is not traits.Undefined and value) \ or not plug.optional: # non-empty value, non-existing file # or mandatory, empty value keep_me = True # the rest is a plugged input from another process, # or an optional empty value if keep_me: plug_list = selected_nodes.setdefault(node_name, []) plug_list.append((plug_name, value)) return selected_nodes
def __init__(self, study_name=None, init_config=None, modules=None, engine=None, **override_config): """ Initialize the StudyConfig class Parameters ---------- study_name: Name of the study to configure. This name is used to identify specific configuration for a study. init_config: if not None, must contain a dictionary that will be used to configure this StudyConfig (instead of reading configuration from configuration files). modules: list of string (default self.default_modules). the names of configuration module classes that will be included in this study configuration. engine: CapsulEngine this parameter is temporary, it just helps to handle the transition to :class:`capsul.engine.CapsulEngine`. Don't use it in client code. override_config: dictionary The content of these keyword parameters will be set on the configuration after it has been initialized from configuration files (or from init_config). """ super(StudyConfig, self).__init__() if study_name: self.study_name = study_name if engine is None: from capsul.engine import capsul_engine self.engine = capsul_engine() self.engine.study_config = weakref.proxy(self) else: self.engine = weakref.proxy(engine) # Read the configuration for the given study if init_config is None: config = self.read_configuration() config.update(override_config) else: self.global_config_file = None self.study_config_file = None if override_config: config = init_config.copy() config.update(override_config) else: config = init_config self.visible_groups = set(['study']) # Create modules if modules is None: # Make it possible for a study to define its own set of modules modules = config.pop('config_modules', self.default_modules) # 'modules_data' is a container for modules-specific internal data # each module is encouraged to prefix its variables there by its # module name self.modules_data = Controller() self.modules = {} for module in modules: self.load_module(module, config) # Set self attributes according to configuration values for k, v in six.iteritems(config): setattr(self, k, v) self.initialize_modules() self.run_lock = threading.RLock() self.run_interruption_request = False
def edit_elements(controller_widget, control_instance, edit_button): """ Callback to view/edit a 'ListControlWidget'. Parameters ---------- control_instance: QFrame (mandatory) the list widget item edit_button: QToolButton the signal sender """ controller_widget = get_ref(controller_widget) widget = QtGui.QDialog(controller_widget) widget.setModal(True) layout = QtGui.QVBoxLayout() widget.setLayout(layout) #hlayout = QtGui.QHBoxLayout() #layout.addLayout(hlayout) temp_controller = Controller() trait = control_instance.trait temp_controller.add_trait(control_instance.trait_name, trait) if temp_controller.trait(control_instance.trait_name).groups \ is not None: temp_controller.trait(control_instance.trait_name).groups = None value = getattr(controller_widget.controller, control_instance.trait_name) try: setattr(temp_controller, control_instance.trait_name, value) except Exception: # invalid value - don't prevent using the GUI pass control_types = dict(controller_widget._defined_controls) control_types['List'] = ListControlWidget control_types['List_File'] = ListControlWidget temp_controller_widget = ScrollControllerWidget( temp_controller, live=True, override_control_types=control_types, user_data=control_instance.user_data) layout.addWidget(temp_controller_widget) hlayout2 = QtGui.QHBoxLayout() layout.addLayout(hlayout2) hlayout2.addStretch(1) ok = QtGui.QPushButton('OK') cancel = QtGui.QPushButton('Cancel') hlayout2.addWidget(ok) hlayout2.addWidget(cancel) ok.pressed.connect(widget.accept) cancel.pressed.connect(widget.reject) if widget.exec_(): ctrl = temp_controller_widget.controller_widget._controls.get( control_instance.trait_name)[None] ListControlWidget.validate_all_values( temp_controller_widget.controller_widget, ctrl[2]) new_trait_value = getattr(temp_controller, control_instance.trait_name) setattr(controller_widget.controller, control_instance.trait_name, new_trait_value) del temp_controller_widget
def edition_widget(engine, environment): ''' Edition GUI for FOM config - see :class:`~capsul.qt_gui.widgets.settings_editor.SettingsEditor` ''' from soma.qt_gui.controller_widget import ScrollControllerWidget from soma.controller import Controller import types def validate_config(widget): controller = widget.controller_widget.controller with widget.engine.settings as session: conf = session.config('fom', widget.environment) values = {'config_id': 'fom'} for k in ('input_fom', 'output_fom', 'shared_fom', 'volumes_format', 'meshes_format', 'auto_fom', 'fom_path', 'input_directory', 'output_directory'): value = getattr(controller, k) if value is traits.Undefined: if k in ('fom_path', ): value = [] else: value = None values[k] = value if conf is None: session.new_config('fom', widget.environment, values) else: for k, value in values.items(): if k == 'config_id': continue setattr(conf, k, values[k]) controller = Controller() controller.add_trait( 'input_fom', traits.Str(traits.Undefined, output=False, desc='input FOM')) controller.add_trait( 'output_fom', traits.Str(traits.Undefined, output=False, desc='output FOM')) controller.add_trait( 'shared_fom', traits.Str(traits.Undefined, output=False, desc='shared data FOM')) controller.add_trait( 'volumes_format', traits.Str(traits.Undefined, output=False, desc='Format used for volumes')) controller.add_trait( 'meshes_format', traits.Str(traits.Undefined, output=False, desc='Format used for meshes')) controller.add_trait( 'auto_fom', traits.Bool( True, output=False, desc='Look in all FOMs when a process is not found (in ' 'addition to the standard share/foms). Note that auto_fom ' 'looks for the first FOM matching the process to get ' 'completion for, and does not handle ambiguities. Moreover ' 'it brings an overhead (typically 6-7 seconds) the first ' 'time it is used since it has to parse all available FOMs.')) controller.add_trait( 'fom_path', traits.List( traits.Directory(output=False), desc='list of additional directories where to look for FOMs')) # FIXME: until directories are included in another config module controller.add_trait( 'input_directory', traits.Directory(traits.Undefined, output=False, desc='input study data directory')) controller.add_trait( 'output_directory', traits.Directory(traits.Undefined, output=False, desc='output study data directory')) conf = engine.settings.select_configurations(environment, {'fom': 'any'}) if conf: fconf = conf.get('capsul.engine.module.fom', {}) controller.input_fom = fconf.get('input_fom', traits.Undefined) controller.output_fom = fconf.get('output_fom', traits.Undefined) controller.shared_fom = fconf.get('shared_fom', traits.Undefined) controller.volumes_format = fconf.get('volumes_format', traits.Undefined) controller.meshes_format = fconf.get('meshes_format', traits.Undefined) controller.auto_fom = fconf.get('auto_fom', traits.Undefined) controller.fom_path = fconf.get('fom_path', traits.Undefined) controller.input_directory = fconf.get('input_directory', traits.Undefined) controller.output_directory = fconf.get('output_directory', traits.Undefined) widget = ScrollControllerWidget(controller, live=True) widget.engine = engine widget.environment = environment widget.accept = types.MethodType(validate_config, widget) return widget