Source code for capsul.qt_gui.widgets.attributed_process_widget

# -*- coding: utf-8 -*-
'''
Process or pipeline parameters view with attributes handling.

Classes
=======
:class:`AttributedProcessWidget`
--------------------------------
'''

from __future__ import print_function

from __future__ import absolute_import
import json
import six
from soma.qt_gui import qt_backend
from soma.qt_gui.qt_backend import QtGui, QtCore
from soma.controller import Controller
from soma.qt_gui.controller_widget \
    import ControllerWidget, ScrollControllerWidget
from traits.api import File, HasTraits, Any, Directory, Undefined, List


[docs] class AttributedProcessWidget(QtGui.QWidget): """Process interface with attributes completion handling""" 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 __del__(self): completion_engine = getattr(self.attributed_process, 'completion_engine', None) if completion_engine is not None: completion_engine.get_attribute_values().on_trait_change( completion_engine.attributes_changed, 'anytrait', remove=True) completion_engine.on_trait_change( self._completion_progress_changed, 'completion_progress', remove=True) @property def userlevel(self): return getattr(self, '_userlevel', 0) @userlevel.setter def userlevel(self, value): self._userlevel = value cw = getattr(self, 'controller_widget', None) if cw: cw.userlevel = value cw = getattr(self, 'outputs_cwidget', None) if cw: cw.userlevel = value cw = getattr(self, 'controller_widget2', None) if cw: cw.userlevel = value # re-hide file params if needed self.show_completion(self._show_completion)
[docs] def on_input_filename_changed(self, text): ''' Input file path to guess completion attributes changed: update attributes ''' completion_engine = getattr(self.attributed_process, 'completion_engine', None) if completion_engine is not None: print('set attributes from path:', text) try: completion_engine.path_attributes(six.text_type(text)) except ValueError as e: print(e) import traceback traceback.print_stack()
[docs] def on_btn_load_json(self): """Load attributes from a json file""" completion_engine = getattr(self.attributed_process, 'completion_engine', None) if completion_engine is None: print('No completion engine with attributes in this process.') return # ask for a file name filename = qt_backend.getOpenFileName( self, 'Select a .json attributes file', '', 'JSON files (*.json)') if filename is None: return print('load', filename) attributes = json.load(open(filename)) print("loaded:", attributes) completion_engine.get_attribute_values().import_from_dict(attributes)
[docs] def on_btn_save_json(self): """Save attributes in a json file""" completion_engine = getattr(self.attributed_process, 'completion_engine', None) if completion_engine is None: print('No attributes in this process.') return # ask for a file name filename = qt_backend.getSaveFileName( self, 'Select a .json attributes file', '', 'JSON files (*.json)') if filename is None: return json.dump(completion_engine.get_attribute_values().export_to_dict(), open(filename, 'w'))
[docs] def set_use_fom(self): ''' Setup the FOM doing its job ''' ret = QtGui.QMessageBox.critical(self, "Critical", 'Going back to completion rules will reset all path files. ' 'Are you sure?', QtGui.QMessageBox.Ok, QtGui.QMessageBox.Cancel) if ret == QtGui.QMessageBox.Ok: #reset attributes and trait of process process = self.attributed_process completion_engine = getattr(self.attributed_process, 'completion_engine', None) if completion_engine is None: return completion_engine.get_attribute_values().on_trait_change( completion_engine.attributes_changed, 'anytrait') try: # WARNING: is it necessary to reset all this ? # create_completion() will do the job anyway ? #for name, trait in six.iteritems(process.user_traits()): #if trait.is_trait_type(File) \ #or trait.is_trait_type(Directory): #setattr(process,name, Undefined) completion_engine.complete_parameters() if hasattr(self, 'input_filename_controller') \ and self.input_filename_controller. \ attributes_from_input_filename \ != '': completion_engine.path_attributes( self.input_filename_controller.attributes_from_input_filename) except Exception as e: print(e) import traceback traceback.print_stack() self.attrib_widget.show() else: # reset it in a timer callback, otherwise the checkbox state is not # correctly recorded, and next time its state change will not # trigger the on_use_fom_change slot. QtCore.QTimer.singleShot(0, self._reset_fom_checkbox)
def _reset_fom_checkbox(self): self.checkbox_fom.setChecked(False)
[docs] def on_use_fom_change(self, state): ''' Use completion checkbox callback ''' if state == QtCore.Qt.Checked: self.set_use_fom() else: self.attrib_widget.hide() completion_engine = getattr(self.attributed_process, 'completion_engine', None) if completion_engine is not None: completion_engine.get_attribute_values().on_trait_change( completion_engine.attributes_changed, 'anytrait', remove=True) self.btn_show_completion.setChecked(True)
[docs] def show_completion(self, visible=None): ''' Show or hide completion (File, Directory, or Any parameters) Parameters ---------- visible: bool (optional) show/hide. If None, switch the current visibility state. ''' if visible is None: visible = not self._show_completion self._show_completion = visible cwidgets = [self.controller_widget] if self.separate_outputs: cwidgets.append(self.outputs_cwidget) for controller_widget in cwidgets: for control_name, control_groups in \ six.iteritems( controller_widget._controls): for group, control in six.iteritems(control_groups): trait, control_class, control_instance, control_label \ = control if not isinstance(trait.trait_type, (File, Any, Directory)) \ and (not isinstance(trait.trait_type, List) or not isinstance( trait.inner_traits[0].trait_type, (File, Directory, Any))): continue if trait.forbid_completion: # when completion is disable, parameters are always # visible is_visible = True else: hidden = trait.hidden \ or (trait.userlevel is not None and trait.userlevel > self.userlevel) is_visible = visible and not hidden control_instance.setVisible(is_visible) if isinstance(control_label, tuple): for cl in control_label: cl.setVisible(is_visible) else: control_label.setVisible(is_visible) for group, group_widget in six.iteritems( controller_widget._groups): if [x for x in group_widget.hideable_widget.children() if isinstance(x, QtGui.QWidget) and not x.isHidden()]: group_widget.show() else: group_widget.hide()
[docs] def on_show_completion(self, visible): ''' Toggle the visibility of paths parameters ''' self.show_completion(visible)
def _completion_progress_changed(self, obj, name, old, new): completion_engine = getattr(self.attributed_process, 'completion_engine', None) if completion_engine is not None: if not hasattr(self, 'progressdialog'): self.progressdialog = QtGui.QWidget() self.layout().insertWidget(1, self.progressdialog) layout = QtGui.QHBoxLayout() self.progressdialog.setLayout(layout) layout.addWidget(QtGui.QLabel('Completion progress:')) self.progressbar = QtGui.QProgressBar() layout.addWidget(self.progressbar) self.progressbar.setRange(0, 100) value = int(round(100 * completion_engine.completion_progress / completion_engine.completion_progress_total)) self.progressbar.setValue(value) if value != 100: self.progressdialog.show() QtGui.qApp.processEvents() else: self.progressdialog.hide()