# -*- coding: utf-8 -*-
#  This software and supporting documentation are distributed by
#      Institut Federatif de Recherche 49
#      CEA/NeuroSpin, Batiment 145,
#      91191 Gif-sur-Yvette cedex
#      France
#
# This software is governed by the CeCILL license version 2 under
# French law and abiding by the rules of distribution of free software.
# You can  use, modify and/or redistribute the software under the
# terms of the CeCILL license version 2 as circulated by CEA, CNRS
# and INRIA at the following URL "http://www.cecill.info".
#
# As a counterpart to the access to the source code and  rights to copy,
# modify and redistribute granted by the license, users are provided only
# with a limited warranty  and the software's author,  the holder of the
# economic rights,  and the successive licensors  have only  limited
# liability.
#
# In this respect, the user's attention is drawn to the risks associated
# with loading,  using,  modifying and/or developing or reproducing the
# software by the user in light of its specific status of free software,
# that may mean  that it is complicated to manipulate,  and  that  also
# therefore means  that it is reserved for developers  and  experienced
# professionals having in-depth computer knowledge. Users are therefore
# encouraged to load and test the software's suitability as regards their
# requirements in conditions enabling the security of their systems and/or
# data to be ensured and,  more generally, to use and operate it in the
# same conditions as regards security.
#
# The fact that you are presently reading this means that you have had
# knowledge of the CeCILL license version 2 and that you accept its terms.
from __future__ import print_function
from __future__ import absolute_import
import brainvisa.processes
from datetime import date
from datetime import datetime
from datetime import timedelta
from six.moves import StringIO
import distutils
import os
import sys
import re
import types
from soma.qt_gui.qt_backend.Qt import (
    Qt, QAction, QApplication, QButtonGroup,
    QCursor, QComboBox, QDialog, QDockWidget, QFile, QFileDialog, QFont,
    QFontInfo, QFormLayout, QFrame, QGridLayout, QHBoxLayout, QIcon,
    QKeySequence, QLabel, QLineEdit, QMainWindow, QMenu, QMenuBar, QMessageBox,
    QMovie, QObject, QPalette, QPixmap, QPlainTextEdit, QProgressBar,
    QPushButton, QRadioButton, QScrollArea, QSize, QSizePolicy, QSpacerItem,
    QSplitter, QStackedWidget, QTextEdit, QTimer, QToolBar, QToolButton,
    QTransform, QTreeWidget, QTreeWidgetItem, QTreeWidgetItemIterator,
    QUrl, QVBoxLayout, QWidget,
)
from soma.qt_gui import qt_backend
from soma.qt_gui.qt_backend import QtCore
from soma.qt_gui.qt_backend import QtGui
from six.moves import range
from soma.qt4gui.text import (QWebEnginePage, use_webengine,
    QWebEngineView, QWebPage)
from soma.qt_gui.qt_backend import loadUi, loadUiType
from brainvisa.configuration import neuroConfig
from brainvisa.configuration.qt4gui import neuroConfigGUI
from brainvisa.processing.qt4gui import neuroLogGUI
from brainvisa.data import neuroData
from brainvisa.history import ProcessExecutionEvent
from brainvisa.data.neuroDiskItems import DiskItem
from brainvisa.data.readdiskitem import ReadDiskItem
from brainvisa.data.writediskitem import WriteDiskItem
from brainvisa.data.qt4gui import lockFilesGUI, neuroDataGUI
import weakref
from soma.minf.xhtml import XHTML
from soma.qtgui.api import QtThreadCall, FakeQtThreadCall, WebBrowserWithSearch, bigIconSize, defaultIconSize
from soma.html import htmlEscape
from soma.wip.application.api import Application
import soma.functiontools
import threading
import socket
import six
try:
    import sip
except ImportError:
    # for sip 3.x (does it work ??)
    import libsip as sip
# for comatibility, make mainThreadActions visible in neuroProcessesGUI
from brainvisa.processes import mainThreadActions
from brainvisa.processing import neuroException
from brainvisa.processing.neuroException import catch_gui_exception
from soma.qtgui.api import EditableTreeWidget, TreeListWidget
from soma.notification import ObservableList, EditableTree
from soma.signature.api import HasSignature
from soma.signature.api import Signature as SomaSignature
from soma.signature.api import FileName as SomaFileName
from soma.signature.api import Choice as SomaChoice
from soma.signature.api import Boolean as SomaBoolean
from soma.qt4gui.api import ApplicationQt4GUI
from brainvisa.data.databaseCheck import BVChecker_3_1
from brainvisa.data import neuroHierarchy
from brainvisa.tools import checkbrainvisaupdates
try:
    from six.moves.urllib import parse as urllib
except ImportError:
    # some six versions do not provide six.moves.urllib (Ubuntu 12.04)
    import six.moves.urllib.request, six.moves.urllib.parse, six.moves.urllib.error
# Because soma_workflow uses soma.qt_gui.qt_backend.uic, we need to first
# initialize
# soma.qt_gui.qt_backend.uic in order to hack soma.qt_gui.qt_backend.uic and
# fix issue #13432
# ref: https://bioproj.extra.cea.fr/redmine/issues/13432
import soma.qt_gui.qt_backend.uic
try:
    from soma_workflow.gui.workflowGui import SomaWorkflowWidget as ComputingResourceWidget
    from soma_workflow.gui.workflowGui import SomaWorkflowMiniWidget as MiniComputingResourceWidget
    import soma_workflow.gui.workflowGui
    from soma_workflow.gui.workflowGui import ComputingResourcePool
    from soma_workflow.gui.workflowGui import ApplicationModel as WorkflowApplicationModel
    import soma_workflow.configuration
except ImportError:
    _soma_workflow = False
    class ComputingResourceWidget(object):
        pass
    class MiniComputingResourceWidget(object):
        pass
    class ComputingResourcePool(object):
        pass
    class WorkflowApplicationModel(object):
        pass
else:
    _soma_workflow = True
#----------------------------------------------------------------------------
def restartAnatomist():
    from brainvisa import anatomist
    a = anatomist.Anatomist(create=False)
    if hasattr(a, '_restartshell_launched'):
        a.launched = True
        del a._restartshell_launched
class _ProcDeleter(object):
    def __init__(self, o):
        self.o = o
    def __del__(self):
        try:
            self.o.kill()
        except Exception:
            pass
def startShell():
    qt_impl = qt_backend.get_qt_backend()
    if qt_impl == 'PyQt6':
        qt_api = "pyqt6"
    elif qt_impl == 'PyQt4':
        # prevent ipython from trying to use PySide
        qt_api = "pyqt"
    elif qt_impl == 'PyQt5':
        qt_api = "pyqt5"
    elif qt_impl == 'PySide':
        qt_api = "pyside"
    else:
        qt_api = "pyqt5"
    os.environ["QT_API"] = qt_api
    ipfunc = None
    print('startShell')
    try:
        import qtconsole  # to check it is installed
        import jupyter_core.application
        ipfunc = 'from jupyter_core import application; ' \
            'app = application.JupyterApp(); app.initialize(); app.start()'
        print('ipfunc:', ipfunc)
    except ImportError:
        try:
            import IPython
            ipversion = [int(x) for x in IPython.__version__.split('.')]
            if ipversion >= [0, 11]:
                # ipython >= 0.11, use client/server mode
                print('ipversion:', ipversion)
                if ipversion >= [1, 0]:
                    ipmodule = 'IPython.terminal.ipapp'
                else:
                    ipmodule = 'IPython.frontend.terminal.ipapp'
                ipfunc = 'from %s import launch_new_instance; launch_new_instance()' % ipmodule
        except Exception:
            print('failed to run Qt console')
    if ipfunc:
        import soma.subprocess
        ipConsole = brainvisa.processes.runIPConsoleKernel()
        cmd = [sys.executable, '-c',
               'import os; os.environ["QT_API"] = "%s"; %s' % (qt_api, ipfunc),
               'qtconsole', '--existing',
               '--shell=%d' % ipConsole.shell_port,
               '--iopub=%d' % ipConsole.iopub_port,
               '--stdin=%d' % ipConsole.stdin_port,
               '--hb=%d' % ipConsole.hb_port]
        print('cmd:', cmd)
        sp = soma.subprocess.Popen(cmd)
        brainvisa.processes._ipsubprocs.append(_ProcDeleter(sp))
        return
    neuroConfig.shell = True
    try:
        if neuroConfig.anatomistImplementation == 'socket':
            from brainvisa import anatomist
            a = anatomist.Anatomist(create=False)
            if a and a.launched:
                a.launched = False
                a._restartshell_launched = True
    except Exception as e:
        print(e)
    mainThreadActions().push(QApplication.instance().exit)
#----------------------------------------------------------------------------
def quitRequest():
    # Called when quitting brainvisa using quit menu or closing the main window
    # print('!!!!!!!!!quitRequest!!!!!!!!')
    a = QMessageBox.warning(
        None, _t_('Quit'), _t_('Do you really want to quit BrainVISA ?'),
                            QMessageBox.Yes | QMessageBox.Default, QMessageBox.No)
    if a == QMessageBox.Yes:
        wids = QApplication.instance().topLevelWidgets()
        for w in wids:
            if isinstance(w, ProcessView) or isinstance(w, SomaWorkflowProcessView):
                w.close()
                del w
        try:
            from brainvisa import anatomist
            a = anatomist.Anatomist(create=False)
            if a:
                close_viewers()
                a.close()
        except Exception:
            pass
        if neuroConfig.shell:
            sys.exit()
        else:
            QApplication.instance().exit()
#----------------------------------------------------------------------------
def cleanupGui():
    # called when quitting a ipython shell
    wids = QApplication.instance().topLevelWidgets()
    for w in wids:
        if isinstance(w, ProcessView):
            w.close()
            del w
    wids = QApplication.instance().topLevelWidgets()
    for w in wids:
        w.close()
        del w
#----------------------------------------------------------------------------
_helpWidget = None
def helpRequest():
    url = QUrl.fromLocalFile(neuroConfig.getDocFile(
        os.path.join('help', 'index.html'))).toString()
    openWeb(url)
[docs]def runHtmlBrowser(source, existingWidget=None):
    ''' run the HTML browser defined in BV config. If it is a builtin browser,
    use an existing widget, or instantiate and return a new one.
    '''
    try:
        browser = neuroConfig.HTMLBrowser
        if browser is not None:
            browser = distutils.spawn.find_executable(browser)
            if browser:
                if sys.platform == "darwin":
                    m = re.match(
                        "\/Applications\/.+\.app/Contents/MacOS/(.*)", browser)
                    if m:
                        if os.system("open -a " + m.group(1) + " '" + source + "'") == 0:
                            return
                env = os.environ.copy()
                if (not browser.startswith(os.path.dirname(neuroConfig.mainPath))):  # external command
                    if neuroConfig.brainvisaSysEnv:
                        env.update(neuroConfig.brainvisaSysEnv.getVariables())
                if os.spawnle(os.P_NOWAIT, browser, browser, source, env) > 0:
                    return
    except Exception:
        pass
    if existingWidget is None:
        existingWidget = HTMLBrowser(None)
        existingWidget.setWindowTitle(_t_('BrainVISA help'))
        existingWidget.resize(800, 600)
    sys.stdout.flush()
    existingWidget.setSource(source)
    existingWidget.show()
    existingWidget.raise_()
    return existingWidget 
def openWeb(source):
    global _helpWidget
    widget = runHtmlBrowser(source, _helpWidget)
    if widget is not None:
        _helpWidget = widget
#----------------------------------------------------------------------------
[docs]def runCsvViewer(source, existingWidget=None):
    ''' run the CSV viewer defined in BV config. If it is a builtin viewer,
    use an existing widget, or instantiate and return a new one.
    '''
    try:
        configuration = Application().configuration
        browser = configuration.brainvisa.csvViewer
        if browser is not None:
            browser = distutils.spawn.find_executable(browser)
            if browser:
                if sys.platform == "darwin":
                    m = re.match(
                        "\/Applications\/.+\.app/Contents/MacOS/(.*)", browser)
                    if m:
                        if os.system("open -a " + m.group(1) + " '" + source + "'") == 0:
                            return
                env = os.environ.copy()
                if (not browser.startswith(os.path.dirname(neuroConfig.mainPath))):  # external command
                    if neuroConfig.brainvisaSysEnv:
                        env.update(neuroConfig.brainvisaSysEnv.getVariables())
                if os.spawnle(os.P_NOWAIT, browser, browser, source, env) > 0:
                    return
    except Exception as e:
        print('exception.')
        print(e)
        pass
    # builtin browser, needs GenericTableEditor
    try:
        from soma.qt_gui.generic_table_editor import GenericTableEditor
        if existingWidget is None:
            existingWidget = GenericTableEditor(None)
            existingWidget.setWindowTitle(_t_('CSV viewer'))
            existingWidget.resize(800, 600)
        existingWidget.load_from_file(source)
        existingWidget.show()
        existingWidget.raise_()
        return existingWidget
    except Exception:
        pass
    # fallback to text editor
    textEditor = configuration.brainvisa.textEditor
    if textEditor is not None:
        textEditor = distutils.spawn.find_executable(textEditor)
        if textEditor:
            env = os.environ.copy()
            if (not textEditor.startswith(os.path.dirname(neuroConfig.mainPath))):  # external command
                if neuroConfig.brainvisaSysEnv:
                    env.update(neuroConfig.brainvisaSysEnv.getVariables())
            if os.spawnle(os.P_NOWAIT, textEditor, textEditor, source, env) != 0:
                return
    raise RuntimeError(_t_('Could not run a CSV viewer program.')) 
#----------------------------------------------------------------------------
[docs]class WorkflowSubmissionDlg(QDialog):
    def __init__(self, parent=None):
        super(WorkflowSubmissionDlg, self).__init__(parent)
        from brainvisa.workflow import ProcessToSomaWorkflow
        loadUi(
            os.path.join(os.path.dirname(__file__), 'sw_submission_dlg.ui'),
                   self)
        # self.setupUi(self)
        resource_list = _computing_resource_pool.resource_ids()
        self.combo_resource.addItems(resource_list)
        current_resource_index = resource_list.index(
            _workflow_application_model.current_resource_id)
        self.combo_resource.setCurrentIndex(current_resource_index)
        self.resource_changed(current_resource_index)
        kind_of_file_processing = [ProcessToSomaWorkflow.NO_FILE_PROCESSING,
                                   ProcessToSomaWorkflow.FILE_TRANSFER,
                                   ProcessToSomaWorkflow.SHARED_RESOURCE_PATH]
        self.combo_out_files.addItems(kind_of_file_processing)
        kind_of_file_processing.append(ProcessToSomaWorkflow.BV_DB_SHARED_PATH)
        self.combo_in_files.addItems(kind_of_file_processing)
        self.lineedit_wf_name.setText("")
        self.dateTimeEdit_expiration.setDateTime(
            datetime.now() + timedelta(days=5))
        self.combo_resource.currentIndexChanged.connect(self.resource_changed)
    @QtCore.Slot(int)
    def resource_changed(self, resource_index):
        resource_id = self.combo_resource.currentText()
        queues = ["default queue"]
        queues.extend(
            _computing_resource_pool.connection(resource_id).config.get_queues())
        self.combo_queue.clear()
        self.combo_queue.addItems(queues) 
