#!/usr/bin/env python
# -*- 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.
# import anatomist.threaded.api as ana
from __future__ import print_function
from __future__ import absolute_import
from soma.qt_gui.qt_backend import QtCore, Qt, QtGui
from soma.qt_gui.qt_backend.QtGui import QRadioButton, QPalette, QButtonGroup, QLabel, QFrame, QVBoxLayout, QColor
from soma.qt_gui.qt_backend.uic import loadUi
from brainvisa.processing.qtgui.neuroProcessesGUI import mainThreadActions
from soma.qt_gui.qtThread import MainThreadLife
from functools import partial
import brainvisa.anatomist as ana
from anatomist.cpp.paletteEditor import PaletteEditor
import weakref
from six.moves import range
from six.moves import zip
#------------------------------------------------------------------------------
[docs]def displayTitledGrid(transformationManager, parent, inverseRawColumn,
                      objPathMatrix,
                      rowTitles=['raw_space', "MRI_native_space", "mask",
                                 "MNI_space", ],
                      rowColors=['darkOrange', 'blue', "MRI", 'blue',
                                 'magenta'],
                      colTitles=['PET', "MRI", "grey"],
                      windowTitle='View grid',
                      linkWindows='space',
                      overlaidImages=[],
                      mainColormap='B-W LINEAR',
                      overlayColormap='RAINBOW',
                      customOverlayColormap='Blue-White',
                      rowButtonSubTitles=None
                      ):
    '''Grid of Anatomist views
    Parameters:
    linkWindows: str
        possible values : 'all' | none | row
    rowColors: list
        default: ['darkOrange', 'blue', "MRI", 'blue', 'magenta']
        orange = rawSpace, blue = mri space, magenta = mni space
    '''
    _mw = mainThreadActions().call(_displayTitledGrid_onGuiThread,
                                   transformationManager, parent,
                                   inverseRawColumn,
                                   objPathMatrix, rowTitles=rowTitles,
                                   rowColors=rowColors, colTitles=colTitles,
                                   windowTitle=windowTitle,
                                   linkWindows=linkWindows,
                                   overlaidImages=overlaidImages,
                                   mainColormap=mainColormap,
                                   overlayColormap=overlayColormap,
                                   customOverlayColormap=customOverlayColormap,
                                   rowButtonSubTitles=rowButtonSubTitles)
    mw = MainThreadLife(
        _mw)  # ensure mw destruction takes place in the GUI thread
    return mw 
def _displayTitledGrid_onGuiThread(transformationManager, parent,
                                   inverseRawColumn, objPathMatrix, rowTitles,
                                   rowColors, colTitles, windowTitle,
                                   linkWindows, overlaidImages, mainColormap,
                                   overlayColormap, customOverlayColormap,
                                   rowButtonSubTitles):
    # DisplayTitledGrid doit etre construit sur le thread de Gui pour etre sur
    # que la destruction de la mw se fasse sur le thread de Gui
    TitledGrid = DisplayTitledGrid(
        objPathMatrix, parent=parent, mainColormap=mainColormap,
      overlayColormap=overlayColormap,
      customOverlayColormap=customOverlayColormap)
    mw = TitledGrid.display(inverseRawColumn=inverseRawColumn,
                            windowFlag=QtCore.Qt.Window, windowTitle=windowTitle, rowTitles=rowTitles,
                            colTitles=colTitles, rowColors=rowColors, linkWindows=linkWindows,
                            overlaidImages=overlaidImages, rowButtonSubTitles=rowButtonSubTitles)[0]
    return TitledGrid
