예제 #1
0
class GUI(QtGui.QWidget):
    def __init__(self):

        super(GUI, self).__init__()

        self.setWindowTitle('Label Cells')
        self.cellNames = [
            '1.p', '4.a', '1.pp', '4.aa', '1.ppa', '1.ppp', '4.aaa', '4.aap',
            'b_1', 'b_4'
        ]
        self.initUI()

    #-----------------------------------------------------------------------------------------------
    # INITIALIZATION OF THE WINDOW - DEFINE AND PLACE ALL THE WIDGETS
    #-----------------------------------------------------------------------------------------------

    def initUI(self):

        # SET THE GEOMETRY

        mainWindow = QtGui.QVBoxLayout()
        mainWindow.setSpacing(15)

        fileBox = QtGui.QHBoxLayout()
        spaceBox1 = QtGui.QHBoxLayout()
        rawDataBox = QtGui.QHBoxLayout()

        mainWindow.addLayout(fileBox)
        mainWindow.addLayout(spaceBox1)
        mainWindow.addLayout(rawDataBox)

        Col1 = QtGui.QGridLayout()
        Col2 = QtGui.QHBoxLayout()
        Col3 = QtGui.QVBoxLayout()

        rawDataBox.addLayout(Col1)
        rawDataBox.addLayout(Col2)
        rawDataBox.addLayout(Col3)

        self.setLayout(mainWindow)

        # DEFINE ALL WIDGETS AND BUTTONS

        loadBtn = QtGui.QPushButton('Load DataSet')
        saveBtn = QtGui.QPushButton('Save data (F12)')

        tpLbl = QtGui.QLabel('Relative Tp:')
        slLbl = QtGui.QLabel('Slice:')
        fNameLbl = QtGui.QLabel('File name:')

        self.tp = QtGui.QSpinBox(self)
        self.tp.setValue(0)
        self.tp.setMaximum(100000)

        self.sl = QtGui.QSpinBox(self)
        self.sl.setValue(0)
        self.sl.setMaximum(100000)

        self.fName = QtGui.QLabel('')

        self._488nmBtn = QtGui.QRadioButton('488nm')
        self._561nmBtn = QtGui.QRadioButton('561nm')
        self.CoolLEDBtn = QtGui.QRadioButton('CoolLED')

        self.sld1 = QtGui.QSlider(QtCore.Qt.Vertical, self)
        self.sld1.setMaximum(2**16 - 1)
        self.sld1.setValue(0)
        self.sld2 = QtGui.QSlider(QtCore.Qt.Vertical, self)
        self.sld2.setMaximum(2**16)
        self.sld2.setValue(2**16 - 1)

        self.fig1 = Figure((8.0, 8.0), dpi=100)
        self.fig1.subplots_adjust(left=0., right=1., top=1., bottom=0.)
        self.ax1 = self.fig1.add_subplot(111)
        self.canvas1 = FigureCanvas(self.fig1)
        self.canvas1.setFocusPolicy(QtCore.Qt.ClickFocus)
        self.canvas1.setFocus()
        self.canvas1.setFixedSize(QtCore.QSize(600, 600))
        self.canvas1.setSizePolicy(QtGui.QSizePolicy.Expanding,
                                   QtGui.QSizePolicy.Expanding)

        self.cellTbl = QtGui.QTableWidget()

        self.fig2 = Figure((4.0, 4.0), dpi=100)
        self.fig2.subplots_adjust(left=0., right=1., top=1., bottom=0.)
        self.ax2 = self.fig2.add_subplot(111)
        self.canvas2 = FigureCanvas(self.fig2)
        self.canvas2.setFixedSize(QtCore.QSize(300, 300))
        self.canvas2.setSizePolicy(QtGui.QSizePolicy.Expanding,
                                   QtGui.QSizePolicy.Expanding)

        # self.cellNamesBox=QtGui.QMenu(self.canvas1)
        # actn = []
        # for cname in self.cellNames:
        #     actn.append( QtGui.QAction(cname, self.cellNamesBox) )
        #     self.cellNamesBox.addAction(actn[-1])
        #     actn[-1].triggered.connect( lambda item = cname : self.actionFunct(cname) )

        self.cellNamesBox = QtGui.QMenu(self.canvas1)

        ### define all actions (can be done better!!!)
        actn1p = QtGui.QAction('1.p', self.cellNamesBox)
        self.cellNamesBox.addAction(actn1p)
        actn1p.triggered.connect(lambda item='1.p': self.actionFunct1p('1.p'))

        actn1pp = QtGui.QAction('1.pp', self.cellNamesBox)
        self.cellNamesBox.addAction(actn1pp)
        actn1pp.triggered.connect(
            lambda item='1.pp': self.actionFunct1pp('1.pp'))

        actn1ppp = QtGui.QAction('1.ppp', self.cellNamesBox)
        self.cellNamesBox.addAction(actn1ppp)
        actn1ppp.triggered.connect(
            lambda item='1.ppp': self.actionFunct1ppp('1.ppp'))

        actn1ppa = QtGui.QAction('1.ppa', self.cellNamesBox)
        self.cellNamesBox.addAction(actn1ppa)
        actn1ppa.triggered.connect(
            lambda item='1.ppa': self.actionFunct1ppa('1.ppa'))

        actn4a = QtGui.QAction('4.a', self.cellNamesBox)
        self.cellNamesBox.addAction(actn4a)
        actn4a.triggered.connect(lambda item='4.a': self.actionFunct4a('4.a'))

        actn4aa = QtGui.QAction('4.aa', self.cellNamesBox)
        self.cellNamesBox.addAction(actn4aa)
        actn4aa.triggered.connect(
            lambda item='4.aa': self.actionFunct4aa('4.aa'))

        actn4aaa = QtGui.QAction('4.aaa', self.cellNamesBox)
        self.cellNamesBox.addAction(actn4aaa)
        actn4aaa.triggered.connect(
            lambda item='4.aaa': self.actionFunct4aaa('4.aaa'))

        actn4aap = QtGui.QAction('4.aap', self.cellNamesBox)
        self.cellNamesBox.addAction(actn4aap)
        actn4aap.triggered.connect(
            lambda item='4.aap': self.actionFunct4aap('4.aap'))

        actnb1 = QtGui.QAction('b_1', self.cellNamesBox)
        self.cellNamesBox.addAction(actnb1)
        actnb1.triggered.connect(lambda item='b_1': self.actionFunctb1('b_1'))

        actnb4 = QtGui.QAction('b_4', self.cellNamesBox)
        self.cellNamesBox.addAction(actnb4)
        actnb4.triggered.connect(lambda item='b_4': self.actionFunctb4('b_4'))
        ### END OF THE UGLY PART :)

        self.cellNamesBox.installEventFilter(self)
        self.canvas1.installEventFilter(self)
        self.cellNamesBox.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self.cellNamesBox.connect(
            self.cellNamesBox,
            QtCore.SIGNAL("customContextMenuRequested(QPoint)"),
            self.leftClicked)

        # PLACE ALL THE WIDGET ACCORDING TO THE GRIDS

        fileBox.addWidget(loadBtn)
        fileBox.addWidget(saveBtn)

        spaceBox1.addWidget(self.HLine())

        Col1.addWidget(tpLbl, 0, 0)  #, 1, 1, Qt.AlignTop)
        Col1.addWidget(self.tp, 0, 1)  #, 1, 1, Qt.AlignTop)
        Col1.addWidget(slLbl, 1, 0)  #, 1, 1, Qt.AlignTop)
        Col1.addWidget(self.sl, 1, 1)  #, 1, 1, Qt.AlignTop)
        Col1.addWidget(fNameLbl, 2, 0)
        Col1.addWidget(self.fName, 2, 1)
        Col1.addWidget(self._488nmBtn, 3, 0)
        Col1.addWidget(self._561nmBtn, 4, 0)
        Col1.addWidget(self.CoolLEDBtn, 5, 0)

        Col2.addWidget(self.sld1)
        Col2.addWidget(self.sld2)
        Col2.addWidget(self.canvas1)

        # Col3.addWidget(self.cellTbl)
        # Col3.addWidget(self.cellNamesBox)
        Col3.addWidget(self.canvas2)

        self.setFocus()

        self.show()

        # BIND BUTTONS TO FUNCTIONS

        loadBtn.clicked.connect(self.selectWorm)
        saveBtn.clicked.connect(self.saveData)

        self.checkNames = True
        self.tp.valueChanged.connect(self.changeTp)
        self.sl.valueChanged.connect(self.updateCanvas1)
        self.sld1.valueChanged.connect(self.updateBC)
        self.sld2.valueChanged.connect(self.updateBC)

        self._488nmBtn.toggled.connect(self.radio488Clicked)
        self._561nmBtn.toggled.connect(self.radio561Clicked)
        self.CoolLEDBtn.toggled.connect(self.radioCoolLEDClicked)

        self.fig1.canvas.mpl_connect('scroll_event', self.wheelEvent)

    #-----------------------------------------------------------------------------------------------
    # FORMATTING THE WINDOW
    #-----------------------------------------------------------------------------------------------

    def HLine(self):

        toto = QtGui.QFrame()
        toto.setFrameShape(QtGui.QFrame.HLine)
        toto.setFrameShadow(QtGui.QFrame.Sunken)
        return toto

    #-----------------------------------------------------------------------------------------------
    # ACTION FUNCTIONS in THE POPUP MENU
    #-----------------------------------------------------------------------------------------------

    def changeName(self, item):
        idx = len(self.currentCells.cname) - 1
        self.currentCells.ix[idx, 'cname'] = str(item)
        self.currentCells = self.currentCells.reset_index(drop=True)

        self.updateCanvas1()
        self.setFocus()

    def actionFunct1p(self, item):
        self.changeName(item)

    def actionFunct1pp(self, item):
        self.changeName(item)

    def actionFunct1ppp(self, item):
        self.changeName(item)

    def actionFunct1ppa(self, item):
        self.changeName(item)

    def actionFunct4a(self, item):
        self.changeName(item)

    def actionFunct4aa(self, item):
        self.changeName(item)

    def actionFunct4aaa(self, item):
        self.changeName(item)

    def actionFunct4aap(self, item):
        self.changeName(item)

    def actionFunctb1(self, item):
        self.changeName(item)

    def actionFunctb4(self, item):
        self.changeName(item)

    def eventFilter(self, widget, event):
        # print( 'eventFilter', widget, event)
        if widget == self.canvas1 and isinstance(
                event,
                QtGui.QMouseEvent) and event.buttons() & QtCore.Qt.LeftButton:
            self.leftClicked(event.pos())
            return True
        if widget == self.canvas1 and isinstance(
                event,
                QtGui.QMouseEvent) and event.buttons() & QtCore.Qt.RightButton:
            self.rightClicked(event.pos())
            return True
        return False

    def leftClicked(self, QPos):
        refpos = np.array([
            int(QPos.x() / 600. * 512.),
            int(QPos.y() / 600. * 512.),
            self.sl.value()
        ])
        # print(refpos)

        # create an empty cell in the currentCells df: the only entries are tidx, xyzpos and cname
        newcell = create_single_cell_pos(refpos.astype(np.uint16),
                                         self.tp.value())
        self.currentCells = pd.concat([self.currentCells, newcell])

        self.currentCells = self.currentCells.reset_index(drop=True)

        self.updateCanvas1()

        parentPosition = self.canvas1.mapToGlobal(QtCore.QPoint(0, 0))
        # print('leftClicked', QPos, parentPosition)
        menuPosition = parentPosition + QPos

        self.cellNamesBox.move(menuPosition)
        self.cellNamesBox.show()
        self.setFocus()

    def rightClicked(self, QPos):
        refpos = np.array(
            [QPos.x() / 600. * 512.,
             QPos.y() / 600. * 512.,
             self.sl.value()])
        # print(refpos)

        if len(self.currentCells) == 0:
            self.setFocus()
            return

        # remove a cell (the closest to the cursor at the moment of right-click)
        idx = closer_cell(refpos.astype(np.uint16), self.currentCells)
        self.currentCells = self.currentCells.drop([idx])
        self.currentCells = self.currentCells.reset_index(drop=True)

        self.updateCanvas1()
        self.setFocus()

    #-----------------------------------------------------------------------------------------------
    # BUTTON FUNCTIONS
    #-----------------------------------------------------------------------------------------------

    def selectWorm(self):

        ### store the folders
        self.pathDial = QtGui.QFileDialog.getExistingDirectory(
            self, 'Select a folder',
            'X:/Simone/160129_MCHERRY_HLH2GFP_onHB101')  #'Y:\\Images')
        self.worm = self.pathDial.split("/")[-1].split('_')[0]
        self.path = os.path.dirname(self.pathDial)
        self.setWindowTitle('Label cells - ' + self.pathDial.split("/")[-2] +
                            ' - ' + self.pathDial.split("/")[-1][:3])

        ### give error message if there is no CoolLED movie in the selected folder
        flist = glob.glob(self.pathDial + '/*_movie.tif')
        if len(
                flist
        ) == 0:  #not os.path.isfile( os.path.join( self.pathDial, '*_movie.tif' ) ):
            QtGui.QMessageBox.about(
                self, 'Warning!',
                'There is no movie in this folder! Create a movie first!')
            return

        ### load parameters and times dataframes
        self.paramsDF = load_data_frame_pandas(self.path,
                                               self.worm + '_01params.pickle')
        self.timesDF = load_data_frame_pandas(self.path,
                                              self.worm + '_01times.pickle')
        self.gpDF = load_data_frame_pandas(self.path,
                                           self.worm + '_02gonadPos.pickle')

        # extract some info
        self.compression = self.paramsDF.compression
        self.hatchingtidx = int(self.paramsDF.tidxHatch)

        ### if the cellPos pickle file already exists, load it, otherwise create a blank one
        if os.path.isfile(
                os.path.join(self.path, self.worm + '_04cellPos.pickle')):
            self.cellPosDF = load_data_frame_pandas(
                self.path, self.worm + '_04cellPos.pickle')

        else:
            self.cellPosDF = create_cell_pos(self.timesDF, self.cellNames)

        # detect available channels
        self.channels = []
        chns = ['CoolLED', '488nm', '561nm']
        for c in chns:

            if os.path.isfile(os.path.join(self.pathDial, c + '_movie.tif')):

                self.channels.append(c)
        self.currentChannel = self.channels[0]

        ### detect size of the cropped images
        tp = np.min(self.gpDF.ix[pd.notnull(self.gpDF.X), 'tidx'])
        self.prevtp = tp - 1
        tRow = self.timesDF.ix[self.timesDF.tidxRel == tp].squeeze()
        fileName = os.path.join(self.pathDial,
                                tRow.fName + self.currentChannel + '.tif')
        firststack = load_stack(fileName)
        self.cropsize = firststack.shape[1]
        self.nslices = firststack.shape[0]

        ### load CoolLED movie
        if 'CoolLED' in self.channels:
            self.LEDmovie = load_stack(
                os.path.join(self.pathDial, 'CoolLED_movie.tif'))
        else:
            self.LEDmovie = load_stack(
                os.path.join(self.pathDial,
                             self.currentChannel + '_movie.tif'))
        self.initializeCanvas1()
        self.initializeCanvas2()

        ### extract current cells already labeled
        self.currentCells = extract_current_cell_pos(self.cellPosDF,
                                                     self.tp.value())

        ### update the text of the fileName
        self.fName.setText(self.timesDF.ix[self.timesDF.tidxRel == tp,
                                           'fName'].values[0])

        ### set the timepoint to the hatching time
        self.tp.setMinimum(np.min(self.timesDF.tidxRel))
        self.tp.setMaximum(np.max(self.timesDF.tidxRel))

        ### set the max slice number
        self.sl.setMaximum(self.nslices - 1)

        self.tp.setValue(tp)

        if self.currentChannel == 'CoolLED':
            self.CoolLEDBtn.setChecked(
                True)  # this uppdates the canvas1 once more
        elif self.currentChannel == '561nm':
            self._561nmBtn.setChecked(
                True)  # this uppdates the canvas1 once more
        elif self.currentChannel == '488nm':
            self._488nmBtn.setChecked(
                True)  # this uppdates the canvas1 once more

        # self.pathDial.show()
        self.setFocus()

    def loadNewStack(self):

        # print(self.fList['gfp'][self.tp.value()])
        tRow = self.timesDF.ix[self.timesDF.tidxRel ==
                               self.tp.value()].squeeze()

        ### update the text of the fileName
        self.fName.setText(
            self.timesDF.ix[self.timesDF.tidxRel == self.tp.value(),
                            'fName'].values[0])

        print('Loading... ', self.pathDial, tRow.fName)

        # calculate the max value of the previous stack
        try:
            prevmax = np.max([np.max(self.stacks[ch]) for ch in self.channels])
        # if it's the first time a stack is to be loaded (so if there is no previous stack), set it to zero
        except:
            prevmax = 0

        # load all the available stacks - this is the slowest part of the code!!!
        self.stacks = {}
        for ch in self.channels:
            fileName = os.path.join(self.pathDial, tRow.fName + ch + '.tif')
            if os.path.isfile(fileName):
                # print(MultiImage('X:\\Simone\\160129_MCHERRY_HLH2GFP_onHB101\\C02_analyzedImages\\Z003_488nm.tif'))
                # print(fileName, MultiImage( fileName ))
                # self.stacks[ch] = MultiImage( fileName )
                self.stacks[ch] = load_stack(fileName)
            # if there are no files for the timepoint, create a blank image
            else:
                self.stacks[ch] = prevmax * np.ones(
                    (self.nslices, self.cropsize, self.cropsize))

        # if the BC bound are different, the BCsliderMinMax will automatically update canvas1. Otherwise, manually update it!
        self.setBCslidersMinMax()

        self.updateCanvas1()
        self.updateCanvas2()

    def changeTp(self):

        # if it's the second time you are checking the same tp, don't do anything
        if self.checkNames:
            cellFine = self.checkConsistencyCellNames()
        else:
            return

        # before changing timepoint, print labeled cells and check if they are OK
        print('cells labeled:\n ', self.currentCells)

        ### extract current cells already labeled
        self.newCells = extract_current_cell_pos(self.cellPosDF,
                                                 self.tp.value())

        if cellFine:
            # if everything fine, load the new stack
            self.currentCells = self.newCells
            self.loadNewStack()
        else:
            # otherwise, go back to prev tp
            self.checkNames = False
            self.tp.setValue(self.prevtp)
            self.checkNames = True

        self.prevtp = self.tp.value()

    def saveData(self):

        if self.checkConsistencyCellNames():
            save_data_frame(self.cellPosDF, self.path,
                            self.worm + '_04cellPos.pickle')
        else:
            QtGui.QMessageBox.about(self, 'Warning!',
                                    'There is a mistake in the cell labels!')
        self.setFocus()

    def radio488Clicked(self, enabled):
        # print('radio 488 clicked')

        if enabled:
            if '488nm' in self.channels:
                self.currentChannel = '488nm'
                self.setFocus()
                self.updateCanvas1()
            else:
                if self.currentChannel == 'CoolLED':
                    self.CoolLEDBtn.setChecked(
                        True)  # this uppdates the canvas1 once more
                elif self.currentChannel == '561nm':
                    self._561nmBtn.setChecked(
                        True)  # this uppdates the canvas1 once more
                QtGui.QMessageBox.about(self, 'Warning', 'No 488nm channel!')

    def radio561Clicked(self, enabled):
        # print('radio 561 clicked')

        if enabled:
            if '561nm' in self.channels:
                self.currentChannel = '561nm'
                self.setFocus()
                self.updateCanvas1()
            else:
                if self.currentChannel == 'CoolLED':
                    self.CoolLEDBtn.setChecked(
                        True)  # this uppdates the canvas1 once more
                elif self.currentChannel == '488nm':
                    self._488nmBtn.setChecked(
                        True)  # this uppdates the canvas1 once more
                QtGui.QMessageBox.about(self, 'Warning', 'No 561nm channel!')

    def radioCoolLEDClicked(self, enabled):
        # print('radio LED clicked')

        if enabled:
            if 'CoolLED' in self.channels:
                self.currentChannel = 'CoolLED'
                self.setFocus()
                self.updateCanvas1()
            else:
                if self.currentChannel == '561nm':
                    self._561nmBtn.setChecked(
                        True)  # this uppdates the canvas1 once more
                elif self.currentChannel == '488nm':
                    self._488nmBtn.setChecked(
                        True)  # this uppdates the canvas1 once more
                QtGui.QMessageBox.about(self, 'Warning', 'No CoolLED channel!')

    def updateBC(self):
        # change brightness and contrast
        self.imgplot1.set_clim(self.sld1.value(), self.sld2.value())
        self.canvas1.draw()

    #-----------------------------------------------------------------------------------------------
    # DEFAULT FUNCTION FOR KEY AND MOUSE PRESS ON WINDOW
    #-----------------------------------------------------------------------------------------------

    def keyPressEvent(self, event):

        # print(event.key())

        # change timepoint
        if event.key() == QtCore.Qt.Key_Right:
            self.changeSpaceTime('time', +1)

        elif event.key() == QtCore.Qt.Key_Left:
            self.changeSpaceTime('time', -1)

        # change slice
        elif event.key() == QtCore.Qt.Key_Up:
            self.changeSpaceTime('space', +1)

        elif event.key() == QtCore.Qt.Key_Down:
            self.changeSpaceTime('space', -1)

        elif event.key() == QtCore.Qt.Key_PageDown:
            idx = self.channels.index(self.currentChannel)
            if self.channels[(idx + 1) % len(self.channels)] == 'CoolLED':
                self.CoolLEDBtn.setChecked(True)
            if self.channels[(idx + 1) % len(self.channels)] == '488nm':
                self._488nmBtn.setChecked(True)
            if self.channels[(idx + 1) % len(self.channels)] == '561nm':
                self._561nmBtn.setChecked(True)
        elif event.key() == QtCore.Qt.Key_PageUp:
            idx = self.channels.index(self.currentChannel)
            if self.channels[(idx - 1) % len(self.channels)] == 'CoolLED':
                self.CoolLEDBtn.setChecked(True)
            if self.channels[(idx - 1) % len(self.channels)] == '488nm':
                self._488nmBtn.setChecked(True)
            if self.channels[(idx - 1) % len(self.channels)] == '561nm':
                self._561nmBtn.setChecked(True)

        # key press on cropped image
        if self.canvas1.underMouse():
            self.onKeyPressOnCanvas1(event)

        self.setFocus()

    def wheelEvent(self, event):
        if self.canvas1.underMouse():
            step = event.step
        else:
            step = event.delta() / abs(event.delta())
        self.sl.setValue(self.sl.value() + step)

    #-----------------------------------------------------------------------------------------------
    # ADDITIONAL FUNCTIONS FOR KEY AND MOUSE PRESS ON CANVASES
    #-----------------------------------------------------------------------------------------------

    def onKeyPressOnCanvas1(self, event):

        motherCells = [QtCore.Qt.Key_1, QtCore.Qt.Key_4, QtCore.Qt.Key_B]
        daughterCells = [QtCore.Qt.Key_A, QtCore.Qt.Key_P]

        # find the position of the cursor relative to the image in pixel
        imgshape = self.stacks[self.currentChannel][self.sl.value()].shape
        canshape = self.canvas1.size()
        cf = imgshape[0] / canshape.width()
        refpos = self.canvas1.mapFromGlobal(QtGui.QCursor.pos())
        refpos = np.array([int(refpos.x() * cf), int(refpos.y() * cf)])
        refpos = np.append(refpos, self.sl.value())

        ### find the closest cell to the cursor
        idx = closer_cell(refpos.astype(np.uint16), self.currentCells)

        ### assign the name to the cell
        if any([event.key() == cn for cn in motherCells]):
            # if labeling bckg, add the 1 or 2 to the name
            if self.currentCells.ix[idx, 'cname'] == 'b_':
                self.currentCells.ix[idx, 'cname'] += QtGui.QKeySequence(
                    event.key()).toString().lower()
            else:
                # if not, rename the cell from scratch
                if event.key() == QtCore.Qt.Key_B:
                    self.currentCells.ix[idx, 'cname'] = QtGui.QKeySequence(
                        event.key()).toString().lower() + '_'
                else:
                    self.currentCells.ix[idx, 'cname'] = QtGui.QKeySequence(
                        event.key()).toString().lower() + '.'

        # add the anterior/posterior to the cell name
        elif any([event.key() == cp for cp in daughterCells]):
            # don't do it if labeling background (bckg doesn't have a/p!)
            if self.currentCells.ix[idx, 'cname'] == 'b_':
                return
            else:
                self.currentCells.ix[idx, 'cname'] += QtGui.QKeySequence(
                    event.key()).toString().lower()

        # remove the last entry of the name with backspace
        elif event.key() == QtCore.Qt.Key_Backspace:
            self.currentCells.ix[idx,
                                 'cname'] = self.currentCells.ix[idx,
                                                                 'cname'][:-1]

        if (event.key() != QtCore.Qt.Key_Left) and (
                event.key() != QtCore.Qt.Key_Right) and (
                    event.key() != QtCore.Qt.Key_Up) and (event.key() !=
                                                          QtCore.Qt.Key_Down):
            self.updateCanvas1()
        self.setFocus()

    #-----------------------------------------------------------------------------------------------
    # UTILS
    #-----------------------------------------------------------------------------------------------

    def setBCslidersMinMax(self):
        self.sld1.setMaximum(
            np.max([np.max(self.stacks[ch]) for ch in self.channels]))
        self.sld1.setMinimum(0)
        self.sld2.setMaximum(
            np.max([np.max(self.stacks[ch]) for ch in self.channels]))
        self.sld2.setMinimum(0)

    def initializeCanvas1(self):
        # print('initializing canvas1')

        self.fig1.clf()
        self.fig1.subplots_adjust(left=0., right=1., top=1., bottom=0.)
        self.ax1 = self.fig1.add_subplot(111)
        self.canvas1.draw()

        # plot the first blank image with the right size
        self.ax1.cla()
        self.imgplot1 = self.ax1.imshow(np.zeros(
            (self.cropsize, self.cropsize)),
                                        cmap='gray')

        # remove the white borders
        self.ax1.autoscale(False)
        self.ax1.axis('Off')
        self.fig1.subplots_adjust(left=0., right=1., top=1., bottom=0.)

        # plot cell pos and name
        self.text1 = []
        self.points1 = []

        # redraw the canvas
        self.canvas1.draw()
        self.setFocus()

    def updateCanvas1(self):
        # print('updating canvas1')

        # plot the image
        self.imgplot1.set_data(
            self.stacks[self.currentChannel][self.sl.value()])

        # change brightness and contrast
        self.imgplot1.set_clim(self.sld1.value(), self.sld2.value())

        # clear cell text and points
        # print(self.text1,self.points1)
        for text in self.text1:
            text.remove()
        self.text1 = []

        for points in self.points1:
            self.ax1.lines.remove(points)
        self.points1 = []

        # draw cell text and point
        for idx, cell in self.currentCells.iterrows():

            if cell.Z == self.sl.value():

                self.text1.append(
                    self.ax1.text(cell.X,
                                  cell.Y + 18,
                                  cell.cname,
                                  color='orange',
                                  fontsize=10,
                                  alpha=.7,
                                  rotation=0))
                self.points1.append(
                    self.ax1.plot(cell.X,
                                  cell.Y,
                                  'o',
                                  color='orange',
                                  alpha=.7,
                                  mew=0,
                                  ms=6)[0])

        # redraw the canvas
        self.canvas1.draw()
        self.setFocus()

    def initializeCanvas2(self):
        # print('initializing canvas2')

        # plot the image
        self.ax2.cla()
        self.imgplot2 = self.ax2.imshow(np.zeros((512, 512)), cmap='gray')
        self.imgplot2.set_clim(np.min(self.LEDmovie), np.max(self.LEDmovie))

        # remove the white borders and plot outline and spline
        self.ax2.autoscale(False)
        self.ax2.axis('Off')
        self.fig2.subplots_adjust(left=0., right=1., top=1., bottom=0.)

        # print gonad position
        gonadPos = [np.nan, np.nan]
        self.points2, = self.ax2.plot(gonadPos[0],
                                      gonadPos[1],
                                      'o',
                                      color='blue',
                                      ms=10,
                                      mew=0,
                                      alpha=.5,
                                      lw=0)

        # print time
        self.text2 = self.ax2.text(5, 25, '--.--', color='white')

        # redraw the canvas
        self.canvas2.draw()
        self.setFocus()

    def updateCanvas2(self):
        # print('updating canvas2')
        # plot the image
        self.imgplot2.set_data(self.LEDmovie[self.tp.value() +
                                             self.hatchingtidx])

        # print gonad position
        gonadPos = extract_pos(self.gpDF.ix[
            self.gpDF.tidx == self.tp.value()].squeeze()) / self.compression
        if len(gonadPos.shape) > 0:
            self.points2.set_xdata(gonadPos[0])
            self.points2.set_ydata(gonadPos[1])
            plt.draw()

        # print time
        # print time
        ### find ecdysis timepoint
        ecd = np.loadtxt(open(os.path.join(self.path, 'skin.txt'), 'rb'))
        # load ecdysis data
        index = np.where(ecd[:, 0] == float(self.worm[1:]))
        mintp = np.min([i for i in ecd[index, 1:6][0][0] if i >= 0])
        lethtidx = ecd[index, 1:6][0][0]
        lethtidx = lethtidx[lethtidx >= 0]
        tpL2 = self.timesDF.ix[self.timesDF.tidxRel == (lethtidx[1] - mintp),
                               'timesRel'].values[0]
        # print(self.timesDF.ix[ self.timesDF.tidxRel == self.tp.value(), 'timesRel' ])
        self.text2.set_text(
            '%.2f' % (self.timesDF.ix[self.timesDF.tidxRel == self.tp.value(),
                                      'timesRel'].values[0] - tpL2))

        # redraw the canvas
        self.canvas2.draw()
        self.setFocus()

    def checkConsistencyCellNames(self):
        ### check consistency of cell names
        tp = self.prevtp

        # if no cells are labeled (currentCells df is empty), remove all labeled cells in the cellPosDF and return
        if len(self.currentCells) == 0:
            newCellPosDF = update_cell_pos_DF(self.currentCells,
                                              self.cellPosDF, tp)
            correctCellNames = True

        if len(self.currentCells) > 0:

            correctCellNames = check_cell_names(self.currentCells,
                                                self.cellNames)

            # if cells are not properly labeled, give a Warning
            if not correctCellNames:
                QtGui.QMessageBox.about(
                    self, 'Warning!', 'There is a mistake in the cell labels!')

            # else, update final cellPosDF and return OK
            else:
                newCellPosDF = update_cell_pos_DF(self.currentCells,
                                                  self.cellPosDF, tp)
                self.cellPosDF = newCellPosDF

        return correctCellNames

    def changeSpaceTime(self, whatToChange, increment):

        if whatToChange == 'time':
            # if they are OK (and not going to negative times), change timepoint
            self.tp.setValue(self.tp.value() + increment)

        if whatToChange == 'space':
            self.sl.setValue(self.sl.value() + increment)