[docs]class SomaWorkflowProcessView(QMainWindow):
    workflow_id = None
    resource_id = None
    model = None
    process = None
    serialized_process = None
    ui = None
    process_view = None
    workflow_tree_view = None
    workflow_item_view = None
    workflow_plot_view = None
    action_monitor_workflow = None
    workflow_menu = None
    workflow_tool_bar = None
    def __init__(self,
                 model,
                 workflow_id,
                 resource_id,
                 process=None,
                 serialized_process=None,
                 parent=None):
        super(SomaWorkflowProcessView, self).__init__(parent)
        Ui_SWProcessView = loadUiType(os.path.join(os.path.dirname(__file__),
                                                   'sw_process_view.ui'))[0]
        self.ui = Ui_SWProcessView()
        self.ui.setupUi(self)
        self.model = model
        self.serialized_process = serialized_process
        self.process = process
        self.workflow_id = workflow_id
        self.resource_id = resource_id
        self.model.current_workflow_changed.connect(
            self.current_workflow_changed)
        self.setCorner(QtCore.Qt.TopLeftCorner, QtCore.Qt.LeftDockWidgetArea)
        self.setCorner(QtCore.Qt.TopRightCorner, QtCore.Qt.RightDockWidgetArea)
        self.setCorner(
            QtCore.Qt.BottomLeftCorner, QtCore.Qt.LeftDockWidgetArea)
        self.setCorner(
            QtCore.Qt.BottomRightCorner, QtCore.Qt.RightDockWidgetArea)
        self.action_monitor_workflow = QAction(_t_('Monitor execution'), self)
        self.action_monitor_workflow.setCheckable(True)
        self.action_monitor_workflow.setIcon(
            QIcon(os.path.join(os.path.dirname(soma_workflow.gui.__file__), "icon/monitor_wf.png")))
        self.action_monitor_workflow.toggled.connect(
            self.enable_workflow_monitoring)
        self.action_monitor_workflow.setChecked(True)
        self.workflow_tree_view = soma_workflow.gui.workflowGui.WorkflowTree(
            _workflow_application_model,
                          assigned_wf_id=self.workflow_id,
                          assigned_resource_id=self.resource_id,
                          parent=self)
        self.workflow_item_view = soma_workflow.gui.workflowGui.WorkflowElementInfo(
            model=_workflow_application_model,
                        proxy_model=self.workflow_tree_view.proxy_model,
                        parent=self)
        self.workflow_plot_view = soma_workflow.gui.workflowGui.WorkflowPlot(
            _workflow_application_model,
                        assigned_wf_id=self.workflow_id,
                        assigned_resource_id=self.resource_id,
                        parent=self)
        if not soma_workflow.gui.workflowGui.MATPLOTLIB:
            self.ui.dock_plot.hide()
            self.ui.dock_plot.toggleViewAction().setVisible(False)
        self.workflow_info_view = soma_workflow.gui.workflowGui.WorkflowInfoWidget(
            _workflow_application_model,
                        assigned_wf_id=self.workflow_id,
                        assigned_resource_id=self.resource_id,
                        parent=self)
        self.workflow_menu = self.ui.menubar.addMenu("&Workflow")
        self.workflow_menu.addAction(_mainWindow.sw_widget.ui.action_stop_wf)
        self.workflow_menu.addAction(_mainWindow.sw_widget.ui.action_restart)
        _addSeparator(self.workflow_menu)
        self.workflow_menu.addAction(
            _mainWindow.sw_widget.ui.action_transfer_infiles)
        self.workflow_menu.addAction(
            _mainWindow.sw_widget.ui.action_transfer_outfiles)
        _addSeparator(self.workflow_menu)
        self.workflow_menu.addAction(
            _mainWindow.sw_widget.ui.action_delete_workflow)
        self.workflow_menu.addAction(
            _mainWindow.sw_widget.ui.action_change_expiration_date)
        view_menu = self.ui.menubar.addMenu("&View")
        view_menu.addAction(self.ui.dock_bv_process.toggleViewAction())
        view_menu.addAction(self.ui.dock_plot.toggleViewAction())
        view_menu.addAction(close_viewers_action(self))
        self.action_rebuild_check_all_databases = QAction(self)
        self.action_rebuild_check_all_databases.setText(
            _t_('Completely rebuild and check ALL databases...'))
        self.action_rebuild_check_all_databases.triggered.connect(
            self.complete_rebuild_and_check_all_databases)
        self.process_menu = self.ui.menubar.addMenu("&Process")
        self.process_menu.addAction(self.action_rebuild_check_all_databases)
        self.workflow_tool_bar = QToolBar(self)
        self.workflow_tool_bar.addWidget(
            self.workflow_info_view.ui.wf_status_icon)
        _addSeparator(self.workflow_tool_bar)
        self.workflow_tool_bar.addAction(
            _mainWindow.sw_widget.ui.action_stop_wf)
        self.workflow_tool_bar.addAction(
            _mainWindow.sw_widget.ui.action_restart)
        _addSeparator(self.workflow_tool_bar)
        self.workflow_tool_bar.addAction(
            _mainWindow.sw_widget.ui.action_transfer_infiles)
        self.workflow_tool_bar.addAction(
            _mainWindow.sw_widget.ui.action_transfer_outfiles)
        self.ui.tool_bar.addWidget(self.workflow_tool_bar)
        _addSeparator(self.ui.tool_bar)
        self.ui.tool_bar.addAction(self.ui.dock_bv_process.toggleViewAction())
        _addSeparator(self.ui.tool_bar)
        self.ui.tool_bar.addAction(self.action_monitor_workflow)
        tree_widget_layout = QtGui.QVBoxLayout()
        tree_widget_layout.setContentsMargins(2, 2, 2, 2)
        tree_widget_layout.addWidget(self.workflow_tree_view)
        self.ui.centralwidget.setLayout(tree_widget_layout)
        self.process_layout = QtGui.QVBoxLayout()
        self.process_layout.setContentsMargins(2, 2, 2, 2)
        self.ui.dock_bv_process_contents.setLayout(self.process_layout)
        item_info_layout = QtGui.QVBoxLayout()
        item_info_layout.setContentsMargins(2, 2, 2, 2)
        item_info_layout.addWidget(self.workflow_item_view)
        self.ui.dock_item_info_contents.setLayout(item_info_layout)
        wf_info_layout = QtGui.QVBoxLayout()
        wf_info_layout.setContentsMargins(2, 2, 2, 2)
        wf_info_layout.addWidget(self.workflow_info_view)
        self.ui.dock_workflow_info_contents.setLayout(wf_info_layout)
        plot_layout = QtGui.QVBoxLayout()
        plot_layout.setContentsMargins(2, 2, 2, 2)
        plot_layout.addWidget(self.workflow_plot_view)
        self.ui.dock_plot_contents.setLayout(plot_layout)
        self.workflow_tree_view.selection_model_changed.connect(
            self.workflow_item_view.setSelectionModel)
        self.workflow_item_view.connection_closed_error.connect(
            _mainWindow.sw_widget.reconnectAfterConnectionClosed)
        self.workflow_tree_view.current_workflow_changed()
        self.workflow_plot_view.current_workflow_changed()
        self.workflow_info_view.current_workflow_changed()
        self.ui.dock_bv_process.toggleViewAction().toggled.connect(
            self.show_process)
        self.ui.dock_bv_process.toggleViewAction().setIcon(
            QIcon(os.path.join(neuroConfig.iconPath, 'icon.png')))
        self.ui.dock_plot.close()
        self.ui.dock_bv_process.close()
        self.ui.dock_workflow_info.close()
        wf_name = self.workflow_info_view.ui.wf_name.text()
        if len(wf_name[len(SomaWorkflowWidget.brainvisa_code):]) == 0:
            title = repr(self.workflow_id) + "@" + self.resource_id
        else:
            title = wf_name[
                len(SomaWorkflowWidget.brainvisa_code):] + "@" + self.resource_id
        self.setWindowTitle(title)
        # warning message in the status bar about the need to check and update
        # databases after execution
        warningMsg = QLabel(
            "<div style='font-size:9pt'><i><font color='red'>Warning:</font> After execution with Soma-Workflow, the databases may need to be updated.<br>Use \"Process -> Check & update databases\" menu to do it.</i></div>")
        warningMsg.setWordWrap(True)
        self.ui.statusbar.addPermanentWidget(warningMsg, 1)
[docs]    def closeEvent(self, event):
        if self.process_view:
            self.process_view.cleanup()
        QMainWindow.closeEvent(self, event) 
    @QtCore.Slot(bool)
    def enable_workflow_monitoring(self, enable):
        if not enable:
            if self.model.current_wf_id == self.workflow_id and \
               
self.model.current_resource_id == self.resource_id:
                self.model.clear_current_workflow()
        else:
            if self.resource_id != self.model.current_resource_id:
                if self.model.resource_pool.resource_exist(self.resource_id):
                    self.model.set_current_connection(self.resource_id)
                else:
                    (resource_id,
                     new_connection) = _mainWindow.sw_widget.createConnection(self.resource_id,
                                                                              editable_resource=False)
                    if new_connection:
                        self.model.add_connection(resource_id, new_connection)
                    else:
                        QMessageBox.warning(
                            self, "Monitoring impossible", "The connection is not active.")
                        self.action_monitor_workflow.setChecked(False)
                        return
            if self.model.is_loaded_workflow(self.workflow_id):
                self.model.set_current_workflow(self.workflow_id)
            else:
                QMessageBox.warning(
                    self, "Monitoring impossible", "The workflow was deleted.")
                self.action_monitor_workflow.setChecked(False)
    @QtCore.Slot(bool)
    def show_process(self, checked):
        if self.process == None and self.serialized_process == None:
            return
        if checked and self.process_view == None:
            if self.process == None:
                # print("before unserialize")
                QtGui.QApplication.setOverrideCursor(
                    QtGui.QCursor(QtCore.Qt.WaitCursor))
                # self.ui.statusbar.showMessage("Unserialize...")
                try:
                    self.process = brainvisa.processes.getProcessInstance(
                        self.serialized_process)
                finally:
                    # self.ui.statusbar.clearMessage()
                    QtGui.QApplication.restoreOverrideCursor()
                # print("after unserialize")
            # print("before process view creation")
            QtGui.QApplication.setOverrideCursor(
                QtGui.QCursor(QtCore.Qt.WaitCursor))
            # self.ui.statusbar.showMessage("Building the process view...")
            try:
                self.process_view = ProcessView(self.process, parent=self,
                                                read_only=True)
            finally:
                QtGui.QApplication.restoreOverrideCursor()
            self.process_view.inlineGUI.hide()
            self.process_view.info.hide()
            self.process_view.eTreeWidget.setOrientation(Qt.Vertical)
            self.process_layout.addWidget(self.process_view)
            # print("After process view creation")
            self.ui.dock_bv_process.toggleViewAction().toggled.disconnect(
                self.show_process)
            process_button_layout = QtGui.QHBoxLayout()
            process_button_layout.setContentsMargins(2, 2, 2, 2)
            self.process_layout.addLayout(process_button_layout)
            self.process_view.action_clone_process.setText("Edit...")
            self.process_view.action_iterate.setText("Iterate...")
            btn_clone = QToolButton(self)
            btn_clone.setDefaultAction(self.process_view.action_clone_process)
            btn_clone.setMinimumWidth(90)
            btn_clone.setSizePolicy(
                QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
            process_button_layout.addWidget(btn_clone)
            btn_iterate = QToolButton(self)
            btn_iterate.setDefaultAction(self.process_view.action_iterate)
            btn_iterate.setMinimumWidth(90)
            btn_iterate.setSizePolicy(
                QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
            process_button_layout.addWidget(btn_iterate)
            btn_save = QToolButton(self)
            btn_save.setDefaultAction(self.process_view.action_save_process)
            btn_save.setMinimumWidth(90)
            btn_save.setSizePolicy(
                QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
            process_button_layout.addWidget(btn_save)
            self.process_menu.addAction(self.process_view.action_save_process)
            self.process_menu.addAction(self.process_view.action_clone_process)
            self.process_menu.addAction(self.process_view.action_iterate)
    @QtCore.Slot()
    def complete_rebuild_and_check_all_databases(self):
        QtGui.QApplication.setOverrideCursor(
            QtGui.QCursor(QtCore.Qt.WaitCursor))
        for dbSettings in neuroConfig.dataPath:
            try:
                if not dbSettings.builtin:
                    db = neuroHierarchy.databases.database(
                        dbSettings.directory)
                    brainvisa.processes.defaultContext().write(
                        "Updating database " + db.name + "...")
                    # first update before checking for missing referentials
                    db.clear(context=brainvisa.processes.defaultContext())
                    db.update(context=brainvisa.processes.defaultContext())
                    # check referentials and transformations information, the
                    # database will be updated after
                    brainvisa.processes.defaultContext().write(
                        "Checking referentials information in database " + db.name + "...")
                    checker = BVChecker_3_1(
                        db, brainvisa.processes.defaultContext())
                    checker.findActions()
                    checker.process()
            except Exception:
                neuroException.showException(
                    beforeError="Error while updating database " + dbSettings.directory)
        QtGui.QApplication.restoreOverrideCursor()
    @QtCore.Slot()
    def current_workflow_changed(self):
        if self.model.current_wf_id == self.workflow_id and \
                
self.model.current_resource_id == self.resource_id:
            self.action_monitor_workflow.setChecked(True)
            self.workflow_tool_bar.setEnabled(True)
            self.workflow_menu.setEnabled(True)
        else:
            self.action_monitor_workflow.setChecked(False)
            self.workflow_tool_bar.setEnabled(False)
            self.workflow_menu.setEnabled(False) 
_aboutWidget = None
#----------------------------------------------------------------------------
def aboutRequest():
    global _aboutWidget
    if _aboutWidget is None:
        _aboutWidget = AboutWidget()
    _aboutWidget.show()
    _aboutWidget.raise_()
#----------------------------------------------------------------------------
def logRequest():
    neuroLogGUI.LogViewer(neuroConfig.logFileName).show()
#----------------------------------------------------------------------------
def _addAction(parent, text=None, callback=None, shortcut=None):
# this 'strange' function is only here to avoid a memory leak in PyQt:
# it apparently does the same as QMenu.addAction() / addSeparator(),
# but if the former are called, the returned QAction is created in the
# C++ layer, and has a strange ownership side effect: the python part
# of the QAction is never destroyed.
# Creating the QAction from Python side seems to work around the problem.
# Seen in PyQt 4.9.3
    if text is None:
        ac = QAction(parent)
        ac.setSeparator(True)
    else:
        ac = QAction(text, parent)
    if callback is not None:
        ac.triggered.connect(callback)
    if shortcut is not None:
        ac.setShortcut(shortcut)
    parent.addAction(ac)
    return ac
def _addSeparator(menu):
    return _addAction(menu)
#----------------------------------------------------------------------------
def addBrainVISAMenu(widget, menuBar):
    bvMenu = QMenu("&BrainVISA", menuBar)  # avoid creating the menu in addMenu
    menuBar.addMenu(bvMenu)  # same problem as addAction()
    _addAction(bvMenu, _t_("&Help"), helpRequest,
               QKeySequence(Qt.CTRL | Qt.Key_H))
    _addAction(bvMenu, _t_("About"), aboutRequest)
    _addAction(bvMenu)
    _addAction(bvMenu, _t_("&Preferences"),
               neuroConfigGUI.editConfiguration,
               QKeySequence(Qt.CTRL | Qt.Key_P))
    _addAction(bvMenu, _t_("Show &Log"), logRequest,
               QKeySequence(Qt.CTRL | Qt.Key_L))
    _addAction(bvMenu, _t_("&Open process..."), ProcessView.open,
               QKeySequence(Qt.CTRL | Qt.Key_O))
    _addAction(bvMenu, _t_("Load process setups"), loadProcessSetupsGUI)
    _addAction(bvMenu, _t_("Reload toolboxes"), reloadToolboxesGUI)
    _addAction(bvMenu, _t_("Start &Shell"), startShell,
               QKeySequence(Qt.CTRL | Qt.Key_S))
    _addAction(bvMenu)
    if not isinstance(widget, ProcessSelectionWidget):
        _addAction(bvMenu, _t_("Close"), widget.close,
                   QKeySequence(Qt.CTRL | Qt.Key_W))
    _addAction(bvMenu, _t_("&Quit"), quitRequest,
               QKeySequence(Qt.CTRL | Qt.Key_Q))
    return bvMenu
#----------------------------------------------------------------------------
[docs]class HTMLBrowser(QWidget):
[docs]    class BVTextBrowser(WebBrowserWithSearch):
        def __init__(self, parent, name=None):
            WebBrowserWithSearch.__init__(self, parent)
            if name:
                self.setObjectName(name)
            # self.mimeSourceFactory().setExtensionType("py", "text/plain")
            self.openWebAction = QAction(_t_('Open in a web browser'), self)
            self.openWebAction.setShortcut(QKeySequence(Qt.CTRL | Qt.Key_W))
            self.openWebAction.triggered.connect(self.openWeb)
            self.openLinkWebAction = QAction(
                _t_('Open link in a web browser'), self)
            self.openLinkWebAction.triggered.connect(self.openLinkWeb)
            self.openLinkInTextEditorAction = QAction(
                _t_('Open link in a text editor'), self)
            self.openLinkInTextEditorAction.triggered.connect(
                self.openSourceLink)
            self.copyLinkAction = QAction(_t_('Copy link URL'), self)
            self.copyLinkAction.triggered.connect(self.copyLinkUrl)
            self.linkUrl = None
        def setSource(self, url):
            text = url.toString()
            bvp = six.text_type(text)
            if bvp.startswith('bvshowprocess://'):
                bvp = bvp[16:]
                # remove tailing '/'
                if bvp[-1] == '/':
                    bvp = bvp[: -1]
                proc = brainvisa.processes.getProcess(bvp)
                if proc is None:
                    print('No process of name', bvp)
                else:
                    win = ProcessView(proc())
                    win.show()
            elif bvp.startswith('file://') and bvp.endswith('.py'):
                WebBrowserWithSearch.setSource(self, url)
                try:
                    self.setHtml('<html><body><pre>' + htmlEscape(
                        open(url.toLocalFile()).read())
                        + '</pre></body></html>')
                except FileNotFoundError:
                    self.setHtml('<font color="#a00000">Documentation file "<tt>%s</tt>" cannot be read</font>' % url.toLocalFile())
            else:
                WebBrowserWithSearch.setSource(self, url)
            if not use_webengine:
                self.page().setLinkDelegationPolicy(
                    QWebEnginePage.DelegateAllLinks)
        def customMenu(self, hit_url=None):
            menu = WebBrowserWithSearch.customMenu(self)
            menu.addSeparator()
            menu.addAction(self.openWebAction)
            self.linkUrl = hit_url
            if hit_url:
                menu.addSeparator()
                menu.addAction(self.openLinkWebAction)
                if six.text_type(hit_url.toString()).endswith('.py'):
                    menu.addAction(self.openLinkInTextEditorAction)
                menu.addAction(self.copyLinkAction)
            return menu
        def copyLinkUrl(self, dummy):
            QApplication.clipboard().setText(self.linkUrl.toString())
        def openLinkWeb(self, dummy):
            openWeb(self.linkUrl.toString())
        def openSourceLink(self, dummy):
            configuration = Application().configuration
            textEditor = configuration.brainvisa.textEditor
            if textEditor is not None:
                textEditor = distutils.spawn.find_executable(textEditor)
                if textEditor:
                    env = os.environ.copy()
                    if not textEditor.startswith(os.path.dirname(neuroConfig.mainPath)):
                        # external command
                        if neuroConfig.brainvisaSysEnv:
                            env.update(
                                neuroConfig.brainvisaSysEnv.getVariables())
                    os.spawnle(
                        os.P_NOWAIT, textEditor, textEditor,
                      six.text_type(self.linkUrl.toString()), env)
        def openWeb(self):
            openWeb(self.url().toString()) 
    def __init__(self, parent=None, name=None, fl=Qt.WindowType(0)):
        QWidget.__init__(self, parent, fl)
        if name:
            self.setObjectName(name)
        vbox = QVBoxLayout(self)
        self.setLayout(vbox)
        vbox.setSpacing(2)
        vbox.setContentsMargins(3, 3, 3, 3)
        if getattr(HTMLBrowser, 'pixHome', None) is None:
            setattr(HTMLBrowser, 'pixIcon', QIcon(
                os.path.join(neuroConfig.iconPath, 'icon_help.png')))
            setattr(HTMLBrowser, 'pixHome', QIcon(
                os.path.join(neuroConfig.iconPath, 'top.png')))
        self.setWindowIcon(HTMLBrowser.pixIcon)
        hbox = QHBoxLayout()
        hbox.setSpacing(6)
        hbox.setContentsMargins(0, 0, 0, 0)
        self.homeAction = QAction(_t_('Home'), self)
        self.homeAction.setIcon(self.pixHome)
        btnHome = QToolButton()
        btnHome.setSizePolicy(
            QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum))
        hbox.addWidget(btnHome)
        btnBackward = QToolButton()
        btnBackward.setSizePolicy(
            QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum))
        hbox.addWidget(btnBackward)
        btnForward = QToolButton()
        btnForward.setSizePolicy(
            QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum))
        hbox.addWidget(btnForward)
        btnReload = QToolButton()
        btnReload.setSizePolicy(
            QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum))
        hbox.addWidget(btnReload)
        vbox.addLayout(hbox)
        browser = self.BVTextBrowser(self)
        browser.setSizePolicy(
            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding))
        vbox.addWidget(browser)
        self.homeAction.triggered.connect(self.home)
        btnHome.setDefaultAction(self.homeAction)
        btnForward.setDefaultAction(browser.pageAction(QWebEnginePage.Forward))
        btnBackward.setDefaultAction(browser.pageAction(QWebEnginePage.Back))
        a = browser.pageAction(QWebEnginePage.Reload)
        a.setShortcut(QtGui.QKeySequence.Refresh)
        btnReload.setDefaultAction(a)
        browser.linkClicked.connect(browser.setSource)
        self.browser = browser
        neuroConfig.registerObject(self)
        hbox.addWidget(QLabel(_t_('Search site:')))
        self._siteSearch = QLineEdit()
        hbox.addWidget(self._siteSearch)
        self._siteSearch.returnPressed.connect(self.siteSearch)
    def setSource(self, source):
        if not isinstance(source, QUrl):
            if os.path.exists(source):
                source = QUrl.fromLocalFile(source)
            else:
                source = QUrl(source)
        self.browser.setSource(source)
    def reload(self):
        self.browser.reload()
    def setText(self, text):
        if text == '':
            self.browser.setPage(None)
        else:
            self.browser.setHtml(text)
    def openWeb(self):
        self.browser.openWeb()