#------------------------------------------------------------------------------
[docs]class DisplayTitledGrid(QtGui.QWidget):
    def __init__(self, objPathMatrix, parent=None,
                 mainColormap='B-W LINEAR',
                 overlayColormap='RAINBOW',
                 customOverlayColormap='Blue-White'):
        super(DisplayTitledGrid, self).__init__(parent)
        self._main_colormap = mainColormap
        self._overlay_colormap = overlayColormap
        self._custom_overlay_colormap = customOverlayColormap
        self._loadObjectInAnatomist(objPathMatrix, interpolation=False)
        self._overlaid_images = []
        self._overlay_fusions = []
        self._custom_overlay_fusions = []
            # momoTODO : pas besoin d'une liste, un seul suffit sinon
            # l'utilisateur s'y perd (selection avec row et column).
        self._selectedRow = -1
        self._selectedColumn = -1
        self._row_titles = []
        self._col_titles = []
        self._paletteEditor = None
    def __del__(self):
        # make sure mw is deleted first, otherwise its connected signals still point
        # to slots (in c++) in self, when self is already destroyed, which
        # generally results in a crash
        self.mw.close()
        del self.mw
    def display(self, inverseRawColumn=False, windowFlag=QtCore.Qt.Window,
                windowTitle='Compare',
                rowTitles=['row_1', 'row_2', 'row_3', 'row_4'],
                colTitles=['col_1', 'col_2', 'col_3'],
                rowColors=['darkOrange', 'blue', 'blue', 'magenta'],
                linkWindows='space',
                overlaidImages=[],
                rowButtonSubTitles=None):
        layout = QtGui.QVBoxLayout(self)
        self.setLayout(layout)
        self.mw = self._loadUserInterface()  # create self.mw.gridLayout
        self.mw.setWindowTitle(windowTitle)
        layout.addWidget(self.mw)
        self._row_titles = rowTitles
        self._col_titles = colTitles
        # self._custom_row_titles = [ x for x in rowTitles ]
        # load overlay (fusionned) images, and make fusions
        self._loadOverlayImages(overlaidImages)
        self._createOverlayFusions()
        self._addColumnButton(colTitles, inverseRawColumn)
        self._addRowButton(
            rowTitles, rowColors, inverseRawColumn, rowButtonSubTitles)
        # self._createWinFrame(self.mw, self.mw.selectedReferenceLabel) #
        # momoTODO : meme cadre autour de selected reference
        self._createAndLinkAnatomistWindowsInMainLayout(
            linkWindows, inverseRawColumn, 'Sagittal', rowTitles)
        self.mw.anatomistObjectList = self.anatomistObjectList  # momo  :ca sert a quoi?
        # replace individual objects by overlays fusions when applicable
        self._addObjectOrFusion_inAnatomistWindows()
        self.mw.comboBox.currentIndexChanged.connect(self._onComboBox_changed)
        self.mw.mixingSlider.valueChanged.connect(self._onMixingRateChanged)
        self.mw.maximizeButton.clicked.connect(self._onMaximizeButtonClicked)
        self.show()
        return [self.mw]