예제 #2
0
class XYView(View):
  AUTOFIT_MARGIN = 0.03  # 3%
  
  # See http://matplotlib.org/api/markers_api.html:
  CURVE_MARKERS = [ "o" ,#  circle
                    "*",  # star
                    "+",  # plus
                    "x",  # x
                    "s",  # square
                    "p",  # pentagon
                    "h",  # hexagon1
                    "8",  # octagon
                    "D",  # diamond
                    "^",  # triangle_up
                    "<",  # triangle_left
                    ">",  # triangle_right
                    "1",  # tri_down
                    "2",  # tri_up
                    "3",  # tri_left
                    "4",  # tri_right
                    "v",  # triangle_down
                    "H",  # hexagon2
                    "d",  # thin diamond
                    "",   # NO MARKER
                   ]
  
  _DEFAULT_LEGEND_STATE = False   # for test purposes mainly - initial status of the legend
  
  def __init__(self, controller):
    View.__init__(self, controller)
    self._eventHandler = EventHandler()
    
    self._curveViews = {}    # key: curve (model) ID, value: CurveView
    self._salomeViewID = None
    self._mplFigure = None
    self._mplAxes = None
    self._mplCanvas = None
    self._plotWidget = None
    self._sgPyQt = self._controller._sgPyQt
    self._toolbar = None
    self._mplNavigationActions = {}
    self._toobarMPL = None
    self._grid = None
    self._currCrv = None   # current curve selected in the view
    
    self._legend = None
    self._legendLoc = "right"  # "right" or "bottom"
    
    self._fitArea = False
    self._zoomPan = False
    self._dragOnDrop = False
    self._move = False
    
    self._patch = None
    self._xdata = None
    self._ydata = None
    self._defaultLineStyle = None
    self._last_point = None
    self._lastMarkerID = -1
    self._blockLogSignal = False
    
    self._axisXSciNotation = False
    self._axisYSciNotation = False
    self._prevTitle = None
    
  def __repaintOK(self):
    """ To be called inside XYView each time a low-level expansive matplotlib methods is to be invoked.
    @return False if painting is currently locked, in which case it will also register the current XYView 
    as needing a refresh when unlocked
    """
    ret = self._controller._plotManager.isRepaintLocked()
    if ret:
      self._controller._plotManager.registerRepaint(self._model)
    return (not ret)
    
  def appendCurve(self, curveID):
    newC = CurveView(self._controller, self)
    newC.setModel(self._model._curves[curveID])
    newC.setMPLAxes(self._mplAxes) 
    newC.draw()
    newC.setMarker(self.getMarker(go_next=True))
    self._curveViews[curveID] = newC 
    
  def removeCurve(self, curveID):
    v = self._curveViews.pop(curveID)
    v.erase()
    if self._currCrv is not None and self._currCrv.getID() == curveID:
      self._currCrv = None
  
  def cleanBeforeClose(self):
    """ Clean some items to avoid accumulating stuff in memory """
    self._mplFigure.clear()
    plt.close(self._mplFigure)
    self._plotWidget.clearAll()
    # For memory debugging only:
    import gc
    gc.collect()
  
  def repaint(self):
    if self.__repaintOK():
      Logger.Debug("XYView::draw")
      self._mplCanvas.draw()

  def onXLabelChange(self):
    if self.__repaintOK():
      self._mplAxes.set_xlabel(self._model._xlabel)
      self.repaint()
    
  def onYLabelChange(self):
    if self.__repaintOK():
      self._mplAxes.set_ylabel(self._model._ylabel)
      self.repaint()
  
  def onTitleChange(self):
    if self.__repaintOK():
      self._mplAxes.set_title(self._model._title)
      self.updateViewTitle()
      self.repaint()
  
  def onCurveTitleChange(self):
    # Updating the legend should suffice
    self.showHideLegend()
  
  def onClearAll(self):
    """ Just does an update with a reset of the marker cycle. """
    if self.__repaintOK():
      self._lastMarkerID = -1
      self.update()
  
  def onPick(self, event):
    """ MPL callback when picking
    """
    if event.mouseevent.button == 1:
      selected_id = -1
      a = event.artist
      for crv_id, cv in self._curveViews.items():
        if cv._mplLines[0] is a:
          selected_id = crv_id
      # Use the plotmanager so that other plot sets get their current reset:
      self._controller._plotManager.setCurrentCurve(selected_id)
  
  def createAndAddLocalAction(self, icon_file, short_name):
    return self._toolbar.addAction(self._sgPyQt.loadIcon("CURVEPLOT", icon_file), short_name)
    
  def createPlotWidget(self):
    self._mplFigure = Figure((8.0,5.0), dpi=100)
    self._mplCanvas = FigureCanvasQTAgg(self._mplFigure)
    self._mplCanvas.installEventFilter(self._eventHandler)
    self._mplCanvas.mpl_connect('pick_event', self.onPick)
    self._mplAxes = self._mplFigure.add_subplot(1, 1, 1)
    self._plotWidget = PlotWidget()
    self._toobarMPL = NavigationToolbar2QT(self._mplCanvas, None) 
    for act in self._toobarMPL.actions():
      actionName = str(act.text()).strip()
      self._mplNavigationActions[actionName] = act
    self._plotWidget.setCentralWidget(self._mplCanvas)
    self._toolbar = self._plotWidget.toolBar
    self.populateToolbar()
     
    self._popupMenu = QtGui.QMenu()
    self._popupMenu.addAction(self._actionLegend)
    
    # Connect evenement for the graphic scene
    self._mplCanvas.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
    self._mplCanvas.customContextMenuRequested.connect(self.onContextMenu) 
    self._mplCanvas.mpl_connect('scroll_event', self.onScroll)
    self._mplCanvas.mpl_connect('button_press_event', self.onMousePress)
  
  def populateToolbar(self):
    # Action to dump view in a file
    a = self.createAndAddLocalAction("dump_view.png", trQ("DUMP_VIEW_TXT"))
    a.triggered.connect(self.dumpView)
    self._toolbar.addSeparator()
    # Actions to manipulate the scene
    a = self.createAndAddLocalAction("fit_all.png", trQ("FIT_ALL_TXT"))
    a.triggered.connect(self.autoFit)
    #    Zoom and pan are mutually exclusive but can be both de-activated: 
    self._zoomAction = self.createAndAddLocalAction("fit_area.png", trQ("FIT_AREA_TXT"))
    self._zoomAction.triggered.connect(self.zoomArea)
    self._zoomAction.setCheckable(True)
    self._panAction = self.createAndAddLocalAction("zoom_pan.png", trQ("ZOOM_PAN_TXT"))
    self._panAction.triggered.connect(self.pan)
    self._panAction.setCheckable(True)
    self._toolbar.addSeparator()
    # Actions to change the representation of curves
    self._curveActionGroup = QtGui.QActionGroup(self._plotWidget)
    self._pointsAction = self.createAndAddLocalAction("draw_points.png", trQ("DRAW_POINTS_TXT"))
    self._pointsAction.setCheckable(True)
    self._linesAction = self.createAndAddLocalAction("draw_lines.png", trQ("DRAW_LINES_TXT"))
    self._linesAction.setCheckable(True)
    self._curveActionGroup.addAction(self._pointsAction)
    self._curveActionGroup.addAction(self._linesAction)
    self._linesAction.setChecked(True)
    self._curveActionGroup.triggered.connect(self.changeModeCurve)
    self._curveActionGroup.setExclusive(True)
    self._toolbar.addSeparator()
    # Actions to draw horizontal curves as linear or logarithmic
    self._horActionGroup = QtGui.QActionGroup(self._plotWidget)
    self._horLinearAction = self.createAndAddLocalAction("hor_linear.png", trQ("HOR_LINEAR_TXT"))
    self._horLinearAction.setCheckable(True)
    self._horLogarithmicAction = self.createAndAddLocalAction("hor_logarithmic.png", trQ("HOR_LOGARITHMIC_TXT"))
    self._horLogarithmicAction.setCheckable(True)
    self._horActionGroup.addAction(self._horLinearAction)
    self._horActionGroup.addAction(self._horLogarithmicAction)
    self._horLinearAction.setChecked(True)
    self._horActionGroup.triggered.connect(self.onViewHorizontalMode)
    self._toolbar.addSeparator()
    # Actions to draw vertical curves as linear or logarithmic
    self._verActionGroup = QtGui.QActionGroup(self._plotWidget)
    self._verLinearAction = self.createAndAddLocalAction("ver_linear.png", trQ("VER_LINEAR_TXT"))
    self._verLinearAction.setCheckable(True)
    self._verLogarithmicAction = self.createAndAddLocalAction("ver_logarithmic.png", trQ("VER_LOGARITHMIC_TXT"))
    self._verLogarithmicAction.setCheckable(True)
    self._verActionGroup.addAction(self._verLinearAction)
    self._verActionGroup.addAction(self._verLogarithmicAction)
    self._verLinearAction.setChecked(True)
    self._verActionGroup.triggered.connect(self.onViewVerticalMode)
    self._verActionGroup.setExclusive(True)
    self._toolbar.addSeparator()
    # Action to show or hide the legend 
    self._actionLegend = self.createAndAddLocalAction("legend.png", trQ("SHOW_LEGEND_TXT"))
    self._actionLegend.setCheckable(True)
    self._actionLegend.triggered.connect(self.showHideLegend)
    if self._DEFAULT_LEGEND_STATE:
      self._actionLegend.setChecked(True)
    self._toolbar.addSeparator()
    # Action to set the preferences
    a = self.createAndAddLocalAction("settings.png", trQ("SETTINGS_TXT"))
    a.triggered.connect(self.onSettings)
    pass
    
  def dumpView(self):
    # Choice of the view backup file
    filters = []
    for form in ["IMAGES_FILES", "PDF_FILES", "POSTSCRIPT_FILES", "ENCAPSULATED_POSTSCRIPT_FILES"]:
      filters.append(trQ(form))
    fileName = self._sgPyQt.getFileName(self._sgPyQt.getDesktop(),
                                        "",
                                        filters,
                                        trQ("DUMP_VIEW_FILE"),
                                        False )
    if not fileName.isEmpty():
      name = str(fileName)
      self._mplAxes.figure.savefig(name)
    pass
    
  def autoFit(self, check=True, repaint=True):
    if self.__repaintOK():
      self._mplAxes.relim()
      xm, xM = self._mplAxes.xaxis.get_data_interval()
      ym, yM = self._mplAxes.yaxis.get_data_interval()
      i = yM-ym
      self._mplAxes.axis([xm, xM, ym-i*self.AUTOFIT_MARGIN, yM+i*self.AUTOFIT_MARGIN])
      if repaint:
        self.repaint()
  
  def zoomArea(self):
    if self._panAction.isChecked() and self._zoomAction.isChecked():
      self._panAction.setChecked(False)
    # Trigger underlying matplotlib action:
    self._mplNavigationActions["Zoom"].trigger()
  
  def pan(self):
    if self._panAction.isChecked() and self._zoomAction.isChecked():
      self._zoomAction.setChecked(False)
    # Trigger underlying matplotlib action:
    self._mplNavigationActions["Pan"].trigger()

  def getMarker(self, go_next=False):
    if go_next:
      self._lastMarkerID = (self._lastMarkerID+1) % len(self.CURVE_MARKERS)
    return self.CURVE_MARKERS[self._lastMarkerID]

  def changeModeCurve(self, repaint=True):
    if not self.__repaintOK():
      return
    action = self._curveActionGroup.checkedAction()
    if action is self._pointsAction :
      for crv_view in self._curveViews.values():
        crv_view.setLineStyle("None")
    elif action is self._linesAction :
      for crv_view in self._curveViews.values():
        crv_view.setLineStyle("-")
    else :
      raise NotImplementedError
    if repaint:
      self.repaint()
  
  def setXLog(self, log, repaint=True):
    if not self.__repaintOK():
      return
    self._blockLogSignal = True
    if log:
      self._mplAxes.set_xscale('log')
      self._horLogarithmicAction.setChecked(True)
    else:
      self._mplAxes.set_xscale('linear')
      self._horLinearAction.setChecked(True)
    if repaint:
      self.autoFit()
      self.repaint()
    self._blockLogSignal = False

  def setYLog(self, log, repaint=True):
    if not self.__repaintOK():
      return
    self._blockLogSignal = True
    if log:
      self._mplAxes.set_yscale('log')
      self._verLogarithmicAction.setChecked(True)
    else:
      self._mplAxes.set_yscale('linear')
      self._verLinearAction.setChecked(True)
    if repaint:
      self.autoFit()
      self.repaint()
    self._blockLogSignal = False
    
  def setXSciNotation(self, sciNotation, repaint=True):
    self._axisXSciNotation = sciNotation
    self.changeFormatAxis()
    if repaint:
      self.repaint()
   
  def setYSciNotation(self, sciNotation, repaint=True):
    self._axisYSciNotation = sciNotation
    self.changeFormatAxis()
    if repaint:
      self.repaint()
    
  def onViewHorizontalMode(self, checked=True, repaint=True):
    if self._blockLogSignal:
      return
    action = self._horActionGroup.checkedAction()  
    if action is self._horLinearAction:
      self.setXLog(False, repaint)
    elif action is self._horLogarithmicAction:
      self.setXLog(True, repaint)
    else:
      raise NotImplementedError
  
  def onViewVerticalMode(self, checked=True, repaint=True):
    if self._blockLogSignal:
      return
    action = self._verActionGroup.checkedAction()  
    if action is self._verLinearAction:
      self.setYLog(False, repaint)
    elif action is self._verLogarithmicAction:
      self.setYLog(True, repaint)
    else:
      raise NotImplementedError
    if repaint:
      self.repaint()
  
  def __adjustFigureMargins(self, withLegend):
    """ Adjust figure margins to make room for the legend """
    if withLegend:
      leg = self._legend
      bbox = leg.get_window_extent()
      # In axes coordinates: 
      bbox2 = bbox.transformed(leg.figure.transFigure.inverted())
      if self._legendLoc == "right":
        self._mplFigure.subplots_adjust(right=1.0-(bbox2.width+0.02))
      elif self._legendLoc == "bottom":
        self._mplFigure.subplots_adjust(bottom=bbox2.height+0.1)
    else:
      # Reset to default (rc) values
      self._mplFigure.subplots_adjust(bottom=0.1, right=0.9)
  
  def setLegendVisible(self, visible, repaint=True):
    if visible and not self._actionLegend.isChecked():
      self._actionLegend.setChecked(True)
      self.showHideLegend(repaint=repaint)
    if not visible and self._actionLegend.isChecked():
      self._actionLegend.setChecked(False)
      self.showHideLegend(repaint=repaint)
  
  def showHideLegend(self, actionChecked=None, repaint=True):
    if not self.__repaintOK():  # Show/hide legend is extremely costly
      return
    
    show = self._actionLegend.isChecked()
    nCurves = len(self._curveViews)
    if nCurves > 10: fontSize = 'x-small'
    else:            fontSize = None
    
    if nCurves == 0:
      # Remove legend 
      leg = self._mplAxes.legend()
      if leg is not None: leg.remove()
    if show and nCurves > 0:
      # Recreate legend from scratch
      if self._legend is not None:
        self._legend = None
        self._mplAxes._legend = None
      if self._legendLoc == "bottom":
        self._legend = self._mplAxes.legend(loc="upper left", bbox_to_anchor=(0.0, -0.05, 1.0, -0.05), 
                                            borderaxespad=0.0, mode="expand", fancybox=True, 
                                            shadow=True, ncol=3, prop={'size':fontSize, 'style': 'italic'})
      elif self._legendLoc == "right":
        self._legend = self._mplAxes.legend(loc="upper left", bbox_to_anchor=(1.02,1.0), borderaxespad=0.0,
                                            ncol=1, fancybox=True, shadow=True, prop={'size':fontSize, 'style': 'italic'})
      else:
        raise Exception("Invalid legend placement! Must be 'bottom' or 'right'")
      # Canvas must be drawn so we can adjust the figure placement:
      self._mplCanvas.draw()
      self.__adjustFigureMargins(withLegend=True)
    else:
      if self._legend is None:
        # Nothing to do
        return
      else:
        self._legend.set_visible(False)
        self._legend = None
        self._mplAxes._legend = None
        self._mplCanvas.draw()
        self.__adjustFigureMargins(withLegend=False)
    
    curr_crv = self._model._currentCurve
    if curr_crv is None: curr_title = None
    else:                curr_title = curr_crv.getTitle()
    if self._legend is not None:
      for label in self._legend.get_texts() :
        text = label.get_text()
        if (text == curr_title):
          label.set_backgroundcolor('0.85')
        else :
          label.set_backgroundcolor('white')
        
    if repaint:
      self.repaint()
   
  def onSettings(self, trigger=False, dlg_test=None):
    dlg = dlg_test or PlotSettings()
    dlg.titleEdit.setText(self._mplAxes.get_title())
    dlg.axisXTitleEdit.setText(self._mplAxes.get_xlabel())
    dlg.axisYTitleEdit.setText(self._mplAxes.get_ylabel())
    dlg.gridCheckBox.setChecked(self._mplAxes.xaxis._gridOnMajor)  # could not find a relevant API to check this
    dlg.axisXSciCheckBox.setChecked(self._axisXSciNotation)
    dlg.axisYSciCheckBox.setChecked(self._axisYSciNotation)
    xmin, xmax = self._mplAxes.get_xlim()
    ymin, ymax = self._mplAxes.get_ylim()
    xminText = "%g" %xmin
    xmaxText = "%g" %xmax
    yminText = "%g" %ymin
    ymaxText = "%g" %ymax
    dlg.axisXMinEdit.setText(xminText)
    dlg.axisXMaxEdit.setText(xmaxText)
    dlg.axisYMinEdit.setText(yminText)
    dlg.axisYMaxEdit.setText(ymaxText)
    # List of markers
    dlg.markerCurve.clear()
    for marker in self.CURVE_MARKERS :
      dlg.markerCurve.addItem(marker)
    curr_crv = self._model.getCurrentCurve()
    if not curr_crv is None:
      dlg.colorCurve.setEnabled(True)
      dlg.markerCurve.setEnabled(True)
      name = curr_crv.getTitle()
      dlg.nameCurve.setText(name)
      view = self._curveViews[curr_crv.getID()] 
      marker = view.getMarker()
      color = view.getColor()
      index = dlg.markerCurve.findText(marker)
      dlg.markerCurve.setCurrentIndex(index)
      rgb = colors.colorConverter.to_rgb(color)
      dlg.setRGB(rgb[0],rgb[1],rgb[2])
    else :
      dlg.colorCurve.setEnabled(False)
      dlg.markerCurve.setEnabled(False)
      dlg.nameCurve.setText("")
      view = None
    if self._legend is None:
      dlg.showLegendCheckBox.setChecked(False)
      dlg.legendPositionComboBox.setEnabled(False)
    else :
      if self._legend.get_visible():
        dlg.showLegendCheckBox.setChecked(True)
        dlg.legendPositionComboBox.setEnabled(True)
        if self._legendLoc == "bottom":
          dlg.legendPositionComboBox.setCurrentIndex(0)
        elif self._legendLoc == "right" :
          dlg.legendPositionComboBox.setCurrentIndex(1)
      else :
        dlg.showLegendCheckBox.setChecked(False)
        dlg.legendPositionComboBox.setEnabled(False)    
             
    if dlg.exec_():
      # Title
      self._model.setTitle(dlg.titleEdit.text())
      # Axis
      self._model.setXLabel(dlg.axisXTitleEdit.text())
      self._model.setYLabel(dlg.axisYTitleEdit.text())
      # Grid
      if dlg.gridCheckBox.isChecked() :
        self._mplAxes.grid(True)
      else :
        self._mplAxes.grid(False)
      # Legend
      if  dlg.showLegendCheckBox.isChecked():
        self._actionLegend.setChecked(True)
        if dlg.legendPositionComboBox.currentIndex() == 0 :
          self._legendLoc = "bottom"
        elif dlg.legendPositionComboBox.currentIndex() == 1 :
          self._legendLoc = "right"
      else :
        self._actionLegend.setChecked(False)
      xminText = dlg.axisXMinEdit.text()
      xmaxText = dlg.axisXMaxEdit.text()
      yminText = dlg.axisYMinEdit.text()
      ymaxText = dlg.axisYMaxEdit.text()
      self._mplAxes.axis([float(xminText), float(xmaxText), float(yminText), float(ymaxText)] )
      self._axisXSciNotation = dlg.axisXSciCheckBox.isChecked()
      self._axisYSciNotation = dlg.axisYSciCheckBox.isChecked()
      self.changeFormatAxis()
      # Color and marker of the curve
      if view:
        view.setColor(dlg.getRGB()) 
        view.setMarker(self.CURVE_MARKERS[dlg.markerCurve.currentIndex()])
      self.showHideLegend(repaint=True)
      self._mplCanvas.draw()
    pass
    
  def updateViewTitle(self):
    s = ""
    if self._model._title != "":
      s = " - %s" % self._model._title
    title = "CurvePlot (%d)%s" % (self._model.getID(), s)
    self._sgPyQt.setViewTitle(self._salomeViewID, title)
    
  def onCurrentPlotSetChange(self):
    """ Avoid a unnecessary call to update() when just switching current plot set! """ 
    pass
  
  def onCurrentCurveChange(self):
    curr_crv2 = self._model.getCurrentCurve()
    if curr_crv2 != self._currCrv:
      if self._currCrv is not None:
        view = self._curveViews[self._currCrv.getID()]
        view.toggleHighlight(False)
      if not curr_crv2 is None:
        view = self._curveViews[curr_crv2.getID()] 
        view.toggleHighlight(True)
      self._currCrv = curr_crv2
      self.showHideLegend(repaint=False) # redo legend
      self.repaint() 
      
  def changeFormatAxis(self) :
    if not self.__repaintOK():
      return
    
    # don't try to switch to sci notation if we are not using the 
    # matplotlib.ticker.ScalarFormatter (i.e. if in Log for ex.)
    if self._horLinearAction.isChecked():
      if self._axisXSciNotation :
        self._mplAxes.ticklabel_format(style='sci',scilimits=(0,0), axis='x')
      else :
        self._mplAxes.ticklabel_format(style='plain',axis='x')
    if self._verLinearAction.isChecked():    
      if self._axisYSciNotation :
        self._mplAxes.ticklabel_format(style='sci',scilimits=(0,0), axis='y')
      else :
        self._mplAxes.ticklabel_format(style='plain',axis='y')
    
  def update(self):
    if self._salomeViewID is None:
      self.createPlotWidget()
      self._salomeViewID = self._sgPyQt.createView("CurvePlot", self._plotWidget)
      Logger.Debug("Creating SALOME view ID=%d" % self._salomeViewID)
      self._sgPyQt.setViewVisible(self._salomeViewID, True)
    
    self.updateViewTitle()
    
    # Check list of curve views:
    set_mod = set(self._model._curves.keys())
    set_view = set(self._curveViews.keys())
    
    # Deleted/Added curves:
    dels = set_view - set_mod
    added = set_mod - set_view
    
    for d in dels:
      self.removeCurve(d)

    if not len(self._curveViews):
      # Reset color cycle 
      self._mplAxes.set_color_cycle(None)

    for a in added:
      self.appendCurve(a)

    # Axes labels and title
    self._mplAxes.set_xlabel(self._model._xlabel)
    self._mplAxes.set_ylabel(self._model._ylabel)
    self._mplAxes.set_title(self._model._title)

    self.onViewHorizontalMode(repaint=False)
    self.onViewVerticalMode(repaint=False)
    self.changeModeCurve(repaint=False)
    self.showHideLegend(repaint=False)   # The canvas is repainted anyway (needed to get legend bounding box)
    self.changeFormatAxis()

    # Redo auto-fit
    self.autoFit(repaint=False)
    self.repaint()
  
  def onDataChange(self):
    # the rest is done in the CurveView:
    self.autoFit(repaint=True)
    
  def onMousePress(self, event):
    if event.button == 3 :
      if self._panAction.isChecked():
        self._panAction.setChecked(False)
      if self._zoomAction.isChecked():
        self._zoomAction.setChecked(False)
    
  def onContextMenu(self, position):
    pos = self._mplCanvas.mapToGlobal(QtCore.QPoint(position.x(),position.y()))
    self._popupMenu.exec_(pos)
    
  def onScroll(self, event):
    # Event location (x and y)
    xdata = event.xdata
    ydata = event.ydata
    
    cur_xlim = self._mplAxes.get_xlim()
    cur_ylim = self._mplAxes.get_ylim()
    
    base_scale = 2.
    if event.button == 'down':
      # deal with zoom in
      scale_factor = 1 / base_scale
    elif event.button == 'up':
      # deal with zoom out
      scale_factor = base_scale
    else:
      # deal with something that should never happen
      scale_factor = 1
    
    new_width = (cur_xlim[1] - cur_xlim[0]) * scale_factor
    new_height = (cur_ylim[1] - cur_ylim[0]) * scale_factor

    relx = (cur_xlim[1] - xdata)/(cur_xlim[1] - cur_xlim[0])
    rely = (cur_ylim[1] - ydata)/(cur_ylim[1] - cur_ylim[0])

    self._mplAxes.set_xlim([xdata - new_width * (1-relx), xdata + new_width * (relx)])
    self._mplAxes.set_ylim([ydata - new_height * (1-rely), ydata + new_height * (rely)])
    
    self.repaint()
    pass
    
  def onPressEvent(self, event):
    if event.button == 3 :
      #self._mplCanvas.emit(QtCore.SIGNAL("button_release_event()"))
      canvasSize = event.canvas.geometry()
      point = event.canvas.mapToGlobal(QtCore.QPoint(event.x,canvasSize.height()-event.y))
      self._popupMenu.exec_(point)
    else :
      print "Press event on the other button"
    #if event.button == 3 :
    #  canvasSize = event.canvas.geometry()
    #  point = event.canvas.mapToGlobal(QtCore.QPoint(event.x,canvasSize.height()-event.y))
    #  self._popupMenu.move(point)
    #  self._popupMenu.show()
   
  def onMotionEvent(self, event):
    print "OnMotionEvent ",event.button
    #if event.button == 3 :
    #  event.button = None
    #  return True
   
  def onReleaseEvent(self, event):
    print "OnReleaseEvent ",event.button