[docs]    def showCategoryDocumentation(self, category):
        """
        Searches for a documentation file associated to this category and opens it  in this browser.
        Documentation files are in docPath/processes/categories/category. Category is a relative path, so if no documentation is found with the entire path, it removes the first item and retries.
        For example, if "t1 mri/viewers" doesn't exists, tries "viewers".
        If there is no documentation file, the browser page is empty.
        """
        categoryPath = category.lower().split("/")
        found = False
        while ((len(categoryPath) > 0) and not found):
            html = neuroConfig.getDocFile(
                os.path.join(os.path.join('processes', 'categories', *categoryPath), 'category_documentation.html'))
            if os.path.exists(html):
                self.setSource(html)
                found = True
            categoryPath = categoryPath[1:]
        if not found:
            self.browser.setHtml('') 
[docs]    def closeEvent(self, event):
        neuroConfig.unregisterObject(self)
        QWidget.closeEvent(self, event) 
    def home(self, void=None):
        newver = checkbrainvisaupdates.checkUpdates()
        if newver:
            text = '''<html><div style="background-color: #ffe8e8">
<hr/>
<h2>A newer BrainVISA version is available</h2>
<p>Version ''' + '.'.join( [ str(x) for x in newver[0] ] ) + ''' is available on the BrainVISA web site.<br/>
Download it on <a href="https://brainvisa.info/download.html">the BrainVISA download page</a>.</p>
<hr/>
</div>
<iframe src="file://''' + neuroConfig.getDocFile(os.path.join( 'help', 'index.html' ) ) + '''" align="center" width="100%" height="100%" scrolling="auto" frameborder="0"></iframe>
</html>'''
            tmp = brainvisa.processes.defaultContext().temporary('HTML')
            open(tmp.fullPath(), 'w').write(text)
            self.setSource(tmp.fullPath())
        else:
            self.setSource(neuroConfig.getDocFile(
                os.path.join('help', 'index.html')))
[docs]    def siteSearch(self):
        '''Search the brainvisa.info website using google search'''
        if self._siteSearch.text():
            if not hasattr(self, '_currentNonSearchPage'):
                self._currentNonSearchPage = self.browser.url()
            #url = 'http://www.google.com/cse?url=brainvisa.info&cref=http%3A%2F%2Fwww.google.com%2Fcse%2Ftools%2Fmakecse%3Furl%3Dbrainvisa.info&ie=&q=' + \
                #six.moves.urllib.parse.quote_plus(self._siteSearch.text())
            url = 'https://duckduckgo.com/?t=h_&ia=web&q=!brainvisa.info+' + \
                
six.moves.urllib.parse.quote_plus(self._siteSearch.text())
            self.setSource(QUrl.fromEncoded(QtCore.QByteArray(url.encode(
                'utf-8'))))
        else:
            # get back to the previous non-search page
            url = self._currentNonSearchPage
            del self._currentNonSearchPage
            self.setSource(url)  
#----------------------------------------------------------------------------
#----------------------------------------------------------------------------
        # self.box.resize( self.visibleWidth(), self.box.height() )
    # def show( self ):
        # self.setMaximumSize( self.maximumWidth(), self.box.size().height() )
        # return QScrollArea.show( self )
    # def resizeEvent( self, e ):
        # result = QScrollArea.resizeEvent( self, e )
        # if self.widget():
            # self.widget().resize( self.visibleWidth(), self.box.height() )
            # self.updateGeometry()
            # self.setMaximumSize( self.maximumWidth(), self.box.sizeHint().height() )
            # if self.box.width() != self.visibleWidth():
                # self.box.resize( self.visibleWidth(), self.box.height() )
                # self.updateGeometry()
        # return result
    # def sizeHint( self ):
        # if self.box:
            # return QWidget.sizeHint( self.box )
        # else:
            # return QScrollArea.sizeHint( self )
    # def visibleWidth(self):
        # return self.viewport().width()
#----------------------------------------------------------------------------
[docs]class ExecutionContextGUI(brainvisa.processes.ExecutionContext):
    def __init__(self):
        brainvisa.processes.ExecutionContext.__init__(self)
[docs]    def ask(self, message, *buttons, **kwargs):
        modal = kwargs.get("modal", 1)
        dlg = self.dialog(modal, message, None, *buttons)
        return mainThreadActions().call(dlg.call) 
[docs]    def dialog(self, parentOrFirstArgument, *args, **kwargs):
        if isinstance( parentOrFirstArgument, QWidget ) or \
           
parentOrFirstArgument is None:
            return self._dialog(parentOrFirstArgument, *args, **kwargs)
        else:
            return self._dialog(*((None, parentOrFirstArgument) + args), **kwargs) 
    def _dialog(self, parent, modal, message, signature, *buttons):
        return mainThreadActions().call(UserDialog, parent, modal,
                                        message, signature, buttons)
[docs]    def mainThreadActions(self):
        '''Returns an object which allows to pass actions to be executed in the main thread. Its implementation may differ according to the presence of a running graphics event loop, thus the returned object may be an instance of different classes: :py:class:`soma.qtgui.api.QtThreadCall`, :py:class:`soma.qtgui.api.FakeQtThreadCall`, or even something else.
        In any case the returned *mainthreadactions* object has 2 methods, *call()* and *push()*:
        ::
          result = mainthreadactions.call(function, *args, **kwargs)
          #or
          mainthreadactions.push(function, *args, **kwargs)
        '''
        return mainThreadActions() 
[docs]    def showProgress(self, value, maxval=None):
        def setProgress(self, value, maxval):
            if not maxval:
                maxval = 100
            if not hasattr(self, '_progressBar'):
                if not hasattr(self, 'inlineGUI'):
                    # no GUI: fallback to text mode
                    brainvisa.processes.ExecutionContext.showProgress(
                        self, value, maxval)
                    return
                layout = self.inlineGUI.parentWidget().layout()
                self._progressBar = QProgressBar(None)
                layout.addWidget(self._progressBar)
                self._progressBar.show()
            if self._progressBar.maximum() != maxval:
                self._progressBar.setRange(0, maxval)
            self._progressBar.setValue(int(round(value)))
        mainThreadActions().push(setProgress, self, value, maxval) 
    @staticmethod
    def createContext():
        return ExecutionContextGUI() 
#----------------------------------------------------------------------------
[docs]class ExecutionNodeGUI(QWidget):
    def __init__(self, parent, parameterized, read_only=False):
        QWidget.__init__(self, parent)
        layout = QVBoxLayout()
        layout.setContentsMargins(5, 5, 5, 5)
        layout.setSpacing(4)
        self.setLayout(layout)
        self.parameterizedWidget = ParameterizedWidget(parameterized, None)
        if read_only:
            self.parameterizedWidget.set_read_only(True)
        layout.addWidget(self.parameterizedWidget)
[docs]    def closeEvent(self, event):
        self.parameterizedWidget.close()
        QWidget.closeEvent(self, event) 
    def _checkReadable(self):
        if self.parameterizedWidget is not None:
            self.parameterizedWidget.checkReadable() 
#----------------------------------------------------------------------------
class VoidClass(object):
    pass
#----------------------------------------------------------------------------
[docs]class RadioItem(QWidget):
    """An custom item to replace a QTreeWidgetItem for the representation of a SelectionExecutionNode item with a radio button.
    QTreeWidgetItem enables only check box items."""
    def __init__(self, text, group, parent=None):
        QWidget.__init__(self, parent)
        layout = QHBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.setSpacing(0)
        self.setLayout(layout)
        self.radio = QRadioButton()
        self.radio.setSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
        layout.addWidget(self.radio)
        group.addButton(self.radio)
        self.icon = QLabel()
        layout.addWidget(self.icon)
        self.label = QLabel(text)
        layout.addWidget(self.label)
        layout.addStretch(1)
        self.setAutoFillBackground(True)
#    self.show()
    def setChecked(self, checked):
        self.radio.setChecked(checked)
    def isChecked(self):
        return self.radio.isChecked()
    def setIcon(self, icon):
        self.icon.setPixmap(icon.pixmap(*defaultIconSize)) 
#----------------------------------------------------------------------------
[docs]class NodeCheckListItem(QTreeWidgetItem):
    def __init__(self, node, parent, index=None, text=None, itemType=None,
                 read_only=False):
        if not index is None and index < parent.childCount():
            QTreeWidgetItem.__init__(self)
            parent.insertChild(index, self)
        else:
            QTreeWidgetItem.__init__(self, parent)
        self._node = node
        self.itemType = itemType
        # the following flag is needed to block itemChanged signal processing
        # before the item is completely setup, otherwise it will trigger a
        # node selection change instead of the contrary
        self.creating = True
        self.read_only = read_only
        if itemType == "radio" and not self.read_only:
            # if the item type is radio, create a custom item RadioItem to replace the current QTreeWidgetItem at display
            # the radio button is included in a button group that is registred
            # in the parent item
            buttonGroup = getattr(self.parent(), "buttonGroup", None)
            if not buttonGroup:
                buttonGroup = QButtonGroup()
                self.parent().buttonGroup = buttonGroup
            self.widget = RadioItem(text, buttonGroup)
            self.treeWidget().setItemWidget(self, 0, self.widget)
            self.widget.radio.clicked.connect(self.radioClicked)
            self.widget.radio.toggled.connect(self.radioToggled)
        else:  # not a radio button or read only, show text directly in the qtreeWidgetItem
            if text:
                self.setText(0, text)
        self.setOn(node._selected)
        node._selectionChange.add(self.nodeStateChanged)
        self.creating = False
    def radioClicked(self, checked):
        self.treeWidget().setCurrentItem(self)
    def radioToggled(self, checked):
        self.stateChange(checked)
    def itemClicked(self):
        if self.itemType == "check":
            self.stateChange(self.isOn())
[docs]    def setIcon(self, col, icon):
        if self.itemType == "radio":
            self.widget.setIcon(icon)
        else:
            QTreeWidgetItem.setIcon(self, col, icon) 
    def stateChange(self, selected):
        self._node.setSelected(selected)
    def nodeStateChanged(self, node):
        self.setOn(node._selected)
    def cleanup(self):
        self._node._selectionChange.remove(self.nodeStateChanged)
        self._node = None
    def setOn(self, b):
        if self.read_only:
            if b:
                self.setFlags(
                    Qt.ItemIsSelectable | Qt.ItemIsDragEnabled | Qt.ItemIsEnabled)
            else:
                self.setFlags(Qt.ItemIsSelectable | Qt.ItemIsDragEnabled)
        elif self.itemType == "radio":
            self.widget.setChecked(b)
        elif self.itemType == "check":
            if b:
                self.setCheckState(0, Qt.Checked)
            else:
                self.setCheckState(0, Qt.Unchecked)
    def isOn(self):
        if self.read_only:
            return int(self.flags() & Qt.ItemIsEnabled) > 0
        elif self.itemType == "radio":
            return self.widget.isChecked()
        elif self.itemType == "check":
            return self.checkState(0) == Qt.Checked
        return True
