Source code for populse_mia.user_interface.pipeline_manager.type_editors

# -*- coding: utf-8 -*-
"""
Define the Mia logger.

The soma control classes are overloaded for the needs of Mia.

:Contains:
    :Class:
        - PopulseFileControlWidget
        - PopulseDirectoryControlWidget
        - PopulseOffscreenListFileControlWidget
        - PopulseUndefinedControlWidget

"""

###############################################################################
# Populse_mia - Copyright (C) IRMaGe/CEA, 2018
# Distributed under the terms of the CeCILL license, as published by
# the CEA-CNRS-INRIA. Refer to the LICENSE file or to
# http://www.cecill.info/licences/Licence_CeCILL_V2.1-en.html
# for details.
###############################################################################

import logging
import os
from functools import partial

import six
import traits.api as traits
from soma.qt_gui.controls.Directory import DirectoryControlWidget
from soma.qt_gui.controls.File import FileControlWidget
from soma.qt_gui.controls.List_File_offscreen import (
    OffscreenListFileControlWidget,
)
from soma.qt_gui.qt_backend import Qt, QtGui, QtWidgets
from soma.utils.weak_proxy import weak_proxy

logger = logging.getLogger(__name__)


[docs] class PopulseFileControlWidget(FileControlWidget): """Control to enter a file. :Contains: :Method: - create_widget: method to create the file widget - filter_clicked: display a filter widget - update_plug_value_from_filter: update the plug value from a filter result """
[docs] @staticmethod def create_widget( parent, control_name, control_value, trait, label_class=None, user_data=None, ): """Method to create the file widget. Parameters ---------- parent: QWidget (mandatory) the parent widget control_name: str (mandatory) the name of the control we want to create control_value: str (mandatory) the default control value trait: Tait (mandatory) the trait associated to the control label_class: Qt widget class (optional, default: None) the label widget will be an instance of this class. Its constructor will be called using 2 arguments: the label string and the parent widget. Returns ------- out: 2-uplet a two element tuple of the form (control widget: QWidget with two elements, a QLineEdit in the 'path' parameter and a browse button in the 'browse' parameter, associated label: QLabel) """ # Create the widget that will be used to select a file widget, label = FileControlWidget.create_widget( parent, control_name, control_value, trait, label_class=label_class, user_data=user_data, ) if user_data is None: user_data = {} widget.user_data = user_data # regular File does not store data layout = widget.layout() project = user_data.get("project") scan_list = user_data.get("scan_list") connected_inputs = user_data.get("connected_inputs", set()) def is_number(x): """Check if x is a number. Parameters ---------- x: the name of the control we want to create (str) Returns ------- out: bool True if the control name is a number, False otherwise """ try: int(x) return True except ValueError: return False # files in a list don't get a Filter button. if ( project and scan_list and not trait.output and control_name not in connected_inputs and not is_number(control_name) ): # Create a browse button button = Qt.QPushButton("Filter", widget) button.setObjectName("filter_button") button.setStyleSheet( "QPushButton#filter_button " "{padding: 2px 10px 2px 10px; margin: 0px;}" ) layout.addWidget(button) widget.filter_b = button # Set a callback on the browse button control_class = parent.get_control_class(trait) node_name = getattr(parent.controller, "name", None) if node_name is None: node_name = parent.controller.__class__.__name__ browse_hook = partial( control_class.filter_clicked, weak_proxy(widget), node_name, control_name, ) widget.filter_b.clicked.connect(browse_hook) return (widget, label)
[docs] @staticmethod def filter_clicked(widget, node_name, plug_name): """Display a filter widget. :param node_name: name of the node :param plug_name: name of the plug """ # this import is not at the beginning of the file to avoid a cyclic # import issue. # fmt: off # isort: off from populse_mia.user_interface.pipeline_manager.\ node_controller import PlugFilter # isort: on # fmt: on project = widget.user_data.get("project") scan_list = widget.user_data.get("scan_list") main_window = widget.user_data.get("main_window") node_controller = widget.user_data.get("node_controller") widget.pop_up = PlugFilter( project, scan_list, None, # (process) node_name, plug_name, node_controller, main_window, ) widget.pop_up.setWindowModality(Qt.Qt.WindowModal) widget.pop_up.show() widget.pop_up.plug_value_changed.connect( partial( PopulseFileControlWidget.update_plug_value_from_filter, widget, plug_name, ) )
[docs] @staticmethod def update_plug_value_from_filter(widget, plug_name, filter_res_list): """Update the plug value from a filter result. :param plug_name: name of the plug :param filter_res_list: list of the filtered files """ # If the list contains only one element, setting # this element as the plug value len_list = len(filter_res_list) if len_list == 1: res = filter_res_list[0] else: res = traits.Undefined if len_list > 1: msg = QtWidgets.QMessageBox() msg.setText( "The '{0}' parameter must by a filename, " "but a value of {1} <class 'list'> was " "specified.".format(plug_name, filter_res_list) ) msg.setIcon(QtWidgets.QMessageBox.Warning) msg.setWindowTitle("TraitError") msg.exec_() res = traits.Undefined # Set the selected file path to the path sub control widget.path.set_value(six.text_type(res))
[docs] class PopulseDirectoryControlWidget(DirectoryControlWidget): """Control to enter a Directory. :Contains: :Method: - create_widget: method to create the file widget - filter_clicked: display a filter widget - update_plug_value_from_filter: update the plug value from a filter result """
[docs] @staticmethod def create_widget( parent, control_name, control_value, trait, label_class=None, user_data=None, ): """Method to create the directory widget.""" return PopulseFileControlWidget.create_widget( parent, control_name, control_value, trait, label_class=label_class, user_data=user_data, )
[docs] @staticmethod def filter_clicked(widget, node_name, plug_name): """Display a filter widget. :param node_name: name of the node :param plug_name: name of the plug """ # this import is not at the beginning of the file to avoid a cyclic # import issue. # fmt: off # isort: off from populse_mia.user_interface.pipeline_manager.\ node_controller import PlugFilter # fmt: on # isort: on project = widget.user_data.get("project") scan_list = widget.user_data.get("scan_list") main_window = widget.user_data.get("main_window") node_controller = widget.user_data.get("node_controller") widget.pop_up = PlugFilter( project, scan_list, None, # (process) node_name, plug_name, node_controller, main_window, ) widget.pop_up.show() widget.pop_up.plug_value_changed.connect( partial( PopulseDirectoryControlWidget.update_plug_value_from_filter, widget, plug_name, ) )
[docs] @staticmethod def update_plug_value_from_filter(widget, plug_name, filter_res_list): """Update the plug value from a filter result. :param plug_name: name of the plug :param filter_res_list: list of the filtered files """ # If the list contains only one element, setting # this element as the plug value len_list = len(filter_res_list) if len_list >= 1: res = six.text_type(filter_res_list[0]) if not os.path.isdir(res): res = os.path.dirname(res) else: res = traits.Undefined # Set the selected file path to the path sub control widget.path.setText(six.text_type(res))
[docs] class PopulseOffscreenListFileControlWidget(OffscreenListFileControlWidget): """Control to enter a list of files. :Contains: :Method: - create_widget: method to create the list of files widget - filter_clicked: display a filter widget - update_plug_value_from_filter: update the plug value from a filter result """
[docs] @staticmethod def create_widget( parent, control_name, control_value, trait, label_class=None, user_data=None, ): """Method to create the list of files widget. Parameters ---------- parent: QWidget (mandatory) the parent widget control_name: str (mandatory) the name of the control we want to create control_value: list of items (mandatory) the default control value trait: Tait (mandatory) the trait associated to the control label_class: Qt widget class (optional, default: None) the label widget will be an instance of this class. Its constructor will be called using 2 arguments: the label string and the parent widget. Returns ------- out: 2-uplet a two element tuple of the form (control widget: , associated labels: (a label QLabel, the tools QWidget)) """ widget, label = OffscreenListFileControlWidget.create_widget( parent, control_name, control_value, trait, label_class=label_class, user_data=user_data, ) layout = widget.layout() project = user_data.get("project") scan_list = user_data.get("scan_list") connected_inputs = user_data.get("connected_inputs", set()) if ( project and scan_list and not trait.output and control_name not in connected_inputs ): # Create a browse button button = Qt.QPushButton("Filter", widget) button.setObjectName("filter_button") button.setStyleSheet( "QPushButton#filter_button " "{padding: 2px 10px 2px 10px; margin: 0px;}" ) layout.addWidget(button) widget.filter_b = button # Set a callback on the browse button control_class = parent.get_control_class(trait) node_name = getattr(parent.controller, "name", None) if node_name is None: node_name = parent.controller.__class__.__name__ browse_hook = partial( control_class.filter_clicked, weak_proxy(widget), node_name, control_name, ) # parameters, process) widget.filter_b.clicked.connect(browse_hook) return (widget, label)
[docs] @staticmethod def filter_clicked(widget, node_name, plug_name): """Display a filter widget. :param node_name: name of the node :param plug_name: name of the plug """ # this import is not at the beginning of the file to avoid a cyclic # import issue. # fmt: off # isort: off from populse_mia.user_interface.pipeline_manager.\ node_controller import PlugFilter # isort: on # fmt: on project = widget.user_data.get("project") scan_list = widget.user_data.get("scan_list") main_window = widget.user_data.get("main_window") node_controller = widget.user_data.get("node_controller") widget.pop_up = PlugFilter( project, scan_list, None, # (process) node_name, plug_name, node_controller, main_window, ) widget.pop_up.show() # fmt: off widget.pop_up.plug_value_changed.connect( partial( ( PopulseOffscreenListFileControlWidget. update_plug_value_from_filter ), widget, plug_name, ) )
# fmt: on
[docs] @staticmethod def update_plug_value_from_filter(widget, plug_name, filter_res_list): """Update the plug value from a filter result. :param plug_name: name of the plug :param filter_res_list: list of the filtered files """ controller = widget.parent().controller try: setattr(controller, plug_name, filter_res_list) except Exception as e: print(e)
# controller_widget.ControllerWidget._defined_controls['File'] \ # = PopulseFileControlWidget # controller_widget.ControllerWidget._defined_controls['Directory'] \ # = PopulseDirectoryControlWidget
[docs] class PopulseUndefinedControlWidget(object): """Control for Undefined value."""
[docs] @staticmethod def is_valid(control_instance, *args, **kwargs): """Method to check if the new control value is correct. Parameters ---------- control_instance: QWidget (mandatory) the control widget we want to validate Returns ------- out: bool True if the control value is Undefined, False otherwise """ # Get the control current value control_text = control_instance.text() is_valid = False if control_text in [ "<undefined>", "<style>background-color: gray; " "text-color: red;</style><undefined>", ]: is_valid = True return is_valid
[docs] @classmethod def check(cls, control_instance): """Check if a controller widget control is filled correctly. Parameters ---------- cls: StrControlWidget (mandatory) a StrControlWidget control control_instance: QLineEdit (mandatory) the control widget we want to validate """ pass
[docs] @staticmethod def create_widget( parent, control_name, control_value, trait, label_class=None, user_data=None, ): """Method to create the Undefined widget. Parameters ---------- parent: QWidget (mandatory) the parent widget control_name: str (mandatory) the name of the control we want to create control_value: str (mandatory) the default control value trait: Tait (mandatory) the trait associated to the control label_class: Qt widget class (optional, default: None) the label widget will be an instance of this class. Its constructor will be called using 2 arguments: the label string and the parent widget. Returns ------- out: 2-uplet a two element tuple of the form (control widget: QLineEdit, associated label: QLabel) """ # Create the widget widget = Qt.QLabel( "<style>background-color: gray; text-color: red;</style>" + str(traits.Undefined), parent, ) # Create the label associated with the string widget control_label = control_name if label_class is None: label_class = QtGui.QLabel if control_label is not None: label = label_class(control_label, parent) else: label = None return (widget, label)
[docs] @staticmethod def update_controller( controller_widget, control_name, control_instance, reset_invalid_value, *args, **kwargs, ): """Update one element of the controller. At the end the controller trait value with the name 'control_name' will match the controller widget user parameters defined in 'control_instance'. 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: QLineEdit (mandatory) the instance of the controller widget control we want to synchronize with the controller """ # Update the controller only if the control is valid if PopulseUndefinedControlWidget.is_valid(control_instance): # Define the control value new_trait_value = traits.Undefined setattr( controller_widget.controller, control_name, new_trait_value ) logger.debug( "'PopulseUndefinedControlWidget' associated controller trait " "'{0}' has been updated with value '{1}'.".format( control_name, new_trait_value ) ) elif reset_invalid_value: # invalid, reset GUI to older value old_trait_value = getattr( controller_widget.controller, control_name ) control_instance.setText(old_trait_value)
[docs] @staticmethod def update_controller_widget( controller_widget, control_name, control_instance ): """Update one element of the controller widget. At the end the controller widget user editable parameter with the name 'control_name' will match the controller trait value with the same name. 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: QLineEdit (mandatory) the instance of the controller widget control we want to synchronize with the controller """ # Define the trait value new_controller_value = str(traits.Undefined) # Set the trait control_instance.setText(new_controller_value) logger.debug( "'PopulseUndefinedControlWidget' has been updated " "with value '{0}'.".format(new_controller_value) ) # Set the controller trait value PopulseUndefinedControlWidget.update_controller( controller_widget, control_name, control_instance, True )
[docs] @classmethod def connect(cls, controller_widget, control_name, control_instance): """Connect a 'Str' or 'String' controller trait and a 'StrControlWidget' controller widget control. Parameters ---------- cls: StrControlWidget (mandatory) a StrControlWidget control 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: QLineEdit (mandatory) the instance of the controller widget control we want to synchronize with the controller """ pass
[docs] @staticmethod def disconnect(controller_widget, control_name, control_instance): """Disconnect a 'Str' or 'String' controller trait and a 'StrControlWidget' controller widget control. 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: QLineEdit (mandatory) the instance of the controller widget control we want to synchronize with the controller """ pass