#-----------------------------------------------------------------------------
# private : begins with _
#-----------------------------------------------------------------------------
    def _loadObjectInAnatomist(self, objPathMatrix, interpolation=True):
        a = ana.Anatomist()
        self.anatomistObjectList = []
        for r in range(0, len(objPathMatrix)):
            objPathRow = objPathMatrix[r]
            anaObjRow = []
            for c in range(0, len(objPathRow)):
                objPath = objPathRow[c]
                if (objPath is not None):
                    obj = a.loadObject(objPath, forceReload=False)
                    if not interpolation:
                        obj.attributed()['volumeInterpolation'] = False
                    obj.setPalette(self._main_colormap)
                    anaObjRow.append(obj)
                else:
                    anaObjRow.append(None)
            self.anatomistObjectList.append(anaObjRow)
    def _loadUserInterface(self):
        dotIdx = __file__.rindex('.')
        uiFileName = __file__[:dotIdx] + '.ui'
        mw = loadUi(uiFileName)
        mw.mixRate.setText('50 %')
        return mw
    def _addColumnButton(self, buttonTitles, inverseRawColumn):
        for buttonIndex in range(0, len(buttonTitles)):
            title = buttonTitles[buttonIndex]
            button = QRadioButton(title)
            if (inverseRawColumn):
                self.mw.gridLayout.addWidget(
                    button, buttonIndex + 1, 0, QtCore.Qt.AlignHCenter)
                self.mw.gridLayout.setRowStretch(buttonIndex + 1, 10)
            else:
                self.mw.gridLayout.addWidget(
                    button, 0, buttonIndex + 1, QtCore.Qt.AlignHCenter)
                self.mw.gridLayout.setColumnStretch(buttonIndex + 1, 10)
            button.clicked.connect(partial(
                                   self.__class__._onColumnButtonClicked, weakref.proxy(
                                       self),
                                   buttonIndex))
    def _addRowButton(self, buttonTitles, buttonColors, inverseRawColumn, rowButtonSubTitles=None):
        self.rowsButtonGroup = QButtonGroup(self.mw)
        self.rowsButtonGroup.setExclusive(True)
        for buttonIndex in range(0, len(buttonTitles)):
            title = buttonTitles[buttonIndex]
            NotNoneCount = len(
                [x for x in self.anatomistObjectList[buttonIndex] if x != None])
            isFusionPossibleOnRow = NotNoneCount > 1 or len(
                self._overlaid_images) > 0
            widget = DisplayTitledGrid._createColoredButton(
                title, buttonColors[buttonIndex])
            self.rowsButtonGroup.addButton(widget, buttonIndex)
            widget.setToolTip(
                '<p>Click on this button to superimpose a different image. To do so, click on this row button, then click on a column button to display the column main image as overlay on this row.<p><p>Click again on the tow button to go back to the initial views.</p>')
            widget.clicked.connect(partial(
                                   self.__class__._onRowButtonClicked, weakref.proxy(
                                       self),
                                   buttonIndex))
            if(rowButtonSubTitles is not None and buttonIndex < len(rowButtonSubTitles)):
                subTitle = rowButtonSubTitles[buttonIndex]
                vLay = QVBoxLayout()
                vLay.insertStretch(0, 2)
                vLay.addWidget(widget)
                vLay.addWidget(QLabel(subTitle))
                vLay.addStretch(2)
                if (inverseRawColumn):
                    self.mw.gridLayout.addLayout(vLay, 0, buttonIndex + 1)
                    self.mw.gridLayout.setColumnStretch(buttonIndex + 1, 10)
                else:
                    self.mw.gridLayout.addLayout(vLay, buttonIndex + 1, 0)
                    self.mw.gridLayout.setRowStretch(buttonIndex + 1, 10)
            else:
                if (inverseRawColumn):
                    self.mw.gridLayout.addWidget(widget, 0, buttonIndex + 1)
                    self.mw.gridLayout.setColumnStretch(buttonIndex + 1, 10)
                else:
                    self.mw.gridLayout.addWidget(widget, buttonIndex + 1, 0)
                    self.mw.gridLayout.setRowStretch(buttonIndex + 1, 10)
    @staticmethod
    def _createColoredButton(title, color):
        button = QRadioButton(title)
        buttonPalette = QPalette()
        buttonPalette.setColor(QPalette.ButtonText, Qt.QColor(color))
        button.setPalette(buttonPalette)
        # button.setDisabled(True)
        button.setCheckable(True)
        return button
    def _createAndLinkAnatomistWindowsInMainLayout(self, linkWindows, inverseRawColumn, initialView, spaceNames):
        mw = self.mw
        mw.anaWinMatrix = []
        for r in range(0, len(self.anatomistObjectList)):
            anaWinRow = self._createAnatomistWindows_InMainLayout(
                inverseRawColumn, initialView, r)
            DisplayTitledGrid._linkAnatomistWindows(
                linkWindows, anaWinRow, spaceNames)
    def _createAnatomistWindows_InMainLayout(self, inverseRawColumn, view, rowIndex):
        mw = self.mw
        a = ana.Anatomist()
        anaObjRow = self.anatomistObjectList[rowIndex]
        anaWinRow = []
        anatomistConfig = a.config()
        isWindowSizeFactorExistInConfig = False
        if 'windowSizeFactor' in anatomistConfig:
            sizefac = a.config()['windowSizeFactor']
            isWindowSizeFactorExistInConfig = True
        a.config()['windowSizeFactor'] = 1.
        for c in range(0, len(anaObjRow)):
            anaObj = anaObjRow[c]
            if (anaObj is not None):
                w = a.createWindow(view, no_decoration=True)
                anaObj.addInWindows([w])
                anaWinRow.append(w)
                frame = self._createWinFrame(mw, w.getInternalRep())
                if (inverseRawColumn):
                    mw.gridLayout.addWidget(frame, c + 1, rowIndex + 1)
                else:
                    mw.gridLayout.addWidget(frame, rowIndex + 1, c + 1)
            else:
                anaWinRow.append(None)
        if(isWindowSizeFactorExistInConfig):
            a.config()['windowSizeFactor'] = sizefac
        mw.anaWinMatrix.append(anaWinRow)
        return mw.anaWinMatrix
    def _createWinFrame(self, mw, widget):
        mw.frame = QFrame()
        mw.flay = QVBoxLayout(mw.frame)
        mw.flay.addWidget(widget)
        mw.frame.setObjectName('winborder')
        mw.frame.setStyleSheet(
            'QFrame#winborder { border: 0px solid; border-radius: 4px; }')
        pal = mw.frame.palette()
        pal.setColor(QPalette.Dark, QColor(255, 192, 0))
        pal.setColor(QPalette.Midlight, QColor(192, 255, 0))
        pal.setColor(QPalette.Shadow, QColor(192, 0, 255))
        pal.setColor(QPalette.Light, QColor(0, 255, 192))
        pal.setColor(QPalette.Mid, QColor(0, 192, 255))
        return mw.frame
    @staticmethod
    def _linkAnatomistWindows(linkWindows, anaWinRow, spaceNames):
        if (linkWindows == 'all'):
            DisplayTitledGrid._linkAnatomistWindows_all(anaWinRow)
        elif (linkWindows == 'row'):
            DisplayTitledGrid._linkAnatomistWindows_byRow(anaWinRow)
        elif (linkWindows == 'space'):
            DisplayTitledGrid._linkAnatomistWindows_bySpace(
                anaWinRow, spaceNames)
    @staticmethod
    def _linkAnatomistWindows_all(anaWinMatrix):
        a = ana.Anatomist()
        wins = []
        for anaWinRow in anaWinMatrix:
            for w in anaWinRow:
                if (w is not None):
                    wins.append(w)
        a.linkWindows(wins, group=None)
        a.execute('WindowConfig', windows=wins,
                  linkedcursor_on_slider_change=1)
    @staticmethod
    def _linkAnatomistWindows_byRow(anaWinMatrix):
        a = ana.Anatomist()
        for anaWinRow in anaWinMatrix:
            wins = []
            for w in anaWinRow:
                if (w is not None):
                    wins.append(w)
            a.linkWindows(wins, group=None)
            a.execute('WindowConfig', windows=wins,
                      linkedcursor_on_slider_change=1)
    @staticmethod
    def _linkAnatomistWindows_bySpace(anaWinMatrix, spaceNames):
        a = ana.Anatomist()
        winsDico = {}
        for anaWinRow, spaceName in zip(anaWinMatrix, spaceNames):
            isRawSpace = spaceName.lower().count('raw') != 0
            if (isRawSpace is False):  # inutile de lier les fenetres si leur images sont des raw, donc dans leur propre espace. Par exemple, ne pas lier la pet avec l'irm
                keySpace = DisplayTitledGrid._convertSpaceName_to_key(
                    spaceName)
                for w in anaWinRow:
                    if (w is not None):
                        if keySpace in winsDico:
                            prevWins = winsDico[keySpace]
                            prevWins.append(w)
                            winsDico.update({keySpace: prevWins})
                        else:
                            winsDico.update({keySpace: [w]})
        for _k, wins in winsDico.items():
            a.linkWindows(wins, group=None)
            a.execute('WindowConfig', windows=wins,
                      linkedcursor_on_slider_change=1)
    @staticmethod
    def _convertSpaceName_to_key(spaceName):
        isMRISpace = spaceName.lower().count('mri') > 0
        isPETSpace = spaceName.lower().count('pet') > 0
        isMNISpace = spaceName.lower().count('mni') > 0
        keySpace = spaceName
        if (isMRISpace):
            keySpace = 'mri'
        elif (isPETSpace):
            keySpace = 'pet'
        elif (isMNISpace):
            keySpace = 'mni'
        return keySpace
    def _onComboBox_changed(self):
        for anaWinRow in self.mw.anaWinMatrix:
            for w in anaWinRow:
                if(w is not None):
                    if(self.mw.comboBox.currentText() == 'Axial'):
                        w.muteAxial()
                    elif(self.mw.comboBox.currentText() == 'Sagittal'):
                        w.muteSagittal()
                    elif(self.mw.comboBox.currentText() == 'Coronal'):
                        w.muteCoronal()
    def _loadOverlayImages(self, overlaidImages):
        a = ana.Anatomist()
        images = []
        for filename in overlaidImages:
            if filename:  # may be None to leave an un-overlayed row
                image = a.loadObject(filename, forceReload=False)
                images.append(image)
                image.setPalette(palette=self._overlay_colormap)
            else:  # None
                images.append(None)
        self._overlaid_images = images
    def _createOverlayFusions(self):
        if len(self._overlaid_images) == 0:
            # no overlays, nothing to be done.
            return
        matriceFusions = []
        if len(self._overlaid_images) == len(self.anatomistObjectList) \
                