[docs]    def check(self, b):
        """
        This method is used to check or uncheck a checkable item and warn the underlying model of the state change.
        It is useful for the feature select/unselect before/after/all in pipelines and iterations.
        """
        if self.itemType == "check" and not self.read_only:
            if b:
                self.setCheckState(0, Qt.Checked)
            else:
                self.setCheckState(0, Qt.Unchecked)
            self.stateChange(b) 
[docs]    def currentItemChanged(self, current):
        """ This function is called when the item gains or lose the status of current item of the tree widget.
        In case the item is a radio button, its background and foreground colors are changed to follow the tree item widget policy.
        :param current: indicates if the item is the current item or not. boolean.
        """
        if self.itemType == "radio" and not self.read_only:
            if current:
                self.widget.setBackgroundRole(QPalette.Highlight)
                self.widget.setForegroundRole(QPalette.HighlightedText)
            else:
                self.widget.setBackgroundRole(QPalette.Base)
                self.widget.setForegroundRole(QPalette.Text)  
#------------------------------------------------------------------------------
[docs]class ParameterLabel(QLabel):
    '''A QLabel that emits signal 'contextMenuEvent' whenever a
    contextMenuEvent occurs'''
    toggleDefault = QtCore.Signal(str)
    lock_system = QtCore.Signal(str)
    unlock_system = QtCore.Signal(str)
    def __init__(self, parameterName, mandatory, parent, userLevel=0):
        if mandatory:
            printParameterName = '<b>' + parameterName + ':</b>'
        else:
            printParameterName = parameterName + ':'
        from soma.wip.application.api import Application
        if Application().configuration.brainvisa.showParamUserLevel:
            if userLevel == 1:
                printParameterName = '<sup style="color:orange;">1</sup> ' + \
                    
printParameterName
            elif userLevel == 2:
                printParameterName = '<sup style="color:red;">2</sup> ' + \
                    
printParameterName
            elif userLevel >= 3:
                printParameterName = '<sup style="color:blue;">*</sup> ' + \
                    
printParameterName
        QLabel.__init__(self, printParameterName, parent)
        # Save parameter name
        self.parameterName = parameterName
        self.printParameterName = printParameterName
        # Create popup menu
        self.contextMenu = QMenu(self)
        # self.contextMenu.setCheckable( True )
        self.default_id = _addAction(self.contextMenu, _t_('default value'),
                                     self.defaultChanged)
        self.default_id.setCheckable(True)
        self.default_id.setChecked(True)
        self.addMenuLock()
        self.setAutoFillBackground(True)
        self.setBackgroundRole(QPalette.Window)
    def set_read_only(self, read_only):
        self.default_id.setEnabled(not read_only)
    def paramLabelText(self,  val_default_id, val_lock_id):
        # CAS : val_default_id / val_lock_id
        # val_default_id = 0 pas valeur par defaut donc icone
        # val_default_id = 1 valeur par defaut donc pas icone
        text = ''  # cas 0 / 0
#    if
#        1 / 0
#        if 1 / 1
#    else 0 / 1
        if val_default_id:
            if val_lock_id:
                text =  text + '<img src="' + \
                    
os.path.join(
                        neuroConfig.iconPath, 'lock.png') + '" height="16"/> '
        else:
            text =  '<img src="' + \
                
os.path.join(
                    neuroConfig.iconPath, 'modified.png') + '" height="16"/> '
            if val_lock_id:
                text =  text + '<img src="' + \
                    
os.path.join(
                        neuroConfig.iconPath, 'lock.png') + '" height="16"/> '
        return(text)
    def addMenuLock(self):
        self.lock_id = _addAction(self.contextMenu, _t_('lock'),
                                  self.lockChanged)
        self.lock_id.setCheckable(False)
        self.lock_id.setChecked(False)
[docs]    def readText(self, txt):
        """ Function to return the value of text without value of tag for images """
        if six.text_type(txt).startswith('<img src='):
            x = txt.find('/> ')
            txt = txt[x + 3:]
            return txt
        return txt 
[docs]    def lockChanged(self, checked=False):
        """ This function is to lock or unlock data if a user click on the lock menu  """
        if checked:
            self.lock_system.emit(self.parameterName)
            # warning the value of lock_id can be become false if we can't write the lock file.
            # For example, if you try to lock a file which doesn't exist
        else:
            self.unlock_system.emit(self.parameterName)
            # txt = self.paramLabelText(self.default_id.isChecked(), False)
        # on remet a jour en fonction du resultat du unlock du fichier
        self.setlock(self.lock_id.isChecked()) 
        # label update is performed by setlock
[docs]    def setlock(self, default):
        """ This function is to set lock or unlock data """
        self.lock_id.setChecked(default)
        txt = self.paramLabelText(
            self.default_id.isChecked(), self.lock_id.isChecked())
        txt += self.printParameterName
        self.setText(six.text_type(txt)) 
    def defaultChanged(self, checked=False):
        # print("-- FUNCTION defaultChanged : neuroProcessesGUI /
        # ParameterLabel --", checked)
        self.toggleDefault.emit(self.parameterName)
        txt = self.paramLabelText(
            self.default_id.isChecked(), self.lock_id.isChecked())
        txt += self.printParameterName
        self.setText(six.text_type(txt))
    def setDefault(self, default):
        # print("-- FUNCTION setDefault : neuroProcessesGUI / ParameterLabel
        # --")
        self.default_id.setChecked(default)
        # self.lockChanged()
        txt = self.paramLabelText(
            self.default_id.isChecked(), self.lock_id.isChecked())
        txt += self.printParameterName
        self.setText(six.text_type(txt)) 
    # def defaultChanged( self, checked=False ):
        # self.default_id.toggle()
        # self.toggleDefault.emit(self.parameterName)
        # if self.default_id.isChecked():
            # txt = unicode( self.text() )
            # if txt.startswith( '<img src=' ):
            # x = txt.find( '/> ' )
            # txt = txt[ x+3 : ]
            # self.setText( txt )
        # else:
            # if not unicode( self.text() ).startswith( '<img src=' ):
            # self.setText( '<img src="' \
                    #+ os.path.join( neuroConfig.iconPath, 'modified.png' ) \
                    #+ '" height="16"/> ' + self.text() )
    # def setDefault( self, default ):
        # self.default_id.setChecked( default )
        # if default:
            # txt = unicode( self.text() )
            # if txt.startswith( '<img src=' ):
            # x = txt.find( '/> ' )
            # txt = txt[ x+3 : ]
            # self.setText( txt )
        # else:
            # if not unicode( self.text() ).startswith( '<img src=' ):
            # self.setText( '<img src="' \
                    #+ os.path.join( neuroConfig.iconPath, 'modified.png' ) \
                    #+ '" height="16"/> ' + self.text() )
#------------------------------------------------------------------------------
#----------------------------------------------------------------------------
[docs]class BrainVISAAnimation(QLabel):
    def __init__(self, parent=None):
        QLabel.__init__(self, parent)
        self.mmovie = QMovie(
            os.path.join(neuroConfig.iconPath, 'rotatingBrainVISA.gif'))  # , 1024*10 )
        self.setMovie(self.mmovie)
        # self.mmovie.setSpeed( 500 )
        # QApplication.instance().processEvents()
        self.mmovie.stop()
    def start(self):
        self.mmovie.start()
    def stop(self):
        self.mmovie.start()
        QApplication.instance().processEvents()
        self.mmovie.stop() 
#----------------------------------------------------------------------------
[docs]class SectionTitle(QLabel):
    """
    This widget is used to create a virtual signature separation, called implicitly by "section" argument.
    All parameters will be grouped by section in signature with this label text as title.
    **Examples**
    'bool_field', Boolean(section='Section title'),
    or
    'image', WriteDiskItem( '4D Volume', 'NIFTI-1 image', section='Output images' ),
    """
    def __init__(self, section_title, section_widgets=[], collapsed=False):
        if collapsed:
            printSectionTitle = '<span style="font-size:smaller;">▶</span> ' + \
                
section_title
        else:
            printSectionTitle = '<span style="font-size:smaller;">▼</span> ' + \
                
section_title
        QLabel.__init__(self, printSectionTitle)
        font = QFont()
        # font.setCapitalization(QFont.Capitalize)
        # font.setUnderline(True)
        font.setItalic(True)
        self.setFont(font)
        self.setAlignment(Qt.AlignCenter)
        styleSheet = """
SectionTitle { border-top: 1px solid #6c6c6c;
               border-top-style: double;
               border-top-color: silver; }"""
        self.setStyleSheet(styleSheet)
        self.section_title = section_title
        self.section_widgets = [w for w in section_widgets]
        self.collapsed = collapsed
[docs]    def mouseReleaseEvent(self, ev):
        self.changeVisibility() 
    def changeVisibility(self):
        self.setCollapsed(not self.collapsed)
    def setCollapsed(self, collapsed):
        self.collapsed = collapsed
        if self.collapsed:
            printSectionTitle = '<span style="font-size:smaller;">▶</span> ' + \
                
self.section_title
        else:
            printSectionTitle = '<span style="font-size:smaller;">▼</span> ' + \
                
self.section_title
        self.setText(printSectionTitle)
        for widget in self.section_widgets:
            if self.collapsed:
                action = 'hide'
                widget.hide()
            else:
                action = 'show'
                widget.show()
    def addSectionWidgets(self, *args):
        for w in args:
            self.section_widgets.append(w)
            if self.collapsed:
                w.hide()
            else:
                w.show() 
#----------------------------------------------------------------------------
[docs]class ProcessView(QWidget, ExecutionContextGUI):
    # actions:
    action_save_process = None
    action_clone_process = None
    action_create_workflow = None
    action_run = None
    action_interupt = None
    action_interupt_step = None
    action_run_with_sw = None
    action_iterate = None
    action_show_doc = None
    eTreeWidget = None
    menu = None
    parameterizedWidget = None
    read_only = None
    closed = QtCore.Signal()
    def __init__(self,
                 processId,
                 parent=None,
                 externalInfo=None,
                 read_only=False):
        ExecutionContextGUI.__init__(self)
        QWidget.__init__(self, parent)
        if getattr(ProcessView, 'pixIcon', None) is None:
            setattr(ProcessView, 'pixIcon', QIcon(
                os.path.join(neuroConfig.iconPath, 'icon_process.png')))
            setattr(ProcessView, 'pixDefault', QIcon(
                os.path.join(neuroConfig.iconPath, 'lock.png')))
            setattr(ProcessView, 'pixInProcess', QIcon(
                os.path.join(neuroConfig.iconPath, 'forward.png')))
            setattr(ProcessView, 'pixProcessFinished', QIcon(
                os.path.join(neuroConfig.iconPath, 'ok.png')))
            setattr(ProcessView, 'pixProcessError', QIcon(
                os.path.join(neuroConfig.iconPath, 'abort.png')))
            setattr(ProcessView, 'pixNone', QIcon())
        self.read_only = read_only
        # ProcessView cannot be a QMainWindow because it have to be included in a QStackedWidget in pipelines.
        # centralWidget=QWidget()
        # self.setCentralWidget(centralWidget)
        centralWidgetLayout = QVBoxLayout()
        self.setLayout(centralWidgetLayout)
        centralWidgetLayout.setContentsMargins(5, 5, 5, 5)
        centralWidgetLayout.setSpacing(4)
        self.setWindowIcon(self.pixIcon)
        self.workflowEnabled = False
        self.action_save_process = QAction(_t_('&Save...'), self)
        self.action_save_process.setShortcut(QKeySequence(Qt.CTRL | Qt.Key_S))
        self.action_save_process.triggered.connect(self.saveAs)
        self.action_clone_process = QAction(_t_('&Clone...'), self)
        self.action_clone_process.setShortcut(QKeySequence(Qt.CTRL | Qt.Key_C))
        self.action_clone_process.triggered.connect(self.clone)
        self.action_create_workflow = QAction(_t_('Create &Workflow...'), self)
        self.action_create_workflow.setShortcut(
            QKeySequence(Qt.CTRL | Qt.Key_D))
        self.action_create_workflow.triggered.connect(self.createWorkflow)
        self.action_run = QAction(_t_('Run'), self)
        self.action_run.triggered.connect(self._run)
        self.action_run_with_sw = QAction(_t_('Run in parallel'), self)
        self.action_run_with_sw.setToolTip(
            'Run in parallel using Soma-workflow')
        self.action_run_with_sw.triggered.connect(
            self._run_with_soma_workflow_button)
        self.action_interupt = QAction(_t_('Interrupt'), self)
        self.action_interupt.triggered.connect(self._interruptButton)
        self.action_interupt.setVisible(False)
        self.action_interupt_step = QAction(
            _t_('Interrupt current step'), self)
        self.action_interupt_step.triggered.connect(self._interruptStepButton)
        self.action_interupt_step.setVisible(False)
        self.action_iterate = QAction(_t_('Iterate'), self)
        self.action_iterate.triggered.connect(self._iterateButton)
        self.action_lock_all = QAction(_t_('Lock all files'), self)
        self.action_lock_all.triggered.connect(self.menuLockAllFiles)
        self.action_unlock_all = QAction(_t_('Unlock all files'), self)
        self.action_unlock_all.triggered.connect(self.menuUnlockAllfiles)
        self.action_manage_process_setups = QAction(
            _t_('Save process setups'), self)
        self.action_manage_process_setups.triggered.connect(
            self.saveProcessSetupsGUI)
        self.action_show_doc = QAction(_t_('Show process documentation'), self)
        self.action_show_doc.setShortcut(Qt.Key_F1)
        self.action_show_doc.triggered.connect(self.show_process_doc)
        if parent is None:
            neuroConfig.registerObject(self)
            # menu bar
            self.menu = QMenuBar(self)
            addBrainVISAMenu(self, self.menu)
            # warning: don't create the menu using addMenu() in PyQt
            processMenu = QMenu(_t_("&Process"), self.menu)
            self.menu.addMenu(processMenu)
            processMenu.addAction(self.action_save_process)
            processMenu.addAction(self.action_clone_process)
            processMenu.addAction(self.action_manage_process_setups)
            processMenu.addAction(self.action_iterate)
            _addSeparator(processMenu)
            processMenu.addAction(self.action_create_workflow)
            _addSeparator(processMenu)
            processMenu.addAction(self.action_run)
            processMenu.addAction(self.action_interupt)
            processMenu.addAction(self.action_interupt_step)
            _addSeparator(processMenu)
            processMenu.addAction(self.action_run_with_sw)
            _addSeparator(processMenu)
            processMenu.addAction(self.action_lock_all)
            processMenu.addAction(self.action_unlock_all)
            # warning: don't create the menu using addMenu() in PyQt
            view_menu = QMenu(_t_("&View"), self.menu)
            self.menu.addMenu(view_menu)
            view_menu.addAction(close_viewers_action(self))
            view_menu.addAction(self.action_show_doc)
            try:
                import soma_workflow
                self.workflowEnabled = True
            except ImportError:
                pass
            if not self.workflowEnabled:
                self.action_create_workflow.setEnabled(False)
            centralWidgetLayout.addWidget(self.menu)
        self.destroyed.connect(self.cleanup)
        process = brainvisa.processes.getProcessInstance(processId)
        if process is None:
            raise RuntimeError(neuroException.HTMLMessage(
                _t_('Cannot open process <em>%s</em>') % (str(processId), )))
        self.process = process
        self.process.guiContext = weakref.proxy(self)
        self._runningProcess = 0
        self.process.signatureChangeNotifier.add(self.signatureChanged)
        self.btnRun = None
        self.btnRunSomaWorkflow = None
        self.btnInterrupt = None
        self.btnInterruptStep = None
        self._running = False
        if self.process.__class__ == brainvisa.processes.IterationProcess:
            self.action_iterate.setVisible(False)
        procdoc = brainvisa.processes.readProcdoc(process)
        documentation = procdoc.get(neuroConfig.language)
        if documentation is None:
            documentation = procdoc.get('en', {})
        t = _t_(process.name) + ' ' + six.text_type(process.instance)
        self.setWindowTitle(t)
        if process.showMaximized:
            self.showMaximized()
        # title of the process : label + rotating icon when it's running
        titleLayout = QHBoxLayout()
        centralWidgetLayout.addLayout(titleLayout)
        if not parent:
            self.labName = QLabel(t, self)
        else:
            self.labName = QLabel(_t_(process.name), self)
        titleLayout.addWidget(self.labName)
        self.labName.setFrameStyle(QFrame.Panel | QFrame.Raised)
        self.labName.setLineWidth(1)
        self.labName.setContentsMargins(5, 5, 5, 5)
        self.labName.setAlignment(Qt.AlignCenter)
        self.labName.setWordWrap(True)
        font = self.labName.font()
        font.setPointSize(QFontInfo(font).pointSize() + 4)
        self.labName.setFont(font)
        self.labName.setSizePolicy(
            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Preferred))
        doc = XHTML.html(documentation.get('short', ''))
        if doc:
            self.labName.setToolTip(html_with_local_images(
                '<center><b>' + _t_(process.name) + '</b></center><hr><b>' + _t_('Description') + ':</b><br/>' + doc))
        if externalInfo is None:
            self.movie = BrainVISAAnimation()
            titleLayout.addWidget(self.movie)
            titleLayout.setSpacing(3)
            # vertical splitter : parameters, log text widget
            splitter = QSplitter(Qt.Vertical)
            centralWidgetLayout.addWidget(splitter)
            # at the top of the splitter : the parameters
            self.parametersWidget = QWidget(splitter)
            parametersWidgetLayout = QVBoxLayout()
            parametersWidgetLayout.setContentsMargins(0, 0, 0, 0)
            self.parametersWidget.setLayout(parametersWidgetLayout)
            # at the bottom of the splitter : the text widget to log
            # information about process execution
            self.info = QTextEdit(splitter)
            self.info.setReadOnly(True)
            self.info.setAcceptRichText(True)
            sizePolicy = QSizePolicy(
                QSizePolicy.Expanding, QSizePolicy.Preferred)
            sizePolicy.setHorizontalStretch(0)
            sizePolicy.setVerticalStretch(50)
            self.info.setSizePolicy(sizePolicy)
            self.infoCounter = None
            # splitter.setResizeMode( self.info, splitter.Stretch )
            # splitter.setResizeMode( self.parametersWidget, QSplitter.FollowSizeHint )
            # splitter.setResizeMode( self.parametersWidget, QSplitter.Auto )
            container = self.parametersWidget
            self.isMainWindow = True
        else:
            self.movie = None
            splitter = None
            self.parametersWidget = self
            container = self
            self.isMainWindow = False
            self.info = externalInfo
        self.splitter = splitter
        self._widgetStack = None
        eNode = getattr(process, '_executionNode', None)
        self._executionNodeLVItems = {}
        # process composed of several processes
        if eNode is not None and self.isMainWindow:
            self.parameterizedWidget = None
            # vb = QVBoxLayout( )
            # container.layout().addLayout(vb)
            vb = container.layout()
            # splitter that shows the composition of the process on the left
            # and the parameters of each step on the right
            self.eTreeWidget = QSplitter(Qt.Horizontal)
            vb.addWidget(self.eTreeWidget)
            # Run and iterate buttons
            self.inlineGUI = self.process.inlineGUI(self.process, self, None,
                                                    externalRunButton=True)
            if self.inlineGUI is None and externalInfo is None:
                self.inlineGUI = self.defaultInlineGUI(None)
            vb.addWidget(self.inlineGUI)
            # composition of the pipeline
            self.executionTree = QTreeWidget(self.eTreeWidget)
            self.executionTree.setSizePolicy(
                QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred))
            self.executionTree.setColumnCount(1)
            self.executionTree.setHeaderLabels(['Name'])
            self.executionTree.setAllColumnsShowFocus(1)
            self.executionTree.setRootIsDecorated(1)
            self.executionTree.setContextMenuPolicy(Qt.CustomContextMenu)
            # Popup Menu for toolboxes
            self.executionTreeMenu = QMenu(self)
            _addAction(self.executionTreeMenu, _t_(
                "Unselect before"), self.menuUnselectBefore)
            _addAction(self.executionTreeMenu, _t_(
                "Unselect after"), self.menuUnselectAfter)
            # self.executionTreeMenu.addAction( _t_("Unselect all"),
            # self.menuUnselectAll )
            _addAction(self.executionTreeMenu, _t_(
                "Select before"), self.menuSelectBefore)
            _addAction(self.executionTreeMenu, _t_(
                "Select after"), self.menuSelectAfter)
            # self.executionTreeMenu.addAction( _t_("Select all"),
            # self.menuSelectAll )
            _addAction(self.executionTreeMenu)
            _addAction(self.executionTreeMenu, _t_(
                "Unselect steps writing locked files"), self.menuUnselectLocked)
            _addAction(self.executionTreeMenu, _t_(
                "Unselect steps upstream of locked files"), self.menuUnselectLockedUpstream)
            _addAction(self.executionTreeMenu)
            self.executionTreeMenu._opennodeaction = _addAction(
                self.executionTreeMenu, _t_("Open this step separately"),
              self.menuDetachExecutionNode)
            self.executionTreeMenu._showdocaction \
                