예제 #3
0
파일: XYView.py 프로젝트: Sunqia/salome-gui
class XYView(View):
    AUTOFIT_MARGIN = 0.03  # 3%

    # See http://matplotlib.org/api/markers_api.html:
    CURVE_MARKERS = [
        "o",  #  circle
        "*",  # star
        "+",  # plus
        "x",  # x
        "s",  # square
        "p",  # pentagon
        "h",  # hexagon1
        "8",  # octagon
        "D",  # diamond
        "^",  # triangle_up
        "<",  # triangle_left
        ">",  # triangle_right
        "1",  # tri_down
        "2",  # tri_up
        "3",  # tri_left
        "4",  # tri_right
        "v",  # triangle_down
        "H",  # hexagon2
        "d",  # thin diamond
        "",  # NO MARKER
    ]

    _DEFAULT_LEGEND_STATE = False  # for test purposes mainly - initial status of the legend

    def __init__(self, controller):
        View.__init__(self, controller)
        self._eventHandler = EventHandler()

        self._curveViews = {}  # key: curve (model) ID, value: CurveView
        self._salomeViewID = None
        self._mplFigure = None
        self._mplAxes = None
        self._mplCanvas = None
        self._plotWidget = None
        self._sgPyQt = self._controller._sgPyQt
        self._toolbar = None
        self._mplNavigationActions = {}
        self._toobarMPL = None
        self._grid = None
        self._currCrv = None  # current curve selected in the view

        self._legend = None
        self._legendLoc = "right"  # "right" or "bottom"

        self._fitArea = False
        self._zoomPan = False
        self._dragOnDrop = False
        self._move = False

        self._patch = None
        self._xdata = None
        self._ydata = None
        self._defaultLineStyle = None
        self._last_point = None
        self._lastMarkerID = -1
        self._blockLogSignal = False

        self._axisXSciNotation = False
        self._axisYSciNotation = False
        self._prevTitle = None

    def __repaintOK(self):
        """ To be called inside XYView each time a low-level expansive matplotlib methods is to be invoked.
    @return False if painting is currently locked, in which case it will also register the current XYView 
    as needing a refresh when unlocked
    """
        ret = self._controller._plotManager.isRepaintLocked()
        if ret:
            self._controller._plotManager.registerRepaint(self._model)
        return (not ret)

    def appendCurve(self, curveID):
        newC = CurveView(self._controller, self)
        newC.setModel(self._model._curves[curveID])
        newC.setMPLAxes(self._mplAxes)
        newC.draw()
        newC.setMarker(self.getMarker(go_next=True))
        self._curveViews[curveID] = newC

    def removeCurve(self, curveID):
        v = self._curveViews.pop(curveID)
        v.erase()
        if self._currCrv is not None and self._currCrv.getID() == curveID:
            self._currCrv = None

    def cleanBeforeClose(self):
        """ Clean some items to avoid accumulating stuff in memory """
        self._mplFigure.clear()
        plt.close(self._mplFigure)
        self._plotWidget.clearAll()
        # For memory debugging only:
        import gc
        gc.collect()

    def repaint(self):
        if self.__repaintOK():
            Logger.Debug("XYView::draw")
            self._mplCanvas.draw()

    def onXLabelChange(self):
        if self.__repaintOK():
            self._mplAxes.set_xlabel(self._model._xlabel)
            self.repaint()

    def onYLabelChange(self):
        if self.__repaintOK():
            self._mplAxes.set_ylabel(self._model._ylabel)
            self.repaint()

    def onTitleChange(self):
        if self.__repaintOK():
            self._mplAxes.set_title(self._model._title)
            self.updateViewTitle()
            self.repaint()

    def onCurveTitleChange(self):
        # Updating the legend should suffice
        self.showHideLegend()

    def onClearAll(self):
        """ Just does an update with a reset of the marker cycle. """
        if self.__repaintOK():
            self._lastMarkerID = -1
            self.update()

    def onPick(self, event):
        """ MPL callback when picking
    """
        if event.mouseevent.button == 1:
            selected_id = -1
            a = event.artist
            for crv_id, cv in self._curveViews.items():
                if cv._mplLines[0] is a:
                    selected_id = crv_id
            # Use the plotmanager so that other plot sets get their current reset:
            self._controller._plotManager.setCurrentCurve(selected_id)

    def createAndAddLocalAction(self, icon_file, short_name):
        return self._toolbar.addAction(
            self._sgPyQt.loadIcon("CURVEPLOT", icon_file), short_name)

    def createPlotWidget(self):
        self._mplFigure = Figure((8.0, 5.0), dpi=100)
        self._mplCanvas = FigureCanvasQTAgg(self._mplFigure)
        self._mplCanvas.installEventFilter(self._eventHandler)
        self._mplCanvas.mpl_connect('pick_event', self.onPick)
        self._mplAxes = self._mplFigure.add_subplot(1, 1, 1)
        self._plotWidget = PlotWidget()
        self._toobarMPL = NavigationToolbar2QT(self._mplCanvas, None)
        for act in self._toobarMPL.actions():
            actionName = str(act.text()).strip()
            self._mplNavigationActions[actionName] = act
        self._plotWidget.setCentralWidget(self._mplCanvas)
        self._toolbar = self._plotWidget.toolBar
        self.populateToolbar()

        self._popupMenu = QtGui.QMenu()
        self._popupMenu.addAction(self._actionLegend)

        # Connect evenement for the graphic scene
        self._mplCanvas.setContextMenuPolicy(QtCore.Qt.CustomContextMenu)
        self._mplCanvas.customContextMenuRequested.connect(self.onContextMenu)
        self._mplCanvas.mpl_connect('scroll_event', self.onScroll)
        self._mplCanvas.mpl_connect('button_press_event', self.onMousePress)

    def populateToolbar(self):
        # Action to dump view in a file
        a = self.createAndAddLocalAction("dump_view.png", trQ("DUMP_VIEW_TXT"))
        a.triggered.connect(self.dumpView)
        self._toolbar.addSeparator()
        # Actions to manipulate the scene
        a = self.createAndAddLocalAction("fit_all.png", trQ("FIT_ALL_TXT"))
        a.triggered.connect(self.autoFit)
        #    Zoom and pan are mutually exclusive but can be both de-activated:
        self._zoomAction = self.createAndAddLocalAction(
            "fit_area.png", trQ("FIT_AREA_TXT"))
        self._zoomAction.triggered.connect(self.zoomArea)
        self._zoomAction.setCheckable(True)
        self._panAction = self.createAndAddLocalAction("zoom_pan.png",
                                                       trQ("ZOOM_PAN_TXT"))
        self._panAction.triggered.connect(self.pan)
        self._panAction.setCheckable(True)
        self._toolbar.addSeparator()
        # Actions to change the representation of curves
        self._curveActionGroup = QtGui.QActionGroup(self._plotWidget)
        self._pointsAction = self.createAndAddLocalAction(
            "draw_points.png", trQ("DRAW_POINTS_TXT"))
        self._pointsAction.setCheckable(True)
        self._linesAction = self.createAndAddLocalAction(
            "draw_lines.png", trQ("DRAW_LINES_TXT"))
        self._linesAction.setCheckable(True)
        self._curveActionGroup.addAction(self._pointsAction)
        self._curveActionGroup.addAction(self._linesAction)
        self._linesAction.setChecked(True)
        self._curveActionGroup.triggered.connect(self.changeModeCurve)
        self._curveActionGroup.setExclusive(True)
        self._toolbar.addSeparator()
        # Actions to draw horizontal curves as linear or logarithmic
        self._horActionGroup = QtGui.QActionGroup(self._plotWidget)
        self._horLinearAction = self.createAndAddLocalAction(
            "hor_linear.png", trQ("HOR_LINEAR_TXT"))
        self._horLinearAction.setCheckable(True)
        self._horLogarithmicAction = self.createAndAddLocalAction(
            "hor_logarithmic.png", trQ("HOR_LOGARITHMIC_TXT"))
        self._horLogarithmicAction.setCheckable(True)
        self._horActionGroup.addAction(self._horLinearAction)
        self._horActionGroup.addAction(self._horLogarithmicAction)
        self._horLinearAction.setChecked(True)
        self._horActionGroup.triggered.connect(self.onViewHorizontalMode)
        self._toolbar.addSeparator()
        # Actions to draw vertical curves as linear or logarithmic
        self._verActionGroup = QtGui.QActionGroup(self._plotWidget)
        self._verLinearAction = self.createAndAddLocalAction(
            "ver_linear.png", trQ("VER_LINEAR_TXT"))
        self._verLinearAction.setCheckable(True)
        self._verLogarithmicAction = self.createAndAddLocalAction(
            "ver_logarithmic.png", trQ("VER_LOGARITHMIC_TXT"))
        self._verLogarithmicAction.setCheckable(True)
        self._verActionGroup.addAction(self._verLinearAction)
        self._verActionGroup.addAction(self._verLogarithmicAction)
        self._verLinearAction.setChecked(True)
        self._verActionGroup.triggered.connect(self.onViewVerticalMode)
        self._verActionGroup.setExclusive(True)
        self._toolbar.addSeparator()
        # Action to show or hide the legend
        self._actionLegend = self.createAndAddLocalAction(
            "legend.png", trQ("SHOW_LEGEND_TXT"))
        self._actionLegend.setCheckable(True)
        self._actionLegend.triggered.connect(self.showHideLegend)
        if self._DEFAULT_LEGEND_STATE:
            self._actionLegend.setChecked(True)
        self._toolbar.addSeparator()
        # Action to set the preferences
        a = self.createAndAddLocalAction("settings.png", trQ("SETTINGS_TXT"))
        a.triggered.connect(self.onSettings)
        pass

    def dumpView(self):
        # Choice of the view backup file
        filters = []
        for form in [
                "IMAGES_FILES", "PDF_FILES", "POSTSCRIPT_FILES",
                "ENCAPSULATED_POSTSCRIPT_FILES"
        ]:
            filters.append(trQ(form))
        fileName = self._sgPyQt.getFileName(self._sgPyQt.getDesktop(), "",
                                            filters, trQ("DUMP_VIEW_FILE"),
                                            False)
        if not fileName.isEmpty():
            name = str(fileName)
            self._mplAxes.figure.savefig(name)
        pass

    def autoFit(self, check=True, repaint=True):
        if self.__repaintOK():
            self._mplAxes.relim()
            xm, xM = self._mplAxes.xaxis.get_data_interval()
            ym, yM = self._mplAxes.yaxis.get_data_interval()
            i = yM - ym
            self._mplAxes.axis([
                xm, xM, ym - i * self.AUTOFIT_MARGIN,
                yM + i * self.AUTOFIT_MARGIN
            ])
            if repaint:
                self.repaint()

    def zoomArea(self):
        if self._panAction.isChecked() and self._zoomAction.isChecked():
            self._panAction.setChecked(False)
        # Trigger underlying matplotlib action:
        self._mplNavigationActions["Zoom"].trigger()

    def pan(self):
        if self._panAction.isChecked() and self._zoomAction.isChecked():
            self._zoomAction.setChecked(False)
        # Trigger underlying matplotlib action:
        self._mplNavigationActions["Pan"].trigger()

    def getMarker(self, go_next=False):
        if go_next:
            self._lastMarkerID = (self._lastMarkerID + 1) % len(
                self.CURVE_MARKERS)
        return self.CURVE_MARKERS[self._lastMarkerID]

    def changeModeCurve(self, repaint=True):
        if not self.__repaintOK():
            return
        action = self._curveActionGroup.checkedAction()
        if action is self._pointsAction:
            for crv_view in self._curveViews.values():
                crv_view.setLineStyle("None")
        elif action is self._linesAction:
            for crv_view in self._curveViews.values():
                crv_view.setLineStyle("-")
        else:
            raise NotImplementedError
        if repaint:
            self.repaint()

    def setXLog(self, log, repaint=True):
        if not self.__repaintOK():
            return
        self._blockLogSignal = True
        if log:
            self._mplAxes.set_xscale('log')
            self._horLogarithmicAction.setChecked(True)
        else:
            self._mplAxes.set_xscale('linear')
            self._horLinearAction.setChecked(True)
        if repaint:
            self.autoFit()
            self.repaint()
        self._blockLogSignal = False

    def setYLog(self, log, repaint=True):
        if not self.__repaintOK():
            return
        self._blockLogSignal = True
        if log:
            self._mplAxes.set_yscale('log')
            self._verLogarithmicAction.setChecked(True)
        else:
            self._mplAxes.set_yscale('linear')
            self._verLinearAction.setChecked(True)
        if repaint:
            self.autoFit()
            self.repaint()
        self._blockLogSignal = False

    def setXSciNotation(self, sciNotation, repaint=True):
        self._axisXSciNotation = sciNotation
        self.changeFormatAxis()
        if repaint:
            self.repaint()

    def setYSciNotation(self, sciNotation, repaint=True):
        self._axisYSciNotation = sciNotation
        self.changeFormatAxis()
        if repaint:
            self.repaint()

    def onViewHorizontalMode(self, checked=True, repaint=True):
        if self._blockLogSignal:
            return
        action = self._horActionGroup.checkedAction()
        if action is self._horLinearAction:
            self.setXLog(False, repaint)
        elif action is self._horLogarithmicAction:
            self.setXLog(True, repaint)
        else:
            raise NotImplementedError

    def onViewVerticalMode(self, checked=True, repaint=True):
        if self._blockLogSignal:
            return
        action = self._verActionGroup.checkedAction()
        if action is self._verLinearAction:
            self.setYLog(False, repaint)
        elif action is self._verLogarithmicAction:
            self.setYLog(True, repaint)
        else:
            raise NotImplementedError
        if repaint:
            self.repaint()

    def __adjustFigureMargins(self, withLegend):
        """ Adjust figure margins to make room for the legend """
        if withLegend:
            leg = self._legend
            bbox = leg.get_window_extent()
            # In axes coordinates:
            bbox2 = bbox.transformed(leg.figure.transFigure.inverted())
            if self._legendLoc == "right":
                self._mplFigure.subplots_adjust(right=1.0 -
                                                (bbox2.width + 0.02))
            elif self._legendLoc == "bottom":
                self._mplFigure.subplots_adjust(bottom=bbox2.height + 0.1)
        else:
            # Reset to default (rc) values
            self._mplFigure.subplots_adjust(bottom=0.1, right=0.9)

    def setLegendVisible(self, visible, repaint=True):
        if visible and not self._actionLegend.isChecked():
            self._actionLegend.setChecked(True)
            self.showHideLegend(repaint=repaint)
        if not visible and self._actionLegend.isChecked():
            self._actionLegend.setChecked(False)
            self.showHideLegend(repaint=repaint)

    def showHideLegend(self, actionChecked=None, repaint=True):
        if not self.__repaintOK():  # Show/hide legend is extremely costly
            return

        show = self._actionLegend.isChecked()
        nCurves = len(self._curveViews)
        if nCurves > 10: fontSize = 'x-small'
        else: fontSize = None

        if nCurves == 0:
            # Remove legend
            leg = self._mplAxes.legend()
            if leg is not None: leg.remove()
        if show and nCurves > 0:
            # Recreate legend from scratch
            if self._legend is not None:
                self._legend = None
                self._mplAxes._legend = None
            if self._legendLoc == "bottom":
                self._legend = self._mplAxes.legend(loc="upper left",
                                                    bbox_to_anchor=(0.0, -0.05,
                                                                    1.0,
                                                                    -0.05),
                                                    borderaxespad=0.0,
                                                    mode="expand",
                                                    fancybox=True,
                                                    shadow=True,
                                                    ncol=3,
                                                    prop={
                                                        'size': fontSize,
                                                        'style': 'italic'
                                                    })
            elif self._legendLoc == "right":
                self._legend = self._mplAxes.legend(loc="upper left",
                                                    bbox_to_anchor=(1.02, 1.0),
                                                    borderaxespad=0.0,
                                                    ncol=1,
                                                    fancybox=True,
                                                    shadow=True,
                                                    prop={
                                                        'size': fontSize,
                                                        'style': 'italic'
                                                    })
            else:
                raise Exception(
                    "Invalid legend placement! Must be 'bottom' or 'right'")
            # Canvas must be drawn so we can adjust the figure placement:
            self._mplCanvas.draw()
            self.__adjustFigureMargins(withLegend=True)
        else:
            if self._legend is None:
                # Nothing to do
                return
            else:
                self._legend.set_visible(False)
                self._legend = None
                self._mplAxes._legend = None
                self._mplCanvas.draw()
                self.__adjustFigureMargins(withLegend=False)

        curr_crv = self._model._currentCurve
        if curr_crv is None: curr_title = None
        else: curr_title = curr_crv.getTitle()
        if self._legend is not None:
            for label in self._legend.get_texts():
                text = label.get_text()
                if (text == curr_title):
                    label.set_backgroundcolor('0.85')
                else:
                    label.set_backgroundcolor('white')

        if repaint:
            self.repaint()

    def onSettings(self, trigger=False, dlg_test=None):
        dlg = dlg_test or PlotSettings()
        dlg.titleEdit.setText(self._mplAxes.get_title())
        dlg.axisXTitleEdit.setText(self._mplAxes.get_xlabel())
        dlg.axisYTitleEdit.setText(self._mplAxes.get_ylabel())
        dlg.gridCheckBox.setChecked(
            self._mplAxes.xaxis._gridOnMajor
        )  # could not find a relevant API to check this
        dlg.axisXSciCheckBox.setChecked(self._axisXSciNotation)
        dlg.axisYSciCheckBox.setChecked(self._axisYSciNotation)
        xmin, xmax = self._mplAxes.get_xlim()
        ymin, ymax = self._mplAxes.get_ylim()
        xminText = "%g" % xmin
        xmaxText = "%g" % xmax
        yminText = "%g" % ymin
        ymaxText = "%g" % ymax
        dlg.axisXMinEdit.setText(xminText)
        dlg.axisXMaxEdit.setText(xmaxText)
        dlg.axisYMinEdit.setText(yminText)
        dlg.axisYMaxEdit.setText(ymaxText)
        # List of markers
        dlg.markerCurve.clear()
        for marker in self.CURVE_MARKERS:
            dlg.markerCurve.addItem(marker)
        curr_crv = self._model.getCurrentCurve()
        if not curr_crv is None:
            dlg.colorCurve.setEnabled(True)
            dlg.markerCurve.setEnabled(True)
            name = curr_crv.getTitle()
            dlg.nameCurve.setText(name)
            view = self._curveViews[curr_crv.getID()]
            marker = view.getMarker()
            color = view.getColor()
            index = dlg.markerCurve.findText(marker)
            dlg.markerCurve.setCurrentIndex(index)
            rgb = colors.colorConverter.to_rgb(color)
            dlg.setRGB(rgb[0], rgb[1], rgb[2])
        else:
            dlg.colorCurve.setEnabled(False)
            dlg.markerCurve.setEnabled(False)
            dlg.nameCurve.setText("")
            view = None
        if self._legend is None:
            dlg.showLegendCheckBox.setChecked(False)
            dlg.legendPositionComboBox.setEnabled(False)
        else:
            if self._legend.get_visible():
                dlg.showLegendCheckBox.setChecked(True)
                dlg.legendPositionComboBox.setEnabled(True)
                if self._legendLoc == "bottom":
                    dlg.legendPositionComboBox.setCurrentIndex(0)
                elif self._legendLoc == "right":
                    dlg.legendPositionComboBox.setCurrentIndex(1)
            else:
                dlg.showLegendCheckBox.setChecked(False)
                dlg.legendPositionComboBox.setEnabled(False)

        if dlg.exec_():
            # Title
            self._model.setTitle(dlg.titleEdit.text())
            # Axis
            self._model.setXLabel(dlg.axisXTitleEdit.text())
            self._model.setYLabel(dlg.axisYTitleEdit.text())
            # Grid
            if dlg.gridCheckBox.isChecked():
                self._mplAxes.grid(True)
            else:
                self._mplAxes.grid(False)
            # Legend
            if dlg.showLegendCheckBox.isChecked():
                self._actionLegend.setChecked(True)
                if dlg.legendPositionComboBox.currentIndex() == 0:
                    self._legendLoc = "bottom"
                elif dlg.legendPositionComboBox.currentIndex() == 1:
                    self._legendLoc = "right"
            else:
                self._actionLegend.setChecked(False)
            xminText = dlg.axisXMinEdit.text()
            xmaxText = dlg.axisXMaxEdit.text()
            yminText = dlg.axisYMinEdit.text()
            ymaxText = dlg.axisYMaxEdit.text()
            self._mplAxes.axis([
                float(xminText),
                float(xmaxText),
                float(yminText),
                float(ymaxText)
            ])
            self._axisXSciNotation = dlg.axisXSciCheckBox.isChecked()
            self._axisYSciNotation = dlg.axisYSciCheckBox.isChecked()
            self.changeFormatAxis()
            # Color and marker of the curve
            if view:
                view.setColor(dlg.getRGB())
                view.setMarker(
                    self.CURVE_MARKERS[dlg.markerCurve.currentIndex()])
            self.showHideLegend(repaint=True)
            self._mplCanvas.draw()
        pass

    def updateViewTitle(self):
        s = ""
        if self._model._title != "":
            s = " - %s" % self._model._title
        title = "CurvePlot (%d)%s" % (self._model.getID(), s)
        self._sgPyQt.setViewTitle(self._salomeViewID, title)

    def onCurrentPlotSetChange(self):
        """ Avoid a unnecessary call to update() when just switching current plot set! """
        pass

    def onCurrentCurveChange(self):
        curr_crv2 = self._model.getCurrentCurve()
        if curr_crv2 != self._currCrv:
            if self._currCrv is not None:
                view = self._curveViews[self._currCrv.getID()]
                view.toggleHighlight(False)
            if not curr_crv2 is None:
                view = self._curveViews[curr_crv2.getID()]
                view.toggleHighlight(True)
            self._currCrv = curr_crv2
            self.showHideLegend(repaint=False)  # redo legend
            self.repaint()

    def changeFormatAxis(self):
        if not self.__repaintOK():
            return

        # don't try to switch to sci notation if we are not using the
        # matplotlib.ticker.ScalarFormatter (i.e. if in Log for ex.)
        if self._horLinearAction.isChecked():
            if self._axisXSciNotation:
                self._mplAxes.ticklabel_format(style='sci',
                                               scilimits=(0, 0),
                                               axis='x')
            else:
                self._mplAxes.ticklabel_format(style='plain', axis='x')
        if self._verLinearAction.isChecked():
            if self._axisYSciNotation:
                self._mplAxes.ticklabel_format(style='sci',
                                               scilimits=(0, 0),
                                               axis='y')
            else:
                self._mplAxes.ticklabel_format(style='plain', axis='y')

    def update(self):
        if self._salomeViewID is None:
            self.createPlotWidget()
            self._salomeViewID = self._sgPyQt.createView(
                "CurvePlot", self._plotWidget)
            Logger.Debug("Creating SALOME view ID=%d" % self._salomeViewID)
            self._sgPyQt.setViewVisible(self._salomeViewID, True)

        self.updateViewTitle()

        # Check list of curve views:
        set_mod = set(self._model._curves.keys())
        set_view = set(self._curveViews.keys())

        # Deleted/Added curves:
        dels = set_view - set_mod
        added = set_mod - set_view

        for d in dels:
            self.removeCurve(d)

        if not len(self._curveViews):
            # Reset color cycle
            self._mplAxes.set_color_cycle(None)

        for a in added:
            self.appendCurve(a)

        # Axes labels and title
        self._mplAxes.set_xlabel(self._model._xlabel)
        self._mplAxes.set_ylabel(self._model._ylabel)
        self._mplAxes.set_title(self._model._title)

        self.onViewHorizontalMode(repaint=False)
        self.onViewVerticalMode(repaint=False)
        self.changeModeCurve(repaint=False)
        self.showHideLegend(
            repaint=False
        )  # The canvas is repainted anyway (needed to get legend bounding box)
        self.changeFormatAxis()

        # Redo auto-fit
        self.autoFit(repaint=False)
        self.repaint()

    def onDataChange(self):
        # the rest is done in the CurveView:
        self.autoFit(repaint=True)

    def onMousePress(self, event):
        if event.button == 3:
            if self._panAction.isChecked():
                self._panAction.setChecked(False)
            if self._zoomAction.isChecked():
                self._zoomAction.setChecked(False)

    def onContextMenu(self, position):
        pos = self._mplCanvas.mapToGlobal(
            QtCore.QPoint(position.x(), position.y()))
        self._popupMenu.exec_(pos)

    def onScroll(self, event):
        # Event location (x and y)
        xdata = event.xdata
        ydata = event.ydata

        cur_xlim = self._mplAxes.get_xlim()
        cur_ylim = self._mplAxes.get_ylim()

        base_scale = 2.
        if event.button == 'down':
            # deal with zoom in
            scale_factor = 1 / base_scale
        elif event.button == 'up':
            # deal with zoom out
            scale_factor = base_scale
        else:
            # deal with something that should never happen
            scale_factor = 1

        new_width = (cur_xlim[1] - cur_xlim[0]) * scale_factor
        new_height = (cur_ylim[1] - cur_ylim[0]) * scale_factor

        relx = (cur_xlim[1] - xdata) / (cur_xlim[1] - cur_xlim[0])
        rely = (cur_ylim[1] - ydata) / (cur_ylim[1] - cur_ylim[0])

        self._mplAxes.set_xlim(
            [xdata - new_width * (1 - relx), xdata + new_width * (relx)])
        self._mplAxes.set_ylim(
            [ydata - new_height * (1 - rely), ydata + new_height * (rely)])

        self.repaint()
        pass

    def onPressEvent(self, event):
        if event.button == 3:
            #self._mplCanvas.emit(QtCore.SIGNAL("button_release_event()"))
            canvasSize = event.canvas.geometry()
            point = event.canvas.mapToGlobal(
                QtCore.QPoint(event.x,
                              canvasSize.height() - event.y))
            self._popupMenu.exec_(point)
        else:
            print "Press event on the other button"
        #if event.button == 3 :
        #  canvasSize = event.canvas.geometry()
        #  point = event.canvas.mapToGlobal(QtCore.QPoint(event.x,canvasSize.height()-event.y))
        #  self._popupMenu.move(point)
        #  self._popupMenu.show()

    def onMotionEvent(self, event):
        print "OnMotionEvent ", event.button
        #if event.button == 3 :
        #  event.button = None
        #  return True

    def onReleaseEvent(self, event):
        print "OnReleaseEvent ", event.button