Source code for brainvisa.anatomist

# -*- 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 sys
import os

import six

from brainvisa.configuration import neuroConfig
from brainvisa.processing.neuroException import showException
use_headless = False
try:
    import anatomist
    anatomist.setDefaultImplementation(neuroConfig.anatomistImplementation)
    if not neuroConfig.gui:
        # use headless version
        from anatomist import headless
        use_headless = True
        headless.setup_headless()
    import anatomist.api as anatomistModule
    anatomistImport = True
except Exception as e:
    import traceback
    showException(beforeError='The Anatomist module failed to load:')
    anatomistImport = False
    noAnatomistReason = '\n'.join(traceback.format_exception_only(type(e), e))

if anatomistImport:
    from brainvisa import registration
    from brainvisa.processing import neuroLog
    from brainvisa.processing import neuroException
    from brainvisa.data import neuroData
    from soma.qtgui.api import QtThreadCall
    import distutils.spawn
    import weakref
    import types
    import threading
    import copy
    from brainvisa.processing.qtgui import backwardCompatibleQt as qt
    from brainvisa.processing.qtgui import neuroProcessesGUI


"""
This module enables to generate an implementation of anatomist api specialized for brainvisa.
It can use either socket or direct(sip bindings) implementation.

To use this implementation, load the module using :

anatomistModule = anatomistapi.bvimpl.loadAnatomistModule(anatomistapi.api.SOCKET) # or DIRECT)
a=anatomistModule.Anatomist()
...
It can be used only in brainvisa application because it uses modules that are loaded in brainvisa.

Returned module contains an Anatomist class which inherits from chosen Anatomist class implementation, and is a thread safe singleton.
Specificities added for brainvisa are :
  - loading referentials and transformation on loading an object using
    brainvisa database informations.
  - writing messages in brainvisa log file.
  - if brainvisa is used in non-GUI mode (neuroConfig.gui is False), anatomist
    is run in headless mode (see anatomist.headless module)
"""


def validation():
    from brainvisa.validation import ValidationError
    if not anatomistImport:
        raise ValidationError('Cannot find anatomist module: {0}'
                              .format(noAnatomistReason))
    elif distutils.spawn.find_executable('anatomist') is None:
        raise ValidationError('Cannot find Anatomist executable')


