def get_output_spec(self): """ Method to access the process output specifications. Returns ------- outputs: str a string representation of all the output trait specifications. """ output = "\nOUTPUT SPECIFICATIONS\n\n" for trait_name, trait in six.iteritems(self.traits(output=True)): output += "{0}: {1}\n".format(trait_name, trait_ids(self.trait(trait_name))) return output
def get_output_spec(self): """ Method to access the process output specifications. Returns ------- outputs: str a string representation of all the output trait specifications. """ output = "\nOUTPUT SPECIFICATIONS\n\n" for trait_name, trait in self.traits(output=True).iteritems(): output += "{0}: {1}\n".format( trait_name, trait_ids(self.trait(trait_name))) return output
def get_input_spec(self): """ Method to access the process input specifications. Returns ------- outputs: str a string representation of all the input trait specifications. """ output = "\nINPUT SPECIFICATIONS\n\n" # self.traits(output=False) skips params with no output property for trait_name, trait in six.iteritems(self.user_traits()): if not trait.output: output += "{0}: {1}\n".format( trait_name, trait_ids(self.trait(trait_name))) return output
def get_input_spec(self): """ Method to access the process input specifications. Returns ------- outputs: str a string representation of all the input trait specifications. """ output = "\nINPUT SPECIFICATIONS\n\n" # self.traits(output=False) skips params with no output property for trait_name, trait in self.user_traits().iteritems(): if not trait.output: output += "{0}: {1}\n".format( trait_name, trait_ids(self.trait(trait_name))) return output
def get_control_class(cls, trait): """ Find the control associated with the input trait. The mapping is defined in the global class parameter '_defined_controls'. Parameters ---------- cls: ControllerWidget (mandatory) a ControllerWidget class trait: Trait (mandatory) a trait item Returns ------- control_class: class the control class associated with the input trait. If no match has been found, return None """ # Initilaize the output variable control_class = None # Go through the trait string description: can have multiple element # when either trait is used # Todo:: we actualy need to create all the controls and let the user # choose which one he wants to fill. for trait_id in trait_ids(trait): # Recursive construction: consider only the top level trait_id = trait_id.split("_")[0] # Try to get the control class control_class = cls._defined_controls.get(trait_id) # Stop when we have a match if control_class is not None: break return control_class
def create_control(self, trait_name, trait): """ Create a control associated to a trait. Parameters ---------- trait_name: str (mandatory) the name of the trait from which we want to create a control. The control widget will share the same name trait: Trait (mandatory) a trait item """ # Search if the current trait has already been processed control_groups = self._controls.get(trait_name) control_instances = [] control_labels = [] # If no control has been found in the class intern parameters if control_groups is None: # Call the search function that will map the trait type to the # corresponding control type control_class = self.get_control_class(trait) # If no control has been found, skip this trait and print # an error message. Note that the parameter will not be # accessible in the user interface. if control_class is None: logger.error("No control defined for trait '{0}': {1}. This " "parameter will not be accessible in the " "user interface.".format(trait_name, trait_ids(trait))) return # handle groups layouts = [] groups = trait.groups if groups: for group in groups: group_widget = self._groups.get(group) if group_widget is None: group_widget = self._create_group_widget(group) self._groups[group] = group_widget layouts.append(group_widget.hideable_widget.layout()) else: layouts.append(self._grid_layout) group = None for i, layout in enumerate(layouts): if groups: group = groups[i] control_instance, control_label \ = self._create_control_in_layout(trait_name, trait, layout, group) control_instances.append(control_instance) if control_label: if not isinstance(control_label, tuple): control_label = [control_label] control_labels += list(control_label) if isinstance(control_label[0], QtGui.QLabel): control_label[0].setTextInteractionFlags( QtCore.Qt.TextSelectableByKeyboard | QtCore.Qt.TextSelectableByMouse) # Otherwise, the control associated with the current trait name is # already inserted in the grid layout, just unpack the values # contained in the private '_controls' class parameter else: for group, control in six.iteritems(control_groups): trait, control_class, control_instance, control_label = control control_instances.append(control_instance) if control_label: if isinstance(control_label, tuple): control_labels += list(control_label) else: control_labels.append(control_label) # Each trait has a hidden property. Take care of this information hide = (getattr(trait, 'hidden', False) or getattr(trait, 'unused', False)) # Show/Hide the control and associated labels for control_instance in control_instances: control_instance.setVisible(not hide) for label in control_labels: label.setVisible(not hide)
def display_parameters(self, node_name, process, pipeline): """ Display the parameters of the selected node The node parameters are read and line labels/line edits/push buttons are created for each of them. This methods consists mainly in widget and layout organization. :param node_name: name of the node :param process: process of the node :param pipeline: current pipeline """ self.node_name = node_name self.current_process = process self.line_edit_input = [] self.line_edit_output = [] self.labels_input = [] self.labels_output = [] # Refreshing the layouts if len(self.children()) > 0: self.clearLayout(self) self.v_box_final = QVBoxLayout() # Node name label_node_name = QLabel() label_node_name.setText('Node name:') self.line_edit_node_name = QLineEdit() # The pipeline global inputs and outputs node name cannot be modified if self.node_name not in ('inputs', 'outputs'): self.line_edit_node_name.setText(self.node_name) self.line_edit_node_name.returnPressed.connect( partial(self.update_node_name, pipeline)) else: self.line_edit_node_name.setText('Pipeline inputs/outputs') self.line_edit_node_name.setReadOnly(True) self.h_box_node_name = QHBoxLayout() self.h_box_node_name.addWidget(label_node_name) self.h_box_node_name.addWidget(self.line_edit_node_name) # Inputs self.button_group_inputs = QGroupBox('Inputs') self.v_box_inputs = QVBoxLayout() idx = 0 for name, trait in process.user_traits().items(): if name == 'nodes_activation': continue if not trait.output: label_input = QLabel() label_input.setText(str(name)) self.labels_input.insert(idx, label_input) try: value = getattr(process, name) except TraitError: value = Undefined trait_type = trait_ids(process.trait(name)) self.line_edit_input.insert(idx, QLineEdit()) self.line_edit_input[idx].setText(str(value)) self.line_edit_input[idx].returnPressed.connect( partial(self.update_plug_value, 'in', name, pipeline, type(value))) h_box = QHBoxLayout() h_box.addWidget(label_input) h_box.addWidget(self.line_edit_input[idx]) # Adding the possibility to filter pipeline global # inputs except if the input is "database_scans" # which means that the scans will be filtered with InputFilter if self.node_name == "inputs" and name != "database_scans": parameters = (idx, pipeline, type(value)) push_button = QPushButton('Filter') push_button.clicked.connect( partial(self.display_filter, self.node_name, name, parameters, process)) h_box.addWidget(push_button) self.v_box_inputs.addLayout(h_box) idx += 1 self.button_group_inputs.setLayout(self.v_box_inputs) # Outputs self.button_group_outputs = QGroupBox('Outputs') self.v_box_outputs = QVBoxLayout() idx = 0 for name, trait in process.traits(output=True).items(): label_output = QLabel() label_output.setText(str(name)) self.labels_output.insert(idx, label_output) value = getattr(process, name) trait_type = trait_ids(process.trait(name)) self.line_edit_output.insert(idx, QLineEdit()) self.line_edit_output[idx].setText(str(value)) self.line_edit_output[idx].returnPressed.connect( partial(self.update_plug_value, 'out', name, pipeline, type(value))) h_box = QHBoxLayout() h_box.addWidget(label_output) h_box.addWidget(self.line_edit_output[idx]) self.v_box_outputs.addLayout(h_box) idx += 1 self.button_group_outputs.setLayout(self.v_box_outputs) self.v_box_final.addLayout(self.h_box_node_name) self.v_box_final.addWidget(self.button_group_inputs) self.v_box_final.addWidget(self.button_group_outputs) self.setLayout(self.v_box_final)
def sync_process_output_traits(process_instance, name, value): """ Event handler function to update the process instance outputs This callback is only called when an input process instance trait is modified. Parameters ---------- process_instance: process instance (mandatory) the process instance that contain the nipype interface we want to update. name: str (mandatory) the name of the trait we want to update. value: type (mandatory) the old trait value """ # Get all the input traits input_traits = process_instance.traits(output=False) output_directory \ = getattr(process_instance, 'output_directory', Undefined) # get the interface name from the process trait name trait_map = getattr(process_instance, '_nipype_trait_mapping', {}) # Try to update all the output process instance traits values when # a process instance input trait is modified or when the dedicated # 'synchronize' trait value is modified if name in input_traits or name in ("synchronize", 'output_directory'): # Try to set all the process instance output traits values from # the nipype autocompleted traits values try: nipype_outputs = ( process_instance._nipype_interface._list_outputs()) except Exception as e: # don't make it all crash because of a nipype trait assign # error print('EXCEPTION:', e, file=sys.stderr) print('while syncing nipype parameter', name, 'on', process_instance.name, file=sys.stderr) # when called during exit, the traceback module might have # already disappeared import traceback traceback.print_exc() ex_type, ex, tb = sys.exc_info() logger.debug("Something wrong in the nipype output trait " "synchronization:\n\n\tError: {0} - {1}\n" "\tTraceback:\n{2}".format( ex_type, ex, "".join(traceback.format_tb(tb)))) nipype_outputs = {} # Synchronize traits: check file existence for out_name, out_value in six.iteritems(nipype_outputs): pname = trait_map.get(out_name, '_' + out_name) try: # if we have an output directory, replace it if output_directory not in (Undefined, None) \ and any([x for x in trait_ids(process_instance.trait( pname)) if 'File' in x or 'Directory' in x]): out_value = _replace_dir(out_value, output_directory) # Set the output process trait value process_instance.set_parameter(pname, out_value) # If we can't update the output process instance traits values, # print a logging debug message. except Exception as e: print('EXCEPTION:', e, file=sys.stderr) print('while setting nipype output parameter', pname, 'on', process_instance.name, 'with value:', out_value, file=sys.stderr) import traceback traceback.print_exc() ex_type, ex, tb = sys.exc_info() logger.debug("Something wrong in the nipype output trait " "synchronization:\n\n\tError: {0} - {1}\n" "\tTraceback:\n{2}".format( ex_type, ex, "".join(traceback.format_tb(tb)))) if name in list( input_traits.keys()) + ['synchronize', 'output_directory']: names = [name] if name == 'output_directory': names = list(input_traits.keys()) for name in names: # check if the input trait is duplicated as an output trait = process_instance.trait(name) if trait.copyfile: out_trait = process_instance.trait('_modified_%s' % name) if out_trait: new_value = getattr(process_instance, name) if output_directory not in (Undefined, None): new_value = _replace_dir(new_value, output_directory) try: process_instance.set_parameter( "_modified_%s" % name, new_value) # If we can't update the output process instance # traits values, print a logging debug message. except Exception as e: print('EXCEPTION:', e) import traceback traceback.print_exc() ex_type, ex, tb = sys.exc_info() logger.debug( "Something wrong in the nipype output trait " "synchronization:\n\n\tError: {0} - {1}\n" "\tTraceback:\n{2}".format( ex_type, ex, "".join(traceback.format_tb(tb))))
def delete_list_item(controller_widget, control_name, control_instance): """ Delete the last control element Parameters ---------- controller_widget: ControllerWidget (mandatory) a controller widget that contains the controller we want to update control_name: str(mandatory) the name of the controller widget control we want to synchronize with the controller control_instance: QFrame (mandatory) the instance of the controller widget control we want to synchronize with the controller """ # Delete the last inserted control last_row = (control_instance.controller_widget._grid_layout.rowCount()) nb_of_items = control_instance.controller_widget._grid_layout.count() item_found = False index_to_remove = last_row - 1 # If the list contain at least one widget if nb_of_items > 0: # While the last inserted widget has not been found while index_to_remove >= 0 and not item_found: # Try to remove the 'index_to_remove' control row item_found, widget = ListControlWidget.delete_one_row( control_instance, index_to_remove) # If a list control has been deleted, remove the associated # tools if hasattr(widget, "controller"): # Remove the list control extra tools row ListControlWidget.delete_one_row(control_instance, index_to_remove - 1) # Get the trait name that has just been deleted from the # controller widget if item_found: trait_name = str(index_to_remove - 1) # Increment index_to_remove -= 1 # No more control to delete else: logger.debug( "No more control to delete in '{0}'.".format(control_instance)) # If one list control item has been deleted if item_found: # If the inner control is a list, convert the control index # Indeed, two elements are inserted for a list item # (tools + widget) if trait_ids(control_instance.inner_trait)[0].startswith("List_"): trait_name = str((int(trait_name) + 1) / 2 - 1) # Remove the trait from the controller control_instance.controller.remove_trait(trait_name) # Get, unpack and delete the control item control_groups \ = control_instance.controller_widget._controls[trait_name] for group, control in six.iteritems(control_groups): (inner_trait, inner_control_class, inner_control_instance, inner_control_label) = control del (control_instance.controller_widget._controls[trait_name]) # Disconnect the removed control inner_control_class.disconnect(controller_widget, trait_name, inner_control_instance) # Update the list controller if hasattr(control_instance, '_controller_connections'): control_instance._controller_connections[0]() logger.debug("Remove 'ListControlWidget' '{0}' controller and " "trait item.".format(trait_name)) control_instance.controller_widget._grid_layout.update()
def get_control_class(self, trait): """ Find the control associated with the input trait. The mapping is defined in the global class parameter '_defined_controls'. Parameters ---------- trait: Trait (mandatory) a trait item Returns ------- control_class: class the control class associated with the input trait. If no match has been found, return None """ # Initilaize the output variable control_class = None todo = [trait] done = set() while todo: trait = todo.pop(0) done.add(trait) trait_id = None ids = trait_ids(trait) if len(ids) >= 2: trait_id = 'Compound' elif len(ids) == 1: trait_id = ids[0] if trait_id is not None: control_class = self._defined_controls.get(trait_id) # Recursive construction: consider only the top level while control_class is None: split_id = trait_id.rsplit("_", 1) if len(split_id) == 1: break trait_id = split_id[0] # Try to get the control class control_class = self._defined_controls.get(trait_id) if control_class is not None: break # not found: look in superclasses bases = trait.trait_type.__class__.__bases__ \ + trait.__class__.__bases__ for base in bases: if issubclass(base, traits.TraitType): trait = self._instantiate_trait(base) todo.append(trait) if control_class is None: # fallback to a label displaying "this value cannot be seen/edited" control_class = self._defined_controls.get('unknown') if inspect.isfunction(control_class): # the function may instantiate a specialized type dynamically control_class = control_class(trait) return control_class