* len(self.anatomistObjectList[0]):
            indiv_index = 0
            for row, objRow in enumerate(self.anatomistObjectList):
                overlays = []
                for col, objCol in enumerate(objRow):
                    index = indiv_index
                    indiv_index += 1
                    overlayimage = self._overlaid_images[index]
                    overlays.append(overlayimage)
                rowFusions = self._createFusionsWithOverlay(objRow, overlays)
                matriceFusions.append(rowFusions)
        else:
            for row, objRow in enumerate(self.anatomistObjectList):
                index = row
                if index >= len(self._overlaid_images):
                    overlayimage = self._overlaid_images[-1]
                else:
                    overlayimage = self._overlaid_images[index]
                rowFusions = self._createFusionsWithOverlay(
                    objRow, overlayimage)
                matriceFusions.append(rowFusions)
        self._overlay_fusions = matriceFusions
    def _createCustomOverlayFusions(self, row, column):
        if row >= 0 and column >= 0:
            overlayimage = self.anatomistObjectList[row][column]
            if overlayimage is not None:
                newoverlay = self._setPaletteOfOverlay(overlayimage)
                rowFusions = self._createFusionsWithOverlay(
                    self.anatomistObjectList[row], newoverlay, overlayimage)
                if len(self._custom_overlay_fusions) <= row:
                    self._custom_overlay_fusions.extend(
                        [[]] * (row + 1 - len(self._custom_overlay_fusions)))
                self._custom_overlay_fusions[row] = rowFusions
                a = ana.Anatomist()
                a.execute('TexturingParams', objects=[
                          x for x in rowFusions if x], texture_index=1, rate=float(self.mw.mixingSlider.value()) / 100)
            elif(row < len(self._custom_overlay_fusions)):
                self._custom_overlay_fusions[row] = None
    def _createFusionsWithOverlay(self, objects, overlayimages,
                                  imageWithoutFusion=None):
        if not isinstance(overlayimages, list):
            overlayimages = [overlayimages]
        a = ana.Anatomist()
        rowFusions = []
        for index, obj in enumerate(objects):
            if index < len(overlayimages):
                overlayimage = overlayimages[index]
            else:
                overlayimage = overlayimages[-1]
            if obj and overlayimage and obj != overlayimage and (not imageWithoutFusion or imageWithoutFusion != obj):
                fusion = a.fusionObjects(
                    objects=[obj, overlayimage], method='Fusion2DMethod')
                rowFusions.append(fusion)
            else:
                rowFusions.append(None)
        return rowFusions
    def _setPaletteOfOverlay(self, overlayimage):
        a = ana.Anatomist()
        if (self._custom_overlay_colormap is not None):
            newoverlay = a.duplicateObject(overlayimage)
            newoverlay.setPalette(self._custom_overlay_colormap)
        else:
            newoverlay = overlayimage
            overlayimagepalette = overlayimage.palette().refPalette()
            paletteName = overlayimagepalette.name()
            newoverlay.setPalette(paletteName)
        return newoverlay
    def _addObjectOrFusion_inAnatomistWindows(self):
        if len(self._overlay_fusions) == len(self.mw.anaWinMatrix):
            byrow = False
        else:
            byrow = True
        indiv_index = 0
        for row, _anaWinRow in enumerate(self.mw.anaWinMatrix):
            if byrow:
                index = row
            else:
                index = indiv_index
                indiv_index += 1
            if index < len(self._overlay_fusions):
                fusRow = self._overlay_fusions[index]
                self._addObjectOrFusion_inAnatomistWindowsRow(row, fusRow)
    def _addObjectOrFusion_inAnatomistWindowsRow(self, rowIndex, rowFusions):  # rowFusions can be self._overlay_fusions or self._custom_overlay_fusions
        if(rowIndex >= 0):
            anaWinRow = self.mw.anaWinMatrix[rowIndex]
            objRow = self.anatomistObjectList[rowIndex]
            for col, win in enumerate(anaWinRow):
                if win:
                    if win.objects:
                        win.removeObjects(win.objects)
                    if rowFusions and rowFusions[col]:
                        win.addObjects(rowFusions[col])
                    elif objRow and objRow[col]:
                        win.addObjects(objRow[col])
    def _removeCustomOverlays(self, row):
        self._custom_overlay_fusions[row] = []
        self._addObjectOrFusion_inAnatomistWindowsRow(
            row, self._overlay_fusions[row])
    def _onMixingRateChanged(self, value):
        self.mw.mixRate.setText(str(value) + ' %')
        a = ana.Anatomist()
        objects = []
        for fusRow in self._overlay_fusions:
            if(fusRow):
                objects.extend([x for x in fusRow if x])
        for fusRow in self._custom_overlay_fusions:
            if(fusRow):
                objects.extend([x for x in fusRow if x])
        a.execute('TexturingParams', objects=objects, texture_index=1,
                  rate=float(value) / 100)
    def _onColumnButtonClicked(self, column):
        oldcolumn = self._selectedColumn
        self._selectedColumn = column
        row = self.rowsButtonGroup.checkedId()
        self._removeWinFrame(row, oldcolumn)
        self._createCustomOverlayFusions(row, column)
        if(0 <= row and row < len(self._custom_overlay_fusions)):
            self._addObjectOrFusion_inAnatomistWindowsRow(
                row, self._custom_overlay_fusions[row])
            self._highlightWinFrame(row, column)
        self._updatePalette()
        self._updateSelectedReferenceName()
    def _onRowButtonClicked(self, row):
        self._createCustomOverlayFusions(row, self._selectedColumn)
        self._addObjectOrFusion_inAnatomistWindowsRow(self._selectedRow, self._selectRowForFusions(
            self._selectedRow, thisRowIsSelected=False))  # reset previous selectedRow
        self._removeWinFrame(self._selectedRow, self._selectedColumn)
        isRowUnselected = self._selectedRow == row
        if (isRowUnselected):
            self._unselectRowForFusion(row)
        else:
            self._addObjectOrFusion_inAnatomistWindowsRow(
                row, self._selectRowForFusions(row))
            self._highlightWinFrame(row, self._selectedColumn)
        self._updatePalette()
        self._updateSelectedReferenceName()
    def _removeWinFrame(self, row, column):
        if row >= 0 and row < len(self.mw.anaWinMatrix) and column >= 0:
            winrow = self.mw.anaWinMatrix[row]
            if column < len(winrow):
                anatomistWindow = winrow[column]
                if(anatomistWindow is not None):
                    anatomistWindow.parent().setStyleSheet(
                        'QFrame#winborder { border: 0px; }')  # momoTODO : il n'y a pas de parent!
    def _highlightWinFrame(self, row, column):
        if row >= 0 and row < len(self.mw.anaWinMatrix) and column >= 0:
            winrow = self.mw.anaWinMatrix[row]
            if column < len(winrow):
                if(winrow[column] is not None):
                    winrow[column].parent().setStyleSheet(
                        'QFrame#winborder { border: 2px solid #c06000; border-radius: 4px; }')
    def _updatePalette(self):
        if (self._selectedColumn >= 0 and self._selectedRow >= 0):
            if (self._paletteEditor is not None):
                self._paletteEditor.close()
            selectedImage = self.anatomistObjectList[
                self._selectedRow][self._selectedColumn]
            if(selectedImage is not None):
                self._paletteEditor = PaletteEditor(
                    selectedImage, parent=self.mw, real_max=10000, sliderPrecision=10000, zoom=1)
                self.mw.horizontalLayout.insertWidget(3, self._paletteEditor)
    def _updateSelectedReferenceName(self):
        if (self._selectedColumn >= 0 and self._selectedRow >= 0):
            self.mw.selectedReferenceName.setText('<b><font color=#c06000>' + self._col_titles[
                                                  self._selectedColumn] + '_' + self._row_titles[self._selectedRow] + '</font></b>')
            self.mw.selectedReferenceName.setStyleSheet(
                '#selectedReferenceName { border: 2px solid #c06000; border-radius: 4px; padding: 4px; }')
        else:
            self.mw.selectedReferenceName.setText('None')
            self.mw.selectedReferenceName.setStyleSheet(
                '#selectedReferenceName { border: 0px; }')
    def _unselectRowForFusion(self, row):
        self._selectedRow = -1
        self._unselectButtonInGroup(self.rowsButtonGroup, row)