if anatomistImport:
    # dynamic class Anatomist inherits from one implementation of anatomist api
    class Anatomist(anatomistModule.Anatomist):
        # We shouldn't change the defaultRefType in fact.
        # Indeed, if Anatomist from brainvisa takes only weak shared references on objects and windows,
        # the user can close a window that is still used by python and it can creates pbs
        # Fo example, an id that is free for Anatomist C++ is reused for a new object in Anatomist python but python still have references
        # on this id for another object, so when this other object is released in python layer, it can lead to the destruction of the new object.
        # But with this change, the user won't be able to really close windows or delete objects in Anatomist of Brainvisa manually.
        # defaultRefType="WeakShared"

        def __new__(cls, *args, **kwargs):
            import threading
            if neuroConfig.anatomistImplementation != 'socket':
                from . import reusablewinhook
                globals()['reusablewinhook'] = reusablewinhook
            instance = super(Anatomist, cls).__new__(cls, *args, **kwargs)
            if instance and '-b' not in args \
                    and neuroConfig.anatomistImplementation != 'socket':
                if not instance.getControlWindow():
                    mainThread = QtThreadCall()
                    instance.createControlWindow()
                    win = instance.getControlWindow()
                    if win:
                        win.enableClose(False)
                        mainThread.push(
                            win.destroyed.connect, instance.anatomist_closed)
            return instance

        def __singleton_init__(self, *args, **kwargs):
            import threading
            self.communicationLogFile = None
            self.outputLog = None
            if neuroConfig.mainLog is not None:
                communicationLog = neuroConfig.mainLog.subTextLog()
                self.communicationLogFile = open(communicationLog.fileName,
                                                 'w')
            super(Anatomist, self).__singleton_init__(*args, **kwargs)
            anatomistParameters = []
            for a in args:
                anatomistParameters.append(a)
            try:
                if neuroConfig.anatomistImplementation == 'socket':
                    # FIXME: why use eval here ?
                    anatomistModule.Anatomist.anatomistExecutable = \
                        eval("neuroConfig.anatomistExecutable")
                # log file for writing traces from this class
                # log for writing traces from anatomist process
                if neuroConfig.mainLog is not None:
                    self.outputLog = neuroConfig.mainLog.subTextLog()
                    # add a trace in brainvisa main log
                    neuroConfig.mainLog.append(
                        'Anatomist ', html=self.outputLog,
                        children=[neuroLog.LogFile.Item(
                            'Communications', html=communicationLog)],
                        icon='anaIcon_small.png')
                    if neuroConfig.anatomistImplementation == 'socket':
                        anatomistParameters += [
                            '--cout', self.outputLog.fileName, '--cerr',
                            self.outputLog.fileName]
            except Exception:
                neuroException.showException()
                self.communicationLogFile = None
                self.outputLog = None
            if neuroConfig.userProfile is not None:
                anatomistParameters += ['-u', neuroConfig.userProfile]
            mainThread = QtThreadCall()
            args = anatomistParameters
            super(Anatomist, self).__singleton_init__(*args, **kwargs)
            self._reusableWindows = []
            self._reusableWindowBlocks = []

            if neuroConfig.anatomistImplementation != 'socket':
                a = anatomistModule.Anatomist(*args, **kwargs)
                if a.getControlWindow() is not None:
                    a.getControlWindow().enableClose(False)
                    mainThread.push(a.getControlWindow().destroyed.connect,
                                    self.anatomist_closed)
                mainThread.push(reusablewinhook.installWindowHandler)

        def anatomist_closed(self, obj):
            self._reusableWindows = []
            self._reusableWindowBlocks = []
            if neuroProcessesGUI:
                neuroProcessesGUI.close_viewers()

        #
        # Methods redefined to use Brainvisa log system.
        def log(self, message):
            """
            This method is redefined to print logs in brainvisa log system.
            """
            self.lock.acquire()
            if self.communicationLogFile is None:
                return
            self.communicationLogFile.write(message + "<BR>")
            self.communicationLogFile.flush()
            self.lock.release()

        def logCommand(self, command, **kwargs):
            if self.communicationLogFile is not None:
                if isinstance(command, str):
                    logcmd = "<B>-->" + command + "</B><UL>"
                    for (name, value) in kwargs.items():
                        if value is not None:
                            logcmd += "<LI>" + name + " = " + str(value)
                    logcmd += "</UL>"
                    self.log(logcmd)
                else:  # if the command is not a string it should an object Command from anatomist bindings
                    cmdDesc = command.write()
                    logcmd = "<B>-->" + command.name() + "</B><UL>"
                    for (name, value) in cmdDesc.items():
                        if value is not None:
                            logcmd += "<LI>" + name + " = " + str(value)
                    logcmd += "</UL>"
                    self.log(logcmd)

        def logEvent(self, event, params):
            """
            Log an event received from anatomist application.

            Parameters
            ----------
            event: string
                event name
            params: string
                event parameters
            """
            self.log("<B> &lt;-- " + event + "</B> : " + params)

        #
        # Methods redefined to use Brainvisa transformation manager (with database informations)
        # When a database object is loaded, associated referential and
        # transformations are loaded

        def loadObject(
            self, fileref, objectName=None, restrict_object_types=None,
                       forceReload=False, loadReferential=True, duplicate=False,
                       hidden=False, loadTransformations=True):
            """
            Loads an object from a file (volume, mesh, graph, texture...)

            Parameters
            ----------
            fileref: string or Diskitem
                name of the file that contains the object to load or a Diskitem
                representing this file.
            objectName: string
                object name
            restrict_object_types: dictionary
                object -> accpepted types list. Ex:
                ``{'Volume' : ['S16', 'FLOAT']}``
            forceReload: boolean
                if True the object will be loaded even if it is already loaded
                in Anatomist. Otherwise, the already loaded one is returned.
            loadReferential: boolean
                if True, use brainvisa database informations to search
                referential and transformations associated to this object and
                load them.

            Returns
            -------
            AObject: the loaded object
            """
            if hasattr(fileref, "fullPath"):  # is it a diskitem ?
                file = fileref.fullPath()
                # if not forceReload:
                    # object=self.getObject(file)
                    # if object is not None: # object is already loaded
                        # files = fileref.fullPaths()
                        # for x in files:
                            # if os.path.exists( x ):
                        # if os.stat(x).st_mtime >= object.loadDate: # reload it if the file has been modified since last load
                            # self.reloadObjects([object])
                            # break
                        # return object
                newObject = anatomistModule.Anatomist.loadObject(
                    self, file, objectName, restrict_object_types, forceReload, duplicate, hidden)
                # search referential for this object
                if loadReferential:
                    try:
                        tm = registration.getTransformationManager()
                        ref = tm.referential(fileref)
                        if ref is not None:
                            # the referential is loaded only if necessary : if
                            # the object has not the right referential assigned
                            ruuid = ref.uuid()
                            oruuid = newObject.referential.refUuid
                            if (ruuid != oruuid):
                        # create referential
                                aref = self.createReferential(ref)

                                # search transformations for this referential
                                # self.loadTransformations(aref)

                                # assign referential to object
                                newObject.assignReferential(aref)

                            # search transformations for this referential
                            if loadTransformations:
                                self.loadTransformations(newObject.referential)

                    except Exception:
                        neuroException.showException(afterError='Cannot load referential and transformations information with '
                                                     'object "' + file + '"')
            else:
                newObject = anatomistModule.Anatomist.loadObject(
                    self, fileref, objectName, restrict_object_types,
                    forceReload, duplicate, hidden)
            return newObject

        def createWindow(self, wintype, geometry=None, block=None,
                         no_decoration=None, options=None, allowreuse=True):
            self.findReusableWindowBlock(block)
            if allowreuse:
                win = self.findReusableWindow(wintype, block=block)
                if win and geometry is not None:
                    a.execute('WindowConfig', window=win, geometry=geometry)
            else:
                win = None
            if win is None:
                win = anatomistModule.Anatomist.createWindow(
                    self, wintype, geometry=geometry, block=block, no_decoration=no_decoration, options=options)
            return win

        def createWindowsBlock(self, nbCols=2, nbRows=0, allowreuse=True):
            if nbRows:
                nbCols = 0
            if allowreuse:
                bw = self.findReusableWindowBlock()
                return self.AWindowsBlock(self, nbCols=nbCols, nbRows=nbRows,
                                          widgetproxy=bw)
            return anatomistModule.Anatomist.createWindowsBlock(
                self, nbCols=nbCols, nbRows=nbRows)

        def createReferential(self, fileref=None):
            """
            Creates a new referential using the informations in the file.

            Parameters
            ----------
            fileref: string or Diskitem
                name or diskitem of a file (minf file, extension .referential)
                containing  informations about the referential : its name and
                uuid

            Returns
            -------
            Referential: the newly created referential
            """
            if hasattr(fileref, "fullPath"):  # is it a diskitem ?
                ruuid = fileref.uuid()
                # if it is a known referential, nothing to load
                if ruuid == registration.talairachACPCReferentialId:
                    newRef = self.centralRef
                    # print("getCentralReferential", newRef)
                elif ruuid == registration.talairachMNIReferentialId:
                    newRef = self.mniTemplateRef
                    # print("getMniRef", newRef)
                # unknown referential
                else:
                    # print("create referential", fileref.fullPath())
                    newRef = anatomistModule.Anatomist.createReferential(
                        self, fileref.fullPath())
                    # print("created referential", newRef)
                    newRef.diskitem = fileref
            else:
                newRef = anatomistModule.Anatomist.createReferential(
                    self, fileref)
            return newRef

        def loadTransformations(self, referential):
            """
            Search and load transformations between this referential and spm
            referential.

            Parameters
            ----------
            referential: Referential
                the referential for which searching transformations
            """

            def _transformWith(self, ref1, ref2, forceLoadTransformation=True):
                tm = registration.getTransformationManager()
                if isinstance(ref1, self.Referential):
                    id1 = None
                    if (getattr(ref1, "diskitem", None) == None):
                        id1 = ref1.refUuid
                    else:
                        id1 = ref1.diskitem.uuid()
                    srcr = ref1
                else:
                    id1 = ref1

                if isinstance(ref2, self.Referential):
                    id2 = None
                    if (getattr(ref2, "diskitem", None) == None):
                        id2 = ref2.refUuid
                    else:
                        id2 = ref2.diskitem.uuid()
                else:
                    id2 = ref2

                pth = tm.findPaths(id1, id2, extensive=False, maxLength=4)

                try:
                    p = next(pth)

                    loadTrAndCreateRef = forceLoadTransformation
                    try:
                        p = next(pth)
                        self.log(
                            ' '.join('processTransformations warning: multiple transformations from', ref1, 'to', ref2))
                        loadTrAndCreateRef = forceLoadTransformation
                    except Exception:
                        loadTrAndCreateRef = True

                    if loadTrAndCreateRef:
                        if not isinstance(ref1, self.Referential):
                            srcrDiskItem = tm.referential(id1)
                            srcr = self.createReferential(srcrDiskItem)
                            ref1 = srcr
                        lastref = id1
                        for t in p:
                            dstrid = t['destination_referential']
                            srcrid = t['source_referential']

                            if lastref == dstrid:  # The transformation should be applied in reverse
                                wantedref = srcrid
                            else:
                                wantedref = dstrid

                            if wantedref != id2 \
                                    or not isinstance(ref2, self.Referential):
                                dstrDiskItem = tm.referential(wantedref)
                                dstr = self.createReferential(dstrDiskItem)
                                # print('new dst ref:', dstr.uuid())
                            else:
                                dstr = ref2

                            # print("load transformation", t, ":", srcr.uuid(),
                            # "->", dstr.uuid())
                            if wantedref == dstrid:
                                self.loadTransformation(
                                    t.fullPath(), srcr, dstr)
                            else:
                                self.loadTransformation(
                                    t.fullPath(), dstr, srcr)
                            srcr = dstr
                            lastref = wantedref

                    return loadTrAndCreateRef  # 0 -> no UNIQUE path, transformations not loaded

                except StopIteration:
                    return 0  # no path

            # end _transformWith-----------------------------------------------

            # try to find transformation between this referential and spm
            # referential
            triedrefs = [self.mniTemplateRef, self.centralRef,
                         registration.globallyRegistredSPAMReferentialId]

            for r in triedrefs:
                x = _transformWith(self, referential, r)
                if x == 1:
                    return x

            # print("no path from/to [mni, central or spam], search for others (if no ambiguity : unique path only).")
            # try to find transformation between this referential and others
            allRefs = self.getReferentials()

            triedUuid = []
            for tr in triedrefs:
                if isinstance(tr, self.Referential):
                    triedUuid.append(tr.refUuid)
                else:
                    triedUuid.append(tr)

            toremove = []
            for r in allRefs:
                if(r.refUuid in triedUuid):
                    toremove.append(r)

            for r in toremove:
                allRefs.remove(r)

            if referential in allRefs:
                allRefs.remove(referential)

            otherRefs = allRefs

            result = 0
            for r in otherRefs:
                x = _transformWith(self, referential, r, False)
                if x == 1:  # seulement si il n y a pas ambiguite
                    result = 1

            return result

        def __getattr__(self, name):
            """
            Called when trying to access to name attribute, which is not defined.
            Used to give a value to centralRef attribute first time it is accessed.
            """
            if name == "centralRef":
                tm = registration.getTransformationManager()
                cr = tm.referential(registration.talairachACPCReferentialId)
                anatomistModule.Anatomist.__getattr__(self, name)
                self.lock.acquire()
                self.centralRef.diskitem = cr
                self.lock.release()
                # print("central ref loaded", self.centralRef,
                # self.centralRef.diskitem)
                return self.centralRef
            elif name == "mniTemplateRef":
                tm = registration.getTransformationManager()
                mnir = tm.referential(registration.talairachMNIReferentialId)
                anatomistModule.Anatomist.__getattr__(self, name)
                self.lock.acquire()
                self.mniTemplateRef.diskitem = mnir
                self.lock.release()
                # print("mni ref loaded", self.mniTemplateRef,
                # self.mniTemplateRef.diskitem)
                return self.mniTemplateRef
            else:
                anatomistModule.Anatomist.__getattr__(self, name)

        def findReusableWindow(self, wintype='3D', block=None):
            self._reusableWindows = [w for w in self._reusableWindows if w]
            todel = set()
            try:
                for w in self._reusableWindows:
                    try:
                        if w.windowType == wintype and len( w.objects ) == 0 and \
                            (block is None or (w.block is not None
                                               and w.block.internalWidget == block.internalWidget)):
                            return w
                    except Exception:  # window probably closed in the meantime
                        todel.add(w)
            finally:
                if len(todel) != 0:
                    self._reusableWindows = [w for w in self._reusableWindows
                                             if w not in todel]
            return None

        def findReusableWindowBlock(self, block=None):
            if neuroConfig.anatomistImplementation == 'socket':
                return None
            if block is not None and hasattr( block, 'internalWidget' ) \
                    and block.internalWidget:
                return block.internalWidget
            self._reusableWindowBlocks = [
                w for w in self._reusableWindowBlocks if w]
            todel = set()
            try:
                for w in self._reusableWindowBlocks:
                    if block is None:
                        return w
                    us = anatomistModule.cpp.CommandContext.defaultContext(
                    ).unserial
                    bid = us.id(w.widget)
                    if bid >= 0:
                        block.internalRep = bid
                    else:
                        if block.internalRep < 0:
                            bid = us.makeID(w.widget)
                            block.internalRep = bid
                            us.registerPointer(w.widget, bid)
                    block.setWidget(w.widget)
                    return block.internalWidget
            finally:
                if len(todel) != 0:
                    self._reusableWindowBlocks = \
                        [w for w in self._reusableWindowBlocks
                         if w not in todel]
            return None

        def setReusableWindow(self, win, state=True):
            self._reusableWindows = [w for w in self._reusableWindows if w]
            if type(win) not in (list, tuple):
                win = [win]
            if state:
                for w in win:
                    self._reusableWindows.append(w.getRef('WeakShared'))
            else:
                s = set([w.getInternalRep() for w in win])
                s2 = set()
                for w in self._reusableWindows:
                    if w.getInternalRep() in s:
                        s2.add(w)
                for w in s2:
                    self._reusableWindows.remove(w)
            if neuroConfig.anatomistImplementation != 'socket':
                mainThread = QtThreadCall()
                for w in win:
                    ac = w.findChild(reusablewinhook.ReusableWindowAction)
                    if ac and ac.isChecked() != state:
                        mainThread.push(ac.setChecked, state)

        def setReusableWindowBlock(self, win, state=True):
            if type(win) not in (list, tuple):
                win = [win]
            win2 = []
            for w in win:
                if isinstance(w, self.AWindowsBlock):
                    w = w.internalWidget
                win2.append(w)
            win = win2
            del win2
            if win is None:
                return
            from soma.qt_gui.qt_backend import QtCore
            if state:
                for w in win:
                    wp = self.AWindowsBlock.findBlock(w)
                    if wp is None:
                        wp = self.AWindowsBlock(self)
                        wp.setWidget(w)
                        wp = wp.widgetProxy()
                    self._reusableWindowBlocks.append(wp)
                    wp.widget.destroyed.connect(
                        self.removeReusableWindowBlock)
            else:
                s2 = set()
                for w in self._reusableWindowBlocks:
                    for ws in win:
                        if w == ws:
                            s2.add(w)
                for w in s2:
                    wp = self.AWindowsBlock.findBlock(w)
                    if wp is not None:
                        self._reusableWindowBlocks.remove(wp)
                        wp.widget.destroyed.disconnect(
                            self.removeReusableWindowBlock)
            mainThread = QtThreadCall()
            for w in win:
                ac = w.findChild(reusablewinhook.ReusableWindowBlockAction)
                if ac and ac.isChecked() != state:
                    mainThread.push(ac.setChecked, state)

        def removeReusableWindowBlock(self, win):
            self.setReusableWindowBlock(win, False)

        # util methods for brainvisa processes
        def viewObject(self, fileRef, wintype="Axial", palette=None):  # AnatomistImageView
            """
            Loads the image in anatomist, opens it in a new window and assigns
            the image's referential to the window.

            Parameters
            ----------
            fileRef: string or DiskItem
                filename of the image to load.

            Returns
            -------
            dictionary ``{"object" : the loaded object, "window" : the new window, "file" : the readed file}``
            """
            if palette is not None:
                # if the palette has to be changed, load object with duplicate
                # option : returned object will be a copy of original object,
                # which palette will not be modified
                object = self.loadObject(fileRef, duplicate=True)
                object.setPalette(palette)
            else:
                object = self.loadObject(fileRef)
            window = self.createWindow(wintype)
            window.assignReferential(object.referential)
            window.addObjects([object])
            return {"object": object, "window": window, "file": fileRef}

        def viewMesh(self, fileRef):
            """
            Loads a mesh and opens it in a 3D window. The window is assigned
            the object's referential.
            """
            return self.viewObject(fileRef, "3D")

        def viewBias(self, fileRef, forceReload=False, wintype="Coronal",
                     hanfile=None, parent=None):
            """
            Loads an image, opens it in a new window in the object's
            referential. A
            palette is assigned to the object (Rainbow2) in order to see the
            bias.

            Parameters
            ----------
            fileRef: string or ReadDiskItem
                file name for the bias corrected image
            forceReload: boolean
                if True, the image is reloaded if already in memory.
            wintype: string
                "Axial", "Coronal", "Sagittal", "3D" (or other)
            hanfile: string or ReadDiskItem
                optional file name for histogram analysis file. If present it
                will be used to set palette bounds.
            parent: QWidget or anatomist block
                parent widget or block
            """
            object = self.loadObject(fileRef, duplicate=True)
            object.setPalette(self.getPalette("Rainbow2"))
            if isinstance(parent, self.AWindowsBlock):
                window = self.createWindow(wintype, block=parent)
            else:
                window = self.createWindow(wintype)
            if isinstance(parent, qt.QWidget):
                mainThread = QtThreadCall()
                mainThread.push(window.getInternalRep().reparent, parent)
            window.assignReferential(object.referential)

            if hanfile is not None and (hasattr(hanfile, 'fullPath')
                                        or os.path.exists(hanfile)):

                def load_histo_analysis(hanfile):
                    '''parse histo analysis file (.han) to extract gray and
                    white mean/std.
                    Returns a tuple in the following shape:
                    ( ( gray_mean, gray_stdev ), ( white_mean, white_stdev ) )
                    '''
                    import re
                    r = re.compile('^.*mean:\s*(-?[0-9]+(\.[0-9]*)?)\s*sigma:\s'
                                   '(-?[0-9]+(\.[0-9]*)?)\s*$')
                    gmean, gsigma, wmean, wsigma = None, None, None, None
                    for l in open(hanfile):
                        l = l.strip()
                        if l.startswith('gray:'):
                            m = r.match(l)
                            if m:
                                gmean = float(m.group(1))
                                gsigma = float(m.group(3))
                        elif l.startswith('white:'):
                            m = r.match(l)
                            if m:
                                wmean = float(m.group(1))
                                wsigma = float(m.group(3))
                    return [gmean, gsigma], [wmean, wsigma]

                if hasattr(hanfile, 'fullPath'):
                    hanfile = hanfile.fullPath()
                try:
                    grey, white = load_histo_analysis(hanfile)
                    object.setPalette(minVal=max(grey[0] - grey[1] * 8, 0),
                                      maxVal=white[0] + white[1] * 3,
                                      absoluteMode=True)
                except Exception:
                    print('Warning: histogram could not be read:', hanfile)

            window.addObjects([object])
            return {"object": object, "window": window, "file": fileRef}

        def viewMaskOnMRI(self, mriFile, maskFile, palette, mode, rate,
                          wintype="Axial"):
            """
            Fusions an irm image with a mask. A palette is associated to the
            mask to highlight it in the fusion.
            Fusion method is Fusion2DMethod. Resulting image is opened in a new
            window.

            Parameters
            ----------
            mriFile: Diskitem or string
                file containing the mri image
            maskFile: Diskitem or string
                file containing the mask to apply
            palette: APalette
                palette to use for the mask
            mode: string
                mix color mode (geometric, linear, replace, decal, blend, add
                or combine)
            rate: float
                mix rate in linear mode
            wintype: string
                type of window to open
            """
            mri = self.loadObject(mriFile)
            duplicate = False
            if palette is not None:
                duplicate = True
            mask = self.loadObject(maskFile, duplicate=True)  # forceReload=1 )
            mask.setPalette(palette)
            fusion = self.fusionObjects([mri, mask], method='Fusion2DMethod')
            fmode = mode
            if mode not in ('linear', 'geometric', 'linear_on_defined'):
                self.execute('TexturingParams', objects=[fusion], mode=mode,
                             texture_index=1, rate=rate)
                fmode = None
            self.execute(
                "Fusion2DParams", object=fusion, mode=fmode, rate=rate,
                                reorder_objects=[mri, mask])
            window = self.createWindow(wintype)
            window.assignReferential(mri.referential)
            window.addObjects([fusion])

            return {'mri': mri, 'mask': mask, 'fusion': fusion,
                    'window': window, 'mriFile': mriFile, 'maskFile': maskFile}

        def viewActivationsOnMRI(
            self, mriFile, fmriFile, transformation=None, both=0,
                  palette=None, mode="geometric",
                  invertTransformation=0,
                  block=None, wintype="Axial"):
            if palette is None:
                palette = self.getPalette("actif_ret")
            mri = self.loadObject(mriFile)
            fmri = self.loadObject(fmriFile, duplicate=True)
            fmri.setPalette(palette, maxVal=1, minVal=0)

            window = self.createWindow(wintype, block=block)
            refMRI = self.createReferential()
            self.assignReferential(refMRI, [mri, window])

            if both:
                window2 = self.createWindow(wintype)
                refFMRI = self.createReferential()
                self.assignReferential(refFMRI, [fmri, window2])
                window2.addObjects([fmri])
            else:
                refFMRI = self.createReferential()
                fmri.assignReferential(refFMRI)
            if invertTransformation:
                ref1, ref2 = refMRI, refFMRI
            else:
                ref1, ref2 = refFMRI, refMRI
            if transformation is not None:
                if type(transformation) in (list, tuple):
                    transformation = self.createTransformation(
                        transformation, ref1, ref2)
                else:
                    transformation = self.loadTransformation(
                        transformation.fullPath(), ref1, ref2)
            fusion = self.fusionObjects(
                [mri, fmri], method='Fusion2DMethod')
            self.execute(
                "Fusion2DParams", object=fusion, mode=mode, rate=0.5,
                                  reorder_objects=[mri, fmri])
            window.addObjects([fusion])

            # Keep a reference on objects  In case of temporary file, it must not be
            # deleted while in Anatomist
            return {"mri": mri, "fmri": fmri, "fusion": fusion,
                    "window": window, "refMRI": refMRI, "refFMRI": refFMRI,
                    "transformation": transformation, "mriFile": mriFile,
                    "fmriFile": fmriFile}

        def viewTextureOnMesh(self, meshFile, textureFile, palette=None,
                              interpolation=None,
                              prefer_internal_palette=True):
            """Load a mesh file and apply the texture with the palette"""
            mesh = self.loadObject(meshFile)
            if not mesh.getInternalRep():
                raise RuntimeError('Anatomist could not read file %s'
                                   % meshFile.fullPath())
            duplicate = False
            if palette is not None and not prefer_internal_palette:
                duplicate = True
            tex = self.loadObject(textureFile, duplicate=duplicate)
            if not tex.getInternalRep():
                raise RuntimeError('Anatomist could not read file %s'
                                   % textureFile.fullPath())
            if palette and prefer_internal_palette:
                info = tex.getInfos()
                if 'palette' not in info \
                        or info['palette']['palette'] == 'Blue-Red':
                    duplicate = True
                    try:
                        self.unregisterObject(tex)
                    except AttributeError:
                        pass  # socket implementation does not have this feature
                    tex = self.duplicateObject(tex)
            if palette and duplicate:
                tex.setPalette(palette)
            # Fusion indexMESH with indexTEX
            fusion = self.fusionObjects(
                [mesh, tex], method='FusionTexSurfMethod')
            if interpolation:
                self.execute("TexturingParams", objects=[
                             tex], interpolation=interpolation)

            window = self.createWindow("3D")
            window.assignReferential(mesh.referential)
            window.addObjects(fusion)
            # Keep a reference on mesh. In case of temporary file, it must not be
            # deleted while in Anatomist
            return {"mesh": mesh, "texture": tex, "fusion": fusion,
                    "window": window, "meshFile": meshFile,
                    "textureFile": textureFile}

        def close(self):
            # print('CLOSE !!!')
            if neuroConfig.anatomistImplementation != 'threaded':
                anatomistModule.Anatomist.close(self)
            else:
                # self.execute( 'DeleteAll' )
                # self.getControlWindow().close()
                if neuroConfig.anatomistImplementation != 'socket':
                    reusablewinhook.uninstallWindowHandler()
                anatomistModule.Anatomist.close(self)
            print('anatomist closed.')

    # end of Anatomist class

    class AWindowChoice(neuroData.Choice):

        '''
        A process parameter to choose an Anatomist window.
        This parameter is a choice between several anatomist windows. By
        default, if anatomist isn't started or if there's no opened windows,
        the choice offer the possibility to create any type of anatomist
        window.

        If anatomist is started, the set of opened window is added to the
        choices list.

        The list of choices is not updated automatically, to refresh the
        choices it is necessary to open a new instance of the process.
        '''

        def __init__(self, noSelectionLabel=None, aslist=False):
            neuroData.Choice.__init__(self)
            self._init2(noSelectionLabel, aslist=aslist)

        def _init2(self, noSelectionLabel=None, aslist=False):
            if noSelectionLabel is None:
                noSelectionLabel = '<' + _t_('None') + '>'
            self._initargs = (noSelectionLabel, aslist)
            self.noSelectionLabel = noSelectionLabel
            self.aslist = aslist
            # initial choice : creating new windows
            self.selWindow = ('<' + _t_('Selected windows in Anatomist') + '>',
                              self.getSelectedWindows)
            self.newWindow = (
                '<' + _t_('New window (3D)') + '>', self._newWindow)
            self.newWindowA = ('<' + _t_('New window (Axial)') + '>',
                               lambda self=self: self._newWindow('Axial'))
            self.newWindowS = ('<' + _t_('New window (Sagittal)') + '>',
                               lambda self=self: self._newWindow('Sagittal'))
            self.newWindowC = ('<' + _t_('New window (Coronal)') + '>',
                               lambda self=self: self._newWindow('Coronal'))
            self.newWindowB = ('<' + _t_('New window (Browser)') + '>',
                               lambda self=self: self._newWindow('Browser'))
            self.refreshChoices()

        def __getinitargs__(self):
            """
            getinitargs, getstate, setstate are used by copy module.
            When a process is opened, its signature is copied. So if the
            signature contains a AWindowChoice, it is copied too.
            If a shallow copy is done, the new AWindowChoice will contain
            reference from the source object and it is a source of problems. So
            these methods are redefined in order to make the shallow copy
            create a new distinct instance.

            With getinitargs, the constructor is called to create the new
            object. And because getstate and setstate do nothing, the source
            object is not copied, the two objects are distinct instances.
            """
            return self._initargs

        def __getstate__(self):
            dic = neuroData.Choice.__getstate__(self)
            dic['values'] = []
            return dic

        def __setstate__(self, state):
            neuroData.Choice.__setstate__(self, state)
            self._init2()

        def getSelectedWindows(self):
            a = Anatomist(create=False)
            if a is not None:
                windows = a.getWindows()
                sels = [w for w in windows if w.getInfos()['selected']]
                if self.aslist:
                    return sels
                if len(sels) == 1:
                    return sels[0]
                else:
                    return None
            if self.aslist:
                return []
            else:
                return None