= _addAction(self.executionTreeMenu, _t_("Show documentation"),
                             self.menuShowDocumentation)
            _addAction(self.executionTreeMenu)
            _addAction(
                self.executionTreeMenu, _t_("Lock files under this node"),
                       self.menuLockStep)
            _addAction(
                self.executionTreeMenu, _t_("Unlock files under this node"),
                       self.menuUnlockStep)
            self.executionTreeMenu._nodeactionseparator \
                
= _addAction(self.executionTreeMenu)
            self.executionTreeMenu._addnodeaction \
                
= _addAction(self.executionTreeMenu, _t_("Add node"),
                             self.menuAddExecutionNode)
            self.executionTreeMenu._removenodeaction \
                
= _addAction(self.executionTreeMenu, _t_("Remove node"),
                             self.menuRemoveExecutionNode)
            self.executionTree.customContextMenuRequested.connect(
                self.openContextMenu)
            # self.executionTree.setSortingEnabled( -1 )
            # self.eTreeWidget.setResizeMode( self.executionTree,
            # QSplitter.KeepSize )
            if self.read_only:
                self.executionTreeMenu.setEnabled(False)
            # parameters of a each step of the pipeline
            self._widgetStack = QStackedWidget(self.eTreeWidget)
            self._widgetStack.setSizePolicy(QSizePolicy(QSizePolicy.Preferred,
                                                        QSizePolicy.Preferred))
            self._widgetStack._children = []
            # set splitter sizes to avoid the widget stack to be hidden in case
            # it is currently empty
            self.eTreeWidget.setSizes([150, 250])
            self._guiId = 0
            self._executionNodeExpanded(self.executionTree, (eNode, (eNode,)))
            self.executionTree.itemExpanded.connect(
                self._executionNodeExpanded)
            self.executionTree.currentItemChanged.connect(
                self.executionNodeSelected)
            # don't listen to clicks, but to item changes instead
            # because stats check can be triggered via the keyboard, not only
            # mouse clicks
            # self.executionTree.itemClicked.connect(self.executionNodeClicked)
            self.executionTree.itemChanged.connect(self.executionNodeChanged)
            # Select and open the first item
            item = self.executionTree.topLevelItem(0)
            if item is not None:
                item.setExpanded(True)
                self.executionTree.setCurrentItem(item)
        # simple process : only a signature, no sub-processes
        else:
            self.executionTree = None
            self.createSignatureWidgets(documentation)
        self._iterationDialog = None
        self._logDialog = None
        # It is necessary to call show() before resize() to have a correct layout.
        # Otherwize,  vertical sliders may be superimposed to widgets. But some
        # window managers compute the window location according to its size during
        # show(). If the window become larger after show() call, part of it will be
        # off screen. Therefore set the widget size before show() ensure that
        # window location is correct and changing size after show() ensure that
        # layout is correct.
        if parent is None:
            self.show()
            self.resize(600, 801)
            # QApplication.instance().processEvents()
        initGUI = getattr(self.process, 'initializationGUI', None)
        if initGUI is not None:
            initGUI(self)
        if self.read_only and self.parameterizedWidget != None:
            self.parameterizedWidget.set_read_only(True)
    def createSignatureWidgets(self, documentation=None):
        eNode = getattr(self.process, '_executionNode', None)
        signatureWidget = None
        # if the process has a signature, creates a widget for the parameters :
        # ParameterizedWidget
        if eNode and self.isMainWindow:
            parent = self._widgetStack
            if self.process.signature:
                signatureWidget = eNode.gui(parent, processView=self)
        else:
            parent = self.parametersWidget.layout()
            if self.process.signature:
                signatureWidget = ParameterizedWidget(self.process, None)
                parent.addWidget(signatureWidget)
            self.parameterizedWidget = signatureWidget
        if eNode is None or not self.isMainWindow:
            self.inlineGUI = self.process.inlineGUI(self.process, self, None)
            if self.inlineGUI is None and self.isMainWindow:
                self.inlineGUI = self.defaultInlineGUI(None)
            if self.inlineGUI is not None:
                parent.addWidget(self.inlineGUI)
        else:
            self._widgetStack.removeWidget(self._widgetStack._children[0])
            self._widgetStack._children[0].close()
            self._widgetStack._children[0].deleteLater()
            if signatureWidget is not None:
                self._widgetStack.insertWidget(0, signatureWidget)
            self._widgetStack._children[0] = signatureWidget
            self._widgetStack.setCurrentIndex(0)
#    if self.parameterizedWidget is not None:
#      if documentation is not None:
#        for ( k, p ) in list(self.process.signature.items()):
#          if neuroConfig.userLevel >= p.userLevel:
#            self.parameterizedWidget.setParameterToolTip( k,
#              XHTML.html( documentation.get( 'parameters', {} ).get( k, '' ) ) \
#              + '<br/><img src="' \
#              + os.path.join( neuroConfig.iconPath, 'lock.png' )+ '"/><em>: ' \
#              + _t_( \
#              'value has been manually changed and is not linked anymore' ) \
#              + '</em>' )
#      self.parameterizedWidget.show()
#    if self.inlineGUI is not None:
#      self.inlineGUI.show()
        if self.parameterizedWidget != None:
            self.parameterizedWidget.set_read_only(self.read_only)
    def eraseSignatureWidgets(self):
        if self.parameterizedWidget is not None:
            self.parameterizedWidget.close()
            self.parameterizedWidget.deleteLater()
        eNode = getattr(self.process, '_executionNode', None)
        if eNode is None and self.inlineGUI is not None:
            self.inlineGUI.close()
            self.inlineGUI.deleteLater()
    def signatureChanged(self, process):
        self.eraseSignatureWidgets()
        self.createSignatureWidgets(None)
    # Execution tree menu
    def changeItemSelection(self, select=True, all=True, before=False):
        item = self.executionTree.currentItem()
        if item:
            parent = item.parent()
            if parent:
                if all:
                    r = range(parent.childCount())
                elif before:
                    r = range(parent.indexOfChild(item))
                else:  # after
                    r = range(
                        parent.indexOfChild(item) + 1, parent.childCount())
                for i in r:
                    parent.child(i).check(select)
            else:
                parent = item.treeWidget()
                if all:
                    r = range(parent.topLevelItemCount())
                elif before:
                    r = range(parent.indexOfTopLevelItem(item))
                else:  # after
                    r = range(parent.indexOfTopLevelItem(
                        item) + 1, parent.topLevelItemCount())
                for i in r:
                    parent.topLevelItem(i).check(select)
    def menuUnselectBefore(self):
        self.changeItemSelection(select=False, all=False, before=True)
    def menuUnselectAfter(self):
        self.changeItemSelection(select=False, all=False, before=False)
    # def menuUnselectAll(self):
        # self.changeItemSelection(select=False, all=True, before=False)
    def menuSelectBefore(self):
        self.changeItemSelection(select=True, all=False, before=True)
    def menuSelectAfter(self):
        self.changeItemSelection(select=True, all=False, before=False)
    # def menuSelectAll(self):
        # self.changeItemSelection(select=True, all=True, before=False)
    def lockedSteps(self, useUnselected=False):
        items = [self.process.executionNode()]
        locked = []
        while items:
            item = items.pop()
            if not useUnselected and not item.isSelected():
                continue
            children = list(item.children())
            if len(children) == 0:  # terminal node
                process = item._process
                for n, p in six.iteritems(process.signature):
                    if isinstance(p, WriteDiskItem):
                        v = getattr(item._process, n)
                        # test if data is locked
                        if v is not None and v.isLockData():
                            locked.append(item)
            else:
                if useUnselected:
                    items += list(item.children())
                else:
                    items += [x for x in item.children() if x.isSelected()]
        return locked
    def parentNodes(self, enode):
        items = [[self.process.executionNode()]]
        chain = []
        # walk the tree "vertically" getting deep in branches first
        while items:
            iteml = items[-1]
            if len(iteml) == 0:
                items.pop()
                chain.pop()
                continue
            item = iteml.pop()
            if item is enode:
                return chain
            children = list(item.children())
            if len(children) != 0:
                chain.append(item)
                items.append(list(item.children()))
    def menuUnselectLocked(self):
        locked = self.lockedSteps()
        for item in locked:
            if item._optional:
                item.setSelected(False)
            else:
                parents = [x for x in self.parentNodes(item) if x._optional]
                if parents:
                    parents[-1].setSelected(False)
    def menuUnselectLockedUpstream(self):
        items = self.lockedSteps(useUnselected=True)
        while items:
            item = items.pop()
            if item._optional:
                item.setSelected(False)
            else:
                parents = [x for x in self.parentNodes(item) if x._optional]
                if parents:
                    parents[-1].setSelected(False)
                    items.append(parents[-1])
                    continue
            parents = self.parentNodes(item)
            parents.reverse()
            for p in parents:
                if p.isSelected():
                    if p.__class__ is brainvisa.processes.SerialExecutionNode:
                        for sp in p.children():
                            if sp is item:
                                break
                            if sp._optional:
                                sp.setSelected(False)
                            else:
                                sparents = [
                                    x for x in self.parentNodes(sp) if x._optional]
                                if sparents:
                                    sparents[-1].setSelected(False)
                    item = p
    def _changeAllLockedFiles(self, procOrNode, setLock):
        files = procOrNode.allParameterFiles()
        # filter out non-existing files and already locked ones
        if setLock:
            files = [
                f for f in files if f.isWriteable() and not f.isLockData()]
        else:
            files = [f for f in files if f.isWriteable() and f.isLockData()]
        # show and confirm
        dialog = lockFilesGUI.LockedFilesListEditor(self, files, setLock)
        if dialog.exec():
            files = dialog.selectedDiskItems()
            if files:
                if setLock:
                    print('Locking...')
                    for f in files:
                        try:
                            f.lockData()
                        except IOError:
                            pass  # probably not writeable
                    print('done.')
                else:
                    print('Unlocking...')
                    for f in files:
                        try:
                            f.unlockData()
                        except IOError:
                            pass
                    print('done.')
    def menuLockAllFiles(self):
        self._changeAllLockedFiles(self.process, True)
    def menuUnlockAllfiles(self):
        self._changeAllLockedFiles(self.process, False)
    def menuDetachExecutionNode(self):
        item = self.executionTree.currentItem()
        if item:
            proc = item._executionNode
            self.readUserValues()
            event = ProcessExecutionEvent()
            event.setProcess(brainvisa.processes.getProcessInstance(proc))
            clone = brainvisa.processes.getProcessInstanceFromProcessEvent(
                event)
            return showProcess(clone)
    def menuShowDocumentation(self):
        try:
            global _mainWindow
            item = self.executionTree.currentItem()
            if item:
                enode = item._executionNode
                if hasattr(enode, '_process'):
                    proc = enode._process
                    doc = brainvisa.processes.getHTMLFileName(proc)
                    if doc is not None and os.path.exists(doc):
                        _mainWindow.info.setSource(doc)
        except Exception:
            showException()
    def menuLockStep(self):
        global _mainWindow
        item = self.executionTree.currentItem()
        if item:
            enode = item._executionNode
            self._changeAllLockedFiles(enode, True)
    def menuUnlockStep(self):
        global _mainWindow
        item = self.executionTree.currentItem()
        if item:
            enode = item._executionNode
            self._changeAllLockedFiles(enode, False)
    def menuAddExecutionNode(self):
        item = self.executionTree.currentItem()
        if item:
            enode = item._executionNode
            if isinstance(enode, brainvisa.processes.ProcessExecutionNode):
                enode = enode._executionNode
            if isinstance( enode, brainvisa.processes.SerialExecutionNode ) \
               
