def __init__(self, transactions, max_price, marker): QtGui.QWidget.__init__(self) layout = QtGui.QVBoxLayout() self.setLayout(layout) figure = plt.Figure(figsize=(6, 3), facecolor="white") canvas = FigureCanvas(figure) canvas.setFixedSize(550, 300) layout.addWidget(canvas) graph = figure.add_subplot(111) if transactions: graph.plot([t["seconds_from_start"] for t in transactions], [t["prix"] for t in transactions], color="k", ls="", marker=marker) graph.set_xlabel("Temps (secondes)") graph.set_xlim( -5, pms.MARKET_TIME.minute * 60 + pms.MARKET_TIME.second + 5) graph.set_xticks( range(0, pms.MARKET_TIME.minute * 60 + pms.MARKET_TIME.second + 1, 10)) graph.set_xticklabels([ "{}".format(i) if i % 30 == 0 else "" for i in range( 0, pms.MARKET_TIME.minute * 60 + pms.MARKET_TIME.second + 1, 10) ]) graph.set_ylabel("Prix") graph.set_ylim(-0.25, max_price + 0.25) graph.set_yticks(np.arange(0, max_price + 0.1, 0.25)) graph.grid(ls="--") figure.tight_layout()
def __init__(self, streamline, parent=None, width=5, height=4, dpi=50, subs=1): self.fig = Figure(figsize=(width - 50, height), dpi=dpi) pos = 0 self.sl = streamline self.subs = subs self.cpunum = self.sl.mDevice.cpu_num self.gpu_pos = 0 self.fps_pos = 0 self.temp_pos = 0 self.axes = [] for i in range(self.cpunum): self.axes.append(self.fig.add_subplot(self.subs, 1, i + 1)) # For CPU CORES # We want the axes cleared every time plot() is called self.axes[i].set_title("CPU" + str(i)) # self.axes[i].set_xticks([]) # not show x self.axes[i].set_xlim(0, 20000) self.axes[i].set_ylim(0, 2500) if self.sl.mDevice.show_gpu == 1: self.gpu_pos = pos self.axes.append(self.fig.add_subplot(self.subs, 1, self.cpunum + pos + 1)) # FOR GPU self.axes[self.cpunum + self.gpu_pos].set_title("GPU") self.axes[self.cpunum + self.gpu_pos].set_xlim(0, 20000) self.axes[self.cpunum + self.gpu_pos].set_ylim(0, 850) pos += 1 if self.sl.mDevice.show_fps == 1: self.fps_pos = pos self.axes.append(self.fig.add_subplot(self.subs, 1, self.cpunum + self.fps_pos + 1)) # FOR FPS self.axes[self.cpunum + self.fps_pos].set_title("FPS") self.axes[self.cpunum + self.fps_pos].set_xlim(0, 20000) self.axes[self.cpunum + self.fps_pos].set_ylim(0, 100) pos += 1 if self.sl.mDevice.show_temp == 1: self.temp_pos = pos self.axes.append(self.fig.add_subplot(self.subs, 1, self.cpunum + self.temp_pos + 1)) # FOR CPU TEMP self.axes[self.cpunum + self.temp_pos].set_title("CPU Temperature") self.axes[self.cpunum + self.temp_pos].set_xlim(0, 20000) self.axes[self.cpunum + self.temp_pos].set_ylim(0, 100) self.axes.append(self.fig.add_subplot(self.subs, 1, self.cpunum + self.temp_pos + 2)) # FOR BOARD TEMP self.axes[self.cpunum + self.temp_pos + 1].set_title("Board Temperature") self.axes[self.cpunum + self.temp_pos + 1].set_xlim(0, 20000) self.axes[self.cpunum + self.temp_pos + 1].set_ylim(0, 100) self.fig.set_tight_layout(True) self.compute_initial_figure() FigureCanvas.__init__(self, self.fig) self.setParent(parent) FigureCanvas.setFixedSize(self, width - 50, subs * 100) FigureCanvas.setSizePolicy(self, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) FigureCanvas.updateGeometry(self)
def _create_plot(self): dpi = plt.rcParams['figure.dpi'] figsize = (self._plot_width / dpi, self._plot_height / dpi) figure = plt.figure(frameon=False, figsize=figsize) axes = figure.add_subplot(111) canvas = FigureCanvas(figure) canvas.setFocusPolicy(QtCore.Qt.ClickFocus) canvas.setFixedSize(self._plot_width, self._plot_height) canvas.setStyleSheet("background: transparent") return axes, canvas
class GUI(QtGui.QWidget): def __init__(self): super(GUI, self).__init__() self.setWindowTitle('Mark Gonad') 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) directionLbl = QtGui.QLabel('Time direction:') self.tDirection = QtGui.QComboBox(self) self.tDirection.addItem('Forward') self.tDirection.addItem('Backward') 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(directionLbl, 1, 0) Col1.addWidget(self.tDirection, 1, 1) Col1.addWidget(fNameLbl, 2, 0) #, 1, 1, Qt.AlignTop) Col1.addWidget(self.fName, 2, 1) #, 1, 1, Qt.AlignTop) 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) self.setFocus() self.show() # BIND BUTTONS TO FUNCTIONS loadBtn.clicked.connect(self.selectWorm) saveBtn.clicked.connect(self.saveData) self.tp.valueChanged.connect(self.updateCanvas1) self.sld1.valueChanged.connect(self.updateCanvas1) self.sld2.valueChanged.connect(self.updateCanvas1) 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 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', 'C:\\Users\\Nicola\\Dropbox\\PhD\\Codes\\test') #Y:\\Images') self.worm = self.pathDial.split("\\")[-1].split('_')[0] self.path = os.path.dirname(self.pathDial) self.setWindowTitle('Mark gonad - ' + 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 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')) print(list(self.channels.keys())[0]) if 'CoolLED' in self.channels.keys(): self.currentChannel = 'CoolLED' else: self.currentChannel = list(self.channels.keys())[0] ### 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') # extract some info self.compression = self.paramsDF.compression self.hatchingtidx = int(self.paramsDF.tidxHatch) ### if the gonadPos pickle file already exists, load it, otherwise create a blank one if os.path.isfile( os.path.join(self.path, self.worm + '_02gonadPos.pickle')): self.gpDF = load_data_frame(self.path, self.worm + '_02gonadPos.pickle') else: self.gpDF = create_gonad_pos(self.timesDF) tp = 0 ### update the text of the fileName self.fName.setText(self.timesDF.ix[self.timesDF.tidxRel == 0, 'fName'].values[0]) ### initialize the figure.astype(np.uint8) self.initializeCanvas1() ### set the timepoint to the hatching time self.tp.setMinimum(np.min(self.timesDF.tidxRel)) self.tp.setMaximum(np.max(self.timesDF.tidxRel)) if tp != self.tp.value(): self.tp.setValue(0) self.setBCslidersMinMax() # this updates the canvas, only once! self.CoolLEDBtn.setChecked( True) # this also updates the canvas, only once! self.setFocus() def saveData(self): save_data_frame(self.gpDF, self.path, self.worm + '_02gonadPos.pickle') 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!') #----------------------------------------------------------------------------------------------- # 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()) 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 onMouseClickOnCanvas1(self, event): # print(event.button,event.xdata,event.ydata) x = event.xdata y = event.ydata if event.button == 1: # left button: add a point to the outline gonadPos = (np.array([x, y]) * self.compression).astype(np.uint16) if event.button == 3: gonadPos = [np.nan, np.nan] # update the dataframe with the new gonadPos self.gpDF.ix[self.gpDF.tidx == self.tp.value(), 'X'] = gonadPos[0] self.gpDF.ix[self.gpDF.tidx == self.tp.value(), 'Y'] = gonadPos[1] # move to next or previous timepoint depending on tDirection chosen if event.button == 1: if self.tDirection.currentText() == 'Forward': tStep = +1 else: tStep = -1 self.tp.setValue(self.tp.value() + tStep) self.fName.setText( self.timesDF.ix[self.timesDF.tidxRel == self.tp.value(), 'fName'].values[0]) if event.button == 3: self.updateCanvas1() # print( self.gpDF.ix[ self.gpDF.tidx == self.tp.value() ] ) self.setFocus() #----------------------------------------------------------------------------------------------- # UTILS #----------------------------------------------------------------------------------------------- def setBCslidersMinMax(self): self.sld1.setMaximum( np.max( [np.max(self.channels[key]) for key in self.channels.keys()])) self.sld1.setMinimum(0) self.sld2.setMaximum( np.max( [np.max(self.channels[key]) for key in self.channels.keys()])) self.sld2.setMinimum(0) def initializeCanvas1(self): print('initializing canvas1... ') # plot the image self.ax1.cla() size = 2048 / self.compression self.imgplot = self.ax1.imshow(np.zeros((size, size)), 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.) # print gonad position gonadPos = [np.nan, np.nan] self.pointsplot, = self.ax1.plot(gonadPos[0], gonadPos[1], 'o', color='w', ms=10, mew=0, alpha=.8, lw=0) # print gonad position gonadBox = np.array([[np.nan, np.nan, np.nan, np.nan], [np.nan, np.nan, np.nan, np.nan]]) self.boxplot, = self.ax1.plot(gonadPos[0], gonadPos[1], '--', color='w', alpha=.8, lw=2) # print time # print(self.timesDF.ix[ self.timesDF.tidxRel == self.tp.value(), 'timesRel' ]) self.textplot = self.ax1.text(5, 20, '--.--', color='white', fontsize=20) # redraw the canvas self.canvas1.draw() self.setFocus() def updateCanvas1(self): # print('updating canvas1... ') size = 2048 / self.compression self.fName.setText( self.timesDF.ix[self.timesDF.tidxRel == self.tp.value(), 'fName'].values[0]) # plot the image self.imgplot.set_data( self.channels[self.currentChannel][self.tp.value() + self.hatchingtidx]) # change brightness and contrast self.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.pointsplot.set_xdata(gonadPos[0]) self.pointsplot.set_ydata(gonadPos[1]) self.boxplot.set_xdata([ gonadPos[0] - size / 8., gonadPos[0] + size / 8., gonadPos[0] + size / 8., gonadPos[0] - size / 8., gonadPos[0] - size / 8. ]) self.boxplot.set_ydata([ gonadPos[1] - size / 8., gonadPos[1] - size / 8., gonadPos[1] + size / 8., gonadPos[1] + size / 8., gonadPos[1] - size / 8. ]) plt.draw() # plot box # 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.textplot.set_text( '%.2f' % (self.timesDF.ix[self.timesDF.tidxRel == self.tp.value(), 'timesRel'].values[0] - tpL2)) # redraw the canvas self.canvas1.draw() self.setFocus()
class GUI(QtGui.QWidget): def __init__(self): super(GUI, self).__init__() self.setWindowTitle( 'Outline 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:') zoomLbl = QtGui.QLabel('Zoom (imgPxl):') self.tp = QtGui.QSpinBox(self) self.tp.setValue(-5) self.tp.setMaximum(100000) self.sl = QtGui.QSpinBox(self) self.sl.setValue(0) self.sl.setMaximum(100000) self.fName = QtGui.QLabel('') self.zoom = QtGui.QSpinBox(self) self.zoom.setValue(50) self.zoom.setMinimum(10) self.zoom.setMaximum(150) self.zoom.setSingleStep(10) 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.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(zoomLbl, 3, 0) Col1.addWidget(self.zoom, 3, 1) Col1.addWidget(self._488nmBtn, 4, 0 ) Col1.addWidget(self._561nmBtn, 5, 0 ) Col1.addWidget(self.CoolLEDBtn, 6, 0 ) Col2.addWidget(self.sld1) Col2.addWidget(self.sld2) Col2.addWidget(self.canvas1) 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.zoom.valueChanged.connect(self.changeZoom) 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 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\\160129_MCHERRY_HLH2GFP_onHB101')#'Y:\\Images') self.worm = self.pathDial.split("\\")[-1].split('_')[0] self.path = os.path.dirname( self.pathDial ) self.setWindowTitle('Outline 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' ) 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 cellOutline pickle file already exists, load it, otherwise create a blank one if os.path.isfile( os.path.join(self.path, self.worm + '_05cellOut.pickle' ) ): self.cellOutDF = load_data_frame( self.path, self.worm + '_05cellOut.pickle' ) else: self.cellOutDF = create_cell_out( 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) print(self.channels) self.currentChannel = self.channels[0] ### detect size of the cropped images tp = first_tidx_pos_all_cells( self.cellPosDF ) 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] ### intialize canvases self.initializeCanvas1() self.initializeCanvas2() ### extract current cells already labeled self.currentCells = extract_current_cell_out( self.cellPosDF, self.cellOutDF, self.tp.value(), self.zoom.value() ) self.analyzedCell = '---' ### 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): # update the cell outline data frame before updating the images and retrireving new cells newCellOutDF = update_cell_out_DF( self.currentCells, self.cellOutDF, self.prevtp ) self.cellOutDF = newCellOutDF self.prevtp = self.tp.value() # before changing timepoint, print labeled cells and check if they are OK # print( 'cells labeled:\n ', self.currentCells ) # 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)) ### extract current cells already labeled self.currentCells = extract_current_cell_out( self.cellPosDF, self.cellOutDF, self.tp.value(), self.zoom.value() ) # print(self.currentCells) # if there are cells labeled and if the previously currently analyzed cell is present, set it as the currently labeled cell and select the right slice if len(self.currentCells) > 0: print(self.currentCells) print('cells detected') ### update currently analyzed cell if self.analyzedCell not in list( self.currentCells.cname ): self.analyzedCell = self.currentCells.cname[0] ### update slice, if it's the same slice number, manually replot the images newslice = self.currentCells.ix[ self.currentCells.cname == self.analyzedCell, 'Z' ].values[0] if newslice != self.sl.value(): self.sl.setValue( newslice ) else: self.updateAllCanvas() # if no cells are found, manually plot the blank images elif len(self.currentCells) == 0: self.updateAllCanvas() # update the BC self.setBCslidersMinMax() def saveData(self): save_data_frame( self.cellOutDF, self.path, self.worm + '_05cellOut.pickle' ) self.setFocus() def updateAllCanvas(self): self.updateCanvas1() self.updateCanvas2() 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.imgplot2.set_clim( self.sld1.value(), self.sld2.value() ) self.canvas1.draw() self.canvas2.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: if len( self.currentCells ) > 0: idx = np.mod( self.currentCells.ix[ self.currentCells.cname == self.analyzedCell ].index + 1, len(self.currentCells) ) self.analyzedCell = self.currentCells.cname.values[idx][0] self.sl.setValue( self.currentCells.ix[ self.currentCells.cname == self.analyzedCell, 'Z' ] ) self.updateAllCanvas() 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 onMouseClickOnCanvas1(self, event): pos = np.array( [ int(np.round(event.xdata)), int(np.round(event.ydata)) ] ) outline = extract_out( self.currentCells.ix[ self.currentCells.cname == self.analyzedCell ].squeeze() ) if event.button == 1: if np.isnan( outline[0,0] ): outline = np.array( [ pos ] ) else: outline = np.vstack( [ outline, pos ] ) elif event.button == 3: if len( outline[ :, 0 ] ) == 1: outline = np.array( [ [ np.nan, np.nan ] ] ) else: outline = outline[:-1,:] idx = self.currentCells.ix[ self.currentCells.cname == self.analyzedCell ].index self.currentCells.Xout.values[ idx ] = [ outline[:,0] ] self.currentCells.Yout.values[ idx ] = [ outline[:,1] ] self.updateCanvas1() self.setFocus() # print(event.button,event.xdata,event.ydata) #----------------------------------------------------------------------------------------------- # UTILS #----------------------------------------------------------------------------------------------- def changeZoom( self ): imgpxl = self.zoom.value() self.currentCells.ix[ self.currentCells.cname == self.analyzedCell, 'imgPxl' ] = imgpxl self.updateCanvas1() 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((1000,1000)), cmap = 'gray', interpolation = 'nearest' ) # 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.text_c1 = [] self.plot_c1 = [] # redraw the canvas self.canvas1.draw() self.setFocus() def updateCanvas1(self): # print('updating canvas1') # clear cell text and points # print(self.text1,self.points1) for text in self.text_c1: text.remove() self.text_c1 = [] for points in self.plot_c1: self.ax1.lines.remove(points) self.plot_c1 = [] # if no cells labeled, leave the image blank if len( self.currentCells ) == 0: self.imgplot1.set_data( np.ones((self.zoom.value(),self.zoom.value())) ) self.canvas1.draw() return # detect and update zoom size imgpxl = self.zoom.value() if not np.isnan( self.currentCells.ix[ self.currentCells.cname == self.analyzedCell, 'imgPxl' ].values[0] ): imgpxl = self.currentCells.ix[ self.currentCells.cname == self.analyzedCell, 'imgPxl' ].values[0] self.zoom.setValue( imgpxl ) else: self.currentCells.ix[ self.currentCells.cname == self.analyzedCell, 'imgPxl' ] = imgpxl # extract current cell data pos = extract_3Dpos( self.currentCells.ix[ self.currentCells.cname == self.analyzedCell ].squeeze() ) # plot the image self.imgplot1.set_data( self.stacks[self.currentChannel][self.sl.value(),pos[1]-imgpxl/2:pos[1]+imgpxl/2+1,pos[0]-imgpxl/2:pos[0]+imgpxl/2+1] ) # change brightness and contrast self.imgplot1.set_clim( self.sld1.value(), self.sld2.value() ) # print cell name if pos[2] == self.sl.value(): self.text_c1.append( self.ax1.text( 10, 50, self.analyzedCell, color='yellow', size='medium', alpha=.8, rotation=0, fontsize = 20 ) ) ### draw outline outline = extract_out( self.currentCells.ix[ self.currentCells.cname == self.analyzedCell ].squeeze() ) # print(self.currentCells.ix[ self.currentCells.cname == self.analyzedCell ].squeeze()) if len( outline ) > 1: outline = np.vstack( [ outline, outline[0] ] ) self.plot_c1.append( self.ax1.plot( outline[:,0], outline[:,1], '-x', color='yellow', ms=2, alpha=1., lw = .5 )[0] ) # # redraw the canvas self.canvas1.draw() self.setFocus() def initializeCanvas2(self): # print('initializing canvas2') 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() # plot the first blank image with the right size self.ax2.cla() self.imgplot2 = self.ax2.imshow( np.zeros((self.cropsize,self.cropsize)), cmap = 'gray' ) # remove the white borders self.ax2.autoscale(False) self.ax2.axis('Off') self.fig2.subplots_adjust(left=0., right=1., top=1., bottom=0.) # plot cell pos and name self.text_c2 = [] self.plot_c2 = [] # redraw the canvas self.canvas2.draw() self.setFocus() def updateCanvas2(self): # print('updating canvas2') # plot the image self.imgplot2.set_data( self.stacks[self.currentChannel][self.sl.value()] ) # change brightness and contrast self.imgplot2.set_clim( self.sld1.value(), self.sld2.value() ) # clear cell text and points # print(self.text1,self.points1) for text in self.text_c2: text.remove() self.text_c2 = [] for points in self.plot_c2: self.ax2.lines.remove(points) self.plot_c2 = [] # extract current cell data for idx, cell in self.currentCells.iterrows(): if cell.Z == self.sl.value(): color = 'red' if cell.cname == self.analyzedCell: color = 'yellow' self.text_c2.append( self.ax2.text( cell.X, cell.Y + 18, cell.cname, color=color, size='medium', alpha=.8, rotation=0 ) ) self.plot_c2.append( self.ax2.plot( cell.X, cell.Y, 'o', color=color, alpha = .8, mew = 0 )[0] ) # redraw the canvas self.canvas2.draw() self.setFocus() 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('Body Length Analysis') self.side = 'L' self.lbltxt = '"wheel" press: change side, currently %s\n"i" or "u" press: change cell sides' self.seamCellNames = ['a', 'b', 'c', '1', '2', '3', '4', '5', '6', 't'] 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', 'Y:\\Images') 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 = list(self.channels.keys())[0] ### 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') # extract some info self.compression = self.paramsDF.compression self.hatchingtidx = int(self.paramsDF.tidxHatch) ### if the gonadPos pickle file already exists, load it, otherwise create a blank one if os.path.isfile( os.path.join(self.path, self.worm + '_02gonadPos.pickle')): self.gpDF = load_data_frame(self.path, self.worm + '_02gonadPos.pickle') else: self.gpDF = create_gonad_pos(self.timesDF) ### 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) ### update the text of the fileName self.fName.setText( self.timesDF.ix[self.timesDF.tidxRel == self.tp.value(), 'fName'].values[0]) ### update canvases for the first time self.updateAllCanvas() self.setFocus() def saveData(self): save_data_frame(self.gpDF, self.path, self.worm + '_02gonadPos.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() #----------------------------------------------------------------------------------------------- # 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()) 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 onMouseClickOnCanvas1(self, event): # print(event.button,event.xdata,event.ydata) x = event.xdata y = event.ydata if event.button == 1: # left button: add a point to the outline gonadPos = (np.array([x, y]) * self.compression).astype(np.uint16) if event.button == 3: gonadPos = [np.nan, np.nan] # update the dataframe with the new gonadPos self.gpDF.ix[self.gpDF.tidx == self.tp.value(), 'X'] = gonadPos[0] self.gpDF.ix[self.gpDF.tidx == self.tp.value(), 'Y'] = gonadPos[1] # print( self.gpDF.ix[ self.gpDF.tidx == self.tp.value() ] ) 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) # 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( '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('Compute Cell Fluorescence') self.cellNames = [ '1.p', '4.a', '1.pp', '4.aa', '1.ppa', '1.ppp', '4.aaa', '4.aap', 'b_1', 'b_4' ] self.imgpxl = 80 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') computeBtn = QtGui.QPushButton('Compute current image') computeBtn.setFixedWidth(200) self.computeAllBtn = QtGui.QPushButton( 'Compute %s signal for all the images' % '???') self.computeAllBtn.setFixedWidth(200) saveBtn = QtGui.QPushButton('Save data (F12)') tpLbl = QtGui.QLabel('Relative Tp:') slLbl = QtGui.QLabel('Slice:') fNameLbl = QtGui.QLabel('File name:') gridLbl = QtGui.QLabel('Grid size:') self.tp = QtGui.QSpinBox(self) self.tp.setValue(-5) self.tp.setMaximum(100000) self.sl = QtGui.QSpinBox(self) self.sl.setValue(0) self.sl.setMaximum(100000) self.gridsize = QtGui.QSpinBox(self) self.gridsize.setValue(5) self.gridsize.setMaximum(20) 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.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.canvas2) Col1.addWidget(gridLbl, 6, 0) Col1.addWidget(self.gridsize, 6, 1) Col1.addWidget(computeBtn, 7, 0) Col1.addWidget(self.computeAllBtn, 8, 0) 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.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) computeBtn.clicked.connect(self.computeFluo) self.computeAllBtn.clicked.connect(self.computeAllFluo) 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\\160129_MCHERRY_HLH2GFP_onHB101') #'Y:\\Images') self.worm = self.pathDial.split("\\")[-1].split('_')[0] self.path = os.path.dirname(self.pathDial) self.setWindowTitle('Outline 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') self.cellPosDF = load_data_frame(self.path, self.worm + '_04cellPos.pickle') self.cellOutDF = load_data_frame(self.path, self.worm + '_05cellOut.pickle') # extract some info self.compression = self.paramsDF.compression self.hatchingtidx = int(self.paramsDF.tidxHatch) ### if the cellOutline pickle file already exists, load it, otherwise create a blank one if os.path.isfile( os.path.join(self.path, self.worm + '_06cellFluo.pickle')): self.cellFluoDF = load_data_frame(self.path, self.worm + '_06cellFluo.pickle') else: self.cellFluoDF = create_cell_fluo(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 = first_tidx_pos_all_cells(self.cellPosDF) 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] ### intialize canvases self.initializeCanvas1() self.initializeCanvas2() ### extract current cells already labeled self.currentCells = extract_current_cell_fluo(self.cellPosDF, self.cellOutDF, self.cellFluoDF, self.tp.value()) self.analyzedCell = '---' ### 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) if tp != self.tp.value(): self.tp.setValue(tp) else: self.loadNewStack() 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): # update the cell outline data frame before updating the images and retrireving new cells newCellFluoDF = update_cell_fluo_DF(self.currentCells, self.cellFluoDF, self.prevtp) self.cellFluoDF = newCellFluoDF self.prevtp = self.tp.value() # before changing timepoint, print labeled cells and check if they are OK 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)) ### extract current cells already labeled print('Cells in previous tp:', self.currentCells) self.currentCells = extract_current_cell_fluo(self.cellPosDF, self.cellOutDF, self.cellFluoDF, self.tp.value()) print('Cells in new tp:', self.currentCells) # if there are cells labeled and if the previously currently analyzed cell is present, set it as the currently labeled cell and select the right slice if len(self.currentCells) > 0: print('cells detected') ### update currently analyzed cell if self.analyzedCell not in list(self.currentCells.cname): self.analyzedCell = self.currentCells.cname[0] ### update slice, if it's the same slice number, manually replot the images newslice = self.currentCells.ix[self.currentCells.cname == self.analyzedCell, 'Z'].values[0] if newslice != self.sl.value(): self.sl.setValue(newslice) else: self.updateAllCanvas() # if no cells are found, manually plot the blank images elif len(self.currentCells) == 0: self.updateAllCanvas() # update the BC self.setBCslidersMinMax() def saveData(self): save_data_frame(self.cellFluoDF, self.path, self.worm + '_06cellFluo.pickle') self.setFocus() def updateAllCanvas(self): self.updateCanvas1() self.updateCanvas2() 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.imgplot2.set_clim(self.sld1.value(), self.sld2.value()) self.canvas1.draw() self.canvas2.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: if len(self.currentCells) > 0: idx = np.mod( self.currentCells.ix[self.currentCells.cname == self.analyzedCell].index + 1, len(self.currentCells)) self.analyzedCell = self.currentCells.cname.values[idx][0] self.sl.setValue(self.currentCells.ix[ self.currentCells.cname == self.analyzedCell, 'Z']) self.updateAllCanvas() 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 onMouseClickOnCanvas1(self, event): if self._488nmBtn.isChecked(): channel = '488nm' elif self._561nmBtn.isChecked(): channel = '561nm' else: QtGui.QMessageBox.about(self, 'Warning', 'Select a proper fluorescence channel!') return currentCell = self.currentCells.ix[self.currentCells.cname == self.analyzedCell].squeeze() imgpxl = currentCell.imgPxl cell = self.currentCells.ix[self.currentCells.cname == self.analyzedCell].squeeze() cellPos = extract_3Dpos(cell) cellOut = extract_out(cell) * imgpxl / 1000. # calculate new drift pos = np.array( [int(np.round(event.xdata)), int(np.round(event.ydata))]) drift = np.array([pos[0] - 500, pos[1] - 500]) * self.imgpxl / 1000. ### perform flat field correction # find gonadpos gonadPos = extract_pos( self.gpDF.ix[self.gpDF.tidx == self.tp.value()].squeeze()) # load darkField darkField = load_stack('W:\\Orca_calibration\\AVG_darkField.tif') # load flatField flatField = load_stack( os.path.join(self.path, 'AVG_flatField_' + channel + '.tif')) medianCorrection = np.median((flatField - darkField)) #correct image imgs = self.stacks[self.currentChannel] imgsCorr = flat_field_correction(imgs, darkField, flatField, gonadPos) imgCorr = imgsCorr[cellPos[2], cellPos[1] - imgpxl / 2:cellPos[1] + imgpxl / 2 + 1, cellPos[0] - imgpxl / 2:cellPos[0] + imgpxl / 2 + 1] ### find the new signal signal = calculate_fluo_intensity(imgCorr, drift, cellOut) # update the current cells newCurrentCells = update_current_cell_fluo(self.currentCells, cell, channel, drift, signal) self.currentCells = newCurrentCells # update the cell outline data frame before updating the images and retrireving new cells newCellFluoDF = update_cell_fluo_DF(self.currentCells, self.cellFluoDF, self.tp.value()) self.cellFluoDF = newCellFluoDF.copy() # update canvas self.updateCanvas1() self.setFocus() # print(event.button,event.xdata,event.ydata) #----------------------------------------------------------------------------------------------- # 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((1000, 1000)), cmap='gray', interpolation='nearest') # 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.text_c1 = [] self.out_c1 = [] self.center_c1 = [] self.outDrift_c1 = [] self.centerDrift_c1 = [] # redraw the canvas self.canvas1.draw() self.setFocus() def updateCanvas1(self): # print('updating canvas1') # clear cell text and points # print(self.text1,self.points1) for text in self.text_c1: text.remove() self.text_c1 = [] for points in self.out_c1: self.ax1.lines.remove(points) self.out_c1 = [] for points in self.outDrift_c1: self.ax1.lines.remove(points) self.outDrift_c1 = [] for points in self.centerDrift_c1: self.ax1.lines.remove(points) self.centerDrift_c1 = [] # if no cells labeled, leave the image blank if len(self.currentCells) == 0: self.imgplot1.set_data(np.ones((10, 10))) self.canvas1.draw() return # current cell currentCell = self.currentCells.ix[self.currentCells.cname == self.analyzedCell].squeeze() # extract the zoom self.imgpxl = currentCell.imgPxl imgpxl = self.imgpxl # extract current cell data pos = extract_3Dpos(currentCell) # plot the image self.imgplot1.set_data( self.stacks[self.currentChannel][self.sl.value(), pos[1] - imgpxl / 2:pos[1] + imgpxl / 2 + 1, pos[0] - imgpxl / 2:pos[0] + imgpxl / 2 + 1]) # change brightness and contrast self.imgplot1.set_clim(self.sld1.value(), self.sld2.value()) # print cell name if pos[2] == self.sl.value(): self.text_c1.append( self.ax1.text(10, 50, self.analyzedCell, color='yellow', size='medium', alpha=.8, rotation=0, fontsize=20)) ### draw outline outline = extract_out(currentCell) # print(self.currentCells.ix[ self.currentCells.cname == self.analyzedCell ].squeeze()) if len(outline) > 1: outline = np.vstack([outline, outline[0]]) self.out_c1.append( self.ax1.plot(outline[:, 0], outline[:, 1], '-x', color='yellow', ms=2, alpha=1., lw=.5)[0]) self.center_c1.append( self.ax1.plot(500, 500, 'o', color='yellow', mew=0.)[0]) # draw drifted outline if self._488nmBtn.isChecked(): drift = (extract_pos488(currentCell) - extract_pos(currentCell)) * 1000 / imgpxl elif self._561nmBtn.isChecked(): drift = (extract_pos561(currentCell) - extract_pos(currentCell)) * 1000 / imgpxl else: drift = np.array([np.nan, np.nan]) self.outDrift_c1.append( self.ax1.plot(drift[0] + outline[:, 0], drift[1] + outline[:, 1], '-x', color='red', ms=2, alpha=1., lw=.5)[0]) self.centerDrift_c1.append( self.ax1.plot(500 + drift[0], 500 + drift[1], 'o', color='red', mew=0.)[0]) # # redraw the canvas self.canvas1.draw() self.setFocus() def initializeCanvas2(self): # print('initializing canvas2') 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() # plot the first blank image with the right size self.ax2.cla() self.imgplot2 = self.ax2.imshow(np.zeros( (self.cropsize, self.cropsize)), cmap='gray') # remove the white borders self.ax2.autoscale(False) self.ax2.axis('Off') self.fig2.subplots_adjust(left=0., right=1., top=1., bottom=0.) # plot cell pos and name self.text_c2 = [] self.plot_c2 = [] # redraw the canvas self.canvas2.draw() self.setFocus() def updateCanvas2(self): # print('updating canvas2') # plot the image self.imgplot2.set_data( self.stacks[self.currentChannel][self.sl.value()]) # change brightness and contrast self.imgplot2.set_clim(self.sld1.value(), self.sld2.value()) # clear cell text and points # print(self.text1,self.points1) for text in self.text_c2: text.remove() self.text_c2 = [] for points in self.plot_c2: self.ax2.lines.remove(points) self.plot_c2 = [] # extract current cell data for idx, cell in self.currentCells.iterrows(): if cell.Z == self.sl.value(): color = 'red' if cell.cname == self.analyzedCell: color = 'yellow' self.text_c2.append( self.ax2.text(cell.X, cell.Y + 18, cell.cname, color=color, size='medium', alpha=.8, rotation=0)) self.plot_c2.append( self.ax2.plot(cell.X, cell.Y, 'o', color=color, alpha=.8, mew=0)[0]) # redraw the canvas self.canvas2.draw() self.setFocus() def changeSpaceTime(self, whatToChange, increment): if whatToChange == 'time': self.tp.setValue(self.tp.value() + increment) if whatToChange == 'space': self.sl.setValue(self.sl.value() + increment) def computeFluo(self): if self._488nmBtn.isChecked(): channel = '488nm' elif self._561nmBtn.isChecked(): channel = '561nm' else: QtGui.QMessageBox.about(self, 'Warning', 'Select a proper fluorescence channel!') return path = self.path worm = self.worm rawImgsPath = os.path.join(path, worm + '_analyzedImages') # load pickle files paramsDF = self.paramsDF timesDF = self.timesDF gpDF = self.gpDF currentCells = self.currentCells # load darkField darkField = load_stack('W:\\Orca_calibration\\AVG_darkField.tif') # load flatField flatField = load_stack( os.path.join(path, 'AVG_flatField_' + channel + '.tif')) medianCorrection = np.median((flatField - darkField)) # for idx, trow in timesDF.ix[ timesDF.tidxRel == 58 ].iterrows(): ### THIS IS TO TEST A PARTICULAR TIMEPOINT!!! trow = self.timesDF.ix[self.timesDF.tidxRel == self.tp.value()].squeeze() # if there is the cropped image if not os.path.isfile( os.path.join(rawImgsPath, trow.fName + channel + '.tif')): QtGui.QMessageBox.about(self, 'Warning', 'No croped image found!') else: # find all cells labeled in the timepoint gonadPos = extract_pos( gpDF.ix[gpDF.tidx == trow.tidxRel].squeeze()) # load the stack and perform FF correction imgs = self.stacks[self.currentChannel] imgsCorr = flat_field_correction(imgs, darkField, flatField, gonadPos) ### for each labeled cell, calculate the signal cell = self.currentCells.ix[self.currentCells.cname == self.analyzedCell].squeeze() cellPos = extract_3Dpos(cell) # extract the zoom self.imgpxl = cell.imgPxl imgpxl = self.imgpxl ### find the XYpos with maximum intensity imgCorr = imgsCorr[cellPos[2], cellPos[1] - imgpxl / 2:cellPos[1] + imgpxl / 2 + 1, cellPos[0] - imgpxl / 2:cellPos[0] + imgpxl / 2 + 1] ### if there is an outline if is_outline_cell(cell): print('detected cell/background with outline: ', cell.cname) cellOut = extract_out(cell) * self.imgpxl / 1000. if not cell.cname[:2] == 'b_': ## this is for the cells - WITH DRIFT CORRECTION _range = self.gridsize.value() signals = np.zeros((2 * _range + 1, 2 * _range + 1)) for i in np.arange(2 * _range + 1): for j in np.arange(2 * _range + 1): signals[i, j] = calculate_fluo_intensity( imgCorr, np.array([i - _range, j - _range]), cellOut) drift = np.array([ np.where(signals == np.max(signals))[0][0], np.where(signals == np.max(signals))[1][0] ]) - _range # print(drift) signal = np.max(signals) else: ### this is for backgrounds in which an outline has been drawn signal = calculate_fluo_intensity(imgCorr, np.array([0, 0]), cellOut) drift = [0, 0] else: ### if the outline of the background has not been drawn, just take a small area arund its center # if cell.cname[:2] == 'b_': print('detected background/cell without outline: ', cell.cname) signal = calculate_fluo_intensity_bckg( imgsCorr[cellPos[2], :, :], 6, cellPos) drift = [0, 0] ### update the currentCells dataframe newCurrentCells = update_current_cell_fluo(currentCells, cell, channel, drift, signal) self.currentCells = newCurrentCells # update the cell outline data frame before updating the images and retrireving new cells newCellFluoDF = update_cell_fluo_DF(self.currentCells, self.cellFluoDF, self.tp.value()) self.cellFluoDF = newCellFluoDF self.updateCanvas1() def computeAllFluo(self): if self.CoolLEDBtn.isChecked(): QtGui.QMessageBox.about(self, 'Warning', 'Select a proper fluorescence channel!') return firsttp = first_tidx_pos_all_cells(self.cellPosDF) lasttp = last_tidx_pos_all_cells(self.cellPosDF) for idx, trow in self.timesDF.ix[(self.timesDF.tidxRel <= lasttp) & ( self.timesDF.tidxRel >= firsttp)].iterrows(): self.tp.setValue(trow.tidxRel) for jdx, cell in self.currentCells.iterrows(): self.analyzedCell = cell.cname self.computeFluo() self.tp.setValue(firsttp)
class GraphUI(QtGui.QDialog): window = None tweetsSum = None scriptsSum = None prediction = None prediction_size = 0 def __init__(self, parent=None): super(GraphUI, self).__init__(parent) #self.ui = Graph_UI.Ui_Form() #self.ui.setupUi(self) # a figure instance to plot on #self.figure = Figure()S self.figure = plt.figure() self.setWindowTitle("Analysis Result") # this is the Canvas Widget that displays the `figure` # it takes the `figure` instance as a parameter to __init__ self.canvas = FigureCanvas(self.figure) # this is the Navigation widget # it takes the Canvas widget and a parent self.toolbar = NavigationToolbar(self.canvas, self) # O u r - C o d e : userSymbol = "@" # If there are no analysis results if GraphUI.scriptsSum is None: GraphUI.scriptsSum = list() for i in range(0, 4): GraphUI.scriptsSum.insert(i, "") GraphUI.scriptsSum.insert(3, 0) GraphUI.scriptsSum[0] = list() for i in range(0, 8): GraphUI.scriptsSum[0].insert(i, 0) if GraphUI.prediction is None: GraphUI.prediction = list() for i in range(0, 8): GraphUI.prediction.insert(i, 0) if GraphUI.tweetsSum is None: GraphUI.tweetsSum = list() for i in range(0, 6): GraphUI.tweetsSum.insert(i, "") GraphUI.tweetsSum.insert(5, 0) GraphUI.tweetsSum[0] = list() for i in range(0, 8): GraphUI.tweetsSum[0].insert(i, 0) userSymbol = "" # Set the window self.setStyleSheet("background-color: rgb(255, 255, 255);") #self.window.setGeometry(self.window.x(), self.window.y() , 1000, 200) # Set the headline labelText = "" if self.window.character_text is not None: labelText = str(self.window.character_text) elif self.window.location_text is not None: labelText = str(self.window.location_text) elif self.window.house is not None: labelText = "House " + str(self.window.house) # Capitalize the name labelText = labelText[0].upper() + labelText[1:] lastNameIndex = labelText.find(" ") + 1 labelText = labelText[:lastNameIndex] + labelText[lastNameIndex].upper( ) + labelText[lastNameIndex + 1:] q = '"' labelText += " in episode " + str(self.window.episode) + " - " + str( self.window.episode_text) self.label = QtGui.QLabel(labelText) self.label.setAlignment(QtCore.Qt.AlignCenter) self.label.setStyleSheet( "background-color: rgb(255, 255, 255);" "color: rgb(0, 0, 0);" "margin-top: 0px; margin-bottom: 10px; margin-right: 15px; margin-left: 15px;" "padding: 0px;" "font-size: 35px;") # Set secondary headline self.second_label = QtGui.QLabel( "Produced from " + str(GraphUI.tweetsSum[5]) + " tweets and " + str(GraphUI.scriptsSum[3]) + " script lines. " + str(round(0.8 * GraphUI.prediction_size)) + " tweets used as a training-set and " + str(round(0.2 * GraphUI.prediction_size)) + " tweets used as a testing-set.") if GraphUI.prediction_size == 0: self.second_label = QtGui.QLabel( "Produced from " + str(GraphUI.tweetsSum[5]) + " tweets and " + str(GraphUI.scriptsSum[3]) + " script lines. No prediction was done, " "due to lack of relevant data.") self.second_label.setAlignment(QtCore.Qt.AlignCenter) self.second_label.setStyleSheet( "background-color: rgb(255, 255, 255);" "color: rgb(0, 0, 0);" "margin-top: 0px; margin-bottom: 10px; margin-right: 15px; margin-left: 15px;" "padding: 0px;" "font-size: 18px;") # Set the graph's size self.canvas.setFixedSize(QtCore.QSize(500, 500)) #self.canvas.setMaximumWidth(1200) # Set the representative tweets self.tweet1 = QtGui.QLabel( str(GraphUI.tweetsSum[1]) + "\n\n" + userSymbol + str(GraphUI.tweetsSum[2])) self.tweet1.setWordWrap(True) self.tweet1.setMaximumWidth(350) self.tweet1.setStyleSheet( "background-color: rgb(29, 202, 255);" "color: rgb(0, 0, 0);" "margin-top: 5px; margin-bottom: 15px; margin-right: 15px; margin-left: 15px;" "padding: 15px;" "font-size: 18px;") self.tweet2 = QtGui.QLabel( str(GraphUI.tweetsSum[3]) + "\n\n" + userSymbol + str(GraphUI.tweetsSum[4])) self.tweet2.setWordWrap(True) self.tweet2.setMaximumWidth(350) self.tweet2.setStyleSheet( "background-color: rgb(29, 202, 255);" "color: rgb(0, 0, 0);" "margin-top: 15px; margin-bottom: 5px; margin-right: 15px; margin-left: 15px;" "padding: 15px;" "font-size: 18px;") # abort if there are no analysis results if userSymbol == "": self.tweet1.setStyleSheet("background-color: rgb(255, 255, 255);") self.tweet2.setStyleSheet("background-color: rgb(255, 255, 255);") # Set the restart and quit buttons #self.restart_button = QtGui.QPushButton('Restart') self.restart_button = QtGui.QPushButton() self.restart_button.clicked.connect(GraphUI.window.startMainPageUI) #self.restart_button.setMaximumWidth(91) #self.restart_button.setMaximumHeight(91) self.restart_button.setIcon( QtGui.QIcon( QtGui.QPixmap('./buttons/icons8-reboot-filled-100.png'))) self.restart_button.setFixedSize(QtCore.QSize(90, 90)) self.restart_button.setIconSize(QtCore.QSize(80, 80)) #self.restart_button.setGeometry(self.restart_button.x(), self.restart_button.y(), 91, 91) #self.restart_button.setStyleSheet("background-color: #000000; color: black; border: 4.5px solid #111111; margin: 4px; border-radius: 40px;") #self.quit_button = QtGui.QPushButton('Quit') self.quit_button = QtGui.QPushButton() self.quit_button.clicked.connect(exit) self.quit_button.setIcon( QtGui.QIcon(QtGui.QPixmap('./buttons/icons8-shutdown-100.png'))) self.quit_button.setFixedSize(QtCore.QSize(90, 90)) self.quit_button.setIconSize(QtCore.QSize(80, 80)) # set the layout layout = QtGui.QVBoxLayout() layout.addWidget(self.label) layout.addWidget(self.second_label) # Set an inner layout for the graph and the tweets middle_layout = QtGui.QHBoxLayout() # Set an inner-inner layout for the quit button leftbtn_layout = QtGui.QVBoxLayout() leftbtn_layout.addStretch() leftbtn_layout.addWidget(self.quit_button) middle_layout.addLayout(leftbtn_layout) middle_layout.addStretch() # Set an inner-inner layout for the graph and its toolbar graph_layout = QtGui.QVBoxLayout() graph_layout.addWidget(self.toolbar) graph_layout.addWidget(self.canvas) middle_layout.addLayout(graph_layout) # Set an inner-inner layout for the tweets tweets_layout = QtGui.QVBoxLayout() tweets_layout.addWidget(self.tweet1) tweets_layout.addWidget(self.tweet2) middle_layout.addLayout(tweets_layout) middle_layout.addStretch() # Set an inner-inner layout for the restart button rightbtn_layout = QtGui.QVBoxLayout() rightbtn_layout.addStretch() rightbtn_layout.addWidget(self.restart_button) middle_layout.addLayout(rightbtn_layout) layout.addLayout(middle_layout) # Set an inner layout for the buttons #down_layout = QtGui.QHBoxLayout() #down_layout.addWidget(self.quit_button) #down_layout.addStretch() #down_layout.addWidget(self.restart_button) #layout.addLayout(down_layout) self.setLayout(layout) # Clean the chosen parameters for the next round self.window.parameters = None self.window.episode = None self.window.episode_text = None self.window.category = None self.window.character = None self.window.character_text = None self.window.location = None self.window.location_text = None self.window.house = None # Show graph self.plot() def plot(self): # Set data df = pd.DataFrame({ 'group': ['Tweets', 'Scripts', 'Prediction'], 'Joy': [ GraphUI.tweetsSum[0][5], GraphUI.scriptsSum[0][5], GraphUI.prediction[5] ], 'Trust': [ GraphUI.tweetsSum[0][7], GraphUI.scriptsSum[0][7], GraphUI.prediction[7] ], 'Fear': [ GraphUI.tweetsSum[0][2], GraphUI.scriptsSum[0][2], GraphUI.prediction[2] ], 'Surprise': [ GraphUI.tweetsSum[0][4], GraphUI.scriptsSum[0][4], GraphUI.prediction[4] ], 'Sadness': [ GraphUI.tweetsSum[0][3], GraphUI.scriptsSum[0][3], GraphUI.prediction[3] ], 'Disgust': [ GraphUI.tweetsSum[0][1], GraphUI.scriptsSum[0][1], GraphUI.prediction[1] ], 'Anger': [ GraphUI.tweetsSum[0][0], GraphUI.scriptsSum[0][0], GraphUI.prediction[0] ], 'Anticipation': [ GraphUI.tweetsSum[0][6], GraphUI.scriptsSum[0][6], GraphUI.prediction[6] ] }) #initialize the figure #my_dpi = 96 #plt.figure(figsize=(1000 / my_dpi, 1000 / my_dpi), dpi=my_dpi) # ------- PART 1: Create background # number of variable categories = list(df)[1:] N = len(categories) # What will be the angle of each axis in the plot? (we divide the plot / number of variable) angles = [n / float(N) * 2 * pi for n in range(N)] angles += angles[:1] # Initialise the spider plot ax = plt.subplot(111, polar=True) # discards the old graph #ax.clear() # If you want the first axis to be on top: ax.set_theta_offset(pi / 2) ax.set_theta_direction(-1) # Draw one axe per variable + add labels labels yet plt.xticks(angles[:-1], categories, size=9) # Set the distance of x-axe labels from the graph ax.tick_params(axis='x', which='major', pad=14) # Draw ylabels ax.set_rlabel_position(0) plt.yticks([10, 20, 30, 40, 50, 60, 70, 80, 90, 100], ["", "20", "", "40", "", "60", "", "80", "", "100"], color="grey", size=7) plt.ylim(0, 100) # ------- PART 2: Add plots # Plot each individual = each line of the data # I don't do a loop, because plotting more than 3 groups makes the chart unreadable # Ind1 values = df.loc[0].drop('group').values.flatten().tolist() values += values[:1] ax.plot(angles, values, linewidth=1, linestyle='solid', label="Tweets") ax.fill(angles, values, 'b', alpha=0.1) # Ind2 values = df.loc[1].drop('group').values.flatten().tolist() values += values[:1] ax.plot(angles, values, linewidth=1, linestyle='solid', label="Scripts") ax.fill(angles, values, 'r', alpha=0.1) # Ind3 values = df.loc[2].drop('group').values.flatten().tolist() values += values[:1] ax.plot(angles, values, linewidth=1, linestyle='solid', label="Prediction") ax.fill(angles, values, 'g', alpha=0.1) # Add legend plt.legend(loc='upper right', bbox_to_anchor=(0.2, 0.05)) # create an axis #ax = self.figure.add_subplot(111) # refresh canvas self.canvas.draw()
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('Outline 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(-5) 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('Outline 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 # 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] ### 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 cellOutline pickle file already exists, load it, otherwise create a blank one if os.path.isfile( os.path.join(self.path, self.worm + '_05cellOut.pickle')): self.cellOutDF = load_data_frame(self.path, self.worm + '_05cellOut.pickle') else: self.cellOutDF = create_cell_out(self.timesDF, self.cellNames) self.analyzedCell = '---' ### 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)) # 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) ### 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_out(self.cellPosDF, self.cellOutDF, self.tp.value()) if len(self.currentCells) > 0: ### update current analyzed cell if self.analyzedCell not in list(self.currentCells.cname): self.analyzedCell = self.currentCells.cname[0] ### update the text of the fileName self.fName.setText( self.timesDF.ix[self.timesDF.tidxRel == self.tp.value(), 'fName'].values[0]) # 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() if len(self.currentCells) > 0: ### update slice self.sl.setValue(self.currentCells.ix[self.currentCells.cname == self.analyzedCell, 'Z']) # self.updateTable() self.updateAllCanvas() def saveData(self): save_data_frame(self.cellOutDF, self.path, self.worm + '_05cellOut.pickle') 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) elif event.key() == QtCore.Qt.Key_Space: if len(self.currentCells) > 0: idx = np.mod( self.currentCells.ix[self.currentCells.cname == self.analyzedCell].index + 1, len(self.currentCells)) self.analyzedCell = self.currentCells.cname.values[idx][0] self.sl.setValue(self.currentCells.ix[ self.currentCells.cname == self.analyzedCell, 'Z']) self.updateAllCanvas() 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 onMouseClickOnCanvas1(self, event): pos = np.array([int(event.xdata), int(event.ydata)]) outline = extract_out(self.currentCells.ix[ self.currentCells.cname == self.analyzedCell].squeeze()) if event.button == 1: if np.isnan(outline[0, 0]): outline = np.array([pos]) else: outline = np.vstack([outline, pos]) elif event.button == 3: if len(outline[:, 0]) == 1: outline = np.array([[np.nan, np.nan]]) else: outline = outline[:-1, :] idx = self.currentCells.ix[self.currentCells.cname == self.analyzedCell].index self.currentCells.Xout.values[idx] = [outline[:, 0]] self.currentCells.Yout.values[idx] = [outline[:, 1]] self.updateCanvas1() self.setFocus() # print(event.button,event.xdata,event.ydata) #----------------------------------------------------------------------------------------------- # 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) or (len(self.currentCells) == 0): # if no images are found, leave the canvas empty return # extract current cell data pos = extract_3Dpos(self.currentCells.ix[self.currentCells.cname == self.analyzedCell].squeeze()) # plot the image imgpxl = 50 self.ax1.cla() imgplot = self.ax1.imshow( self.stacks[self.currentChannel][self.sl.value(), pos[1] - imgpxl / 2:pos[1] + imgpxl / 2 + 1, pos[0] - imgpxl / 2:pos[0] + imgpxl / 2 + 1], cmap='gray', interpolation='nearest') # remove the white borders self.ax1.autoscale(False) self.ax1.axis('Off') self.fig1.subplots_adjust(left=0., right=1., top=1., bottom=0.) # print cell name if pos[2] == self.sl.value(): self.ax1.text(1, 2, self.analyzedCell, color='yellow', size='medium', alpha=.8, rotation=0, fontsize=20) self.ax1.plot(imgpxl / 2, imgpxl / 2, 'x', color='yellow', alpha=.8, ms=5) ### draw outline outline = extract_out(self.currentCells.ix[ self.currentCells.cname == self.analyzedCell].squeeze()) # print(self.currentCells.ix[ self.currentCells.cname == self.analyzedCell ].squeeze()) if len(outline) > 1: outline = np.vstack([outline, outline[0]]) self.ax1.plot(outline[:, 0], outline[:, 1], '-x', color='yellow', ms=2, alpha=1., lw=.5) # 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.stacks[self.currentChannel][self.sl.value()], cmap='gray') # remove the white borders self.ax2.autoscale(False) self.ax2.axis('Off') self.fig2.subplots_adjust(left=0., right=1., top=1., bottom=0.) # extract current cell data if len(self.currentCells) > 0: pos = extract_3Dpos(self.currentCells.ix[ self.currentCells.cname == self.analyzedCell].squeeze()) for idx, cell in self.currentCells.iterrows(): if cell.Z == self.sl.value(): color = 'red' if cell.cname == self.analyzedCell: color = 'yellow' self.ax2.text(cell.X + 10, cell.Y + 10, cell.cname, color=color, size='medium', alpha=.8, rotation=0) self.ax2.plot(cell.X, cell.Y, 'o', color=color, alpha=.8, ms=5, mew=0) # redraw the canvas self.canvas2.draw() self.setFocus() def changeSpaceTime(self, whatToChange, increment): if whatToChange == 'time': newCellOutDF = update_cell_out_DF(self.currentCells, self.cellOutDF, self.tp.value()) self.cellOutDF = newCellOutDF # 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( 'Compute Cell Fluorescence' ) self.cellNames = ['1.p','4.a','1.pp','4.aa','1.ppa','1.ppp','4.aaa','4.aap','b_1','b_4'] self.imgpxl = 80 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') computeBtn = QtGui.QPushButton('Compute current image') computeBtn.setFixedWidth(200) self.computeAllBtn = QtGui.QPushButton('Compute %s signal for all the images' %'???') self.computeAllBtn.setFixedWidth(200) saveBtn = QtGui.QPushButton('Save data (F12)') tpLbl = QtGui.QLabel('Relative Tp:') slLbl = QtGui.QLabel('Slice:') fNameLbl = QtGui.QLabel('File name:') gridLbl = QtGui.QLabel('Grid size:') self.tp = QtGui.QSpinBox(self) self.tp.setValue(-5) self.tp.setMaximum(100000) self.sl = QtGui.QSpinBox(self) self.sl.setValue(0) self.sl.setMaximum(100000) self.gridsize = QtGui.QSpinBox(self) self.gridsize.setValue(5) self.gridsize.setMaximum(20) 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.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.canvas2) Col1.addWidget(gridLbl,6,0) Col1.addWidget(self.gridsize,6,1) Col1.addWidget(computeBtn,7,0) Col1.addWidget(self.computeAllBtn,8,0) 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.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) computeBtn.clicked.connect(self.computeFluo) self.computeAllBtn.clicked.connect(self.computeAllFluo) 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\\160129_MCHERRY_HLH2GFP_onHB101')#'Y:\\Images') self.worm = self.pathDial.split("\\")[-1].split('_')[0] self.path = os.path.dirname( self.pathDial ) self.setWindowTitle('Outline 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' ) self.cellPosDF = load_data_frame( self.path, self.worm + '_04cellPos.pickle' ) self.cellOutDF = load_data_frame( self.path, self.worm + '_05cellOut.pickle' ) # extract some info self.compression = self.paramsDF.compression self.hatchingtidx = int( self.paramsDF.tidxHatch ) ### if the cellOutline pickle file already exists, load it, otherwise create a blank one if os.path.isfile( os.path.join(self.path, self.worm + '_06cellFluo.pickle' ) ): self.cellFluoDF = load_data_frame( self.path, self.worm + '_06cellFluo.pickle' ) else: self.cellFluoDF = create_cell_fluo( 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 = first_tidx_pos_all_cells( self.cellPosDF ) 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] ### intialize canvases self.initializeCanvas1() self.initializeCanvas2() ### extract current cells already labeled self.currentCells = extract_current_cell_fluo( self.cellPosDF, self.cellOutDF, self.cellFluoDF, self.tp.value() ) self.analyzedCell = '---' ### 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 ) if tp != self.tp.value(): self.tp.setValue( tp ) else: self.loadNewStack() 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): # update the cell outline data frame before updating the images and retrireving new cells newCellFluoDF = update_cell_fluo_DF( self.currentCells, self.cellFluoDF, self.prevtp ) self.cellFluoDF = newCellFluoDF self.prevtp = self.tp.value() # before changing timepoint, print labeled cells and check if they are OK 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)) ### extract current cells already labeled print('Cells in previous tp:', self.currentCells) self.currentCells = extract_current_cell_fluo( self.cellPosDF, self.cellOutDF, self.cellFluoDF, self.tp.value() ) print('Cells in new tp:', self.currentCells) # if there are cells labeled and if the previously currently analyzed cell is present, set it as the currently labeled cell and select the right slice if len(self.currentCells) > 0: print('cells detected') ### update currently analyzed cell if self.analyzedCell not in list( self.currentCells.cname ): self.analyzedCell = self.currentCells.cname[0] ### update slice, if it's the same slice number, manually replot the images newslice = self.currentCells.ix[ self.currentCells.cname == self.analyzedCell, 'Z' ].values[0] if newslice != self.sl.value(): self.sl.setValue( newslice ) else: self.updateAllCanvas() # if no cells are found, manually plot the blank images elif len(self.currentCells) == 0: self.updateAllCanvas() # update the BC self.setBCslidersMinMax() def saveData(self): save_data_frame( self.cellFluoDF, self.path, self.worm + '_06cellFluo.pickle' ) self.setFocus() def updateAllCanvas(self): self.updateCanvas1() self.updateCanvas2() 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.imgplot2.set_clim( self.sld1.value(), self.sld2.value() ) self.canvas1.draw() self.canvas2.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: if len( self.currentCells ) > 0: idx = np.mod( self.currentCells.ix[ self.currentCells.cname == self.analyzedCell ].index + 1, len(self.currentCells) ) self.analyzedCell = self.currentCells.cname.values[idx][0] self.sl.setValue( self.currentCells.ix[ self.currentCells.cname == self.analyzedCell, 'Z' ] ) self.updateAllCanvas() 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 onMouseClickOnCanvas1(self, event): if self._488nmBtn.isChecked(): channel = '488nm' elif self._561nmBtn.isChecked(): channel = '561nm' else: QtGui.QMessageBox.about(self, 'Warning', 'Select a proper fluorescence channel!') return currentCell = self.currentCells.ix[ self.currentCells.cname == self.analyzedCell ].squeeze() imgpxl = currentCell.imgPxl cell = self.currentCells.ix[ self.currentCells.cname == self.analyzedCell ].squeeze() cellPos = extract_3Dpos( cell ) cellOut = extract_out( cell ) * imgpxl / 1000. # calculate new drift pos = np.array( [ int(np.round(event.xdata)), int(np.round(event.ydata)) ] ) drift = np.array([pos[0]-500,pos[1]-500]) * self.imgpxl / 1000. ### perform flat field correction # find gonadpos gonadPos = extract_pos( self.gpDF.ix[ self.gpDF.tidx == self.tp.value() ].squeeze() ) # load darkField darkField = load_stack( 'X:\\Orca_calibration\\AVG_darkField.tif' ) # load flatField flatField = load_stack( os.path.join( self.path, 'AVG_flatField_'+channel+'.tif' ) ) medianCorrection = np.median( ( flatField - darkField ) ) #correct image imgs = self.stacks[self.currentChannel] imgsCorr = flat_field_correction( imgs, darkField, flatField, gonadPos ) imgCorr = imgsCorr[cellPos[2],cellPos[1]-imgpxl/2:cellPos[1]+imgpxl/2+1,cellPos[0]-imgpxl/2:cellPos[0]+imgpxl/2+1] ### find the new signal signal = calculate_fluo_intensity( imgCorr, drift, cellOut ) # update the current cells newCurrentCells = update_current_cell_fluo( self.currentCells, cell, channel, drift, signal ) self.currentCells = newCurrentCells # update the cell outline data frame before updating the images and retrireving new cells newCellFluoDF = update_cell_fluo_DF( self.currentCells, self.cellFluoDF, self.tp.value() ) self.cellFluoDF = newCellFluoDF.copy() # update canvas self.updateCanvas1() self.setFocus() # print(event.button,event.xdata,event.ydata) #----------------------------------------------------------------------------------------------- # 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((1000,1000)), cmap = 'gray', interpolation = 'nearest' ) # 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.text_c1 = [] self.out_c1 = [] self.center_c1 = [] self.outDrift_c1 = [] self.centerDrift_c1 = [] # redraw the canvas self.canvas1.draw() self.setFocus() def updateCanvas1(self): # print('updating canvas1') # clear cell text and points # print(self.text1,self.points1) for text in self.text_c1: text.remove() self.text_c1 = [] for points in self.out_c1: self.ax1.lines.remove(points) self.out_c1 = [] for points in self.outDrift_c1: self.ax1.lines.remove(points) self.outDrift_c1 = [] for points in self.centerDrift_c1: self.ax1.lines.remove(points) self.centerDrift_c1 = [] # if no cells labeled, leave the image blank if len( self.currentCells ) == 0: self.imgplot1.set_data( np.ones((10,10)) ) self.canvas1.draw() return # current cell currentCell = self.currentCells.ix[ self.currentCells.cname == self.analyzedCell ].squeeze() # extract the zoom self.imgpxl = currentCell.imgPxl imgpxl = self.imgpxl # extract current cell data pos = extract_3Dpos( currentCell ) # plot the image self.imgplot1.set_data( self.stacks[self.currentChannel][self.sl.value(),pos[1]-imgpxl/2:pos[1]+imgpxl/2+1,pos[0]-imgpxl/2:pos[0]+imgpxl/2+1] ) # change brightness and contrast self.imgplot1.set_clim( self.sld1.value(), self.sld2.value() ) # print cell name if pos[2] == self.sl.value(): self.text_c1.append( self.ax1.text( 10, 50, self.analyzedCell, color='yellow', size='medium', alpha=.8, rotation=0, fontsize = 20 ) ) ### draw outline outline = extract_out( currentCell ) # print(self.currentCells.ix[ self.currentCells.cname == self.analyzedCell ].squeeze()) if len( outline ) > 1: outline = np.vstack( [ outline, outline[0] ] ) self.out_c1.append( self.ax1.plot( outline[:,0], outline[:,1], '-x', color='yellow', ms=2, alpha=1., lw = .5 )[0] ) self.center_c1.append( self.ax1.plot( 500, 500, 'o', color = 'yellow', mew = 0. )[0] ) # draw drifted outline if self._488nmBtn.isChecked(): drift = ( extract_pos488(currentCell) - extract_pos(currentCell) ) * 1000 / imgpxl elif self._561nmBtn.isChecked(): drift = ( extract_pos561(currentCell) - extract_pos(currentCell) ) * 1000 / imgpxl else: drift = np.array([np.nan,np.nan]) self.outDrift_c1.append( self.ax1.plot( drift[0] + outline[:,0], drift[1] + outline[:,1], '-x', color='red', ms=2, alpha=1., lw = .5 )[0] ) self.centerDrift_c1.append( self.ax1.plot( 500 + drift[0], 500 + drift[1], 'o', color = 'red', mew = 0. )[0] ) # # redraw the canvas self.canvas1.draw() self.setFocus() def initializeCanvas2(self): # print('initializing canvas2') 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() # plot the first blank image with the right size self.ax2.cla() self.imgplot2 = self.ax2.imshow( np.zeros((self.cropsize,self.cropsize)), cmap = 'gray' ) # remove the white borders self.ax2.autoscale(False) self.ax2.axis('Off') self.fig2.subplots_adjust(left=0., right=1., top=1., bottom=0.) # plot cell pos and name self.text_c2 = [] self.plot_c2 = [] # redraw the canvas self.canvas2.draw() self.setFocus() def updateCanvas2(self): # print('updating canvas2') # plot the image self.imgplot2.set_data( self.stacks[self.currentChannel][self.sl.value()] ) # change brightness and contrast self.imgplot2.set_clim( self.sld1.value(), self.sld2.value() ) # clear cell text and points # print(self.text1,self.points1) for text in self.text_c2: text.remove() self.text_c2 = [] for points in self.plot_c2: self.ax2.lines.remove(points) self.plot_c2 = [] # extract current cell data for idx, cell in self.currentCells.iterrows(): if cell.Z == self.sl.value(): color = 'red' if cell.cname == self.analyzedCell: color = 'yellow' self.text_c2.append( self.ax2.text( cell.X, cell.Y + 18, cell.cname, color=color, size='medium', alpha=.8, rotation=0 ) ) self.plot_c2.append( self.ax2.plot( cell.X, cell.Y, 'o', color=color, alpha = .8, mew = 0 )[0] ) # redraw the canvas self.canvas2.draw() self.setFocus() def changeSpaceTime(self, whatToChange, increment): if whatToChange == 'time': self.tp.setValue( self.tp.value() + increment ) if whatToChange == 'space': self.sl.setValue( self.sl.value() + increment ) def computeFluo( self ): if self._488nmBtn.isChecked(): channel = '488nm' elif self._561nmBtn.isChecked(): channel = '561nm' else: QtGui.QMessageBox.about(self, 'Warning', 'Select a proper fluorescence channel!') return path = self.path worm = self.worm rawImgsPath = os.path.join( path, worm + '_analyzedImages' ) # load pickle files paramsDF = self.paramsDF timesDF = self.timesDF gpDF = self.gpDF currentCells = self.currentCells # load darkField darkField = load_stack( 'X:\\Orca_calibration\\AVG_darkField.tif' ) # load flatField flatField = load_stack( os.path.join( path, 'AVG_flatField_'+channel+'.tif' ) ) medianCorrection = np.median( ( flatField - darkField ) ) # for idx, trow in timesDF.ix[ timesDF.tidxRel == 58 ].iterrows(): ### THIS IS TO TEST A PARTICULAR TIMEPOINT!!! trow = self.timesDF.ix[ self.timesDF.tidxRel == self.tp.value() ].squeeze() # if there is the cropped image if not os.path.isfile( os.path.join( rawImgsPath, trow.fName + channel + '.tif' ) ): QtGui.QMessageBox.about(self, 'Warning', 'No croped image found!') else: # find all cells labeled in the timepoint gonadPos = extract_pos( gpDF.ix[ gpDF.tidx == trow.tidxRel ].squeeze() ) # load the stack and perform FF correction imgs = self.stacks[self.currentChannel] imgsCorr = flat_field_correction( imgs, darkField, flatField, gonadPos ) ### for each labeled cell, calculate the signal cell = self.currentCells.ix[ self.currentCells.cname == self.analyzedCell ].squeeze() cellPos = extract_3Dpos( cell ) # extract the zoom self.imgpxl = cell.imgPxl imgpxl = self.imgpxl ### find the XYpos with maximum intensity imgCorr = imgsCorr[cellPos[2],cellPos[1]-imgpxl/2:cellPos[1]+imgpxl/2+1,cellPos[0]-imgpxl/2:cellPos[0]+imgpxl/2+1] ### if there is an outline if is_outline_cell( cell ): print('detected cell/background with outline: ', cell.cname) cellOut = extract_out( cell ) * self.imgpxl / 1000. if not cell.cname[:2] == 'b_': ## this is for the cells - WITH DRIFT CORRECTION _range = self.gridsize.value() signals = np.zeros((2*_range+1,2*_range+1)) for i in np.arange(2*_range+1): for j in np.arange(2*_range+1): signals[i,j] = calculate_fluo_intensity( imgCorr, np.array([i-_range,j-_range]), cellOut ) drift = np.array( [ np.where(signals == np.max(signals))[0][0], np.where(signals == np.max(signals))[1][0] ] ) - _range # print(drift) signal = np.max( signals ) else: ### this is for backgrounds in which an outline has been drawn signal = calculate_fluo_intensity( imgCorr, np.array([0,0]), cellOut ) drift = [ 0, 0 ] else: ### if the outline of the background has not been drawn, just take a small area arund its center # if cell.cname[:2] == 'b_': print('detected background/cell without outline: ', cell.cname) signal = calculate_fluo_intensity_bckg( imgsCorr[cellPos[2],:,:], 6, cellPos ) drift = [ 0, 0 ] ### update the currentCells dataframe newCurrentCells = update_current_cell_fluo( currentCells, cell, channel, drift, signal ) self.currentCells = newCurrentCells # update the cell outline data frame before updating the images and retrireving new cells newCellFluoDF = update_cell_fluo_DF( self.currentCells, self.cellFluoDF, self.tp.value() ) self.cellFluoDF = newCellFluoDF self.updateCanvas1() def computeAllFluo( self ): if self.CoolLEDBtn.isChecked(): QtGui.QMessageBox.about(self, 'Warning', 'Select a proper fluorescence channel!') return firsttp = first_tidx_pos_all_cells( self.cellPosDF ) lasttp = last_tidx_pos_all_cells( self.cellPosDF ) for idx, trow in self.timesDF.ix[ ( self.timesDF.tidxRel <= lasttp ) & ( self.timesDF.tidxRel >= firsttp ) ].iterrows(): self.tp.setValue(trow.tidxRel) for jdx, cell in self.currentCells.iterrows(): self.analyzedCell = cell.cname self.computeFluo() self.tp.setValue( firsttp )
class Ui_MainWindow(object): def setupUi(self, MainWindow): # self._fig = figure(facecolor="white") # self._ax = self._fig.add_subplot(111) screen = QtGui.QDesktopWidget().screenGeometry() MainWindow.setObjectName(_fromUtf8("MainWindow")) MainWindow.resize(screen.width(), screen.height()) self.centralwidget = QtGui.QWidget(MainWindow) self.centralwidget.setObjectName(_fromUtf8("centralwidget")) MainWindow.setCentralWidget(self.centralwidget) # self.frame = QtGui.QFrame(self.centralwidget) # self.frame.setGeometry(QtCore.QRect(200, 0, 4*320, 4*240)) # self.frame.setFrameShape(QtGui.QFrame.Box) # self.frame.setFrameShadow(QtGui.QFrame.Plain) # self.frame.setObjectName(_fromUtf8("frame")) # self.gridLayout = QtGui.QGridLayout(self.frame) # self.gridLayout.setSpacing(10) # self.gridLayout.setObjectName(_fromUtf8("gridLayout")) self.videoFrame = QtGui.QLabel(self.centralwidget) self.videoFrame.setGeometry(QtCore.QRect(500, 0, 2*320, 2*240)) self.videoFrame.setObjectName(_fromUtf8("videoFrame")) #self.gridLayout.addWidget(self.videoFrame,0,0) layout = QtGui.QVBoxLayout(self.centralwidget) layout.setAlignment(QtCore.Qt.AlignTop) self.figure = matplot.figure(figsize=(5, 4), dpi=100) matplot.ylim(0,0.5) self.canvas = FigureCanvas(self.figure) self.canvas.setFixedSize(440, 470) self.axes = self.figure.add_subplot(111) self.axes.hold(False) layout.addWidget(self.canvas) self.label1 = QtGui.QLabel(self.centralwidget) font = QtGui.QFont() font.setBold(True) font.setWeight(75) font.setPointSize(70) self.label1.setFont(font) self.label1.setGeometry(QtCore.QRect(700,600, 800, 200)) self.label1.setText('') # self.histogram = QtGui.QLabel(self.can) # self.histogram.setGeometry(QtCore.QRect(500, 400, 2*320, 2*240)) # self.histogram.setObjectName(_fromUtf8("histogram")) # self.label1 = QtGui.QLabel(self.centralwidget) # self.label1.setGeometry(QtCore.QRect(1300,100, 200, 10)) # self.label1.setText('TEST') # self.label2 = QtGui.QLabel(self.centralwidget) # self.label2.setGeometry(QtCore.QRect(1300,200, 200, 10)) # self.label2.setText('TEST') # self.label3 = QtGui.QLabel(self.centralwidget) # self.label3.setGeometry(QtCore.QRect(1300,300, 200, 10)) # self.label3.setText('TEST') # self.label4 = QtGui.QLabel(self.centralwidget) # self.label4.setGeometry(QtCore.QRect(1300,400, 200, 10)) # self.label4.setText('TEST') # self.label5 = QtGui.QLabel(self.centralwidget) # self.label5.setGeometry(QtCore.QRect(1300,500, 200, 10)) # self.label5.setText('TEST') self.retranslateUi(MainWindow) QtCore.QMetaObject.connectSlotsByName(MainWindow) def retranslateUi(self, MainWindow): MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8)) self.videoFrame.setText(QtGui.QApplication.translate("MainWindow", "TextLabel", None, QtGui.QApplication.UnicodeUTF8))
class GUI(QtGui.QWidget): def __init__(self): super(GUI, self).__init__() self.setWindowTitle( 'Mark Gonad' ) 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.updateCanvas1) self.sld1.valueChanged.connect(self.updateCanvas1) self.sld2.valueChanged.connect(self.updateCanvas1) 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 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', 'C:\\Users\\Nicola\\Dropbox\\PhD\\Codes\\test')#Y:\\Images') 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 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 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' ) ) print(list(self.channels.keys())[0]) if 'CoolLED' in self.channels.keys(): self.currentChannel = 'CoolLED' else: self.currentChannel = list(self.channels.keys())[0] ### 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' ) # extract some info self.compression = self.paramsDF.compression self.hatchingtidx = int( self.paramsDF.tidxHatch ) ### if the gonadPos pickle file already exists, load it, otherwise create a blank one if os.path.isfile( os.path.join(self.path, self.worm + '_02gonadPos.pickle' ) ): self.gpDF = load_data_frame( self.path, self.worm + '_02gonadPos.pickle' ) else: self.gpDF = create_gonad_pos( self.timesDF ) tp = 0 ### update the text of the fileName self.fName.setText(self.timesDF.ix[self.timesDF.tidxRel == 0, 'fName'].values[0]) ### initialize the figure.astype(np.uint8) self.initializeCanvas1() ### set the timepoint to the hatching time self.tp.setMinimum(np.min(self.timesDF.tidxRel)) self.tp.setMaximum(np.max(self.timesDF.tidxRel)) if tp != self.tp.value(): self.tp.setValue( 0 ) self.setBCslidersMinMax() # this updates the canvas, only once! self.CoolLEDBtn.setChecked(True) # this also updates the canvas, only once! self.setFocus() def saveData(self): save_data_frame( self.gpDF, self.path, self.worm + '_02gonadPos.pickle' ) 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!') #----------------------------------------------------------------------------------------------- # 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()) 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 onMouseClickOnCanvas1(self, event): # print(event.button,event.xdata,event.ydata) x = event.xdata y = event.ydata if event.button == 1: # left button: add a point to the outline gonadPos = ( np.array( [ x, y ] ) * self.compression ).astype( np.uint16 ) if event.button == 3: gonadPos = [ np.nan, np.nan ] # update the dataframe with the new gonadPos self.gpDF.ix[ self.gpDF.tidx == self.tp.value(), 'X' ] = gonadPos[0] self.gpDF.ix[ self.gpDF.tidx == self.tp.value(), 'Y' ] = gonadPos[1] # print( self.gpDF.ix[ self.gpDF.tidx == self.tp.value() ] ) self.updateCanvas1() self.setFocus() #----------------------------------------------------------------------------------------------- # UTILS #----------------------------------------------------------------------------------------------- def setBCslidersMinMax(self): self.sld1.setMaximum( np.max( [ np.max(self.channels[key]) for key in self.channels.keys() ] ) ) self.sld1.setMinimum(0) self.sld2.setMaximum( np.max( [ np.max(self.channels[key]) for key in self.channels.keys() ] ) ) self.sld2.setMinimum(0) def initializeCanvas1(self): print('initializing canvas1... ') # plot the image self.ax1.cla() size = 2048 / self.compression self.imgplot = self.ax1.imshow( np.zeros((size,size)), 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.) # print gonad position gonadPos = [np.nan,np.nan] self.pointsplot, = self.ax1.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.textplot = self.ax1.text( 5, 15, '--.--', color = 'red' ) # redraw the canvas self.canvas1.draw() self.setFocus() def updateCanvas1(self): # print('updating canvas1... ') self.fName.setText( self.timesDF.ix[ self.timesDF.tidxRel == self.tp.value(), 'fName' ].values[0] ) # plot the image self.imgplot.set_data( self.channels[self.currentChannel][self.tp.value() + self.hatchingtidx] ) # change brightness and contrast self.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.pointsplot.set_xdata( gonadPos[0] ) self.pointsplot.set_ydata( gonadPos[1] ) plt.draw() # print time # print(self.timesDF.ix[ self.timesDF.tidxRel == self.tp.value(), 'timesRel' ]) self.textplot.set_text( '%.2f' % self.timesDF.ix[ self.timesDF.tidxRel == self.tp.value(), 'timesRel' ].values[0] ) # 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.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 Window(QtGui.QMainWindow): win_ry = 900 win_cx = 1600 saved = 0 circTick = 0 gateNames = ("X","Y","Z","H","S","T","Rx","Ry","Rz","CX","CZ","Tf",u"\u23F2",u"\U0001F441") gateCodes = ("x","y","z","h","s","t","rx","ry","rz","cnot","cphase","toffoli","measure","display") gateSets = ("All","Universal {H, Tf}","Universal {1Qb, CX}") gateMasks = ("11111111111111","00010000000111","11111100010011") drawGateQ = [] drawMaxQ = 0 delGateLno = 0 def __init__(self): super(Window,self).__init__() self.setGeometry(150,50,self.win_cx,self.win_ry) # window placement and size (TBD: variable/fullscreen) self.setWindowTitle("Quantum Integrated Development Environment") self.setWindowIcon(QtGui.QIcon('icons/app.png')) # (TBD: decide logo) self.menu() self.quickaccess() self.stsmsg = self.statusBar() self.stsmsg.showMessage('Create new project or Open existing project') self.show() ########################## MAIN MENUS ########################## def menu(self): self.mainMenu = self.menuBar() self.menuFile = self.mainMenu.addMenu('&File') self.fileMenu() self.menuView = self.mainMenu.addMenu('&View') self.viewMenu() self.menuHelp = self.mainMenu.addMenu('&Help') self.helpMenu() #~~~~~~~~~~~~~~~~~~~~~~~~~ file menu ~~~~~~~~~~~~~~~~~~~~~~~~~# def fileMenu(self): self.menuFileNewProj = QtGui.QAction("&New Project",self) self.menuFileNewProj.setShortcut("Ctrl+N") self.menuFileNewProj.triggered.connect(self.newProject) self.menuFile.addAction(self.menuFileNewProj) self.menuFile.addSeparator() self.menuFileOpenProj = QtGui.QAction("&Open Project",self) self.menuFileOpenProj.setShortcut("Ctrl+O") self.menuFileOpenProj.triggered.connect(self.openProject) self.menuFile.addAction(self.menuFileOpenProj) self.menuFileOpenO = QtGui.QAction("&Import OpenQL",self) self.menuFileOpenO.triggered.connect(self.importOpenql) self.menuFile.addAction(self.menuFileOpenO) self.menuFileOpenO.setEnabled(False) self.menuFileOpenQ = QtGui.QAction("&Import QASM",self) self.menuFileOpenQ.triggered.connect(self.importQasm) self.menuFile.addAction(self.menuFileOpenQ) self.menuFileOpenQ.setEnabled(False) self.menuFileOpenC = QtGui.QAction("&Import QCircuit",self) self.menuFileOpenC.triggered.connect(self.TBD) self.menuFile.addAction(self.menuFileOpenC) self.menuFileOpenC.setEnabled(False) # (TBD) self.menuFile.addSeparator() self.menuFileSaveP = QtGui.QAction("&Save Project",self) self.menuFileSaveP.setShortcut("Ctrl+S") self.menuFileSaveP.triggered.connect(self.saveProject) self.menuFile.addAction(self.menuFileSaveP) self.menuFileSaveP.setEnabled(False) self.menuFileSaveO = QtGui.QAction("&Export OpenQL",self) self.menuFileSaveO.triggered.connect(self.exportOpenql) self.menuFile.addAction(self.menuFileSaveO) self.menuFileSaveO.setEnabled(False) self.menuFileSaveQ = QtGui.QAction("&Export QASM",self) self.menuFileSaveQ.triggered.connect(self.exportQasm) self.menuFile.addAction(self.menuFileSaveQ) self.menuFileSaveQ.setEnabled(False) self.menuFileSaveC = QtGui.QAction("&Export QCircuit",self) self.menuFileSaveC.triggered.connect(self.TBD) self.menuFile.addAction(self.menuFileSaveC) self.menuFileSaveC.setEnabled(False) # (TBD) self.menuFileExport = QtGui.QAction("&Export QCircuit Image",self) self.menuFileExport.triggered.connect(self.exportQCircImg) self.menuFile.addAction(self.menuFileExport) self.menuFileExport.setEnabled(False) self.menuFile.addSeparator() self.menuFileExit = QtGui.QAction("&Exit",self) self.menuFileExit.setShortcut("Ctrl+Q") self.menuFileExit.triggered.connect(self.closeApp) self.menuFile.addAction(self.menuFileExit) def closeApp(self): if self.saved == 0: choice = QtGui.QMessageBox.question(self,'sanity check',"Quit?", QtGui.QMessageBox.Yes | QtGui.QMessageBox.No) if choice == QtGui.QMessageBox.Yes: sys.exit() else: pass def newProject(self): self.dirProj = str(QtGui.QFileDialog.getExistingDirectory(self,'New Project Directory')) self.nameProj = self.dirProj[self.dirProj.rindex('/')+1:] self.configProj = {'name':self.nameProj} self.configProj.update({'path':self.dirProj}) self.centerLayout() file = open(self.fileQCirc,'w') file.write("") file.close() def saveProject(self): self.configProjJson = self.dirProj+'/'+self.nameProj+'.qide' with open(self.configProjJson,'w') as file: json.dump(self.configProj,file,indent=2) file.close() self.saveOpenql() self.saveQasm() def saveOpenql(self): file = open(self.fileOpenql,'w') text = self.textOpenql.toPlainText() file.write(text) file.close() def saveQasm(self): file = open(self.fileQasm,'w') text = self.textQasm.toPlainText() file.write(text) file.close() def openProject(self): self.configProjJson = QtGui.QFileDialog.getOpenFileName(self,'Open QuIDE Project File','',"*.qide") print(self.configProjJson) pathCnfg = str(self.configProjJson) self.dirProj = pathCnfg[:pathCnfg.rindex('/')] nameCnfg = pathCnfg[pathCnfg.rindex('/')+1:] self.nameProj = nameCnfg[:nameCnfg.rindex('.')] self.configProj = {'name':self.nameProj} self.configProj.update({'path':self.dirProj}) self.centerLayout() self.openOpenql() self.openQasm() self.openQcirc() def openOpenql(self): file = open(self.fileOpenql,'r') with file: text = file.read() self.textOpenql.setText(text) def openQasm(self): file = open(self.fileQasm,'r') with file: text = file.read() self.textQasm.setText(text) def openQcirc(self): self.circReset() gates = len(self.gateNames) file = open(self.fileQCirc,'r') self.drawMaxQ = 1 # For drawing measure and display, if circuit was not executed before for line in iter(file): # Qubits ptrn = re.compile('^qubits\s(\d+)',re.IGNORECASE) mtch = ptrn.search(line) if mtch != None: self.drawMaxQ = int(mtch.group(1)) # Single Qubit Gates for g in range(0,9): ptrn = re.compile('^\s*'+self.gateNames[g]+'\sq(\d+)',re.IGNORECASE) mtch = ptrn.search(line) if mtch != None: self.circGate(g,[int(mtch.group(1))]) # Two Qubit Gates ptrn = re.compile("cnot"+'\sq(\d+),q(\d+)',re.IGNORECASE) mtch = ptrn.search(line) if mtch != None: self.circGate(9,[int(mtch.group(1)),int(mtch.group(2))]) # Three Qubit Gates ptrn = re.compile("toffoli"+'\sq(\d+),q(\d+),q(\d+)',re.IGNORECASE) mtch = ptrn.search(line) if mtch != None: self.circGate(11,[int(mtch.group(1)),int(mtch.group(2)),int(mtch.group(3))]) # Measure ptrn = re.compile("measure") mtch = ptrn.search(line) if mtch != None: self.circGate(12,[0]) # Display ptrn = re.compile("display") mtch = ptrn.search(line) if mtch != None: self.circGate(13,[0]) # self.circuit.setDragMode(QtGui.QGraphicsView.ScrollHandDrag) def importOpenql(self): name = QtGui.QFileDialog.getOpenFileName(self,'Import OpenQL File','',"*.py") file = open(name,'r') with file: text = file.read() self.textOpenql.setText(text) def importQasm(self): name = QtGui.QFileDialog.getOpenFileName(self,'Import QASM File','',"*.qasm") file = open(name,'r') with file: text = file.read() self.textQasm.setText(text) def exportOpenql(self): name = QtGui.QFileDialog.getSaveFileName(self,'Export OpenQL File','',"*.py") file = open(name,'w') text = self.textOpenql.toPlainText() file.write(text) file.close() def exportQasm(self): name = QtGui.QFileDialog.getSaveFileName(self,'Export QASM File','',"*.qasm") file = open(name,'w') text = self.textQasm.toPlainText() file.write(text) file.close() def exportQCircImg(self): name = QtGui.QFileDialog.getSaveFileName(self,'Export Circuit Image','',"*.png") pixmap = QtGui.QPixmap(self.qcircuit.sceneRect().width(),self.qcircuit.sceneRect().height()) painter = QtGui.QPainter(pixmap) painter.setRenderHint(QtGui.QPainter.Antialiasing) self.qcircuit.render(painter) painter.end() pixmap.save(name) #~~~~~~~~~~~~~~~~~~~~~~~~~ view menu ~~~~~~~~~~~~~~~~~~~~~~~~~# def viewMenu(self): self.menuViewThemes = QtGui.QMenu("&Themes",self) self.menuView.addMenu(self.menuViewThemes) qtactn = QtGui.QAction("gtk+",self.menuViewThemes) qtactn.triggered.connect(lambda: QtGui.QApplication.setStyle(QtGui.QStyleFactory.create("gtk+"))) self.menuViewThemes.addAction(qtactn) qtactn = QtGui.QAction("motif",self.menuViewThemes) qtactn.triggered.connect(lambda: QtGui.QApplication.setStyle(QtGui.QStyleFactory.create("motif"))) self.menuViewThemes.addAction(qtactn) qtactn = QtGui.QAction("plastique",self.menuViewThemes) qtactn.triggered.connect(lambda: QtGui.QApplication.setStyle(QtGui.QStyleFactory.create("plastique"))) self.menuViewThemes.addAction(qtactn) qtactn = QtGui.QAction("windows",self.menuViewThemes) qtactn.triggered.connect(lambda: QtGui.QApplication.setStyle(QtGui.QStyleFactory.create("windows"))) self.menuViewThemes.addAction(qtactn) qtactn = QtGui.QAction("cleanlooks",self.menuViewThemes) qtactn.triggered.connect(lambda: QtGui.QApplication.setStyle(QtGui.QStyleFactory.create("cleanlooks"))) self.menuViewThemes.addAction(qtactn) qtactn = QtGui.QAction("cde",self.menuViewThemes) qtactn.triggered.connect(lambda: QtGui.QApplication.setStyle(QtGui.QStyleFactory.create("cde"))) self.menuViewThemes.addAction(qtactn) #~~~~~~~~~~~~~~~~~~~~~~~~~ help menu ~~~~~~~~~~~~~~~~~~~~~~~~~# def helpMenu(self): self.menuHelpTutorial = QtGui.QMenu("&Learn",self) self.menuHelp.addMenu(self.menuHelpTutorial) qtactn = QtGui.QAction("&QuIDE and QCirc",self.menuViewThemes) qtactn.triggered.connect(self.learnQuide) self.menuHelpTutorial.addAction(qtactn) qtactn = QtGui.QAction("&OpenQL",self.menuViewThemes) qtactn.triggered.connect(self.learnOpenql) self.menuHelpTutorial.addAction(qtactn) qtactn = QtGui.QAction("&QX-Sim",self.menuViewThemes) qtactn.triggered.connect(self.learnQxsim) self.menuHelpTutorial.addAction(qtactn) self.menuHelp.addSeparator() self.menuHelpRelease = QtGui.QAction("&Release Notes",self) self.menuHelpRelease.triggered.connect(self.TBD) self.menuHelp.addAction(self.menuHelpRelease) self.menuHelpRelease.setEnabled(False) # (TBD) self.menuHelpUpdate = QtGui.QAction("&Update",self) self.menuHelpUpdate.triggered.connect(self.TBD) self.menuHelp.addAction(self.menuHelpUpdate) self.menuHelpUpdate.setEnabled(False) # (TBD) self.menuHelp.addSeparator() self.menuHelpLicense = QtGui.QAction("&License",self) self.menuHelpLicense.triggered.connect(self.license) self.menuHelp.addAction(self.menuHelpLicense) self.menuHelpAbout = QtGui.QAction("&About",self) self.menuHelpAbout.triggered.connect(self.about) self.menuHelp.addAction(self.menuHelpAbout) def learnQuide(self): QtGui.QMessageBox.about(self,"Learn QuIDE & QCirc","Visit : https://gitlab.com/prince-ph0en1x/QIDE") def learnOpenql(self): QtGui.QMessageBox.about(self,"Learn OpenQL","Visit : https://gitlab.com/qutech-ce/openql") def learnQxsim(self): QtGui.QMessageBox.about(self,"Learn OpenQL","Visit : https://gitlab.com/qutech-ce/qx-simulator") def license(self): QtGui.QMessageBox.about(self,"License",open("license.txt",'r').read()) def about(self): QtGui.QMessageBox.about(self,"About",open("about.txt",'r').read()) #~~~~~~~~~~~~~~~~~~~~~~~~~ shortcuts ~~~~~~~~~~~~~~~~~~~~~~~~~# def quickaccess(self): self.toolBar = self.addToolBar("QuickAccess") extractAction = QtGui.QAction(QtGui.QIcon('icons/plot5.png'),'Custom Plot',self) extractAction.triggered.connect(self.TBD) self.toolBar.addAction(extractAction) extractAction.setEnabled(False) extractAction = QtGui.QAction(QtGui.QIcon('icons/layout3.png'),'Custom Layout',self) extractAction.triggered.connect(self.TBD) self.toolBar.addAction(extractAction) extractAction.setEnabled(False) extractAction = QtGui.QAction(QtGui.QIcon('icons/uc3.png'),'View Microcode',self) extractAction.triggered.connect(self.TBD) self.toolBar.addAction(extractAction) extractAction.setEnabled(False) ########################## MAIN LAYOUT ########################## def centerLayout(self): self.menuFileSaveP.setEnabled(True) self.stsmsg.showMessage('Current Project Directory : '+self.dirProj) self.fileOpenql = self.dirProj+"/"+self.nameProj+".py" self.fileQasm = self.dirProj+"/"+self.nameProj+".qasm" self.fileQCirc = self.dirProj+"/"+self.nameProj+".qcir" self.centralWidget = QtGui.QWidget(self) self.setCentralWidget(self.centralWidget) self.topLayoutV = QtGui.QVBoxLayout(self.centralWidget) self.topLayoutH1 = QtGui.QHBoxLayout() self.openqlEditor() self.qasmEditor() self.topLayoutV.addLayout(self.topLayoutH1,QtCore.Qt.AlignBottom) self.topLayoutH2 = QtGui.QHBoxLayout() self.gateset() self.circuitEditor() self.resultTabs() self.topLayoutV.addLayout(self.topLayoutH2,QtCore.Qt.AlignBottom) self.menuFileOpenO.setEnabled(True) self.menuFileOpenQ.setEnabled(True) # self.menuFileOpenC.setEnabled(True) self.menuFileSaveO.setEnabled(True) self.menuFileSaveQ.setEnabled(True) # self.menuFileSaveC.setEnabled(True) self.menuFileExport.setEnabled(True) #~~~~~~~~~~~~~~~~~~~~~~~~~ openql ed ~~~~~~~~~~~~~~~~~~~~~~~~~# def openqlEditor(self): self.topLayoutH1.addSpacing(90) self.textOpenql = QtGui.QTextEdit(self) self.textOpenql.setFixedSize(990,415) self.textOpenql.setText("OpenQL Editor") self.topLayoutH1.addWidget(self.textOpenql,QtCore.Qt.AlignRight) self.btnLayoutOQ = QtGui.QVBoxLayout() self.btnO2Q = QtGui.QPushButton(u"\u2192",self) self.btnO2Q.clicked.connect(self.convO2Q) self.btnO2Q.setFixedSize(30,30) self.btnLayoutOQ.addWidget(self.btnO2Q,0,QtCore.Qt.AlignBottom) self.btnQ2O = QtGui.QPushButton(u"\u2190",self) self.btnQ2O.clicked.connect(self.TBD) self.btnQ2O.setFixedSize(30,30) self.btnLayoutOQ.addWidget(self.btnQ2O,QtCore.Qt.AlignBottom) self.btnQ2O.setEnabled(False) self.btnLayoutOQ.addSpacing(40) self.topLayoutH1.addLayout(self.btnLayoutOQ,QtCore.Qt.AlignCenter) self.editorConfig() def editorConfig(self): # (TBD : Load from project json) self.textOpenql.setLineWrapMode(0) self.highlight = syntax.PythonHighlighter(self.textOpenql.document()) self.textOpenql.setTabStopWidth(40) self.textOpenql.setFontFamily("Monospace") def convO2Q(self): self.saveOpenql() self.saveQasm() # current QASM backup os.system("python3 "+self.fileOpenql+" > "+self.dirProj+"/"+"logO2Q.txt 2>&1") name = "test_output/qg.qasm" # (TBD: get output file name from openql) file = open(name,'r') with file: text = file.read() self.textQasm.setText(text) file.close() text = open(self.dirProj+"/"+"logO2Q.txt",'r').read() self.textLog.setText(text) self.tabLayout.setCurrentWidget(self.textLog) #~~~~~~~~~~~~~~~~~~~~~~~~~ qasm ed ~~~~~~~~~~~~~~~~~~~~~~~~~# def qasmEditor(self): self.qasmLayout = QtGui.QVBoxLayout() self.textQasm = QtGui.QTextEdit(self) self.textQasm.setFixedSize(460,380) self.textQasm.setText("QASM Editor") self.textQasm.setLineWrapMode(0) self.qasmLayout.addWidget(self.textQasm,0,QtCore.Qt.AlignRight) self.btnLayoutQ = QtGui.QHBoxLayout() self.btnQ2C = QtGui.QPushButton(u"\u2193",self) self.btnQ2C.clicked.connect(self.convQ2C) self.btnQ2C.setFixedSize(30,30) self.btnLayoutQ.addWidget(self.btnQ2C,QtCore.Qt.AlignLeft) self.btnC2Q = QtGui.QPushButton(u"\u2191",self) self.btnC2Q.clicked.connect(self.convC2Q) self.btnC2Q.setFixedSize(30,30) self.btnLayoutQ.addWidget(self.btnC2Q,QtCore.Qt.AlignLeft) self.btnLayoutQ.addSpacing(40) self.btnQ2G = QtGui.QPushButton(u"\u2193",self) self.btnQ2G.clicked.connect(self.convQ2G) self.btnQ2G.setFixedSize(30,30) self.btnLayoutQ.addWidget(self.btnQ2G,0,QtCore.Qt.AlignLeft) self.qasmLayout.addLayout(self.btnLayoutQ,QtCore.Qt.AlignRight) self.topLayoutH1.addLayout(self.qasmLayout,QtCore.Qt.AlignRight) def convQ2G(self): self.saveQasm() os.system("./qx_simulator_1.0.beta_linux_x86_64 "+self.fileQasm+" > "+self.dirProj+"/"+"logQ2G.txt") file = open(self.dirProj+"/"+"logQ2G.txt",'r') self.plotOutput(file) file.close() text = open(self.dirProj+"/"+"logQ2G.txt",'r').read() self.textLog.setText(text) self.tabLayout.setCurrentWidget(self.textLog) def convQ2C(self): self.saveQasm() file = open(self.fileQasm,'r') with file: text = file.read() file.close() file = open(self.fileQCirc,'w') file.write(text) file.close() self.openQcirc() #~~~~~~~~~~~~~~~~~~~~~~~~~ qcircuit ~~~~~~~~~~~~~~~~~~~~~~~~~# def circuitEditor(self): penG = QtGui.QPen(QtCore.Qt.blue) fill = QtGui.QBrush(QtCore.Qt.cyan) self.qcircuit = QtGui.QGraphicsScene() self.qcircuit.setBackgroundBrush(QtCore.Qt.white) self.btnLayoutC = QtGui.QHBoxLayout() self.circuit = QtGui.QGraphicsView(self.qcircuit) self.circuit.setFixedSize(1100,420) # self.circuit.mousePressEvent = self.pixelSelect self.btnLayoutC.addWidget(self.circuit,QtCore.Qt.AlignLeft) self.circReset() self.btnC2G = QtGui.QPushButton(u"\u2192",self) self.btnC2G.clicked.connect(self.convC2G) self.btnC2G.setFixedSize(30,30) self.btnLayoutC.addWidget(self.btnC2G,0,QtCore.Qt.AlignTop) self.topLayoutH2.addLayout(self.btnLayoutC,QtCore.Qt.AlignLeft) def circReset(self): for item in self.qcircuit.items(): self.qcircuit.removeItem(item) pen = QtGui.QPen(QtCore.Qt.black) qubits = 50 for i in range(0,qubits): t = self.qcircuit.addText("q["+str(i)+"]") t.setPos(0,i*40) self.qcircuit.addLine(QtCore.QLineF(50,i*40+12,80,i*40+12),pen) self.circTick = 0 self.circuit.centerOn(QtCore.QPointF(0,0)) # self.qcircuit.addEllipse(QtCore.QRectF(0,0,10,10),QtCore.Qt.black,QtCore.Qt.black) def gateset(self): gsz = 39 bsz = gsz - 0 gsr = self.win_ry/2 + gsz/2 + 5 gsc = 10 self.btn_g = {} self.btnLayoutGates = QtGui.QGridLayout() comboBox = QtGui.QComboBox(self) for gs in range(0,len(self.gateSets)): comboBox.addItem(self.gateSets[gs]) comboBox.setFixedSize(2*gsz+5,gsz) comboBox.activated[str].connect(self.gateEnbl) self.btnLayoutGates.addWidget(comboBox,0,0,1,2,QtCore.Qt.AlignTop) gsr = gsr+gsz gr = 0 gc = 0 gates = len(self.gateNames) for g in range(0,gates): self.btn_g[g] = QtGui.QPushButton(self.gateNames[g],self) self.btn_g[g].setFixedSize(bsz,bsz-15) self.btnLayoutGates.addWidget(self.btn_g[g],g/2+1,g%2,QtCore.Qt.AlignTop) gc = (gc + 1)%2 if gc == 0: gr = gr + 1 self.btn_g[0].clicked.connect(lambda: self.circSelGate(0)) self.btn_g[1].clicked.connect(lambda: self.circSelGate(1)) self.btn_g[2].clicked.connect(lambda: self.circSelGate(2)) self.btn_g[3].clicked.connect(lambda: self.circSelGate(3)) self.btn_g[4].clicked.connect(lambda: self.circSelGate(4)) self.btn_g[5].clicked.connect(lambda: self.circSelGate(5)) self.btn_g[6].clicked.connect(lambda: self.circSelGate(6)) self.btn_g[7].clicked.connect(lambda: self.circSelGate(7)) self.btn_g[8].clicked.connect(lambda: self.circSelGate(8)) self.btn_g[9].clicked.connect(lambda: self.circSelGate(9)) self.btn_g[10].clicked.connect(lambda: self.circSelGate(10)) self.btn_g[11].clicked.connect(lambda: self.circSelGate(11)) self.btn_g[12].clicked.connect(lambda: self.circSelGate(12)) self.btn_g[13].clicked.connect(lambda: self.circSelGate(13)) g = gates self.btnGDel = QtGui.QPushButton(u"\u2700",self) #self.btnGDel = QtGui.QPushButton(QtGui.QIcon("icons/del1.png"),'',self) self.btnGDel.setFixedSize(bsz,bsz) self.btnLayoutGates.addWidget(self.btnGDel,g/2+1,g%2,QtCore.Qt.AlignTop) self.btnGDel.clicked.connect(self.delGate) g = g+1 #self.btnGNew = QtGui.QPushButton(u"\U0001F441",self) self.btnGNew = QtGui.QPushButton(QtGui.QIcon("icons/cg.png"),'',self) self.btnGNew.setFixedSize(bsz,bsz) self.btnLayoutGates.addWidget(self.btnGNew,g/2+1,g%2,QtCore.Qt.AlignTop) self.btnGNew.clicked.connect(self.makeGate) g = g+1 comboBox = QtGui.QComboBox(self) for gs in range(0,5): comboBox.addItem("Cust. G"+str(gs)) comboBox.setFixedSize(2*gsz+5,gsz) comboBox.activated[str].connect(self.TBD) self.btnLayoutGates.addWidget(comboBox,g/2+1,g%2,1,2,QtCore.Qt.AlignTop) self.topLayoutH2.addLayout(self.btnLayoutGates,QtCore.Qt.AlignLeft) def delGate(self): self.qcircuit.mousePressEvent = self.getDelGate return def getDelGate(self,event): beg = 80 xsp = 50 ysp = 40 pos = QtCore.QPointF(event.scenePos()) #qb = round((pos.y()-12)/ysp) tk = round((pos.x() - beg - xsp/4)/xsp) file = open(self.fileQCirc,'r+') lines = file.readlines() file.close() file = open(self.fileQCirc,'w') lno = 0 for line in lines: if lno != int(tk)+1: file.write(line) lno = lno + 1 file.close() self.openQcirc() def makeGate(self): # enable drag box # self.circuit.dropEvent = self.lol # print("hi") # print(self.circuit.QGraphicsSceneDragDropEvent.pos()) self.makeGateBound = [] self.qcircuit.mousePressEvent = self.getGateBound return def getGateBound(self,event): beg = 80 xsp = 50 ysp = 40 pos = QtCore.QPointF(event.scenePos()) qb = round((pos.y()-12)/ysp) tk = round((pos.x() - beg - xsp/4)/xsp) self.delGateLno = tk print([qb,tk]) def gateEnbl(self,set): gs = 0 for i in range(0,len(self.gateSets)): if set == self.gateSets[i]: gs = i break for g in range(0,len(self.gateMasks[0])): if self.gateMasks[gs][g] == '1': self.btn_g[g].setEnabled(True) else: self.btn_g[g].setEnabled(False) def circSelGate(self,gateId): self.drawGate = gateId self.drawGateQ = [] # self.circuit.setDragMode(QtGui.QGraphicsView.NoDrag) self.qcircuit.mousePressEvent = self.pixelSelect # LOGIC RESIDES WHERE WE THINK IT RESIDES # UNIVERSE EVOLVED INTELLIGENCE TO REDISCOVER ITSELF # IF WE ARE THE CENTRE OF OUR FRAME OF REFERENCE, WHY DO WE NEED TO USE ENERGY # DREAM IS THE ILLUSION (MAYA) AND THE URGE TO BE IN IT IS THE DESIRE (MOH) # THE INFORMATIONAL DIFFERENCE BETWEEN A SARCASTIC AND NORMAL ANSWER IS 0, YET IT CAN TRANSMIT 1 SHANNON BIT OF INFORMATION TO THE RECEIVER def pixelSelect(self, event): # undo/redo gate button # drag toggle button # make new kernel button # make new unitary button # insert custom kernel/unitary button file = open(self.fileQCirc,'r+') content = file.read() ptrn = re.compile('qubits') mtch = ptrn.search(content) if mtch == None: file.seek(0, 0) file.write("qubits "+str(int(self.drawMaxQ))+"\n" + content) file.close() file = open(self.fileQCirc,'a') ysp = 40 qb = math.floor(event.scenePos().y()/ysp) self.drawGateQ.append(qb) if self.drawGate < 9: if len(self.drawGateQ) == 1: self.circGate(self.drawGate,self.drawGateQ) file.write(self.gateCodes[self.drawGate]+" q"+str(int(self.drawGateQ[0]))+"\n") # self.circuit.mousePressEvent = None # self.circuit.setDragMode(QtGui.QGraphicsView.ScrollHandDrag) elif self.drawGate == 9: # CNOT if len(self.drawGateQ) == 2: self.circGate(self.drawGate,self.drawGateQ) file.write(self.gateCodes[self.drawGate]+" q"+str(int(self.drawGateQ[0]))+",q"+str(int(self.drawGateQ[1]))+"\n") elif self.drawGate == 11: # Toffoli if len(self.drawGateQ) == 3: self.circGate(self.drawGate,self.drawGateQ) file.write(self.gateCodes[self.drawGate]+" q"+str(int(self.drawGateQ[0]))+",q"+str(int(self.drawGateQ[1]))+",q"+str(int(self.drawGateQ[2]))+"\n") elif self.drawGate >= 12: if len(self.drawGateQ) == 1: self.circGate(self.drawGate,self.drawGateQ) file.write(self.gateCodes[self.drawGate]+"\n") file.close() def circGate(self,gateId,qbset): beg = 80 xsp = 50 ysp = 40 for i in range (0,len(qbset)): if qbset[i]+1 > self.drawMaxQ: self.drawMaxQ = qbset[i]+1 pen = QtGui.QPen(QtCore.Qt.black) qubits = 50 for i in range(0,qubits): self.qcircuit.addLine(QtCore.QLineF(beg+xsp*self.circTick,i*40+12,beg+xsp*(self.circTick+1),i*40+12),pen) penG = QtGui.QPen(QtCore.Qt.gray) fill = QtGui.QBrush(QtCore.Qt.cyan) rsz = 24 csz = 14 if gateId < 9: qbt = qbset[0] self.qcircuit.addRect(QtCore.QRectF(beg+xsp*self.circTick,ysp*qbt,rsz,rsz),penG,fill) t = self.qcircuit.addText(self.gateNames[gateId]) t.setPos(beg+xsp*self.circTick+4,ysp*qbt) elif gateId == 9: qbc1 = qbset[0] qbt = qbset[1] self.qcircuit.addLine(QtCore.QLineF(beg+xsp*self.circTick+5+7,ysp*qbc1+5+7,beg+xsp*self.circTick+12,ysp*qbt+12),penG) self.qcircuit.addEllipse(QtCore.QRectF(beg+xsp*self.circTick+(rsz-csz)/2,ysp*qbc1+(rsz-csz)/2,csz,csz),penG,QtCore.Qt.black) self.qcircuit.addRect(QtCore.QRectF(beg+xsp*self.circTick,ysp*qbt,rsz,rsz),penG,fill) t = self.qcircuit.addText("X") t.setPos(beg+xsp*self.circTick+4,ysp*qbt) elif gateId == 11: qbc2 = qbset[0] qbc1 = qbset[1] qbt = qbset[2] self.qcircuit.addLine(QtCore.QLineF(beg+xsp*self.circTick+5+7,ysp*qbc1+5+7,beg+xsp*self.circTick+12,ysp*qbt+12),penG) self.qcircuit.addLine(QtCore.QLineF(beg+xsp*self.circTick+5+7,ysp*qbc2+5+7,beg+xsp*self.circTick+12,ysp*qbt+12),penG) self.qcircuit.addEllipse(QtCore.QRectF(beg+xsp*self.circTick+(rsz-csz)/2,ysp*qbc1+(rsz-csz)/2,csz,csz),penG,QtCore.Qt.black) self.qcircuit.addEllipse(QtCore.QRectF(beg+xsp*self.circTick+(rsz-csz)/2,ysp*qbc2+(rsz-csz)/2,csz,csz),penG,QtCore.Qt.black) self.qcircuit.addRect(QtCore.QRectF(beg+xsp*self.circTick,ysp*qbt,rsz,rsz),penG,fill) t = self.qcircuit.addText("X") t.setPos(beg+xsp*self.circTick+4,ysp*qbt) elif gateId >= 12: # Display if gateId == 13: fill = QtGui.QBrush(QtCore.Qt.green) self.qcircuit.addRect(QtCore.QRectF(beg+xsp*self.circTick,ysp*0,rsz,ysp*int(self.drawMaxQ-1)+rsz),penG,fill) for qbt in range(0,int(self.drawMaxQ)): t = self.qcircuit.addText(self.gateNames[gateId]) t.setPos(beg+xsp*self.circTick+1,ysp*qbt) pen.setStyle(QtCore.Qt.DotLine) self.qcircuit.addLine(QtCore.QLineF(beg+xsp*self.circTick-xsp/4,0,beg+xsp*self.circTick-xsp/4,ysp*qubits-ysp/4),pen) self.circTick = self.circTick + 1 # allow parallel scheduling def convC2Q(self): file = open(self.fileQCirc,'r') with file: text = file.read() self.textQasm.setText(text) file.close() def convC2G(self): file = open(self.fileQCirc,'r+') content = file.read() file.close() ptrn = re.compile('^qubits\s(\d+)',re.IGNORECASE) mtch = ptrn.search(content) if mtch == None: file = open(self.fileQCirc,'r+') file.seek(0, 0) file.write("qubits "+str(int(self.drawMaxQ))+"\n" + content) file.close() else: file = open(self.fileQCirc,'r+') lines = file.readlines() file.close() file = open(self.fileQCirc,'w') for line in lines: mtch = ptrn.search(line) if mtch == None: file.write(line) file.close() file = open(self.fileQCirc,'r+') content = file.read() file.seek(0, 0) file.write("qubits "+str(int(self.drawMaxQ))+"\n" + content) file.close() os.system("./qx_simulator_1.0.beta_linux_x86_64 "+self.fileQCirc+" > "+self.dirProj+"/"+"logC2G.txt") file = open(self.dirProj+"/"+"logC2G.txt",'r') self.plotOutput(file) file.close() text = open(self.dirProj+"/"+"logC2G.txt",'r').read() self.textLog.setText(text) self.tabLayout.setCurrentWidget(self.textLog) #~~~~~~~~~~~~~~~~~~~~~~~~~ results ~~~~~~~~~~~~~~~~~~~~~~~~~# def resultTabs(self): tsz = 325 self.figure = Figure() self.graphres = FigureCanvas(self.figure) self.graphres.setFixedSize(tsz,tsz) self.toolbar = NavigationToolbar(self.graphres, self) self.toolbar.setFixedSize(tsz,25) self.qcoutputw = QtGui.QWidget() self.qcoutput = QtGui.QVBoxLayout(self.qcoutputw) self.qcoutput.addWidget(self.toolbar) self.qcoutput.addWidget(self.graphres) self.textLog = QtGui.QTextEdit(self) self.textLog.setFixedSize(343,388) self.textLog.setText("Run Logs") self.textLog.setLineWrapMode(0) self.textLog.setFontFamily("Monospace") # self.scene1 = QtGui.QGraphicsScene() # self.qclayout = QtGui.QGraphicsView(self.scene1) # self.qclayout.setFixedSize(tsz,tsz) # self.pixmap_item = QtGui.QGraphicsPixmapItem(QtGui.QPixmap('topologies/surface17a.png').scaled(tsz,tsz), None, self.scene1) self.tabLayout = QtGui.QTabWidget(self) self.tabLayout.addTab(self.qcoutputw,"Output") self.tabLayout.addTab(self.textLog,"Logs") self.topLayoutH2.addWidget(self.tabLayout,0,QtCore.Qt.AlignRight) def plotOutput(self,file): qbno = 0 ptrn = re.compile('(\d*)\squbits') for line in iter(file): mtch = ptrn.search(line) if mtch != None: qbno = int(mtch.group(1)) break ploty = [0]*(2**qbno) ptrn = re.compile('\(([+-]\d+.\d*),([+-]\d+.\d*)\)\s[|]([0-1]*)>') for line in iter(file): mtch = ptrn.search(line) if mtch != None: x = float(mtch.group(1)) y = float(mtch.group(2)) state = int(mtch.group(3),2) ploty[state] = x #x**2 + y**2 # check, sqrt reqd? OR z.z' cstmPloty = customPlot(ploty) #return ax = self.figure.add_subplot(1,1,1) ax.clear() #ax.plot(cstmPloty, '*-r') ax.bar(range(len(cstmPloty)),cstmPloty) ax.set_xlim([0,len(cstmPloty)]) ax.tick_params(axis='x',labelbottom='off') ax.set_ylim([-1,1]) self.graphres.draw() ########################## LEGACIES ########################## def color_picker(self): color = QtGui.QColorDialog.getColor() self.penColor = color # self.styleChoice.setStyleSheet("QWidget { background-color: %s}" % color.name()) def TBD(self): print("TBD")
class GUI(QtGui.QWidget): def __init__(self): super(GUI, self).__init__() self.setWindowTitle( 'Body Length Analysis' ) self.scaleFactor = 4 self.side = 'L' self.lbltxt = '"wheel" press: change side, currently %s\n"i" or "u" press: change cell sides' self.seamCellNames = ['a','b','c','1','2','3','4','5','6','t'] 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() spaceBox2 = QtGui.QHBoxLayout() mainWindow.addLayout(fileBox) mainWindow.addLayout(spaceBox1) mainWindow.addLayout(rawDataBox) mainWindow.addLayout(spaceBox2) Col1 = QtGui.QGridLayout() Col2 = QtGui.QHBoxLayout() Col3 = QtGui.QVBoxLayout() Col4 = QtGui.QVBoxLayout() rawDataBox.addLayout(Col1) rawDataBox.addLayout(Col2) rawDataBox.addLayout(Col3) rawDataBox.addLayout(Col4) 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:') widthLbl = QtGui.QLabel('Straightening Width:') 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.straightWidth = QtGui.QSpinBox(self) self.straightWidth.setMaximum(1000) self.straightWidth.setValue(100) 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(600,600)) self.canvas1.setSizePolicy( QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding ) self.cellTbl = QtGui.QTableWidget() # 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(widthLbl, 7, 0)#, 1, 1, Qt.AlignTop) Col1.addWidget(self.straightWidth, 7, 1)#, 1, 1, Qt.AlignTop) Col1.addWidget(straightenBtn, 8, 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()) # 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) 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): self.pathDial = QtGui.QFileDialog.getExistingDirectory(self, 'Select a folder', 'Y:\\Images') self.worm = self.pathDial.split("\\")[-1] self.path = self.pathDial[:-len(self.worm)] 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.setWindowTitle('Body Length Analysis - ' + self.pathDial) 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() 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, width = self.straightWidth.value() ) self.loadNewStack() self.canvas1.setFocus() #----------------------------------------------------------------------------------------------- # 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() 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 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() #----------------------------------------------------------------------------------------------- # 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 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() cellsNow = self.df[(self.df.rowtype=='cell')&(self.df.tidx==self.tp.value()-self.hatch.value())] horHeaders = ['tidx','times','cell name','cell side','cellXpos','cellYpos','cellZpos'] self.cellTbl.setColumnCount(len(horHeaders)) self.cellTbl.setRowCount(len(cellsNow)) self.cellTbl.setHorizontalHeaderLabels(horHeaders) self.cellTbl.horizontalHeader().setResizeMode(QtGui.QHeaderView.ResizeToContents) for idx, cell in cellsNow.iterrows(): row = idx-np.min(cellsNow.index.values) self.cellTbl.setItem(row,0,QtGui.QTableWidgetItem(str(int(cell.tidx)))) 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)))) 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)
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 Data_Visualization_GUI(object): def __init__(self,Data_Visualization): object.__init__(self) Data_Visualization.resize(1000,650) Data_Visualization.setWindowTitle("Didi's Data Visualizer") self.mainLayout = QtGui.QGridLayout(Data_Visualization) self.mainLayout.setSpacing(5) self.trackingPlotWidget = QtGui.QWidget() self.fig = pylab.figure() self.axes = pylab.Axes(self.fig, [.1,.1,.8,.8]) self.fig.add_axes(self.axes) self.plot, = pylab.plot([0],[0],'ro') self.axes.set_xlim(-5, 5) self.axes.set_ylim(-5, 5) self.plotTitle = pylab.title('Tracking Data') self.plotxLabel = pylab.xlabel('X') self.plotyLabel = pylab.ylabel('Z') self.canvas = FigureCanvas(self.fig) self.canvas.setParent(self.trackingPlotWidget) self.canvas.setFixedSize(400,400) self.mpl_toolbar = NavigationToolbar(self.canvas, self.trackingPlotWidget) ## ## ## self.XYZPlotWidget = QtGui.QWidget() ## pylab.subplot(311) ## self.fig2 = pylab.figure() ## self.axes2 = pylab.Axes(self.fig2, [.1,.1,.8,.8]) ## self.fig2.add_axes(self.axes2) ## self.plot2 = pylab.plot([0],[0],'r', [0],[0],'g', [0],[0],'b') ## self.axes2.set_xlim(-200, 0) ## self.axes2.set_ylim(-5, 5) ## pylab.legend([self.plot2[0], self.plot2[1], self.plot2[2]], ["X", "Y", "Z"], loc=3) ## self.plotxLabel2 = pylab.xlabel('Time') ## self.plotyLabel2 = pylab.ylabel('XYZ') ## self.canvas2 = FigureCanvas(self.fig2) ## self.canvas2.setParent(self.XYZPlotWidget) ## self.canvas2.setFixedSize(500,150) ## ## pylab.subplot(312) ## self.fig3 = pylab.figure() ## self.axes3 = pylab.Axes(self.fig3, [.1,.1,.8,.8]) ## self.fig3.add_axes(self.axes3) ## self.plot3 = pylab.plot([0],[0],'g', [0],[0],'r') ## self.axes3.set_xlim(-200, 0) ## self.axes3.set_ylim(-12, 12) ## pylab.legend([self.plot3[0], self.plot3[1]], ["Pitch", "Roll"], loc=3) ## self.plotxLabel3 = pylab.xlabel('Time') ## self.plotyLabel3 = pylab.ylabel('PR') ## self.canvas3 = FigureCanvas(self.fig3) ## self.canvas3.setParent(self.XYZPlotWidget) ## self.canvas3.setFixedSize(500,150) ## ## pylab.subplot(313) ## self.fig4 = pylab.figure() ## self.axes4 = pylab.Axes(self.fig4, [.1,.1,.8,.8]) ## self.fig4.add_axes(self.axes4) ## self.plot4 = pylab.plot([0],[0],'r', [0],[0],'g', [0],[0],'b', [0],[0],'y') ## pylab.legend([self.plot4[0], self.plot4[1], self.plot4[2], self.plot4[3]], ["w1", "w2", "w3", "w4"], loc=3) ## self.axes4.set_xlim(-200, 0) ## self.axes4.set_ylim(-0.5, 0.5) ## self.plotxLabel4 = pylab.xlabel('Time') ## self.plotyLabel4 = pylab.ylabel('WWWW') ## self.canvas4 = FigureCanvas(self.fig4) ## self.canvas4.setParent(self.XYZPlotWidget) ## self.canvas4.setFixedSize(500,150) self.XYZPlotWidget = QtGui.QWidget() self.fig2 = pylab.figure() self.axes2 = pylab.Axes(self.fig2, [.1,.1,.8,.8]) self.fig2.add_axes(self.axes2) self.plot2 = pylab.plot([0],[0],'r', [0],[0],'g', [0],[0],'b') self.axes2.set_xlim(-200, 0) self.axes2.set_ylim(-5, 5) pylab.legend([self.plot2[0], self.plot2[1], self.plot2[2]], ["X", "Y", "Z"], loc=3) # self.plotTitle2 = pylab.title('XYZ Data') self.plotxLabel2 = pylab.xlabel('Time') self.plotyLabel2 = pylab.ylabel('XYZ') self.canvas2 = FigureCanvas(self.fig2) self.canvas2.setParent(self.XYZPlotWidget) self.canvas2.setFixedSize(500,150) self.PRPlotWidget = QtGui.QWidget() self.fig3 = pylab.figure() self.axes3 = pylab.Axes(self.fig3, [.1,.1,.8,.8]) self.fig3.add_axes(self.axes3) self.plot3 = pylab.plot([0],[0],'g', [0],[0],'r') self.axes3.set_xlim(-200, 0) self.axes3.set_ylim(-12, 12) pylab.legend([self.plot3[0], self.plot3[1]], ["Pitch", "Roll"], loc=3) # self.plotTitle3 = pylab.title('Pitch/Roll Data') self.plotxLabel3 = pylab.xlabel('Time') self.plotyLabel3 = pylab.ylabel('PR') self.canvas3 = FigureCanvas(self.fig3) self.canvas3.setParent(self.PRPlotWidget) self.canvas3.setFixedSize(500,150) self.WWWWPlotWidget = QtGui.QWidget() self.fig4 = pylab.figure() self.axes4 = pylab.Axes(self.fig4, [.1,.1,.8,.8]) self.fig4.add_axes(self.axes4) self.plot4 = pylab.plot([0],[0],'r', [0],[0],'g', [0],[0],'b', [0],[0],'y') pylab.legend([self.plot4[0], self.plot4[1], self.plot4[2], self.plot4[3]], ["w1", "w2", "w3", "w4"], loc=3) self.axes4.set_xlim(-200, 0) self.axes4.set_ylim(-0.5, 0.5) # self.plotTitle4 = pylab.title('WWWW Data') self.plotxLabel4 = pylab.xlabel('Time') self.plotyLabel4 = pylab.ylabel('WWWW') self.canvas4 = FigureCanvas(self.fig4) self.canvas4.setParent(self.WWWWPlotWidget) self.canvas4.setFixedSize(500,150) self.lblX = QtGui.QLabel('X: ') self.lblY = QtGui.QLabel('Y: ') self.lblZ = QtGui.QLabel('Z: ') self.lblYaw = QtGui.QLabel('Yaw: ') self.lblPitch = QtGui.QLabel('Pitch: ') self.lblRoll = QtGui.QLabel('Roll: ') self.lblAltitude = QtGui.QLabel('Altitude: ') self.lblVX = QtGui.QLabel('Speed X: ') self.lblVY = QtGui.QLabel('Speed Y: ') self.lblVZ = QtGui.QLabel('Speed Z: ') self.lblTime = QtGui.QLabel('Time: ') self.lblCommand = QtGui.QLabel('Command: ') self.lblBattery = QtGui.QLabel('Battery: ') self.btnLoad = QtGui.QPushButton('Load') self.btnReplay = QtGui.QPushButton('Replay') self.mainLayout.addWidget(self.btnLoad,1,1) self.mainLayout.addWidget(self.btnReplay,1,2) self.mainLayout.addWidget(self.lblX,2,0) self.mainLayout.addWidget(self.lblY,2,1) self.mainLayout.addWidget(self.lblZ,2,2) self.mainLayout.addWidget(self.lblYaw,3,0) self.mainLayout.addWidget(self.lblPitch,3,1) self.mainLayout.addWidget(self.lblRoll,3,2) self.mainLayout.addWidget(self.lblAltitude,3,3) self.mainLayout.addWidget(self.lblVX,4,0) self.mainLayout.addWidget(self.lblVY,4,1) self.mainLayout.addWidget(self.lblVZ,4,2) self.mainLayout.addWidget(self.lblTime,5,0) self.mainLayout.addWidget(self.lblCommand,5,1) self.mainLayout.addWidget(self.lblBattery,5,2) self.mainLayout.addWidget(self.mpl_toolbar,6,0,1,4) self.mainLayout.addWidget(self.trackingPlotWidget,7,0,5,4) self.mainLayout.addWidget(self.XYZPlotWidget,2,5,6,4) self.mainLayout.addWidget(self.PRPlotWidget,8,5,2,4) self.mainLayout.addWidget(self.WWWWPlotWidget,10,5,2,4) for i in range(self.mainLayout.count()): w = self.mainLayout.itemAt(i).widget() if type(w)==QtGui.QLabel: w.setFixedSize(100, 15) QtCore.QObject.connect(self.btnReplay, QtCore.SIGNAL('clicked()'), Data_Visualization.visualize) QtCore.QObject.connect(self.btnLoad, QtCore.SIGNAL('clicked()'), Data_Visualization.open_file)
class GUI(QtGui.QWidget): def __init__(self): self.FFimage = 0 self.BCK = 0 self.Std_Dev_image = [0, 0] self.mask = [0, 0] self.savepath = 0 self.BFpath = 0 self.randcmap = plt.matplotlib.colors.ListedColormap( np.random.rand(256, 3)) self.Hoffset = 0 self.Voffset = 0 super(GUI, self).__init__() self.setWindowTitle('FROS ANALYSIS') self.initUI() #----------------------------------------------------------------------------------------------- # INITIALIZATION OF THE WINDOW - DEFINE AND PLACE ALL THE WIDGETS #----------------------------------------------------------------------------------------------- def initUI(self): # SET THE GEOMETRY mainWindow = QtGui.QVBoxLayout() self.setLayout(mainWindow) self.setGeometry(100, 100, 900, 900) IObox = QtGui.QHBoxLayout() Canvasbox = QtGui.QHBoxLayout() Actionsbox = QtGui.QHBoxLayout() Textbox = QtGui.QVBoxLayout() Spinboxes = QtGui.QGridLayout() mainWindow.addLayout(IObox) mainWindow.addLayout(Canvasbox) mainWindow.addLayout(Actionsbox) mainWindow.addLayout(Textbox) Canvasbox.addLayout(Spinboxes) # mainWindow.setRowMinimumHeight(9, 100) # mainWindow.setRowMinimumHeight(12, 100) # DEFINE ALL WIDGETS AND BUTTONS #I/O loadBtn = QtGui.QPushButton('Load Bright Field image') loadBCKbtn = QtGui.QPushButton('Load Background Image') saveBtn = QtGui.QPushButton('Save data') #ACTIONS st_devBtn = QtGui.QPushButton('Compute Std Dev Image') SegmentationBtn = QtGui.QPushButton('Compute Segmentation Mask') remove_oddshsapeBtn = QtGui.QPushButton('Remove odd shaped objects') ALLSegmentationBtn = QtGui.QPushButton('Do ALL segmentation') ThScLbl = QtGui.QLabel('Threshold scaling:') SeedMinLbl = QtGui.QLabel('Seeds minimum size:') SeedMaxLbl = QtGui.QLabel('Seeds maximum size:') CellMinLbl = QtGui.QLabel('Cells minimum size:') CellMaxLbl = QtGui.QLabel('Cells maximum size:') OffStLbl = QtGui.QLabel('Offset:') BlckSzLbl = QtGui.QLabel('Block Size:') #SPINBOXES self.ThSc = QtGui.QDoubleSpinBox(self) self.ThSc.setSingleStep(0.01) self.ThSc.setMaximum(10000) self.ThSc.setValue(1) self.SeedMin = QtGui.QSpinBox(self) self.SeedMin.setMaximum(100000) self.SeedMin.setValue(0) self.SeedMax = QtGui.QSpinBox(self) self.SeedMax.setMaximum(100000) self.SeedMax.setValue(500) self.CellMin = QtGui.QSpinBox(self) self.CellMin.setMaximum(100000) self.CellMin.setValue(50) self.CellMax = QtGui.QSpinBox(self) self.CellMax.setMaximum(100000) self.CellMax.setValue(500) self.OffSt = QtGui.QSpinBox(self) self.OffSt.setRange(-100000, 100000) self.OffSt.setValue(-300) self.BlckSz = QtGui.QSpinBox(self) self.BlckSz.setMaximum(100000) self.BlckSz.setValue(101) self.StdBox = QtGui.QCheckBox('Method : Std Dev') self.OtsuBox = QtGui.QCheckBox('Method : Otsu') self.AdaptBox = QtGui.QCheckBox('Method : Adapt Threshold') 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.setFixedSize(QtCore.QSize(400, 400)) self.fig2 = Figure((8.0, 8.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(400, 400)) # PLACE ALL THE WIDGET ACCORDING TO THE mainWindowS IObox.addWidget(loadBtn) IObox.addWidget(loadBCKbtn) IObox.addWidget(saveBtn) Canvasbox.addWidget(self.canvas1) Canvasbox.addWidget(self.canvas2) Actionsbox.addWidget(st_devBtn) Actionsbox.addWidget(remove_oddshsapeBtn) Actionsbox.addWidget(SegmentationBtn) Actionsbox.addWidget(ALLSegmentationBtn) self.Toprinttext = QtGui.QTextEdit() Textbox.addWidget(self.Toprinttext) mainWindow.addWidget(self.HLine()) Spinboxes.addWidget(ThScLbl, 0, 0, 1, 1, QtCore.Qt.AlignTop) Spinboxes.addWidget(self.ThSc, 0, 1, 1, 1, QtCore.Qt.AlignTop) Spinboxes.addWidget(SeedMinLbl, 1, 0, 1, 1, QtCore.Qt.AlignTop) Spinboxes.addWidget(self.SeedMin, 1, 1, 1, 1, QtCore.Qt.AlignTop) Spinboxes.addWidget(SeedMaxLbl, 2, 0, 1, 1, QtCore.Qt.AlignTop) Spinboxes.addWidget(self.SeedMax, 2, 1, 1, 1, QtCore.Qt.AlignTop) Spinboxes.addWidget(CellMinLbl, 3, 0, 1, 1, QtCore.Qt.AlignTop) Spinboxes.addWidget(self.CellMin, 3, 1, 1, 1, QtCore.Qt.AlignTop) Spinboxes.addWidget(CellMaxLbl, 4, 0, 1, 1, QtCore.Qt.AlignTop) Spinboxes.addWidget(self.CellMax, 4, 1, 1, 1, QtCore.Qt.AlignTop) Spinboxes.addWidget(self.AdaptBox, 5, 0, 1, 1) Spinboxes.addWidget(self.OtsuBox, 6, 0, 1, 1) Spinboxes.addWidget(self.StdBox, 7, 0, 1, 1) Spinboxes.addWidget(self.HLine(), 8, 0, 1, 1, QtCore.Qt.AlignTop) Spinboxes.addWidget(OffStLbl, 9, 0, 1, 1, QtCore.Qt.AlignTop) Spinboxes.addWidget(self.OffSt, 9, 1, 1, 1, QtCore.Qt.AlignTop) Spinboxes.addWidget(BlckSzLbl, 10, 0, 1, 1, QtCore.Qt.AlignTop) Spinboxes.addWidget(self.BlckSz, 10, 1, 1, 1, QtCore.Qt.AlignTop) mainWindow.addWidget(self.HLine()) mainWindow.addWidget(self.VLine()) self.setFocus() self.show() # BIND BUTTONS TO FUNCTIONS loadBtn.clicked.connect(self.loadDataset) loadBCKbtn.clicked.connect(self.loadBCKimage) saveBtn.clicked.connect(self.saveMask) SegmentationBtn.clicked.connect(self.Compute_SegmentationMask) remove_oddshsapeBtn.clicked.connect(self.Remove_odd_shaped_objects) st_devBtn.clicked.connect(self.Compute_StdDev) ALLSegmentationBtn.clicked.connect(self.do_ALL_segmentation) self.fig2.canvas.mpl_connect('button_press_event', self.remove_cells) # self.SeedMin.valueChanged.connect(self.updateCanvas1) #----------------------------------------------------------------------------------------------- # 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 #----------------------------------------------------------------------------------------------- # BUTTON FUNCTIONS #----------------------------------------------------------------------------------------------- def loadDataset(self): if self.BFpath: self.BFpath = QtGui.QFileDialog.getOpenFileName( self, 'Choose an Image', self.BFpath) else: self.BFpath = QtGui.QFileDialog.getOpenFileName( self, 'Choose an Image', '.') self.BF = io.imread(str(self.BFpath)) self.bfname = str(self.BFpath).split("/")[-1] self.updateCanvas1() self.print_on_textbox(self.BFpath) self.setFocus() def loadBCKimage(self): self.BCKpath = QtGui.QFileDialog.getOpenFileName() self.BCK = io.imread(str(self.BCKpath)) plt.imshow(self.BCK) plt.show() def Compute_StdDev(self): self.Std_Dev_image = std_image(self.BF) def Remove_odd_shaped_objects(self): for i in range(self.mask2.max()): tmpmask = self.mask2 == i rps = msr.regionprops(tmpmask.astype(np.int8)) if rps[0].eccentricity < 0.85: self.mask2[self.mask2 == i] = 0 [l, n] = msr.label(self.mask2, return_num=True) remove_reg(l, 50, 1000) self.mask2 = l self.updateCanvas1() self.updateCanvas2() def Compute_SegmentationMask(self): if self.StdBox.isChecked(): if sum(sum(self.Std_Dev_image)) == 0: QtGui.QMessageBox.warning( self, "Cannot perform required operation", "Compute the standard deviation image first!", QtGui.QMessageBox.Ok) return else: method = 'std' if self.AdaptBox.isChecked(): method = 'adaptive' if self.OtsuBox.isChecked(): method = 'otsu' [s, n, l] = mySegmentation(self.BF, self.Std_Dev_image, method, 'B', self.ThSc.value(), self.CellMin.value(), self.CellMax.value(), self.SeedMin.value(), self.SeedMax.value(), self.BlckSz.value(), self.OffSt.value()) self.s = s self.mask = l self.mask2 = np.copy(self.mask) self.print_on_textbox(str(n)) self.updateCanvas2() self.updateCanvas1() def do_ALL_segmentation(self): self.ALLpath = QtGui.QFileDialog.getExistingDirectory( self, 'choose folder containing all the bright field images', 'H:\\Jacopo') self.savepath = QtGui.QFileDialog.getExistingDirectory( self, 'choose folder to save masks', 'D:\\ANALISYS\\2015\\1.CLUSTER_OF_RECEPTORS_TRACKING') files = glob.glob(self.ALLpath + '/*.tif') print(files) for i in files: self.BF = io.imread(i) self.bfname = str(i).split("\\")[-1] self.Compute_SegmentationMask() self.Remove_odd_shaped_objects() myreply = QtGui.QMessageBox.warning( self, "are you ok with this segmentation?", "go on?", QtGui.QMessageBox.Ok, QtGui.QMessageBox.No, QtGui.QMessageBox.Cancel) if myreply == QtGui.QMessageBox.Ok: io.imsave( str(self.savepath) + '/' + self.bfname[:-4] + '_mask' + '.tif', self.mask2) if myreply == QtGui.QMessageBox.Cancel: break def saveMask(self): self.savepath = QtGui.QFileDialog.getExistingDirectory( self, 'choose saving position', 'D:\\ANALISYS\\2015\\1.CLUSTER_OF_RECEPTORS_TRACKING\\1.Tracks_and_masks_for_mobility_structures' ) io.imsave( str(self.savepath) + '/' + self.bfname[:-4] + '_mask' + '.tif', np.uint16(self.mask2)) def remove_cells(self, event): value = self.mask2[np.round(event.ydata), np.round(event.xdata)] self.mask2[self.mask2 == value] = 0 self.updateCanvas2() self.updateCanvas1() #----------------------------------------------------------------------------------------------- # UPDATE FUNCTIONS #----------------------------------------------------------------------------------------------- def print_on_textbox(self, mystring): self.Toprinttext.append(mystring) def updateCanvas1(self): self.ax1.cla() if sum(sum(self.mask)) != 0: self.BF2 = np.copy(self.BF) borders = sgm.find_boundaries(self.mask2) self.BF2[borders] = 0.2 * np.double(self.BF2.max()) self.ax1.imshow(self.BF2, cmap='gray', interpolation='none') self.canvas1.draw() self.setFocus() else: self.ax1.imshow(self.BF, cmap='gray', interpolation='none') #,vmax=mymin*15,vmin=mymin) self.fig1.subplots_adjust(left=0., right=1., top=1., bottom=0.) self.canvas1.draw() self.setFocus() def updateCanvas2(self): cmap = self.randcmap cmap.set_under(color='white') self.ax2.cla() self.ax2.imshow(self.mask2, cmap=cmap, interpolation='none', vmin=0.0001) #,vmax=mymin*15,vmin=mymin) self.fig2.subplots_adjust(left=0., right=1., top=1., bottom=0.) self.canvas2.draw() self.setFocus() def keyPressEvent(self, event): # print(event.key()) self.mask2 = self.mask2.astype(np.float64) self.mask = self.mask.astype(np.float64) if event.key() == QtCore.Qt.Key_Right: self.Hoffset -= 1 self.tform = tr.SimilarityTransform( translation=(int(self.Hoffset), int(self.Voffset))) self.mask2 = tr.warp(self.mask, self.tform, output_shape=(int(512), int(512))) [self.mask2, n] = msr.label(self.mask2, return_num=True) self.mask2 = sgm.clear_border(self.mask2) self.updateCanvas2() self.updateCanvas1() if event.key() == QtCore.Qt.Key_Left: self.Hoffset += 1 self.tform = tr.SimilarityTransform(translation=(self.Hoffset, self.Voffset)) self.mask2 = tr.warp(self.mask, self.tform, output_shape=(int(512), int(512))) [self.mask2, n] = msr.label(self.mask2, return_num=True) self.mask2 = sgm.clear_border(self.mask2) self.updateCanvas2() self.updateCanvas1() if event.key() == QtCore.Qt.Key_Up: self.Voffset += 1 self.tform = tr.SimilarityTransform(translation=(self.Hoffset, self.Voffset)) self.mask2 = tr.warp(self.mask, self.tform, output_shape=(int(512), int(512))) [self.mask2, n] = msr.label(self.mask2, return_num=True) self.mask2 = sgm.clear_border(self.mask2) self.updateCanvas2() self.updateCanvas1() if event.key() == QtCore.Qt.Key_Down: self.Voffset -= 1 self.tform = tr.SimilarityTransform(translation=(self.Hoffset, self.Voffset)) self.mask2 = tr.warp(self.mask, self.tform, output_shape=(int(512), int(512))) [self.mask2, n] = msr.label(self.mask2, return_num=True) self.mask2 = sgm.clear_border(self.mask2) self.updateCanvas2() self.updateCanvas1() 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) # 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 PomoWindow(QtGui.QWidget): def __init__(self, pomo): """ Makes necessary variable initializations and calls other init methods. """ super(PomoWindow, self).__init__() self.pomo = pomo # Enhance readability self.getString = self.pomo.getString self.initUI() if useAudio: self.initAudio() self.timerActive = False self.timer = QtCore.QTimer() self.timer.timeout.connect(self.tick) def initUI(self): """ Initializes the UI. """ # Set some general window settings. self.setFixedSize(480, 310) self.move(250, 250) self.setWindowTitle(self.getString("app_name")) self.setWindowIcon(QtGui.QIcon('/usr/share/icons/pomodorino.png')) self.trayIcon = QtGui.QSystemTrayIcon( QtGui.QIcon('/usr/share/icons/pomodorino.png'), self) self.trayIcon.setVisible(True) self.trayIcon.activated.connect(self.trayClick) # Add a minimal context menu for the tray icon. #trayMenu = QtGui.QMenu(self) #trayMenu.addAction("Quit", self.close) #self.trayIcon.setContextMenu(trayMenu) # Initialize and display the tabs pomoTab = self.initPomoTab() tasksTab = self.initTasksTab() activityTab = self.initActivityTab() tabWidget = QtGui.QTabWidget(self) tabWidget.resize(479, 309) tabWidget.addTab(pomoTab, self.getString("tab_pomo")) tabWidget.addTab(tasksTab, self.getString("tab_tasks")) tabWidget.addTab(activityTab, self.getString("tab_activity")) self.show() def initAudio(self): """ Detects an ALSA audio device and buffers our ringtone. """ # Detect an ALSA audio device self.audioDevice = alsaaudio.PCM() # Open the ringtone and set some audio settings. ring = wave.open("/usr/share/pomodorino/ring.wav", 'rb') self.audioDevice.setchannels(ring.getnchannels()) self.audioDevice.setrate(ring.getframerate()) self.audioDevice.setformat(alsaaudio.PCM_FORMAT_S16_LE) self.audioDevice.setperiodsize(320) # Read the audio data of the ringtone. self.audioData = list() buf = ring.readframes(320) while buf: self.audioData.append(buf) buf = ring.readframes(320) def closeEvent(self, event): """ Prevents accidental shutdowns by asking the user. """ if self.timerActive: self.stopTimer() if self.promptUser("ask_paused"): event.accept() else: event.ignore() self.startTimer(0, restart=True) else: event.accept() def setTitle(self, title): """ Sets the window title. """ if title is None: title = self.getString("app_name") else: title += " - " + self.getString("app_name") self.window().setWindowTitle(title) def startTimer(self, timeSpan, restart=False): """ Starts the timer. """ if restart is False: self.timerType = timeSpan self.timerCount = timeSpan * 60 self.timerActive = True self.timer.start(1000) def stopTimer(self): """ Stops the timer. """ self.timerActive = False self.timer.stop() def resetTimer(self): """ Resets the timer. """ self.stopTimer() self.timerCount = 0 def finishTimer(self, task): """ Is called once the timer finished. """ self.resetTimer() # Define the regular length of a pomodoro. Purely for debugging reasons POMO_CONST = 25 pomos = math.floor(self.timerType / POMO_CONST) self.setVisible(True) if pomos >= 1 and task != '': newTask = self.pomo.pomoData.addPomo(task, pomos) if newTask is True: self.pomoTaskBar.addItem(task, None) self.fillActivityTab() self.updateTasksTab() def trayClick(self, reason): """ Is called when the user clicks on the tray icon. """ if reason == 2 or reason == 3: if self.isVisible(): self.setVisible(False) else: self.setVisible(True) def promptUser(self, identifier, additional=None): """ Creates predefined confirmation/warning dialogs. """ if identifier == 'ask_paused': reply = QtGui.QMessageBox.question( self, self.getString("ask_paused_title"), self.getString("ask_paused_text"), QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.Yes: return True else: return False elif identifier == 'warn_notask': QtGui.QMessageBox.warning(self, self.getString("warn_notask_title"), self.getString("warn_notask_text")) return elif identifier == 'ask_taskdel' and additional != None: askText = self.getString("ask_taskdel_text") askText = str.replace(askText, "%taskname%", str(additional)) reply = QtGui.QMessageBox.question( self, self.getString("ask_taskdel_title"), askText, QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No) if reply == QtGui.QMessageBox.Yes: return True else: return False raise KeyError ############## ## Pomo Tab ## ############## def initPomoTab(self): """ Creates the layout of the pomodoro tab """ pomoTab = QtGui.QWidget() # Create a combobox with a lineedit for task selection. taskBar = QtGui.QComboBox() taskBar.setEditable(True) taskBarLine = taskBar.lineEdit() taskBarLine.setMaxLength(64) taskBarLine.setPlaceholderText(self.getString("input_task")) taskBar.setFixedSize(375, 32) taskBar.setInsertPolicy(QtGui.QComboBox.InsertAlphabetically) taskBar.setStyleSheet('font-size: 11pt;') taskBar.addItem("", None) self.pomoTaskBar = taskBar # Add all the task names. tasks = self.pomo.pomoData.tasks for taskID, taskName, pomoCount, pomoLast in tasks: taskBar.addItem(taskName, None) # Create the main button. mainBtn = QtGui.QPushButton(self.getString("btn_start"), pomoTab) mainBtn.setFixedSize(180, 60) mainBtn.setStyleSheet('font-size: 17pt;') # Create the 25 min button. timeBtn = QtGui.QPushButton(self.getString("lbl_regularpomo"), pomoTab) timeBtn.setToolTip(self.getString("ttp_regularpomo")) timeBtn.setFixedSize(50, 25) timeBtn.setStyleSheet('font-size: 9pt;') # Create the 50 min button. longTimeBtn = QtGui.QPushButton(self.getString("lbl_longpomo"), pomoTab) longTimeBtn.setToolTip(self.getString("ttp_longpomo")) longTimeBtn.setFixedSize(50, 25) longTimeBtn.setStyleSheet('font-size: 9pt;') # Create the 5 min button. pauseBtn = QtGui.QPushButton(self.getString("lbl_shortpause"), pomoTab) pauseBtn.setToolTip(self.getString("ttp_shortpause")) pauseBtn.setFixedSize(50, 25) pauseBtn.setStyleSheet('font-size: 9pt;') # Create the 10 min button. longPauseBtn = QtGui.QPushButton(self.getString("lbl_longpause"), pomoTab) longPauseBtn.setToolTip(self.getString("ttp_longpause")) longPauseBtn.setFixedSize(50, 25) longPauseBtn.setStyleSheet('font-size: 9pt;') # Select 25 min button as default on startup. timeBtn.setDisabled(True) self.pomoButtonActive = timeBtn # Save button references for later usage. self.pomoBtns = dict() self.pomoBtns['main'] = mainBtn self.pomoBtns['time'] = timeBtn self.pomoBtns['longTime'] = longTimeBtn self.pomoBtns['pause'] = pauseBtn self.pomoBtns['longPause'] = longPauseBtn # Connect the buttons to the handler function. for name, button in self.pomoBtns.items(): button.clicked.connect(self.onClicked) # Create and set the layout. firstRow = QtGui.QHBoxLayout() firstRow.addWidget(taskBar) secondRow = QtGui.QHBoxLayout() secondRow.addStretch() secondRow.addWidget(pauseBtn) secondRow.addWidget(longPauseBtn) secondRow.addStretch() secondRow.addWidget(timeBtn) secondRow.addWidget(longTimeBtn) secondRow.addStretch() thirdRow = QtGui.QHBoxLayout() thirdRow.addWidget(mainBtn) vbox = QtGui.QVBoxLayout() vbox.addStretch() vbox.addLayout(firstRow) vbox.addStretch() vbox.addLayout(secondRow) vbox.addStretch() vbox.addLayout(thirdRow) vbox.addStretch() pomoTab.setLayout(vbox) return pomoTab def onClicked(self): """ Main function for catching button clicks in the pomo tab. """ sender = self.sender() if sender == self.pomoBtns['main']: return self.onClickedPomoMain() elif (sender == self.pomoBtns['pause'] or sender == self.pomoBtns['longPause'] or sender == self.pomoBtns['time'] or sender == self.pomoBtns['longTime']): return self.onClickedPomoTime() raise ValueError() def onClickedPomoMain(self): """ Starts/Stops the timer depending on the state of the button. """ sender = self.sender() if self.timerActive: self.stopTimer() # Ask the user whether he wants to reset the running timer. if self.promptUser("ask_paused"): self.resetPomoTab() self.resetTimer() else: self.startTimer(0, restart=True) else: newText = self.pomoTaskBar.currentText().strip() self.pomoTaskBar.setEditText(newText) if newText != "" or self.pomoTaskBar.isEnabled() is False: self.pomoTaskBar.setDisabled(True) for k, v in self.pomoBtns.items(): if k != 'main': v.setDisabled(True) timeSpan = int(self.pomoButtonActive.text()) title = "{0:0>2}:00".format(timeSpan) sender.setText(title) self.setTitle(title) self.startTimer(timeSpan) else: self.promptUser("warn_notask") def onClickedPomoTime(self): """ Toggles the state of the timer buttons in the pomo tab. """ sender = self.sender() if self.pomoBtns['main'].text() == 'start': self.pomoButtonActive.setDisabled(False) sender.setDisabled(True) self.pomoButtonActive = sender if sender == self.pomoBtns['pause'] or sender == self.pomoBtns[ 'longPause']: self.pomoTaskBar.setDisabled(True) else: self.pomoTaskBar.setDisabled(False) def resetPomoTab(self): """ Resets the button states in the pomo tab. """ if self.pomoButtonActive != self.pomoBtns[ 'pause'] and self.pomoButtonActive != self.pomoBtns[ 'longPause']: self.pomoTaskBar.setDisabled(False) self.pomoBtns['main'].setText("start") for k, v in self.pomoBtns.items(): if k != 'main' and v is not self.pomoButtonActive: v.setDisabled(False) # Also reset the window title. self.setTitle(None) def tick(self): """ Updates the GUI every second when the timer is running. """ self.timerCount -= 1 timeStr = "{0:0>2}:{1:0>2}".format(math.floor(self.timerCount / 60), self.timerCount % 60) self.pomoBtns['main'].setText(timeStr) # Show current time in the title. self.setTitle(timeStr) # Timer finished? if self.timerCount == 0: self.playRingtone() self.finishTimer(self.pomoTaskBar.currentText()) self.resetPomoTab() def playRingtone(self): """ Creates a thread for playing the ringtone. """ t = threading.Thread(target=self.playRingThread) # Kill the thread once it finishes. t.daemon = True t.start() def playRingThread(self): """ Plays the Ringtone. """ for data in self.audioData: self.audioDevice.write(data) ############### ## tasks tab ## ############### def initTasksTab(self): """ Creates the layout of the tasks tab. """ tasksTab = QtGui.QWidget() self.tasksTable = self.fillTasksTable() self.tasksVBox = QtGui.QVBoxLayout() self.tasksVBox.addWidget(self.tasksTable) self.tasksTable.sortItems(0) tasksTab.setLayout(self.tasksVBox) return tasksTab def fillTasksTable(self): """ Fills the table in the tasks tab. """ tasks = self.pomo.pomoData.tasks self.taskTableSelChange = False # Create a table with three columns. table = QtGui.QTableWidget(len(tasks), 3) table.itemSelectionChanged.connect(self.taskListSelectionChanged) table.itemClicked.connect(self.taskListClick) table.setShowGrid(True) table.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) table.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) table.setFocusPolicy(QtCore.Qt.NoFocus) table.setHorizontalHeaderLabels([ self.getString("lbl_stats_task"), self.getString("lbl_stats_pomos"), self.getString("lbl_stats_last") ]) table.verticalHeader().setVisible(False) table.horizontalHeader().setHighlightSections(False) # Create a context menu for the table. def ctMenuEvent(event): self.taskTableSelChange = False menu = QtGui.QMenu(table) rnAct = menu.addAction("Rename", self.renameTask) dlAct = menu.addAction("Delete", self.deleteTask) menu.popup(QtGui.QCursor.pos()) if self.timerActive and self.pomoTaskBar.currentText( ) == self.tasksTable.selectedItems()[0].text(): rnAct.setEnabled(False) dlAct.setEnabled(False) table.contextMenuEvent = ctMenuEvent # Columwidth depends on the existence of a scrollbar. if len(tasks) <= 7: table.setColumnWidth(0, 345) else: table.setColumnWidth(0, 329) table.setColumnWidth(1, 48) table.setColumnWidth(2, 60) # There must be a row counter since the taskID can be different. rowCount = 0 # Fill the table rows. for taskID, taskName, pomoCount, pomoLast in tasks: # First column: taskName item = QtGui.QTableWidgetItem() item.setText(taskName) item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable) table.setItem(rowCount, 0, item) # Second column: pomoCount item = QtGui.QTableWidgetItem() item.setText(str(pomoCount)) item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable) item.setToolTip("~" + str(round((pomoCount * 25) / 60, 1)) + "h") table.setItem(rowCount, 1, item) # Third column: pomoLast pomoLastDate = datetime.datetime.fromtimestamp(pomoLast) item = QtGui.QTableWidgetItem() item.setText(pomoLastDate.strftime("%x")) item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable) table.setItem(rowCount, 2, item) rowCount += 1 return table def updateTasksTab(self): """ Updates the pomodoro statistics in the tasks tab. """ self.tasksVBox.removeWidget(self.tasksTable) self.tasksTable.close() self.tasksTable = self.fillTasksTable() self.tasksVBox.insertWidget(0, self.tasksTable) def taskListSelectionChanged(self): """ Sets a flag when the selection in the task table was changed. """ if self.tasksTable.selectedItems() != []: self.taskTableSelChange = True def taskListClick(self, item): """ Detects when an item in the task table was clicked and clears selection if necessary. """ if self.taskTableSelChange == False: self.tasksTable.clearSelection() self.taskTableSelChange = False def deleteTask(self): """ Deletes a task by the users request. """ taskName = self.tasksTable.selectedItems()[0].text() okay = self.promptUser("ask_taskdel", additional=taskName) if okay: # Delete entry from GUI pbID = self.pomoTaskBar.findText(taskName) self.pomoTaskBar.removeItem(pbID) stID = self.activitySelTask.findText(taskName) self.activitySelTask.removeItem(stID) rownum = self.tasksTable.row(self.tasksTable.selectedItems()[0]) self.tasksTable.removeRow(rownum) if self.tasksTable.rowCount() <= 7: self.tasksTable.setColumnWidth(0, 345) # Delete entry from db and cache taskID = self.pomo.pomoData.getTaskID(taskName) self.pomo.pomoData.delTask(taskID) # Reset the activity tab self.fillActivityTab() def renameTask(self, warn=""): """ Renames a task by the users request. """ oldname = self.tasksTable.selectedItems()[0].text() name, okay = QtGui.QInputDialog.getText( self, self.getString("lbl_rename_task"), warn, text=oldname) if okay and name != '' and name != oldname: try: self.pomo.pomoData.getTaskID(name) self.renameTask(self.getString("lbl_rename_taken")) except KeyError: # Update entry in GUI self.tasksTable.selectedItems()[0].setText(name) pbID = self.pomoTaskBar.findText(oldname) self.pomoTaskBar.setItemText(pbID, name) stID = self.activitySelTask.findText(oldname) self.activitySelTask.setItemText(stID, name) # Update entry in db and cache tID = self.pomo.pomoData.getTaskID(oldname) self.pomo.pomoData.renameTask(tID, name) ################## ## activity tab ## ################## def initActivityTab(self): """ Creates the layout of the activity tab and prepares the graph. """ activityTab = QtGui.QWidget() # Get the background color of the window to make the graph fit in. color = self.palette().color(QtGui.QPalette.Background) # Create a fixed-size canvas for the graph. self.figure = plt.figure(facecolor=color.name(), tight_layout=False) self.canvas = FigureCanvas(self.figure) self.canvas.setFixedSize(460, 236) # Set default values for some attributes used in fillActivityTab self.lockYLim = False self.intervalShift = 0 self.intervalScale = 3 # [3] days - [2] weeks - [1] months self.activityTaskID = 0 # Combobox for selecting the active task to be displayed. selTask = QtGui.QComboBox() selTask.insertItem(0, self.getString("lbl_stats_all"), None) for tID, tskName, pomoCount, pomoLast in self.pomo.pomoData.tasks: selTask.addItem(tskName, None) selTask.currentIndexChanged['QString'].connect(self.onChangeTask) # Save handle for later use. self.activitySelTask = selTask # Navigation buttons to change the active timespan. farLeftButton = QtGui.QPushButton("<<", activityTab) farLeftButton.setStyleSheet('font-size: 12pt;') farLeftButton.setFixedSize(30, 20) farLeftButton.clicked.connect(self.timeNavigate) # Save the handle for toggling the button state self.farLeftButtonHandle = farLeftButton farRightButton = QtGui.QPushButton(">>", activityTab) farRightButton.setStyleSheet('font-size: 12pt;') farRightButton.setFixedSize(30, 20) farRightButton.setDisabled(True) farRightButton.clicked.connect(self.timeNavigate) # Save the handle for toggling the button state. self.farRightButtonHandle = farRightButton leftButton = QtGui.QPushButton("<", activityTab) leftButton.setStyleSheet('font-size: 12pt;') leftButton.setFixedSize(30, 20) leftButton.clicked.connect(self.timeNavigate) # Save the handle for toggling the button state self.leftButtonHandle = leftButton rightButton = QtGui.QPushButton(">", activityTab) rightButton.setStyleSheet('font-size: 12pt;') rightButton.setFixedSize(30, 20) rightButton.setDisabled(True) rightButton.clicked.connect(self.timeNavigate) # Save the handle for toggling the button state. self.rightButtonHandle = rightButton # Disable left navigation buttons when there are no finished pomos. if self.pomo.pomoData.firstPomo == 0: leftButton.setDisabled(True) farLeftButton.setDisabled(True) # Zoom buttons to change the active timespan. zoomOutButton = QtGui.QPushButton("−", activityTab) zoomOutButton.setStyleSheet('font-size: 12pt;') zoomOutButton.setFixedSize(30, 20) zoomOutButton.clicked.connect(self.timeZoom) # Save the handle for toggling the button state. self.zoomOutButtonHandle = zoomOutButton zoomInButton = QtGui.QPushButton("+", activityTab) zoomInButton.setStyleSheet('font-size: 12pt;') zoomInButton.setFixedSize(30, 20) zoomInButton.setDisabled(True) zoomInButton.clicked.connect(self.timeZoom) # Save the handle for toggling the button state. self.zoomInButtonHandle = zoomInButton # Get highest pomo count on a single day. self.highestPomoCount = list() self.highestPomoCount.append( self.pomo.pomoData.getHighestPomoCountMonthly()) self.highestPomoCount.append( self.pomo.pomoData.getHighestPomoCountWeekly()) self.highestPomoCount.append( self.pomo.pomoData.getHighestPomoCountDaily()) # Draw the graph. self.fillActivityTab() # Create and set the layout. layout = QtGui.QVBoxLayout() layout.addWidget(self.canvas) layout2 = QtGui.QHBoxLayout() layout2.addWidget(farLeftButton) layout2.addWidget(leftButton) layout2.addWidget(rightButton) layout2.addWidget(farRightButton) layout2.addStretch() layout2.addWidget(zoomOutButton) layout2.addWidget(zoomInButton) layout2.addStretch() layout2.addWidget(selTask) layout.addLayout(layout2) activityTab.setLayout(layout) return activityTab def fillActivityTab(self): """ Fills and displays the bar graph in the activity tab. """ taskID = self.activityTaskID # First get the absolute value of today. today = datetime.date.today() delta = datetime.timedelta(days=1) # Now construct shiftable intervals. beginInt = datetime.datetime endInt = datetime.datetime # Default scale (days): Begin interval at midnight of today. beginInt = datetime.datetime(today.year, today.month, today.day, 0, 0, 0) shiftDelta = delta if self.intervalScale == 2: # Scale: Weeks # Begin interval at midnight of this weeks monday. weekDayNum = calendar.weekday(today.year, today.month, today.day) beginInt = beginInt - delta * weekDayNum shiftDelta = 7 * delta elif self.intervalScale == 1: # Scale: Months # Begin interval at midnight of the first day of the month. beginInt = datetime.datetime(today.year, today.month, 1, 0, 0, 0) shiftDelta = 30 * delta self.shiftDelta = shiftDelta # Get the data of the last units since today. units = list() values = list() size = 6 + self.intervalScale for i in range(size): # Shift offset = (size - i - 1 - self.intervalShift) shiftedBegin = beginInt - offset * shiftDelta # When scaled to months, an arithmetical shift is not practical. if self.intervalScale == 1: yearDiff, monDiff = divmod(offset, 12) newMon = beginInt.month - monDiff if newMon < 0: newMon = 12 + newMon yearDiff += 1 if newMon == 0: newMon = 12 yearDiff += 1 shiftedBegin = datetime.datetime(beginInt.year - yearDiff, newMon, 1, 0, 0, 0) shiftedEnd = datetime.datetime if self.intervalScale == 3: units.append( str(shiftedBegin.month) + "/" + str(shiftedBegin.day)) shiftedEnd = datetime.datetime(shiftedBegin.year, shiftedBegin.month, shiftedBegin.day, 23, 59, 59) elif self.intervalScale == 2: units.append(shiftedBegin.strftime("CW %W")) shiftedEnd = datetime.datetime(shiftedBegin.year, shiftedBegin.month, shiftedBegin.day, 23, 59, 59) shiftedEnd = shiftedEnd + delta * 6 else: units.append(shiftedBegin.strftime("%b %y")) lastDay = calendar.monthrange(shiftedBegin.year, shiftedBegin.month)[1] shiftedEnd = datetime.datetime(shiftedBegin.year, shiftedBegin.month, lastDay, 23, 59, 59) timeInt = [ int(shiftedBegin.timestamp()), int(shiftedEnd.timestamp()) ] values.append(self.pomo.pomoData.getPomoCount(timeInt, taskID)) # Disable left buttons once we scrolled far enough if self.pomo.pomoData.firstPomo != 0: shiftedBegin = beginInt - (size - 1 - self.intervalShift) * shiftDelta self.shiftedBegin = shiftedBegin if shiftedBegin.timestamp() <= self.pomo.pomoData.firstPomo: self.leftButtonHandle.setDisabled(True) self.farLeftButtonHandle.setDisabled(True) else: self.leftButtonHandle.setDisabled(False) self.farLeftButtonHandle.setDisabled(False) # Create a new subplot. ax = self.figure.add_subplot(111) ax.hold(False) # Create the bar graphs bars = ax.bar(list(range(1, size + 1)), values, width=0.4, align="center", color="#E04B3F") for bar in bars: height = bar.get_height() plt.text(bar.get_x() + bar.get_width() / 2., height + 0.08, '%d' % int(height), ha='center', va='bottom', weight="medium") plt.xticks(list(range(1, size + 1)), units) # y-Limit of the graph depends on the maximum bar height. yLim = self.highestPomoCount[self.intervalScale - 1] * 1.24 # To avoid rescaling the graph when changing the task, we lock the # y-Limit to the first one generated after startup. if self.lockYLim is False: if yLim == 0: # When no pomodoros have been done, use a constant y-Limit. yLim = 15 else: self.lockYLim = True self.yLim = yLim else: # Update the y-Limit when it exceeds the saved one. if yLim > self.yLim: self.yLim = yLim # Set the graph limits. ax.set_ylim([0, self.yLim]) ax.set_xlim([0.5, size + 0.5]) # Additional plot and graph settings. plt.subplots_adjust(left=0, right=0.99, top=1, bottom=0.087) ax.get_yaxis().set_visible(False) plt.minorticks_off() for tick in ax.get_xticklines(): tick.set_visible(False) # Write currently viewed month and year in the upper right corner, # when zoomed out, only display the year. if self.intervalScale != 1: tempDate = beginInt - (size - 1 - self.intervalShift) * shiftDelta dateString = tempDate.strftime("%b %Y") if self.intervalScale != 3: dateString = tempDate.strftime("%Y") plt.text(0.99, 0.937, dateString, horizontalalignment='right', verticalalignment='center', transform=ax.transAxes, weight="bold") # Show. self.canvas.draw() def timeNavigate(self): """ Handling function for the navigation buttons in the activity tab. """ sender = self.sender() if sender.text() == '<': self.intervalShift -= 6 self.rightButtonHandle.setDisabled(False) self.farRightButtonHandle.setDisabled(False) elif sender.text() == '>': self.intervalShift += 6 self.leftButtonHandle.setDisabled(False) self.farLeftButtonHandle.setDisabled(False) if self.intervalShift == 0: # Once we hit todays date, disable the right button. sender.setDisabled(True) self.farRightButtonHandle.setDisabled(True) elif sender.text() == '<<': sender.setDisabled(True) self.leftButtonHandle.setDisabled(True) self.rightButtonHandle.setDisabled(False) self.farRightButtonHandle.setDisabled(False) date = self.shiftedBegin while date.timestamp() >= self.pomo.pomoData.firstPomo: self.intervalShift -= 6 date -= 6 * self.shiftDelta elif sender.text() == '>>': self.intervalShift = 0 sender.setDisabled(True) self.rightButtonHandle.setDisabled(True) self.leftButtonHandle.setDisabled(False) self.farLeftButtonHandle.setDisabled(False) self.fillActivityTab() def timeZoom(self): """ Handling function for the zoom buttons in the activity tab. """ sender = self.sender() # Always reset the navigation while zooming self.intervalShift = 0 self.lockYLim = False if self.pomo.pomoData.firstPomo != 0: self.leftButtonHandle.setDisabled(False) self.farLeftButtonHandle.setDisabled(False) self.rightButtonHandle.setDisabled(True) self.farRightButtonHandle.setDisabled(True) # Zoom Out: if sender.text() == '−': self.intervalScale -= 1 if self.intervalScale == 1: self.zoomOutButtonHandle.setDisabled(True) self.zoomInButtonHandle.setDisabled(False) # Zoom In: else: self.intervalScale += 1 if self.intervalScale == 3: sender.setDisabled(True) self.zoomOutButtonHandle.setDisabled(False) self.fillActivityTab() def onChangeTask(self, string=str()): """ Will be called when the user changes the task in the activity tab. """ try: self.activityTaskID = self.pomo.pomoData.getTaskID(string) except KeyError: self.activityTaskID = 0 self.fillActivityTab()
class GUI(QtGui.QWidget): def __init__(self): super(GUI, self).__init__() self.setWindowTitle( 'Outline 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(-5) 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('Outline 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 # 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] ### 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 cellOutline pickle file already exists, load it, otherwise create a blank one if os.path.isfile( os.path.join(self.path, self.worm + '_05cellOut.pickle' ) ): self.cellOutDF = load_data_frame( self.path, self.worm + '_05cellOut.pickle' ) else: self.cellOutDF = create_cell_out( self.timesDF, self.cellNames ) self.analyzedCell = '---' ### 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 ) ) # 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 ) ### 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_out( self.cellPosDF, self.cellOutDF, self.tp.value() ) if len(self.currentCells) > 0: ### update current analyzed cell if self.analyzedCell not in list( self.currentCells.cname ): self.analyzedCell = self.currentCells.cname[0] ### update the text of the fileName self.fName.setText( self.timesDF.ix[ self.timesDF.tidxRel == self.tp.value(), 'fName' ].values[0]) # 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() if len( self.currentCells ) > 0: ### update slice self.sl.setValue( self.currentCells.ix[ self.currentCells.cname == self.analyzedCell, 'Z' ] ) # self.updateTable() self.updateAllCanvas() def saveData(self): save_data_frame( self.cellOutDF, self.path, self.worm + '_05cellOut.pickle' ) 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 ) elif event.key() == QtCore.Qt.Key_Space: if len( self.currentCells ) > 0: idx = np.mod( self.currentCells.ix[ self.currentCells.cname == self.analyzedCell ].index + 1, len(self.currentCells) ) self.analyzedCell = self.currentCells.cname.values[idx][0] self.sl.setValue( self.currentCells.ix[ self.currentCells.cname == self.analyzedCell, 'Z' ] ) self.updateAllCanvas() 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 onMouseClickOnCanvas1(self, event): pos = np.array( [ int(event.xdata), int(event.ydata) ] ) outline = extract_out( self.currentCells.ix[ self.currentCells.cname == self.analyzedCell ].squeeze() ) if event.button == 1: if np.isnan( outline[0,0] ): outline = np.array( [ pos ] ) else: outline = np.vstack( [ outline, pos ] ) elif event.button == 3: if len( outline[ :, 0 ] ) == 1: outline = np.array( [ [ np.nan, np.nan ] ] ) else: outline = outline[:-1,:] idx = self.currentCells.ix[ self.currentCells.cname == self.analyzedCell ].index self.currentCells.Xout.values[ idx ] = [ outline[:,0] ] self.currentCells.Yout.values[ idx ] = [ outline[:,1] ] self.updateCanvas1() self.setFocus() # print(event.button,event.xdata,event.ydata) #----------------------------------------------------------------------------------------------- # 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 ) or ( len( self.currentCells ) == 0 ): # if no images are found, leave the canvas empty return # extract current cell data pos = extract_3Dpos( self.currentCells.ix[ self.currentCells.cname == self.analyzedCell ].squeeze() ) # plot the image imgpxl = 50 self.ax1.cla() imgplot = self.ax1.imshow(self.stacks[self.currentChannel][self.sl.value(),pos[1]-imgpxl/2:pos[1]+imgpxl/2+1,pos[0]-imgpxl/2:pos[0]+imgpxl/2+1], cmap = 'gray', interpolation = 'nearest') # remove the white borders self.ax1.autoscale(False) self.ax1.axis('Off') self.fig1.subplots_adjust(left=0., right=1., top=1., bottom=0.) # print cell name if pos[2] == self.sl.value(): self.ax1.text( 1, 2, self.analyzedCell, color='yellow', size='medium', alpha=.8, rotation=0, fontsize = 20 ) self.ax1.plot( imgpxl/2, imgpxl/2, 'x', color='yellow', alpha = .8, ms = 5 ) ### draw outline outline = extract_out( self.currentCells.ix[ self.currentCells.cname == self.analyzedCell ].squeeze() ) # print(self.currentCells.ix[ self.currentCells.cname == self.analyzedCell ].squeeze()) if len( outline ) > 1: outline = np.vstack( [ outline, outline[0] ] ) self.ax1.plot( outline[:,0], outline[:,1], '-x', color='yellow', ms=2, alpha=1., lw = .5 ) # 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.stacks[self.currentChannel][self.sl.value()], cmap = 'gray') # remove the white borders self.ax2.autoscale(False) self.ax2.axis('Off') self.fig2.subplots_adjust(left=0., right=1., top=1., bottom=0.) # extract current cell data if len( self.currentCells ) > 0: pos = extract_3Dpos( self.currentCells.ix[ self.currentCells.cname == self.analyzedCell ].squeeze() ) for idx, cell in self.currentCells.iterrows(): if cell.Z == self.sl.value(): color = 'red' if cell.cname == self.analyzedCell: color = 'yellow' self.ax2.text( cell.X+10, cell.Y + 10, cell.cname, color=color, size='medium', alpha=.8, rotation=0) self.ax2.plot( cell.X, cell.Y, 'o', color=color, alpha = .8, ms=5, mew = 0 ) # redraw the canvas self.canvas2.draw() self.setFocus() def changeSpaceTime(self, whatToChange, increment): if whatToChange == 'time': newCellOutDF = update_cell_out_DF( self.currentCells, self.cellOutDF, self.tp.value() ) self.cellOutDF = newCellOutDF # 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 )
def plot_data(self, recordings): fig = Figure(figsize=(200,200), dpi=72, facecolor=(1,1,1), edgecolor=(0,0,0)) ax = fig.add_subplot(111) # Sort the recordings by increasing date recordings = sorted(recordings, key=lambda r: r.date) # list of tuples (year, month, day, consumption, index) interpolated_consumptions = [] for r in recordings: if(len(interpolated_consumptions) == 0): year = r.date.year month = r.date.month day = r.date.day cons = 0 index = r.value interpolated_consumptions.append((year, month, day, cons, index)) continue prev_entry = interpolated_consumptions[-1] prev_year, prev_month, prev_day, prev_cons, prev_index = prev_entry cur_year, cur_month, cur_day, cur_index = r.date.year, r.date.month, r.date.day, r.value # Necessarily, as we sorted the data, we have : # prev_year <= cur_year and prev_month <= cur_month # Point : determine r.cons and fill in between with intermediate consumptions # to get one entry per month in interpolated_consumptions if prev_year == cur_year and prev_month == cur_month: # We can guarantee that we have consumed cur_index - prev_index # on this month; interpolated_consumptions[-1] = (cur_year, cur_month, cur_day, prev_cons + (cur_index - prev_index), cur_index) elif prev_year == cur_year: # and prev_month != cur_month #print("Last entry : %i - %i - %i" % (prev_year, prev_month, prev_day)) tot_cons = cur_index - prev_index tot_days = 0 nb_days = [] # For the first month nb_days.append((prev_year, prev_month, nb_days_in_month(prev_year, prev_month) - prev_day)) tot_days += nb_days_in_month(prev_year, prev_month) - prev_day # For the months in between for m in range(prev_month+1, cur_month): days = nb_days_in_month(prev_year, m) nb_days.append((prev_year, m, days)) tot_days += days # For the last month tot_days += cur_day nb_days.append((prev_year, cur_month, cur_day)) #print(nb_days) #print(" A total of %i days" % tot_days) # We can now provide the interpolated values for interpolated_consumptions # The last entry has to be modified to close the month interpolated_consumptions[-1] = (prev_year, prev_month, nb_days_in_month(prev_year, prev_month), prev_cons + float(nb_days[0][2])/tot_days * tot_cons, -1) for (yr, mth, nbd) in nb_days[1:-1]: interpolated_consumptions.append((yr, mth, nb_days_in_month(yr, mth), float(nbd) / tot_days * tot_cons, -1)) # For the last month, we need to take the correct day.. (yr, mth, nbd) = nb_days[-1] interpolated_consumptions.append((yr, mth, cur_day, float(nbd) / tot_days * tot_cons, cur_index)) else: # we are not in the same year tot_cons = cur_index - prev_index tot_days = 0 nb_days = [] ##### ## Deal with the consummption until the end of the year # For the first month nb_days.append((prev_year, prev_month, nb_days_in_month(prev_year, prev_month) - prev_day)) tot_days += nb_days_in_month(prev_year, prev_month) - prev_day for m in range(prev_month, 13): nb_days.append((prev_year, m, nb_days_in_month(prev_year, m))) tot_days += nb_days_in_month(prev_year, m) # Deal with the years in between for yr in range(prev_year+1, cur_year): for m in range(1, 13): nb_days.append((yr, m, nb_days_in_month(yr, m))) tot_days += nb_days_in_month(yr, m) # Deal with the months until the current month and year for m in range(1, cur_month): nb_days.append((cur_year, m, nb_days_in_month(cur_year, m))) tot_days += nb_days_in_month(cur_year, m) # Deal with the last month nb_days.append((cur_year, cur_month, cur_day)) tot_days += cur_day interpolated_consumptions[-1] = (prev_year, prev_month, nb_days_in_month(prev_year, prev_month), prev_cons + float(nb_days[0][2])/tot_days * tot_cons, -1) for (yr, mth, nbd) in nb_days[1:-1]: interpolated_consumptions.append((yr, mth, nb_days_in_month(yr, mth), float(nbd) / tot_days * tot_cons, -1)) # For the last month, we need to take the correct day.. (yr, mth, nbd) = nb_days[-1] interpolated_consumptions.append((yr, mth, cur_day, float(nbd) / tot_days * tot_cons, cur_index)) # We now have the interpolated_consumptions # We split them by year ; years = [] for yr,mth,day,cons,index in interpolated_consumptions: if yr not in years: years.append(yr) nyears = len(years) data = np.zeros((len(years),12)) data.fill(np.nan) for yr, mth, day, cons, index in interpolated_consumptions: data[years.index(yr), mth-1] = cons ax.plot(range(1,13), data.T) ax.set_xlim([1,12]) ylim = ax.get_ylim() ax.set_ylim([0, ylim[1]]) ax.legend(years, loc='upper center') ax.set_xticks(range(1,13)) ax.set_xticklabels( ['Jan.','Feb.','March', 'April', 'May', 'June', 'July', 'Aug.', 'Sept.', 'Oct.', 'Nov.', 'Dec.'], rotation=45 ) ax.set_ylabel('Delta index') #fig.autofmt_xdate() #ax.fmt_xdata = mdates.DateFormatter('%m') # generate the canvas to display the plot canvas = FigureCanvas(fig) canvas.setFixedSize(QSize(600,400)) return canvas
def __init__(self, streamline, parent=None, width=5, height=4, dpi=50, subs=1): self.fig = Figure(figsize=(width - 50, height), dpi=dpi) pos = 0 self.sl = streamline self.subs = subs self.cpunum = self.sl.mDevice.cpu_num self.gpu_pos = 0 self.fps_pos = 0 self.temp_pos = 0 self.axes = [] for i in range(self.cpunum): self.axes.append(self.fig.add_subplot(self.subs, 1, i + 1)) # For CPU CORES # We want the axes cleared every time plot() is called self.axes[i].set_title('CPU' + str(i)) # self.axes[i].set_xticks([]) # not show x self.axes[i].set_xlim(0, 20000) self.axes[i].set_ylim(0, 2500) if self.sl.mDevice.show_gpu == 1: self.gpu_pos = pos self.axes.append(self.fig.add_subplot(self.subs, 1, self.cpunum + pos + 1)) # FOR GPU self.axes[self.cpunum + self.gpu_pos].set_title('GPU') self.axes[self.cpunum + self.gpu_pos].set_xlim(0, 20000) self.axes[self.cpunum + self.gpu_pos].set_ylim(0, 850) pos += 1 if self.sl.mDevice.show_fps == 1: self.fps_pos = pos self.axes.append(self.fig.add_subplot(self.subs, 1, self.cpunum + self.fps_pos + 1)) # FOR FPS self.axes[self.cpunum + self.fps_pos].set_title('FPS') self.axes[self.cpunum + self.fps_pos].set_xlim(0, 20000) self.axes[self.cpunum + self.fps_pos].set_ylim(0, 100) pos += 1 if self.sl.mDevice.show_temp == 1: self.temp_pos = pos self.axes.append(self.fig.add_subplot(self.subs, 1, self.cpunum + self.temp_pos + 1)) # FOR CPU TEMP self.axes[self.cpunum + self.temp_pos].set_title('CPU Temperature') self.axes[self.cpunum + self.temp_pos].set_xlim(0, 20000) self.axes[self.cpunum + self.temp_pos].set_ylim(0, 100) self.axes.append(self.fig.add_subplot(self.subs, 1, self.cpunum + self.temp_pos + 2)) # FOR BOARD TEMP self.axes[self.cpunum + self.temp_pos + 1].set_title('Board Temperature') self.axes[self.cpunum + self.temp_pos + 1].set_xlim(0, 20000) self.axes[self.cpunum + self.temp_pos + 1].set_ylim(0, 100) self.fig.set_tight_layout(True) self.compute_initial_figure() FigureCanvas.__init__(self, self.fig) self.setParent(parent) FigureCanvas.setFixedSize(self, width - 50, subs * 100) FigureCanvas.setSizePolicy(self, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Expanding) FigureCanvas.updateGeometry(self)
class PomoWindow(QtGui.QWidget): def __init__(self, pomo): """ Makes necessary variable initializations and calls other init methods. """ super(PomoWindow, self).__init__() self.pomo = pomo # Enhance readability self.getString = self.pomo.getString self.initUI() self.initAudio() self.timerActive = False self.timer = QtCore.QTimer() self.timer.timeout.connect(self.tick) def initUI(self): """ Initializes the UI. """ # Set some general window settings. self.setFixedSize(480, 310) self.move(250, 250) self.setWindowTitle(self.getString("app_name")) self.setWindowIcon(QtGui.QIcon("/usr/share/icons/pomodorino.png")) self.trayIcon = QtGui.QSystemTrayIcon(QtGui.QIcon("/usr/share/icons/pomodorino.png"), self) self.trayIcon.setVisible(True) self.trayIcon.activated.connect(self.trayClick) # Add a minimal context menu for the tray icon. # trayMenu = QtGui.QMenu(self) # trayMenu.addAction("Quit", self.close) # self.trayIcon.setContextMenu(trayMenu) # Initialize and display the tabs pomoTab = self.initPomoTab() tasksTab = self.initTasksTab() activityTab = self.initActivityTab() tabWidget = QtGui.QTabWidget(self) tabWidget.resize(479, 309) tabWidget.addTab(pomoTab, self.getString("tab_pomo")) tabWidget.addTab(tasksTab, self.getString("tab_tasks")) tabWidget.addTab(activityTab, self.getString("tab_activity")) self.show() def initAudio(self): """ Detects an ALSA audio device and buffers our ringtone. """ # Detect an ALSA audio device self.audioDevice = alsaaudio.PCM() # Open the ringtone and set some audio settings. ring = wave.open("/usr/share/pomodorino/ring.wav", "rb") self.audioDevice.setchannels(ring.getnchannels()) self.audioDevice.setrate(ring.getframerate()) self.audioDevice.setformat(alsaaudio.PCM_FORMAT_S16_LE) self.audioDevice.setperiodsize(320) # Read the audio data of the ringtone. self.audioData = list() buf = ring.readframes(320) while buf: self.audioData.append(buf) buf = ring.readframes(320) def closeEvent(self, event): """ Prevents accidental shutdowns by asking the user. """ if self.timerActive: self.stopTimer() if self.promptUser("ask_paused"): event.accept() else: event.ignore() self.startTimer(0, restart=True) else: event.accept() def setTitle(self, title): """ Sets the window title. """ if title is None: title = self.getString("app_name") else: title += " - " + self.getString("app_name") self.window().setWindowTitle(title) def startTimer(self, timeSpan, restart=False): """ Starts the timer. """ if restart is False: self.timerType = timeSpan self.timerCount = timeSpan * 60 self.timerActive = True self.timer.start(1000) def stopTimer(self): """ Stops the timer. """ self.timerActive = False self.timer.stop() def resetTimer(self): """ Resets the timer. """ self.stopTimer() self.timerCount = 0 def finishTimer(self, task): """ Is called once the timer finished. """ self.resetTimer() # Define the regular length of a pomodoro. Purely for debugging reasons POMO_CONST = 25 pomos = math.floor(self.timerType / POMO_CONST) self.setVisible(True) if pomos >= 1 and task != "": newTask = self.pomo.pomoData.addPomo(task, pomos) if newTask is True: self.pomoTaskBar.addItem(task, None) self.fillActivityTab() self.updateTasksTab() def trayClick(self, reason): """ Is called when the user clicks on the tray icon. """ if reason == 2 or reason == 3: if self.isVisible(): self.setVisible(False) else: self.setVisible(True) def promptUser(self, identifier, additional=None): """ Creates predefined confirmation/warning dialogs. """ if identifier == "ask_paused": reply = QtGui.QMessageBox.question( self, self.getString("ask_paused_title"), self.getString("ask_paused_text"), QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No, ) if reply == QtGui.QMessageBox.Yes: return True else: return False elif identifier == "warn_notask": QtGui.QMessageBox.warning(self, self.getString("warn_notask_title"), self.getString("warn_notask_text")) return elif identifier == "ask_taskdel" and additional != None: askText = self.getString("ask_taskdel_text") askText = str.replace(askText, "%taskname%", str(additional)) reply = QtGui.QMessageBox.question( self, self.getString("ask_taskdel_title"), askText, QtGui.QMessageBox.Yes | QtGui.QMessageBox.No, QtGui.QMessageBox.No, ) if reply == QtGui.QMessageBox.Yes: return True else: return False raise KeyError ############## ## Pomo Tab ## ############## def initPomoTab(self): """ Creates the layout of the pomodoro tab """ pomoTab = QtGui.QWidget() # Create a combobox with a lineedit for task selection. taskBar = QtGui.QComboBox() taskBar.setEditable(True) taskBarLine = taskBar.lineEdit() taskBarLine.setMaxLength(64) taskBarLine.setPlaceholderText(self.getString("input_task")) taskBar.setFixedSize(375, 32) taskBar.setInsertPolicy(QtGui.QComboBox.InsertAlphabetically) taskBar.setStyleSheet("font-size: 11pt;") taskBar.addItem("", None) self.pomoTaskBar = taskBar # Add all the task names. tasks = self.pomo.pomoData.tasks for taskID, taskName, pomoCount, pomoLast in tasks: taskBar.addItem(taskName, None) # Create the main button. mainBtn = QtGui.QPushButton(self.getString("btn_start"), pomoTab) mainBtn.setFixedSize(180, 60) mainBtn.setStyleSheet("font-size: 17pt;") # Create the 25 min button. timeBtn = QtGui.QPushButton(self.getString("lbl_regularpomo"), pomoTab) timeBtn.setToolTip(self.getString("ttp_regularpomo")) timeBtn.setFixedSize(50, 25) timeBtn.setStyleSheet("font-size: 9pt;") # Create the 50 min button. longTimeBtn = QtGui.QPushButton(self.getString("lbl_longpomo"), pomoTab) longTimeBtn.setToolTip(self.getString("ttp_longpomo")) longTimeBtn.setFixedSize(50, 25) longTimeBtn.setStyleSheet("font-size: 9pt;") # Create the 5 min button. pauseBtn = QtGui.QPushButton(self.getString("lbl_shortpause"), pomoTab) pauseBtn.setToolTip(self.getString("ttp_shortpause")) pauseBtn.setFixedSize(50, 25) pauseBtn.setStyleSheet("font-size: 9pt;") # Create the 10 min button. longPauseBtn = QtGui.QPushButton(self.getString("lbl_longpause"), pomoTab) longPauseBtn.setToolTip(self.getString("ttp_longpause")) longPauseBtn.setFixedSize(50, 25) longPauseBtn.setStyleSheet("font-size: 9pt;") # Select 25 min button as default on startup. timeBtn.setDisabled(True) self.pomoButtonActive = timeBtn # Save button references for later usage. self.pomoBtns = dict() self.pomoBtns["main"] = mainBtn self.pomoBtns["time"] = timeBtn self.pomoBtns["longTime"] = longTimeBtn self.pomoBtns["pause"] = pauseBtn self.pomoBtns["longPause"] = longPauseBtn # Connect the buttons to the handler function. for name, button in self.pomoBtns.items(): button.clicked.connect(self.onClicked) # Create and set the layout. firstRow = QtGui.QHBoxLayout() firstRow.addWidget(taskBar) secondRow = QtGui.QHBoxLayout() secondRow.addStretch() secondRow.addWidget(pauseBtn) secondRow.addWidget(longPauseBtn) secondRow.addStretch() secondRow.addWidget(timeBtn) secondRow.addWidget(longTimeBtn) secondRow.addStretch() thirdRow = QtGui.QHBoxLayout() thirdRow.addWidget(mainBtn) vbox = QtGui.QVBoxLayout() vbox.addStretch() vbox.addLayout(firstRow) vbox.addStretch() vbox.addLayout(secondRow) vbox.addStretch() vbox.addLayout(thirdRow) vbox.addStretch() pomoTab.setLayout(vbox) return pomoTab def onClicked(self): """ Main function for catching button clicks in the pomo tab. """ sender = self.sender() if sender == self.pomoBtns["main"]: return self.onClickedPomoMain() elif ( sender == self.pomoBtns["pause"] or sender == self.pomoBtns["longPause"] or sender == self.pomoBtns["time"] or sender == self.pomoBtns["longTime"] ): return self.onClickedPomoTime() raise ValueError() def onClickedPomoMain(self): """ Starts/Stops the timer depending on the state of the button. """ sender = self.sender() if self.timerActive: self.stopTimer() # Ask the user whether he wants to reset the running timer. if self.promptUser("ask_paused"): self.resetPomoTab() self.resetTimer() else: self.startTimer(0, restart=True) else: newText = self.pomoTaskBar.currentText().strip() self.pomoTaskBar.setEditText(newText) if newText != "" or self.pomoTaskBar.isEnabled() is False: self.pomoTaskBar.setDisabled(True) for k, v in self.pomoBtns.items(): if k != "main": v.setDisabled(True) timeSpan = int(self.pomoButtonActive.text()) title = "{0:0>2}:00".format(timeSpan) sender.setText(title) self.setTitle(title) self.startTimer(timeSpan) else: self.promptUser("warn_notask") def onClickedPomoTime(self): """ Toggles the state of the timer buttons in the pomo tab. """ sender = self.sender() if self.pomoBtns["main"].text() == "start": self.pomoButtonActive.setDisabled(False) sender.setDisabled(True) self.pomoButtonActive = sender if sender == self.pomoBtns["pause"] or sender == self.pomoBtns["longPause"]: self.pomoTaskBar.setDisabled(True) else: self.pomoTaskBar.setDisabled(False) def resetPomoTab(self): """ Resets the button states in the pomo tab. """ if self.pomoButtonActive != self.pomoBtns["pause"] and self.pomoButtonActive != self.pomoBtns["longPause"]: self.pomoTaskBar.setDisabled(False) self.pomoBtns["main"].setText("start") for k, v in self.pomoBtns.items(): if k != "main" and v is not self.pomoButtonActive: v.setDisabled(False) # Also reset the window title. self.setTitle(None) def tick(self): """ Updates the GUI every second when the timer is running. """ self.timerCount -= 1 timeStr = "{0:0>2}:{1:0>2}".format(math.floor(self.timerCount / 60), self.timerCount % 60) self.pomoBtns["main"].setText(timeStr) # Show current time in the title. self.setTitle(timeStr) # Timer finished? if self.timerCount == 0: self.playRingtone() self.finishTimer(self.pomoTaskBar.currentText()) self.resetPomoTab() def playRingtone(self): """ Creates a thread for playing the ringtone. """ t = threading.Thread(target=self.playRingThread) # Kill the thread once it finishes. t.daemon = True t.start() def playRingThread(self): """ Plays the Ringtone. """ for data in self.audioData: self.audioDevice.write(data) ############### ## tasks tab ## ############### def initTasksTab(self): """ Creates the layout of the tasks tab. """ tasksTab = QtGui.QWidget() self.tasksTable = self.fillTasksTable() self.tasksVBox = QtGui.QVBoxLayout() self.tasksVBox.addWidget(self.tasksTable) self.tasksTable.sortItems(0) tasksTab.setLayout(self.tasksVBox) return tasksTab def fillTasksTable(self): """ Fills the table in the tasks tab. """ tasks = self.pomo.pomoData.tasks self.taskTableSelChange = False # Create a table with three columns. table = QtGui.QTableWidget(len(tasks), 3) table.itemSelectionChanged.connect(self.taskListSelectionChanged) table.itemClicked.connect(self.taskListClick) table.setShowGrid(True) table.setSelectionMode(QtGui.QAbstractItemView.SingleSelection) table.setSelectionBehavior(QtGui.QAbstractItemView.SelectRows) table.setFocusPolicy(QtCore.Qt.NoFocus) table.setHorizontalHeaderLabels( [self.getString("lbl_stats_task"), self.getString("lbl_stats_pomos"), self.getString("lbl_stats_last")] ) table.verticalHeader().setVisible(False) table.horizontalHeader().setHighlightSections(False) # Create a context menu for the table. def ctMenuEvent(event): self.taskTableSelChange = False menu = QtGui.QMenu(table) rnAct = menu.addAction("Rename", self.renameTask) dlAct = menu.addAction("Delete", self.deleteTask) menu.popup(QtGui.QCursor.pos()) if self.timerActive and self.pomoTaskBar.currentText() == self.tasksTable.selectedItems()[0].text(): rnAct.setEnabled(False) dlAct.setEnabled(False) table.contextMenuEvent = ctMenuEvent # Columwidth depends on the existence of a scrollbar. if len(tasks) <= 7: table.setColumnWidth(0, 345) else: table.setColumnWidth(0, 329) table.setColumnWidth(1, 48) table.setColumnWidth(2, 60) # There must be a row counter since the taskID can be different. rowCount = 0 # Fill the table rows. for taskID, taskName, pomoCount, pomoLast in tasks: # First column: taskName item = QtGui.QTableWidgetItem() item.setText(taskName) item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable) table.setItem(rowCount, 0, item) # Second column: pomoCount item = QtGui.QTableWidgetItem() item.setText(str(pomoCount)) item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable) item.setToolTip("~" + str(round((pomoCount * 25) / 60, 1)) + "h") table.setItem(rowCount, 1, item) # Third column: pomoLast pomoLastDate = datetime.datetime.fromtimestamp(pomoLast) item = QtGui.QTableWidgetItem() item.setText(pomoLastDate.strftime("%x")) item.setFlags(QtCore.Qt.ItemIsEnabled | QtCore.Qt.ItemIsSelectable) table.setItem(rowCount, 2, item) rowCount += 1 return table def updateTasksTab(self): """ Updates the pomodoro statistics in the tasks tab. """ self.tasksVBox.removeWidget(self.tasksTable) self.tasksTable.close() self.tasksTable = self.fillTasksTable() self.tasksVBox.insertWidget(0, self.tasksTable) def taskListSelectionChanged(self): """ Sets a flag when the selection in the task table was changed. """ if self.tasksTable.selectedItems() != []: self.taskTableSelChange = True def taskListClick(self, item): """ Detects when an item in the task table was clicked and clears selection if necessary. """ if self.taskTableSelChange == False: self.tasksTable.clearSelection() self.taskTableSelChange = False def deleteTask(self): """ Deletes a task by the users request. """ taskName = self.tasksTable.selectedItems()[0].text() okay = self.promptUser("ask_taskdel", additional=taskName) if okay: # Delete entry from GUI pbID = self.pomoTaskBar.findText(taskName) self.pomoTaskBar.removeItem(pbID) stID = self.activitySelTask.findText(taskName) self.activitySelTask.removeItem(stID) rownum = self.tasksTable.row(self.tasksTable.selectedItems()[0]) self.tasksTable.removeRow(rownum) if self.tasksTable.rowCount() <= 7: self.tasksTable.setColumnWidth(0, 345) # Delete entry from db and cache taskID = self.pomo.pomoData.getTaskID(taskName) self.pomo.pomoData.delTask(taskID) # Reset the activity tab self.fillActivityTab() def renameTask(self, warn=""): """ Renames a task by the users request. """ oldname = self.tasksTable.selectedItems()[0].text() name, okay = QtGui.QInputDialog.getText(self, self.getString("lbl_rename_task"), warn, text=oldname) if okay and name != "" and name != oldname: try: self.pomo.pomoData.getTaskID(name) self.renameTask(self.getString("lbl_rename_taken")) except KeyError: # Update entry in GUI self.tasksTable.selectedItems()[0].setText(name) pbID = self.pomoTaskBar.findText(oldname) self.pomoTaskBar.setItemText(pbID, name) stID = self.activitySelTask.findText(oldname) self.activitySelTask.setItemText(stID, name) # Update entry in db and cache tID = self.pomo.pomoData.getTaskID(oldname) self.pomo.pomoData.renameTask(tID, name) ################## ## activity tab ## ################## def initActivityTab(self): """ Creates the layout of the activity tab and prepares the graph. """ activityTab = QtGui.QWidget() # Get the background color of the window to make the graph fit in. color = self.palette().color(QtGui.QPalette.Background) # Create a fixed-size canvas for the graph. self.figure = plt.figure(facecolor=color.name(), tight_layout=False) self.canvas = FigureCanvas(self.figure) self.canvas.setFixedSize(460, 236) # Set default values for some attributes used in fillActivityTab self.lockYLim = False self.intervalShift = 0 self.intervalScale = 3 # [3] days - [2] weeks - [1] months self.activityTaskID = 0 # Combobox for selecting the active task to be displayed. selTask = QtGui.QComboBox() selTask.insertItem(0, self.getString("lbl_stats_all"), None) for tID, tskName, pomoCount, pomoLast in self.pomo.pomoData.tasks: selTask.addItem(tskName, None) selTask.currentIndexChanged["QString"].connect(self.onChangeTask) # Save handle for later use. self.activitySelTask = selTask # Navigation buttons to change the active timespan. farLeftButton = QtGui.QPushButton("<<", activityTab) farLeftButton.setStyleSheet("font-size: 12pt;") farLeftButton.setFixedSize(30, 20) farLeftButton.clicked.connect(self.timeNavigate) # Save the handle for toggling the button state self.farLeftButtonHandle = farLeftButton farRightButton = QtGui.QPushButton(">>", activityTab) farRightButton.setStyleSheet("font-size: 12pt;") farRightButton.setFixedSize(30, 20) farRightButton.setDisabled(True) farRightButton.clicked.connect(self.timeNavigate) # Save the handle for toggling the button state. self.farRightButtonHandle = farRightButton leftButton = QtGui.QPushButton("<", activityTab) leftButton.setStyleSheet("font-size: 12pt;") leftButton.setFixedSize(30, 20) leftButton.clicked.connect(self.timeNavigate) # Save the handle for toggling the button state self.leftButtonHandle = leftButton rightButton = QtGui.QPushButton(">", activityTab) rightButton.setStyleSheet("font-size: 12pt;") rightButton.setFixedSize(30, 20) rightButton.setDisabled(True) rightButton.clicked.connect(self.timeNavigate) # Save the handle for toggling the button state. self.rightButtonHandle = rightButton # Disable left navigation buttons when there are no finished pomos. if self.pomo.pomoData.firstPomo == 0: leftButton.setDisabled(True) farLeftButton.setDisabled(True) # Zoom buttons to change the active timespan. zoomOutButton = QtGui.QPushButton("−", activityTab) zoomOutButton.setStyleSheet("font-size: 12pt;") zoomOutButton.setFixedSize(30, 20) zoomOutButton.clicked.connect(self.timeZoom) # Save the handle for toggling the button state. self.zoomOutButtonHandle = zoomOutButton zoomInButton = QtGui.QPushButton("+", activityTab) zoomInButton.setStyleSheet("font-size: 12pt;") zoomInButton.setFixedSize(30, 20) zoomInButton.setDisabled(True) zoomInButton.clicked.connect(self.timeZoom) # Save the handle for toggling the button state. self.zoomInButtonHandle = zoomInButton # Get highest pomo count on a single day. self.highestPomoCount = list() self.highestPomoCount.append(self.pomo.pomoData.getHighestPomoCountMonthly()) self.highestPomoCount.append(self.pomo.pomoData.getHighestPomoCountWeekly()) self.highestPomoCount.append(self.pomo.pomoData.getHighestPomoCountDaily()) # Draw the graph. self.fillActivityTab() # Create and set the layout. layout = QtGui.QVBoxLayout() layout.addWidget(self.canvas) layout2 = QtGui.QHBoxLayout() layout2.addWidget(farLeftButton) layout2.addWidget(leftButton) layout2.addWidget(rightButton) layout2.addWidget(farRightButton) layout2.addStretch() layout2.addWidget(zoomOutButton) layout2.addWidget(zoomInButton) layout2.addStretch() layout2.addWidget(selTask) layout.addLayout(layout2) activityTab.setLayout(layout) return activityTab def fillActivityTab(self): """ Fills and displays the bar graph in the activity tab. """ taskID = self.activityTaskID # First get the absolute value of today. today = datetime.date.today() delta = datetime.timedelta(days=1) # Now construct shiftable intervals. beginInt = datetime.datetime endInt = datetime.datetime # Default scale (days): Begin interval at midnight of today. beginInt = datetime.datetime(today.year, today.month, today.day, 0, 0, 0) shiftDelta = delta if self.intervalScale == 2: # Scale: Weeks # Begin interval at midnight of this weeks monday. weekDayNum = calendar.weekday(today.year, today.month, today.day) beginInt = beginInt - delta * weekDayNum shiftDelta = 7 * delta elif self.intervalScale == 1: # Scale: Months # Begin interval at midnight of the first day of the month. beginInt = datetime.datetime(today.year, today.month, 1, 0, 0, 0) shiftDelta = 30 * delta self.shiftDelta = shiftDelta # Get the data of the last units since today. units = list() values = list() size = 6 + self.intervalScale for i in range(size): # Shift offset = size - i - 1 - self.intervalShift shiftedBegin = beginInt - offset * shiftDelta # When scaled to months, an arithmetical shift is not practical. if self.intervalScale == 1: yearDiff, monDiff = divmod(offset, 12) newMon = beginInt.month - monDiff if newMon < 0: newMon = 12 + newMon yearDiff += 1 if newMon == 0: newMon = 12 yearDiff += 1 shiftedBegin = datetime.datetime(beginInt.year - yearDiff, newMon, 1, 0, 0, 0) shiftedEnd = datetime.datetime if self.intervalScale == 3: units.append(str(shiftedBegin.month) + "/" + str(shiftedBegin.day)) shiftedEnd = datetime.datetime(shiftedBegin.year, shiftedBegin.month, shiftedBegin.day, 23, 59, 59) elif self.intervalScale == 2: units.append(shiftedBegin.strftime("CW %W")) shiftedEnd = datetime.datetime(shiftedBegin.year, shiftedBegin.month, shiftedBegin.day, 23, 59, 59) shiftedEnd = shiftedEnd + delta * 6 else: units.append(shiftedBegin.strftime("%b %y")) lastDay = calendar.monthrange(shiftedBegin.year, shiftedBegin.month)[1] shiftedEnd = datetime.datetime(shiftedBegin.year, shiftedBegin.month, lastDay, 23, 59, 59) timeInt = [int(shiftedBegin.timestamp()), int(shiftedEnd.timestamp())] values.append(self.pomo.pomoData.getPomoCount(timeInt, taskID)) # Disable left buttons once we scrolled far enough if self.pomo.pomoData.firstPomo != 0: shiftedBegin = beginInt - (size - 1 - self.intervalShift) * shiftDelta self.shiftedBegin = shiftedBegin if shiftedBegin.timestamp() <= self.pomo.pomoData.firstPomo: self.leftButtonHandle.setDisabled(True) self.farLeftButtonHandle.setDisabled(True) else: self.leftButtonHandle.setDisabled(False) self.farLeftButtonHandle.setDisabled(False) # Create a new subplot. ax = self.figure.add_subplot(111) ax.hold(False) # Create the bar graphs bars = ax.bar(list(range(1, size + 1)), values, width=0.4, align="center", color="#E04B3F") for bar in bars: height = bar.get_height() plt.text( bar.get_x() + bar.get_width() / 2.0, height + 0.08, "%d" % int(height), ha="center", va="bottom", weight="medium", ) plt.xticks(list(range(1, size + 1)), units) # y-Limit of the graph depends on the maximum bar height. yLim = self.highestPomoCount[self.intervalScale - 1] * 1.24 # To avoid rescaling the graph when changing the task, we lock the # y-Limit to the first one generated after startup. if self.lockYLim is False: if yLim == 0: # When no pomodoros have been done, use a constant y-Limit. yLim = 15 else: self.lockYLim = True self.yLim = yLim else: # Update the y-Limit when it exceeds the saved one. if yLim > self.yLim: self.yLim = yLim # Set the graph limits. ax.set_ylim([0, self.yLim]) ax.set_xlim([0.5, size + 0.5]) # Additional plot and graph settings. plt.subplots_adjust(left=0, right=0.99, top=1, bottom=0.087) ax.get_yaxis().set_visible(False) plt.minorticks_off() for tick in ax.get_xticklines(): tick.set_visible(False) # Write currently viewed month and year in the upper right corner, # when zoomed out, only display the year. if self.intervalScale != 1: tempDate = beginInt - (size - 1 - self.intervalShift) * shiftDelta dateString = tempDate.strftime("%b %Y") if self.intervalScale != 3: dateString = tempDate.strftime("%Y") plt.text( 0.99, 0.937, dateString, horizontalalignment="right", verticalalignment="center", transform=ax.transAxes, weight="bold", ) # Show. self.canvas.draw() def timeNavigate(self): """ Handling function for the navigation buttons in the activity tab. """ sender = self.sender() if sender.text() == "<": self.intervalShift -= 6 self.rightButtonHandle.setDisabled(False) self.farRightButtonHandle.setDisabled(False) elif sender.text() == ">": self.intervalShift += 6 self.leftButtonHandle.setDisabled(False) self.farLeftButtonHandle.setDisabled(False) if self.intervalShift == 0: # Once we hit todays date, disable the right button. sender.setDisabled(True) self.farRightButtonHandle.setDisabled(True) elif sender.text() == "<<": sender.setDisabled(True) self.leftButtonHandle.setDisabled(True) self.rightButtonHandle.setDisabled(False) self.farRightButtonHandle.setDisabled(False) date = self.shiftedBegin while date.timestamp() >= self.pomo.pomoData.firstPomo: self.intervalShift -= 6 date -= 6 * self.shiftDelta elif sender.text() == ">>": self.intervalShift = 0 sender.setDisabled(True) self.rightButtonHandle.setDisabled(True) self.leftButtonHandle.setDisabled(False) self.farLeftButtonHandle.setDisabled(False) self.fillActivityTab() def timeZoom(self): """ Handling function for the zoom buttons in the activity tab. """ sender = self.sender() # Always reset the navigation while zooming self.intervalShift = 0 self.lockYLim = False if self.pomo.pomoData.firstPomo != 0: self.leftButtonHandle.setDisabled(False) self.farLeftButtonHandle.setDisabled(False) self.rightButtonHandle.setDisabled(True) self.farRightButtonHandle.setDisabled(True) # Zoom Out: if sender.text() == "−": self.intervalScale -= 1 if self.intervalScale == 1: self.zoomOutButtonHandle.setDisabled(True) self.zoomInButtonHandle.setDisabled(False) # Zoom In: else: self.intervalScale += 1 if self.intervalScale == 3: sender.setDisabled(True) self.zoomOutButtonHandle.setDisabled(False) self.fillActivityTab() def onChangeTask(self, string=str()): """ Will be called when the user changes the task in the activity tab. """ try: self.activityTaskID = self.pomo.pomoData.getTaskID(string) except KeyError: self.activityTaskID = 0 self.fillActivityTab()