[docs] def refreshChoices(self, *args): """ Updates choice list. Adds to default choices curently opened anatomist windows. """ # if anatomist is not started, this command will not start it. a = Anatomist(create=False) if a is not None: try: windows = a.getWindows() choices = [self.selWindow, self.newWindow, self.newWindowA, self.newWindowS, self.newWindowC, self.newWindowB ] + \ [ # (repr(w), lambda w=w: w), windows ) # label=repr(window), value=a lambda function that returns the window (repr(w), lambda wr=w.getRef("Weak"): wr) for w in windows] # label=repr(window), value=a lambda function that returns the window # before value was weakref.ref(w, # self.refreshChoices) but it doesn't work with # current anatomist api objects : the window object # is not a permanant object, it is only created for # the getWindows method and is destroyed at the end # of refreshChoices, which calls the callback, # which create a new window object (proxy for # anatomist window)... -> infinite loop self.setChoices(*choices) except Exception: # if it is impossible to get informations about anatomist # windows, it may be already closed (close window event may # occur after exit event...), so retrieve default choices. self.clearChoices() else: self.clearChoices()
def clearChoices(self): self.setChoices(self.selWindow, self.newWindow, self.newWindowA, self.newWindowS, self.newWindowC, self.newWindowB) def _newWindow(self, type="3D"): """ Creates a new Anatomist window of the given type. """ win = Anatomist().createWindow(type) if self.aslist: return [win] return win else: # if anatomist module is not available: empty classes
[docs] class Anatomist(object): pass
[docs] class AWindowChoice(object): def __init__(self, *args, **kwargs): pass