and enode.possibleChildrenProcesses:
                defaultProcess = list(enode.possibleChildrenProcesses.keys())[0]
                defaultProcessOptions = enode.possibleChildrenProcesses[
                    defaultProcess]
                child = brainvisa.processes.ProcessExecutionNode(
                    defaultProcess,
                          optional=defaultProcessOptions.get(
                              'optional', True),
                          selected=defaultProcessOptions.get(
                              'selected', True),
                          expandedInGui=defaultProcessOptions.get(
                              'expandedInGui', False)
                )
                enode.addChild(node=child)
    def menuRemoveExecutionNode(self):
        item = self.executionTree.currentItem()
        parent = item.parent()
        if parent:
            pnode = parent._executionNode
            if isinstance(pnode, brainvisa.processes.ProcessExecutionNode):
                pnode = pnode._executionNode
            if isinstance( pnode, brainvisa.processes.SerialExecutionNode ) \
                    
and pnode.possibleChildrenProcesses:
                n = pnode.childrenNames()
                for k in n:
                    c = pnode._children[k]
                    if c and ((c is item._executionNode)
                              or (weakref.proxy(c) is item._executionNode)):
                        pnode.removeChild(k)
    def defaultInlineGUI(self, parent, externalRunButton=False, container=None):
        if container is None:
            container = QWidget()
            layout = QHBoxLayout()
            container.setLayout(layout)
            layout.setContentsMargins(5, 5, 5, 5)
        else:
            layout = container.layout()
        if not externalRunButton:
            self.btnRun = QToolButton(self)
            self.btnRun.setDefaultAction(self.action_run)
            self.btnRun.setToolButtonStyle(Qt.ToolButtonTextOnly)
            layout.addWidget(self.btnRun)
            self.btnRun.setMinimumWidth(90)
            self.btnRun.setSizePolicy(
                QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
            self.btnInterrupt = QToolButton(self)
            self.btnInterrupt.setDefaultAction(self.action_interupt)
            self.btnInterrupt.setToolButtonStyle(Qt.ToolButtonTextOnly)
            layout.addWidget(self.btnInterrupt)
            self.btnInterrupt.setMinimumWidth(90)
            self.btnInterrupt.setSizePolicy(
                QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
            self.btnInterrupt.setVisible(False)
            self.btnInterruptStep = QToolButton(self)
            self.btnInterruptStep.setDefaultAction(self.action_interupt_step)
            self.btnInterruptStep.setToolButtonStyle(Qt.ToolButtonTextOnly)
            layout.addWidget(self.btnInterruptStep)
            self.btnInterruptStep.setMinimumWidth(90)
            self.btnInterruptStep.setSizePolicy(
                QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
            self.btnInterruptStep.setVisible(False)
        self.btnIterate = QToolButton(self)
        self.btnIterate.setDefaultAction(self.action_iterate)
        self.btnIterate.setToolButtonStyle(Qt.ToolButtonTextOnly)
        layout.addWidget(self.btnIterate)
        self.btnIterate.setMinimumWidth(90)
        self.btnIterate.setSizePolicy(
            QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
        if self.process.__class__ == brainvisa.processes.IterationProcess:
            self.btnIterate.setVisible(False)
        if self.process.executionNode() != None and _workflow_application_model != None:
            self.btnRunSomaWorkflow = QToolButton(self)
            self.btnRunSomaWorkflow.setDefaultAction(self.action_run_with_sw)
            self.btnRunSomaWorkflow.setToolButtonStyle(Qt.ToolButtonTextOnly)
            layout.addWidget(self.btnRunSomaWorkflow)
            self.btnRunSomaWorkflow.setMinimumWidth(90)
            self.btnRunSomaWorkflow.setSizePolicy(
                QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
        else:
            self.action_run_with_sw.setVisible(False)
        container.setSizePolicy(
            QSizePolicy(QSizePolicy.MinimumExpanding, QSizePolicy.Fixed))
        container.setMaximumHeight(container.sizeHint().height())
        return container
    def getEditor(self, parameterName):
        if self.parameterizedWidget is not None:
            return self.parameterizedWidget.editors.get(parameterName)
        return None
[docs]    def closeEvent(self, event):
        self.cleanup()
        self.closed.emit()
        QWidget.closeEvent(self, event) 
    def cleanup(self):
        self.process.cleanup()
        if self.parameterizedWidget is not None:
            self.parameterizedWidget.cleanup()
        if self._widgetStack is not None:
            for gui in self._widgetStack._children:
                cleanup = getattr(gui, 'cleanup', None)
                if cleanup is not None:
                    cleanup()
                if gui is not None and sip is not None and not sip.isdeleted(gui):
                    gui.deleteLater()
            self._widgetStack = None
        if self.executionTree is not None and QTreeWidgetItemIterator:
            it = QTreeWidgetItemIterator(self.executionTree)
            while it.value():
                cleanup = getattr(it.value(), 'cleanup', None)
                if cleanup is not None:
                    cleanup()
                it += 1
            self.executionTree = None
        if neuroConfig:
            neuroConfig.unregisterObject(self)
        self.process.signatureChangeNotifier.remove(self.signatureChanged)
        self._executionNodeLVItems.clear()
        self.parametersWidget = None
        self.info = None
        self.process._lastResult = None
    def _run(self):
        self._runButton()
    def _runButton(self, executionFunction=None):
        try:
            try:
                # disable run button when clicked to avoid several successive clicks
                # it is enabled when the process starts, the label of the
                # button switch to interrupt
                self.action_run.setEnabled(False)
                processView = self._checkReloadProcess()
                if processView is None:
                    processView = self
                    processView.info.setText('')
                else:
                    processView.info.setText('')
                    processView.warning(
                        _t_('processes %s updated') % _t_(processView.process.name))
                if hasattr(self.process, 'executionWorkflow'):
                    return self._run_with_soma_workflow(
                        executionFunction=executionFunction)
                else:
                    processView._runningProcess = 0
                    processView._startCurrentProcess(executionFunction)
            except Exception:
                neuroException.showException()
        finally:
            self.action_run.setEnabled(True)
    def _run_with_soma_workflow(self, executionFunction=None):
        from brainvisa.workflow import ProcessToSomaWorkflow
        submission_dlg = WorkflowSubmissionDlg(self)
        if submission_dlg.exec() != QtGui.QDialog.Accepted:
            return
        resource_id = submission_dlg.combo_resource.currentText()
        if resource_id != _workflow_application_model.current_resource_id:
            _workflow_application_model.set_current_connection(resource_id)
        qtdt = submission_dlg.dateTimeEdit_expiration.dateTime()
        date = datetime(
            qtdt.date().year(), qtdt.date().month(), qtdt.date().day(),
                        qtdt.time().hour(), qtdt.time().minute(), qtdt.time().second())
        queue = six.ensure_str(six.text_type(submission_dlg.combo_queue.currentText()),
                               'utf-8')
        if queue == "default queue":
            queue = None
        input_file_processing = submission_dlg.combo_in_files.currentText()
        output_file_processing = submission_dlg.combo_out_files.currentText()
        brainvisa_cmd = [os.path.basename(sys.executable),
                         '-m', 'brainvisa.axon.runprocess']
        self.readUserValues()
        builtin_db = []
        if input_file_processing == ProcessToSomaWorkflow.BV_DB_SHARED_PATH:
            for db_setting in neuroConfig.dataPath:
                if db_setting.builtin:
                    uuid = db_setting.expert_settings.uuid
                    if uuid:
                        builtin_db.append(uuid)
                    else:
                        print("warning ! db " + repr(
                            db_setting.directory) + " has no uuid.")
        ptowf = ProcessToSomaWorkflow(
            self.process,
            input_file_processing=input_file_processing,
            output_file_processing=output_file_processing,
            brainvisa_cmd=brainvisa_cmd,
            brainvisa_db=builtin_db,
            context=self)
        workflow = ptowf.doIt()
        name = six.text_type(submission_dlg.lineedit_wf_name.text())
        if name == "":
            if workflow.name != None:
                name = workflow.name  # brainvisa_code is already included
            else:
                name = SomaWorkflowWidget.brainvisa_code
        else:
            name = SomaWorkflowWidget.brainvisa_code + name
        # store the process in workflow.user_storage
        serialized_process = StringIO()
        event = self.createProcessExecutionEvent()
        event.save(serialized_process)
        to_store = [
            SomaWorkflowWidget.brainvisa_code, serialized_process.getvalue()]
        workflow.user_storage = to_store
        serialized_process.close()
        _workflow_application_model.add_workflow(
            soma_workflow.gui.workflowGui.NOT_SUBMITTED_WF_ID,
                              datetime.now() + timedelta(days=5),
                              name,
                              soma_workflow.constants.WORKFLOW_NOT_STARTED,
                              workflow)
        (wf_id, resource_id) = _mainWindow.sw_widget.submit_workflow(date,
                                                                     name,
                                                                     queue)
        if wf_id == None:
            return
        view = SomaWorkflowProcessView(
            _workflow_application_model,
                              wf_id,
                              resource_id,
                              process=self.process,
                              parent=_mainWindow)
        view.setAttribute(QtCore.Qt.WA_DeleteOnClose)
        view.show()
    def _run_with_soma_workflow_button(self, executionFunction=None):
        try:
            self._run_with_soma_workflow(executionFunction=executionFunction)
        except Exception:
            neuroException.showException()
        finally:
            self.action_run_with_sw.setEnabled(True)
    def _interruptButton(self):
        if self._running:
            try:
                self._setInterruptionRequest(
                    brainvisa.processes.ExecutionContext.UserInterruption())
            except Exception:
                neuroException.showException()
    def _interruptStepButton(self, executionFunction=None):
        if self._running:
            self._setInterruptionRequest(
                brainvisa.processes.ExecutionContext.UserInterruptionStep())
    def _checkReloadProcess(self):
        self.readUserValues()
        reload = False
        for p in self.process.allProcesses():
            pp = brainvisa.processes.getProcess(p)
            if pp is not None and pp is not p and pp is not p.__class__:
                reload = True
                break
        result = None
        if reload:
            eNode = getattr(self.process, '_executionNode', None)
            if eNode is None:
                # Get current process arguments values
                event = ProcessExecutionEvent()
                event.setProcess(self.process)
                # Forget about old process
                self.process.signatureChangeNotifier.remove(
                    self.signatureChanged)
                self.eraseSignatureWidgets()
                # Care about new process
                self.process = brainvisa.processes.getProcessInstanceFromProcessEvent(
                    event)
                self.process.signatureChangeNotifier.add(
                    self.signatureChanged)
                procdoc = brainvisa.processes.readProcdoc(self.process)
                documentation = procdoc.get(neuroConfig.language)
                if documentation is None:
                    documentation = procdoc.get('en', {})
                self.createSignatureWidgets(documentation)
                result = self
            else:
                result = self.clone()
                self.deleteLater()
        return result
    def _startCurrentProcess(self, executionFunction):
        # Remove icon from all ListView items
        for item in self._executionNodeLVItems.values():
            item.setIcon(0, self.pixNone)
        self._lastProcessRaisedException = None
        try:
            self._startProcess(self.process, executionFunction)
            self._running = True
        except Exception as e:
            self._lastProcessRaisedException = e
            neuroException.showException()
    def _write(self, html):
        mainThreadActions().push(self._appendInfo, html)
    def _appendInfo(self, msg):
        # the tags font are here just to avoid display problems in QTextEdit
        # with non html content (color staying red after an error for example)
        if not self.info is None:
            self.info.append("<font>" + msg + "</font>")
    def _processStarted(self):
        if self._depth() == 1:
            if self.movie is not None:
                mainThreadActions().push(self.movie.start)
            mainThreadActions().push(self.action_run.setEnabled, False)
            mainThreadActions().push(self.action_interupt.setVisible, True)
            if self.process.__class__ == brainvisa.processes.IterationProcess:
                mainThreadActions().push(
                    self.action_interupt_step.setVisible, True)
            if self.btnRun != None:
                mainThreadActions().push(self.btnRun.setVisible, False)
                mainThreadActions().push(self.btnInterrupt.setVisible, True)
                if self.process.__class__ == brainvisa.processes.IterationProcess:
                    mainThreadActions().push(
                        self.btnInterruptStep.setVisible, True)
        # Adds an icon on the ListViewItem corresponding to the current process
        # if any
        p = self._currentProcess()
        eNodeItem = self._executionNodeLVItems.get(p)
        if eNodeItem is not None:
            mainThreadActions().push(eNodeItem.setIcon, 0, self.pixInProcess)
        ExecutionContextGUI._processStarted(self)
    def _processFinished(self, result):
        self.process._lastResult = result
        ExecutionContextGUI._processFinished(self, result)
        # Remove icon from the ListViewItem corresponding to the current process
        # if any
        p = self._currentProcess()
        eNodeItem = self._executionNodeLVItems.get(p)
        if eNodeItem is not None:
            if self._lastProcessRaisedException:
                mainThreadActions().push(
                    eNodeItem.setIcon, 0, self.pixProcessError)
            else:
                mainThreadActions().push(eNodeItem.setIcon, 0,
                                         self.pixProcessFinished)
        if self._depth() == 1:
            if self.movie is not None:
                mainThreadActions().push(self.movie.stop)
            mainThreadActions().push(self.action_run.setEnabled, True)
            mainThreadActions().push(self.action_interupt.setVisible, False)
            if self.process.__class__ == brainvisa.processes.IterationProcess:
                mainThreadActions().push(
                    self.action_interupt_step.setVisible, False)
            if self.btnRun != None:
                mainThreadActions().push(self.btnRun.setVisible, True)
                mainThreadActions().push(self.btnInterrupt.setVisible, False)
                if self.process.__class__ == brainvisa.processes.IterationProcess:
                    mainThreadActions().push(
                        self.btnInterruptStep.setVisible, False)
            mainThreadActions().push(self._checkReadable)
            self._running = False
        else:
            mainThreadActions().push(self._checkReadable)
[docs]    def system(self, *args, **kwargs):
        ret = ExecutionContextGUI.system(self, *args, **kwargs)
        mainThreadActions().push(self._checkReadable)
        return ret 
    def _checkReadable(self):
        if self.parameterizedWidget is not None:
            self.parameterizedWidget.checkReadable()
        if self.executionTree is not None:
            for gui in self._widgetStack._children:
                checkReadable = getattr(gui, '_checkReadable', None)
                if checkReadable is not None:
                    checkReadable()
[docs]    def dialog(self, modal, message, signature, *buttons):
        return mainThreadActions().call(UserDialog, self, modal,
                                        message, signature, buttons) 
    # def showCallScript( self ):
        # text = "defaultContext().runProcess( '" + self.process.id() + "'"
        # text2 = ''
        # for n in self.process.signature.keys():
            # if not self.process.isDefault( n ):
                # text2 += '  ' + n + ' = ' + repr( self.editors[ n ].getValue() ) + ',\n'
        # if text2:
            # text += ',\n' + text2
        # text += ')\n'
        # txt = TextEditor( text )
        # txt.resize( 800, 600 )
        # txt.show()
    def executionNodeRemoveChild(self, item, eNode, key=None, childNode=None):
        childItem = getattr(childNode, '_guiItem', None)
        if childItem:
            # Remove matching child item
            for i in range(item.childCount()):
                c = item.child(i)
                if (childItem == c) or (childItem is weakref.proxy(c)):
                    item.takeChild(i)
                    break
        func = getattr(item, 'hasChildren', None)
        # Update children indicator for the current item
        if func and func():
            item.setChildIndicatorPolicy(item.DontShowIndicator)
    def executionNodeAddChild(self, item, eNode, key=None, childNode=None,
                              previous=None):
        # 10-02-2014 : added possibility to hide nodes in GUI
        if isinstance(item, QTreeWidgetItem) and not item.isExpanded():
            item.setChildIndicatorPolicy(item.ShowIndicator)
            return item
        newItem = None
        oldItem = getattr(childNode, '_guiItem', None)
        if oldItem:
            # WARNING: an execution node has *one* _guiItem, so the current design
            # only allows to have one widget view to the process tree. Thus this
            # forbids displaying in a workflow window the pipeline if it is already
            # displayed in a ProcessView. Even more problematic, it prevents
            # displaying in a workflow window if it happened to have a GUI once in
            # the past, since _guiItem is not cleared when the widget is
            # closed.
            oldItem.setHidden(getattr(childNode, '_hidden', False))
            newItem = oldItem
        else:
            if isinstance(childNode, brainvisa.processes.ProcessExecutionNode):
                en = childNode._executionNode
                if en is None:
                    en = childNode
            else:
                en = childNode
            if eNode is not childNode \
                
and (isinstance(eNode, brainvisa.processes.SelectionExecutionNode)
                     or (isinstance(eNode, brainvisa.processes.ProcessExecutionNode)
                         and isinstance(eNode._executionNode, brainvisa.processes.SelectionExecutionNode))):
                itemType = "radio"
            elif childNode._optional:
                itemType = "check"
            else:
                itemType = None
            # Try to insert node at the matching index
            if key:
                index = eNode._children.index(key)
            else:
                index = None
            name = childNode.name()
            newItem = NodeCheckListItem(childNode, item, index, _t_(name),
                                        itemType, read_only=self.read_only)
            newItem.setHidden(getattr(childNode, '_hidden', False))
            if isinstance(childNode, weakref.ProxyType):
                newItem._executionNode = childNode
            else:
                newItem._executionNode = weakref.proxy(childNode)
            if isinstance(newItem, weakref.ProxyType):
                childNode._guiItem = newItem
            else:
                childNode._guiItem = weakref.proxy(newItem)
            # Add callback to warn about child add and remove
            beforeChildRemovedCallback = getattr(
                en, 'beforeChildRemoved', None)
            if beforeChildRemovedCallback:
                beforeChildRemovedCallback.add(
                    soma.functiontools.partial(
                        self.__class__.executionNodeRemoveChild, weakref.proxy(
                            self),
                        newItem))
            afterChildAddedCallback = getattr(en, 'afterChildAdded', None)
            if afterChildAddedCallback:
                afterChildAddedCallback.add(
                    soma.functiontools.partial(
                        self.__class__.executionNodeAddChild, weakref.proxy(
                            self),
                        newItem))
            # set children indicator for the new item
            hiddens = [getattr(c, '_hidden', False) for c in en.children()]
            hasvisiblechildren = (len([x for x in hiddens if not x]) > 0)
            if en.hasChildren() and hasvisiblechildren:
                newItem.setChildIndicatorPolicy(newItem.ShowIndicator)
            # newItem.setExpandable( en.hasChildren() )
            if isinstance(childNode, brainvisa.processes.ProcessExecutionNode):
                self._executionNodeLVItems[childNode._process] = newItem
            # Update children indicator for the current item
            hiddens = [getattr(c, '_hidden', False)
                       for c in eNode.children()]
            hasvisiblechildren = (len([x for x in hiddens if not x]) > 0)
            func = getattr(item, 'hasChildren', None)
            if func and func() \
                    
and hasvisiblechildren:
                item.setChildIndicatorPolicy(item.ShowIndicator)
        if self._depth():
            p = self._currentProcess()
            eNodeItem = self._executionNodeLVItems.get(p)
            if eNodeItem is not None:
                eNodeItem.setIcon(0, self.pixInProcess)
        return newItem
    def executionNodeSelected(self, item, previous):
        if item is not None:
            if (getattr(item, "_guiId", None)) is not None:
                self._widgetStack.setCurrentIndex(item._guiId)
            else:
                gui = item._executionNode.gui(
                    self._widgetStack, processView=self)
                if gui is not None:
                    item._guiId = self._widgetStack.addWidget(gui)
                    self._widgetStack._children.append(gui)
                    self._guiId += 1
                else:
                    self._emptyWidget = QWidget(self._widgetStack)
                    item._guiId = self._widgetStack.addWidget(
                        self._emptyWidget)
                self._widgetStack.setCurrentIndex(item._guiId)
            item.currentItemChanged(True)
        if previous is not None:
            previous.currentItemChanged(False)
            # Trick to have correct slider
#      size = self.size()
            # self.resize( size.width()+1, size.height() )
            # QApplication.instance().processEvents()
            # self.resize( size )
    # def executionNodeClicked( self, item, column ):
        # item.itemClicked()
    def executionNodeChanged(self, item, column):
        if hasattr(item, '_node') and item._node._selected != item.isOn() \
                
and (not hasattr(item, 'creating') or not item.creating):
            item.itemClicked()
    def _executionNodeExpanded(self, item, eNodeAndChildren=None):
        if item is not None and getattr(item, '_notExpandedYet', True):
            item._notExpandedYet = False
            previous = None
            if eNodeAndChildren is None:
                eNode = item._executionNode
                eNodeChildren = (eNode.child(k)
                                 for k in eNode.childrenNames())
            else:
                eNode, eNodeChildren = eNodeAndChildren
            for childNode in eNodeChildren:
                previous = self.executionNodeAddChild(item, eNode,
                                                      childNode=childNode)
                if childNode._expandedInGui and previous is not None:
                    previous.setExpanded(True)
    def _executionNodeActivated(self, item):
        if getattr(item, "activate", None):
            item.activate()
    def createWorkflow(self):
        from brainvisa.workflow import ProcessToSomaWorkflow
        class Options(HasSignature):
            signature = SomaSignature(
                'output',
              SomaFileName,
              dict(doc='Name of the output workflow file.'),
              'input_file_processing',
              SomaChoice((_t_(ProcessToSomaWorkflow.NO_FILE_PROCESSING), 0),
                                     (_t_(
                                         ProcessToSomaWorkflow.FILE_TRANSFER), 1),
                                     (_t_(
                                      ProcessToSomaWorkflow.SHARED_RESOURCE_PATH), 2),
                                     (_t_(ProcessToSomaWorkflow.BV_DB_SHARED_PATH), 3)),
              dict(defaultValue=0),
              'output_file_processing',
              SomaChoice((_t_(ProcessToSomaWorkflow.NO_FILE_PROCESSING), 0),
                                     (_t_(
                                         ProcessToSomaWorkflow.FILE_TRANSFER), 1),
                                     (_t_(ProcessToSomaWorkflow.SHARED_RESOURCE_PATH), 2)),
              dict(defaultValue=0)
            )
        options = Options()
        if ApplicationQt4GUI().edit(options):
            input_file_processing = ProcessToSomaWorkflow.NO_FILE_PROCESSING
            if options.input_file_processing == 1:
                input_file_processing = ProcessToSomaWorkflow.FILE_TRANSFER
            if options.input_file_processing == 2:
                input_file_processing = ProcessToSomaWorkflow.SHARED_RESOURCE_PATH
            if options.input_file_processing == 3:
                input_file_processing = ProcessToSomaWorkflow.BV_DB_SHARED_PATH
            output_file_processing = ProcessToSomaWorkflow.NO_FILE_PROCESSING
            if options.output_file_processing == 1:
                output_file_processing = ProcessToSomaWorkflow.FILE_TRANSFER
            if options.output_file_processing == 2:
                output_file_processing = ProcessToSomaWorkflow.SHARED_RESOURCE_PATH
            builtin_db = []
            if input_file_processing == ProcessToSomaWorkflow.BV_DB_SHARED_PATH:
                for db_setting in neuroConfig.dataPath:
                    if db_setting.builtin:
                        uuid = db_setting.expert_settings.uuid
                        if uuid:
                            builtin_db.append(uuid)
                        else:
                            print("warning ! db " + repr(
                                db_setting.directory) + " has no uuid.")
            ptowf = ProcessToSomaWorkflow(
                self.process,
                options.output,
                input_file_processing=input_file_processing,
                output_file_processing=output_file_processing,
                brainvisa_db=builtin_db,
                context=self)
            ptowf.doIt()
    def _iterateButton(self):
        # if the process has a method custom_iteration(), then it is used to
        # obtain an iterated pipeline (Capsul processes...) without using the
        # interactive iteration dialog.
        if hasattr(self.process, 'custom_iteration'):
            iterated_process = self.process.custom_iteration()
            showProcess(iterated_process)
        else:
            self.readUserValues()
            self._iterationDialog = IterationDialog(self, self.process, self)
            # make it window modal to avoid user changes in the parent process
            # window
            self._iterationDialog.setWindowModality(QtCore.Qt.WindowModal)
            self._iterationDialog.accepted.connect(self._iterateAccept)
            self._iterationDialog.show()
    def _iterateAccept(self):
        try:
            params = self._iterationDialog.getLists()
            processes = self.process._iterate(**params)
            iterationProcess = brainvisa.processes.IterationProcess(
                self.process.name + " iteration",
                                                                     processes,
                                                                     self.process.name)
            showProcess(iterationProcess)
        except Exception:
            neuroException.showException()
            self._iterationDialog.show()
    def setValue(self, name, value):
        setattr(self.process, name, value)
    def readUserValues(self):
        if self.parameterizedWidget is not None:
            self.parameterizedWidget.readUserValues()
        if self._widgetStack is not None:
            for pw in self._widgetStack._children:
                ruv = getattr(pw, 'readUserValues', None)
                if ruv is None:
                    ruv = getattr(
                        getattr(pw, 'parameterizedWidget', None),  'readUserValues', None)
                if ruv is not None:
                    ruv()
    def createProcessExecutionEvent(self):
        event = super(ProcessView, self).createProcessExecutionEvent()
        mainThreadActions().call(event.setWindow, self)
        return event
    def saveAs(self):
        minf = getattr(self.process, '_savedAs', '')
        # workaround a bug in PyQt ? Param 5 doesn't work; try to use kwargs
        import sipconfig
        if sipconfig.Configuration().sip_version >= 0x040a00:
            minf = six.text_type(qt_backend.getSaveFileName(
                None, 'Save a process file', minf,
                'BrainVISA process (*.bvproc);;All files (*)', options=QFileDialog.DontUseNativeDialog))
        else:
            minf = six.text_type(
                QFileDialog.getSaveFileName(None, 'Save a process file', minf,
                                            'BrainVISA process (*.bvproc);;All files (*)', None, QFileDialog.DontUseNativeDialog))
        if minf:
            if not minf.endswith('.bvproc'):
                minf += '.bvproc'
            self.readUserValues()
            event = self.createProcessExecutionEvent()
            self.process._savedAs = minf
            event.save(minf)
    def saveProcessSetupsGUI(self):
        from brainvisa.processing.qt4gui.ProcessSetupsGUI import SaveProcessSetupsGUI
        save_process_setups_GUI = SaveProcessSetupsGUI(self)
        save_process_setups_GUI.show()
        save_process_setups_GUI.exec()
    def clone(self):
        try:
            self.readUserValues()
            clone = brainvisa.processes.getProcessInstanceFromProcessEvent(
                self.createProcessExecutionEvent())
            return showProcess(clone)
        except Exception:
            showException()
    def show_process_doc(self):
        global _mainWindow
        doc = brainvisa.processes.getHTMLFileName(self.process)
        if os.path.exists(doc):
            _mainWindow.info.setSource(doc)
    @staticmethod
    @catch_gui_exception
    def open():
        import sipconfig
        if sipconfig.Configuration().sip_version >= 0x040a00:
            minf = six.text_type(qt_backend.getOpenFileName(
                None, _t_('Open a process file'), '',
                'BrainVISA process (*.bvproc);;All files (*)',
                options=QFileDialog.DontUseNativeDialog))
        else:
            minf = six.text_type(QFileDialog.getOpenFileName(
                None, _t_('Open a process file'), '',
                'BrainVISA process (*.bvproc);;All files (*)', None,
                QFileDialog.DontUseNativeDialog))
        if minf:
            showProcess(brainvisa.processes.getProcessInstance(minf)) 
#----------------------------------------------------------------------------
[docs]def showProcess(process_id, *args, **kwargs):
    '''Opens a process window and set the corresponding arguments'''
    global _mainWindow
    view = None
    try:
        process = brainvisa.processes.getProcessInstance(process_id)
        if process is None:
            raise RuntimeError(neuroException.HTMLMessage(
                _t_('Invalid process <em>%s</em>') % (str(process_id), )))
        for i in range(len(args)):
            k, p = list(process.signature.items())[i]
            process.setValue(k, args[i])
        for k, v in kwargs.items():
            process.setValue(k, v)
        gui = getattr(process, 'overrideGUI', None)
        if gui is None:
            view = ProcessView(process)
        else:
            view = gui()
        windowGeometry = getattr(process, '_windowGeometry', None)
        if windowGeometry is not None:
            view.move(*windowGeometry['position'])
            view.resize(*windowGeometry['size'])
        view.show()
    except Exception:  # an exception can occur if the process is reloaded and an error has been introduced in its code.
        neuroException.showException()
    return view 
#----------------------------------------------------------------------------
[docs]class IterationDialog(QDialog):
    def __init__(self, parent, parameterized, context):
        QDialog.__init__(self, parent)
        layout = QVBoxLayout()
        self.setLayout(layout)
        layout.setContentsMargins(10, 10, 10, 10)
        self.setWindowTitle(_t_('%s iteration') %
                            six.text_type(parent.windowTitle()))
        params = []
        for (n, p) in parameterized.signature.items():
            if neuroConfig.userLevel >= p.userLevel:
                params += [n, neuroData.ListOf(p)]
        self.parameterized = brainvisa.processes.Parameterized(
            neuroData.Signature(*params))
        for n in self.parameterized.signature.keys():
            setattr(self.parameterized, n, None)
        self.parameterizedWidget = ParameterizedWidget(
            self.parameterized, None)
        layout.addWidget(self.parameterizedWidget)
        w = QWidget()
        hb = QHBoxLayout()
        w.setLayout(hb)
        layout.addWidget(w)
        hb.setContentsMargins(5, 5, 5, 5)
        w.setSizePolicy(
            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed))
        btn = QPushButton(_t_('Ok'))
        hb.addWidget(btn)
        btn.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
        btn.clicked.connect(self.accept)
        btn = QPushButton(_t_('Cancel'))
        hb.addWidget(btn)
        btn.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
        btn.clicked.connect(self.reject)
    def getLists(self):
        result = {}
        for n in self.parameterized.signature.keys():
            result[n] = getattr(self.parameterized, n, None)
        return result
[docs]    def accept(self):
        self.parameterizedWidget.readUserValues()
        QDialog.accept(self)  
#----------------------------------------------------------------------------
[docs]class UserDialog(QDialog):
    def __init__(self, parent, modal, message, signature, buttons):
        flags = Qt.Window | Qt.Dialog
        QDialog.__init__(self, parent, flags)
        if modal:
            self.setWindowModality(Qt.WindowModal)
        self.setAttribute(Qt.WA_DeleteOnClose, True)
        layout = QVBoxLayout()
        self.setLayout(layout)
        layout.setContentsMargins(10, 10, 10, 10)
        layout.setSpacing(5)
        self.condition = None
        self.signature = signature
        self._currentDirectory = None
        if message is not None:
            lab = QLabel(six.text_type(message))
            lab.setWordWrap(True)
            layout.addWidget(lab)
            lab.setSizePolicy(
                QSizePolicy(QSizePolicy.Preferred, QSizePolicy.Fixed))
        self.editors = {}
        if signature is not None:
            sv = WidgetScrollV()
            layout.addWidget(sv)
            first = None
            svWidget = QWidget()
            svWidgetLayout = QVBoxLayout()
            svWidget.setLayout(svWidgetLayout)
            for (k, p) in self.signature.items():
                hb = QHBoxLayout()
                svWidgetLayout.addLayout(hb)
                l = QLabel(k + ': ')
                hb.addWidget(l)
                e = p.editor(None, k, self)
                hb.addWidget(e)
                self.editors[k] = e
                if first is None:
                    first = e
            sv.setWidget(svWidget)
        self.group1 = QButtonGroup()
        group1Widget = QWidget()
        group1Layout = QHBoxLayout()
        group1Widget.setLayout(group1Layout)
        layout.addWidget(group1Widget)
        self._actions = {}
        self.group2 = QButtonGroup()
        group2Widget = QWidget()
        group2Layout = QHBoxLayout()
        group2Widget.setLayout(group2Layout)
        layout.addWidget(group2Widget)
        deleteGroup1 = 1
        i = 0
        for b in buttons:
            if type(b) in (tuple, list):
                caption, action = b
                btn = QPushButton(six.text_type(caption))
                group1Layout.addWidget(btn)
                self.group1.addButton(btn, i)
                btn.setSizePolicy(
                    QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
                self._actions[self.group1.id(btn)] = action
                deleteGroup1 = 0
            else:
                btn = QPushButton(six.text_type(b))
                group2Layout.addWidget(btn)
                self.group2.addButton(btn, i)
                btn.setSizePolicy(
                    QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
            i += 1
        if deleteGroup1:
            group1Widget.close()
        else:
            group1Widget.setSizePolicy(
                QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed))
            self.group1.buttonClicked[int].connect(self._doAction)
        group2Widget.setSizePolicy(
            QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Fixed))
        self.group2.buttonClicked[int].connect(self.select)
    def select(self, value):
        for e in self.editors.values():
            e.checkValue()
        self._result = value
        self.done(1)
    def setValue(self, name, value):
        mainThreadActions().push(self.editors[name].setValue, value)
    def getValue(self, name):
        return mainThreadActions().call(self.editors[name].getValue)
    def _doAction(self, index):
        self._actions[index](self)
    def call(self):
        if neuroConfig.gui:
            self._result = None
            mainThreadActions().call(self.show)
            mainThreadActions().call(self.exec)
            result = self._result
            del self._result
            return result
        return -1 
#----------------------------------------------------------------------------
[docs]class ProcessEdit(QDialog):
    def __init__(self, process):
        QDialog.__init__(self, None)
        layout = QVBoxLayout()
        self.setLayout(layout)
        layout.setContentsMargins(10, 10, 10, 10)
        layout.setSpacing(5)
        neuroConfig.registerObject(self)
        self.process = process
        t = _t_(process.name)
        self.setWindowTitle(t)
        spl = QSplitter(Qt.Vertical)
        layout.addWidget(spl)
        w = QWidget(spl)
        vb = QVBoxLayout()
        w.setLayout(vb)
        self.labName = QLabel('<center><h3>' + t + '</h3></center>', spl)
        vb.addWidget(self.labName)
        hb = QHBoxLayout()
        vb.addLayout(hb)
        l = QLabel(_t_('HTML Path') + ': ')
        hb.addWidget(l)
        self.leHTMLPath = QLineEdit()
        hb.addWidget(self.leHTMLPath)
        hb = QHBoxLayout()
        vb.addLayout(hb)
        l = QLabel(_t_('Language') + ': ')
        hb.addWidget(l)
        self.cmbLanguage = QComboBox()
        self.cmbLanguage.setEditable(False)
        hb.addWidget(self.cmbLanguage)
        for i in neuroConfig._languages:
            self.cmbLanguage.addItem(i)
            if i == neuroConfig.language:
                self.cmbLanguage.setCurrentIndex(self.cmbLanguage.count() - 1)
        self.cmbLanguage.activated.connect(self.changeLanguage)
        l = QLabel(_t_('Short description') + ':')
        vb.addWidget(l)
        self.mleShort = QPlainTextEdit()
        vb.addWidget(self.mleShort)
        w = QWidget(spl)
        vb = QVBoxLayout()
        w.setLayout(vb)
        # spl.setLayout(vb)
        hb = QHBoxLayout()
        vb.addLayout(hb)
        l = QLabel(_t_('Parameter') + ': ')
        hb.addWidget(l)
        self.cmbParameter = QComboBox()
        self.cmbParameter.setEditable(False)
        hb.addWidget(self.cmbParameter)
        stack = QStackedWidget()
        vb.addWidget(stack)
        self.mleParameters = {}
        for n in self.process.signature.keys():
            mle = QPlainTextEdit()
            vb.addWidget(mle)
            stack.addWidget(mle)
            self.mleParameters[self.cmbParameter.count()] = mle
            self.cmbParameter.addItem(n)
            self.cmbParameter.activated.connect(stack.setCurrentIndex)
        stack.setCurrentIndex(0)
        w = QWidget(spl)
        vb = QVBoxLayout()
        w.setLayout(vb)
        l = QLabel(_t_('Long description') + ':')
        vb.addWidget(l)
        self.mleLong = QPlainTextEdit()
        vb.addWidget(self.mleLong)
        try:
            self.readDocumentation()
            self.setLanguage(six.text_type(self.cmbLanguage.currentText()))
        except Exception:
            showException()
        w = QWidget()
        hb = QHBoxLayout()
        w.setLayout(hb)
        vb.addWidget(w)
        hb.setContentsMargins(5, 5, 5, 5)
        w.setSizePolicy(
            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Fixed))
        btn = QPushButton(_t_('apply'))
        hb.addWidget(btn)
        btn.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
        btn.clicked.connect(self.applyChanges)
        btn = QPushButton(_t_('Ok'))
        hb.addWidget(btn)
        btn.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
        btn.clicked.connect(self.accept)
        btn = QPushButton(_t_('Cancel'))
        hb.addWidget(btn)
        btn.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
        btn.clicked.connect(self.reject)
        self.resize(800, 600)
[docs]    def closeEvent(self, event):
        neuroConfig.unregisterObject(self)
        QWidget.closeEvent(self, event) 
    def readDocumentation(self):
        try:
            self.documentation = brainvisa.processes.readProcdoc(self.process)
        except Exception:
            import traceback
            traceback.print_exc()
    def writeDocumentation(self):
        brainvisa.processes.procdocToXHTML(self.documentation)
        self.setLanguage(self.language)
        brainvisa.processes.writeProcdoc(self.process, self.documentation)
    def setLanguage(self, lang):
        self.leHTMLPath.setText(
            XHTML.html(self.documentation.get('htmlPath', '')))
        self.language = lang
        d = self.documentation.get(lang, {})
        self.mleShort.setPlainText(XHTML.html(d.get('short', '')))
        self.mleLong.setPlainText(XHTML.html(d.get('long', '')))
        p = d.get('parameters', {})
        for i, j in self.mleParameters.items():
            j.setPlainText(
                XHTML.html(p.get(six.text_type(self.cmbParameter.itemText(i)), '')))
    def saveLanguage(self):
        d = {}
        d['short'] = self.escapeXMLEntities(
            six.text_type(self.mleShort.toPlainText()))
        d['long'] = self.escapeXMLEntities(
            six.text_type(self.mleLong.toPlainText()))
        d['parameters'] = p = {}
        for i, j in self.mleParameters.items():
            param_name = six.text_type(self.cmbParameter.itemText(i))
            if j.toPlainText():
                p[param_name] = self.escapeXMLEntities(
                    six.text_type(j.toPlainText()))
            else:
                if param_name in p:
                    del p[param_name]
            self.documentation[self.language] = d
        htmlPath = six.text_type(self.leHTMLPath.text())
        if htmlPath:
            self.documentation['htmlPath'] = htmlPath
        else:
            try:
                del self.documentation['htmlPath']
            except KeyError:
                pass
    @staticmethod
    def escapeXMLEntities(s):
        return re.sub(r'&(?![a-z]+;)', '&', s)
    def changeLanguage(self):
        self.saveLanguage()
        self.setLanguage(six.text_type(self.cmbLanguage.currentText()))
    def applyChanges(self):
        try:
            self.saveLanguage()
            self.writeDocumentation()
            brainvisa.processes.generateHTMLProcessesDocumentation(
                self.process)
            mainWindow().info.reload()
        except Exception:
            neuroException.showException()
[docs]    def accept(self):
        self.applyChanges()
        QDialog.accept(self)  
#----------------------------------------------------------------------------
#----------------------------------------------------------------------------
#----------------------------------------------------------------------------
[docs]class MainWindow(QWidget):
    def __init__(self):
        QWidget.__init__(self)
        self.myLayout = QVBoxLayout(self)
        self.setLayout(self.myLayout)
        self.mainModules = []
        self.subLayouts = []
        neuroConfig.registerObject(self)
[docs]    def closeEvent(self, event):
        neuroConfig.unregisterObject(self)
        QWidget.closeEvent(self, event) 
    def addMainModule(self, identifier, name, image, description):
        # Create main module widget
        w = QWidget(self)
        layout = QVBoxLayout(w)
        w.setLayout(layout)
        l = QLabel(_t_(name), self)
        layout.addWidget(l)
        hb = QHBoxLayout()
        layout.addLayout(hb)
        l = QLabel()
        hb.addWidget(l)
        l.setIcon(
            QIcon(
                os.path.join(
                    neuroConfig.mainPath, 'doc', 'images', image))),
        l.setSizePolicy(QSizePolicy(QSizePolicy.Fixed, QSizePolicy.Fixed))
        l.resize(320, 200)
        l = QTextEdit()
        hb.addWidget(l)
        l.setText(description)
        l.setReadOnly(True)
        if (len(self.mainModules) % 2 == 0):
            self.subLayouts.append(QHBoxLayout(self.layout()))
        self.subLayouts[-1].addWidget(w)
        w.show()
        self.mainModules.append(w) 
#----------------------------------------------------------------------------
[docs]class RemoteContextGUI(QTreeWidgetItem):
    """
    Specific GUI to display messages returned by processes executed remotely.
    """
    def __init__(self, parent, name='Remote Hosts:'):
        """
        The specific GUI is a QListView. It is composed of an arborescence of QListViewItems that sorts
        the messages according to the process and the host they belong to::
        Remote Hosts:
        |
        --host
          |
          --process
            |
            --message
            --message
          --process
            |
            --message
        :param parent: the QListView.
        :param name: name
        """
        remoteView = QTreeWidget(parent)
        remoteView.setSizePolicy(QSizePolicy(QSizePolicy.Expanding,
                                             QSizePolicy.Preferred))
        remoteView.setWindowTitle('Remote messages')
        remoteView.setColumnCount(4)
        remoteView.setHeaderLabels(['IP', 'ID', 'Status', 'Messages'])
        QTreeWidgetItem.__init__(self, remoteView)
        self.setText(0, name)
        self.setExpanded(True)
        self.processList = {}
        self.ipList = {}
    def addIP(self, ip):
        i_item = QTreeWidgetItem(self, [ip])
        self.ipList[str(ip)] = i_item
        i_item.setExpanded(True)
    def addProcess(self, ip, pid, status=' Starting...', message=''):
        p_item = QTreeWidgetItem(self.ipList[str(ip)],
                                 ['Process', '%03d' % pid, status, message])
        # p_item.setText( 0, 'Process' )
        # p_item.setText( 1, '%03d'%pid )
        # p_item.setText( 2, status )
        # p_item.setText( 3, message )
        self.processList[str(pid)] = p_item
        # self.ipList[str(ip)].insertItem(p_item)
    def addMessage(self, pid, message):
        m_item = QTreeWidgetItem(self.processList[str(pid)],
                                 ['Message', '', '', message])
        # m_item.setText( 0, 'Message' )
        # m_item.setText( 1, '' )
        # m_item.setText( 2, '' )
        # m_item.setText( 3, message )
        # self.processList[str(pid)].insertItem(m_item)
    def setProcessStatus(self, pid, status):
        self.processList[str(pid)].setText(2, status)
    def setCurrentMessage(self, pid, message):
        self.processList[str(pid)].setText(3, message)
    def clear(self):
        for item in self.ipList.values():
            self.takeChild(self.indexOfChild(item))
            del(item)
        self.processList = {}
        self.ipList = {}
    def sort(self):
        pass
    def sortChildItems(self, col, asc):
        pass 
#----------------------------------------------------------------------------
def mainWindow():
    global _mainWindow
    return _mainWindow
def showMainWindow():
    global _mainWindow
    if neuroConfig.openMainWindow:
        #_mainWindow = ProcessSelection()
        # window with customizable lists of processes
        _mainWindow = ProcessSelectionWidget()
        _mainWindow.show()
        for w in QApplication.instance().topLevelWidgets():
            if w is not _mainWindow:
                w.raise_()
    else:
        _mainWindow = None
def close_viewers_action(parent):
    action = QAction(parent)
    action.setText(_t_("Close all viewers"))
    action.triggered.connect(close_viewers_slot)
    return action
def close_viewers_slot():
    close_viewers(warn=True)
def close_viewers(warn=False):
    if not warn:
      a = QMessageBox.Yes
    else:
        a = QMessageBox.warning(
            None, _t_('Quit'),
            _t_('Close all viewers and unload all viewed objects ?\n'
                '(unsaved modified objects will be lost)'),
            QMessageBox.Yes | QMessageBox.Default, QMessageBox.No)
    if a == QMessageBox.Yes:
        from brainvisa.data.qt4gui.readdiskitemGUI import DiskItemEditor
        from brainvisa.data.qt4gui.hierarchyBrowser import HierarchyBrowser
        for w in QApplication.instance().allWidgets():
            if isinstance(w, DiskItemEditor):
                w.close_viewer()
            elif isinstance(w, ProcessView):
                process_info \
                    = brainvisa.processes.getProcessInfo(w.process.id())
                if process_info is not None and "viewer" in process_info.roles:
                    w.close()
            elif isinstance(w, HierarchyBrowser):
                w.close_viewers()
#----------------------------------------------------------------------------
def updateProcessList():
    _mainWindow.updateList()
def loadProcessSetupsGUI():
    from brainvisa.processing.qt4gui.ProcessSetupsGUI import LoadProcessSetupsGUI
    load_process_setups_GUI = LoadProcessSetupsGUI()
    load_process_setups_GUI.show()
    load_process_setups_GUI.exec()
def save_and_close_all_processes():
    close_viewers()
    saved = []
    for w in QApplication.instance().allWidgets():
        if isinstance(w, ProcessView):
            # dont' remember updateDatabases process, it will be shown
            # again if needed after reload.
            if w.process.id() != 'updateDatabases':
                w.readUserValues()
                clone = w.createProcessExecutionEvent()
                saved.append(clone)
            w.close()
    return saved
def restore_all_processes(saved):
    uis = []
    for event in saved:
        proc = brainvisa.processes.getProcessInstanceFromProcessEvent(event)
        procui = showProcess(proc)
        uis.append(procui)
    return uis
#----------------------------------------------------------------------------
def initializeProcessesGUI():
    global _computing_resource_pool, _workflow_application_model
    import brainvisa.processes
    brainvisa.processes.setMainThreadActionsMethod(QtThreadCall())
    _computing_resource_pool = None
    _workflow_application_model = None
    if neuroConfig.gui:
        if _soma_workflow:
            _computing_resource_pool = ComputingResourcePool()
            _computing_resource_pool.add_default_connection()
            _workflow_application_model = WorkflowApplicationModel(
                _computing_resource_pool)
        six.exec_(
            'from brainvisa.processing.qt4gui.neuroProcessesGUI import *',
                  brainvisa.processes.__dict__)
        brainvisa.processes._defaultContext = ExecutionContextGUI()
def html_with_local_images(html):
    images_basepath = neuroConfig.getDocPath('images', project='axon') \
        + '-' + neuroConfig.shortVersion
    html_repl = html.replace(' src="../../../../',
                             ' src="%s/' % images_basepath)
    html_repl = html_repl.replace(' SRC="../../../../',
                                  ' src="%s/' % images_basepath)
    html_repl = html_repl.replace(' src="../../',
                                  ' src="%s/' % images_basepath)
    html_repl = html_repl.replace(' SRC="../../',
                                  ' src="%s/' % images_basepath)
    return html_repl