class GUI(QtGui.QWidget): def __init__(self): super(GUI, self).__init__() self.setWindowTitle( 'AP-DV Analaysis' ) self.lbltxt = '"wheel" press: change side, currently %s\n"i" or "u" press: change cell sides' 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() rawDataBox.addLayout(Col1) rawDataBox.addLayout(Col2) self.setLayout(mainWindow) # DEFINE ALL WIDGETS AND BUTTONS loadBtn = QtGui.QPushButton('Load DataSet') saveBtn = QtGui.QPushButton('Save data (F12)') tpLbl = QtGui.QLabel('Relative Tp:') fNameLbl = QtGui.QLabel('File name:') self.tp = QtGui.QSpinBox(self) self.tp.setValue(0) self.tp.setMinimum(-100000) self.tp.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 ) # 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(fNameLbl, 1, 0)#, 1, 1, Qt.AlignTop) Col1.addWidget(self.fName, 1, 1)#, 1, 1, Qt.AlignTop) Col1.addWidget(self._488nmBtn, 2, 0 ) Col1.addWidget(self._561nmBtn, 3, 0 ) Col1.addWidget(self.CoolLEDBtn, 4, 0 ) Col2.addWidget(self.sld1) Col2.addWidget(self.sld2) Col2.addWidget(self.canvas1) self.setFocus() self.show() # BIND BUTTONS TO FUNCTIONS loadBtn.clicked.connect(self.selectWorm) saveBtn.clicked.connect(self.saveData) self.tp.valueChanged.connect(self.updateAllCanvas) self.sld1.valueChanged.connect(self.updateAllCanvas) self.sld2.valueChanged.connect(self.updateAllCanvas) self._488nmBtn.toggled.connect(self.radioClicked) self._561nmBtn.toggled.connect(self.radioClicked) self.CoolLEDBtn.toggled.connect(self.radioClicked) self.fig1.canvas.mpl_connect('button_press_event',self.onMouseClickOnCanvas1) self.fig1.canvas.mpl_connect('scroll_event',self.wheelEvent) #----------------------------------------------------------------------------------------------- # FORMATTING THE WINDOW #----------------------------------------------------------------------------------------------- def center(self): qr = self.frameGeometry() cp = QtGui.QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def HLine(self): toto = QtGui.QFrame() toto.setFrameShape(QtGui.QFrame.HLine) toto.setFrameShadow(QtGui.QFrame.Sunken) return toto def VLine(self): toto = QtGui.QFrame() toto.setFrameShape(QtGui.QFrame.VLine) toto.setFrameShadow(QtGui.QFrame.Sunken) return toto def heightForWidth(self, width): return width #----------------------------------------------------------------------------------------------- # BUTTON FUNCTIONS #----------------------------------------------------------------------------------------------- def selectWorm(self): ### store the folders self.pathDial = QtGui.QFileDialog.getExistingDirectory(self, 'Select a folder', 'X:\\Simone\\160226_lag2YFP_histmCherry') self.worm = self.pathDial.split("\\")[-1].split('_')[0] self.path = os.path.dirname( self.pathDial ) self.setWindowTitle('Body Length Analysis - ' + self.pathDial) ### give error message if there is no CoolLED movie in the selected folder if not os.path.isfile( os.path.join( self.pathDial, 'CoolLED_movie.tif' ) ): QtGui.QMessageBox.about(self,'Warning!','There is no movie in this folder! Create a movie first!') return ### load all movies (without timestamps, we will add it later on) self.channels = {} if os.path.isfile( os.path.join( self.pathDial, '488nm_movie.tif' ) ): self.channels['488nm'] = load_stack( os.path.join( self.pathDial, '488nm_movie.tif') ) if os.path.isfile( os.path.join( self.pathDial, '561nm_movie.tif') ): self.channels['561nm'] = load_stack( os.path.join( self.pathDial, '561nm_movie.tif') ) if os.path.isfile( os.path.join( self.pathDial, 'CoolLED_movie.tif' ) ): self.channels['CoolLED'] = load_stack( os.path.join( self.pathDial, 'CoolLED_movie.tif' ) ) self.currentChannel = 'CoolLED' ### load parameters and times dataframes self.paramsDF = load_data_frame( self.path, self.worm + '_01params.pickle' ) self.timesDF = load_data_frame( self.path, self.worm + '_01times.pickle' ) self.gpDF = load_data_frame( self.path, self.worm + '_02gonadPos.pickle' ) self.cellPosDF = load_data_frame( self.path, self.worm + '_04cellPos.pickle' ) # extract some info self.compression = self.paramsDF.compression self.hatchingtidx = int( self.paramsDF.tidxHatch ) ### if the AP pickle file already exists, load it, otherwise create a blank one if os.path.isfile( os.path.join(self.path, self.worm + '_08apdvPos.pickle' ) ): self.apdvPosDF = load_data_frame( self.path, self.worm + '_08apdvPos.pickle' ) else: self.apdvPosDF = create_apdv_pos( self.timesDF ) ### extract current cells already labeled self.currentPos = extract_current_apdv_pos( self.apdvPosDF, first_tidx_pos_all_cells( self.cellPosDF ) ) print(self.currentPos) ### set the timepoint to the hatching time self.tp.setMinimum(np.min(self.timesDF.tidxRel)) self.tp.setMaximum(np.max(self.timesDF.tidxRel)) self.tp.setValue( first_tidx_pos_all_cells( self.cellPosDF ) ) ### update the text of the fileName self.fName.setText(self.timesDF.ix[self.timesDF.tidxRel == self.tp.value(), 'fName'].values[0]) self.setFocus() def saveData(self): save_data_frame( self.apdvPosDF, self.path, self.worm + '_08apdvPos.pickle' ) def updateAllCanvas(self): self.updateRadioBtn() self.updateCanvas1() def radioClicked(self): if self._488nmBtn.isChecked(): if '488nm' in self.channels.keys(): self.currentChannel = '488nm' else: QtGui.QMessageBox.about(self, 'Warning', 'No 488nm channel!') elif self._561nmBtn.isChecked(): if '561nm' in self.channels.keys(): self.currentChannel = '561nm' else: QtGui.QMessageBox.about(self, 'Warning', 'No 561nm channel!') elif self.CoolLEDBtn.isChecked(): if 'CoolLED' in self.channels.keys(): self.currentChannel = 'CoolLED' else: QtGui.QMessageBox.about(self, 'Warning', 'No CoolLED channel!') self.setBCslidersMinMax() self.resetBC() self.setFocus() self.updateAllCanvas() def keyPressEvent(self, event): # key press on cropped image if self.canvas1.underMouse(): self.onKeyPressOnCanvas1(event) self.setFocus() #----------------------------------------------------------------------------------------------- # DEFAULT FUNCTION FOR KEY AND MOUSE PRESS ON WINDOW #----------------------------------------------------------------------------------------------- def wheelEvent(self,event): if self.canvas1.underMouse(): step = event.step else: step = event.delta()/abs(event.delta()) ### update daytaframe with previously labeled cells newapdvDF = update_apdv_pos_DF( self.currentPos, self.apdvPosDF, self.tp.value() ) self.apdvPosDF = newapdvDF print(self.currentPos) # print previously labeled positions ### extract current cells already labeled self.currentPos = extract_current_apdv_pos( self.apdvPosDF, self.tp.value() + step ) self.tp.setValue( self.tp.value() + step ) self.fName.setText( self.timesDF.ix[ self.timesDF.tidxRel == self.tp.value(), 'fName' ].values[0] ) #----------------------------------------------------------------------------------------------- # ADDITIONAL FUNCTIONS FOR KEY AND MOUSE PRESS ON CANVASES #----------------------------------------------------------------------------------------------- def onKeyPressOnCanvas1(self, event): posNameList = [ QtCore.Qt.Key_A, QtCore.Qt.Key_P, QtCore.Qt.Key_D ] # find the position of the cursor relative to the image in pixel imgshape = self.channels[self.currentChannel][0].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 )]) * self.compression ### find the closest cell to the cursor idx = closer_2Dpos( refpos.astype( np.uint16 ), self.currentPos ) ### assign the name to the cell if any( [ event.key() == cn for cn in posNameList ] ): self.currentPos.ix[ idx, 'pname' ] = QtGui.QKeySequence(event.key()).toString().lower() self.updateCanvas1() self.setFocus() def onMouseClickOnCanvas1(self, event): refpos = np.array( [ event.xdata, event.ydata ] ) * self.compression if event.button == 1: # create an empty cell in the currentCells df: the only entries are tidx, xyzpos and cname newpos = create_single_apdv_pos( refpos.astype(np.uint16), self.tp.value() ) self.currentPos = pd.concat( [ self.currentPos, newpos ] ) elif event.button == 3: # remove a cell (the closest to the cursor at the moment of right-click) idx = closer_2Dpos( refpos.astype(np.uint16), self.currentPos ) self.currentPos = self.currentPos.drop( [ idx ] ) self.currentPos = self.currentPos.reset_index(drop=True) self.updateCanvas1() self.setFocus() #----------------------------------------------------------------------------------------------- # UTILS #----------------------------------------------------------------------------------------------- def updateRadioBtn(self): if self.currentChannel == '488nm': self._488nmBtn.setChecked(True) elif self.currentChannel == '561nm': self._561nmBtn.setChecked(True) elif self.currentChannel == 'CoolLED': self.CoolLEDBtn.setChecked(True) self.setFocus() def setBCslidersMinMax(self): self.sld1.setMaximum(np.max(self.channels[self.currentChannel])) self.sld1.setMinimum(np.min(self.channels[self.currentChannel])) self.sld2.setMaximum(np.max(self.channels[self.currentChannel])) self.sld2.setMinimum(np.min(self.channels[self.currentChannel])) def resetBC(self): self.sld1.setValue(np.min(self.channels[self.currentChannel])) self.sld2.setValue(np.max(self.channels[self.currentChannel])) def updateCanvas1(self): # plot the image self.ax1.cla() imgplot = self.ax1.imshow( self.channels[self.currentChannel][self.tp.value() + self.hatchingtidx], cmap = 'gray' ) # remove the white borders and plot outline and spline self.ax1.autoscale(False) self.ax1.axis('Off') self.fig1.subplots_adjust(left=0., right=1., top=1., bottom=0.) # change brightness and contrast self.sld1.setValue(np.min([self.sld1.value(),self.sld2.value()])) self.sld2.setValue(np.max([self.sld1.value(),self.sld2.value()])) imgplot.set_clim(self.sld1.value(), self.sld2.value()) # print gonad position gonadPos = extract_pos( self.gpDF.ix[ self.gpDF.tidx == self.tp.value() ].squeeze() ) / self.compression if len( gonadPos.shape ) > 0: self.ax1.plot( gonadPos[0], gonadPos[1], 'o', color='red', ms=10, mew=0, alpha=.5, lw = 0 ) # pritn apdv pos for idx, pos in self.currentPos.iterrows(): p = extract_pos( pos ) / self.compression self.ax1.plot( p[0], p[1], 'o', color='red', ms=10, mew=0, alpha=.8, lw = 0 ) self.ax1.text( p[0], p[1] + 20, pos.pname, color='red', size='medium', alpha=.8, rotation=0 ) # print time # print(self.timesDF.ix[ self.timesDF.tidxRel == self.tp.value(), 'timesRel' ]) self.ax1.text( 5, 15, '%.2f' % self.timesDF.ix[ self.timesDF.tidxRel == self.tp.value(), 'timesRel' ].values[0], color = 'red' ) # redraw the canvas self.canvas1.draw() self.setFocus()
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 ) # 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.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('button_press_event',self.onMouseClickOnCanvas1) 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 #----------------------------------------------------------------------------------------------- # 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('Mark Cells - ' + self.pathDial) ### 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( self.path, self.worm + '_01params.pickle' ) self.timesDF = load_data_frame( self.path, self.worm + '_01times.pickle' ) self.gpDF = load_data_frame( self.path, self.worm + '_02gonadPos.pickle' ) # extract some info self.compression = self.paramsDF.compression self.hatchingtidx = int( self.paramsDF.tidxHatch ) ### 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, 2:6 ][0][0] - 1 tpL2 = self.timesDF.ix[ self.timesDF.tidxRel == lethtidx[0], 'timesRel' ].values[0] tpL1 = self.timesDF.ix[ self.timesDF.tidxRel == mintp, 'timesRel' ].values[0] # relative to hatching time self.tpHatch=tpL1 ### 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( 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_Space: 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() def onMouseClickOnCanvas1(self, event): refpos = np.array( [ event.xdata, event.ydata, self.sl.value() ] ) if event.button == 1: # 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 ] ) elif event.button == 3: 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() #----------------------------------------------------------------------------------------------- # 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='red', size='medium', alpha=.8, rotation=0 ) ) self.points1.append( self.ax1.plot( cell.X, cell.Y, 'o', color='red', alpha = .8, mew = 0 )[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='red', ms=10, mew=0, alpha=.5, lw = 0 ) # print time self.text2 = self.ax2.text( 5, 25, '--.--', color = 'red' ) # 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(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] - self.tpHatch ) ) # 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 )
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)
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) # 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.canvas2) self.setFocus() self.show() # BIND BUTTONS TO FUNCTIONS loadBtn.clicked.connect(self.selectWorm) saveBtn.clicked.connect(self.saveData) self.tp.valueChanged.connect(self.loadNewStack) self.sl.valueChanged.connect(self.updateAllCanvas) self.sld1.valueChanged.connect(self.updateAllCanvas) self.sld2.valueChanged.connect(self.updateAllCanvas) self._488nmBtn.toggled.connect(self.radioClicked) self._561nmBtn.toggled.connect(self.radioClicked) self.CoolLEDBtn.toggled.connect(self.radioClicked) self.fig1.canvas.mpl_connect('button_press_event', self.onMouseClickOnCanvas1) self.fig1.canvas.mpl_connect('scroll_event', self.wheelEvent) #----------------------------------------------------------------------------------------------- # FORMATTING THE WINDOW #----------------------------------------------------------------------------------------------- def center(self): qr = self.frameGeometry() cp = QtGui.QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def HLine(self): toto = QtGui.QFrame() toto.setFrameShape(QtGui.QFrame.HLine) toto.setFrameShadow(QtGui.QFrame.Sunken) return toto def VLine(self): toto = QtGui.QFrame() toto.setFrameShape(QtGui.QFrame.VLine) toto.setFrameShadow(QtGui.QFrame.Sunken) return toto def heightForWidth(self, width): return width #----------------------------------------------------------------------------------------------- # BUTTON FUNCTIONS #----------------------------------------------------------------------------------------------- def selectWorm(self): ### store the folders self.pathDial = QtGui.QFileDialog.getExistingDirectory( self, 'Select a folder', 'Y:\\Images') self.worm = self.pathDial.split("\\")[-1].split('_')[0] self.path = os.path.dirname(self.pathDial) self.setWindowTitle('Mark Cells - ' + self.pathDial) ### give error message if there is no CoolLED movie in the selected folder if not os.path.isfile(os.path.join(self.pathDial, 'CoolLED_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(self.path, self.worm + '_01params.pickle') self.timesDF = load_data_frame(self.path, self.worm + '_01times.pickle') self.gpDF = load_data_frame(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(self.path, self.worm + '_04cellPos.pickle') else: self.cellPosDF = create_cell_pos(self.timesDF, self.cellNames) ### load all movies (without timestamps, we will add it later on) self.LEDmovie = load_stack( os.path.join(self.pathDial, 'CoolLED_movie.tif')) ### set the timepoint to the hatching time self.tp.setMinimum(np.min(self.timesDF.tidxRel)) self.tp.setMaximum(np.max(self.timesDF.tidxRel)) self.tp.setValue(0) ### extract current cells already labeled self.currentCells = extract_current_cell_pos(self.cellPosDF, self.tp.value()) # 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] ### update the text of the fileName self.fName.setText( self.timesDF.ix[self.timesDF.tidxRel == self.tp.value(), 'fName'].values[0]) self.loadNewStack() # self.pathDial.show() self.updateAllCanvas() self.setFocus() def loadNewStack(self): # print(self.fList['gfp'][self.tp.value()]) tRow = self.timesDF.ix[self.timesDF.tidxRel == self.tp.value()].squeeze() print('Loading... ', self.pathDial, tRow.fName) # load all the available stacks self.stacks = {} for ch in self.channels: fileName = os.path.join(self.pathDial, tRow.fName + ch + '.tif') if os.path.isfile(fileName): self.stacks[ch] = load_stack(fileName) if len(self.stacks.keys()) > 0: # print(self.stacks.keys(), self.stacksStraight) self.sl.setMaximum(self.stacks[self.currentChannel].shape[0] - 1) self.setBCslidersMinMax() ### update the text of the fileName self.fName.setText( self.timesDF.ix[self.timesDF.tidxRel == self.tp.value(), 'fName'].values[0]) ### extract current cells already labeled self.currentCells = extract_current_cell_pos(self.cellPosDF, self.tp.value()) # self.updateTable() self.updateAllCanvas() 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 updateAllCanvas(self): self.updateRadioBtn() self.updateCanvas1() self.updateCanvas2() def radioClicked(self): if self._488nmBtn.isChecked(): if '488nm' in self.channels: self.currentChannel = '488nm' else: QtGui.QMessageBox.about(self, 'Warning', 'No 488nm channel!') elif self._561nmBtn.isChecked(): if '561nm' in self.channels: self.currentChannel = '561nm' else: QtGui.QMessageBox.about(self, 'Warning', 'No 561nm channel!') elif self.CoolLEDBtn.isChecked(): if 'CoolLED' in self.channels: self.currentChannel = 'CoolLED' else: QtGui.QMessageBox.about(self, 'Warning', 'No CoolLED channel!') self.setBCslidersMinMax() self.resetBC() self.setFocus() self.updateAllCanvas() #----------------------------------------------------------------------------------------------- # 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) # 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] self.updateCanvas1() self.setFocus() def onMouseClickOnCanvas1(self, event): refpos = np.array([event.xdata, event.ydata, self.sl.value()]) if event.button == 1: # 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]) elif event.button == 3: # 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() #----------------------------------------------------------------------------------------------- # UTILS #----------------------------------------------------------------------------------------------- def updateRadioBtn(self): if self.currentChannel == '488nm': self._488nmBtn.setChecked(True) elif self.currentChannel == '561nm': self._561nmBtn.setChecked(True) elif self.currentChannel == 'CoolLED': self.CoolLEDBtn.setChecked(True) self.setFocus() def setBCslidersMinMax(self): self.sld1.setMaximum(np.max(self.stacks[self.currentChannel])) self.sld1.setMinimum(np.min(self.stacks[self.currentChannel])) self.sld2.setMaximum(np.max(self.stacks[self.currentChannel])) self.sld2.setMinimum(np.min(self.stacks[self.currentChannel])) def resetBC(self): self.sld1.setValue(np.min(self.stacks[self.currentChannel])) self.sld2.setValue(np.max(self.stacks[self.currentChannel])) def updateCanvas1(self): 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() if len(self.stacks.keys()) == 0: # if no images are found, leave the canvas empty return # plot the image self.ax1.cla() imgplot = self.ax1.imshow( self.stacks[self.currentChannel][self.sl.value()], cmap='gray') # remove the white borders and plot outline and spline self.ax1.autoscale(False) self.ax1.axis('Off') self.fig1.subplots_adjust(left=0., right=1., top=1., bottom=0.) # cell text on the image for idx, cell in self.currentCells.iterrows(): if cell.Z == self.sl.value(): self.ax1.text(cell.X, cell.Y + 18, cell.cname, color='red', size='medium', alpha=.8, rotation=0) self.ax1.plot(cell.X, cell.Y, 'o', color='red', alpha=.8, mew=0) # change brightness and contrast self.sld1.setValue(np.min([self.sld1.value(), self.sld2.value()])) self.sld2.setValue(np.max([self.sld1.value(), self.sld2.value()])) imgplot.set_clim(self.sld1.value(), self.sld2.value()) # redraw the canvas self.canvas1.draw() self.setFocus() def updateCanvas2(self): # plot the image self.ax2.cla() imgplot = self.ax2.imshow(self.LEDmovie[self.tp.value() + self.hatchingtidx], cmap='gray') # 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 = extract_pos(self.gpDF.ix[ self.gpDF.tidx == self.tp.value()].squeeze()) / self.compression self.ax2.plot(gonadPos[0], gonadPos[1], 'o', color='red', ms=10, mew=0, alpha=.5, lw=0) # print time # print(self.timesDF.ix[ self.timesDF.tidxRel == self.tp.value(), 'timesRel' ]) self.ax2.text(5, 25, '%.2f' % self.timesDF.ix[self.timesDF.tidxRel == self.tp.value(), 'timesRel'].values[0], color='red') # redraw the canvas self.canvas2.draw() self.setFocus() def checkConsistencyCellNames(self): ### check consistency of cell names tp = self.tp.value() # 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': # before changinf timepoint, print labeled cells and check if they are OK print(self.currentCells) # if they are OK (and not going to negative times), change timepoint if self.checkConsistencyCellNames() and (self.tp.value() + increment) >= 0: self.tp.setValue(self.tp.value() + increment) if whatToChange == 'space': self.sl.setValue(self.sl.value() + increment)
class GUI(QtGui.QWidget): #----------------------------------------------------------------------------------------------- # INITIALIZATION OF THE WINDOW - DEFINE AND PLACE ALL THE WIDGETS #----------------------------------------------------------------------------------------------- def __init__(self): super(GUI, self).__init__() self.setWindowTitle('Seam Cells Analysis') self.scaleFactor = 4 self.side = 'L' self.lbltxt = '"wheel" press: change side, currently %s\n"i" or "u" press: change cell sides' self.initUI() def initUI(self): # SET THE GEOMETRY mainWindow = QtGui.QVBoxLayout() mainWindow.setSpacing(15) fileBox = QtGui.QHBoxLayout() spaceBox1 = QtGui.QHBoxLayout() rawDataBox = QtGui.QHBoxLayout() spaceBox2 = QtGui.QHBoxLayout() straightBox = QtGui.QVBoxLayout() mainWindow.addLayout(fileBox) mainWindow.addLayout(spaceBox1) mainWindow.addLayout(rawDataBox) mainWindow.addLayout(spaceBox2) mainWindow.addLayout(straightBox) Col1 = QtGui.QGridLayout() Col2 = QtGui.QHBoxLayout() Col3 = QtGui.QVBoxLayout() Col4 = QtGui.QVBoxLayout() rawDataBox.addLayout(Col1) rawDataBox.addLayout(Col2) rawDataBox.addLayout(Col3) rawDataBox.addLayout(Col4) Raw1 = QtGui.QHBoxLayout() Raw2 = QtGui.QHBoxLayout() Raw3 = QtGui.QHBoxLayout() straightBox.addLayout(Raw1) straightBox.addLayout(Raw2) straightBox.addLayout(Raw3) self.setLayout(mainWindow) # DEFINE ALL WIDGETS AND BUTTONS loadBtn = QtGui.QPushButton('Load DataSet') saveBtn = QtGui.QPushButton('Save data (F12)') tpLbl = QtGui.QLabel('Timepoint:') slLbl = QtGui.QLabel('Slice:') hatchLbl = QtGui.QLabel('Hatching Time:') 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.hatch = QtGui.QSpinBox(self) self.hatch.setValue(0) self.hatch.setMaximum(100000) self._488nmBtn = QtGui.QRadioButton('488nm') self._561nmBtn = QtGui.QRadioButton('561nm') self.CoolLEDBtn = QtGui.QRadioButton('CoolLED') automaticOutlineBtn = QtGui.QPushButton('Automatic Outline') straightenBtn = QtGui.QPushButton('Straighten images') 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(500,500)) ############################# change this value to set the figure size self.canvas1.setSizePolicy( QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding ) self.cellTbl = QtGui.QTableWidget() self.sideLbl = QtGui.QLabel(self.lbltxt % self.side) autonameBtn = QtGui.QPushButton('Restore Automatic Cell Names') wrt2Btn = QtGui.QPushButton('Compute wrt-2 Expression') wrt2FullBtn = QtGui.QPushButton('Compute wrt-2 Expression on the raw data (SLOW!)') self.fig2 = Figure((4.0, 2.0)) 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.setFocusPolicy( QtCore.Qt.StrongFocus ) self.canvas2.setFocus() self.canvas2.setSizePolicy( QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding ) self.fig3 = Figure((4.0, 2.0), dpi=100) self.fig3.subplots_adjust(left=0., right=1., top=1., bottom=0.) self.ax3 = self.fig3.add_subplot(111) self.canvas3 = FigureCanvas(self.fig3) self.canvas3.setFocusPolicy( QtCore.Qt.ClickFocus ) self.canvas3.setFocus() self.canvas3.setSizePolicy( QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding ) # 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(hatchLbl, 2, 0)#, 1, 1, Qt.AlignTop) Col1.addWidget(self.hatch, 2, 1)#, 1, 1, Qt.AlignTop) Col1.addWidget(self._488nmBtn, 3, 0 ) Col1.addWidget(self._561nmBtn, 4, 0 ) Col1.addWidget(self.CoolLEDBtn, 5, 0 ) Col1.addWidget(automaticOutlineBtn, 6, 0 ) Col1.addWidget(straightenBtn, 7, 0 ) Col2.addWidget(self.sld1) Col2.addWidget(self.sld2) Col2.addWidget(self.canvas1) Col3.addWidget(self.VLine()) Col4.addWidget(self.cellTbl) spaceBox2.addWidget(self.HLine()) Raw1.addWidget(self.sideLbl) Raw1.addWidget(autonameBtn) Raw1.addWidget(wrt2Btn) Raw1.addWidget(wrt2FullBtn) Raw2.addWidget(self.canvas2) Raw3.addWidget(self.canvas3) # Raw3.addWidget(QtGui.QDockWidget()) self.setFocus() self.show() # BIND BUTTONS TO FUNCTIONS loadBtn.clicked.connect(self.selectWorm) saveBtn.clicked.connect(self.saveData) self.tp.valueChanged.connect(self.loadNewStack) self.sl.valueChanged.connect(self.updateAllCanvas) self.sld1.valueChanged.connect(self.updateAllCanvas) self.sld2.valueChanged.connect(self.updateAllCanvas) self.hatch.valueChanged.connect(self.updateTidxDataFrame) self._488nmBtn.toggled.connect(self.radioClicked) self._561nmBtn.toggled.connect(self.radioClicked) self.CoolLEDBtn.toggled.connect(self.radioClicked) automaticOutlineBtn.clicked.connect(self.automaticOutline) straightenBtn.clicked.connect(self.straightenWorm) autonameBtn.clicked.connect(self.automaticCellNames) self.fig1.canvas.mpl_connect('button_press_event',self.onMouseClickOnCanvas1) self.fig2.canvas.mpl_connect('button_press_event',self.onMouseClickOnCanvas2) self.fig1.canvas.mpl_connect('scroll_event',self.wheelEvent) self.fig2.canvas.mpl_connect('scroll_event',self.wheelEvent) self.fig3.canvas.mpl_connect('scroll_event',self.wheelEvent) #----------------------------------------------------------------------------------------------- # FORMATTING THE WINDOW #----------------------------------------------------------------------------------------------- def center(self): qr = self.frameGeometry() cp = QtGui.QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def HLine(self): toto = QtGui.QFrame() toto.setFrameShape(QtGui.QFrame.HLine) toto.setFrameShadow(QtGui.QFrame.Sunken) return toto def VLine(self): toto = QtGui.QFrame() toto.setFrameShape(QtGui.QFrame.VLine) toto.setFrameShadow(QtGui.QFrame.Sunken) return toto def heightForWidth(self, width): return width #----------------------------------------------------------------------------------------------- # BUTTON FUNCTIONS #----------------------------------------------------------------------------------------------- def selectWorm(self): self.pathDial = QtGui.QFileDialog.getExistingDirectory(self, 'Select a folder', 'Y:\\Images') self.worm = self.pathDial.split("\\")[-1] self.path = self.pathDial[:-len(self.worm)] self.setWindowTitle('Seam Cells Analysis - ' + self.pathDial) if 'downsized' not in self.worm: QtGui.QMessageBox.about(self,'Warning!','These are not downsized (512x512) images! Convert the images first!') return # print(path + worm + '\\z*.txt') self.fList = {} if os.path.isfile(self.path + self.worm + '\\z001_488nm.tif'): self.fList['488nm'] = glob.glob(self.path + self.worm + '\\z*488nm.tif') self.fList['488nm'].sort() if os.path.isfile(self.path + self.worm + '\\z001_561nm.tif'): self.fList['561nm'] = glob.glob(self.path + self.worm + '\\z*561nm.tif') self.fList['561nm'].sort() if os.path.isfile(self.path + self.worm + '\\z001_CoolLED.tif'): self.fList['CoolLED'] = glob.glob(self.path + self.worm + '\\z*CoolLED.tif') self.fList['CoolLED'].sort() self.fMetaList = glob.glob(self.path + self.worm + '\\z*.txt') self.channel = list(self.fList.keys())[0] self.tp.setMaximum(len(self.fList[self.channel])-1) if not os.path.isfile( self.path + '\\worm' + self.worm.split('_')[0] + '.pickle' ): create_worm( self.path, self.worm.split('_')[0], 40, len(self.fList[self.channel]) - self.hatch.value(), self.hatch.value()+1 ) self.df = pickle.load( open(self.path + '\\worm' + self.worm.split('_')[0] + '.pickle','rb') ) # if there are more timepoints, add the body rows to the dataframe if np.max(self.df.tidx)-np.min(self.df.tidx) < len(self.fMetaList): times = extract_times( self.path+'\\'+self.worm, -np.min(self.df.tidx) )[int(np.max(self.df.tidx)-np.min(self.df.tidx)+1):] df1 = pd.DataFrame( { 'rowtype': 'body', 'tidx': np.arange(np.max(self.df.tidx)+1,len(self.fMetaList)+np.min(self.df.tidx)), 'times': times, 'outline': np.nan, 'spline': np.nan} ) df1.outline = df1.outline.astype(object) df1.spline = df1.spline.astype(object) for idx, row in df1.iterrows(): df1.outline.values[idx] = np.array([[np.nan,np.nan,np.nan]]) df1.spline.values[idx] = np.array([[np.nan,np.nan,np.nan]]) self.df = pd.concat([self.df,df1]).sort(['tidx','rowtype','cside','cXpos']).reset_index(drop=True) self.hatch.setValue(-np.min(self.df.tidx)) self.tp.setValue( self.hatch.value() ) self.loadNewStack() # self.pathDial.show() self.setFocus() def saveData(self): pickle.dump( self.df, open(self.path+'\\worm'+self.worm.split('_')[0]+'.pickle','wb'), protocol=2 ) def loadNewStack(self): # print(self.fList['gfp'][self.tp.value()]) self.stacks = {} for key in self.fList.keys(): self.stacks[key] = loadstack(self.fList[key][self.tp.value()]) self.scaleFactor = 2048 / self.stacks[self.channel][0].shape[0] self.stacksStraight = {} self.straightFile = self.path+self.worm.split('_')[0]+'_straighten\\straight%.3d_%s.tif'%(self.tp.value()+1,self.channel) if os.path.isfile(self.straightFile): for key in self.fList.keys(): self.stacksStraight[key] = loadstack(self.path+self.worm.split('_')[0]+'_straighten\\straight%.3d_%s.tif'%(self.tp.value()+1,key)) # print(self.stacks.keys(), self.stacksStraight) self.sl.setMaximum(self.stacks[self.channel].shape[0]-1) self.setBCslidersMinMax() self.updateTable() self.updateAllCanvas() def updateAllCanvas(self): self.updateRadioBtn() self.updateCanvas1() self.updateCanvas2() self.updateCanvas3() def updateTidxDataFrame(self): times = extract_times(self.path+self.worm, self.hatch.value()+1) self.df.ix[self.df.rowtype=='body','tidx'] = np.arange(len(times))-self.hatch.value() self.df.ix[self.df.rowtype=='body','times'] = times def radioClicked(self): if self._488nmBtn.isChecked(): if '488nm' in self.fList.keys(): self.channel = '488nm' else: QtGui.QMessageBox.about(self, 'Warning', 'No 488nm channel!') elif self._561nmBtn.isChecked(): if '561nm' in self.fList.keys(): self.channel = '561nm' else: QtGui.QMessageBox.about(self, 'Warning', 'No 561nm channel!') elif self.CoolLEDBtn.isChecked(): if 'CoolLED' in self.fList.keys(): self.channel = 'CoolLED' else: QtGui.QMessageBox.about(self, 'Warning', 'No CoolLED channel!') self.setBCslidersMinMax() self.resetBC() self.setFocus() self.updateAllCanvas() def automaticOutline(self): tp = self.tp.value() sl = self.sl.value() rowmask = self.df['rowtype']=='body' outline = self.df.ix[ rowmask, 'outline' ].values spline = self.df.ix[ rowmask, 'spline' ].values for idx, o in enumerate( outline ): if len(o) == 2: stack = loadstack( self.fList['488nm'][idx] ) # automatically create the outline outline[idx] = automatic_outline_michalis( stack, o, sl, idx ) # create the spline interpolation spline[idx] = interp_spline( outline[idx] ) # update the dataframe with the new outline and spline self.df.ix[ rowmask, 'outline' ] = outline self.df.ix[ rowmask, 'spline' ] = spline self.updateCanvas1() def straightenWorm(self): raw_worm = self.worm.split('_')[0] path = self.path+raw_worm if not os.path.exists(path): QtGui.QMessageBox.about(self, 'Warning! No raw data found in the standard directory. No straightening will be performed', 'Y:\\Images') return spline = [] for idx in self.df.ix[ pd.notnull(self.df.spline)].index: spline.append( np.array( [ self.df.spline.values[idx][:,0]*self.scaleFactor, self.df.spline.values[idx][:,1]*self.scaleFactor, self.df.spline.values[idx][:,2] ] ).T ) fList = {} if os.path.isfile(path + '\\z001_488nm.tif'): fList['488nm'] = glob.glob(path + '\\z*488nm.tif') fList['488nm'].sort() if os.path.isfile(path+'\\z001_561nm.tif'): fList['561nm'] = glob.glob(path + '\\z*561nm.tif') fList['561nm'].sort() if os.path.isfile(path + '\\z001_CoolLED.tif'): fList['CoolLED'] = glob.glob(path + '\\z*CoolLED.tif') fList['CoolLED'].sort() # print(25*self.scaleFactor) for key in list( fList.keys() ): straighten( path, fList[str(key)], spline ) self.loadNewStack() self.updateCanvas2() self.canvas1.setFocus() def automaticCellNames(self): tidxNow = self.tp.value() - self.hatch.value() tidxNowMask = self.df.tidx == tidxNow cellMask = self.df.rowtype == 'cell' sideMask = self.df.cside == self.side # find the previous timepoint with labeled cells if np.sum( sideMask & (self.df.tidx < tidxNow) ) == 0: QtGui.QMessageBox.about(self, 'Warning', 'No cells are labeled in the %s side yet!' % self.side) return else: tidxPrev = np.max( self.df.ix[ sideMask & ( self.df.tidx<tidxNow ), 'tidx' ] ) # print(self.side, tidxPrev) tidxPrevMask = self.df['tidx'] == tidxPrev # filter the cells from the dataframe cellsNow = self.df[cellMask&tidxNowMask&sideMask] cellsPrev = self.df[cellMask&tidxPrevMask&sideMask] # find the relative positions and sort the cells wNowLen = np.max(cellsNow.cXpos)-np.min(cellsNow.cXpos) wPrevLen = np.max(cellsPrev.cXpos)-np.min(cellsPrev.cXpos) for idx in cellsNow.index: cellsNow.ix[idx,'relPos'] = ( cellsNow.ix[idx,'cXpos'] - np.min(cellsNow.cXpos) )/wNowLen for idx in cellsPrev.index: cellsPrev.ix[idx,'relPos'] = ( cellsPrev.ix[idx,'cXpos'] - np.min(cellsPrev.cXpos) )/wPrevLen cellsNow = cellsNow.sort(['relPos']) cellsPrev = cellsPrev.sort(['relPos']) # assign all the names according to closest cell in previous timepoint # if there are more cells now, two cells will have the same name for idx, cell in cellsNow.iterrows(): closestCell = self.closestCell( cell, cellsPrev ) cellsNow.ix[idx,'cname'] = closestCell.cname.values[0] # if more cells, a division happened. if len(cellsNow) > len(cellsPrev): # print('I am f****d...') # for each cell (but first the most left one), find the first one to the left for idx, cell in cellsNow.drop( np.min(cellsNow.index.values) ).iterrows(): cellToTheLeft = cellsNow.ix[ idx - 1 ] # if it has the same name, a division happened if cellsNow.ix[idx,'cname'] == cellToTheLeft.cname: cellsNow.ix[idx-1,'cname'] += 'a' cellsNow.ix[idx,'cname'] += 'p' # update dataframe for idx, cell in cellsNow.iterrows(): self.df.ix[idx,'cname'] = cell.cname # update canvas self.updateTable() self.updateCanvas2() #----------------------------------------------------------------------------------------------- # 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( self.tp, +1 ) if event.key() == QtCore.Qt.Key_Left: self.changeSpaceTime( self.tp, -1 ) # change slice if event.key() == QtCore.Qt.Key_Up: self.changeSpaceTime( self.sl, +1 ) if event.key() == QtCore.Qt.Key_Down: self.changeSpaceTime( self.sl, -1 ) # change channel if event.key() == QtCore.Qt.Key_Space: currentidx = list(self.fList.keys()).index(self.channel) nextidx = np.mod(currentidx+1,len(self.fList.keys())) self.channel = list(self.fList.keys())[nextidx] self.setBCslidersMinMax() self.resetBC() self.updateAllCanvas() # key press on straighten worm if self.canvas2.underMouse(): self.onKeyPressOnCanvas2(event) self.setFocus() def wheelEvent(self,event): if any([ self.canvas1.underMouse(), self.canvas2.underMouse(), self.canvas3.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 onKeyPressOnCanvas2(self, event): # print(event.key()) cellsname = [ QtCore.Qt.Key_A, QtCore.Qt.Key_B, QtCore.Qt.Key_C, QtCore.Qt.Key_1, QtCore.Qt.Key_2, QtCore.Qt.Key_3, QtCore.Qt.Key_4, QtCore.Qt.Key_5, QtCore.Qt.Key_6, QtCore.Qt.Key_T ] cellspos = [ QtCore.Qt.Key_Q, QtCore.Qt.Key_W ] # find the position of the cursor relative to the image in pixel imgshape = self.stacksStraight[self.channel][self.sl.value()].shape arimg = imgshape[1]/imgshape[0] canshape = self.canvas2.size() arcan = canshape.width()/canshape.height() cf = 1 if arimg>arcan: cf = imgshape[1]/canshape.width() origin = ( canshape.height()*cf - imgshape[0] ) / 2. origin = np.array( [ 0, ( canshape.width()*cf - imgshape[1] ) / 2. ] ) else: cf = imgshape[0]/canshape.height() origin = np.array( [ ( canshape.width()*cf - imgshape[1] ) / 2., 0 ] ) refpos = self.canvas2.mapFromGlobal(QtGui.QCursor.pos()) refpos = np.array([refpos.x(),refpos.y()])*cf - origin refpos = np.append(refpos,self.sl.value()) # find the closest cell to the cursor bodymask = self.df['rowtype']=='body' cellmask = self.df['rowtype']=='cell' tpmask = self.df['tidx'] == (self.tp.value()-self.hatch.value()) idx, cell = closer_cell( refpos, self.df[ cellmask & tpmask ], self.fMetaList[self.tp.value()] ) if any( [ event.key() == cn for cn in cellsname ] ): self.df.ix[ idx, 'cname' ] = QtGui.QKeySequence(event.key()).toString().lower() + '.' elif any( [ event.key() == cp for cp in cellspos ] ): if event.key() == cellspos[0]: self.df.ix[ idx, 'cname' ] += 'a' elif event.key() == cellspos[1]: self.df.ix[ idx, 'cname' ] += 'p' elif event.key() == QtCore.Qt.Key_Backspace: self.df.ix[ idx, 'cname' ] = self.df.ix[ idx, 'cname' ][:-1] elif event.key() == QtCore.Qt.Key_I: sideLmask = self.df['cside']=='L' self.df.ix[ tpmask & cellmask & sideLmask,'cside'] = 'R' sideRmask = self.df['cside']=='R' self.df.ix[ tpmask & cellmask & sideRmask,'cside'] = 'L' elif event.key() == QtCore.Qt.Key_U: if self.df.ix[idx,'cside']=='L': self.df.ix[ idx,'cside'] = 'R' elif self.df.ix[idx,'cside']=='R': self.df.ix[ idx,'cside'] = 'L' self.df = self.df.sort(['tidx','rowtype','cside','cXpos']).reset_index(drop=True) self.updateTable() self.updateAllCanvas() def onMouseClickOnCanvas1(self, event): # print(event.button,event.xdata,event.ydata) tp = self.tp.value() sl = self.sl.value() rowmask = self.df['rowtype']=='body' outline = self.df.ix[ rowmask, 'outline' ].values spline = self.df.ix[ rowmask, 'spline' ].values x = event.xdata y = event.ydata # left button: add a point to the outline if event.button == 1: if not np.all(np.isnan(outline[tp])): idx = find_closest_point( [ x, y ], outline[tp] ) else: idx = 0 outline[tp] = np.insert( outline[tp], idx+1, [ x, y, sl ], axis=0 ) # if the first line is still full of nan, remove it if np.all(np.isnan(outline[tp][0])): outline[tp] = np.delete(outline[tp],0,axis=0) # right button: remove the closest point from the outline elif event.button == 3 and len(outline[tp]) > 0: idx = find_closest_point([x,y],outline[tp]) if outline[tp].shape[0]!=1: outline[tp] = np.delete( outline[tp], idx, axis=0 ) else: outline[tp] = np.array([[np.nan,np.nan,np.nan]]) # update the dataframe with the new outline and spline spline[tp] = interp_spline( outline[tp] ) self.df.ix[ rowmask, 'outline' ] = outline self.df.ix[ rowmask, 'spline' ] = spline self.updateCanvas1() self.setFocus() def onMouseClickOnCanvas2(self, event): refpos = np.array( [ event.xdata, event.ydata, self.sl.value() ] ) # print(refpos) bodymask = self.df['rowtype']=='body' cellmask = self.df['rowtype']=='cell' tpmask = self.df['tidx'] == (self.tp.value()-self.hatch.value()) # print( 'mouse button pressed:', event.button, 'at pos:', refpos ) if all( refpos[:-1] ) > 0: if event.button == 1: # create an empty cell: the only entries are tidx, times, xyzpos, side newcell = create_cell( refpos, self.df[ bodymask & tpmask ], self.side ) self.df = pd.concat( [ self.df, newcell ] ) elif event.button == 3: if any( self.df[tpmask].rowtype == 'cell' ): idx, cell = closer_cell( refpos, self.df[ cellmask & tpmask ], self.fMetaList[self.tp.value()] ) self.df = self.df.drop([idx]) elif event.button == 2: if self.side == 'L': self.side = 'R' elif self.side == 'R': self.side = 'L' self.sideLbl.setText(self.lbltxt % self.side) self.df = self.df.sort(['tidx','rowtype','cside','cXpos']).reset_index(drop=True) self.updateCanvas2() self.updateTable() self.setFocus() #----------------------------------------------------------------------------------------------- # UTILS #----------------------------------------------------------------------------------------------- def updateRadioBtn(self): if self.channel == '488nm': self._488nmBtn.setChecked(True) elif self.channel == '561nm': self._561nmBtn.setChecked(True) elif self.channel == 'CoolLED': self.CoolLEDBtn.setChecked(True) self.setFocus() def setBCslidersMinMax(self): self.sld1.setMaximum(np.max(self.stacks[self.channel])) self.sld1.setMinimum(np.min(self.stacks[self.channel])) self.sld2.setMaximum(np.max(self.stacks[self.channel])) self.sld2.setMinimum(np.min(self.stacks[self.channel])) def resetBC(self): self.sld1.setValue(np.min(self.stacks[self.channel])) self.sld2.setValue(np.max(self.stacks[self.channel])) def updateCanvas1(self): rowmask = self.df['rowtype']=='body' tidxmask = self.df['tidx']==(self.tp.value()-self.hatch.value()) # extract and rescale the outline and spline outline = np.copy( self.df.ix[ rowmask & tidxmask, 'outline' ].values[0] ) spline = np.copy( self.df.ix[ rowmask & tidxmask, 'spline' ].values[0] ) # plot the image self.ax1.cla() imgplot = self.ax1.imshow(self.stacks[self.channel][self.sl.value()], cmap = 'gray') # remove the white borders and plot outline and spline self.ax1.autoscale(False) self.ax1.axis('Off') self.fig1.subplots_adjust(left=0., right=1., top=1., bottom=0.) # change brightness and contrast self.sld1.setValue(np.min([self.sld1.value(),self.sld2.value()])) self.sld2.setValue(np.max([self.sld1.value(),self.sld2.value()])) imgplot.set_clim(self.sld1.value(), self.sld2.value()) # print(outline, spline) self.ax1.plot( outline[:,0], outline[:,1], 'o', color='red', ms=6, mew=1, alpha=.5, lw = 1 ) self.ax1.plot( spline[:,0], spline[:,1], '-', color='yellow', lw = 1 ) # redraw the canvas self.canvas1.draw() self.setFocus() def updateCanvas2(self): # plot the image self.ax2.cla() if not os.path.isfile(self.straightFile): self.fig2.clf() self.fig2.subplots_adjust(left=0., right=1., top=1., bottom=0.) self.ax2 = self.fig2.add_subplot(111) self.canvas2.draw() return imgplot = self.ax2.imshow(self.stacksStraight[self.channel][self.sl.value()], cmap = 'gray') imgplot.set_clim(self.sld1.value(), self.sld2.value()) # remove the white borders self.ax2.autoscale(False) self.ax2.axis('Off') self.fig2.subplots_adjust(left=0., right=1., top=1., bottom=0.) # cell text on the image tpmask = self.df['tidx'] == (self.tp.value() - self.hatch.value()) cellmask = self.df['rowtype'] == 'cell' for idx, cell in self.df[tpmask & cellmask].iterrows(): if cell.cZpos == self.sl.value(): clabel = str(cell.cname) + ' ' + str(cell.cside) self.ax2.text( cell.cXpos, cell.cYpos + 10, clabel, color='red', size='medium', alpha=.8, rotation=90) self.ax2.plot( cell.cXpos, cell.cYpos, 'x', color='red', alpha = .8 ) # redraw the canvas self.canvas2.draw() self.setFocus() def updateCanvas3(self): # print('updating canvas 3') tidxNow = self.tp.value() - self.hatch.value() tidxNowMask = self.df.tidx == tidxNow cellMask = self.df.rowtype == 'cell' sideMask = self.df.cside == self.side # find the previous timepoint with labeled cells if np.sum( sideMask & (self.df.tidx < tidxNow) ) == 0: self.fig3.clf() self.fig3.subplots_adjust(left=0., right=1., top=1., bottom=0.) self.ax3 = self.fig3.add_subplot(111) self.canvas3.draw() return else: tidxPrev = np.max( self.df.ix[ sideMask & ( self.df.tidx<tidxNow ), 'tidx' ] ) tidxPrevMask = self.df['tidx'] == tidxPrev # load images prevstacksStraight = {} for key in self.fList.keys(): prevstacksStraight[key] = loadstack(self.path+self.worm.split('_')[0]+'_straighten\\straight%.3d_%s.tif'%(tidxPrev+self.hatch.value()+1,key)) # plot the image self.ax3.cla() imgplot = self.ax3.imshow(prevstacksStraight[self.channel][self.sl.value()], cmap = 'gray') imgplot.set_clim(self.sld1.value(), self.sld2.value()) # remove the white borders self.ax3.autoscale(False) self.ax3.axis('Off') self.fig3.subplots_adjust(left=0., right=1., top=1., bottom=0.) # cell text on the image for idx, cell in self.df[tidxPrevMask & cellMask].iterrows(): if cell.cZpos == self.sl.value(): clabel = str(cell.cname) + ' ' + str(cell.cside) self.ax3.text( cell.cXpos, cell.cYpos + 10, clabel, color='red', size='small', alpha=.8, rotation=90) self.ax3.plot( cell.cXpos, cell.cYpos, 'x', color='red', alpha = .8 ) # redraw the canvas self.canvas3.draw() self.setFocus() def closestCell(self,cell,clist): dist = np.abs( clist.relPos - cell.relPos ) return clist[ dist == np.min(dist) ] def changeSpaceTime(self, whatToChange, increment): whatToChange.setValue( whatToChange.value() + increment ) def rescaledImage(self, img): Nbig = img.shape[0] Nsmall = img.shape[0]/self.scaleFactor return img.reshape([Nsmall, Nbig/Nsmall, Nsmall, Nbig/Nsmall]).mean(3).mean(1) def updateTable(self): self.cellTbl.clear() tidxNow = self.tp.value() - self.hatch.value() tidxNowMask = self.df.tidx == tidxNow cellMask = self.df.rowtype == 'cell' cellsNow = self.df[cellMask&tidxNowMask] cellsPrev = pd.DataFrame({}) if len(cellsNow) > 0: sideMask = self.df.cside == cellsNow.cside.values[0] # find the previous timepoint with labeled cells if np.sum( sideMask & (self.df.tidx < tidxNow) ) == 0: cellsPrev = pd.DataFrame({}) else: tidxPrev = np.max( self.df.ix[ sideMask & ( self.df.tidx<tidxNow ), 'tidx' ] ) tidxPrevMask = self.df['tidx'] == tidxPrev cellsPrev = self.df[cellMask&tidxPrevMask] horHeaders = ['tidx','times','cell name','cell side','cellXpos','cellYpos','cellZpos','-', 'tidx','times','cell name','cell side','cellXpos','cellYpos','cellZpos'] self.cellTbl.setColumnCount(len(horHeaders)) self.cellTbl.setRowCount(np.max([len(cellsNow),len(cellsPrev)])) self.cellTbl.setHorizontalHeaderLabels(horHeaders) self.cellTbl.horizontalHeader().setResizeMode(QtGui.QHeaderView.ResizeToContents) self.cellTbl.verticalHeader().setResizeMode(QtGui.QHeaderView.ResizeToContents) row = 0 for idx, cell in cellsNow.iterrows(): self.cellTbl.setItem(row,0,QtGui.QTableWidgetItem(str(int(cell.tidx)),.0001)) self.cellTbl.setItem(row,1,QtGui.QTableWidgetItem(str('%.2f'%cell.times))) self.cellTbl.setItem(row,2,QtGui.QTableWidgetItem(str(cell.cname))) self.cellTbl.setItem(row,3,QtGui.QTableWidgetItem(cell.cside)) self.cellTbl.setItem(row,4,QtGui.QTableWidgetItem(str(int(cell.cXpos)))) self.cellTbl.setItem(row,5,QtGui.QTableWidgetItem(str(int(cell.cYpos)))) self.cellTbl.setItem(row,6,QtGui.QTableWidgetItem(str(int(cell.cZpos)))) row += 1 row = 0 for idx, cell in cellsPrev.iterrows(): self.cellTbl.setItem(row,8,QtGui.QTableWidgetItem(str(int(cell.tidx)))) self.cellTbl.setItem(row,9,QtGui.QTableWidgetItem(str('%.2f'%cell.times))) self.cellTbl.setItem(row,10,QtGui.QTableWidgetItem(str(cell.cname))) self.cellTbl.setItem(row,11,QtGui.QTableWidgetItem(cell.cside)) self.cellTbl.setItem(row,12,QtGui.QTableWidgetItem(str(int(cell.cXpos)))) self.cellTbl.setItem(row,13,QtGui.QTableWidgetItem(str(int(cell.cYpos)))) self.cellTbl.setItem(row,14,QtGui.QTableWidgetItem(str(int(cell.cZpos)))) row += 1 self.setFocus()
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 ) # 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.canvas2) self.setFocus() self.show() # BIND BUTTONS TO FUNCTIONS loadBtn.clicked.connect(self.selectWorm) saveBtn.clicked.connect(self.saveData) self.tp.valueChanged.connect(self.loadNewStack) self.sl.valueChanged.connect(self.updateAllCanvas) self.sld1.valueChanged.connect(self.updateAllCanvas) self.sld2.valueChanged.connect(self.updateAllCanvas) self._488nmBtn.toggled.connect(self.radioClicked) self._561nmBtn.toggled.connect(self.radioClicked) self.CoolLEDBtn.toggled.connect(self.radioClicked) self.fig1.canvas.mpl_connect('button_press_event',self.onMouseClickOnCanvas1) self.fig1.canvas.mpl_connect('scroll_event',self.wheelEvent) #----------------------------------------------------------------------------------------------- # FORMATTING THE WINDOW #----------------------------------------------------------------------------------------------- def center(self): qr = self.frameGeometry() cp = QtGui.QDesktopWidget().availableGeometry().center() qr.moveCenter(cp) self.move(qr.topLeft()) def HLine(self): toto = QtGui.QFrame() toto.setFrameShape(QtGui.QFrame.HLine) toto.setFrameShadow(QtGui.QFrame.Sunken) return toto def VLine(self): toto = QtGui.QFrame() toto.setFrameShape(QtGui.QFrame.VLine) toto.setFrameShadow(QtGui.QFrame.Sunken) return toto def heightForWidth(self, width): return width #----------------------------------------------------------------------------------------------- # BUTTON FUNCTIONS #----------------------------------------------------------------------------------------------- def selectWorm(self): ### store the folders self.pathDial = QtGui.QFileDialog.getExistingDirectory(self, 'Select a folder', 'Y:\\Images') self.worm = self.pathDial.split("\\")[-1].split('_')[0] self.path = os.path.dirname( self.pathDial ) self.setWindowTitle('Mark Cells - ' + self.pathDial) ### give error message if there is no CoolLED movie in the selected folder if not os.path.isfile( os.path.join( self.pathDial, 'CoolLED_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( self.path, self.worm + '_01params.pickle' ) self.timesDF = load_data_frame( self.path, self.worm + '_01times.pickle' ) self.gpDF = load_data_frame( 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( self.path, self.worm + '_04cellPos.pickle' ) else: self.cellPosDF = create_cell_pos( self.timesDF, self.cellNames ) ### load all movies (without timestamps, we will add it later on) self.LEDmovie = load_stack( os.path.join( self.pathDial, 'CoolLED_movie.tif' ) ) ### set the timepoint to the hatching time self.tp.setMinimum(np.min(self.timesDF.tidxRel)) self.tp.setMaximum(np.max(self.timesDF.tidxRel)) self.tp.setValue( 0 ) ### extract current cells already labeled self.currentCells = extract_current_cell_pos( self.cellPosDF, self.tp.value() ) # 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] ### update the text of the fileName self.fName.setText( self.timesDF.ix[ self.timesDF.tidxRel == self.tp.value(), 'fName' ].values[0]) self.loadNewStack() # self.pathDial.show() self.updateAllCanvas() self.setFocus() def loadNewStack(self): # print(self.fList['gfp'][self.tp.value()]) tRow = self.timesDF.ix[ self.timesDF.tidxRel == self.tp.value() ].squeeze() print( 'Loading... ', self.pathDial, tRow.fName ) # load all the available stacks self.stacks = {} for ch in self.channels: fileName = os.path.join( self.pathDial, tRow.fName + ch + '.tif') if os.path.isfile( fileName ): self.stacks[ch] = load_stack( fileName ) if len( self.stacks.keys() ) > 0: # print(self.stacks.keys(), self.stacksStraight) self.sl.setMaximum(self.stacks[self.currentChannel].shape[0]-1) self.setBCslidersMinMax() ### update the text of the fileName self.fName.setText( self.timesDF.ix[ self.timesDF.tidxRel == self.tp.value(), 'fName' ].values[0]) ### extract current cells already labeled self.currentCells = extract_current_cell_pos( self.cellPosDF, self.tp.value() ) # self.updateTable() self.updateAllCanvas() 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 updateAllCanvas(self): self.updateRadioBtn() self.updateCanvas1() self.updateCanvas2() def radioClicked(self): if self._488nmBtn.isChecked(): if '488nm' in self.channels: self.currentChannel = '488nm' else: QtGui.QMessageBox.about(self, 'Warning', 'No 488nm channel!') elif self._561nmBtn.isChecked(): if '561nm' in self.channels: self.currentChannel = '561nm' else: QtGui.QMessageBox.about(self, 'Warning', 'No 561nm channel!') elif self.CoolLEDBtn.isChecked(): if 'CoolLED' in self.channels: self.currentChannel = 'CoolLED' else: QtGui.QMessageBox.about(self, 'Warning', 'No CoolLED channel!') self.setBCslidersMinMax() self.resetBC() self.setFocus() self.updateAllCanvas() #----------------------------------------------------------------------------------------------- # 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 ) # 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] self.updateCanvas1() self.setFocus() def onMouseClickOnCanvas1(self, event): refpos = np.array( [ event.xdata, event.ydata, self.sl.value() ] ) if event.button == 1: # 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 ] ) elif event.button == 3: # 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() #----------------------------------------------------------------------------------------------- # UTILS #----------------------------------------------------------------------------------------------- def updateRadioBtn(self): if self.currentChannel == '488nm': self._488nmBtn.setChecked(True) elif self.currentChannel == '561nm': self._561nmBtn.setChecked(True) elif self.currentChannel == 'CoolLED': self.CoolLEDBtn.setChecked(True) self.setFocus() def setBCslidersMinMax(self): self.sld1.setMaximum(np.max(self.stacks[self.currentChannel])) self.sld1.setMinimum(np.min(self.stacks[self.currentChannel])) self.sld2.setMaximum(np.max(self.stacks[self.currentChannel])) self.sld2.setMinimum(np.min(self.stacks[self.currentChannel])) def resetBC(self): self.sld1.setValue(np.min(self.stacks[self.currentChannel])) self.sld2.setValue(np.max(self.stacks[self.currentChannel])) def updateCanvas1(self): 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() if len( self.stacks.keys() ) == 0: # if no images are found, leave the canvas empty return # plot the image self.ax1.cla() imgplot = self.ax1.imshow( self.stacks[self.currentChannel][self.sl.value()], cmap = 'gray' ) # remove the white borders and plot outline and spline self.ax1.autoscale(False) self.ax1.axis('Off') self.fig1.subplots_adjust(left=0., right=1., top=1., bottom=0.) # cell text on the image for idx, cell in self.currentCells.iterrows(): if cell.Z == self.sl.value(): self.ax1.text( cell.X, cell.Y + 18, cell.cname, color='red', size='medium', alpha=.8, rotation=0 ) self.ax1.plot( cell.X, cell.Y, 'o', color='red', alpha = .8, mew = 0 ) # change brightness and contrast self.sld1.setValue(np.min([self.sld1.value(),self.sld2.value()])) self.sld2.setValue(np.max([self.sld1.value(),self.sld2.value()])) imgplot.set_clim(self.sld1.value(), self.sld2.value()) # redraw the canvas self.canvas1.draw() self.setFocus() def updateCanvas2(self): # plot the image self.ax2.cla() imgplot = self.ax2.imshow( self.LEDmovie[ self.tp.value() + self.hatchingtidx ], cmap = 'gray' ) # 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 = extract_pos( self.gpDF.ix[ self.gpDF.tidx == self.tp.value() ].squeeze() ) / self.compression self.ax2.plot( gonadPos[0], gonadPos[1], 'o', color='red', ms=10, mew=0, alpha=.5, lw = 0 ) # print time # print(self.timesDF.ix[ self.timesDF.tidxRel == self.tp.value(), 'timesRel' ]) self.ax2.text( 5, 25, '%.2f' % self.timesDF.ix[ self.timesDF.tidxRel == self.tp.value(), 'timesRel' ].values[0], color = 'red' ) # redraw the canvas self.canvas2.draw() self.setFocus() def checkConsistencyCellNames( self ): ### check consistency of cell names tp = self.tp.value() # 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': # before changinf timepoint, print labeled cells and check if they are OK print( self.currentCells ) # if they are OK (and not going to negative times), change timepoint if self.checkConsistencyCellNames() and ( self.tp.value() + increment ) >= 0 : self.tp.setValue( self.tp.value() + increment ) if whatToChange == 'space': self.sl.setValue( self.sl.value() + increment )