# button.setText(self._row_titles[self._selectedRow])# momoTODO : pas
# besoin de changer le text si c'est un radio bouton. Le text peut
# contenir une information d'espace (mni, mri...) à ne pas mélanger avec
# la fusion
    def _unselectButtonInGroup(self, group, buttonId):
        if(buttonId >= 0):
            button = group.button(buttonId)
            group.setExclusive(False)
            button.setChecked(False)
            group.setExclusive(True)
    def _selectRowForFusions(self, row, thisRowIsSelected=True):
        self._selectedRow = row
        fusions = None
        isCustomFusionsExist = len(self._custom_overlay_fusions) > 0 and len(
            self._custom_overlay_fusions) > self._selectedRow
        isFusionsExist = len(self._overlay_fusions) > 0 and len(
            self._overlay_fusions) > self._selectedRow
        if isCustomFusionsExist and thisRowIsSelected:
            fusions = self._custom_overlay_fusions[self._selectedRow]
        elif isFusionsExist:
            fusions = self._overlay_fusions[self._selectedRow]
        return fusions
    def _onMaximizeButtonClicked(self):
        print("_onMaximizeButtonClicked") 
              # momoTODO utiliser display fusion et afficher la fusion de tous
              # les objets de la ligne sélectionnée
# momoTODO : encadrer la reference utiliser pour la fusion
#    painter = QPainter(mw)
#    painter.setPen(Qt.QColor('yellow'))
#    cellRect = mw.gridLayout.cellRect (rowIndex + 1, c + 1 )
#    cellRectWidth = cellRect.width()
#    cellRect.setWidth(cellRectWidth+200)
#    painter.fillRect(cellRect, Qt.QColor('yellow'))
#    painter.drawRect(cellRect)