class ImagePanel(tk.Frame): """ This is a wrapper class for matplotlib figure canvas. It implements the mediator pattern for communication with collegues. """ def __init__(self, parent, name, figsize=(2,2), dpi=100, title='', wl=False, info=False, toolbar=False, cb=False): self.name = name tk.Frame.__init__(self, parent) self.parent = parent self.img_is_set = False self.figsize = figsize self.dpi = dpi self.title = title self.toolbar = toolbar self.wl = wl self.make_canvas() if info: self.make_info() self.images = None self.original_img = None self.nZoom = 0 self.indexOfImg = 0 self.level_is_set = False self.window_is_set = False self.initial_level = None self.initial_window = None self.img = None self.cb = cb self.cbar = None self.metadata = {} self.collegues = [] def register(self, collegue): self.collegues.append(collegue) def inform(self, event): for collegue in self.collegues: collegue.update_(self.name, event) def doubleclick(self, event): if event.dblclick: self.inform('<Double-Button-1>') print('DoubleClick') def make_canvas(self): self.canvas_frame = tk.Frame(self, padx=5, pady=5, cursor='crosshair') self.canvas_frame.pack(side=tk.TOP, fill=tk.BOTH, expand=1) if self.toolbar: self.toolbar_frame = self.make_toolbar() self.toolbar_frame.pack(side=tk.TOP, fill=tk.X, expand=0) self.f = Figure(figsize=self.figsize, dpi=self.dpi) self.subplot = self.f.add_subplot(111) self.subplot.set_title(self.title, fontsize=10) plt.setp(self.subplot.get_xticklabels(), fontsize=4) plt.setp(self.subplot.get_yticklabels(), fontsize=4) self.canvas = FigureCanvasTkAgg(self.f, master=self.canvas_frame) self.img = self.canvas.show() self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1) self.canvas.mpl_connect('scroll_event', self.mouseWheel) self.canvas.mpl_connect('button_press_event', self.doubleclick) if self.wl: self.wl_scale = self.make_wl() self.wl_scale.pack(side=tk.TOP, fill=tk.X, expand=0) def make_wl(self): f = font.Font(size=6) wl_frame = tk.Frame(self.canvas_frame) self.levelScale = tk.Scale(wl_frame, orient=tk.HORIZONTAL, from_=0.0, to=256.0, width=8, font=f, command=self.set_level) self.levelScale.pack(side=tk.TOP, fill=tk.X, expand=0) self.windowScale = tk.Scale(wl_frame, orient=tk.HORIZONTAL, from_=0.0, to=256.0, width=8, font=f, command=self.set_window) self.windowScale.pack(side=tk.TOP, fill=tk.X, expand=0) return wl_frame def make_info(self): self.info = tk.LabelFrame(self.parent, text='Image Info', padx=5, pady=5, width=400) self.info.pack(side=tk.RIGHT, fill=tk.BOTH, expand=0) def make_toolbar(self): toolbar_frame = tk.Frame(self.canvas_frame) set = ('DrawRectangle$icons/Rectangle$tk.FLAT$self.drawRectangle$tk.LEFT', 'Delete$icons/Delete$tk.FLAT$self.deleteRectangle$tk.LEFT', 'ZoomIn$icons/ZoomIn$tk.FLAT$self.zoomIn$tk.LEFT', 'ZoomOut$icons/ZoomOut$tk.FLAT$self.zoomOut$tk.LEFT', 'Reset$icons/ResetZoom$tk.FLAT$self.resetZoom$tk.LEFT', 'Move$icons/Move$tk.FLAT$self.move$tk.LEFT', 'Ruler$icons/Ruler$tk.FLAT$self.ruler$tk.LEFT', 'Histogram$icons/Histogram$tk.FLAT$self.histogram$tk.LEFT', 'Info$icons/Info$tk.FLAT$self.info$tk.LEFT', 'Save$icons/Save18$tk.FLAT$self.savePicture$tk.LEFT' ) self.imgToolbar= [] for v in set: text, image, relief, command, side = v.split('$') self.imgToolbar.append(tk.PhotoImage(file=image+'.gif')) button = tk.Button(toolbar_frame, image=self.imgToolbar[-1], text=text, relief=eval(relief), command=eval(command)) button.pack(side=tk.LEFT, fill=tk.BOTH, expand=0) return toolbar_frame def drawRectangle(self): print('Draw rectangle!') self.x0 = None self.y0 = None self.x1 = None self.y1 = None self.xp0 = None self.yp0 = None self.xp1 = None self.yp1 = None self.rectangle = Rectangle((0,0), 1, 1, facecolor='None', edgecolor='green') self.subplot.add_patch(self.rectangle) self.ispressed = False self.bpe = self.canvas.mpl_connect('button_press_event', self.drawRectangle_onPress) self.bre = self.canvas.mpl_connect('button_release_event', self.drawRectangle_onRelease) self.mne = self.canvas.mpl_connect('motion_notify_event', self.drawRectangle_onMotion) def drawRectangle_onPress(self, event): self.xp0 = event.x self.yp0 = event.y self.x0 = event.xdata self.y0 = event.ydata self.x1 = event.xdata self.y1 = event.ydata self.rectangle.set_width(self.x1-self.x0) self.rectangle.set_xy((self.x0, self.y0)) self.rectangle.set_linestyle('dashed') self.canvas.draw() self.ispressed = True def drawRectangle_onRelease(self, event): self.xp1 = event.x self.yp1 = event.y self.x1 = event.xdata self.y1 = event.ydata self.rectangle.set_width(self.x1-self.x0) self.rectangle.set_height(self.y1-self.y0) self.rectangle.set_xy((self.x0, self.y0)) self.rectangle.set_linestyle('solid') self.canvas.draw() self.ispressed = False self.canvas.mpl_disconnect(self.bpe) self.canvas.mpl_disconnect(self.bre) self.canvas.mpl_disconnect(self.mne) print(self.xp0, self.yp0, self.xp1, self.yp1) self.inform('<DrawRectangle>') return (self.xp0, self.yp0, self.xp1, self.yp1) def getRectanglePoints(self): return (self.xp0, self.yp0, self.xp1, self.yp1) def drawRectangle_onMotion(self, event): if self.ispressed is True: self.x1 = event.xdata self.y1 = event.ydata self.rectangle.set_width(self.x1-self.x0) self.rectangle.set_height(self.y1-self.y0) self.rectangle.set_xy((self.x0, self.y0)) self.rectangle.set_linestyle('dashed') self.canvas.draw() def deleteRectangle(self): print('Delete rectangle!') self.rectangle.remove() self.canvas.draw() self.inform('<DeleteRectangle>') def zoomIn(self): print('Zoom in!') print(np.shape(self.images)) self.images = self.images[:,10:-10, 10:-10] self.show_images() self.nZoom = self.nZoom+1 def zoomOut(self): print('ZoomOut!') if np.shape(self.images) != np.shape(self.original_img): if self.nZoom>1: self.images = self.original_img[:,(self.nZoom-1)*10:-(self.nZoom-1)*10, (self.nZoom-1)*10:-(self.nZoom-1)*10] self.show_images() self.nZoom = self.nZoom-1 else: self.images = self.original_img self.nZoom = 0 def resetZoom(self): print('Reset zoom!') self.images = self.original_img self.show_images() self.nZoom = 0 def histogram(self): print('Histogram!') histo = tk.Toplevel() f = Figure(figsize=(4,4), dpi=100) subplot = f.add_subplot(111) subplot.set_title('Histogram', fontsize=10) plt.setp(subplot.get_xticklabels(), fontsize=8) plt.setp(subplot.get_yticklabels(), fontsize=8) canvas = FigureCanvasTkAgg(f, master=histo) canvas.show() canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1) subplot.hist(self.images.flatten(), 100, normed=True, histtype='step', fc='k', ec='k') canvas.draw() close_button = tk.Button(histo, text='Close', command = histo.destroy) close_button.pack(side=tk.TOP) def savePicture(self): print('Save!') savefile = tk.filedialog.asksaveasfilename(title='Save image as ...', defaultextension='png', filetypes=[('all files', '.*'), ('png files', '.png')]) if savefile: self.f.savefig(savefile, dpi=1200, format='png', ) def mouseWheel(self, event): print('Test mouseWheel.') if event.button == 'down': self.indexOfImg = self.indexOfImg+1 self.show_images() self.inform('<MouseWheelDown') if event.button == 'up': self.indexOfImg = self.indexOfImg-1 self.show_images() self.inform('<MouseWheelUp') def change_wl(self, arg): print('Button press event test') def set_images(self, images): self.images = images if self.img_is_set == False: self.original_img = images self.show_images() self.img_is_set = True def set_metadata(self, data): self.metadata = data def show_images(self): plt.clf() if (self.indexOfImg < np.size(self.images, 0))and (self.indexOfImg >= 0): self.img = self.subplot.imshow(self.images[self.indexOfImg]) if self.level_is_set == False: self.level = (np.max(self.images[self.indexOfImg])-np.min(self.images[self.indexOfImg]))/2 self.window = 2*self.level self.levelScale.config(from_=np.min(self.images[self.indexOfImg])) self.levelScale.config(to=np.max(self.images[self.indexOfImg])) self.levelScale.set(self.level) self.windowScale.config(from_=0) self.windowScale.config(to=self.window) self.windowScale.set(self.window) if self.window < 1: self.levelScale.config(resolution=0.0001) self.windowScale.config(resolution=0.0001) self.img.set_clim(self.level-self.window/2, self.level+self.window/2) else: self.indexOfImg = 0 self.img = self.subplot.imshow(self.images[self.indexOfImg]) if self.level_is_set == False: self.level = (np.max(self.images[self.indexOfImg])-np.min(self.images[self.indexOfImg]))/2 self.window = 2*self.level self.levelScale.config(from_=np.min(self.images[self.indexOfImg])) self.levelScale.config(to=np.max(self.images[self.indexOfImg])) self.levelScale.set(self.level) self.windowScale.config(from_=0) self.windowScale.config(to=self.window) self.windowScale.set(self.window) if self.window < 1: self.levelScale.config(resolution=0.0001) self.windowScale.config(resolution=0.0001) self.img.set_clim(self.level-self.window/2, self.level+self.window/2) if self.cb: if self.cbar: self.cbar.set_clim(np.min(self.images[self.indexOfImg]), np.max(self.images[self.indexOfImg])) else: self.cbar = self.f.colorbar(self.img) self.canvas.draw() def set_level(self, event): self.level = self.levelScale.get() if self.level >= (np.max(self.images[self.indexOfImg])-np.min(self.images[self.indexOfImg]))/2: self.window = 2*(np.max(self.images[self.indexOfImg])-self.level) elif self.level < (np.max(self.images[self.indexOfImg])-np.min(self.images[self.indexOfImg]))/2: self.window = 2*(self.level-np.min(self.images[self.indexOfImg])) print(self.level, self.window) if self.windowScale.get() <= self.window: self.img.set_clim(float(self.level-self.windowScale.get()/2), float(self.level+self.windowScale.get()/2)) else: self.img.set_clim(float(self.level-self.window/2), float(self.level+self.window/2)) #self.level_is_set = True self.windowScale.config(to = self.window) self.canvas.draw() def set_window(self, event): self.window = self.windowScale.get() self.img.set_clim(float(self.level-self.window/2), float(self.level+self.window/2)) #self.level_is_set = True self.canvas.draw() def set_indexOfImg(self, index): self.indexOfImg = index def ruler(self): print('Measure!') def move(self): print('Move!') def info(self): info = tk.Toplevel() tk.Button(info, text='Close', command = info.destroy).pack(side=tk.TOP)
class wishbone_gui(tk.Tk): def __init__(self, parent): tk.Tk.__init__(self, parent) self.parent = parent self.initialize() def initialize(self): self.grid() self.vals = None self.currentPlot = None #set up menu bar self.menubar = tk.Menu(self) self.fileMenu = tk.Menu(self.menubar, tearoff=0) self.menubar.add_cascade(label="File", menu=self.fileMenu) self.fileMenu.add_command(label="Load data", command=self.loadData) self.fileMenu.add_command(label="Save data", state='disabled', command=self.saveData) self.fileMenu.add_command(label="Exit Wishbone", command=self.quitWB) self.analysisMenu = tk.Menu(self.menubar, tearoff=0) self.menubar.add_cascade(label="Analysis", menu=self.analysisMenu) self.analysisMenu.add_command(label="Principal component analysis", state='disabled', command=self.runPCA) self.analysisMenu.add_command(label="tSNE", state='disabled', command=self.runTSNE) self.analysisMenu.add_command(label="Diffusion map", state='disabled', command=self.runDM) self.analysisMenu.add_command(label="GSEA", state='disabled', command=self.runGSEA) self.analysisMenu.add_command(label="Wishbone", state='disabled', command=self.runWishbone) self.visMenu = tk.Menu(self.menubar, tearoff=0) self.menubar.add_cascade(label="Visualization", menu=self.visMenu) self.visMenu.add_command(label="Principal component analysis", state='disabled', command=self.plotPCA) self.visMenu.add_command(label="tSNE", state='disabled', command=self.plotTSNE) self.visMenu.add_command(label="Diffusion map", state='disabled', command=self.plotDM) self.visMenu.add_command(label="GSEA Results", state='disabled', command=self.showGSEAResults) self.wishboneMenu = tk.Menu(self) self.visMenu.add_cascade(label="Wishbone", menu=self.wishboneMenu) self.wishboneMenu.add_command(label="On tSNE", state='disabled', command=self.plotWBOnTsne) self.wishboneMenu.add_command(label="Marker trajectory", state='disabled', command=self.plotWBMarkerTrajectory) self.wishboneMenu.add_command(label="Heat map", state='disabled', command=self.plotWBHeatMap) self.visMenu.add_command(label="Gene expression", state='disabled', command=self.plotGeneExpOntSNE) self.visMenu.add_command(label="Set gate", state='disabled', command=self.setGate) self.config(menu=self.menubar) #intro screen tk.Label(self, text=u"Wishbone", font=('Helvetica', 48), fg="black", bg="white", padx=100, pady=50).grid(row=0) tk.Label( self, text= u"To get started, select a data file by clicking File > Load Data", fg="black", bg="white", padx=100, pady=25).grid(row=1) #update self.protocol('WM_DELETE_WINDOW', self.quitWB) self.grid_columnconfigure(0, weight=1) self.resizable(True, True) self.update() self.geometry(self.geometry()) self.focus_force() def loadData(self): self.dataFileName = filedialog.askopenfilename( title='Load data file', initialdir='~/.wishbone/data') if (self.dataFileName != ""): #pop up data options menu self.fileInfo = tk.Toplevel() self.fileInfo.title("Data options") tk.Label(self.fileInfo, text=u"File name: ").grid(column=0, row=0) tk.Label(self.fileInfo, text=self.dataFileName.split('/')[-1]).grid(column=1, row=0) tk.Label(self.fileInfo, text=u"Name:", fg="black", bg="white").grid(column=0, row=1) self.fileNameEntryVar = tk.StringVar() tk.Entry(self.fileInfo, textvariable=self.fileNameEntryVar).grid(column=1, row=1) if self.dataFileName.split('.')[len(self.dataFileName.split('.')) - 1] == 'fcs': tk.Label(self.fileInfo, text=u"Cofactor:", fg="black", bg="white").grid(column=0, row=2) self.cofactorVar = tk.IntVar() self.cofactorVar.set(5) tk.Entry(self.fileInfo, textvariable=self.cofactorVar).grid(column=1, row=2) elif self.dataFileName.split('.')[len(self.dataFileName.split('.')) - 1] == 'csv': self.normalizeVar = tk.BooleanVar() tk.Checkbutton(self.fileInfo, text=u"Normalize", variable=self.normalizeVar).grid(column=0, row=2, columnspan=2) tk.Label( self.fileInfo, text= u"The normalize parameter is used for correcting for library size among cells." ).grid(column=0, row=3, columnspan=2) tk.Button(self.fileInfo, text="Cancel", command=self.fileInfo.destroy).grid(column=0, row=4) tk.Button(self.fileInfo, text="Load", command=self.processData).grid(column=1, row=4) self.wait_window(self.fileInfo) def processData(self): #clear intro screen for item in self.grid_slaves(): item.grid_forget() #display file name tk.Label(self, text=u"File name: " + self.fileNameEntryVar.get(), fg="black", bg="white").grid(column=0, row=0) #set up canvas for plots self.fig, self.ax = wishbone.wb.get_fig() self.canvas = FigureCanvasTkAgg(self.fig, self) self.canvas.show() self.canvas.get_tk_widget().grid(column=1, row=1, rowspan=10, columnspan=4, sticky='NSEW') tk.Label(self, text=u"Visualizations:", fg='black', bg='white').grid(column=0, row=1) #load data based on input type if self.dataFileName.split('.')[len(self.dataFileName.split('.')) - 1] == 'fcs': # mass cytometry data self.scdata = wishbone.wb.SCData.from_fcs( os.path.expanduser(self.dataFileName), cofactor=self.cofactorVar.get()) self.wb = None elif self.dataFileName.split('.')[len(self.dataFileName.split('.')) - 1] == 'csv': # sc-seq data self.scdata = wishbone.wb.SCData.from_csv( os.path.expanduser(self.dataFileName), data_type='sc-seq', normalize=self.normalizeVar.get()) self.wb = None else: self.wb = wishbone.wb.Wishbone.load(self.dataFileName) self.scdata = self.wb.scdata #set up buttons based on data type if self.scdata.data_type == 'sc-seq': self.PCAButton = tk.Button(self, text=u"PCA", state='disabled', command=self.plotPCA) self.PCAButton.grid(column=0, row=2) self.tSNEButton = tk.Button(self, text=u"tSNE", state='disabled', command=self.plotTSNE) self.tSNEButton.grid(column=0, row=3) self.DMButton = tk.Button(self, text=u"Diffusion map", state='disabled', command=self.plotDM) self.DMButton.grid(column=0, row=4) self.GSEAButton = tk.Button(self, text=u"GSEA Results", state='disabled', command=self.showGSEAResults) self.GSEAButton.grid(column=0, row=5) self.WBButton = tk.Button(self, text=u"Wishbone", state='disabled', command=self.plotWBOnTsne) self.WBButton.grid(column=0, row=6) self.geneExpButton = tk.Button(self, text=u"Gene expression", state='disabled', command=self.plotGeneExpOntSNE) self.geneExpButton.grid(column=0, row=7) self.setGateButton = tk.Button(self, text=u"Set gate", state='disabled', command=self.setGate) self.setGateButton.grid(column=0, row=8) self.saveButton = tk.Button(self, text=u"Save plot", state='disabled', command=self.savePlot) self.saveButton.grid(column=4, row=0) self.diff_component = tk.StringVar() self.diff_component.set('Component 1') self.component_menu = tk.OptionMenu(self, self.diff_component, 'Component 1', 'Component 2', 'Component 3', 'Component 4', 'Component 5', 'Component 6', 'Component 7', 'Component 8', 'Component 9') self.component_menu.config(state='disabled') self.component_menu.grid(row=0, column=2) self.updateButton = tk.Button(self, text=u"Update component", command=self.updateComponent, state='disabled') self.updateButton.grid(column=3, row=0) #enable buttons based on current state of scdata object if self.scdata.pca: self.analysisMenu.entryconfig(1, state='normal') self.visMenu.entryconfig(0, state='normal') self.PCAButton.config(state='normal') if isinstance(self.scdata.tsne, pd.DataFrame): self.analysisMenu.entryconfig(2, state='normal') self.visMenu.entryconfig(1, state='normal') self.visMenu.entryconfig(5, state='normal') self.tSNEButton.config(state='normal') self.geneExpButton.config(state='normal') if isinstance(self.scdata.diffusion_eigenvectors, pd.DataFrame): self.analysisMenu.entryconfig(3, state='normal') self.analysisMenu.entryconfig(4, state='normal') self.visMenu.entryconfig(2, state='normal') self.DMButton.config(state='normal') else: self.tSNEButton = tk.Button(self, text=u"tSNE", state='disabled', command=self.plotTSNE) self.tSNEButton.grid(column=0, row=2) self.DMButton = tk.Button(self, text=u"Diffusion map", state='disabled', command=self.plotDM) self.DMButton.grid(column=0, row=3) self.WBButton = tk.Button(self, text=u"Wishbone", state='disabled', command=self.plotWBOnTsne) self.WBButton.grid(column=0, row=4) self.geneExpButton = tk.Button(self, text=u"Gene expression", state='disabled', command=self.plotGeneExpOntSNE) self.geneExpButton.grid(column=0, row=5) self.setGateButton = tk.Button(self, text=u"Set gate", state='disabled', command=self.setGate) self.setGateButton.grid(column=0, row=6) self.saveButton = tk.Button(self, text=u"Save plot", state='disabled', command=self.savePlot) self.saveButton.grid(column=4, row=0) self.analysisMenu.delete(0) self.analysisMenu.delete(2) self.visMenu.delete(0) self.visMenu.delete(2) self.analysisMenu.entryconfig(1, state='normal') #enable buttons based on current state of scdata object if isinstance(self.scdata.tsne, pd.DataFrame): self.visMenu.entryconfig(0, state='normal') self.visMenu.entryconfig(3, state='normal') self.tSNEButton.config(state='normal') self.geneExpButton.config(state='normal') if isinstance(self.scdata.diffusion_eigenvectors, pd.DataFrame): self.analysisMenu.entryconfig(2, state='normal') self.visMenu.entryconfig(1, state='normal') self.DMButton.config(state='normal') #enable buttons self.analysisMenu.entryconfig(0, state='normal') self.fileMenu.entryconfig(1, state='normal') if self.wb: if isinstance(self.wb.trajectory, pd.Series): self.wishboneMenu.entryconfig(0, state='normal') self.wishboneMenu.entryconfig(1, state='normal') self.wishboneMenu.entryconfig(2, state='normal') self.WBButton.config(state='normal') #get genes self.genes = self.scdata.data.columns.values self.gates = {} self.geometry('800x550') #destroy pop up menu self.fileInfo.destroy() def saveData(self): pickleFileName = filedialog.asksaveasfilename( title='Save Wishbone Data', defaultextension='.p', initialfile=self.fileNameEntryVar.get()) if pickleFileName != None: if self.wb != None: self.wb.save(pickleFileName) else: self.scdata.save_as_wishbone(pickleFileName) def runPCA(self): self.scdata.run_pca() #enable buttons self.analysisMenu.entryconfig(1, state='normal') self.visMenu.entryconfig(0, state='normal') self.PCAButton.config(state='normal') def runTSNE(self): #pop up for # components self.tsneOptions = tk.Toplevel() self.tsneOptions.title("tSNE options") if self.scdata.data_type == 'sc-seq': tk.Label(self.tsneOptions, text=u"Number of components:", fg="black", bg="white").grid(column=0, row=0) self.nCompVar = tk.IntVar() self.nCompVar.set(15) tk.Entry(self.tsneOptions, textvariable=self.nCompVar).grid(column=1, row=0) tk.Label(self.tsneOptions, text=u"Perplexity:", fg="black", bg="white").grid(column=0, row=1) self.perplexityVar = tk.IntVar() self.perplexityVar.set(30) tk.Entry(self.tsneOptions, textvariable=self.perplexityVar).grid(column=1, row=1) tk.Button(self.tsneOptions, text="Run", command=self._runTSNE).grid(column=1, row=2) tk.Button(self.tsneOptions, text="Cancel", command=self.tsneOptions.destroy).grid(column=0, row=2) self.wait_window(self.tsneOptions) def _runTSNE(self): if self.scdata.data_type == 'sc-seq': self.scdata.run_tsne(n_components=self.nCompVar.get(), perplexity=self.perplexityVar.get()) else: self.scdata.run_tsne(n_components=None, perplexity=self.perplexityVar.get()) self.gates = {} #enable buttons if self.scdata.data_type == 'sc-seq': self.analysisMenu.entryconfig(2, state='normal') self.visMenu.entryconfig(1, state='normal') self.visMenu.entryconfig(5, state='normal') else: self.visMenu.entryconfig(0, state='normal') self.visMenu.entryconfig(3, state='normal') self.tSNEButton.config(state='normal') self.geneExpButton.config(state='normal') self.tsneOptions.destroy() def runDM(self): self.scdata.run_diffusion_map() #enable buttons if self.scdata.data_type == 'sc-seq': self.analysisMenu.entryconfig(3, state='normal') self.analysisMenu.entryconfig(4, state='normal') self.visMenu.entryconfig(2, state='normal') else: self.analysisMenu.entryconfig(2, state='normal') self.visMenu.entryconfig(1, state='normal') self.DMButton.config(state='normal') def runGSEA(self): self.GSEAFileName = filedialog.askopenfilename( title='Select gmt File', initialdir='~/.wishbone/tools') if self.GSEAFileName != "": self.scdata.run_diffusion_map_correlations() self.scdata.data.columns = self.scdata.data.columns.str.upper() self.outputPrefix = filedialog.asksaveasfilename( title='Input file prefix for saving output', initialdir='~/.wishbone/gsea') if 'mouse' in self.GSEAFileName: gmt_file_type = 'mouse' else: gmt_file_type = 'human' self.reports = self.scdata.run_gsea( output_stem=os.path.expanduser(self.outputPrefix), gmt_file=(gmt_file_type, self.GSEAFileName.split('/')[-1])) #enable buttons self.visMenu.entryconfig(3, state='normal') self.GSEAButton.config(state='normal') def runWishbone(self): #popup menu for wishbone options self.wbOptions = tk.Toplevel() self.wbOptions.title("Wishbone Options") #s tk.Label(self.wbOptions, text=u"Start cell:", fg="black", bg="white").grid(column=0, row=0) self.start = tk.StringVar() tk.Entry(self.wbOptions, textvariable=self.start).grid(column=1, row=0) if (len(self.gates) > 0): self.cell_gate = tk.StringVar() self.cell_gate.set('Use cell gate') self.gate_menu = tk.OptionMenu(self.wbOptions, self.cell_gate, *list(self.gates.keys())) self.gate_menu.grid(row=0, column=2) #k tk.Label(self.wbOptions, text=u"k:", fg="black", bg="white").grid(column=0, row=1) self.k = tk.IntVar() tk.Entry(self.wbOptions, textvariable=self.k).grid(column=1, row=1) self.k.set(15) #components list tk.Label(self.wbOptions, text=u"Components list:", fg='black', bg='white').grid(column=0, row=2) self.compList = tk.StringVar() tk.Entry(self.wbOptions, textvariable=self.compList).grid(column=1, row=2) self.compList.set("1, 2, 3") #num waypoints tk.Label(self.wbOptions, text=u"Number of waypoints:", fg='black', bg='white').grid(column=0, row=3) self.numWaypoints = tk.IntVar() tk.Entry(self.wbOptions, textvariable=self.numWaypoints).grid(column=1, row=3) self.numWaypoints.set(250) #branch self.branch = tk.BooleanVar() self.branch.set(True) tk.Checkbutton(self.wbOptions, text=u"Branch", variable=self.branch).grid(column=0, row=4, columnspan=2) tk.Button(self.wbOptions, text="Run", command=self._runWishbone).grid(column=1, row=5) tk.Button(self.wbOptions, text="Cancel", command=self.wbOptions.destroy).grid(column=0, row=5) self.wait_window(self.wbOptions) def _runWishbone(self): self.wb = wishbone.wb.Wishbone(self.scdata) if self.cell_gate.get() == 'Use cell gate': self.wb.run_wishbone(start_cell=self.start.get(), k=self.k.get(), components_list=[ int(comp) for comp in self.compList.get().split(',') ], num_waypoints=self.numWaypoints.get()) else: #randomly select start cell in gate print('Using cell gate:') print(self.cell_gate.get()) start_cell = random.sample(list(self.gates[self.cell_gate.get()]), 1)[0] print(start_cell) self.wb.run_wishbone(start_cell=start_cell, k=self.k.get(), components_list=[ int(comp) for comp in self.compList.get().split(',') ], num_waypoints=self.numWaypoints.get()) #enable buttons self.wishboneMenu.entryconfig(0, state='normal') self.wishboneMenu.entryconfig(1, state='normal') self.wishboneMenu.entryconfig(2, state='normal') self.WBButton.config(state='normal') self.wbOptions.destroy() def plotPCA(self): self.saveButton.config(state='normal') self.setGateButton.config(state='disabled') if self.scdata.data_type == 'sc-seq': self.component_menu.config(state='disabled') self.updateButton.config(state='disabled') self.visMenu.entryconfig(6, state='disabled') else: self.visMenu.entryconfig(4, state='disabled') #pop up for # components self.PCAOptions = tk.Toplevel() self.PCAOptions.title("PCA Plot Options") tk.Label(self.PCAOptions, text=u"Max variance explained (ylim):", fg="black", bg="white").grid(column=0, row=0) self.yLimVar = tk.DoubleVar() self.yLimVar.set(round(self.scdata.pca['eigenvalues'][0][0], 2)) tk.Entry(self.PCAOptions, textvariable=self.yLimVar).grid(column=1, row=0) tk.Label(self.PCAOptions, text=u"Number of components:", fg='black', bg='white').grid(column=0, row=1) self.compVar = tk.IntVar() self.compVar.set(15) tk.Entry(self.PCAOptions, textvariable=self.compVar).grid(column=1, row=1) tk.Button(self.PCAOptions, text="Plot", command=self._plotPCA).grid(column=1, row=2) tk.Button(self.PCAOptions, text="Cancel", command=self.PCAOptions.destroy).grid(column=0, row=2) self.wait_window(self.PCAOptions) def _plotPCA(self): self.resetCanvas() self.fig, self.ax = self.scdata.plot_pca_variance_explained( ylim=(0, self.yLimVar.get()), n_components=self.compVar.get()) self.canvas = FigureCanvasTkAgg(self.fig, self) self.canvas.show() self.canvas.get_tk_widget().grid(column=1, row=1, rowspan=10, columnspan=4, sticky='NW') self.currentPlot = 'pca' #enable buttons self.saveButton.config(state='normal') self.PCAOptions.destroy() def plotTSNE(self): self.saveButton.config(state='normal') self.setGateButton.config(state='normal') if self.scdata.data_type == 'sc-seq': self.component_menu.config(state='disabled') self.updateButton.config(state='disabled') self.visMenu.entryconfig(6, state='normal') else: self.visMenu.entryconfig(4, state='normal') self.resetCanvas() self.fig, self.ax = self.scdata.plot_tsne() self.canvas = FigureCanvasTkAgg(self.fig, self) self.canvas.show() self.canvas.get_tk_widget().grid(column=1, row=1, rowspan=10, columnspan=4, sticky='NW') self.currentPlot = 'tsne' def plotDM(self): self.saveButton.config(state='normal') self.setGateButton.config(state='disabled') if self.scdata.data_type == 'sc-seq': self.component_menu.config(state='disabled') self.updateButton.config(state='disabled') self.visMenu.entryconfig(6, state='disabled') else: self.visMenu.entryconfig(4, state='disabled') self.geometry('950x550') self.resetCanvas() self.fig, self.ax = self.scdata.plot_diffusion_components() self.canvas = FigureCanvasTkAgg(self.fig, self) self.canvas.show() self.canvas.get_tk_widget().grid(column=1, row=1, rowspan=10, columnspan=4, sticky='W') self.currentPlot = 'dm_components' def showGSEAResults(self): self.saveButton.config(state='disabled') self.component_menu.config(state='normal') self.updateButton.config(state='normal') self.setGateButton.config(state='disabled') self.visMenu.entryconfig(6, state='disabled') self.resetCanvas() self.canvas = tk.Canvas(self, width=600, height=300) self.canvas.grid(column=1, row=1, rowspan=17, columnspan=4) self.outputText(1) self.currentPlot = 'GSEA_result_' + self.diff_component.get() def updateComponent(self): self.resetCanvas() self.canvas = tk.Canvas(self, width=600, height=300) self.canvas.grid(column=1, row=1, rowspan=17, columnspan=4, sticky='NSEW') self.outputText(int(self.diff_component.get().split(' ')[-1])) self.currentPlot = 'GSEA_result_' + self.diff_component.get() def outputText(self, diff_component): pos_text = str(self.reports[diff_component]['pos']).split('\n') pos_text = pos_text[1:len(pos_text) - 1] pos_text = '\n'.join(pos_text) neg_text = str(self.reports[diff_component]['neg']).split('\n') neg_text = neg_text[1:len(neg_text) - 1] neg_text = '\n'.join(neg_text) self.canvas.create_text(5, 5, anchor='nw', text='Positive correlations:\n\n', font=('Helvetica', 16, 'bold')) self.canvas.create_text(5, 50, anchor='nw', text=pos_text) self.canvas.create_text(5, 150, anchor='nw', text='Negative correlations:\n\n', font=('Helvetica', 16, 'bold')) self.canvas.create_text(5, 200, anchor='nw', text=neg_text) def plotWBOnTsne(self): self.saveButton.config(state='normal') self.setGateButton.config(state='disabled') if self.scdata.data_type == 'sc-seq': self.component_menu.config(state='disabled') self.updateButton.config(state='disabled') self.visMenu.entryconfig(6, state='disabled') else: self.visMenu.entryconfig(4, state='disabled') self.resetCanvas() self.fig, self.ax = self.wb.plot_wishbone_on_tsne() self.canvas = FigureCanvasTkAgg(self.fig, self) self.canvas.show() self.canvas.get_tk_widget().grid(column=1, row=1, rowspan=10, columnspan=4) self.currentPlot = 'wishbone_on_tsne' def plotWBMarkerTrajectory(self): self.getGeneSelection() if len(self.selectedGenes) < 1: print('Error: must select at least one gene') else: self.saveButton.config(state='normal') self.setGateButton.config(state='disabled') if self.scdata.data_type == 'sc-seq': self.component_menu.config(state='disabled') self.updateButton.config(state='disabled') self.visMenu.entryconfig(6, state='disabled') else: self.visMenu.entryconfig(4, state='disabled') self.resetCanvas() self.vals, self.fig, self.ax = self.wb.plot_marker_trajectory( self.selectedGenes) self.fig.set_size_inches(10, 4, forward=True) self.fig.tight_layout() self.fig.subplots_adjust(right=0.8) self.canvas = FigureCanvasTkAgg(self.fig, self) self.canvas.show() self.canvas.get_tk_widget().grid(column=1, row=1, rowspan=10, columnspan=5, sticky='W') self.currentPlot = 'wishbone_marker_trajectory' self.geometry('1050x550') #enable buttons self.wishboneMenu.entryconfig(2, state='normal') def plotWBHeatMap(self): self.getGeneSelection() if len(self.selectedGenes) < 1: print('Error: must select at least one gene') else: self.saveButton.config(state='normal') self.setGateButton.config(state='disabled') if self.scdata.data_type == 'sc-seq': self.component_menu.config(state='disabled') self.updateButton.config(state='disabled') self.visMenu.entryconfig(6, state='disabled') else: self.visMenu.entryconfig(4, state='disabled') self.resetCanvas() self.vals, self.fig, self.ax = self.wb.plot_marker_trajectory( self.selectedGenes) self.fig, self.ax = self.wb.plot_marker_heatmap(self.vals) self.fig.set_size_inches(10, 4, forward=True) self.fig.tight_layout() self.canvas = FigureCanvasTkAgg(self.fig, self) self.canvas.show() self.canvas.get_tk_widget().grid(column=1, row=1, rowspan=10, columnspan=5, sticky='W') self.currentPlot = 'wishbone_marker_heatmap' def plotGeneExpOntSNE(self): self.getGeneSelection() if len(self.selectedGenes) < 1: print('Error: must select at least one gene') else: self.saveButton.config(state='normal') self.setGateButton.config(state='disabled') if self.scdata.data_type == 'sc-seq': self.component_menu.config(state='disabled') self.updateButton.config(state='disabled') self.visMenu.entryconfig(6, state='disabled') else: self.visMenu.entryconfig(4, state='disabled') self.resetCanvas() self.fig, self.ax = self.scdata.plot_gene_expression( self.selectedGenes) self.canvas = FigureCanvasTkAgg(self.fig, self) self.canvas.show() self.canvas.get_tk_widget().grid(column=1, row=1, rowspan=10, columnspan=4, sticky='W') self.currentPlot = 'gene_expression_tsne' self.geometry('950x550') def getGeneSelection(self): #popup menu to get selected genes self.geneSelection = tk.Toplevel() self.geneSelection.title("Select Genes") tk.Label(self.geneSelection, text=u"Genes:", fg="black", bg="white").grid(row=0) self.geneInput = wishbone.autocomplete_entry.AutocompleteEntry( self.genes.tolist(), self.geneSelection, listboxLength=6) self.geneInput.grid(row=1) self.geneInput.bind('<Return>', self.AddToSelected) self.geneSelectBox = tk.Listbox(self.geneSelection, selectmode=tk.EXTENDED) self.geneSelectBox.grid(row=2, rowspan=10) self.geneSelectBox.bind('<BackSpace>', self.DeleteSelected) self.selectedGenes = [] tk.Button(self.geneSelection, text="Use selected genes", command=self.geneSelection.destroy).grid(row=12) tk.Button(self.geneSelection, text="Cancel", command=self.cancelGeneSelection).grid(row=13) self.wait_window(self.geneSelection) def cancelGeneSelection(self): self.selectedGenes = [] self.geneSelection.destroy() def AddToSelected(self, event): self.selectedGenes.append(self.geneInput.get()) self.geneSelectBox.insert( tk.END, self.selectedGenes[len(self.selectedGenes) - 1]) def DeleteSelected(self, event): selected = self.geneSelectBox.curselection() pos = 0 for i in selected: idx = int(i) - pos self.geneSelectBox.delete(idx, idx) self.selectedGenes = self.selectedGenes[:idx] + self.selectedGenes[ idx + 1:] pos = pos + 1 def savePlot(self): self.plotFileName = filedialog.asksaveasfilename( title='Save Plot', defaultextension='.png', initialfile=self.fileNameEntryVar.get() + "_" + self.currentPlot) if self.plotFileName != None: self.fig.savefig(self.plotFileName) def setGate(self): #pop up for gate name self.gateOptions = tk.Toplevel() self.gateOptions.title("Create gate for start cells") tk.Label(self.gateOptions, text=u"Gate name:", fg="black", bg="white").grid(column=0, row=0) self.gateName = tk.StringVar() self.gateName.set('Gate ' + str(len(self.gates) + 1)) tk.Entry(self.gateOptions, textvariable=self.gateName).grid(column=1, row=0) tk.Button(self.gateOptions, text="Select gate", command=self._setGate).grid(column=1, row=1) tk.Button(self.gateOptions, text="Cancel", command=self.gateOptions.destroy).grid(column=0, row=1) self.wait_window(self.gateOptions) def _setGate(self): self.gateOptions.destroy() self.buttonPress = self.canvas.mpl_connect('button_press_event', self._startGate) self.buttonRelease = self.canvas.mpl_connect('button_release_event', self._endGate) self.canvas.get_tk_widget().config(cursor='plus') def _startGate(self, event): self.start_x = event.xdata self.start_y = event.ydata def _endGate(self, event): #draw gate rectangle start_x = self.start_x if self.start_x < event.xdata else event.xdata start_y = self.start_y if self.start_y < event.ydata else event.ydata width = np.absolute(event.xdata - self.start_x) height = np.absolute(event.ydata - self.start_y) rect = Rectangle((start_x, start_y), width, height, fill=False, ec='black', alpha=1, lw=2) self.ax.add_patch(rect) self.canvas.draw() #disable mouse events self.canvas.mpl_disconnect(self.buttonPress) self.canvas.mpl_disconnect(self.buttonRelease) self.canvas.get_tk_widget().config(cursor='arrow') #save cell gate gate = Path([[start_x, start_y], [start_x + width, start_y], [start_x + width, start_y + height], [start_x, start_y + height], [start_x, start_y]]) gated_cells = self.scdata.tsne.index[gate.contains_points( self.scdata.tsne)] self.gates[self.gateName.get()] = gated_cells #replot tSNE w gate colored self.fig.clf() plt.scatter(self.scdata.tsne['x'], self.scdata.tsne['y'], s=10, edgecolors='none', color='lightgrey') plt.scatter(self.scdata.tsne.ix[gated_cells, 'x'], self.scdata.tsne.ix[gated_cells, 'y'], s=10, edgecolors='none') self.canvas.draw() self.setGateButton.config(state='disabled') self.visMenu.entryconfig(6, state='disabled') def resetCanvas(self): self.fig.clf() if type(self.canvas) is FigureCanvasTkAgg: for item in self.canvas.get_tk_widget().find_all(): self.canvas.get_tk_widget().delete(item) else: for item in self.canvas.find_all(): self.canvas.delete(item) def quitWB(self): self.quit() self.destroy()
class MultilevelEditingEditingLayout: def __init__(self, root_frame, figure, max_lod): print('hi from MultilevelEditingEditingLayout') self.root_frame = root_frame self.fig = figure self.max_lod = max_lod self.root_frame.columnconfigure(0, weight=1) self.root_frame.rowconfigure(0, weight=1) self.left_frame = ttk.Frame(self.root_frame) self.left_frame.grid(column=0, row=0, sticky=('N', 'E', 'S', 'W')) self.left_frame.columnconfigure(0, weight=1) self.left_frame.rowconfigure(0, weight=1) self.canvas = FigureCanvasTkAgg(self.fig, master=self.left_frame) self.canvas.draw() self.canvas.get_tk_widget().grid(column=0, row=0, sticky=('N', 'S', 'E', 'W')) self.display_lod_scale_head = ttk.Label( self.left_frame, text='Level of Detail of the displayed curve:') self.display_lod_scale_head.grid(column=0, row=1, sticky=('W', 'N', 'E', 'S')) self.display_lod_scale = ttk.Scale(self.left_frame, from_=0, to=self.max_lod, orient='horizontal') self.display_lod_scale.grid(column=0, row=2, sticky=('W', 'N', 'E', 'S')) self.edit_lod_scale_head = ttk.Label( self.left_frame, text='Level of Detail for editing:') self.edit_lod_scale_head.grid(column=0, row=3, sticky=('W', 'N', 'E', 'S')) self.edit_lod_scale = ttk.Scale(self.left_frame, from_=0, to=self.max_lod, orient='horizontal') self.edit_lod_scale.grid(column=0, row=4, sticky=('W', 'N', 'E', 'S')) self.toolbar_frame = ttk.Frame(self.root_frame) self.toolbar_frame.grid(column=1, row=0, sticky=('N', 'E', 'S', 'W')) self.toolbar_frame.columnconfigure(0, weight=1) self.toolbar_frame.rowconfigure(1, weight=1) self.toolbar_label = ttk.Label(self.toolbar_frame, text='Toolbar') self.toolbar_label.grid(column=0, row=0, sticky=('W', 'E')) self.back_btn = ttk.Button(self.toolbar_frame, text='Back') self.back_btn.grid(column=0, row=1, sticky=('W', 'E', 'S')) def mpl_connect(self, event_type, callback): return self.canvas.mpl_connect(event_type, callback) def mpl_disconnect(self, cid): self.canvas.mpl_disconnect(cid) def redraw_figure(self): self.canvas.draw() def set_display_lod_scale_value(self, value): self.display_lod_scale.set(value) def set_edit_lod_scale_value(self, value): self.edit_lod_scale.set(value) def set_display_lod_scale_callback(self, callback): self.display_lod_scale['command'] = callback def set_edit_lod_scale_callback(self, callback): self.edit_lod_scale['command'] = callback def set_back_callback(self, callback): self.back_btn['command'] = callback def clean_up(self): print('MultilevelEditingEditingLayout cleaning up') self.back_btn.grid_forget() self.toolbar_label.grid_forget() self.toolbar_frame.grid_forget() self.edit_lod_scale.grid_forget() self.edit_lod_scale_head.grid_forget() self.display_lod_scale.grid_forget() self.display_lod_scale_head.grid_forget() self.canvas.get_tk_widget().grid_forget() self.left_frame.grid_forget()
class Graph(tk.Frame): ax, original_xlim, original_ylim, coll, pick_id = None, None, None, None, None start_end_bool = -1 # 0 is start, 1 is end, -1 is None start_index, end_index, move_index, remove_index = 0, None, None, None max_time, max_speed, max_height, min_height = 0, 0, 0, 0 data = [] connect_ids, move_ids = [], [] current_color_scheme = 'd' def __init__(self, master): super().__init__(master) self.master = master self.fig = Figure(figsize=(10, 7), dpi=100, tight_layout=True) self.fig.set_facecolor('#f0f0ed') self.zoom_pan = GraphInteractions.ZoomPan() self.master_string = tk.StringVar() self.canvas = FigureCanvasTkAgg(self.fig, self) self.canvas.draw() self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1) def redraw(self, input_file_location): x, y, c = zip(*self.read_data(input_file_location)) self.fig.clear() self.ax = self.fig.add_subplot(111, frameon=False) self.coll = self.ax.scatter(x, y, color=c, picker=True) self.end_index = len(self.data) - 1 self.connect_ids.append( GraphInteractions.ZoomPan.zoom_factory(self.zoom_pan, self.ax, base_scale=1.3)) self.connect_ids.extend( GraphInteractions.ZoomPan.pan_factory(self.zoom_pan, self.ax)) self.original_xlim = self.ax.get_xlim() self.original_ylim = self.ax.get_ylim() self.canvas.draw() def redraw_color(self, x, y, c): self.fig.clear() self.ax = self.fig.add_subplot(111, frameon=False) self.coll = self.ax.scatter(x, y, color=c, picker=True) self.ax.set_xlim(self.zoom_pan.cur_xlim) self.ax.set_ylim(self.zoom_pan.cur_ylim) self.connect_ids.append( GraphInteractions.ZoomPan.zoom_factory(self.zoom_pan, self.ax, base_scale=1.3)) self.connect_ids.extend( GraphInteractions.ZoomPan.pan_factory(self.zoom_pan, self.ax)) self.canvas.draw() def reset_zoom(self): self.ax.set_xlim(self.original_xlim) self.ax.set_ylim(self.original_ylim) self.ax.figure.canvas.draw() # force re-draw def reset_start(self, master_string): self.start_index = 0 master_string.set("") self.redraw_ext() def reset_end(self, master_string): self.end_index = len(self.data) - 1 master_string.set("") self.redraw_ext() def read_data(self, input_file_location): x_array = [] y_array = [] color_array = [] self.data.clear() json_file = open(input_file_location, "r") file_data = json.load(json_file) json_file.close() old_time, old_x, old_y = 0, file_data['recording']['path'][0][ 'x'], file_data['recording']['path'][0]['y'] self.max_time, self.max_speed, self.max_height, self.min_height = 0, 0, 0, file_data[ 'recording']['path'][0]['z'] speed = 0 for time in file_data['recording']['path']: x_array.append(time['x']) y_array.append(time['y']) time_div = time['t'] - old_time if time['t'] > self.max_time: self.max_time = time['t'] if time_div != 0: speed = distance(time['x'], old_x, time['y'], old_y) / time_div if speed > self.max_speed: self.max_speed = speed if time['z'] > self.max_height: self.max_height = time['z'] elif time['z'] < self.min_height: self.min_height = time['z'] self.data.append( [time['x'], time['y'], time['t'], speed, time['z']]) color_array.append('tab:blue') old_time = time['t'] old_x, old_y = time['x'], time['y'] return zip(x_array, y_array, color_array) def redraw_ext(self): for cid in self.connect_ids: self.canvas.mpl_disconnect(cid) self.connect_ids.clear() x = [] y = [] c = [] for idx, row in enumerate(self.data): x.append(row[0]) y.append(row[1]) if idx == self.start_index or idx == self.end_index or idx == self.remove_index: c.append('tab:red') elif idx < self.start_index or idx > self.end_index or\ (self.start_index == 0 and self.end_index == len(self.data) - 1): c.append('tab:blue') elif self.start_index < idx < self.end_index: c.append('c') self.redraw_color(x, y, c) def redraw_simp(self): for cid in self.connect_ids: self.canvas.mpl_disconnect(cid) self.connect_ids.clear() x = [] y = [] c = [] for idx, row in enumerate(self.data): x.append(row[0]) y.append(row[1]) if idx == self.move_index: c.append('tab:red') else: c.append('tab:blue') self.redraw_color(x, y, c) def change_color(self, event): if event.mouseevent.button != 1: return if self.start_end_bool == 0: self.start_index = event.ind[0] elif self.start_end_bool == 1: self.end_index = event.ind[0] elif self.start_end_bool == 2: self.remove_index = event.ind[0] self.redraw_ext() self.master_string.set( 'x: %.4f, y: %.4f' % (self.data[event.ind[0]][0], self.data[event.ind[0]][1])) def show_time(self): self.current_color_scheme = 't' for cid in self.connect_ids: self.canvas.mpl_disconnect(cid) self.connect_ids.clear() x = [] y = [] c = [] for idx, row in enumerate(self.data): x.append(row[0]) y.append(row[1]) gradient = row[2] / self.max_time c.append((0, 1 - gradient, gradient)) self.redraw_color(x, y, c) def show_speed(self): self.current_color_scheme = 's' for cid in self.connect_ids: self.canvas.mpl_disconnect(cid) self.connect_ids.clear() x = [] y = [] c = [] for idx, row in enumerate(self.data): x.append(row[0]) y.append(row[1]) gradient = row[3] / self.max_speed c.append((1 - gradient, gradient, 0)) self.redraw_color(x, y, c) def show_height(self): self.current_color_scheme = 'h' for cid in self.connect_ids: self.canvas.mpl_disconnect(cid) self.connect_ids.clear() x = [] y = [] c = [] for idx, row in enumerate(self.data): x.append(row[0]) y.append(row[1]) gradient = (row[4] - self.min_height) / (self.max_height - self.min_height) c.append((gradient * 0.5 + 0.5, gradient * 0.8, gradient * 0.8)) self.redraw_color(x, y, c) def reset_color(self): self.current_color_scheme = 'd' for cid in self.connect_ids: self.canvas.mpl_disconnect(cid) self.connect_ids.clear() x = [] y = [] c = [] for idx, row in enumerate(self.data): x.append(row[0]) y.append(row[1]) c.append('tab:blue') self.redraw_color(x, y, c) def attach_start_stop(self, master_string_var, start_end): self.detach_start_stop() self.start_end_bool = start_end self.master_string = master_string_var self.pick_id = self.canvas.mpl_connect('pick_event', self.change_color) def detach_start_stop(self): if self.pick_id is None: return self.start_end_bool = -1 self.canvas.mpl_disconnect(self.pick_id) for move_id in self.move_ids: self.canvas.mpl_disconnect(move_id) def attach_move_node(self): def on_press(event): if event.mouseevent.button != 1: return self.move_index = event.ind[0] self.master.move_node_location.set( 'x: %.4f, y: %.4f' % (self.data[self.move_index][0], self.data[self.move_index][1])) def on_release(event): self.redraw_simp() def on_motion(event): if event.button != 1: return self.data[self.move_index][0] = event.xdata self.data[self.move_index][1] = event.ydata self.master.move_node_location.set( 'x: %.4f, y: %.4f' % (self.data[self.move_index][0], self.data[self.move_index][1])) self.redraw_simp() self.detach_start_stop() self.pick_id = self.canvas.mpl_connect('pick_event', on_press) self.move_ids.append( self.canvas.mpl_connect('key_press_event', self.move_node_button)) self.move_ids.append( self.canvas.mpl_connect('button_release_event', on_release)) self.move_ids.append( self.canvas.mpl_connect('motion_notify_event', on_motion)) def attach_remove_node(self, master_string): self.detach_start_stop() self.start_end_bool = 2 self.master_string = master_string self.pick_id = self.canvas.mpl_connect('pick_event', self.change_color) def next_start(self, master_string, diff): self.start_index += diff if self.start_index < 0: self.start_index = 0 self.redraw_ext() master_string.set( 'x: %.4f, y: %.4f' % (self.data[self.start_index][0], self.data[self.start_index][1])) def next_end(self, master_string, diff): self.end_index += diff if self.end_index > len(self.data) - 1: self.end_index = len(self.data) - 1 self.redraw_ext() master_string.set( 'x: %.4f, y: %.4f' % (self.data[self.end_index][0], self.data[self.end_index][1])) def move_node_button(self, event): direction = "" if event.key == "up": direction = "N" elif event.key == "right": direction = "E" elif event.key == "down": direction = "S" elif event.key == "left": direction = "W" if direction != "": self.move_node(direction, float(self.master.move_entry.get())) def move_node(self, direction, move_distance): if direction == "N": self.data[self.move_index][1] += move_distance elif direction == "E": self.data[self.move_index][0] += move_distance elif direction == "S": self.data[self.move_index][1] -= move_distance elif direction == "W": self.data[self.move_index][0] -= move_distance self.master.move_node_location.set( 'x: %.4f, y: %.4f' % (self.data[self.move_index][0], self.data[self.move_index][1])) self.redraw_simp() def remove_node(self): del self.data[self.remove_index] self.end_index = len(self.data) - 1 self.redraw_ext() self.master_string.set( 'x: %.4f, y: %.4f' % (self.data[self.remove_index][0], self.data[self.remove_index][1])) def apply_local_speedup(self, value): speedup = 1 speed = 0 previous_time = 0 previous_old_time = 0 old_x, old_y = self.data[0][0], self.data[0][1] for idx, row in enumerate(self.data): if self.start_index == idx: speedup = 1 - float(value) / 100 elif self.end_index == idx: speedup = 1 old_time = row[2] if old_time < previous_old_time: previous_old_time = old_time / 2 row[2] = previous_time + (min(old_time - previous_old_time, 2) * speedup) time_div = row[2] - previous_time if row[2] > self.max_time: self.max_time = row[2] if time_div != 0: speed = distance(row[0], old_x, row[1], old_y) / time_div if speed > self.max_speed: self.max_speed = speed row[3] = speed previous_time = row[2] previous_old_time = old_time old_x, old_y = row[0], row[1] self.update_unknown() def update_unknown(self): if self.current_color_scheme == 'h': self.show_height() elif self.current_color_scheme == 's': self.show_speed() elif self.current_color_scheme == 't': self.show_time() else: self.reset_color()
class PASAview(Frame): #define parent window def __init__(self, parent): Frame.__init__(self, parent) #save a reference to the parent widget self.parent = parent #delegate the creation of the user interface to the initUI() method self.fig = Figure(figsize=(12, 8), dpi=100) self.fig.subplots_adjust(bottom=0.15, left=0.13, right=0.95, top=0.95) self.canvas = FigureCanvasTkAgg(self.fig, self) self.toolbar = NavigationToolbar2TkAgg(self.canvas, self) self.toolbar.pack(side=TOP) #frame for buttons (at bottom) #buttons are arranged on grid, not pack self.frame_button = Frame(self) self.frame_button.pack(side='bottom', fill=BOTH, expand=1) #global variables #list containing wavelength domain values self.wavelength = [] self.wavelength_reference = [] #list containing wavenumber domain values self.wavenumber = [] self.wavenumber_reference = [] #list containing reflectance values self.refl = [] self.refl_reference = [] #list containing filename for legend self.spec_name = [] self.spec_name_legend = [] self.spec_name_reference = [] self.spec_name_reference_legend = [] self.legend_fontsize = 10 #offset/scale values self.offset = [0.0] self.scale = [1.0] self.offset_reference = [] self.scale_reference = [] #flag to indicate autoscaling or user-defined scaling of axis #autoscale flag=0 #user-defined flag=1 self.define_axis_flag = 0 self.xlim = [1.6, 3.6] self.ylim = [0, 1] #linestyle definition self.linestyle_format = ['k-'] self.linestyle_format_legend = ['k-'] self.linestyle_format_reference = [] self.linestyle_format_reference_legend = [] #flag to indicate whether wavelength or wavenumber is displayed #wavelength flag=0 #wavenumber flag=1 self.wavelength_wavenumber_flag = 0 self.display_spectrum = IntVar() self.display_spectrum.set(1) self.display_spectrum_reference = [] #list containing label values self.label_wavelength = [] self.label_wavenumber = [] self.label_refl = [] self.label_size = float(10) #self.label_format = "%.0f" self.label_value = [] #self.digits_after = 0 #self.label_offset = 0.0 #calibration point id self.cid = -99 self.initUI() #container for other widgets def initUI(self): #set the title of the window using the title() method self.parent.title("PASA View") #apply a theme to widget self.style = Style() self.style.theme_use('default') #the Frame widget (accessed via the delf attribute) in the root window #it is expanded in both directions self.pack(fill=BOTH, expand=1) menubar = Menu(self.parent) self.parent.config(menu=menubar) #contains file menu items fileMenu = Menu(menubar, tearoff=0) menubar.add_cascade(label="File", menu=fileMenu) self.openMenu = Menu(menubar, tearoff=0) fileMenu.add_cascade(label="Open", menu=self.openMenu, state=NORMAL) self.openMenu.add_command(label="Open PASA Spectrum", command=self.onOpen) self.openMenu.add_command(label="Open Reference Spectrum", command=self.onOpenReference, state=DISABLED) #fileMenu.add_command(label="Open", command=self.onOpen) fileMenu.add_command(label="Export Data (ASCII file)", command=self.onExport) fileMenu.add_command(label="Quit", command=self.quit_destroy) #generate figure (no data at first) self.generate_figure([0, 1], [[], []], ' ', [[], []]) Label(self.frame_button, text="Modify Spectrum").grid(row=0, column=0, padx=5, pady=5, sticky=W) #smooth spectrum self.smoothButton = Button(self.frame_button, text="Smooth", command=self.smooth_data, state=DISABLED) self.smoothButton.grid(row=0, column=1, padx=5, pady=5, sticky=W) #offset spectrum self.offsetButton = Button(self.frame_button, text="Offset", command=self.offset_data, state=DISABLED) self.offsetButton.grid(row=0, column=2, padx=5, pady=5, sticky=W) Label(self.frame_button, text="Label Spectrum").grid(row=1, column=0, padx=5, pady=5, sticky=W) #label features in figure self.labelButton = Button(self.frame_button, text="Label Feature", command=self.label_feature, state=DISABLED) self.labelButton.grid(row=1, column=1, padx=5, pady=5, sticky=W) #remove all labels self.deletelabelButton = Button(self.frame_button, text="Remove Labels", command=self.delete_labels, state=DISABLED) self.deletelabelButton.grid(row=1, column=2, padx=5, pady=5, sticky=W) #define the font size of labels self.formatelabelButton = Button(self.frame_button, text="Format Label", command=self.format_labels) self.formatelabelButton.grid(row=1, column=3, padx=5, pady=5, sticky=W) Label(self.frame_button, text="Convert Domain").grid(row=2, column=0, padx=5, pady=5, sticky=W) #convert to wavelength domain self.wavelengthButton = Button(self.frame_button, text="Wavelength", command=self.wavelength_domain, state=DISABLED) self.wavelengthButton.grid(row=2, column=1, padx=5, pady=5, sticky=W) #convert to wavenumber domain self.wavenumberButton = Button(self.frame_button, text="Wavenumber", command=self.wavenumber_domain, state=DISABLED) self.wavenumberButton.grid(row=2, column=2, padx=5, pady=5, sticky=W) Label(self.frame_button, text="Configure Plot").grid(row=3, column=0, padx=5, pady=5, sticky=W) #define axes self.axesButton = Button(self.frame_button, text="Define Axes", command=self.define_axes, state=DISABLED) self.axesButton.grid(row=3, column=1, padx=5, pady=5, sticky=W) #define linestyle and legend self.legendButton = Button(self.frame_button, text="Legend Format", command=self.define_legend, state=DISABLED) self.legendButton.grid(row=3, column=2, padx=5, pady=5, sticky=W) #remove all spectra from plotting windows, reset variables self.resetButton = Button(self.frame_button, text="Reset", command=self.reset_plots, state=DISABLED) self.resetButton.grid(row=3, column=5, padx=5, pady=5, sticky=W) #quit PASAview self.quitButton = Button(self.frame_button, text="Quit", command=self.quit_destroy) self.quitButton.grid(row=3, column=6, padx=5, pady=5, sticky=E) #plot figure def generate_figure(self, data, label, xaxis_title, data_reference): #clear figure plt.clf() ax = self.fig.add_subplot(111) #clear axis ax.cla() spectrum = ax.plot(data[0], data[1], self.linestyle_format_legend[0], label=self.spec_name_legend) ax.set_xlabel(xaxis_title) #, fontsize=6) ax.set_ylabel('reflectance') #, fontsize=6) #for autoscale if self.define_axis_flag == 0: ax.autoscale_view() #for user-defined scaling if self.define_axis_flag == 1: ax.set_xlim(self.xlim) ax.set_ylim(self.ylim) #add reference spectra for index, wave in enumerate(data_reference[0]): ax.plot(wave, data_reference[1][index], self.linestyle_format_reference_legend[index], label=self.spec_name_reference_legend[index]) #insert legend #the position could be user-defined #might consider adding this... ax.legend(fontsize=self.legend_fontsize) #add labels for index, label_x in enumerate(label[0]): an1 = ax.annotate(self.label_value[index], xy=(label_x, label[1][index]), xytext=(label_x, label[1][index]), fontsize=self.label_size) an1.draggable(state=True) self.canvas.show() self.canvas.get_tk_widget().pack(side=BOTTOM, fill=BOTH, expand=True) self.toolbar.update() self.canvas._tkcanvas.pack(side=TOP, fill=BOTH, expand=True) #reset figure def reset_figure(self, data, label, xaxis_title): #clear figure plt.clf() ax = self.fig.add_subplot(111) #clear axis ax.cla() spectrum = ax.plot(data[0], data[1]) ax.set_xlabel(xaxis_title) #, fontsize=6) ax.set_ylabel('reflectance') #, fontsize=6) #for autoscale if self.define_axis_flag == 0: ax.autoscale_view() #for user-defined scaling if self.define_axis_flag == 1: ax.set_xlim(self.xlim) ax.set_ylim(self.ylim) for index, label_x in enumerate(label[0]): an1 = ax.annotate(str( self.label_format % round(label_x + self.label_offset, int(self.digits_after))), xy=(label_x, label[1][index]), xytext=(label_x, label[1][index]), fontsize=self.label_size) an1.draggable(state=True) self.canvas.show() self.canvas.get_tk_widget().pack(side=BOTTOM, fill=BOTH, expand=True) self.toolbar.update() self.canvas._tkcanvas.pack(side=TOP, fill=BOTH, expand=True) #open file def onOpen(self): #displays .txt files in browser window #only reads wavelength domain data files ftypes = [('txt files', '*.txt'), ('csv files', '*.csv'), ('ASCII files', '*.asc')] dlg = tkFileDialog.Open(self, filetypes=ftypes) fl = dlg.show() self.spec_name = fl.rsplit('/', 1)[1][0:-4] if fl != '': if fl[-3:] == 'txt': data = self.readFile(fl) self.refl = data[1] #self.refl = [x+0.00075 for x in data[1]] self.wavelength = data[0] elif fl[-3:] == 'csv': data = self.readFile(fl) self.refl = data[1] #self.refl = [x+0.00075 for x in data[1]] self.wavelength = data[0] elif fl[-3:] == 'asc': data = self.readFile(fl) #ignore data with refl values less than 0 index_pos = bisect.bisect(data[1], 0) self.refl = data[1][index_pos:] self.wavelength = data[0][index_pos:] if self.wavelength_wavenumber_flag == 0: self.wavelength_domain() elif self.wavelength_wavenumber_flag == 1: self.wavenumber_domain() #allows reference spectrum to be added self.openMenu.entryconfig("Open Reference Spectrum", state=NORMAL) #allows labels to be generated self.labelButton.config(state=NORMAL) self.deletelabelButton.config(state=NORMAL) #allows for smoothing/offset of data self.smoothButton.config(state=NORMAL) self.offsetButton.config(state=NORMAL) #allows for domain conversion self.wavelengthButton.config(state=NORMAL) self.wavenumberButton.config(state=NORMAL) #allows plot to be configured self.axesButton.config(state=NORMAL) self.resetButton.config(state=NORMAL) self.legendButton.config(state=NORMAL) def onOpenReference(self): #displays .txt files in browser window #only reads wavelength domain data files ftypes = [('txt files', '*.txt'), ('csv files', '*.csv'), ('ASCII files', '*.asc')] dlg = tkFileDialog.Open(self, filetypes=ftypes) fl = dlg.show() self.spec_name_reference.append(fl.rsplit('/', 1)[1][0:-4]) self.linestyle_format_reference.append('g-') if fl != '': if fl[-3:] == 'txt': data = self.readFile(fl) #self.refl_reference=data[1] self.refl_reference.append(data[1]) self.wavelength_reference.append(data[0]) elif fl[-3:] == 'csv': data = self.readFile(fl) #self.refl_reference=data[1] self.refl_reference.append(data[1]) self.wavelength_reference.append(data[0]) elif fl[-3:] == 'asc': data = self.readFile(fl) #ignore leading negative values index_pos = bisect.bisect(data[1], 0) self.refl_reference.append(data[1][index_pos:]) self.wavelength_reference.append(data[0][index_pos:]) self.display_spectrum_reference.append(IntVar()) self.display_spectrum_reference[len(self.spec_name_reference) - 1].set(1) if self.wavelength_wavenumber_flag == 0: self.wavelength_domain() elif self.wavelength_wavenumber_flag == 1: self.wavenumber_domain() #function for file input def readFile(self, filename): file = open(filename, 'r') if filename[-3:] == 'asc': #there is some extra formatting on the first several lines header = file.readline() header = file.readline() header = file.readline() header = file.readline() header = file.readline() header = file.readline() header = file.readline() header = file.readline() header = file.readline() header = file.readline() header = file.readline() header = file.readline() header = file.readline() header = file.readline() header = file.readline() header = file.readline() #read each row in the file #list for temporary wavelength data wavelength = [] #list for temporary reflectance data refl = [] for line in file: #check file format: if ',' in line: #read each row - store data in columns wavelength = line.split(',') wavelength.append(float(temp[0])) refl.append(float(temp[1].rstrip('/n'))) elif '\t' in line: temp = line.split('\t') wavelength.append(float(temp[0])) refl.append(float(temp[1].rstrip('/n'))) elif ' ' in line: temp = line.split(' ') wavelength.append(float(temp[1])) refl.append(float(temp[2])) elif ' ' in line: temp = line.split(' ') wavelength.append(float(temp[0])) refl.append(float(temp[1].rstrip('/n'))) elif ' ' in line: temp = line.split(' ') wavelength.append(float(temp[0])) refl.append(float(temp[1].rstrip('/n'))) data = [wavelength, refl] return data #export data to txt file def onExport(self): #save header info (date/time, ask user for instrument)? #ask for save file name savefile = tkFileDialog.asksaveasfile(mode='wb', defaultextension=".txt") # asksaveasfile return `None` if dialog closed with "cancel". if savefile is None: return #export wavelength or wavenumber domain data depending on flag (what is plotted) if self.wavelength_wavenumber_flag == 0: x = self.wavelength else: x = self.wavenumber y = self.refl for i in range(len(x)): savefile.write(str(x[i])) savefile.write(' ') savefile.write(str(y[i])) savefile.write('\r\n') savefile.close() #convert to wavelength domain #disabled until spectrum is plotted def wavelength_domain(self): self.wavelength_wavenumber_flag = 0 #self.generate_figure([self.wavelength,self.refl], [self.label_wavelength, self.label_refl], 'wavelength ($\mu$m)', [self.wavelength_reference, self.refl_reference]) #only display checked spectra if self.display_spectrum.get() == 1: wavelength_temp = self.wavelength refl_temp = self.refl self.linestyle_format_legend = self.linestyle_format self.spec_name_legend = self.spec_name elif self.display_spectrum.get() == 0: wavelength_temp = [] refl_temp = [] self.linestyle_format_legend = [''] self.spec_name_legend = '' wavelength_ref_temp = [] refl_ref_temp = [] self.spec_name_reference_legend = [] self.linestyle_format_reference_legend = [] for index, i in enumerate(self.wavelength_reference): if self.display_spectrum_reference[index].get() == 1: wavelength_ref_temp.append(self.wavelength_reference[index]) refl_ref_temp.append(self.refl_reference[index]) self.spec_name_reference_legend.append( self.spec_name_reference[index]) self.linestyle_format_reference_legend.append( self.linestyle_format_reference[index]) self.generate_figure([wavelength_temp, refl_temp], [self.label_wavelength, self.label_refl], 'wavelength ($\mu$m)', [wavelength_ref_temp, refl_ref_temp]) #convert to wavenumber domain #disabled until spectrum is plotted def wavenumber_domain(self): self.wavelength_wavenumber_flag = 1 self.label_wavenumber = [10000 / x for x in self.label_wavelength] #convert wavelength reference spectra to wavenumber self.wavenumber_reference = [] for i in self.wavelength_reference: temp = [10000 / x for x in i] self.wavenumber_reference.append(temp) self.wavenumber = [10000 / x for x in self.wavelength] #self.generate_figure([self.wavenumber,self.refl], [self.label_wavenumber, self.label_refl], 'wavenumber (cm$^{-1}$)', [self.wavenumber_reference ,self.refl_reference]) if self.display_spectrum.get() == 1: wavenumber_temp = self.wavenumber refl_temp = self.refl self.linestyle_format_legend = self.linestyle_format self.spec_name_legend = self.spec_name elif self.display_spectrum.get() == 0: wavenumber_temp = [] refl_temp = [] self.linestyle_format_legend = [''] self.spec_name_legend = '' wavenumber_ref_temp = [] refl_ref_temp = [] self.spec_name_reference_legend = [] self.linestyle_format_reference_legend = [] for index, i in enumerate(self.wavelength_reference): if self.display_spectrum_reference[index].get() == 1: wavenumber_ref_temp.append(self.wavenumber_reference[index]) refl_ref_temp.append(self.refl_reference[index]) self.spec_name_reference_legend.append( self.spec_name_reference[index]) self.linestyle_format_reference_legend.append( self.linestyle_format_reference[index]) self.generate_figure([wavenumber_temp, refl_temp], [self.label_wavenumber, self.label_refl], 'wavenumber (cm$^{-1}$)', [wavenumber_ref_temp, refl_ref_temp]) #smooth data #disabled until spectrum is plotted def smooth_data(self): #purpose of smoothing function: to display smoothed plot self.SmoothDialog(self.parent) #wait for dialog to close self.top.wait_window(self.top) if self.window != 0: smoothed_signal = scipy.signal.savgol_filter( self.refl, self.window, self.poly) #convert from array to list self.refl = smoothed_signal.tolist() if self.spec_name_reference != []: for index, name in enumerate(self.spec_name_reference): if self.window_reference[index] != 0: smoothed_signal = scipy.signal.savgol_filter( self.refl_reference[index], self.window_reference[index], self.poly_reference[index]) self.refl_reference[index] = smoothed_signal.tolist() if self.wavelength_wavenumber_flag == 1: self.wavenumber_domain() else: self.wavelength_domain() #called when Smooth button is pressed #asks user for smooth input data def SmoothDialog(self, parent): top = self.top = Toplevel(self.parent) Label(top, text="PASA Spectrum:").grid(row=0, column=0, sticky=W, padx=5, pady=5) #window length for smoothing function #must be greater than poly value, positive, odd (51) Label(top, text=self.spec_name).grid(row=1, column=0, sticky=W, padx=5, pady=5) Label(top, text="Window Length:").grid(row=1, column=1, sticky=W, padx=5, pady=5) self.w = Entry(top) self.w.insert(0, '51') self.w.grid(row=1, column=2, padx=5, pady=5) #polynomial order #must be less than window length (3) Label(top, text="Polynomial Order:").grid(row=1, column=3, sticky=W, padx=5, pady=5) self.p = Entry(top) self.p.insert(0, '3') self.p.grid(row=1, column=4, padx=5, pady=5) index = -2 if self.spec_name_reference != []: Label(top, text="Reference Spectrum:").grid(row=2, column=0, sticky=W, padx=5, pady=5) for index, name in enumerate(self.spec_name_reference): if index == 0: self.rw = [' '] * len(self.spec_name_reference) self.rp = [' '] * len(self.spec_name_reference) Label(top, text=name).grid(row=3 + index, column=0, sticky=W, padx=5, pady=5) Label(top, text="Window Length:").grid(row=3 + index, column=1, sticky=W, padx=5, pady=5) self.rw[index] = Entry(top) self.rw[index].insert(0, '0') self.rw[index].grid(row=3 + index, column=2, padx=5, pady=5) Label(top, text="Polynomial Order:").grid(row=3 + index, column=3, sticky=W, padx=5, pady=5) self.rp[index] = Entry(top) self.rp[index].insert(0, '0') self.rp[index].grid(row=3 + index, column=4, padx=5, pady=5) #calls DialogSmoothOK to set these values b = Button(top, text="OK", command=self.DialogSmoothOK) b.grid(row=4 + index, column=4, padx=5, pady=5) #called from smooth dialog box within smooth routine def DialogSmoothOK(self): #sets user-defined window length self.window = float(self.w.get()) #sets user-defined polynomail order self.poly = float(self.p.get()) if self.spec_name_reference != []: self.window_reference = [] self.poly_reference = [] for index, wave in enumerate(self.wavelength_reference): #self.spec_name_reference.append(str(self.rn.get())) self.window_reference.append(float(self.rw[index].get())) self.poly_reference.append(float(self.rp[index].get())) self.top.destroy() #offset data #disabled until spectrum is plotted def offset_data(self): self.OffsetDialog(self.parent) #wait for dialog to close self.top.wait_window(self.top) #scale/offset spectrum self.refl = [(x * self.scale) + self.offset for x in self.refl] if self.spec_name_reference != []: temp_refl = [] for index, name in enumerate(self.spec_name_reference): temp_refl.append([(x * self.scale_reference[index]) + self.offset_reference[index] for x in self.refl_reference[index]]) self.refl_reference = temp_refl if self.wavelength_wavenumber_flag == 1: self.wavenumber_domain() else: self.wavelength_domain() #called when Offset button is pressed #asks user for smooth input data def OffsetDialog(self, parent): top = self.top = Toplevel(self.parent) Label(top, text="PASA Spectrum:").grid(row=1, column=0, sticky=W, padx=5, pady=5) Label(top, text=self.spec_name).grid(row=2, column=0, sticky=W, padx=5, pady=5) Label(top, text="Offset:").grid(row=2, column=1, sticky=W, padx=5, pady=5) self.po = Entry(top) self.po.insert(0, '0') self.po.grid(row=2, column=2, padx=5, pady=5) Label(top, text="Scale:").grid(row=2, column=3, sticky=W, padx=5, pady=5) self.ps = Entry(top) self.ps.insert(0, '1.0') self.ps.grid(row=2, column=4, padx=5, pady=5) index = -2 if self.spec_name_reference != []: Label(top, text="Reference Spectrum:").grid(row=3, column=0, sticky=W, padx=5, pady=5) for index, name in enumerate(self.spec_name_reference): if index == 0: self.ro = [' '] * len(self.spec_name_reference) self.rs = [' '] * len(self.spec_name_reference) Label(top, text=name).grid(row=4 + index, column=0, sticky=W, padx=5, pady=5) Label(top, text="Offset:").grid(row=4 + index, column=1, sticky=W, padx=5, pady=5) self.ro[index] = Entry(top) self.ro[index].insert(0, '0') self.ro[index].grid(row=4 + index, column=2, padx=5, pady=5) Label(top, text="Scale:").grid(row=4 + index, column=3, sticky=W, padx=5, pady=5) self.rs[index] = Entry(top) self.rs[index].insert(0, '1.0') self.rs[index].grid(row=4 + index, column=4, padx=5, pady=5) #calls DialogOffsetOK to set these values b = Button(top, text="OK", command=self.DialogOffsetOK) b.grid(row=5 + index, column=4, padx=5, pady=5) #called from offset dialog box within smooth routine def DialogOffsetOK(self): self.offset = float(self.po.get()) self.scale = float(self.ps.get()) if self.spec_name_reference != []: self.offset_reference = [] self.scale_reference = [] for index, wave in enumerate(self.wavelength_reference): #self.spec_name_reference.append(str(self.rn.get())) self.offset_reference.append(float(self.ro[index].get())) self.scale_reference.append(float(self.rs[index].get())) self.top.destroy() #format labels def format_labels(self): self.FormatLabelDialog(self.parent) #wait for dialog to close self.top.wait_window(self.top) self.label_size = float(self.label_size) #self.label_format = "%."+self.digits_after+"f" if self.wavelength_wavenumber_flag == 0: self.wavelength_domain() elif self.wavelength_wavenumber_flag == 1: self.wavelength_domain() def FormatLabelDialog(self, parent): top = self.top = Toplevel(self.parent) #font size Label(top, text="Font Size:").grid(row=0, column=0, sticky=W, padx=5, pady=5) self.fs = Entry(top) self.fs.insert(0, '10') self.fs.grid(row=0, column=1, padx=5, pady=5) #calls DialogLabelOK to set these values b = Button(top, text="OK", command=self.DialogLabelOK) b.grid(row=3, column=1, padx=5, pady=5) #called from label dialog box within label format routine def DialogLabelOK(self): #sets user-defined font size self.label_size = float(self.fs.get()) self.top.destroy() #function to label features on figure in order to save figure as an image file with labels #disabled until spectrum is plotted def label_feature(self): #ask for peak (on_click_label) self.cid = self.canvas.mpl_connect('button_press_event', self.on_click_label) #called when click is made to label peak def on_click_label(self, event): temp_len = len(self.label_refl) #if wavelength flag is set if self.wavelength_wavenumber_flag == 0: #temp_len=len(self.label_wavenumber) #when the click is made within the plotting window if event.inaxes is not None: self.label_wavelength.append(event.xdata) self.label_refl.append(event.ydata) #self.LabelDialog(self.parent) #self.wavelength_domain() #if click is outside plotting window else: print 'Clicked ouside axes bounds but inside plot window' #if the wavenumber flag is set elif self.wavelength_wavenumber_flag == 1: #temp_len=len(self.label_wavelength) #when the click is made within the plotting window if event.inaxes is not None: self.label_wavenumber.append(event.xdata) self.label_refl.append(event.ydata) #self.LabelDialog(self.parent) #self.wavenumber_domain() #if click is outside plotting window else: print 'Clicked ouside axes bounds but inside plot window' #self.peak_intensity=event.ydata #plot label #disconnect from click event when a new peak label value is set if temp_len < len(self.label_refl): self.LabelDialog(self.parent) self.canvas.mpl_disconnect(self.cid) #asks user for featue label associated with selection def LabelDialog(self, parent): top = self.top = Toplevel(self.parent) Label(top, text="Spectral Feature:").pack() self.v = Entry(top) self.v.insert(0, 'H$_2$O') self.v.pack(padx=5) #calls DialogOK to set mass value to input b = Button(top, text="OK", command=self.DialogOK) b.pack(pady=5) #called from label dialog box within feature label routine def DialogOK(self): #sets user-defined mass calibration value to mass cal list self.label_value.append(self.v.get()) #replots with labels if self.wavelength_wavenumber_flag == 0: self.wavelength_domain() elif self.wavelength_wavenumber_flag == 1: self.wavelength_domain() #closes dialog box self.top.destroy() #remove labels when button is selected #disabled until spectrum is plotted def delete_labels(self): #deletes peak label arrays self.label_wavenumber = [] self.label_wavelength = [] self.label_refl = [] #if wavelength/wavenumber flag is set if self.wavelength_wavenumber_flag == 0: self.wavelength_domain() elif self.wavelength_wavenumber_flag == 1: self.wavenumber_domain() #called when define axes button is pressed #asks user to define axes range #disabled until spectrum is plotted def define_axes(self): self.DefineAxes(self.parent) #wait for dialog to close self.top.wait_window(self.top) self.define_axis_flag = 1 if self.wavelength_wavenumber_flag == 1: self.wavenumber_domain() else: self.wavelength_domain() def DefineAxes(self, parent): top = self.top = Toplevel(self.parent) #x axis range Label(top, text="X Axis:").grid(row=0, column=0, sticky=W, padx=5, pady=5) self.x1 = Entry(top) if self.wavelength_wavenumber_flag == 0: self.x1.insert(0, 1.6) elif self.wavelength_wavenumber_flag == 1: self.x1.insert(0, 2780) self.x1.grid(row=0, column=1, padx=5, pady=5) self.x2 = Entry(top) if self.wavelength_wavenumber_flag == 0: self.x2.insert(0, 3.6) elif self.wavelength_wavenumber_flag == 1: self.x2.insert(0, 6250) self.x2.grid(row=0, column=2, padx=5, pady=5) #y axis range Label(top, text="Y Axis:").grid(row=1, column=0, sticky=W, padx=5, pady=5) self.y1 = Entry(top) self.y1.insert(0, self.ylim[0]) self.y1.grid(row=1, column=1, padx=5, pady=5) self.y2 = Entry(top) self.y2.insert(0, self.ylim[1]) self.y2.grid(row=1, column=2, padx=5, pady=5) #calls DialogSmoothOK to set these values b = Button(top, text="OK", command=self.DialogAxesOK) b.grid(row=2, column=1, padx=5, pady=5) #called from axes define dialog box def DialogAxesOK(self): #sets user-defined x axis values x_temp1 = float(self.x1.get()) x_temp2 = float(self.x2.get()) self.xlim = [x_temp1, x_temp2] #sets user-defined y axis values y_temp1 = float(self.y1.get()) y_temp2 = float(self.y2.get()) self.ylim = [y_temp1, y_temp2] self.top.destroy() #called when legend button is pressed #asks user to define linestyle, legend name, legend font size #disabled until spectrum is plotted def define_legend(self): self.DefineLegend(self.parent) #wait for dialog to close self.top.wait_window(self.top) if self.wavelength_wavenumber_flag == 1: self.wavenumber_domain() else: self.wavelength_domain() def DefineLegend(self, parent): top = self.top = Toplevel(self.parent) #PASA spectrum Label(top, text="PASA Spectrum:").grid(row=1, column=0, sticky=W, padx=5, pady=5) Label(top, text="Name:").grid(row=2, column=0, sticky=W, padx=5, pady=5) self.pn = Entry(top) self.pn.insert(0, self.spec_name) self.pn.grid(row=2, column=1, padx=5, pady=5) Label(top, text="Linestyle:").grid(row=2, column=2, sticky=W, padx=5, pady=5) self.pl = Entry(top) self.pl.insert(0, self.linestyle_format[0]) self.pl.grid(row=2, column=3, padx=5, pady=5) Checkbutton(top, variable=self.display_spectrum, text="Display", onvalue=1, offvalue=0).grid(row=2, column=4, sticky=W, padx=5, pady=5) index = -2 if self.spec_name_reference != []: Label(top, text="Reference Spectrum:").grid(row=3, column=0, sticky=W, padx=5, pady=5) for index, name in enumerate(self.spec_name_reference): if index == 0: self.rn = [' '] * len(self.spec_name_reference) self.rl = [' '] * len(self.spec_name_reference) Label(top, text="Name:").grid(row=4 + index, column=0, sticky=W, padx=5, pady=5) self.rn[index] = Entry(top) self.rn[index].insert(0, name) self.rn[index].grid(row=4 + index, column=1, padx=5, pady=5) Label(top, text="Linestyle:").grid(row=4 + index, column=2, sticky=W, padx=5, pady=5) self.rl[index] = Entry(top) self.rl[index].insert(0, self.linestyle_format_reference[index]) self.rl[index].grid(row=4 + index, column=3, padx=5, pady=5) Checkbutton(top, variable=self.display_spectrum_reference[index], text="Display", onvalue=1, offvalue=0).grid(row=4 + index, column=4, sticky=W, padx=5, pady=5) #asks for legend font size Label(top, text="Legend:").grid(row=5 + index, column=0, sticky=W, padx=5, pady=5) Label(top, text="Font Size:").grid(row=6 + index, column=0, sticky=W, padx=5, pady=5) self.lf = Entry(top) self.lf.insert(0, self.legend_fontsize) self.lf.grid(row=6 + index, column=1, padx=5, pady=5) #calls DialogLegendOK to set these values b = Button(top, text="OK", command=self.DialogLegendOK) b.grid(row=7 + index, column=4, padx=5, pady=5) #called from linestyle define dialog box def DialogLegendOK(self): #sets user-defined x axis values self.spec_name = str(self.pn.get()) self.linestyle_format = [str(self.pl.get())] if self.spec_name_reference != []: self.spec_name_reference = [] self.linestyle_format_reference = [] for index, wave in enumerate(self.wavelength_reference): #self.spec_name_reference.append(str(self.rn.get())) self.spec_name_reference.append(str(self.rn[index].get())) self.linestyle_format_reference.append( str(self.rl[index].get())) self.legend_fontsize = (self.lf.get()) self.top.destroy() #reset plotting window #disabled until spectrum is plotted def reset_plots(self): #generate figure (no data at first) #resets all necessary variables self.wavelength_reference = [] self.wavenumber_reference = [] self.refl_reference = [] self.wavelength_wavenumber_flag = 0 self.label_wavelength = [] self.label_wavenumber = [] self.label_refl = [] self.label_value = [] self.define_axis_flag = 0 self.xlim = [1.6, 3.6] self.ylim = [0, 1] self.linestyle_format = ['k-'] self.reset_figure([0, 1], [[], []], ' ') self.spec_name_reference = [] self.spec_name = [] self.rn = [] self.rl = [] self.ro = [] self.rs = [] self.rw = [] self.rp = [] #prohibits reference spectrum to be added self.openMenu.entryconfig("Open Reference Spectrum", state=DISABLED) #prohibits labels to be generated self.labelButton.config(state=DISABLED) self.deletelabelButton.config(state=DISABLED) #prohibits for modification of data self.smoothButton.config(state=DISABLED) self.offsetButton.config(state=DISABLED) #prohibits for domain conversion self.wavelengthButton.config(state=DISABLED) self.wavenumberButton.config(state=DISABLED) #prohibits plot to be configured self.axesButton.config(state=DISABLED) self.legendButton.config(state=DISABLED) self.resetButton.config(state=DISABLED) #need to destroy parent before quitting, otherwise python crashes on Windows def quit_destroy(self): self.parent.destroy() self.parent.quit()
class Unsplit_Proliferation_Gating_GUI(tk.Frame): def __init__(self,master): self.root = master.root tk.Frame.__init__(self, master) experimentNameWindow = tk.Frame(self) experimentNameWindow.pack(side=tk.TOP,padx=10,pady=10) experimentNameLabel = tk.Label(experimentNameWindow,text=folderName+':').pack() plotFrame = tk.Frame(self) plotFrame.pack() row = pickle.load(open('inputFiles/rowValIterationRange.pkl','rb')) groupedGateList = pickle.load(open('inputFiles/groupedGateList.pkl','rb')) if groupVariable == 'Condition-Time': currentDf = logicleDataStacked.to_frame('GFI') rawDf = rawDataStacked.to_frame('GFI') else: currentLevelValues = logicleDataUnstacked.iloc[row,:].name currentDf = logicleDataStacked.xs(currentLevelValues,level=levelNames).to_frame('GFI') rawDf = rawDataStacked.xs(currentLevelValues,level=levelNames).to_frame('GFI') plottingDf = currentDf.reset_index() if groupVariable == 'Condition-Time' or groupVariable == 'Condition': nonEventLevelNames = currentDf.index.names[:-1] groupingColumn = [] for conditionGroupbyTuple in currentDf.groupby(level=nonEventLevelNames,sort=False): conditionGroupbyTupleNew = [str(i) for i in conditionGroupbyTuple[0]] conditionName = '-'.join(conditionGroupbyTupleNew) for event in range(conditionGroupbyTuple[1].shape[0]): groupingColumn.append(conditionName) plottingDf[groupVariable] = groupingColumn #Plot facetgrid of histograms of ctv values of all times for current condition g = sns.FacetGrid(plottingDf,legend_out=True,hue=groupVariable,height=6,palette=sns.color_palette("Purples", len(pd.unique(plottingDf[groupVariable])))) #g.map(sns.distplot,'GFI',kde=False,bins=256) g.map(sns.kdeplot,'GFI',bw=15) plt.subplots_adjust(top=0.95) xtickValues,xtickLabels = returnTicks([-1000,100,1000,10000,100000]) axis = g.fig.get_axes()[0] axis.set_xticks(xtickValues) axis.set_xticklabels(xtickLabels) startingGateLogicle = 725 startingGateRaw = rawDf.values[find_nearest(currentDf,startingGateLogicle)[1]][0] logicleGates = returnGates(currentDf,rawDf,startingGateRaw,numGens) maxY = list(plt.gca().get_yticks())[-1] X = startingGateLogicle Ymin = -1 Ymax = maxY gates = logicleGates[1:] numGates = len(logicleGates)-1 self.canvas = FigureCanvasTkAgg(g.fig,master=plotFrame) self.ax = plt.gca() self.canvas.draw() self.canvas.get_tk_widget().pack() self.X = X self.gates= gates self.gateVals = gates gateLengths = [y-x for x, y in zip(gates[:-1], gates[1:])][1:] self.gateLengths = gateLengths self.numGates = numGates x = [X, X] y = [Ymin, Ymax] self.line = linesmpl.Line2D(x, y, picker=5,linestyle=':',color='r') self.ax.add_line(self.line) self.childlines = [] for gate in range(self.numGates): newX = self.gates[gate] childline = linesmpl.Line2D([newX,newX], y,linestyle=':',color='k',picker=5) self.childlines.append(childline) for childline in self.childlines: self.ax.add_line(childline) self.canvas.draw() self.sid = self.canvas.mpl_connect('pick_event', self.clickOnParentLine) childsids = [] for childline,i in zip(self.childlines,range(self.numGates)): childsids.append(self.canvas.mpl_connect('pick_event', self.clickOnChildLines)) self.childsids = childsids #Add title if groupVariable == 'Condition-Time': plt.title(titleVariable) else: if not isinstance(currentLevelValues, (tuple,)): currentLevelValues = [str(currentLevelValues)] plt.title('-'.join(currentLevelValues)+' ('+titleVariable+' '+str(row+1)+'/'+str(logicleDataUnstacked.shape[0])+')') def collectInputs(): for names in range(len(pd.unique(plottingDf[groupVariable]))): groupedGateList.append(self.getAllX()) with open('inputFiles/groupedGateList.pkl','wb') as f: pickle.dump(groupedGateList,f) row = pickle.load(open('inputFiles/rowValIterationRange.pkl','rb')) row+=1 with open('inputFiles/rowValIterationRange.pkl','wb') as f: pickle.dump(row,f) if row != pickle.load(open('inputFiles/iterationRange.pkl','rb')): master.switch_frame(Unsplit_Proliferation_Gating_GUI) else: i = 0 row = 0 singleCellProliferationDf = pd.DataFrame(np.zeros(logicleDataStacked.shape),logicleDataStacked.index,columns=['Generation']) for outerVariable in outerVariableValues: for innerVariable in innerVariableValues: sampleGenerationGates = groupedGateList[i] #sampleGenerationGates = groupedGate.getAllX() if groupVariable == 'Condition': indexingTuple = tuple(list(innerVariable)+[outerVariable,slice(None)]) else: indexingTuple = tuple(list(outerVariable)+[innerVariable,slice(None)]) ctvValues = logicleDataStacked.loc[indexingTuple] generationValues = np.zeros(ctvValues.shape) for sampleEvent,row in zip(ctvValues,range(ctvValues.shape[0])): for generation in range(len(sampleGenerationGates)-1): upperGate = sampleGenerationGates[generation] lowerGate = sampleGenerationGates[generation+1] if(sampleEvent > lowerGate and sampleEvent <= upperGate): generationValues[row] = generation singleCellProliferationDf.loc[indexingTuple,:] = generationValues print(singleCellProliferationDf) with open('semiProcessedData/singleCellDataFrame-proliferation-'+folderName+'.pkl', 'wb') as f: pickle.dump(singleCellProliferationDf,f) master.switch_frame(secondaryhomepage,folderName,expNum,ex_data) buttonWindow = tk.Frame(self) buttonWindow.pack(side=tk.TOP,pady=10) tk.Button(buttonWindow, text="OK",command=lambda: collectInputs()).grid(row=5,column=0) tk.Button(buttonWindow, text="Quit",command=quit).grid(row=5,column=2) def clickOnParentLine(self, event): if event.artist == self.line: print("line selected ", event.artist) self.follower = self.canvas.mpl_connect("motion_notify_event", self.followParentMouse) self.releaser = self.canvas.mpl_connect("button_press_event", self.releaseParentOnClick) def clickOnChildLines(self, event): if event.artist in self.childlines: print("line selected ", event.artist) self.currentArtist = event.artist self.follower = self.canvas.mpl_connect("motion_notify_event", self.followChildMouse) self.releaser = self.canvas.mpl_connect("button_press_event", self.releaseChildOnClick) def followChildMouse(self, event): for childline,gate in zip(self.childlines,range(self.numGates)): if self.childlines[gate] == self.currentArtist: self.childlines[gate].set_xdata([event.xdata, event.xdata]) self.canvas.draw() #self.c.draw_idle() def releaseChildOnClick(self, event): newGates = [] for childline in self.childlines: newGates.append(childline.get_xdata()[0]) self.gates = newGates self.canvas.mpl_disconnect(self.releaser) self.canvas.mpl_disconnect(self.follower) def followParentMouse(self, event): self.line.set_xdata([event.xdata, event.xdata]) newGates = [] for childline,gate in zip(self.childlines,range(self.numGates)): newX = (event.xdata-self.X)+self.gates[gate] childline.set_xdata([newX,newX]) newGates.append(newX) self.gateVals = newGates self.canvas.draw() def releaseParentOnClick(self, event): self.X = self.line.get_xdata()[0] newGates = [] for childline in self.childlines: newGates.append(childline.get_xdata()[0]) self.gates = newGates self.canvas.mpl_disconnect(self.releaser) self.canvas.mpl_disconnect(self.follower) def getParentX(self): return self.X def getChildX(self): childGates = [] for childline in self.childlines: childGates.append(childline.get_xdata()[0]) return childGates def getAllX(self): return [self.getParentX()]+self.getChildX()
class DrawLineLayout: def __init__(self, root_frame, figure): print('hi from DrawLineLayout') self.root_frame = root_frame self.root_frame.columnconfigure(0, weight=1) self.root_frame.rowconfigure(0, weight=1) self.fig = figure self.canvas = FigureCanvasTkAgg(self.fig, master=self.root_frame) self.canvas.draw() self.canvas.get_tk_widget().grid(column=0, row=0, sticky=('N', 'S', 'E', 'W')) self.toolbar_frame = ttk.Frame(self.root_frame) self.toolbar_frame.grid(column=1, row=0, sticky=('N', 'E', 'S', 'W')) self.toolbar_frame.columnconfigure(0, weight=1) self.toolbar_frame.rowconfigure(5, weight=1) self.toolbar_label = ttk.Label(self.toolbar_frame, text='Toolbar') self.toolbar_label.grid(column=0, row=0, sticky=('W', 'E')) self.upsample_btn = ttk.Button(self.toolbar_frame, text='Upsample') self.upsample_btn.grid(column=0, row=1, sticky=('W', 'E')) self.upsample_btn.state(['disabled']) self.done_btn = ttk.Button(self.toolbar_frame, text='Done') self.done_btn.grid(column=0, row=2, sticky=('W', 'E', 'S')) self.done_btn.state(['disabled']) self.num_points_head = ttk.Label(self.toolbar_frame, text='Number of Points:') self.num_points_head.grid(column=0, row=3, sticky=('W', 'E')) self.num_points_label = ttk.Label(self.toolbar_frame) self.num_points_label.grid(column=0, row=4, sticky=('W', 'E', 'S')) self.num_points_var = tk.StringVar() self.num_points_var.set(0) self.num_points_label['textvariable'] = self.num_points_var self.discard_btn = ttk.Button(self.toolbar_frame, text='Discard') self.discard_btn.grid(column=0, row=5, sticky=('W', 'E', 'S')) def enable_upsample_btn(self): self.upsample_btn.state(['!disabled']) def enable_done_btn(self): self.done_btn.state(['!disabled']) def disable_upsample_btn(self): self.upsample_btn.state(['disabled']) def disable_done_btn(self): self.done_btn.state(['disabled']) def set_num_points(self, num_points): self.num_points_var.set(num_points) def redraw_figure(self): self.canvas.draw() def mpl_connect(self, event_type, callback): return self.canvas.mpl_connect(event_type, callback) def mpl_disconnect(self, cid): self.canvas.mpl_disconnect(cid) def set_upsample_callback(self, callback): self.upsample_btn['command'] = callback def set_done_callback(self, callback): self.done_btn['command'] = callback def set_discard_callback(self, callback): self.discard_btn['command'] = callback def clean_up(self): print('DrawLineLayout cleaning up') self.discard_btn.grid_forget() self.num_points_label.grid_forget() self.num_points_head.grid_forget() self.done_btn.grid_forget() self.upsample_btn.grid_forget() self.toolbar_label.grid_forget() self.toolbar_frame.grid_forget() self.canvas.get_tk_widget().grid_forget()
class PlotWindow(tk.Toplevel): def __init__(self, plot_type='', master=None, parameters=dict(), data=list(), figure_number=None, application=None, *args, **kwargs): # Class parameters self.master = master self.parameters = parameters self.application = application self.data = data self.pick_data_points_flag = False self.normalization_flag = False self.normalization_single_flag = False self.normalization_value = 1 self.new_normalization_value = 1 self.base_value = 0 self.new_base_value = 0 self.right_profile = None self.bottom_profile = None self.plot_type = plot_type self.widgets = dict() # To be used by class descendants self.picker_tolerance = 5.0 self.selected_artist = None self.profiler = Profiler() self.figure_number = figure_number # Init window tk.Toplevel.__init__(self, master=self.master) # Set window title if plot_type == 'Clipboard': self.title('Clipboard') elif plot_type == 'MogonioCalibration': self.title('Mogonio Calibration Wizard') elif plot_type == 'Calibration': self.title('Calibration Fit Curve') else: if figure_number is not None: self.title('Figure %.0f - %s - %s' % (figure_number, plot_type, (self.application.filename))) self.protocol("WM_DELETE_WINDOW", self.action_close) self.fig = Figure() self.main_axes = self.fig.add_subplot(111) self.config_plot() self.add_default_widgets() # This functions loops every 10 seconds self.after(0, self.timer()) def artist_label_prefix(self): return 'Fig. ' + str(self.figure_number) def find_closest_in_array(self, X, x0): # Find x position in array X x_pos = 0 x_index = -1 last_diff = np.abs(np.amax(X)-np.amin(X)) for x in X: if abs(x-x0) < last_diff: x_index = x_pos last_diff = abs(x-x0) x_pos += 1 return x_index def onpick(self, event): xy = [ event.mouseevent.x, event.mouseevent.y ] xydata = [event.mouseevent.xdata, event.mouseevent.ydata] # Reset previous selected artist if self.selected_artist is not None: if xy == self.selected_artist['xy'] and xydata == self.selected_artist['xydata']: return # Make sure that only one artist is selected by a single mouseclick self.selected_artist['artist'].set_color(self.selected_artist['color']) self.selected_artist['artist'].set_linewidth(self.selected_artist['width']) if self.selected_artist is not None and self.selected_artist['artist'] is event.artist: self.selected_artist = None # Clear selection if clicking twice on the same object else: self.select_artist(event.artist, xy, xydata) self.log('* Selected plot object: ' + event.artist.get_label()) # Redraw changes self.fig.canvas.draw() def select_artist(self, artist, xy, xydata): # Store current selected artist self.selected_artist = { 'artist': artist, 'color': artist.get_color(), 'width': artist.get_linewidth(), 'xy': xy, 'xydata': xydata } # Change some attributes to show the user that the artist is currently selected artist.set_color('purple') artist.set_linewidth(3) def add_profiles_and_colorbar(self): # Right axes sharing Y axis divider = make_axes_locatable(self.main_axes) self.right_axes = divider.append_axes("right", size=1, pad=0.2, sharey=self.main_axes) # Colorbar axes self.colorbar_axes = divider.append_axes("right", size="5%", pad=0.2) # Bottom axes sharing X axis self.bottom_axes = divider.append_axes("bottom", size=1, pad=0.2, sharex=self.main_axes) def add_default_widgets(self): ########################## # Top frame ########################## # New container for buttons self.widgets['frame_widgets'] = ttk.Frame(self) # Normalization button self.widgets['btn_normalization'] = ttk.Button(self.widgets['frame_widgets'], text='Normalize y-axis') self.widgets['btn_normalization']["command"] = self.action_btn_normalization # Zoom all button self.widgets['btn_zoomall'] = ttk.Button(self.widgets['frame_widgets'], text='Zoom all') self.widgets['btn_zoomall']["command"] = self.action_btn_zoomall self.widgets['btn_zoomall'].pack(side=tk.LEFT, padx=10, pady=5) # Export button self.widgets['btn_export'] = ttk.Button(self.widgets['frame_widgets'], text='Export data') self.widgets['btn_export']["command"] = self.action_btn_export self.widgets['btn_export'].pack(side=tk.LEFT, padx=10, pady=5) # Quick normalization button self.widgets['btn_quick_normalization'] = ttk.Button(self.widgets['frame_widgets'], text='Auto normalization') self.widgets['btn_quick_normalization']["command"] = self.action_btn_quick_normalization self.widgets['btn_quick_normalization'].pack(side=tk.LEFT, padx=10, pady=5) # Sum plots checkbox #self.widgets['cb_auto_refresh'] = Checkbox(self.widgets['frame_widgets'], text='Auto refresh') #self.widgets['cb_auto_refresh'].pack(side=tk.LEFT, padx=10, pady=10) # Pack buttons frame self.widgets['frame_widgets'].grid(row=0, column=0) ########################## # Selection artist widgets ########################## # New container for buttons self.widgets['frame_artist_widgets'] = ttk.Frame(self) # Pack buttons frame self.widgets['frame_artist_widgets'].grid(row=1, column=0) # Copy to desktop plot self.widgets['btn_copy'] = ttk.Button(self.widgets['frame_artist_widgets'], text='Copy to clipboard') self.widgets['btn_copy']["command"] = self.action_btn_copy self.widgets['btn_copy'].pack(side=tk.LEFT, padx=10, pady=5) # Zoom line button self.widgets['btn_zoomsingle'] = ttk.Button(self.widgets['frame_artist_widgets'], text='Zoom curve') self.widgets['btn_zoomsingle']["command"] = self.action_btn_zoomsingle self.widgets['btn_zoomsingle'].pack(side=tk.LEFT, padx=10, pady=5) ########################## # Bottom frame ########################## self.widgets['bottom_frame'] = ttk.Frame(self) #self.widgets['btn_test'] = ttk.Button(self.widgets['bottom_frame'], text='Teste') #self.widgets['btn_test'].grid(row=0, column=0, sticky='nsew') self.widgets['log_listbox'] = ScrollableListbox(self.widgets['bottom_frame'], height=4) self.widgets['log_listbox'].grid(row=0, column=0, sticky='nsew') self.widgets['bottom_frame'].grid(row=3, column=0, sticky="nsew", pady=10, padx=10) # Elastic columns tk.Grid.columnconfigure(self.widgets['bottom_frame'], 0, weight=1) tk.Grid.columnconfigure(self, 0, weight=1) tk.Grid.rowconfigure(self, 2, weight=1) tk.Grid.rowconfigure(self, 0, weight=0) def log(self, text): self.widgets['log_listbox'].append(text) self.widgets['log_listbox'].see(tk.END) def default_config(self): self.main_axes.get_xaxis().get_major_formatter().set_useOffset(False) self.main_axes.get_yaxis().get_major_formatter().set_useOffset(False) self.fig.set_tight_layout(True) def action_btn_zoomsingle(self, *args, **kwargs): if self.selected_artist is not None: line = self.selected_artist['artist'] min_x = min(line.get_xdata()) max_x = max(line.get_xdata()) min_y = min(line.get_ydata()) max_y = max(line.get_ydata()) self.main_axes.set_xlim([min_x, max_x]) self.main_axes.set_ylim([min_y, max_y]) # Redraw changes self.fig.canvas.draw() def action_btn_export(self, *args, **kwargs): file_path = fd.asksaveasfilename() if file_path: line_num = 0 for line in self.main_axes.get_lines(): line_num += 1 path = file_path + '_' + self.plot_type + '_' + str(line_num) + '.txt' np.savetxt(path, np.column_stack([line.get_xdata(), line.get_ydata()])) def action_btn_quick_normalization(self, *args, **kwargs): for line in self.main_axes.get_lines(): y = line.get_ydata() y = y-np.amin(y) y = np.divide(y, np.amax(y)) line.set_ydata(y) self.action_btn_zoomall() def disable_picker(self, *args, **kwargs): for line in self.main_axes.get_lines(): line.set_picker(None) def action_btn_copy(self, *args, **kwargs): if self.application is not None and self.selected_artist is not None: if self.application.clipboard_plot is None: self.application.clipboard_plot = ClipboardPlot(master = self.application.master, application = self.application) self.application.clipboard_plot.main_axes.plot(self.selected_artist['artist'].get_xdata(), self.selected_artist['artist'].get_ydata(), picker=self.picker_tolerance, label=self.selected_artist['artist'].get_label()) self.application.clipboard_plot.fig.canvas.draw() def action_cb_transferred_click(self, *args, **kwargs): self.refresh_plot() # This functions loops every 10 seconds def timer(self): #auto_refresh = self.widgets['cb_auto_refresh'].var.get() auto_refresh = False if auto_refresh: self.application.update_current_selected_data() self.data = self.application.current_selected_data self.parameters = self.application.current_parameters self.refresh_plot() self.after(10000, self.timer) def show(self): # Show plot self.widgets['canvas_frame'] = ttk.Frame(self) self.canvas = FigureCanvasTkAgg(self.fig, master=self.widgets['canvas_frame']) self.canvas.show() self.canvas.get_tk_widget().pack() self.toolbar = NavigationToolbar2TkAgg(self.canvas, self.widgets['canvas_frame']) self.toolbar.update() self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True) self.canvas.mpl_connect('pick_event', self.onpick) self.widgets['canvas_frame'].grid(row=2, column=0, sticky="nsew", pady=10, padx=10) def config_plot(self): self.default_config() # Try to execute custom config from children, if exists try: self.config_plot_custom() except AttributeError: # If method does not exist pass def plot_redraw(self): self.config_plot() self.fig.canvas.draw() def columns_names_parse_as_int(self, columns_names): return_list = list() for name in columns_names: return_list.append(int(re.findall('([0-9]+)', str(name))[0])) return return_list def columns_names_parse_as_float(self, columns_names): return_list = list() for name in columns_names: return_list.append(float(re.findall('([0-9]+)', str(name))[0])) return np.array(return_list) def action_close(self): # Custom code when closing plot window try: self.action_close_custom() except AttributeError: # If method does not exist pass plt.close(self.fig) self.destroy() def roi_axis(self): ' Used on plots which have ROI as x axis ' p = self.parameters rois_numbers = self.columns_names_parse_as_float(p['intensity_names']) if p['use_calibration'] and p['calibration_data']: try: self.bottom_axes.set_xlabel('Energy (keV)') self.main_axes.set_xlabel('') except: self.main_axes.set_xlabel('Energy (keV)') return self.rois_to_energies() else: try: self.bottom_axes.set_xlabel('ROI') self.main_axes.set_xlabel('') except: self.main_axes.set_xlabel('ROI') return rois_numbers def mogonio_to_energy(self): p = self.parameters energies_values = np.array(self.data[:, p['energy_column']], dtype=float) if p['use_mogonio_calibration']: A = p['mogonio_calibration_a'] B = p['mogonio_calibration_b'] hc = 1239.8 # nm.eV a = 0.543102 # lattice parameter for Si, in nm miller_indices = [1, 1, 1] m = miller_indices return (hc * np.sqrt(m[0]**2 + m[1]**2 + m[2]**2)/(2*a*np.sin(np.radians(A + B*energies_values))))/1000.0 else: return energies_values def rois_to_energies(self, fresh=False): ' Used on plots which have ROI as x axis ' p = self.parameters rois_numbers = self.columns_names_parse_as_int(p['intensity_names']) # Fitting if not fresh: calib = Tools.list_to_numpy(p['calibration_data']) else: calib = Tools.list_to_numpy(self.application.widgets['calib_tree'].get_data()) equation_parameters = np.polyfit(calib[:, 1], calib[:, 0], min(2, calib.shape[0]-1)) self.log('* Energy calibration coefficients: ' + str(equation_parameters)) # Apply fitting equation to ROIs numbers energies = np.polyval(equation_parameters, rois_numbers) return energies def action_btn_zoomall(self, *args, **kwargs): first = True for line in self.main_axes.lines: if first: min_x = min(line.get_xdata()) max_x = max(line.get_xdata()) min_y = min(line.get_ydata()) max_y = max(line.get_ydata()) first = False else: min_x = min(min_x, min(line.get_xdata())) max_x = max(max_x, max(line.get_xdata())) min_y = min(min_y, min(line.get_ydata())) max_y = max(max_y, max(line.get_ydata())) if not first: # If there is at least 1 line self.main_axes.set_xlim([min_x, max_x]) self.main_axes.set_ylim([min_y, max_y]) # Redraw changes self.fig.canvas.draw() def action_btn_normalization(self, *args, **kwargs): if not self.normalization_flag: self.normalization_flag = True self.widgets['btn_normalization']['text'] = 'Please double-click on y=0...' self.normalization_connection = self.canvas.mpl_connect('button_press_event', self.action_normalization_firstclick) else: self.widgets['btn_normalization']['text'] = 'Normalize y-axis' self.normalization_flag = False self.canvas.mpl_disconnect(self.normalization_connection) def action_normalization_firstclick(self, event, *args, **kwargs): if event.dblclick and event.inaxes == self.main_axes: y = event.ydata self.new_base_value = self.base_value + y * self.normalization_value self.canvas.mpl_disconnect(self.normalization_connection) self.normalization_connection = self.canvas.mpl_connect('button_press_event', self.action_normalization_secondclick) self.widgets['btn_normalization']['text'] = 'Please double-click on y=1...' def action_normalization_secondclick(self, event, *args, **kwargs): if event.dblclick and event.inaxes == self.main_axes: y = event.ydata self.normalization_value = (self.normalization_value * y + self.base_value - self.new_base_value) self.base_value = self.new_base_value self.action_btn_normalization() self.refresh_plot() def action_btn_derivative(self, *args, **kwargs): if self.selected_artist is not None: y = self.selected_artist['artist'].get_ydata() x = self.selected_artist['artist'].get_xdata() # Border effects may exist derivative_data = np.convolve(np.array([-1, 0, 1]), y, mode='same') delta_data = np.convolve(np.array([-1, 0, 1]), x, mode='same') derivative_data = np.divide(derivative_data, delta_data) self.main_axes.plot(x, derivative_data, picker=self.picker_tolerance, label='<Derivative of ' + self.selected_artist['artist'].get_label() + '>') self.fig.canvas.draw()
class PhasePlot: def __init__(self, frame, tag_filter, tag_var, menubar): self.tag_filter = tag_filter self.tag_var = tag_var self.menubar = menubar # filter_frame2 = tk.LabelFrame(frame, text='Phase Filter', bg='white', bd=0, # font=('Times New Roman', 12, 'bold'), labelanchor='n') # filter_frame2.pack(side=tk.LEFT, fill=tk.BOTH) self.phase_vars = [tk.IntVar() for i in range(16)] self.phase_filter = CheckFilter(self.menubar, self.phase_vars) self.x_start = 0 self.x_end = 400 self.frame = frame self.fig = plt.figure() self.ax = self.fig.add_subplot(111) self.canvas = FigureCanvasTkAgg(self.fig, master=frame) self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1) self.canvas.mpl_connect("scroll_event", self.zoom) self.canvas.mpl_connect("button_press_event", self.input_Motionx_y) self.canvas.mpl_connect("button_release_event", self.output_Motionx_y) def update_plot(self, data): self.phase_filter.update_filter([i for i in range(16)]) self.ax.cla() self.ax.set(xlabel='sample (#)', ylabel='phase (rad)', title='Phase Plot') ys = data[self.tag_var.get()]['phase'] self.data_len = len(ys[0]) if self.data_len > 400 and self.x_end == self.data_len-1: self.x_start +=1 self.x_end += 1 if self.x_end - self.x_start <400 and self.x_end == self.data_len-1: self.x_start += 1 self.x_end += 1 ls = [] labels = [] # print(self.tag_var.get()) for i in range(16): l, = self.ax.plot(ys[i], label='antenna%d' % (i + 1), visible=self.phase_vars[i].get()) if l.get_visible(): ls.append(l) labels.append(l.get_label()) self.ax.set_ylim(-6, 6) self.ax.set_xlim(self.x_start, self.x_end) self.ax.legend(ls, labels, loc=1) self.canvas.draw() def zoom(self,event): if event.button == "up": if self.x_end-self.x_start >50: self.x_end -= 50 self.ax.set_xlim(self.x_start, self.x_end) else: if self.x_end-self.x_start <400: if self.x_start <= 25: mid = self.x_start self.x_start = 0 self.x_end += 25+abs(mid) self.ax.set_xlim(self.x_start, self.x_end) elif self.x_end >=400: mid = self.x_end - 400 self.x_end = self.data_len - 1 self.x_start += 25 + mid self.ax.set_xlim(self.x_start, self.x_end) else: self.x_start -= 25 self.x_end += 25 self.ax.set_xlim(self.x_start, self.x_end) elif self.x_end-self.x_start >= 400: pass def input_Motionx_y(self,event): self.c_list_x = [] self.c_list_y = [] self.c_list_x.append(event.x) self.c_list_y.append(event.y) self.move_f = self.canvas.mpl_connect("motion_notify_event", self.moveon) def output_Motionx_y(self,event): self.canvas.mpl_disconnect(self.move_f) def moveon(self,event): self.c_list_x.append(event.x) if self.c_list_x[-1]-self.c_list_x[-2] >0: if self.x_start >= 0: self.x_start -= 1 self.x_end -= 1 if self.x_start <= 0: mid = self.x_start self.x_start =0 self.x_end += abs(mid) else: if self.data_len > self.x_end: self.x_start += 1 self.x_end += 1 self.ax.set_xlim(self.x_start, self.x_end)
class GUI: def __init__(self, env_name, n_channels): self.n_channels = n_channels # The seaborn color_palette cubhelix is used to assign visually distinct colors to each channel for the env self.cmap = sns.color_palette("cubehelix", self.n_channels) self.cmap.insert(0, (0, 0, 0)) self.cmap = colors.ListedColormap(self.cmap) bounds = [i for i in range(self.n_channels + 2)] self.norm = colors.BoundaryNorm(bounds, self.n_channels + 1) self.root = Tk.Tk() self.root.title(env_name) self.root.config(background='white') self.root.attributes("-topmost", True) if platform() == 'Darwin': # How Mac OS X is identified by Python system( '''/usr/bin/osascript -e 'tell app "Finder" to set frontmost of process "Python" to true' ''' ) self.root.focus_force() self.text_message = Tk.StringVar() self.label = Tk.Label(self.root, textvariable=self.text_message) self.fig = Figure() self.ax = self.fig.add_subplot(111) self.canvas = FigureCanvasTkAgg(self.fig, master=self.root) self.canvas.get_tk_widget().pack(side=Tk.TOP, fill=Tk.BOTH, expand=1) self.key_press_handler = self.canvas.mpl_connect( 'key_press_event', self.on_key_event) self.key_release_handler = self.canvas.mpl_connect( 'key_press_event', lambda x: None) # Set the message for the label on screen def set_message(self, str): self.text_message.set(str) self.label.pack() # Show the current frame def display_state(self, state): self.ax.cla() numerical_state = np.amax( state * np.reshape(np.arange(self.n_channels) + 1, (1, 1, -1)), 2) + 0.5 self.ax.imshow(numerical_state, cmap=self.cmap, norm=self.norm, interpolation='none') self.canvas.draw() # Allow user to handle their own keyboard input def overwrite_key_handle(self, key_press_handler, key_release_handler=None): self.canvas.mpl_disconnect(self.key_press_handler) self.key_press_handler = self.canvas.mpl_connect( 'key_press_event', key_press_handler) if (key_release_handler is not None): self.canvas.mpl_disconnect(self.key_release_handler) self.key_release_handler = self.canvas.mpl_connect( 'key_release_event', key_release_handler) # Default key handler def on_key_event(self, event): if event.key == "q": self.quit() # Quit the GUI def quit(self): self.root.quit() # After millisecond, calls function func def update(self, millisecond, func): self.root.after(millisecond, func) # Start the GUI def run(self): self.root.mainloop()
class PyplotEmbed(tk.Frame): """ Class that will make a tkinter frame with a matplotlib plot area embedded in the frame """ def __init__(self, master, data): tk.Frame.__init__(self, master=master) self.index = 1 self.lines = [] self.labels = [] self.colors = [] self.vert_line = None self.data = data self.cursor_connect = None self.graph_area = tk.Frame(self) self.figure_bed = plt.figure(figsize=(6, 4)) self.axis = self.figure_bed.add_subplot(111) self.canvas = FigureCanvasTkAgg(self.figure_bed, master=self) self.canvas._tkcanvas.config(highlightthickness=0) self.toolbar = NavToolbar(self.canvas, self) # TODO: check this # self.toolbar.pack_forget() self.toolbar.pack() self.canvas.draw() self.canvas.get_tk_widget().pack(side='left', fill=tk.BOTH, expand=1) def plot(self, data, _label, color=None): # self.data.plot(ax=self.graph_area.axis, label='channel {0}'.format(self.index)) # line = self.axis.plot(data.index, data['voltage'], label=_label)[0] line = self.axis.plot(data.index, data, label=_label)[0] if color: line.set_color(color) self.colors.append(line.get_color()) self.labels.append(_label) self.lines.append(line) self.axis.legend() self.index += 1 self.canvas.show() def delete_all(self): while self.lines: l = self.lines.pop() l.remove() del l self.canvas.draw() self.index = 1 self.axis.legend() def delete_some_data(self, picks): print('delete in graph: ', picks) for index in reversed(picks): self.delete_line(index) def delete_line(self, _index): self.index -= 1 del self.labels[_index] line = self.lines.pop(_index) line.remove() del line self.update_legend() def change_line_color(self, _color, index): print('change line:', index, _color) self.lines[index].set_color(_color) self.colors[index] = _color def change_line_style(self, style, index): if style != 'solid': self.lines[index].set_linestyle(style) self.lines[index].set_dashes((1, 5)) def update_legend(self): """ Update the legend and redraw the graph """ handle, labels = self.axis.get_legend_handles_labels() self.axis.legend( handle, self.labels, loc='best', # bbox_to_anchor=(1, 0.5), # title='Data series', prop={'size': 10}, fancybox=True) # not adding all this screws it up # up for some reason self.canvas.show() # update the canvas where the data is being shown def time_shift(self, data_index: int, time_to_shift: float): adj_time_shift = time_to_shift - self.data.time_start[data_index] x_data = self.data.adjusted_data[data_index].index - adj_time_shift self.lines[data_index].set_xdata(x_data) self.canvas.draw() def get_fit_start(self): print('make cursor') # cursor = self.axis.axvline(color='r') self.cursor_connect = self.canvas.mpl_connect('motion_notify_event', self.onMouseMove) self.canvas.mpl_connect('button_press_event', self.onclick) self.canvas.show() def onMouseMove(self, event): if not event.xdata: # mouse is not over the plot area return if self.vert_line: self.vert_line.remove() del self.vert_line self.vert_line = None self.vert_line = self.axis.axvline(x=event.xdata, color='r', linewidth=2) self.canvas.show() def onclick(self, event): if event.dblclick == 1: full_data_set = self.data.adjusted_data[-1] start_index = full_data_set.index.get_loc(event.xdata, 'nearest') # start_index = pd.Index(self.data.adjusted_data[-1]).get_loc(event.xdata, 'nearest') start_num = full_data_set.index[start_index] data_to_fit = self.data.adjusted_data[-1][start_num:start_num + 20] self.fit_data(data_to_fit, start_num) def fit_data(self, _data, starting_place): print('fitting') self.canvas.mpl_disconnect(self.cursor_connect) t = _data.index - starting_place # change the time so t=0 at the start of the recording amplitude_guess = -_data.voltage.min() time_shift = 0 bounds = (0.0, [ 1.2 * amplitude_guess, 1.2 * amplitude_guess, 1.2 * amplitude_guess, 1.2 * amplitude_guess, 0.2, 2, 100, 100 ]) initial_guess = (2. * amplitude_guess / 3, amplitude_guess / 3., amplitude_guess / 2., amplitude_guess / 2., 0.2, 2, 2, 15) fitted_parameters, _ = curve_fit(fitting_func_2a_2i_exp, t, _data.voltage, p0=initial_guess, bounds=bounds, ftol=0.0000005, xtol=0.0000005) # fitted_parameters, _ = curve_fit(fitting_func_2a_2i_exp, t, _data.voltage, p0=initial_guess) print('fitting params: {0}'.format(fitted_parameters)) # make a new line with the fitted parameters and draw it y_fitted = fitting_func_2a_2i_exp(t, *fitted_parameters) # plt.plot(t + _data.index, y_fitted, linewidth=2, label="Check") fitted_line = self.axis.plot(_data.index, y_fitted, linewidth=2, label='fitted')[0] def toogle_data_decimation(self, data): # line: matplotlib.lines.Line2D # type hint the for loop variable for i, line in enumerate(self.lines): line.set_xdata(data[i].index.values) line.set_ydata(data[i]) self.canvas.show()
class App(object): #swapped out object, tk.Frame def __init__(self, master): self.rt = master self.rt.protocol("WM_DELETE_WINDOW", self.onClosing) self.menu = tk.Menu(self.rt) self.rt.title('NMR GUI - Potter') self.rt.config(menu=self.menu) self.filemenu = tk.Menu(self.menu) self.menu.add_cascade(label='File', menu=self.filemenu) self.Master = master self.filemenu.add_command(label='New') self.filemenu.add_command(label='Open', command=self.openFile) self.filemenu.add_separator() self.filemenu.add_command( label='Exit', command=lambda: [root.quit(), root.destroy()]) self.helpmenu = tk.Menu(self.menu) self.menu.add_cascade(label='Help', menu=self.helpmenu) self.helpmenu.add_command(label='About', command=self.launchHelpWindow) self.analysismenu = tk.Menu(self.menu) self.menu.add_cascade(label='Analysis', menu=self.analysismenu) self.analysismenu.add_command(label='Integrate', command=self.openIntegrationModule) self.analysismenu.add_command(label='EditIntegralList', command=self.showIntegralList) self.analysismenu.add_command(label='Calibrate', command=self.calibrate) self.xShift = 0.0 self.showIntVar1 = tk.IntVar() self.calOn = 0 self.intVar1 = tk.IntVar() self.showPeakVar1 = tk.IntVar() self.calVar1 = tk.IntVar() self.startCoords = [] self.endCoords = [] self.integrals = [] self.intListVar = [] self.intGraphics1 = [] self.intGraphics2 = [] self.peakList = [] self.peakGraphics1 = [] self.intCheckBoxes = [] self.intDeleteButtons = [] self.rt.mainloop() def openFile(self): self.filename = askopenfilename(parent=self.rt) with open(self.filename, mode='r') as file: self.data = pd.read_csv(file, names=['x', 'y']) self.th = 0.0 self.t1Top = tk.Frame(self.rt) self.t1Bottom = tk.Frame(self.rt) self.t1Top.pack(side=tk.TOP) self.t1Bottom.pack(side=tk.BOTTOM) self.t1Left = tk.Frame(self.t1Top) self.t1Right = tk.Frame(self.t1Top) self.t1Left.pack(side=tk.LEFT) self.t1Right.pack(side=tk.RIGHT) self.fig = plt.figure() self.ax = self.fig.add_subplot(111) self.ax.set_xlim(self.data['x'].max(), self.data['x'].min()) self.ax.plot(self.data['x'], self.data['y']) self.canvas = FigureCanvasTkAgg(self.fig, master=self.rt) self.canvas.draw() #self.canvas.get_tk_widget().pack(in_ = self.t1Right, fill = tk.BOTH, expand = 1) self.canvas.get_tk_widget().pack(in_=self.t1Right, fill=tk.BOTH, expand=True) self.toolbar = NavigationToolbar2Tk(self.canvas, self.rt) self.toolbar.update() #self.canvas.get_tk_widget().pack(in_ = self.t1Right, fill = tk.BOTH, expand = 1) self.canvas.get_tk_widget().pack(in_=self.t1Right, fill=tk.BOTH, expand=True) self.xShiftScale = tk.Scale(self.rt, from_=self.data['y'].max(), to_=self.data['y'].min()) self.xShiftScale.pack(in_=self.t1Left) self.showIntToggle = tk.Checkbutton(self.rt, text='Show Integrals', variable=self.showIntVar1, command=self.showIntegrals) self.showIntToggle.pack(in_=self.t1Bottom, side=tk.RIGHT) self.showPeaksToggle = tk.Checkbutton(self.rt, text='Show Peaks', variable=self.showPeakVar1, command=self.showPeaks) self.showPeaksToggle.pack(in_=self.t1Bottom, side=tk.RIGHT) def openIntegrationModule(self): self.t = tk.Toplevel(self.rt) self.t.protocol("WM_DELETE_WINDOW", self.onIntClosing) self.t.wm_title("Integration Module") self.intFig = plt.figure() self.intAx = self.intFig.add_subplot(111) self.intAx.set_xlim(self.data['x'].max(), self.data['x'].min()) self.intAx.plot(self.data['x'], self.data['y']) self.canvas2 = FigureCanvasTkAgg(self.intFig, master=self.t) self.canvas2.draw() self.canvas2.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1) self.toolbar2 = NavigationToolbar2Tk(self.canvas2, self.t) self.toolbar2.update() self.canvas2.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1) self.intToggle = tk.Checkbutton(self.t, text="Integration Mode", variable=self.intVar1, command=self.integrate) self.intToggle.pack(side=tk.LEFT) def onclick(self, event): ix = event.xdata self.startCoords.append(ix) def onrelease(self, event): ix = event.xdata self.endCoords.append(ix) pint.drawTraps(self.data, min([self.startCoords[-1], self.endCoords[-1]]), max([self.startCoords[-1], self.endCoords[-1]]), self.intAx) self.canvas2.draw() def integrate(self): if self.intVar1.get() == 1: self.cid1 = self.canvas2.mpl_connect('button_press_event', self.onclick) self.cid2 = self.canvas2.mpl_connect('button_release_event', self.onrelease) else: self.canvas2.mpl_disconnect(self.cid1) self.canvas2.mpl_disconnect(self.cid2) def onClosing(self): if messagebox.askokcancel("Quit", "Do you want to quit?"): self.rt.quit() self.rt.destroy() def onIntClosing(self): if messagebox.askokcancel("Quit", "Save integrals?"): tempInts = [] for i in range(len(self.startCoords)): tempInts.append(min([self.startCoords[i], self.endCoords[i]])) tempInts.append(max([self.startCoords[i], self.endCoords[i]])) tempInts.append( pint.trapInt(self.data, tempInts[0], tempInts[1])) tempInts.append(1) self.integrals.append(tempInts.copy()) tempInts.clear() self.startCoords.clear() self.endCoords.clear() self.t.destroy() def showIntegrals(self): if self.showIntVar1.get() == 1: for i in range(len(self.integrals)): #this is the region marker if (self.integrals[i][3] == 1 and not self.intListVar) or ( self.integrals[i][3] == 1 and self.intListVar[i].get() == 1): self.intGraphics1.append( self.ax.annotate('', xy=(self.integrals[i][0], 0), xytext=(self.integrals[i][1], 0), arrowprops=dict(arrowstyle='<->', facecolor='red'), annotation_clip=False)) for i in range(len(self.integrals) ): #this is the line connecting the integral value if (self.integrals[i][3] == 1 and not self.intListVar) or ( self.integrals[i][3] == 1 and self.intListVar[i].get() == 1): self.intGraphics2.append( self.ax.annotate( '{:.2f}'.format(self.integrals[i][2]), xy=(self.integrals[i][0] + ( (self.integrals[i][1] - self.integrals[i][0]) / 2), 0), xytext=(self.integrals[i][0] + ( (self.integrals[i][1] - self.integrals[i][0]) / 2), -3), arrowprops=dict(arrowstyle='-', facecolor='red'), annotation_clip=False)) self.canvas.draw() else: for i in self.intGraphics1: i.remove() for i in self.intGraphics2: i.remove() self.intGraphics1.clear() self.intGraphics2.clear() self.canvas.draw() def showIntegralList(self): self.t2 = tk.Toplevel(self.rt) self.t2.protocol("WM_DELETE_WINDOW", self.onCloseIntegralListWindow) self.t2.wm_title("Integrals") self.t2Top = tk.Frame(self.t2) self.t2Bottom = tk.Frame(self.t2) self.t2Top.pack(side=tk.TOP) self.t2Bottom.pack(side=tk.BOTTOM) self.t2Left = tk.Frame(self.t2Bottom) self.t2Right = tk.Frame(self.t2Bottom) self.t2Left.pack(side=tk.LEFT) self.t2Right.pack(side=tk.RIGHT) for i in range(len(self.integrals)): self.intListVar.append(tk.IntVar(value=1)) self.intCheckBoxes.append( tk.Checkbutton(self.t2Left, text=str(len(self.integrals) - i) + '\t' + '{:.2f}'.format(self.integrals[i][0]) + '\t' + '{:.2f}'.format(self.integrals[i][1]) + '\t' + '{:.2f}'.format(self.integrals[i][2]), variable=self.intListVar[i], width=40)) self.intDeleteButtons.append( tk.Button( self.t2Right, text='Delete: ' + str((len(self.integrals) - i)), command=lambda c=i: self.deleteInt(self.integrals, c)) ) # why not lambda:self.integrals[len(self.integrals)-i-1] ???? self.intCheckBoxes[i].pack(in_=self.t2Left, side=tk.BOTTOM, fill=tk.X) self.intDeleteButtons[i].pack(in_=self.t2Right, side=tk.BOTTOM) def deleteInt(self, ar, inx): del self.integrals[int(inx)] def onCloseIntegralListWindow(self): self.intCheckBoxes.clear() self.intDeleteButtons.clear() self.canvas.draw() self.t2Left.destroy() self.t2Right.destroy() self.t2Bottom.destroy() self.t2Top.destroy() self.t2.destroy() def showPeaks(self): # removed parameter th self.th = self.xShiftScale.get() self.peakList = find_peaks(self.data['y'].to_numpy(), self.th) if self.showPeakVar1.get() == 1: a = 0 for i in range(len(self.peakList[0])): if ((self.data.iloc[self.peakList[0][i - 1]][1] < self.data.iloc[self.peakList[0][i]][1]) and ((self.data.iloc[self.peakList[0][i - 1]][1]) >= (self.data.iloc[self.peakList[0][i]][1] - 0.1)) or ((self.data.iloc[self.peakList[0][i - 1]][1] > (self.data.iloc[self.peakList[0][i]][1])) and ((self.data.iloc[self.peakList[0][i - 1]][1]) <= (self.data.iloc[self.peakList[0][i]][1] + 0.1)))): a = a + 1 else: a = a + 0 self.peakGraphics1.append( self.ax.annotate( '{:.2f}'.format( self.data.iloc[self.peakList[0][i]][0]), xy=(self.data.iloc[self.peakList[0][i]][0], self.data.iloc[self.peakList[0][i]][1]), xytext=(self.data.iloc[self.peakList[0][i]][0], self.data.iloc[self.peakList[0][i]][1] + a))) self.canvas.draw() else: for i in self.peakGraphics1: i.remove() self.peakGraphics1.clear() self.canvas.draw() def onclick2(self, event): ix = event.xdata self.xShift = ix self.data['x'] = self.data['x'] - self.xShift self.shiftIntegrals(self.integrals, self.xShift) self.calOn = 0 self.canvas.mpl_disconnect(self.cid3) self.rt.config(cursor='') def shiftIntegrals(self, iL, sh): msgBox = tk.messagebox.askquestion( 'Shift Integrals', 'Shift integrals along with spectrum?', icon='warning') if msgBox == 'yes': self.integralShift(iL, sh) self.ax.cla() self.ax.set_xlim(self.data['x'].max(), self.data['x'].min()) self.ax.plot(self.data['x'], self.data['y']) self.canvas.draw() else: self.ax.cla() self.ax.set_xlim(self.data['x'].max(), self.data['x'].min()) self.ax.plot(self.data['x'], self.data['y']) self.canvas.draw() def integralShift(self, ints, xsh): for i in range(len(self.integrals)): self.integrals[i][0] = self.integrals[i][0] - xsh self.integrals[i][1] = self.integrals[i][1] - xsh def calibrate(self): self.rt.config(cursor='cross') self.calOn = 1 self.cid3 = self.canvas.mpl_connect('button_press_event', self.onclick2) def onHelpClosing(self): self.t3.destroy() def launchHelpWindow(self): self.t3 = tk.Toplevel(self.rt) self.t3.protocol("WM_DELETE_HELPWINDOW", self.onHelpClosing) self.helpText = tk.Text(self.t3, height=10, width=30, wrap='word') self.helpText.pack(expand=True, fill=tk.BOTH) __location__ = os.path.realpath( os.path.join(os.getcwd(), os.path.dirname(__file__))) aboutFile = open(os.path.join(__location__, 'about.rtf')) self.helpText.insert(tk.END, aboutFile.read()) aboutFile.close()
class GUI(): def __init__(self,root,song): # given parameters self.root = root self.song = song # main window self.root.title("Song segment player") self.root.iconbitmap(r'Images/icon.ico') self.root.configure(background='white') # menubar self.menubar = Menu(self.root) self.filemenu = Menu(self.menubar,tearoff=0) self.filemenu.add_command(label="Open", command=lambda:b.openSong(self)) self.menubar.add_cascade(label="File",menu=self.filemenu) # images for buttons self.segmentIm = PhotoImage(file=r'Images/SegmentB.png') self.repSegIm = PhotoImage(file=r'Images/Repeat.png') self.playIm = PhotoImage(file=r'Images/PlayB.png') self.pauseIm = PhotoImage(file=r'Images/PauseB.png') self.stopIm = PhotoImage(file=r'Images/StopB.png') self.ffIm = PhotoImage(file=r'Images/ffB.png') self.rwIm = PhotoImage(file=r'Images/rwB.png') self.fullSpeedIm = PhotoImage(file=r'Images/FullSpeedB.png') self.halfSpeedIm = PhotoImage(file=r'Images/HalfSpeedB.png') self.sliderIm = PhotoImage(file=r'Images/SliderB.png') self.cursorIm = PhotoImage(file=r'Images/Cursor.png') self.magPlusIm = PhotoImage(file=r'Images/MagPlus.png') self.magMinusIm = PhotoImage(file=r'Images/MagMinus.png') # making buttons self.segmentB = Button(image=self.segmentIm, command=lambda:b.playSeg(self.song)) self.repSegB = Button(image=self.repSegIm, command=lambda:b.repeatSeg(self.song,self.repSegB)) self.playB = Button(image=self.playIm, command=lambda:b.playStream(self.song)) self.pauseB = Button(image=self.pauseIm, command=lambda:b.pauseStream(self.song)) self.stopB = Button(image=self.stopIm, command=lambda:b.stopStream(self.song)) self.ffB = Button(image=self.ffIm, command=lambda:b.ffStream(self.song)) self.rwB = Button(image=self.rwIm, command=lambda:b.rwStream(self.song)) self.fullSpeedB = Button(image=self.fullSpeedIm) self.halfSpeedB = Button(image=self.halfSpeedIm) # making radio buttons self.CLICKMODE = StringVar(value="slide") self.sliderB = Radiobutton( self.root, image=self.sliderIm, variable=self.CLICKMODE, value='slide', indicatoron=0) self.cursorB = Radiobutton( self.root, image=self.cursorIm, variable=self.CLICKMODE, value='curse', indicatoron=0) self.magPlusB = Radiobutton( self.root, image=self.magPlusIm, variable=self.CLICKMODE, value='mag+', indicatoron=0) self.magMinusB = Radiobutton( self.root, image=self.magMinusIm, variable=self.CLICKMODE, value='mag-', indicatoron=0) # setting button parameters self.allButts = [self.segmentB,self.repSegB,self.playB,self.stopB, self.pauseB,self.ffB,self.rwB,self.fullSpeedB, self.halfSpeedB,self.sliderB,self.cursorB, self.magPlusB,self.magMinusB] [butt.config(bg='white') for butt in self.allButts] # disable all buttons to start with [butt.config(state='disabled') for butt in self.allButts] self.sliderB.config(state='normal') # placing buttons rowc = 3 self.segmentB.grid(row=0,column=2) self.repSegB.grid(row=0,column=3) self.playB.grid(row=rowc,column=3) self.pauseB.grid(row=rowc,column=2) self.stopB.grid(row=rowc,column=1) self.ffB.grid(row=rowc,column=4) self.rwB.grid(row=rowc,column=0) self.fullSpeedB.grid(row=0,column=4,columnspan=3) self.halfSpeedB.grid(row=0,column=5,columnspan=3) self.sliderB.grid(row=2,column=0) self.cursorB.grid(row=2,column=1) self.magPlusB.grid(row=2,column=2) self.magMinusB.grid(row=2,column=3) # display song title if self.song.songname: t = self.song.songname[0:-4] else: t = '' self.songTitleL = Label(self.root,text=t,bg='white') self.songTitleL.grid(row=0,column=0) # making figure canvas self.canvas = FigureCanvasTkAgg(self.song.fig,master=self.root) self.canvas.draw() self.canvas.get_tk_widget().grid(row=rowc-2,column=0,columnspan=5) # connecting canvas to monitor events self.cidpress = self.canvas.mpl_connect( 'button_press_event', lambda event:e.on_press(event,self.song,self.CLICKMODE)) self.cidrelease = self.canvas.mpl_connect( 'button_release_event', lambda event:e.on_release(event,self.song)) self.cidmotion = self.canvas.mpl_connect( 'motion_notify_event', lambda event:e.on_motion(event,self.song)) self.root.bind('<space>', lambda event:b.playPauseStream(self.song)) self.root.bind('<Return>', lambda event:b.playSeg(self.song)) # set cursor updating self.root.after(10000,lambda: e.updateCursor(self)) # insert menu bar self.root.config(menu=self.menubar) # closing behaviour self.root.protocol("WM_DELETE_WINDOW",self.on_closing) def on_closing(self): self.canvas.mpl_disconnect(self.cidpress) self.canvas.mpl_disconnect(self.cidrelease) self.canvas.mpl_disconnect(self.cidmotion) self.root.destroy() self.song.wf.close() self.song.stream.close() self.song.p.terminate() [os.remove(s) for s in self.song.createdFilepaths] def updateCursor(self): if self.song.wf: pos = self.song.wf.tell()/self.song.RATE self.song.cursor[0].set_x(pos) self.canvas.draw() self.root.after(250,self.updateCursor)
class MapPage(tk.Frame): ''' A page that will show the map of currently plotted earthquakes from the 'current_data.json' file ''' def __init__(self, parent, controller): super().__init__(parent) self.controller = controller self.local_url = None self.mappage_balloon = Pmw.Balloon(self) self.bind("<<RefreshPlot>>", self.refresh_plot) self.menubar = Pmw.MenuBar(self, hull_relief="raised", hull_borderwidth=1, balloon=self.mappage_balloon) self.menubar.pack(side="top", fill="x") self.menubar.addmenu("pages", "Switch Pages", "", "right") self.menubar.addmenu("file", "File And Data Options") self.menubar.addmenuitem("file", "command", "Quit The Program", command=self.controller.on_close_window, label="Quit") self.menubar.addmenuitem("pages", "command", "Switch To Settings Page", command=lambda: self.controller.show_frame("SettingsPage"), label="Settings Page") self.map_frame = tk.Frame(self) self.map_frame.pack(side="bottom", fill="both", expand=True) #Line2D objects are used here to be placed in the legend of the graph for more detail on the map legend_elements = [Line2D([0], [0], marker="o", color="green", label="Small (below 3)"), Line2D([0], [0], marker="o", color="yellow", label="Medium (below 6)"), Line2D([0], [0], marker="o", color="red", label="Large (above 6)")] self.map_figure = plt.figure(num=None, figsize=(12, 4)) self.map_axes = self.map_figure.add_subplot(111) self.map_axes_legend = self.map_axes.legend(handles=legend_elements, loc="upper right") #placing the legend self.map_axes.set_title("Earthquake Events - Mercator Projection") self.map_figure.tight_layout() #makes sure that when placing the map onto the GUI, it is responsive self.figure_basemap = Basemap(projection="merc", llcrnrlat=-80, urcrnrlat=80, llcrnrlon=-180, urcrnrlon=180, resolution="c") #defines that the map is using mercator projection self.figure_basemap.drawcoastlines() self.figure_basemap.fillcontinents(color="tan", lake_color="lightblue") self.figure_basemap.drawstates(color="darkred") self.figure_basemap.drawparallels(np.arange(-90.,91.,30.), labels=(True, True, False, False), dashes=(2,2)) self.figure_basemap.drawmeridians(np.arange(-180.,181.,60.), labels=(False, False, False, True), dashes=(2,2)) self.figure_basemap.drawmapboundary(fill_color="lightblue") self.figure_basemap.drawcountries() self.figure_canvas = FigureCanvasTkAgg(self.map_figure, self.map_frame) #creates the figure and draws the map onto it self.figure_canvas.draw() self.figure_canvas.get_tk_widget().pack(side="bottom", fill="both", expand=True) self.canvas_pick_event = self.figure_canvas.mpl_connect("pick_event", self.display_point_info) self.figure_toolbar = NavigationToolbar2Tk(self.figure_canvas, self.map_frame) #Toolbar with additional options is added to the figure self.figure_toolbar.update() self.figure_canvas._tkcanvas.pack(side="top", fill="both", expand=True) def refresh_plot(self, event): ''' Method for checking whether there is any different data to plot and if so reads it from the json file, done by checking the local_url against controller's current_url ''' if self.local_url == self.controller.current_url: return "Same Request" self.local_url = self.controller.current_url with open("current_data.json", "r") as json_file: data = json.load(json_file) self.plot_points(data) messagebox.showinfo(title="Data Plotted", message="{} points plotted".format(data["metadata"]["count"])) def plot_points(self, filedata): ''' Method for creating MapPoint objects and plotting them on the figure, by first clearing the figure of any previous plots and then reading 'current_data.json' ''' self.map_axes.lines.clear() for quake in filedata["features"]: lat=quake["geometry"]["coordinates"][1] if lat > 80: lat=80 elif lat <-80: lat=-80 nx,ny = self.figure_basemap((quake["geometry"]["coordinates"][0],), (lat,)) new_point = MapPoint(nx, ny, quake["properties"]["mag"], quake["properties"]["place"], quake["properties"]["time"], quake["properties"]["felt"], quake["properties"]["cdi"], quake["properties"]["mmi"], quake["properties"]["alert"], quake["properties"]["tsunami"], quake["properties"]["sig"], quake["properties"]["title"], quake["properties"]["status"], quake["properties"]["dmin"], quake["properties"]["gap"], quake["properties"]["magType"], quake["properties"]["type"]) self.map_axes.add_line(new_point) self.figure_canvas.draw() def display_point_info(self, event): ''' Method when an individual point is picked, prompts the user to view information about it ''' line_obj = event.artist messagebox.showinfo(title="Point Selected", message="Here is more info about the point - {}".format(line_obj.place)) self.figure_canvas.mpl_disconnect(self.canvas_pick_event) self.controller.call_display_info(line_obj) self.controller.show_frame("PointInfoPage") def reconnect_pick_event(self): ''' Method to reconnect pick event with the figure after a previous disconnect ''' self.canvas_pick_event = self.figure_canvas.mpl_connect("pick_event", self.display_point_info)
class NeuriteDialogBox: def __init__(self, parent): top1 = self.top1 = Toplevel(parent) top2 = self.top2 = Toplevel(parent) top3 = self.top3 = Toplevel(parent) top1.title("Neurite analysis") top2.title("Neurite analysis") top3.title("Neurite analysis") top1.bind("<Escape>",self.cancel) top2.bind("<Escape>",self.cancel) top3.bind("<Escape>",self.cancel) top2.withdraw() top3.withdraw() # Window 1: self.entr_lab=[None]*6 self.entr_var_map=[None]*6 self.entr_map=[None]*6 self.entr_var_data=[None]*6 self.entr_data=[None]*6 self.entr_lab[0]=Label(top1, text="Image 1") self.entr_lab[1]=Label(top1, text="Image 2") self.entr_lab[2]=Label(top1, text="Image 3") self.entr_lab[3]=Label(top1, text="Image 4") self.entr_lab[4]=Label(top1, text="Image 5") self.entr_lab[5]=Label(top1, text="Image 6") for i in range(0,6): self.entr_lab[i].grid(row=i+2,column=0,sticky=W) for i in range(0,6): self.entr_var_map[i]=StringVar() for i in range(0,6): self.entr_map[i] = Entry(top1,textvariable=self.entr_var_map[i]) for i in range(0,6): self.entr_map[i].grid(row=i+2,column=1,columnspan=2) for i in range(0,6): self.entr_var_map[i].trace('w',self.nxt_button1) self.entr_var_map[i].trace('u',self.nxt_button1) for i in range(0,6): self.entr_var_data[i]=StringVar() for i in range(0,6): self.entr_data[i] = Entry(top1,textvariable=self.entr_var_data[i]) for i in range(0,6): self.entr_data[i].grid(row=i+2,column=3,columnspan=2) for i in range(0,6): self.entr_var_data[i].trace('w',self.nxt_button1) self.entr_var_data[i].trace('u',self.nxt_button1) self.img_num_lab=Label(top1,text="Number of images:") self.img_num_lab.grid(row=0,column=0,columnspan=2) self.img_num_var=IntVar() self.img_num=OptionMenu(top1,self.img_num_var,1,2,3,4,5,6) self.img_num.grid(row=0,column=2,sticky=W) self.img_num_var.set(1) self.img_num_var.trace('w',self.refresh_win1) self.out_entry_lab=Label(top1,text="Output file: ") self.out_entry_lab.grid(row=0,column=3) self.out_entry_var=StringVar() self.out_entry=Entry(top1,textvariable=self.out_entry_var,width=10) self.out_entry.grid(row=0,column=4,columnspan=2) self.out_entry_var.trace('w',self.nxt_button1) self.out_entry_var.trace('u',self.nxt_button1) self.nxt1 = Button(top1, text="Next", command=self.wind2_open,width=10) canc1 = Button(top1,text="Cancel",command=self.cancel,width=10) self.nxt1.grid(row=8, column=1) canc1.grid(row=8, column=3) self.mapping_label=Label(top1,text="Mapping").grid(row=1,column=1,columnspan=2) self.data_label=Label(top1,text="Data").grid(row=1,column=3,columnspan=2) self.nxt1.configure(state="disabled") for i in range(1,6): self.entr_map[i].configure(state="disabled") self.entr_data[i].configure(state="disabled") self.entr_lab[i].configure(state="disabled") # Window 2: self.map_box_lab=Label(top2, text="Mapping box size:") self.data_box_lab=Label(top2, text="Data box radius:") self.clust_rb_lab=Label(top2, text="Clustering") self.clust_size_lab=Label(top2, text="Cluster size:") self.spac_lab=Label(top2, text="Spacing:") self.map_box_lab.grid(row=0,column=0,sticky=W) self.data_box_lab.grid(row=0,column=2,sticky=W) self.spac_lab.grid(row=1,column=0,sticky=W) self.clust_rb_lab.grid(row=2,column=0,rowspan=2,sticky=W) self.clust_size_lab.grid(row=2,column=2,rowspan=2,sticky=W) self.map_box_var=StringVar() self.data_box_var=StringVar() self.clust_rb_var=BooleanVar() self.clust_size_var=StringVar() self.spac_var=StringVar() self.map_box=Entry(top2,width=5,textvariable=self.map_box_var) self.data_box=Entry(top2,width=5,textvariable=self.data_box_var) self.clust_size=Entry(top2,width=5,textvariable=self.clust_size_var) self.spac=Entry(top2,width=5,textvariable=self.spac_var) self.clust_rb_y=Radiobutton(top2,text="Yes",variable=self.clust_rb_var,value=True) self.clust_rb_n=Radiobutton(top2,text="No",variable=self.clust_rb_var,value=False) self.map_box.grid(row=0,column=1) self.data_box.grid(row=0,column=3) self.clust_size.grid(row=2,column=3,rowspan=2) self.clust_rb_y.grid(row=2,column=1) self.clust_rb_n.grid(row=3,column=1) self.spac.grid(row=1,column=1) self.clust_rb_var.trace('w',self.refresh_win2) self.map_box_var.trace('w',self.nxt_button2) self.data_box_var.trace('w',self.nxt_button2) self.clust_rb_var.trace('w',self.nxt_button2) self.clust_size_var.trace('w',self.nxt_button2) self.spac_var.trace('w',self.nxt_button2) self.nxt2 = Button(top2, text="Next", command=self.wind3_open,width=10) canc2 = Button(top2,text="Cancel",command=self.cancel,width=10) self.nxt2.grid(row=4, column=0,columnspan=2) canc2.grid(row=4, column=2,columnspan=2) self.nxt2.configure(state="disabled") self.clust_size.configure(state="disabled") self.clust_size_lab.configure(state="disabled") self.clust_rb_var.set(False) #Window 3: top3.columnconfigure(0,minsize=300) top3.columnconfigure(1,minsize=300) #frames: frame=self.frame=[None]*6 for i in range(0,6): frame[i]=Frame(top3,borderwidth=1,relief=SUNKEN) frame[i].grid(row=int(i/2),column=i%2) frame[i].rowconfigure(1,minsize=5) frame[i].columnconfigure(2,minsize=100) frame[i].columnconfigure(4,minsize=50) frame[i].columnconfigure(5,minsize=50) #main buttons: self.exe=Button(top3,text="Execute",command=self.multi_fil,width=10,pady=3) canc3 = Button(top3,text="Cancel",command=self.cancel,width=10,pady=3) self.exe.grid(row=3,column=0) canc3.grid(row=3,column=1) self.exe.configure(state="disabled") #frame elements: self.map_file_lab=[None]*6 self.data_file_lab=[None]*6 self.map_file_name=[None]*6 self.data_file_name=[None]*6 self.empty=[None]*6 self.hor_distance_lab=[None]*6 self.ver_distance_lab=[None]*6 self.hor_distance_var=[None]*6 self.ver_distance_var=[None]*6 self.hor_distance=[None]*6 self.ver_distance=[None]*6 self.wid_lab=[None]*6 self.hig_lab=[None]*6 self.wid=[None]*6 self.hig=[None]*6 self.hor_scale_lab=[None]*6 self.ver_scale_lab=[None]*6 self.hor_scale=[None]*6 self.ver_scale=[None]*6 self.hor_var=[None]*6 self.ver_var=[None]*6 self.starting_point_lab=[None]*6 self.end_point_lab=[None]*6 self.sp_x_var=[None]*6 self.sp_y_var=[None]*6 self.ep_x_var=[None]*6 self.ep_y_var=[None]*6 self.sp_x=[None]*6 self.sp_y=[None]*6 self.ep_x=[None]*6 self.ep_y=[None]*6 self.set=[None]*6 self.soma_lab=[None]*6 self.soma_var=[None]*6 self.soma=[None]*6 self.rev_var=[None]*6 self.rev=[None]*6 self.tolerance_lab=[None]*6 self.tolerance_var=[None]*6 self.tolerance=[None]*6 for i in range(0,6): self.map_file_lab[i]=Label(frame[i],text="Mapping file:",font="bold") self.data_file_lab[i]=Label(frame[i],text="Data file:",font="bold") self.map_file_lab[i].grid(row=0,column=0) self.data_file_lab[i].grid(row=0,column=3) self.map_file_name[i]=Label(frame[i],font="bold") self.data_file_name[i]=Label(frame[i],font="bold") self.map_file_name[i].grid(row=0,column=1,columnspan=2) self.data_file_name[i].grid(row=0,column=4,columnspan=2) self.empty[i]=Label(frame[i],text="") self.empty[i].grid(row=1) self.tolerance_lab[i]=Label(frame[i],text="Tolerance") self.tolerance_lab[i].grid(row=2,column=0,columnspan=2,sticky=W) self.tolerance_var[i]=IntVar() self.tolerance[i]=OptionMenu(frame[i],self.tolerance_var[i],1,2,3,4,5,6,7,8,9,10) self.tolerance[i].grid(row=2,column=2) self.tolerance_var[i].set(1) self.hor_distance_lab[i]=Label(frame[i],text="Horizontal distance [um]: ") self.ver_distance_lab[i]=Label(frame[i],text="Vertical distance [um]:") self.hor_distance_lab[i].grid(row=3,column=0,columnspan=2,sticky=W) self.ver_distance_lab[i].grid(row=4,column=0,columnspan=2,sticky=W) self.hor_distance_var[i]=StringVar() self.ver_distance_var[i]=StringVar() self.hor_distance[i]=Entry(frame[i],textvariable=self.hor_distance_var[i],width=5) self.ver_distance[i]=Entry(frame[i],textvariable=self.ver_distance_var[i],width=5) self.hor_distance[i].grid(row=3,column=2) self.ver_distance[i].grid(row=4,column=2) self.hor_distance_var[i].trace('w',lambda a,b,c,j=i: self.scaling(a,b,c,j)) self.hor_distance_var[i].trace('u',lambda a,b,c,j=i: self.scaling(a,b,c,j)) self.ver_distance_var[i].trace('w',lambda a,b,c,j=i: self.scaling(a,b,c,j)) self.ver_distance_var[i].trace('u',lambda a,b,c,j=i: self.scaling(a,b,c,j)) self.wid_lab[i]=Label(frame[i],text="Width:") self.hig_lab[i]=Label(frame[i],text="Height:") self.wid_lab[i].grid(row=5,column=0,sticky=W) self.hig_lab[i].grid(row=6,column=0,sticky=W) self.wid[i]=Label(frame[i]) self.hig[i]=Label(frame[i]) self.wid[i].grid(row=5,column=2) self.hig[i].grid(row=6,column=2) self.hor_scale_lab[i]=Label(frame[i],text="Horizontal scale:") self.ver_scale_lab[i]=Label(frame[i],text="Vertical scale:") self.hor_scale_lab[i].grid(row=7,column=0,sticky=W) self.ver_scale_lab[i].grid(row=8,column=0,sticky=W) self.hor_var[i]=DoubleVar() self.ver_var[i]=DoubleVar() self.hor_var[i].trace('w',self.exe_button) self.ver_var[i].trace('w',self.exe_button) self.hor_scale[i]=Label(frame[i]) self.ver_scale[i]=Label(frame[i]) self.hor_scale[i].grid(row=7,column=2) self.ver_scale[i].grid(row=8,column=2) self.rev_var[i]=BooleanVar() self.rev[i]=Checkbutton(frame[i],text="Reverse",variable=self.rev_var[i],onvalue=True,offvalue=False) self.rev[i].grid(row=2,column=3,columnspan=2,sticky=W) self.rev_var[i].set(False) self.starting_point_lab[i]=Label(frame[i],text="Starting point:") self.end_point_lab[i]=Label(frame[i],text="End point:") self.starting_point_lab[i].grid(row=3,column=3,sticky=W) self.end_point_lab[i].grid(row=4,column=3,sticky=W) self.sp_x_var[i]=StringVar() self.sp_y_var[i]=StringVar() self.ep_x_var[i]=StringVar() self.ep_y_var[i]=StringVar() self.sp_x_var[i].trace('w',self.exe_button) self.sp_y_var[i].trace('w',self.exe_button) self.ep_x_var[i].trace('w',self.exe_button) self.ep_y_var[i].trace('w',self.exe_button) self.sp_x[i]=Label(frame[i]) self.sp_y[i]=Label(frame[i]) self.ep_x[i]=Label(frame[i]) self.ep_y[i]=Label(frame[i]) self.sp_x[i].grid(row=3,column=4) self.sp_y[i].grid(row=3,column=5) self.ep_x[i].grid(row=4,column=4) self.ep_y[i].grid(row=4,column=5) self.set[i]=Button(frame[i],text="Set points",width=15) self.set[i].grid(row=7,column=3,columnspan=3,rowspan=2) self.soma_lab[i]=Label(frame[i],text="Starting distance"+"\n"+"from soma [um]:",justify=LEFT) self.soma_lab[i].grid(row=5,column=3,rowspan=2,sticky=W) self.soma_var[i]=StringVar() self.soma_var[i].trace('w',self.exe_button) self.soma_var[i].trace('u',self.exe_button) self.soma[i]=Entry(frame[i],textvariable=self.soma_var[i],width=5) self.soma[i].grid(row=5,column=5,rowspan=2) def cancel(self,event=None): self.top1.destroy() self.top2.destroy() self.top3.destroy() def refresh_win1(self,*args): for i in range(self.img_num_var.get(),6): self.entr_map[i].configure(state="disabled") self.entr_data[i].configure(state="disabled") self.entr_lab[i].configure(state="disabled") self.entr_var_map[i].set("") self.entr_var_data[i].set("") for j in range(1,self.img_num_var.get()): self.entr_map[j].configure(state="normal") self.entr_data[j].configure(state="normal") self.entr_lab[j].configure(state="normal") def nxt_button1(self,*args): if self.out_entry_var.get()!="": if all(self.entr_var_map[i].get()!="" for i in range(0,self.img_num_var.get())) and all(self.entr_var_data[i].get()!="" for i in range(0,self.img_num_var.get())): self.nxt1.configure(state="normal") self.top1.bind("<Return>",self.wind2_open) else: self.nxt1.configure(state="disabled") self.top1.unbind("<Return>") else: self.nxt1.configure(state="disabled") self.top1.unbind("<Return>") def refresh_win2(self,*args): if self.clust_rb_var.get(): self.clust_size.configure(state="normal") self.clust_size_lab.configure(state="normal") else: self.clust_size.configure(state="disabled") self.clust_size_lab.configure(state="disabled") self.clust_size_var.set('') def nxt_button2(self,*args): if self.clust_rb_var.get(): try: int(self.clust_size.get()) int(self.map_box.get()) int(self.data_box.get()) int(self.spac.get()) except ValueError: self.nxt2.configure(state="disabled") else: if int(self.map_box.get())<=1: self.map_box_var.set('2') if int(self.data_box.get())<1: self.data_box_var.set('1') if int(self.spac.get())<0: self.data_box_var.set('0') if int(self.clust_size.get())<2: self.data_box_var.set('2') self.nxt2.configure(state="normal") else: try: int(self.map_box.get()) int(self.data_box.get()) int(self.spac.get()) except ValueError: self.nxt2.configure(state="disabled") else: if int(self.map_box.get())<=1: self.map_box_var.set('2') if int(self.data_box.get())<1: self.data_box_var.set('1') if int(self.spac.get())<0: self.data_box_var.set('0') self.nxt2.configure(state="normal") def exe_button(self,*args): if all(self.hor_var[i].get()!=0 for i in range(0,self.img_num_var.get())) and all(self.ver_var[i].get()!=0 for i in range(0,self.img_num_var.get())): try: for j in range(0,self.img_num_var.get()): float(self.soma_var[j].get()) int(self.sp_x_var[j].get()) int(self.sp_y_var[j].get()) int(self.ep_x_var[j].get()) int(self.ep_y_var[j].get()) except ValueError: self.exe.configure(state="disabled") else: self.exe.configure(state="normal") def wind2_open(self,event=None): self.top1.withdraw() self.top2.deiconify() def wind3_open(self,event=None): self.top2.withdraw() self.top3.deiconify() txt1=[None]*self.img_num_var.get() txt2=[None]*self.img_num_var.get() for i in range(0,self.img_num_var.get()): self.map_file_name[i].configure(text=self.entr_var_map[i].get()) self.data_file_name[i].configure(text=self.entr_var_data[i].get()) txt1[i]=str(count(self.entr_var_map[i].get())[1])+" px" txt2[i]=str(count(self.entr_var_map[i].get())[0])+" px" self.wid[i].configure(text=txt1[i]) self.hig[i].configure(text=txt2[i]) self.set[i].configure(command=lambda fl_name=self.entr_var_map[i].get(),j=i: self.canv(fl_name,j)) for j in range(self.img_num_var.get(),6): for child in self.frame[j].winfo_children(): child.configure(state='disabled') def scaling(self,a,b,c,j): try: vd=float(self.ver_distance_var[j].get()) matrix_height=int(count(self.entr_var_map[j].get())[0]) v_scale="{:.2f}".format(matrix_height/abs(vd)) except ValueError: self.ver_scale[j].configure(text="") self.exe.configure(state="disabled") except ZeroDivisionError: self.ver_scale[j].configure(text="") self.exe.configure(state="disabled") else: txt=v_scale+" px/um" self.ver_scale[j].configure(text=txt) self.ver_var[j].set(matrix_height/abs(vd)) try: hd=float(self.hor_distance_var[j].get()) matrix_width=int(count(self.entr_var_map[j].get())[1]) h_scale="{:.2f}".format(matrix_width/abs(hd)) except ValueError: self.hor_scale[j].configure(text="") self.exe.configure(state="disabled") except ZeroDivisionError: self.hor_scale[j].configure(text="") self.exe.configure(state="disabled") else: txt=h_scale+" px/um" self.hor_scale[j].configure(text=txt) self.hor_var[j].set(matrix_width/abs(hd)) def canv(self,fl_name,num): top4=self.top4=Toplevel(self.top3) top4.grab_set() mat=[] in_file=open(fl_name,"r") mat=[line.split() for line in in_file] self.fig=plt.figure() self.sbplt=self.fig.add_subplot(111) X=np.arange(1,len(mat)+1) Y=np.arange(1,len(mat[0])+1) X,Y=np.meshgrid(X,Y) for x in range(0,len(mat)): for y in range(0,len(mat[0])): mat[x][y]=float(mat[x][y]) im=plt.imshow(mat,cmap="bone") self.fig.suptitle(fl_name) self.frame=Frame(self.top4) self.frame.grid(row=0,column=0,rowspan=20) self.canvas=FigureCanvasTkAgg(self.fig,self.frame) self.canvas.show() self.canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=True) toolbar = NavigationToolbar2TkAgg(self.canvas,self.frame) toolbar.update() toolbar.pack(side=BOTTOM) self.canvas._tkcanvas.pack(side=BOTTOM, fill=BOTH, expand=True) en=Button(top4,text="Ending point",command=self.click_en,width=10) en.grid(row=10,column=1) st=Button(top4,text="Starting point",command=self.click_st,width=10) st.grid(row=6,column=1) self.clr=Button(top4,text="Clear",command=lambda: self.clear(num),width=5) self.clr.grid(row=16,column=1) self.ok=Button(top4,text="OK",command=lambda: self.close_canvas(num),width=5) self.ok.grid(row=18,column=1) self.ok.configure(state="disabled") self.en_lab=Label(top4) self.st_lab=Label(top4) self.en_lab.grid(row=11,column=1) self.st_lab.grid(row=7,column=1) self.st_x_var=StringVar() self.st_y_var=StringVar() self.en_x_var=StringVar() self.en_y_var=StringVar() try: self.circ_s=patches.Circle((int(self.sp_x_var[num].get()),int(self.sp_y_var[num].get())),radius=5,color="green",fill=True) self.sbplt.add_patch(self.circ_s) self.circ_e=patches.Circle((int(self.ep_x_var[num].get()),int(self.ep_y_var[num].get())),radius=5,color="red",fill=True) self.sbplt.add_patch(self.circ_e) txt_s="X "+self.sp_x_var[num].get()+" Y "+self.sp_y_var[num].get() self.st_lab.configure(text=txt_s) txt_e="X "+self.ep_x_var[num].get()+" Y "+self.ep_y_var[num].get() self.en_lab.configure(text=txt_e) self.st_x_var.set(self.sp_x_var[num].get()) self.st_y_var.set(self.sp_y_var[num].get()) self.en_x_var.set(self.ep_x_var[num].get()) self.en_y_var.set(self.ep_y_var[num].get()) except AttributeError: pass except ValueError: pass else: self.ok.configure(state="normal") self.canvas.draw() self.top3.wait_window(top4) def clear(self,i): self.en_lab.configure(text='') self.st_lab.configure(text='') self.st_x_var.set("") self.st_y_var.set("") self.en_x_var.set("") self.en_y_var.set("") self.ok.configure(state="disabled") try: self.circ_e.remove() except AttributeError: pass try: self.circ_s.remove() except AttributeError: pass self.canvas.draw() self.sp_x_var[i].set('') self.sp_y_var[i].set('') self.ep_x_var[i].set('') self.ep_y_var[i].set('') self.sp_x[i].configure(text='') self.sp_y[i].configure(text='') self.ep_x[i].configure(text='') self.ep_y[i].configure(text='') self.clr.configure(state="disabled") def canv_click_en(self,event): try: self.circ_e.remove() except AttributeError: pass except ValueError: pass try: txt="X "+str(rnd(event.xdata))+" Y "+str(rnd(event.ydata)) self.en_lab.configure(text=txt) self.circ_e=patches.Circle((event.xdata,event.ydata),radius=5,color="red",fill=True) self.sbplt.add_patch(self.circ_e) self.canvas.draw() self.en_x_var.set(str(rnd(event.xdata))) self.en_y_var.set(str(rnd(event.ydata))) except TypeError: pass self.canvas.mpl_disconnect(self.cid_en) self.frame.configure(cursor="") self.clr.configure(state="normal") if self.st_x_var.get()!='' and self.st_y_var.get()!='' and self.en_x_var.get()!='' and self.en_y_var.get()!='': self.ok.configure(state="normal") def canv_click_st(self,event): try: self.circ_s.remove() except AttributeError: pass except ValueError: pass try: txt="X "+str(rnd(event.xdata))+" Y "+str(rnd(event.ydata)) self.st_lab.configure(text=txt) self.circ_s=patches.Circle((event.xdata,event.ydata),radius=5,color="green",fill=True) self.sbplt.add_patch(self.circ_s) self.canvas.draw() self.st_x_var.set(str(rnd(event.xdata))) self.st_y_var.set(str(rnd(event.ydata))) except TypeError: pass self.canvas.mpl_disconnect(self.cid_st) self.frame.configure(cursor="") self.clr.configure(state="normal") if self.st_x_var.get()!='' and self.st_y_var.get()!='' and self.en_x_var.get()!='' and self.en_y_var.get()!='': self.ok.configure(state="normal") def click_en(self): try: self.canvas.mpl_disconnect(self.cid_en) self.canvas.mpl_disconnect(self.cid_st) except AttributeError: pass self.frame.configure(cursor="cross") self.cid_en=self.canvas.mpl_connect('button_press_event',lambda event: self.canv_click_en(event)) def click_st(self): try: self.canvas.mpl_disconnect(self.cid_en) self.canvas.mpl_disconnect(self.cid_st) except AttributeError: pass self.frame.configure(cursor="cross") self.cid_st=self.canvas.mpl_connect('button_press_event',lambda event: self.canv_click_st(event)) def close_canvas(self,k): self.sp_x_var[k].set(self.st_x_var.get()) self.sp_y_var[k].set(self.st_y_var.get()) self.sp_x[k].configure(text="X "+self.st_x_var.get()) self.sp_y[k].configure(text="Y "+self.st_y_var.get()) self.ep_x_var[k].set(self.en_x_var.get()) self.ep_y_var[k].set(self.en_y_var.get()) self.ep_x[k].configure(text="X "+self.en_x_var.get()) self.ep_y[k].configure(text="Y "+self.en_y_var.get()) self.top4.destroy() plt.close(self.fig) def multi_fil(self): mapping_files=[] data_files=[] h_scales=[] v_scales=[] s_distances=[] st_points=[] en_points=[] tols=[] revs=[] if not self.clust_rb_var.get(): self.clust_size_var.set(1) for i in range(0,self.img_num_var.get()): mapping_files.append(self.entr_var_map[i].get()) data_files.append(self.entr_var_data[i].get()) h_scales.append(self.hor_var[i].get()) v_scales.append(self.ver_var[i].get()) s_distances.append(float(self.soma_var[i].get())) st_points.append((int(self.sp_x_var[i].get()),int(self.sp_y_var[i].get()))) en_points.append((int(self.ep_x_var[i].get()),int(self.ep_y_var[i].get()))) tols.append(self.tolerance_var[i].get()) revs.append(self.rev_var[i].get()) self.top1.destroy() self.top2.destroy() self.top3.destroy() multi_filament(mapping_files,data_files,h_scales,v_scales,s_distances,st_points,en_points,int(self.map_box_var.get()),int(self.data_box_var.get()),int(self.clust_size_var.get()),int(self.spac_var.get()),tols,revs,self.out_entry_var.get())
class labTOF(Frame): #define parent window def __init__(self, parent): #from Tkinter frame: #Frame.__init__(self, parent, background="white") #from ttk frame: Frame.__init__(self, parent) #save a reference to the parent widget self.parent = parent #delegate the creation of the user interface to the initUI() method #self.parent.title("Lab TOF") #self.pack(fill=BOTH, expand=1) self.fig = Figure(figsize=(4,4), dpi=100) #fig = Figure(facecolor='white', edgecolor='white') self.fig.subplots_adjust(bottom=0.15, left=0.15) self.canvas = FigureCanvasTkAgg(self.fig, self) self.toolbar = NavigationToolbar2TkAgg(self.canvas, self) self.toolbar.pack(side=TOP) #canvas.get_tk_widget().pack(side=BOTTOM, fill=BOTH, expand=True) #list containing time domain values self.time=[] #list containing mass domain values self.mass=[] #flag to indicate whether time or mass is displayed #time flag=0 #mass flag=1 self.time_mass_flag=0 #flag to indicate MS or MSMS calibration #MS = 0 #MSMS = 1 self.MSMS_flag=-99 #list containing intensity values self.intensity=[] #list containing calibration values self.cal_time=[] self.cal_mass=[] self.label_mass=[] self.label_time=[] self.label_intensity=[] #calibration point id self.cid=-99 self.initUI() #zero mass corresponds to ~40% of parent mass time self.MSMS_zero_time=0.40838#0.4150#0.4082 #container for other widgets def initUI(self): #set the title of the window using the title() method self.parent.title("Lab TOF") #apply a theme to widget self.style = Style() self.style.theme_use('default') #the Frame widget (accessed via the delf attribute) in the root window #it is expanded in both directions #CHANGE all pack to grid layout self.pack(fill=BOTH, expand=1) menubar = Menu(self.parent) self.parent.config(menu=menubar) #contains file menu items fileMenu = Menu(menubar, tearoff=0) menubar.add_cascade(label="File", menu=fileMenu) fileMenu.add_command(label="Open", command=self.onOpen) fileMenu.add_command(label="Export Data (ASCII file)", command=self.onExport) fileMenu.add_command(label="Quit", command=self.quit_destroy) #contains calibration menu items #"self" is needed to access items later (to change state) #default state for save calibration is disabled #state is enabled once a file is loaded self.calMenu=Menu(menubar, tearoff=0) menubar.add_cascade(label="Calibration", menu=self.calMenu) self.MScalMenu=Menu(menubar, tearoff=0) self.calMenu.add_cascade(label="MS Calibration", menu=self.MScalMenu, state=DISABLED) self.MScalMenu.add_command(label="Start New Calibration", command=self.onCalStart) self.MScalMenu.add_command(label="Open Calibration File", command=self.onOpenCal) self.MSMScalMenu=Menu(menubar, tearoff=0) self.calMenu.add_cascade(label="MSMS Calibration", menu=self.MSMScalMenu, state=DISABLED) self.MSMScalMenu.add_command(label="Start New Calibration", command=self.onMSMSCalStart) self.MSMScalMenu.add_command(label="Open Calibration File", command=self.onMSMSOpenCal) #default state for save calibration is disabled #state is enabled once a calibration is defined self.calMenu.add_command(label="Save Calibration File", command=self.onSaveCal, state=DISABLED) #generate figure (no data at first) self.generate_figure([0,1], [[],[]], ' ') #create instance of the button widget #command specifies the method to be called quitButton = Button(self, text="Quit", command = self.quit_destroy) #quitButton.place(x=50, y=50) quitButton.pack(side=RIGHT, padx=5, pady=5) #quitButton.grid(row=5, column=5, padx=5) #convert to time domain timeButton = Button(self, text="Time Domain", command = self.time_domain) timeButton.pack(side=RIGHT, padx=5, pady=5) #convert to mass domain self.massButton = Button(self, text="Mass Domain", command = self.mass_domain, state=DISABLED) self.massButton.pack(side=RIGHT, padx=5, pady=5) #smooth spectrum self.smoothButton = Button(self, text="Smooth", command = self.smooth_data, state=DISABLED) self.smoothButton.pack(side=RIGHT, padx=5, pady=5) #label peaks in figure self.labelButton = Button(self, text="Label Peak", command = self.label_peaks, state=DISABLED) self.labelButton.pack(side=RIGHT, padx=5, pady=5) self.deletelabelButton = Button(self, text="Remove Labels", command = self.delete_labels, state=DISABLED) self.deletelabelButton.pack(side=RIGHT, padx=5, pady=5) #calibration #identify parent peak in MSMS self.parentButton = Button(self, text="Identify Parent Peak", command = self.calibrate_parent, state=DISABLED) self.parentButton.pack(side=LEFT, padx=5, pady=5) #add calibration point self.calibrateButton = Button(self, text="Add Calibration Point", command = self.calibrate, state=DISABLED) self.calibrateButton.pack(side=LEFT, padx=5, pady=5) #finish calibration and convert to mass domain self.finishcalButton = Button(self, text="Finish Calibration", command = self.finish_calibrate, state=DISABLED) self.finishcalButton.pack(side=LEFT, padx=5, pady=5) #convert to mass domain #disabled until calibration is defined def mass_domain(self): self.time_mass_flag=1 self.generate_figure([self.mass,self.intensity], [self.label_mass, self.label_intensity], 'mass (Da)') #convert to time domain def time_domain(self): self.time_mass_flag=0 #plot in micro seconds self.generate_figure([[1E6*x for x in self.time],self.intensity], [self.label_time, self.label_intensity], 'time ($\mu$s)') #smooth data def smooth_data(self): #smooth self.time/mass/int variables - permanant #probably not a good idea #purpose of smoothing function: to display smoothed plot self.SmoothDialog(self.parent) #wait for dialog to close self.top.wait_window(self.top) smoothed_signal = scipy.signal.savgol_filter(self.intensity,self.window,self.poly) #convert from array to list self.intensity=smoothed_signal.tolist() #print 'intensity: ', len(self.intensity), type(self.intensity) if self.time_mass_flag == 1: #print 'mass: ', len(self.mass), type(self.mass) self.mass_domain() else: #print 'time: ', len(self.time), type(self.time) self.time_domain() #plot figure def generate_figure(self, data, label, xaxis_title): #clear figure plt.clf() ax = self.fig.add_subplot(111) #clear axis ax.cla() spectrum=ax.plot(data[0],data[1]) ax.set_xlabel(xaxis_title) ax.set_ylabel('intensity') #add peak labels for index, label_x in enumerate(label[0]): #ax.text(0.94*label_x, 1.05*label[1][index], str("%.1f" % label_x)) #ax.annotate(str("%.1f" % label_x), xy=(label_x, label[1][index]), xytext=(1.1*label_x, 1.1*label[1][index]), arrowprops=dict(facecolor='black', shrink=0.1),) #ax.annotate(str("%.1f" % label_x), xy=(label_x, label[1][index]), xytext=(1.0*label_x, 1.2*label[1][index]),) an1 = ax.annotate(str("%.1f" % label_x), xy=(label_x, label[1][index]), xytext=(label_x, label[1][index]), fontsize=12) an1.draggable() #remove self.label_time values #set label_peaks to generate figure again with loop that allows peak labeling (datacursor()) - set flag, reset flag in loop ##otherwise label will appear when selecting peaks for calibration #have Tim download mpldatacursor package with pip? #need to figure out how to remove labels ##canvas.delete("all") in function with a redraw function? #datacursor(spectrum, display='multiple', draggable=True, formatter='{x:.1f}'.format, bbox=None) self.canvas.show() self.canvas.get_tk_widget().pack(side=BOTTOM, fill=BOTH, expand=True) #self.can.grid(row=1, column=0, columnspan=6, rowspan=3, padx=5, sticky=E+W+S+N) self.toolbar.update() self.canvas._tkcanvas.pack(side=TOP, fill=BOTH, expand=True) #def delete_figure(self): #Canvas.delete("all") #.destroy() #open file def onOpen(self): #displays .txt files in browser window #only reads time domain data files ftypes = [('binary files', '*.trc'), ('txt files', '*.txt')] dlg = tkFileDialog.Open(self, filetypes = ftypes) fl = dlg.show() if fl != '': if fl[-3:] == 'trc': self.time, self.intensity = read_lecroy_binary.read_timetrace(fl) elif fl[-3:] == 'txt': data = self.readFile(fl) self.time=data[0] self.intensity=data[1] #plots data in time domain self.time_domain() self.labelButton.config(state=NORMAL) self.deletelabelButton.config(state=NORMAL) #allows for smoothing of data self.smoothButton.config(state=NORMAL) #allows for calibration self.calMenu.entryconfig("MS Calibration", state=NORMAL) self.calMenu.entryconfig("MSMS Calibration", state=NORMAL) #export data to txt file def onExport(self): #save header info (date/time, ask user for instrument)? #ask for save file name savefile = tkFileDialog.asksaveasfile(mode='wb', defaultextension=".txt") # asksaveasfile return `None` if dialog closed with "cancel". if savefile is None: return #export mass or time domain data depending on flag (what is plotted) if self.time_mass_flag == 0: x=self.time else: x=self.mass y=self.intensity for i in range(len(x)): savefile.write(str(x[i])) savefile.write(' ') savefile.write(str(y[i])) savefile.write('\r\n') savefile.close() #save calibration file def onSaveCal(self): #ask for save file name savefile = tkFileDialog.asksaveasfile(mode='wb', defaultextension=".txt") if savefile is None: return #write header information? #date/time, file name for i in range(len(self.cal_time)): savefile.write(str(self.cal_time[i])) savefile.write(' ') savefile.write(str(self.cal_mass[i])) #\r\n is the newline character for windows savefile.write('\r\n') savefile.close() #open previous MS (regular) calibration file def onOpenCal(self): #display .txt files in browser window ftypes = [('txt files', '*.txt')] dlg = tkFileDialog.Open(self, filetypes = ftypes) filename = dlg.show() if filename != '': #list with time calibration value calt_read=[] #list with mass calibration value calm_read=[] file=open(filename,'r') #header=file.readline() for line in file: #read each row - store data in columns temp = line.split(' ') calt_read.append(float(temp[0])) calm_read.append(float(temp[1].rstrip('/n'))) #store values in calibration lists self.cal_time=calt_read self.cal_mass=calm_read #sets MSMS flag for finish_calibrate routine self.MSMS_flag=0 #call finish_calibrate method to calibrate and plot data in mass domain self.finish_calibrate() #open previous MSMS calibration file def onMSMSOpenCal(self): #display .txt files in browser window ftypes = [('txt files', '*.txt')] dlg = tkFileDialog.Open(self, filetypes = ftypes) filename = dlg.show() if filename != '': #list with time calibration value calt_read=[] #list with mass calibration value calm_read=[] file=open(filename,'r') #header=file.readline() for line in file: #read each row - store data in columns temp = line.split(' ') calt_read.append(float(temp[0])) calm_read.append(float(temp[1].rstrip('/n'))) #store values in calibration lists self.cal_time=calt_read self.cal_mass=calm_read #sets MSMS flag for finish_calibrate routine self.MSMS_flag=1 #call finish_MSMScalibrate method to calibrate and plot data in mass domain self.finish_calibrate() #called when "Start New Calibration" is selected from MS cascade def onCalStart(self): #sets MSMS flag for finish_calibrate routine self.MSMS_flag=0 #reset calibration points self.cal_time=[] self.cal_mass=[] #sets (0,0) as default self.cal_time.append(0) self.cal_mass.append(0) #plot data in time domain (reset plot) self.time_domain() #set calibration buttons (add new, finish) to enabled state self.calibrateButton.config(state=NORMAL) self.finishcalButton.config(state=NORMAL) #called when "Start New Calibration" is selected in MSMS mode def onMSMSCalStart(self): #sets MSMS flag for finish_calibrate routine self.MSMS_flag=1 #reset calibration points self.cal_time=[] self.cal_mass=[] #plot data in time domain (reset plot) self.time_domain() #set calibration button (ID parent peak, finish) to enabled state self.parentButton.config(state=NORMAL) self.finishcalButton.config(state=NORMAL) #called when click is made in plotting environment during calibration def on_click(self, event): temp_len=len(self.cal_time) #when the click is made within the plotting window if event.inaxes is not None: #print 'clicked: ', event.xdata, event.ydata self.cal_time.append(event.xdata*1E-6) #sets up dialog box for user to enter mass value self.MassDialog(self.parent) #if click is outside plotting window else: print 'Clicked ouside axes bounds but inside plot window' #disconnect from calibration click event when a new calibration value is set if len(self.cal_time) > temp_len: self.canvas.mpl_disconnect(self.cid) #called during calibration #asks user for mass value associated with time value selected def MassDialog(self, parent): top = self.top = Toplevel(self.parent) Label(top, text="Actual Mass Value:").pack() self.e=Entry(top) self.e.pack(padx=5) #calls DialogOK to set mass value to input b=Button(top, text="OK", command=self.DialogOK) b.pack(pady=5) #called when Smooth button is pressed #asks user for smooth input data def SmoothDialog(self, parent): top = self.top = Toplevel(self.parent) #window length for smoothing function #must be greater than poly value, positive, odd (51) Label(top, text="Window Length:").grid(row=0, column=0, sticky=W, padx=5, pady=5) self.w=Entry(top) self.w.grid(row=0, column=1, padx=5, pady=5) #polynomial order #must be less than window length (3) Label(top, text="Polynomial Order:").grid(row=1, column=0, sticky=W, padx=5, pady=5) self.p=Entry(top) self.p.grid(row=1, column=1, padx=5, pady=5) #calls DialogSmoothOK to set these values b=Button(top, text="OK", command=self.DialogSmoothOK) b.grid(row=2, column=1, padx=5, pady=5) #called from smooth dialog box within smooth routine def DialogSmoothOK(self): #sets user-defined window length self.window=float(self.w.get()) #sets user-defined polynomail order self.poly=float(self.p.get()) self.top.destroy() #called from mass dialog box within calibration routine def DialogOK(self): #sets user-defined mass calibration value to mass cal list self.cal_mass.append(float(self.e.get())) #closes dialog box self.top.destroy() #called from calibration routine #stores user-selected point in cid def calibrate(self): #user selects point self.cid=self.canvas.mpl_connect('button_press_event', self.on_click) #called from MSMS calibration routine #stores user-selected point in cid def calibrate_parent(self): #user selects point self.cid=self.canvas.mpl_connect('button_press_event', self.on_click) self.parentButton.config(state=DISABLED) #called from calibration routine after "finish" button is pressed def finish_calibrate(self): #disables buttons until "start new calibration" is selected self.calibrateButton.config(state=DISABLED) self.finishcalButton.config(state=DISABLED) #allows plotting in mass domain self.massButton.config(state=NORMAL) #for MSMS calibration if self.MSMS_flag==1: #add second calibration time point equal to some percentage of parent peak time self.cal_time.append(self.cal_time[0]*self.MSMS_zero_time) self.cal_mass.append(0) #allows user to save calibration file self.calMenu.entryconfig("Save Calibration File", state=NORMAL) #if MS mode (regular) if self.MSMS_flag==0: #fit quadratic fuction through cal points popt, pcov = curve_fit(func_quad, self.cal_time, self.cal_mass) #convert time to mass self.mass[:] = [popt[0]*(x**2)*(1E10) + popt[1] for x in self.time] #if MSMS mode elif self.MSMS_flag==1: #fit quadratic fuction through cal points popt, pcov = curve_fit(func_lin, self.cal_time, self.cal_mass) #convert time to mass self.mass[:] = [popt[0]*x*(1E10) + popt[1] for x in self.time] #discard all data below 0 mass mass_temp=self.mass int_temp=self.intensity time_temp=self.time self.mass=[] self.intensity=[] self.time=[] for index, mass in enumerate(mass_temp): if mass > 0: self.mass.append(mass) self.intensity.append(int_temp[index]) self.time.append(time_temp[index]) #plots figure in mass domain self.mass_domain() #function for file input def readFile(self, filename): file=open(filename,'r') #there is some extra formatting on the first line - delete this data header=file.readline() header=file.readline() header=file.readline() header=file.readline() header=file.readline() #read each row in the file #list for temporary time data time=[] #list for temporary intensity data intensity=[] for line in file: #check file format: if ',' in line: #read each row - store data in columns temp = line.split(',') time.append(float(temp[0])) intensity.append(float(temp[1].rstrip('/n'))) if '\t' in line: temp = line.split('\t') time.append(float(temp[0])) intensity.append(float(temp[1].rstrip('/n'))) data=[time, intensity] return data #function to label peaks on figure in order to save figure as an image file with labels def label_peaks(self): #ask for peak (on_click_label) self.cid=self.canvas.mpl_connect('button_press_event', self.on_click_label) #remove peak labels when button is selected def delete_labels(self): #deletes peak label arrays self.label_time=[] self.label_mass=[] self.label_intensity=[] #if time flag is set if self.time_mass_flag==0: self.time_domain() elif self.time_mass_flag==1: self.mass_domain() #called when click is made to label peak def on_click_label(self, event): temp_len=len(self.label_intensity) #if time flag is set if self.time_mass_flag==0: #temp_len=len(self.label_time) #when the click is made within the plotting window if event.inaxes is not None: #print 'clicked: ', event.xdata, event.ydata self.label_time.append(event.xdata) self.label_intensity.append(event.ydata) #print 'time: ', self.label_time #print 'intensity: ', self.label_intensity self.time_domain() #if click is outside plotting window else: print 'Clicked ouside axes bounds but inside plot window' #if the mass flag is set elif self.time_mass_flag==1: #temp_len=len(self.label_mass) #when the click is made within the plotting window if event.inaxes is not None: #print 'clicked: ', event.xdata, event.ydata self.label_mass.append(event.xdata) self.label_intensity.append(event.ydata) #print 'mass: ', self.label_mass #print 'intensity: ', self.label_intensity self.mass_domain() #if click is outside plotting window else: print 'Clicked ouside axes bounds but inside plot window' #self.peak_intensity=event.ydata #plot label #disconnect from click event when a new peak label value is set if temp_len < len(self.label_intensity): self.canvas.mpl_disconnect(self.cid) #need to destroy parent before quitting, otherwise python crashes on Windows def quit_destroy(self): self.parent.destroy() self.parent.quit()
class TaskerCanvas(ttk.Frame): """ Displays the map and plot :ivar TaskerGui parent: The parent application :ivar int canvas_width: The width of the canvas :ivar int canvas_height: The height of the canvas :ivar TaskerOrbitPlotter plotter: Manages orbit data and plotting for the canvas :ivar FigureCanvasTkAgg canvas: The canvas :param int width: The desired width of the canvas :param int height: The desired height of the canvas """ def __init__(self, mainframe, width=500, height=500): ttk.Frame.__init__(self, master=mainframe) self.parent = mainframe # Vertical and horizontal scrollbars for canvas vbar = tk.Scrollbar(self, orient='vertical') hbar = tk.Scrollbar(self, orient='horizontal') # Create canvas and put map on it self.canvas_width = width self.canvas_height = height self.plotter = TaskerOrbitPlotter(self) fig = self.plotter.show() t = np.arange(0, 3, .01) self.canvas = FigureCanvasTkAgg(fig, master = mainframe) self.canvas.draw() self.canvas.get_tk_widget().pack(side="top",fill=tk.BOTH,expand=True) def enableZoomIn(self): """ Enables zooming in when clicking on the map and changes the cursor. """ self.zoomInID = self.canvas.mpl_connect('button_press_event', self.onZoomIn) self.master.config(cursor = "cross") def disableZoomIn(self): """ Disables zooming in. Changes cursor back to normal. """ self.canvas.mpl_disconnect(self.zoomInID) self.master.config(cursor = "arrow") def enableZoomOut(self): """ Enables zooming out when clicking on the map and changes the cursor. """ self.zoomOutID = self.canvas.mpl_connect('button_press_event', self.onZoomOut) self.master.config(cursor = "cross") def disableZoomOut(self): """ Disables zooming out. Changes cursor back to normal. """ self.canvas.mpl_disconnect(self.zoomOutID) self.master.config(cursor = "arrow") def onZoomIn(self, event): """ Called when the map is clicked. Zooms in on the quadrant clicked on. """ try: print('%s click: button=%d, x=%d, y=%d, xdata=%f, ydata=%f' % ('double' if event.dblclick else 'single', event.button, event.x, event.y, event.xdata, event.ydata)) except: return self.plotter.zoomIn(event) def onZoomOut(self, event): """ Called when the map is clicked. Zooms out by one zoom level. """ self.plotter.zoomOut(event) def on_resize_parent(self,event): """ Called when app is resized """ #print("parent event size="+str(event.width)+" X "+str(event.height)) self.canvas_width = event.width self.canvas_height = event.height self.canvas.get_tk_widget().config(width=self.canvas_width, height=self.canvas_height) self.show_image() def on_resize_parentx(self,event): """ Called only by Panedwindow to resize in x-dir only. """ ##print("parent event size="+str(event.width)+" X "+str(event.height)) self.canvas_width = event.width self.canvas.get_tk_widget().config(width=self.canvas_width) self.show_image() def event_subscribe(self, obj_ref): """ Subscribes obj_ref to the TaskerGui. :param obj_ref: object to be subscribed to TaskerGui """ self.subscribers.append(obj_ref) def event_publish(self, cmd): """ Publishes an event to all subscribers :param str cmd: Command to be published """ for sub in self.subscribers: sub.event_receive(cmd) def event_receive(self,event): """ Receives an event from a subscription :param event: The event received from a subscription """ pass
class wishbone_gui(tk.Tk): def __init__(self,parent): tk.Tk.__init__(self,parent) self.parent = parent self.initialize() def initialize(self): self.grid() self.vals = None self.currentPlot = None #set up menu bar self.menubar = tk.Menu(self) self.fileMenu = tk.Menu(self.menubar, tearoff=0) self.menubar.add_cascade(label="File", menu=self.fileMenu) self.fileMenu.add_command(label="Load data", command=self.loadData) self.fileMenu.add_command(label="Save data", state='disabled', command=self.saveData) self.fileMenu.add_command(label="Exit Wishbone", command=self.quitWB) self.analysisMenu = tk.Menu(self.menubar, tearoff=0) self.menubar.add_cascade(label="Analysis", menu=self.analysisMenu) self.analysisMenu.add_command(label="Principal component analysis", state='disabled', command=self.runPCA) self.analysisMenu.add_command(label="tSNE", state='disabled', command=self.runTSNE) self.analysisMenu.add_command(label="Diffusion map", state='disabled', command=self.runDM) self.analysisMenu.add_command(label="GSEA", state='disabled', command=self.runGSEA) self.analysisMenu.add_command(label="Wishbone", state='disabled', command=self.runWishbone) self.visMenu = tk.Menu(self.menubar, tearoff=0) self.menubar.add_cascade(label="Visualization", menu=self.visMenu) self.visMenu.add_command(label="Principal component analysis", state='disabled', command=self.plotPCA) self.visMenu.add_command(label="tSNE", state='disabled', command=self.plotTSNE) self.visMenu.add_command(label="Diffusion map", state='disabled', command=self.plotDM) self.visMenu.add_command(label="GSEA Results", state='disabled', command=self.showGSEAResults) self.wishboneMenu = tk.Menu(self) self.visMenu.add_cascade(label="Wishbone", menu=self.wishboneMenu) self.wishboneMenu.add_command(label="On tSNE", state='disabled', command=self.plotWBOnTsne) self.wishboneMenu.add_command(label="Marker trajectory", state='disabled', command=self.plotWBMarkerTrajectory) self.wishboneMenu.add_command(label="Heat map", state='disabled', command=self.plotWBHeatMap) self.visMenu.add_command(label="Gene expression", state='disabled', command=self.plotGeneExpOntSNE) self.visMenu.add_command(label="Set gate", state='disabled', command=self.setGate) self.config(menu=self.menubar) #intro screen tk.Label(self, text=u"Wishbone", font=('Helvetica', 48), fg="black", bg="white", padx=100, pady=50).grid(row=0) tk.Label(self, text=u"To get started, select a data file by clicking File > Load Data", fg="black", bg="white", padx=100, pady=25).grid(row=1) #update self.protocol('WM_DELETE_WINDOW', self.quitWB) self.grid_columnconfigure(0,weight=1) self.resizable(True,True) self.update() self.geometry(self.geometry()) self.focus_force() def loadData(self): self.dataFileName = filedialog.askopenfilename(title='Load data file', initialdir='~/.wishbone/data') if(self.dataFileName != ""): #pop up data options menu self.fileInfo = tk.Toplevel() self.fileInfo.title("Data options") tk.Label(self.fileInfo, text=u"File name: ").grid(column=0, row=0) tk.Label(self.fileInfo, text=self.dataFileName.split('/')[-1]).grid(column=1, row=0) tk.Label(self.fileInfo,text=u"Name:" ,fg="black",bg="white").grid(column=0, row=1) self.fileNameEntryVar = tk.StringVar() tk.Entry(self.fileInfo, textvariable=self.fileNameEntryVar).grid(column=1,row=1) if self.dataFileName.split('.')[len(self.dataFileName.split('.'))-1] == 'fcs': tk.Label(self.fileInfo,text=u"Cofactor:" ,fg="black",bg="white").grid(column=0, row=2) self.cofactorVar = tk.IntVar() self.cofactorVar.set(5) tk.Entry(self.fileInfo, textvariable=self.cofactorVar).grid(column=1,row=2) elif self.dataFileName.split('.')[len(self.dataFileName.split('.'))-1] == 'csv': self.normalizeVar = tk.BooleanVar() tk.Checkbutton(self.fileInfo, text=u"Normalize", variable=self.normalizeVar).grid(column=0, row=2, columnspan=2) tk.Label(self.fileInfo, text=u"The normalize parameter is used for correcting for library size among cells.").grid(column=0, row=3, columnspan=2) tk.Button(self.fileInfo, text="Cancel", command=self.fileInfo.destroy).grid(column=0, row=4) tk.Button(self.fileInfo, text="Load", command=self.processData).grid(column=1, row=4) self.wait_window(self.fileInfo) def processData(self): #clear intro screen for item in self.grid_slaves(): item.grid_forget() #display file name tk.Label(self,text=u"File name: " + self.fileNameEntryVar.get(), fg="black",bg="white").grid(column=0,row=0) #set up canvas for plots self.fig, self.ax = wishbone.wb.get_fig() self.canvas = FigureCanvasTkAgg(self.fig, self) self.canvas.show() self.canvas.get_tk_widget().grid(column=1, row=1, rowspan=10, columnspan=4,sticky='NSEW') tk.Label(self, text=u"Visualizations:", fg='black', bg='white').grid(column=0, row=1) #load data based on input type if self.dataFileName.split('.')[len(self.dataFileName.split('.'))-1] == 'fcs': # mass cytometry data self.scdata = wishbone.wb.SCData.from_fcs(os.path.expanduser(self.dataFileName), cofactor=self.cofactorVar.get()) self.wb = None elif self.dataFileName.split('.')[len(self.dataFileName.split('.'))-1] == 'csv': # sc-seq data self.scdata = wishbone.wb.SCData.from_csv(os.path.expanduser(self.dataFileName), data_type='sc-seq', normalize=self.normalizeVar.get()) self.wb = None else: self.wb = wishbone.wb.Wishbone.load(self.dataFileName) self.scdata = self.wb.scdata #set up buttons based on data type if self.scdata.data_type == 'sc-seq': self.PCAButton = tk.Button(self, text=u"PCA", state='disabled', command=self.plotPCA) self.PCAButton.grid(column=0, row=2) self.tSNEButton = tk.Button(self, text=u"tSNE", state='disabled', command=self.plotTSNE) self.tSNEButton.grid(column=0, row=3) self.DMButton = tk.Button(self, text=u"Diffusion map", state='disabled', command=self.plotDM) self.DMButton.grid(column=0, row=4) self.GSEAButton = tk.Button(self, text=u"GSEA Results", state='disabled', command=self.showGSEAResults) self.GSEAButton.grid(column=0, row=5) self.WBButton = tk.Button(self, text=u"Wishbone", state='disabled', command=self.plotWBOnTsne) self.WBButton.grid(column=0, row=6) self.geneExpButton = tk.Button(self, text=u"Gene expression", state='disabled', command=self.plotGeneExpOntSNE) self.geneExpButton.grid(column=0, row=7) self.setGateButton = tk.Button(self, text=u"Set gate", state='disabled', command=self.setGate) self.setGateButton.grid(column=0, row=8) self.saveButton = tk.Button(self, text=u"Save plot", state='disabled', command=self.savePlot) self.saveButton.grid(column = 4, row=0) self.diff_component = tk.StringVar() self.diff_component.set('Component 1') self.component_menu = tk.OptionMenu(self, self.diff_component, 'Component 1', 'Component 2', 'Component 3', 'Component 4', 'Component 5', 'Component 6', 'Component 7', 'Component 8', 'Component 9') self.component_menu.config(state='disabled') self.component_menu.grid(row=0, column=2) self.updateButton = tk.Button(self, text=u"Update component", command=self.updateComponent, state='disabled') self.updateButton.grid(column=3, row=0) #enable buttons based on current state of scdata object if self.scdata.pca: self.analysisMenu.entryconfig(1, state='normal') self.visMenu.entryconfig(0, state='normal') self.PCAButton.config(state='normal') if isinstance(self.scdata.tsne, pd.DataFrame): self.analysisMenu.entryconfig(2, state='normal') self.visMenu.entryconfig(1, state='normal') self.visMenu.entryconfig(5, state='normal') self.tSNEButton.config(state='normal') self.geneExpButton.config(state='normal') if isinstance(self.scdata.diffusion_eigenvectors, pd.DataFrame): self.analysisMenu.entryconfig(3, state='normal') self.analysisMenu.entryconfig(4, state='normal') self.visMenu.entryconfig(2, state='normal') self.DMButton.config(state='normal') else: self.tSNEButton = tk.Button(self, text=u"tSNE", state='disabled', command=self.plotTSNE) self.tSNEButton.grid(column=0, row=2) self.DMButton = tk.Button(self, text=u"Diffusion map", state='disabled', command=self.plotDM) self.DMButton.grid(column=0, row=3) self.WBButton = tk.Button(self, text=u"Wishbone", state='disabled', command=self.plotWBOnTsne) self.WBButton.grid(column=0, row=4) self.geneExpButton = tk.Button(self, text=u"Gene expression", state='disabled', command=self.plotGeneExpOntSNE) self.geneExpButton.grid(column=0, row=5) self.setGateButton = tk.Button(self, text=u"Set gate", state='disabled', command=self.setGate) self.setGateButton.grid(column=0, row=6) self.saveButton = tk.Button(self, text=u"Save plot", state='disabled', command=self.savePlot) self.saveButton.grid(column = 4, row=0) self.analysisMenu.delete(0) self.analysisMenu.delete(2) self.visMenu.delete(0) self.visMenu.delete(2) self.analysisMenu.entryconfig(1, state='normal') #enable buttons based on current state of scdata object if isinstance(self.scdata.tsne, pd.DataFrame): self.visMenu.entryconfig(0, state='normal') self.visMenu.entryconfig(3, state='normal') self.tSNEButton.config(state='normal') self.geneExpButton.config(state='normal') if isinstance(self.scdata.diffusion_eigenvectors, pd.DataFrame): self.analysisMenu.entryconfig(2, state='normal') self.visMenu.entryconfig(1, state='normal') self.DMButton.config(state='normal') #enable buttons self.analysisMenu.entryconfig(0, state='normal') self.fileMenu.entryconfig(1, state='normal') if self.wb: if isinstance(self.wb.trajectory, pd.Series): self.wishboneMenu.entryconfig(0, state='normal') self.wishboneMenu.entryconfig(1, state='normal') self.wishboneMenu.entryconfig(2, state='normal') self.WBButton.config(state='normal') #get genes self.genes = self.scdata.data.columns.values self.gates = {} self.geometry('800x550') #destroy pop up menu self.fileInfo.destroy() def saveData(self): pickleFileName = filedialog.asksaveasfilename(title='Save Wishbone Data', defaultextension='.p', initialfile=self.fileNameEntryVar.get()) if pickleFileName != None: if self.wb != None: self.wb.save(pickleFileName) else: self.scdata.save_as_wishbone(pickleFileName) def runPCA(self): self.scdata.run_pca() #enable buttons self.analysisMenu.entryconfig(1, state='normal') self.visMenu.entryconfig(0, state='normal') self.PCAButton.config(state='normal') def runTSNE(self): #pop up for # components self.tsneOptions = tk.Toplevel() self.tsneOptions.title("tSNE options") if self.scdata.data_type == 'sc-seq': tk.Label(self.tsneOptions,text=u"Number of components:" ,fg="black",bg="white").grid(column=0, row=0) self.nCompVar = tk.IntVar() self.nCompVar.set(15) tk.Entry(self.tsneOptions, textvariable=self.nCompVar).grid(column=1,row=0) tk.Label(self.tsneOptions,text=u"Perplexity:" ,fg="black",bg="white").grid(column=0, row=1) self.perplexityVar = tk.IntVar() self.perplexityVar.set(30) tk.Entry(self.tsneOptions, textvariable=self.perplexityVar).grid(column=1,row=1) tk.Button(self.tsneOptions, text="Run", command=self._runTSNE).grid(column=1, row=2) tk.Button(self.tsneOptions, text="Cancel", command=self.tsneOptions.destroy).grid(column=0, row=2) self.wait_window(self.tsneOptions) def _runTSNE(self): if self.scdata.data_type == 'sc-seq': self.scdata.run_tsne(n_components=self.nCompVar.get(), perplexity=self.perplexityVar.get()) else: self.scdata.run_tsne(n_components=None, perplexity=self.perplexityVar.get()) self.gates = {} #enable buttons if self.scdata.data_type == 'sc-seq': self.analysisMenu.entryconfig(2, state='normal') self.visMenu.entryconfig(1, state='normal') self.visMenu.entryconfig(5, state='normal') else: self.visMenu.entryconfig(0, state='normal') self.visMenu.entryconfig(3, state='normal') self.tSNEButton.config(state='normal') self.geneExpButton.config(state='normal') self.tsneOptions.destroy() def runDM(self): self.scdata.run_diffusion_map() #enable buttons if self.scdata.data_type == 'sc-seq': self.analysisMenu.entryconfig(3, state='normal') self.analysisMenu.entryconfig(4, state='normal') self.visMenu.entryconfig(2, state='normal') else: self.analysisMenu.entryconfig(2, state='normal') self.visMenu.entryconfig(1, state='normal') self.DMButton.config(state='normal') def runGSEA(self): self.GSEAFileName = filedialog.askopenfilename(title='Select gmt File', initialdir='~/.wishbone/tools') if self.GSEAFileName != "": self.scdata.run_diffusion_map_correlations() self.scdata.data.columns = self.scdata.data.columns.str.upper() self.outputPrefix = filedialog.asksaveasfilename(title='Input file prefix for saving output', initialdir='~/.wishbone/gsea') if 'mouse' in self.GSEAFileName: gmt_file_type = 'mouse' else: gmt_file_type = 'human' self.reports = self.scdata.run_gsea(output_stem= os.path.expanduser(self.outputPrefix), gmt_file=(gmt_file_type, self.GSEAFileName.split('/')[-1])) #enable buttons self.visMenu.entryconfig(3, state='normal') self.GSEAButton.config(state='normal') def runWishbone(self): #popup menu for wishbone options self.wbOptions = tk.Toplevel() self.wbOptions.title("Wishbone Options") #s tk.Label(self.wbOptions,text=u"Start cell:",fg="black",bg="white").grid(column=0,row=0) self.start = tk.StringVar() tk.Entry(self.wbOptions, textvariable=self.start).grid(column=1,row=0) if(len(self.gates) > 0): self.cell_gate = tk.StringVar() self.cell_gate.set('Use cell gate') self.gate_menu = tk.OptionMenu(self.wbOptions, self.cell_gate, *list(self.gates.keys())) self.gate_menu.grid(row=0, column=2) #k tk.Label(self.wbOptions,text=u"k:",fg="black",bg="white").grid(column=0,row=1) self.k = tk.IntVar() tk.Entry(self.wbOptions, textvariable=self.k).grid(column=1,row=1) self.k.set(15) #components list tk.Label(self.wbOptions, text=u"Components list:", fg='black', bg='white').grid(column=0, row=2) self.compList = tk.StringVar() tk.Entry(self.wbOptions, textvariable=self.compList).grid(column=1, row=2) self.compList.set("1, 2, 3") #num waypoints tk.Label(self.wbOptions, text=u"Number of waypoints:", fg='black', bg='white').grid(column=0, row=3) self.numWaypoints = tk.IntVar() tk.Entry(self.wbOptions, textvariable=self.numWaypoints).grid(column=1, row=3) self.numWaypoints.set(250) #branch self.branch = tk.BooleanVar() self.branch.set(True) tk.Checkbutton(self.wbOptions, text=u"Branch", variable=self.branch).grid(column=0, row=4, columnspan=2) tk.Button(self.wbOptions, text="Run", command=self._runWishbone).grid(column=1, row=5) tk.Button(self.wbOptions, text="Cancel", command=self.wbOptions.destroy).grid(column=0, row=5) self.wait_window(self.wbOptions) def _runWishbone(self): self.wb = wishbone.wb.Wishbone(self.scdata) if self.cell_gate.get() == 'Use cell gate': self.wb.run_wishbone(start_cell=self.start.get(), k=self.k.get(), components_list=[int(comp) for comp in self.compList.get().split(',')], num_waypoints=self.numWaypoints.get()) else: #randomly select start cell in gate print('Using cell gate:') print(self.cell_gate.get()) start_cell = random.sample(list(self.gates[self.cell_gate.get()]), 1)[0] print(start_cell) self.wb.run_wishbone(start_cell=start_cell, k=self.k.get(), components_list=[int(comp) for comp in self.compList.get().split(',')], num_waypoints=self.numWaypoints.get()) #enable buttons self.wishboneMenu.entryconfig(0, state='normal') self.wishboneMenu.entryconfig(1, state='normal') self.wishboneMenu.entryconfig(2, state='normal') self.WBButton.config(state='normal') self.wbOptions.destroy() def plotPCA(self): self.saveButton.config(state='normal') self.setGateButton.config(state='disabled') if self.scdata.data_type == 'sc-seq': self.component_menu.config(state='disabled') self.updateButton.config(state='disabled') self.visMenu.entryconfig(6, state='disabled') else: self.visMenu.entryconfig(4, state='disabled') #pop up for # components self.PCAOptions = tk.Toplevel() self.PCAOptions.title("PCA Plot Options") tk.Label(self.PCAOptions,text=u"Max variance explained (ylim):",fg="black",bg="white").grid(column=0, row=0) self.yLimVar = tk.DoubleVar() self.yLimVar.set(round(self.scdata.pca['eigenvalues'][0][0], 2)) tk.Entry(self.PCAOptions, textvariable=self.yLimVar).grid(column=1,row=0) tk.Label(self.PCAOptions, text=u"Number of components:", fg='black', bg='white').grid(column=0, row=1) self.compVar = tk.IntVar() self.compVar.set(15) tk.Entry(self.PCAOptions, textvariable=self.compVar).grid(column=1, row=1) tk.Button(self.PCAOptions, text="Plot", command=self._plotPCA).grid(column=1, row=2) tk.Button(self.PCAOptions, text="Cancel", command=self.PCAOptions.destroy).grid(column=0, row=2) self.wait_window(self.PCAOptions) def _plotPCA(self): self.resetCanvas() self.fig, self.ax = self.scdata.plot_pca_variance_explained(ylim=(0, self.yLimVar.get()), n_components=self.compVar.get()) self.canvas = FigureCanvasTkAgg(self.fig, self) self.canvas.show() self.canvas.get_tk_widget().grid(column=1, row=1, rowspan=10, columnspan=4,sticky='NW') self.currentPlot = 'pca' #enable buttons self.saveButton.config(state='normal') self.PCAOptions.destroy() def plotTSNE(self): self.saveButton.config(state='normal') self.setGateButton.config(state='normal') if self.scdata.data_type == 'sc-seq': self.component_menu.config(state='disabled') self.updateButton.config(state='disabled') self.visMenu.entryconfig(6, state='normal') else: self.visMenu.entryconfig(4, state='normal') self.resetCanvas() self.fig, self.ax = self.scdata.plot_tsne() self.canvas = FigureCanvasTkAgg(self.fig, self) self.canvas.show() self.canvas.get_tk_widget().grid(column=1, row=1, rowspan=10, columnspan=4,sticky='NW') self.currentPlot = 'tsne' def plotDM(self): self.saveButton.config(state='normal') self.setGateButton.config(state='disabled') if self.scdata.data_type == 'sc-seq': self.component_menu.config(state='disabled') self.updateButton.config(state='disabled') self.visMenu.entryconfig(6, state='disabled') else: self.visMenu.entryconfig(4, state='disabled') self.geometry('950x550') self.resetCanvas() self.fig, self.ax = self.scdata.plot_diffusion_components() self.canvas = FigureCanvasTkAgg(self.fig, self) self.canvas.show() self.canvas.get_tk_widget().grid(column=1, row=1, rowspan=10, columnspan=4,sticky='W') self.currentPlot = 'dm_components' def showGSEAResults(self): self.saveButton.config(state='disabled') self.component_menu.config(state='normal') self.updateButton.config(state='normal') self.setGateButton.config(state='disabled') self.visMenu.entryconfig(6, state='disabled') self.resetCanvas() self.canvas = tk.Canvas(self, width=600, height=300) self.canvas.grid(column=1, row=1, rowspan=17, columnspan=4) self.outputText(1) self.currentPlot = 'GSEA_result_'+self.diff_component.get() def updateComponent(self): self.resetCanvas() self.canvas = tk.Canvas(self, width=600, height=300) self.canvas.grid(column=1, row=1, rowspan=17, columnspan=4,sticky='NSEW') self.outputText(int(self.diff_component.get().split(' ')[-1])) self.currentPlot = 'GSEA_result_'+self.diff_component.get() def outputText(self, diff_component): pos_text = str(self.reports[diff_component]['pos']).split('\n') pos_text = pos_text[1:len(pos_text)-1] pos_text = '\n'.join(pos_text) neg_text = str(self.reports[diff_component]['neg']).split('\n') neg_text = neg_text[1:len(neg_text)-1] neg_text = '\n'.join(neg_text) self.canvas.create_text(5, 5, anchor='nw', text='Positive correlations:\n\n', font=('Helvetica', 16, 'bold')) self.canvas.create_text(5, 50, anchor='nw', text=pos_text) self.canvas.create_text(5, 150, anchor='nw', text='Negative correlations:\n\n', font=('Helvetica', 16, 'bold')) self.canvas.create_text(5, 200, anchor='nw', text=neg_text) def plotWBOnTsne(self): self.saveButton.config(state='normal') self.setGateButton.config(state='disabled') if self.scdata.data_type == 'sc-seq': self.component_menu.config(state='disabled') self.updateButton.config(state='disabled') self.visMenu.entryconfig(6, state='disabled') else: self.visMenu.entryconfig(4, state='disabled') self.resetCanvas() self.fig, self.ax = self.wb.plot_wishbone_on_tsne() self.canvas = FigureCanvasTkAgg(self.fig, self) self.canvas.show() self.canvas.get_tk_widget().grid(column=1, row=1, rowspan=10, columnspan=4) self.currentPlot = 'wishbone_on_tsne' def plotWBMarkerTrajectory(self): self.getGeneSelection() if len(self.selectedGenes) < 1: print('Error: must select at least one gene') else: self.saveButton.config(state='normal') self.setGateButton.config(state='disabled') if self.scdata.data_type == 'sc-seq': self.component_menu.config(state='disabled') self.updateButton.config(state='disabled') self.visMenu.entryconfig(6, state='disabled') else: self.visMenu.entryconfig(4, state='disabled') self.resetCanvas() self.vals, self.fig, self.ax = self.wb.plot_marker_trajectory(self.selectedGenes) self.fig.set_size_inches(10, 4, forward=True) self.fig.tight_layout() self.fig.subplots_adjust(right=0.8) self.canvas = FigureCanvasTkAgg(self.fig, self) self.canvas.show() self.canvas.get_tk_widget().grid(column=1, row=1, rowspan=10, columnspan=5, sticky='W') self.currentPlot = 'wishbone_marker_trajectory' self.geometry('1050x550') #enable buttons self.wishboneMenu.entryconfig(2, state='normal') def plotWBHeatMap(self): self.getGeneSelection() if len(self.selectedGenes) < 1: print('Error: must select at least one gene') else: self.saveButton.config(state='normal') self.setGateButton.config(state='disabled') if self.scdata.data_type == 'sc-seq': self.component_menu.config(state='disabled') self.updateButton.config(state='disabled') self.visMenu.entryconfig(6, state='disabled') else: self.visMenu.entryconfig(4, state='disabled') self.resetCanvas() self.vals, self.fig, self.ax = self.wb.plot_marker_trajectory(self.selectedGenes) self.fig, self.ax = self.wb.plot_marker_heatmap(self.vals) self.fig.set_size_inches(10, 4, forward=True) self.fig.tight_layout() self.canvas = FigureCanvasTkAgg(self.fig, self) self.canvas.show() self.canvas.get_tk_widget().grid(column=1, row=1, rowspan=10, columnspan=5, sticky='W') self.currentPlot = 'wishbone_marker_heatmap' def plotGeneExpOntSNE(self): self.getGeneSelection() if len(self.selectedGenes) < 1: print('Error: must select at least one gene') else: self.saveButton.config(state='normal') self.setGateButton.config(state='disabled') if self.scdata.data_type == 'sc-seq': self.component_menu.config(state='disabled') self.updateButton.config(state='disabled') self.visMenu.entryconfig(6, state='disabled') else: self.visMenu.entryconfig(4, state='disabled') self.resetCanvas() self.fig, self.ax = self.scdata.plot_gene_expression(self.selectedGenes) self.canvas = FigureCanvasTkAgg(self.fig, self) self.canvas.show() self.canvas.get_tk_widget().grid(column=1, row=1, rowspan=10, columnspan=4,sticky='W') self.currentPlot = 'gene_expression_tsne' self.geometry('950x550') def getGeneSelection(self): #popup menu to get selected genes self.geneSelection = tk.Toplevel() self.geneSelection.title("Select Genes") tk.Label(self.geneSelection,text=u"Genes:",fg="black",bg="white").grid(row=0) self.geneInput = wishbone.autocomplete_entry.AutocompleteEntry(self.genes.tolist(), self.geneSelection, listboxLength=6) self.geneInput.grid(row=1) self.geneInput.bind('<Return>', self.AddToSelected) self.geneSelectBox = tk.Listbox(self.geneSelection, selectmode=tk.EXTENDED) self.geneSelectBox.grid(row=2, rowspan=10) self.geneSelectBox.bind('<BackSpace>', self.DeleteSelected) self.selectedGenes = [] tk.Button(self.geneSelection, text="Use selected genes", command=self.geneSelection.destroy).grid(row=12) tk.Button(self.geneSelection, text="Cancel", command=self.cancelGeneSelection).grid(row=13) self.wait_window(self.geneSelection) def cancelGeneSelection(self): self.selectedGenes = [] self.geneSelection.destroy() def AddToSelected(self, event): self.selectedGenes.append(self.geneInput.get()) self.geneSelectBox.insert(tk.END, self.selectedGenes[len(self.selectedGenes)-1]) def DeleteSelected(self, event): selected = self.geneSelectBox.curselection() pos = 0 for i in selected: idx = int(i) - pos self.geneSelectBox.delete( idx,idx ) self.selectedGenes = self.selectedGenes[:idx] + self.selectedGenes[idx+1:] pos = pos + 1 def savePlot(self): self.plotFileName = filedialog.asksaveasfilename(title='Save Plot', defaultextension='.png', initialfile=self.fileNameEntryVar.get()+"_"+self.currentPlot) if self.plotFileName != None: self.fig.savefig(self.plotFileName) def setGate(self): #pop up for gate name self.gateOptions = tk.Toplevel() self.gateOptions.title("Create gate for start cells") tk.Label(self.gateOptions,text=u"Gate name:" ,fg="black",bg="white").grid(column=0, row=0) self.gateName = tk.StringVar() self.gateName.set('Gate ' + str(len(self.gates) + 1)) tk.Entry(self.gateOptions, textvariable=self.gateName).grid(column=1,row=0) tk.Button(self.gateOptions, text="Select gate", command=self._setGate).grid(column=1, row=1) tk.Button(self.gateOptions, text="Cancel", command=self.gateOptions.destroy).grid(column=0, row=1) self.wait_window(self.gateOptions) def _setGate(self): self.gateOptions.destroy() self.buttonPress = self.canvas.mpl_connect('button_press_event', self._startGate) self.buttonRelease = self.canvas.mpl_connect('button_release_event', self._endGate) self.canvas.get_tk_widget().config(cursor='plus') def _startGate(self, event): self.start_x = event.xdata self.start_y = event.ydata def _endGate(self, event): #draw gate rectangle start_x = self.start_x if self.start_x < event.xdata else event.xdata start_y = self.start_y if self.start_y < event.ydata else event.ydata width = np.absolute(event.xdata-self.start_x) height = np.absolute(event.ydata-self.start_y) rect = Rectangle((start_x, start_y), width, height, fill=False, ec='black', alpha=1, lw=2) self.ax.add_patch(rect) self.canvas.draw() #disable mouse events self.canvas.mpl_disconnect(self.buttonPress) self.canvas.mpl_disconnect(self.buttonRelease) self.canvas.get_tk_widget().config(cursor='arrow') #save cell gate gate = Path([[start_x, start_y], [start_x + width, start_y], [start_x + width, start_y + height], [start_x, start_y + height], [start_x, start_y]]) gated_cells = self.scdata.tsne.index[gate.contains_points(self.scdata.tsne)] self.gates[self.gateName.get()] = gated_cells #replot tSNE w gate colored self.fig.clf() plt.scatter(self.scdata.tsne['x'], self.scdata.tsne['y'], s=10, edgecolors='none', color='lightgrey') plt.scatter(self.scdata.tsne.ix[gated_cells, 'x'], self.scdata.tsne.ix[gated_cells, 'y'], s=10, edgecolors='none') self.canvas.draw() self.setGateButton.config(state='disabled') self.visMenu.entryconfig(6, state='disabled') def resetCanvas(self): self.fig.clf() if type(self.canvas) is FigureCanvasTkAgg: for item in self.canvas.get_tk_widget().find_all(): self.canvas.get_tk_widget().delete(item) else: for item in self.canvas.find_all(): self.canvas.delete(item) def quitWB(self): self.quit() self.destroy()
class labTOF(Frame): #define parent window def __init__(self, parent): #from Tkinter frame: #Frame.__init__(self, parent, background="white") #from ttk frame: Frame.__init__(self, parent) #save a reference to the parent widget self.parent = parent #delegate the creation of the user interface to the initUI() method #self.parent.title("Lab TOF") #self.pack(fill=BOTH, expand=1) self.fig = Figure(figsize=(12, 8), dpi=100) #fig = Figure(facecolor='white', edgecolor='white') self.fig.subplots_adjust(bottom=0.15, left=0.13, right=0.95, top=0.95) self.canvas = FigureCanvasTkAgg(self.fig, self) self.toolbar = NavigationToolbar2TkAgg(self.canvas, self) self.toolbar.pack(side=TOP) #canvas.get_tk_widget().pack(side=BOTTOM, fill=BOTH, expand=True) #frame for buttons (at bottom) #buttons are arranged on grid, not pack self.frame_button = Frame(self) self.frame_button.pack(side='bottom', fill=BOTH, expand=1) #list containing time domain values self.time = [] #list containing mass domain values self.mass = [] #flag to indicate whether time or mass is displayed #time flag=0 #mass flag=1 self.time_mass_flag = 0 #flag to indicate MS or MSMS calibration #MS = 0 #MSMS = 1 self.MSMS_flag = -99 #list containing intensity values self.intensity = [] #list containing calibration values self.cal_time = [] self.cal_mass = [] self.label_mass = [] self.label_time = [] self.label_intensity = [] self.label_size = float(10) self.label_format = "%.0f" self.digits_after = 0 self.label_offset = 0.0 #calibration point id self.cid = -99 self.initUI() #zero mass corresponds to ~40% of parent mass time self.MSMS_zero_time = 0.40838 #0.4150#0.4082 #container for other widgets def initUI(self): #set the title of the window using the title() method self.parent.title("Lab TOF") #apply a theme to widget self.style = Style() self.style.theme_use('default') #the Frame widget (accessed via the delf attribute) in the root window #it is expanded in both directions #CHANGE all pack to grid layout self.pack(fill=BOTH, expand=1) menubar = Menu(self.parent) self.parent.config(menu=menubar) #contains file menu items fileMenu = Menu(menubar, tearoff=0) menubar.add_cascade(label="File", menu=fileMenu) fileMenu.add_command(label="Open", command=self.onOpen) fileMenu.add_command(label="Export Data (ASCII file)", command=self.onExport) fileMenu.add_command(label="Quit", command=self.quit_destroy) #contains calibration menu items #"self" is needed to access items later (to change state) #default state for save calibration is disabled #state is enabled once a file is loaded self.calMenu = Menu(menubar, tearoff=0) menubar.add_cascade(label="Calibration", menu=self.calMenu) self.MScalMenu = Menu(menubar, tearoff=0) self.calMenu.add_cascade(label="MS Calibration", menu=self.MScalMenu, state=DISABLED) self.MScalMenu.add_command( label="Start New Calibration: include (0,0)", command=self.onCalStartInclude) self.MScalMenu.add_command( label="Start New Calibration: exclude (0,0)", command=self.onCalStartExclude) self.MScalMenu.add_command(label="Open Calibration File", command=self.onOpenCal) self.MSMScalMenu = Menu(menubar, tearoff=0) self.calMenu.add_cascade(label="MSMS Calibration", menu=self.MSMScalMenu, state=DISABLED) self.MSMScalMenu.add_command(label="Start New Calibration", command=self.onMSMSCalStart) self.MSMScalMenu.add_command(label="Open Calibration File", command=self.onMSMSOpenCal) #default state for save calibration is disabled #state is enabled once a calibration is defined self.calMenu.add_command(label="Save Calibration File", command=self.onSaveCal, state=DISABLED) #generate figure (no data at first) self.generate_figure([0, 1], [[], []], ' ') #calibration Label(self.frame_button, text="Calibration").grid(row=0, column=0, padx=5, pady=5, sticky=W) #create instance of the button widget #command specifies the method to be called #identify parent peak in MSMS self.parentButton = Button(self.frame_button, text="Identify Parent Peak", command=self.calibrate_parent, state=DISABLED) #self.parentButton.pack(side=LEFT, padx=5, pady=5) self.parentButton.grid(row=0, column=1, padx=5, pady=5, sticky=W) #identify half-parent peak in MSMS #self.halfparentButton = Button(self.frame_button, text="Identify Half-Parent Peak", command = self.calibrate_halfparent, state=DISABLED) #self.parentButton.pack(side=LEFT, padx=5, pady=5) #self.halfparentButton.grid(row=0, column=2, padx=5, pady=5, sticky=W) #add calibration point self.calibrateButton = Button(self.frame_button, text="Add Calibration Point", command=self.calibrate, state=DISABLED) #self.calibrateButton.pack(side=LEFT, padx=5, pady=5) self.calibrateButton.grid(row=0, column=2, padx=5, pady=5, sticky=W) #set MSMS calibration constant MSMS self.constantButton = Button(self.frame_button, text="Set MSMS Constant", command=self.calibration_constant, state=DISABLED) #self.parentButton.pack(side=LEFT, padx=5, pady=5) self.constantButton.grid(row=0, column=3, padx=5, pady=5, sticky=W) #finish calibration and convert to mass domain self.finishcalButton = Button(self.frame_button, text="Finish Calibration", command=self.finish_calibrate, state=DISABLED) #self.finishcalButton.pack(side=LEFT, padx=5, pady=5) self.finishcalButton.grid(row=0, column=4, padx=5, pady=5, sticky=W) Label(self.frame_button, text="Label Spectrum").grid(row=1, column=0, padx=5, pady=5, sticky=W) #label peaks in figure self.labelButton = Button(self.frame_button, text="Label Peak", command=self.label_peaks, state=DISABLED) #self.labelButton.pack(side=RIGHT, padx=5, pady=5) self.labelButton.grid(row=1, column=1, padx=5, pady=5, sticky=W) self.deletelabelButton = Button(self.frame_button, text="Remove Labels", command=self.delete_labels, state=DISABLED) #self.deletelabelButton.pack(side=RIGHT, padx=5, pady=5) self.deletelabelButton.grid(row=1, column=2, padx=5, pady=5, sticky=W) self.formatelabelButton = Button(self.frame_button, text="Format Label", command=self.format_labels) self.formatelabelButton.grid(row=1, column=3, padx=5, pady=5, sticky=W) Label(self.frame_button, text="Smooth Spectrum").grid(row=2, column=0, padx=5, pady=5, sticky=W) #smooth spectrum self.smoothButton = Button(self.frame_button, text="Smooth", command=self.smooth_data, state=DISABLED) #self.smoothButton.pack(side=RIGHT, padx=5, pady=5) self.smoothButton.grid(row=2, column=1, padx=5, pady=5, sticky=W) Label(self.frame_button, text="Convert Domain").grid(row=3, column=0, padx=5, pady=5, sticky=W) #convert to time domain self.timeButton = Button(self.frame_button, text="Time Domain", command=self.time_domain) #timeButton.pack(side=RIGHT, padx=5, pady=5) self.timeButton.grid(row=3, column=1, padx=5, pady=5, sticky=W) #convert to mass domain self.massButton = Button(self.frame_button, text="Mass Domain", command=self.mass_domain) #self.massButton.pack(side=RIGHT, padx=5, pady=5) self.massButton.grid(row=3, column=2, padx=5, pady=5, sticky=W) self.quitButton = Button(self.frame_button, text="Quit", command=self.quit_destroy) #quitButton.place(x=50, y=50) #quitButton.pack(side=RIGHT, padx=5, pady=5) self.quitButton.grid(row=3, column=6, padx=5, pady=5, sticky=E) #plot figure def generate_figure(self, data, label, xaxis_title): #clear figure plt.clf() ax = self.fig.add_subplot(111) #clear axis ax.cla() spectrum = ax.plot(data[0], data[1], color='k') ax.set_xlabel(xaxis_title) #, fontsize=6) ax.set_ylabel('intensity (V)') #, fontsize=6) #ax.tick_params('both', length=4, width=1, which='major') #ax.tick_params('both', length=2, width=0.5, which='minor') #for tick in ax.xaxis.get_major_ticks(): #tick.label.set_fontsize(6) #for tick in ax.yaxis.get_major_ticks(): #tick.label.set_fontsize(6) #add peak labels for index, label_x in enumerate(label[0]): #ax.text(0.94*label_x, 1.05*label[1][index], str("%.1f" % label_x)) #ax.annotate(str("%.1f" % label_x), xy=(label_x, label[1][index]), xytext=(1.1*label_x, 1.1*label[1][index]), arrowprops=dict(facecolor='black', shrink=0.1),) #ax.annotate(str("%.1f" % label_x), xy=(label_x, label[1][index]), xytext=(1.0*label_x, 1.2*label[1][index]),) #I could separate format, offset, precision into an array for each label an1 = ax.annotate(str( self.label_format % round(label_x + self.label_offset, int(self.digits_after))), xy=(label_x, label[1][index]), xytext=(label_x, label[1][index]), fontsize=self.label_size) an1.draggable(state=True) #need to retrieve new label position and update position array #self.label_mass[index]=(event.xdata) #self.label_intensity[index]=(event.ydata) #self.label_time[index]=(event.xdata) #remove self.label_time values #set label_peaks to generate figure again with loop that allows peak labeling (datacursor()) - set flag, reset flag in loop ##otherwise label will appear when selecting peaks for calibration #have Tim download mpldatacursor package with pip? #need to figure out how to remove labels ##canvas.delete("all") in function with a redraw function? #datacursor(spectrum, display='multiple', draggable=True, formatter='{x:.1f}'.format, bbox=None) self.canvas.show() #self.canvas.get_tk_widget().pack(side=BOTTOM) self.canvas.get_tk_widget().pack(side=BOTTOM, fill=BOTH, expand=True) #self.can.grid(row=1, column=0, columnspan=6, rowspan=3, padx=5, sticky=E+W+S+N) self.toolbar.update() #self.canvas._tkcanvas.pack(side=TOP) #not sure what this line does, comment out for now #it appears to be necessary for matplotlib grid? self.canvas._tkcanvas.pack(side=TOP, fill=BOTH, expand=True) #def delete_figure(self): #Canvas.delete("all") #.destroy() #open file def onOpen(self): #displays .txt files in browser window #only reads time domain data files ftypes = [('txt files', '*.txt'), ('binary files', '*.trc')] dlg = tkFileDialog.Open(self, filetypes=ftypes) fl = dlg.show() if fl != '': if fl[-3:] == 'trc': self.time, self.intensity = read_lecroy_binary.read_timetrace( fl) #plots data in time domain if self.time_mass_flag == 0: self.time_domain() #plots data in mass domain elif self.time_mass_flag == 1: self.mass_domain() elif fl[-3:] == 'txt': data = self.readFile(fl) self.intensity = data[1] #self.intensity = [x+0.00075 for x in data[1]] if self.time_mass_flag == 0: self.time = data[0] self.time_domain() elif self.time_mass_flag == 1: self.mass = data[0] self.mass_domain() self.labelButton.config(state=NORMAL) self.deletelabelButton.config(state=NORMAL) #allows for smoothing of data self.smoothButton.config(state=NORMAL) #allows for calibration self.calMenu.entryconfig("MS Calibration", state=NORMAL) self.calMenu.entryconfig("MSMS Calibration", state=NORMAL) #export data to txt file def onExport(self): #save header info (date/time, ask user for instrument)? #ask for save file name savefile = tkFileDialog.asksaveasfile(mode='wb', defaultextension=".txt") # asksaveasfile return `None` if dialog closed with "cancel". if savefile is None: return #export mass or time domain data depending on flag (what is plotted) if self.time_mass_flag == 0: x = self.time else: x = self.mass y = self.intensity for i in range(len(x)): savefile.write(str(x[i])) savefile.write(' ') savefile.write(str(y[i])) savefile.write('\r\n') savefile.close() #convert to mass domain #disabled until calibration is defined def mass_domain(self): self.time_mass_flag = 1 self.generate_figure([self.mass, self.intensity], [self.label_mass, self.label_intensity], 'mass (Da)') #convert to time domain def time_domain(self): self.time_mass_flag = 0 #plot in micro seconds self.generate_figure([[1E6 * x for x in self.time], self.intensity], [self.label_time, self.label_intensity], 'time ($\mu$s)') #smooth data def smooth_data(self): #smooth self.time/mass/int variables - permanant #probably not a good idea #purpose of smoothing function: to display smoothed plot self.SmoothDialog(self.parent) #wait for dialog to close self.top.wait_window(self.top) smoothed_signal = scipy.signal.savgol_filter(self.intensity, int(self.window), int(self.poly)) #convert from array to list self.intensity = smoothed_signal.tolist() #print 'intensity: ', len(self.intensity), type(self.intensity) if self.time_mass_flag == 1: #print 'mass: ', len(self.mass), type(self.mass) self.mass_domain() else: #print 'time: ', len(self.time), type(self.time) self.time_domain() #format labels def format_labels(self): self.FormatLabelDialog(self.parent) #wait for dialog to close self.top.wait_window(self.top) self.label_size = float(self.label_size) self.label_format = "%." + self.digits_after + "f" #save calibration file def onSaveCal(self): #ask for save file name savefile = tkFileDialog.asksaveasfile(mode='wb', defaultextension=".txt") if savefile is None: return #write header information? #date/time, file name for i in range(len(self.cal_time)): savefile.write(str(self.cal_time[i])) savefile.write(' ') savefile.write(str(self.cal_mass[i])) #\r\n is the newline character for windows savefile.write('\r\n') savefile.close() #open previous MS (regular) calibration file def onOpenCal(self): #display .txt files in browser window ftypes = [('txt files', '*.txt')] dlg = tkFileDialog.Open(self, filetypes=ftypes) filename = dlg.show() if filename != '': #list with time calibration value calt_read = [] #list with mass calibration value calm_read = [] file = open(filename, 'r') #header=file.readline() for line in file: #read each row - store data in columns temp = line.split(' ') calt_read.append(float(temp[0])) calm_read.append(float(temp[1].rstrip('/n'))) #store values in calibration lists self.cal_time = calt_read self.cal_mass = calm_read #sets MSMS flag for finish_calibrate routine self.MSMS_flag = 0 #call finish_calibrate method to calibrate and plot data in mass domain self.finish_calibrate() #open previous MSMS calibration file def onMSMSOpenCal(self): #display .txt files in browser window ftypes = [('txt files', '*.txt')] dlg = tkFileDialog.Open(self, filetypes=ftypes) filename = dlg.show() if filename != '': #list with time calibration value calt_read = [] #list with mass calibration value calm_read = [] file = open(filename, 'r') #header=file.readline() for line in file: #read each row - store data in columns temp = line.split(' ') calt_read.append(float(temp[0])) calm_read.append(float(temp[1].rstrip('/n'))) #store values in calibration lists self.cal_time = calt_read self.cal_mass = calm_read #sets MSMS flag for finish_calibrate routine self.MSMS_flag = 1 #call finish_MSMScalibrate method to calibrate and plot data in mass domain self.finish_calibrate() #called when "Start New Calibration" is selected from MS cascade def onCalStartInclude(self): #sets MSMS flag for finish_calibrate routine self.MSMS_flag = 0 #reset calibration points self.cal_time = [] self.cal_mass = [] #sets (0,0) as default self.cal_time.append(0) self.cal_mass.append(0) #plot data in time domain (reset plot) self.time_domain() #set calibration buttons (add new, finish) to enabled state self.calibrateButton.config(state=NORMAL) self.finishcalButton.config(state=DISABLED) self.counter = 1 #called when "Start New Calibration" is selected from MS cascade def onCalStartExclude(self): #sets MSMS flag for finish_calibrate routine self.MSMS_flag = 0 #reset calibration points self.cal_time = [] self.cal_mass = [] #sets (0,0) as default #self.cal_time.append(0) #self.cal_mass.append(0) #plot data in time domain (reset plot) self.time_domain() #set calibration buttons (add new, finish) to enabled state self.calibrateButton.config(state=NORMAL) self.finishcalButton.config(state=DISABLED) self.counter = 0 #called when "Start New Calibration" is selected in MSMS mode def onMSMSCalStart(self): #sets MSMS flag for finish_calibrate routine self.MSMS_flag = 1 #reset calibration points self.cal_time = [] self.cal_mass = [] #plot data in time domain (reset plot) self.time_domain() #set calibration button (ID parent peak, finish) to enabled state self.parentButton.config(state=NORMAL) #self.halfparentButton.config(state=DISABLED) self.calibrateButton.config(state=DISABLED) self.constantButton.config(state=NORMAL) self.finishcalButton.config(state=DISABLED) self.counter = 1 #called when click is made in plotting environment during calibration def on_click(self, event): temp_len = len(self.cal_time) #when the click is made within the plotting window if event.inaxes is not None: #print 'clicked: ', event.xdata, event.ydata self.cal_time.append(event.xdata * 1E-6) #sets up dialog box for user to enter mass value self.MassDialog(self.parent) #if click is outside plotting window else: print 'Clicked ouside axes bounds but inside plot window' #disconnect from calibration click event when a new calibration value is set if len(self.cal_time) > temp_len: self.canvas.mpl_disconnect(self.cid) #called during calibration #asks user for mass value associated with time value selected def MassDialog(self, parent): top = self.top = Toplevel(self.parent) Label(top, text="Actual Mass Value:").pack() self.e = Entry(top) self.e.pack(padx=5) #calls DialogOK to set mass value to input b = Button(top, text="OK", command=self.DialogOK) b.pack(pady=5) #called when Smooth button is pressed #asks user for smooth input data def SmoothDialog(self, parent): top = self.top = Toplevel(self.parent) #window length for smoothing function #must be greater than poly value, positive, odd (51) Label(top, text="Window Length:").grid(row=0, column=0, sticky=W, padx=5, pady=5) self.w = Entry(top) self.w.insert(0, '151') self.w.grid(row=0, column=1, padx=5, pady=5) #polynomial order #must be less than window length (3) Label(top, text="Polynomial Order:").grid(row=1, column=0, sticky=W, padx=5, pady=5) self.p = Entry(top) self.p.insert(0, '3') self.p.grid(row=1, column=1, padx=5, pady=5) #calls DialogSmoothOK to set these values b = Button(top, text="OK", command=self.DialogSmoothOK) b.grid(row=2, column=1, padx=5, pady=5) def FormatLabelDialog(self, parent): top = self.top = Toplevel(self.parent) #font size Label(top, text="Font Size:").grid(row=0, column=0, sticky=W, padx=5, pady=5) self.fs = Entry(top) self.fs.insert(0, '10') self.fs.grid(row=0, column=1, padx=5, pady=5) #Precision Label(top, text="Digits After Decimal:").grid(row=1, column=0, sticky=W, padx=5, pady=5) self.dg = Entry(top) self.dg.insert(0, '0') self.dg.grid(row=1, column=1, padx=5, pady=5) #label offset Label(top, text="Label Offset:").grid(row=2, column=0, sticky=W, padx=5, pady=5) self.lo = Entry(top) self.lo.insert(0, '0.0') self.lo.grid(row=2, column=1, padx=5, pady=5) #calls DialogLabelOK to set these values b = Button(top, text="OK", command=self.DialogLabelOK) b.grid(row=3, column=1, padx=5, pady=5) #called from smooth dialog box within smooth routine def DialogLabelOK(self): #sets user-defined font size self.label_size = float(self.fs.get()) #sets user-defined precision self.digits_after = str(self.dg.get()) #sets user-defined label offset self.label_offset = float(self.lo.get()) self.top.destroy() #called from smooth dialog box within smooth routine def DialogSmoothOK(self): #sets user-defined window length self.window = float(self.w.get()) #sets user-defined polynomail order self.poly = float(self.p.get()) self.top.destroy() #called from mass dialog box within calibration routine def DialogOK(self): #sets user-defined mass calibration value to mass cal list self.cal_mass.append(float(self.e.get())) #closes dialog box self.top.destroy() #called from calibration routine #stores user-selected point in cid def calibrate(self): #user selects point self.cid = self.canvas.mpl_connect('button_press_event', self.on_click) self.counter = self.counter + 1 if self.counter > 1: self.finishcalButton.config(state=NORMAL) #called from MSMS calibration routine #stores user-selected point in cid def calibrate_parent(self): #user selects point self.cid = self.canvas.mpl_connect('button_press_event', self.on_click) self.parentButton.config(state=DISABLED) self.finishcalButton.config(state=NORMAL) #self.halfparentButton.config(state=NORMAL) self.calibrateButton.config(state=NORMAL) #called from MSMS calibration routine #stores user-selected point in cid #def calibrate_halfparent(self): #user selects point #self.cid=self.canvas.mpl_connect('button_press_event', self.on_click) #self.halfparentButton.config(state=NORMAL) #self.finishcalButton.config(state=NORMAL) #self.calibrateButton.config(state=NORMAL) #user defined calibration constant def calibration_constant(self): self.ConstantDialog(self.parent) #wait for dialog to close self.top.wait_window(self.top) #recompute time-mass conversion #if len(self.cal_time) > 0: #self.finishcalButton.config(state=NORMAL) def ConstantDialog(self, parent): top = self.top = Toplevel(self.parent) Label(top, text="MSMS Calibration Constant:").grid(row=0, column=0, sticky=W, padx=5, pady=5) self.c = Entry(top) self.c.insert(0, self.MSMS_zero_time) self.c.grid(row=0, column=1, padx=5, pady=5) #calls DialogConstantOK to set these values b = Button(top, text="OK", command=self.DialogConstantOK) b.grid(row=2, column=1, padx=5, pady=5) def DialogConstantOK(self): #sets user-defined calibration constant self.MSMS_zero_time = float(self.c.get()) self.top.destroy() #called from calibration routine after "finish" button is pressed def finish_calibrate(self): #disables buttons until "start new calibration" is selected self.calibrateButton.config(state=DISABLED) self.finishcalButton.config(state=DISABLED) self.parentButton.config(state=DISABLED) #self.halfparentButton.config(state=DISABLED) self.constantButton.config(state=DISABLED) #allows plotting in mass domain self.massButton.config(state=NORMAL) #for MSMS calibration if self.MSMS_flag == 1: #add second calibration time point equal to some percentage of parent peak time if necessary #if cal_time has only one value: if len(self.cal_time) == 1: self.cal_time.append(self.cal_time[0] * self.MSMS_zero_time) self.cal_mass.append(0) #if MS mode (regular) if self.MSMS_flag == 0: #fit quadratic fuction through cal points popt, pcov = curve_fit(func_quad, self.cal_time, self.cal_mass) #convert time to mass #self.mass[:] = [popt[0]*(x**2)*(1E10) + popt[1] for x in self.time] self.mass = [] for x in self.time: if x < 0: self.mass.append(-popt[0] * (x**2) * (1E10) + popt[1]) else: self.mass.append(popt[0] * (x**2) * (1E10) + popt[1]) #if MSMS mode elif self.MSMS_flag == 1: #fit quadratic fuction through cal points popt, pcov = curve_fit(func_lin, self.cal_time, self.cal_mass) #convert time to mass self.mass[:] = [popt[0] * x * (1E10) + popt[1] for x in self.time] #discard all data below 0 mass mass_temp = self.mass int_temp = self.intensity time_temp = self.time self.mass = [] self.intensity = [] self.time = [] for index, mass in enumerate(mass_temp): if mass > 0: self.mass.append(mass) self.intensity.append(int_temp[index]) self.time.append(time_temp[index]) #allows user to save calibration file self.calMenu.entryconfig("Save Calibration File", state=NORMAL) #plots figure in mass domain self.mass_domain() #function for file input def readFile(self, filename): flag_pico = False file = open(filename, 'r') #there is some extra formatting on the first line - delete this data header = file.readline() header = file.readline() #check if Picoscope: if header[1:3] == 'us': flag_pico = True header = file.readline() header = file.readline() header = file.readline() #read each row in the file #list for temporary time data time = [] #list for temporary intensity data intensity = [] for line in file: #check file format: if ',' in line: #read each row - store data in columns temp = line.split(',') if flag_pico: time.append(float(temp[0]) * 1E-6) else: time.append(float(temp[0])) intensity.append(float(temp[1].rstrip('/n'))) if '\t' in line: temp = line.split('\t') if flag_pico: time.append(float(temp[0]) * 1E-6) else: time.append(float(temp[0])) intensity.append(float(temp[1].rstrip('/n'))) data = [time, intensity] return data #function to label peaks on figure in order to save figure as an image file with labels def label_peaks(self): #ask for peak (on_click_label) self.cid = self.canvas.mpl_connect('button_press_event', self.on_click_label) #remove peak labels when button is selected def delete_labels(self): #deletes peak label arrays self.label_time = [] self.label_mass = [] self.label_intensity = [] #if time flag is set if self.time_mass_flag == 0: self.time_domain() elif self.time_mass_flag == 1: self.mass_domain() #called when click is made to label peak def on_click_label(self, event): temp_len = len(self.label_intensity) #if time flag is set if self.time_mass_flag == 0: #temp_len=len(self.label_time) #when the click is made within the plotting window if event.inaxes is not None: #print 'clicked: ', event.xdata, event.ydata self.label_time.append(event.xdata) self.label_intensity.append(event.ydata) #print 'time: ', self.label_time #print 'intensity: ', self.label_intensity self.time_domain() #if click is outside plotting window else: print 'Clicked ouside axes bounds but inside plot window' #if the mass flag is set elif self.time_mass_flag == 1: #temp_len=len(self.label_mass) #when the click is made within the plotting window if event.inaxes is not None: #print 'clicked: ', event.xdata, event.ydata self.label_mass.append(event.xdata) self.label_intensity.append(event.ydata) #print 'mass: ', self.label_mass #print 'intensity: ', self.label_intensity self.mass_domain() #if click is outside plotting window else: print 'Clicked ouside axes bounds but inside plot window' #self.peak_intensity=event.ydata #plot label #disconnect from click event when a new peak label value is set if temp_len < len(self.label_intensity): self.canvas.mpl_disconnect(self.cid) #need to destroy parent before quitting, otherwise python crashes on Windows def quit_destroy(self): self.parent.destroy() self.parent.quit()
class ContTweaker: def __init__(self,iwvlngth,ispectrum,espectrum,oldxspline,oldyspline,outfile): #Initialize input variables for the class self.outfile=outfile self.iwvlngth=iwvlngth self.ispectrum=ispectrum self.espectrum=espectrum #intialize CT.XSPLINE/YSPLINE to the old values at first (will be replaced) self.xspline=oldxspline self.yspline=oldyspline self.oldxspline=oldxspline self.oldyspline=oldyspline #Interpolate cubic spline of the continuum for both "NEW" and old spline self.oldspline=scipy.interpolate.interp1d(oldxspline,oldyspline,kind='cubic') self.spline=scipy.interpolate.interp1d(oldxspline,oldyspline,kind='cubic') #Make the MPL figures appear self.MakePlot() #Add buttons to the bottom of the main TB_CONTFIT widget menu self.popup=Tkinter.Frame() self.popup.grid() But1=Tkinter.Button(self.popup,text='Refresh Plot',command=self.Refresh) But1.grid(column=0,row=0) But3=Tkinter.Button(self.popup,text='Remove Point',command=self.RemovePoint) But3.grid(column=0,row=1) But4=Tkinter.Button(self.popup,text='Add Point',command=self.AddPoint) But4.grid(column=0,row=2) But5=Tkinter.Button(self.popup,text='Reopen Window',command=self.MakePlot) But5.grid(column=0,row=4) But6=Tkinter.Button(self.popup,text='Save Spline',command=self.Save) But6.grid(column=0,row=5) But7=Tkinter.Button(self.popup,text='Exit',command=self.Exit) But7.grid(column=0,row=6) #Toggle between a Continous-editing and single click modes MODES=[("Single",False),("Continuous",True)] self.useContinuous=Tkinter.BooleanVar() self.useContinuous.set(False) ii=1 labelMode=Tkinter.StringVar() labelmode=Tkinter.Label(self.popup,textvariable=labelMode,anchor="w",fg="black") labelmode.grid(column=1, row=0, columnspan=2, sticky='S') labelMode.set(u"Point-editing mode") for text,mode in MODES: b=Tkinter.Radiobutton(self.popup,text=text,variable=self.useContinuous,value=mode) b.grid(column=ii,row=1) ii+=1 if ii==1: b.select() #Wait until the window is closed (i.e. CT.EXIT() is run by clicking EXIT button) self.popup.wait_window() def MakePlot(self): #This function generates the plot window that will be the vidual interface for how #the continuum tweaking is going #Make the Spline from the CURRENT spline values xcont=self.iwvlngth ycont=self.spline(xcont) #Make the spline from the OLD spline values ospectrum=self.oldspline(xcont) #Create a MPL figure self.tweakfig=plt.figure(figsize=(8,8)) #First subplot is for displaying the original fit to the spectrum self.ax1=self.tweakfig.add_subplot(3,1,1) #Second subplot is for displaying the current fit to the spectrum #To help with looking at all regions of the same time, share the X and Y #axes to CT.AX1. self.ax2=self.tweakfig.add_subplot(3,1,2,sharex=self.ax1,sharey=self.ax1) #Third subplot for displaying the continuum-fitted spectrum based on the #current spline. Because this is now 0 and 1ish, only share the X axis #to CT.AX1 self.ax3=self.tweakfig.add_subplot(3,1,3,sharex=self.ax1) #Create the TK window for embedding the figure, set it up, and draw self.TFroot=Tkinter.Tk() self.TFroot.wm_title("Continuum Tweaker") self.TweakPlot=FigureCanvasTkAgg(self.tweakfig,master=self.TFroot) #This toolbar is mainly for zooming. By sharing the x (and y) axes, when zooming in #on one axes, all three will zoom appropriately NavTweakPlot=NavigationToolbar2TkAgg(self.TweakPlot, self.TFroot) self.TweakPlot.get_tk_widget().pack(side=Tkinter.TOP, fill=Tkinter.BOTH, expand=1) #For CT.AX1, plot the original specturm, the error spectrum, and the original continuum fitted self.ax1.plot(self.iwvlngth,self.ispectrum,'k',drawstyle='steps',label='Spectrum') self.ax1.plot(self.iwvlngth,self.espectrum,'g',drawstyle='steps',label='Error') self.ax1.plot(xcont,ycont,'b',label='Continuum') self.ax1.plot(self.oldxspline,self.oldyspline,'or',label='Spline Pts.') #Make a legend self.ax1.legend(ncol=4,frameon=False, loc=9, bbox_to_anchor=(0.5, 1.3)) self.ymin,self.ymax=self.ax1.get_ylim() self.ax1.set_ylabel('Flux\n(original)') #For CT.AX2, plot the spectrum, and the current spline points and the resulting continuum self.ax2.plot(self.iwvlngth,self.ispectrum,'k',drawstyle='steps') self.ax2.plot(xcont,ycont,'b') self.ax2.plot(self.xspline,self.yspline,'or',picker=5)#Picker needed to pick which point self.ax2.set_ylabel('Flux\n(tweaked fit)') #For CT.AX3, plot the spectrum upon dividing by the continuum. Plot the #error spectrum as 1-err and 1+err to reflect how much noise one MIGHT expect #As a rule of thumb, one should aim to have the continuum fluctuations within the error self.ax3.plot(xcont,self.ispectrum/ospectrum,'k',drawstyle='steps') self.ax3.plot(xcont,1.0-self.espectrum/ospectrum,'--g',drawstyle='steps') self.ax3.plot(xcont,1.0+self.espectrum/ospectrum,'--g',drawstyle='steps') self.xmin,self.xmax=self.ax3.get_xlim() self.ax3.plot([self.xmin,self.xmax],[1,1],'--r') self.ax3.plot([self.xmin,self.xmax],[0,0],'--r') #restrict CT.AX3 to only show the normalized range of values self.ax3.set_ylim(-1,2) self.ax3.set_ylabel('Relative flux') self.ax3.set_xlabel('Wavlength') #Draw all the new plotted stuff self.TweakPlot.draw() def Refresh(self,yrefresh=True): #This function takes any modifications that have taken place and updates #the plots accordingly. If YREFRESH=TRUE, this sets the y-axis to the #original scaling. Otherwise keep the current values. #Get the current x values (incase the user has zoomed in using toolbar) xmin,xmax=self.ax2.get_xlim() ymin,ymax=self.ax2.get_ylim() #if YREFRESH=TRUE, set to original values (SELF.YMIN/YMAX) if yrefresh: ymin=self.ymin ymax=self.ymax #Clear CT.AX2/AX3 of all previous information self.ax2.clear() self.ax3.clear() #Remake the spline based on the new XSPLINE/YSPLINE points, and save self.spline=scipy.interpolate.interp1d(self.xspline,self.yspline,kind='cubic') #Generate the continuum based on the new spline xcont=self.iwvlngth ycont=self.spline(xcont) #Update CT.AX1, perserve the x-axis bounds, and return to original y-axis self.ax1.set_ylim(ymin,ymax) self.ax1.set_xlim(xmin,xmax) #Update CT.AX2 by plotting new spline, also perserve the x-axis bounds self.ax2.plot(self.iwvlngth,self.ispectrum,'k',drawstyle='steps') self.ax2.plot(xcont,ycont,'b') self.ax2.plot(self.xspline,self.yspline,'or',picker=5) self.ax2.set_ylabel('Flux\n(tweaked fit)') self.ax2.set_ylim(ymin,ymax) self.ax2.set_xlim(xmin,xmax) #in CT.AX2, Divide out the spectrum&errorspectrum by the continuum, and plot self.ax3.plot(xcont,self.ispectrum/ycont,'k',drawstyle='steps') self.ax3.plot(xcont,1.0-self.espectrum/ycont,'--g',drawstyle='steps') self.ax3.plot(xcont,1.0+self.espectrum/ycont,'--g',drawstyle='steps') self.ax3.set_ylabel('Relative flux') self.ax3.set_xlabel('Wavlength') self.ax3.set_ylim(-1,2) self.ax3.plot([self.xmin,self.xmax],[1,1],'--r') self.ax3.plot([self.xmin,self.xmax],[0,0],'--r') self.ax3.set_xlim(xmin,xmax) #Update plotting window self.TweakPlot.draw() #Function for closing the current MPL event by it's ID, and stop the event loop #It might seem redundant that I have both things, but I intend to have a continous #editing method, which would need the looper. def QuitEdit(self,cid): #Close event ID self.TweakPlot.mpl_disconnect(cid) #Stop event loop self.TweakPlot.stop_event_loop() #Function when "Add Point" button is clicked. def AddPoint(self): #Show Tutorial message for what to do. if usetutorial: tkMessageBox.showinfo("Help Message", "Click where to add point.") #Start mouse click event, and run CT.ClickAdd self.cidbut=self.TweakPlot.mpl_connect('button_press_event',self.ClickAdd) self.TweakPlot.start_event_loop(0) #If use continuous button is on, repeat adding points while self.useContinuous.get(): if usetutorial: tkMessageBox.showinfo("Help Message", "Continuous point addition on. Keep adding points.") try: self.cidbut=self.TweakPlot.mpl_connect('button_press_event',self.ClickAdd) self.TweakPlot.start_event_loop(0) except: self.useContinuous.set(False) #Given a mouse event for adding a point... def ClickAdd(self,event): #Grab the x/y coordiantes of the click, and add to spline self.xspline.append(event.xdata) self.yspline.append(event.ydata) #Sort the spline data to be in order by wavelength self.xspline,self.yspline=SortList(self.xspline,self.yspline) #Refresh the plot with new data, but keep y-axis self.Refresh(yrefresh=False) #Close the MPL event stuff self.QuitEdit(self.cidbut) #Function ro remove a point when "Remove Point" button pressed def RemovePoint(self): #Show tutorial message on what to do if usetutorial: tkMessageBox.showinfo("Help Message", "Click point to remove.") #Start MPL event for picking an MPL artist, and start the loop. Run CT.ClickRemove self.cidpick=self.TweakPlot.mpl_connect('pick_event',self.ClickRemove) self.TweakPlot.start_event_loop(0) #If Use continuous button is on, repeat removing points while self.useContinuous.get(): if usetutorial: tkMessageBox.showinfo("Help Message", "Continuous point removal on. Keep removing points.") try: self.cidpick=self.TweakPlot.mpl_connect('pick_event',self.ClickRemove) self.TweakPlot.start_event_loop(0) except: self.useContinuous.set(False) #Given a picker event for removing a point... def ClickRemove(self,event): #Get the spline point that you picked, it's x and y coordinates splinepoint = event.artist xsplineval=splinepoint.get_xdata() ysplineval=splinepoint.get_ydata() #Index of the artist ind = event.ind #Make sure the point is in the spline point lists if xsplineval[ind] in self.xspline: if ysplineval[ind] in self.yspline: #Remove that point from the spline, I think this is where sorting is important... self.xspline.pop(ind) self.yspline.pop(ind) #Refresh the plot with new spline, but keep y-axis self.Refresh(yrefresh=False) #Close the event and stop the event loop self.QuitEdit(self.cidpick) #Function saves the spline using SaveSpline function def Save(self): print "Saving Spline" SaveSpline(self.xspline,self.yspline,self.iwvlngth,self.outfile) #Destroy CT.POPUP and CT.TFROOT (the masters for the CT buttons and plots) and leave CT. def Exit(self): self.popup.destroy() self.TFroot.destroy() return
class ImageLoadedLayout: def __init__(self, root_frame, img_fig, plot_fig): print('hi from ImageLoadedLayout') self.root_frame = root_frame self.root_frame.columnconfigure(0, weight=1) self.root_frame.rowconfigure(1, weight=1) self.toolbar_frame = ttk.Frame(self.root_frame) self.toolbar_frame.grid(column=0, row=0, sticky=('W', 'N', 'E', 'S')) self.toolbar_frame.columnconfigure(0, weight=1) self.toolbar_frame.rowconfigure(1, weight=1) self.back_btn = ttk.Button(self.toolbar_frame, text='Back') self.back_btn.grid(column=0, row=0, sticky='W') s = ttk.Style() s.configure('figures.TFrame', background='#334353') self.figure_frame = ttk.Frame(self.root_frame, style='figures.TFrame') self.figure_frame.grid(column=0, row=1, sticky=('W', 'N', 'E', 'S')) self.figure_frame.columnconfigure(0, weight=1) self.figure_frame.columnconfigure(1, weight=1) self.figure_frame.rowconfigure(0, weight=1) self.img_fig = img_fig self.img_canvas = FigureCanvasTkAgg(self.img_fig, master=self.figure_frame) self.img_canvas.get_tk_widget().grid(column=0, row=0, sticky=('W', 'N', 'E', 'S')) self.img_canvas.draw() self.plot_fig = plot_fig self.plot_canvas = FigureCanvasTkAgg(self.plot_fig, master=self.figure_frame) self.plot_canvas.get_tk_widget().grid(column=1, row=0, sticky=('W', 'N', 'E', 'S')) self.plot_canvas.draw() self.info_frame = ttk.Frame(self.root_frame) self.info_frame.grid(column=0, row=2, sticky=('W', 'N', 'E', 'S')) self.info_frame.columnconfigure(0, weight=1) #self.info_frame.rowconfigure(0, weight=1) ttk.Label(self.info_frame, text='Additional Information May Go Here').grid(column=0, row=0) def img_mpl_connect(self, event_type, callback): return self.img_canvas.mpl_connect(event_type, callback) def img_mpl_disconnect(self, cid): self.img_canvas.mpl_disconnect(cid) def plot_mpl_connect(self, event_type, callback): return self.plot_canvas.mpl_connect(event_type, callback) def plot_mpl_disconnect(self, cid): self.plot_canvas.mpl_disconnect(cid) def redraw_img_fig(self): self.img_canvas.draw() def redraw_plot_fig(self): self.plot_canvas.draw() def set_back_callback(self, callback): self.back_btn['command'] = callback def clean_up(self): self.info_frame.grid_forget() self.info_frame.destroy() self.figure_frame.grid_forget() self.figure_frame.destroy() self.toolbar_frame.grid_forget() self.toolbar_frame.destroy()
class PyplotEmbed(tk.Frame): """ Class that will make a tkinter frame with a matplotlib plot area embedded in the frame """ def __init__(self, master, data): tk.Frame.__init__(self, master=master) self.index = 1 self.lines = [] self.vert_line = None self.data = data self.cursor_connect = None self.graph_area = tk.Frame(self) self.figure_bed = plt.figure(figsize=(6,4)) self.axis = self.figure_bed.add_subplot(111) self.canvas = FigureCanvasTkAgg(self.figure_bed, master=self) self.canvas._tkcanvas.config(highlightthickness=0) self.toolbar = NavToolbar(self.canvas, self) # TODO: check this # self.toolbar.pack_forget() self.toolbar.pack() self.canvas.draw() self.canvas.get_tk_widget().pack(side='left', fill=tk.BOTH, expand=1) def plot(self, data, _label): # self.data.plot(ax=self.graph_area.axis, label='channel {0}'.format(self.index)) # line = self.axis.plot(data.index, data['voltage'], label=_label)[0] line = self.axis.plot(data.index, data, label=_label)[0] self.lines.append(line) self.axis.legend() self.index += 1 self.canvas.show() def delete_all(self): while self.lines: l = self.lines.pop() l.remove() del l self.canvas.draw() self.index = 1 self.axis.legend() def time_shift(self, data_index: int, time_to_shift: float): adj_time_shift = time_to_shift - self.data.time_start[data_index] x_data = self.data.adjusted_data[data_index].index - adj_time_shift self.lines[data_index].set_xdata(x_data) self.canvas.draw() def get_fit_start(self): print('make cursor') # cursor = self.axis.axvline(color='r') self.cursor_connect = self.canvas.mpl_connect('motion_notify_event', self.onMouseMove) self.canvas.mpl_connect('button_press_event', self.onclick) self.canvas.show() def onMouseMove(self, event): if not event.xdata: # mouse is not over the plot area return if self.vert_line: self.vert_line.remove() del self.vert_line self.vert_line = None self.vert_line = self.axis.axvline(x=event.xdata, color='r', linewidth=2) self.canvas.show() def onclick(self, event): if event.dblclick == 1: full_data_set = self.data.adjusted_data[-1] start_index = full_data_set.index.get_loc(event.xdata, 'nearest') # start_index = pd.Index(self.data.adjusted_data[-1]).get_loc(event.xdata, 'nearest') start_num = full_data_set.index[start_index] data_to_fit = self.data.adjusted_data[-1][start_num:start_num+20] self.fit_data(data_to_fit, start_num) def fit_data(self, _data, starting_place): print('fitting') self.canvas.mpl_disconnect(self.cursor_connect) t = _data.index - starting_place # change the time so t=0 at the start of the recording amplitude_guess = -_data.voltage.min() time_shift = 0 bounds = ( 0.0, [1.2*amplitude_guess, 1.2*amplitude_guess, 1.2*amplitude_guess, 1.2*amplitude_guess, 0.2, 2, 100, 100]) initial_guess = ( 2. * amplitude_guess / 3, amplitude_guess / 3., amplitude_guess / 2., amplitude_guess / 2., 0.2, 2, 2, 15) fitted_parameters, _ = curve_fit(fitting_func_2a_2i_exp, t, _data.voltage, p0=initial_guess, bounds=bounds, ftol=0.0000005, xtol=0.0000005) # fitted_parameters, _ = curve_fit(fitting_func_2a_2i_exp, t, _data.voltage, p0=initial_guess) print('fitting params: {0}'.format(fitted_parameters)) # make a new line with the fitted parameters and draw it y_fitted = fitting_func_2a_2i_exp(t, *fitted_parameters) # plt.plot(t + _data.index, y_fitted, linewidth=2, label="Check") fitted_line = self.axis.plot(_data.index, y_fitted, linewidth=2, label='fitted')[0]
class Interface(object): """Test""" def __init__(self): self.kymogen = KymoGen() self.image_index = 1 self.len_img_stack = 0 self.overlay = False self.images_loaded = False self.store_datapoints = [] self.cid = None self.gui = tk.Tk() self.gui.wm_title("KymoGen") self.top_frame = tk.Frame(self.gui, width=1200, height=10) self.top_frame.pack(fill="x") self.load_stack_button = tk.Button(self.top_frame, command=self.load_stack, text="Load Stack", width=10) self.load_stack_button.pack(side="left") self.draw_button = tk.Button(self.top_frame, command=self.draw_region_of_interest, text="Draw ROI", width=10) self.draw_button.pack(side="left") self.draw_button.config(state="disabled") self.next_image_button = tk.Button(self.top_frame, command=self.next_image, text="Next Image", width=10) self.next_image_button.pack(side="right") self.next_image_button.config(state="disabled") self.previous_image_button = tk.Button(self.top_frame, command=self.previous_image, text="Previous Image", width=10) self.previous_image_button.pack(side="right") self.previous_image_button.config(state="disabled") self.image_index_status = tk.StringVar() self.image_index_status.set("No Image Loaded") self.image_index_label = tk.Label(self.top_frame, textvariable=self.image_index_status) self.image_index_label.pack(side="right") self.show_overlay_button = tk.Button(self.top_frame, command=self.show_overlay, text="Show Overlay", width=10) self.show_overlay_button.pack(side="left") self.show_overlay_button.config(state="disabled") self.hide_overlay_button = tk.Button(self.top_frame, command=self.hide_overlay, text="Hide Overlay", width=10) self.hide_overlay_button.pack(side="left") self.hide_overlay_button.config(state="disabled") self.gen_kymo_button = tk.Button(self.top_frame, command=self.gen_kymo, text="Generate Kymograph") self.gen_kymo_button.pack(side="left") self.gen_kymo_button.config(state="disabled") self.gen_kymo_button_3px_av = tk.Button( self.top_frame, command=self.gen_kymo_3px_average, text="Generate Kymograph 3px Average") self.gen_kymo_button_3px_av.pack(side="left") self.gen_kymo_button_3px_av.config(state="disabled") self.rescaling_label = tk.Label(self.top_frame, text="Use Rescaling: ") self.rescaling_label.pack(side="left") self.rescaling_value = tk.BooleanVar() self.rescaling_label_checkbox = tk.Checkbutton( self.top_frame, variable=self.rescaling_value, onvalue=True, offvalue=False) self.rescaling_label_checkbox.pack(side="left") self.rescaling_value.set(False) self.middle_frame = tk.Frame(self.gui) self.middle_frame.pack(fill="x") self.fig = plt.figure(figsize=(22, 11), frameon=True) self.canvas = FigureCanvasTkAgg(self.fig, self.middle_frame) self.canvas.show() self.canvas.get_tk_widget().pack(side="top") self.ax = plt.subplot(111) plt.subplots_adjust(left=0, bottom=0, right=1, top=1) self.ax.axis("off") plt.autoscale(True) self.ax.format_coord = self.remove_coord self.canvas.show() self.toolbar = NavigationToolbar2TkAgg(self.canvas, self.middle_frame) self.toolbar.update() self.canvas._tkcanvas.pack(fill="both") def show_overlay(self): self.overlay = True self.show_image() def hide_overlay(self): self.overlay = False self.show_image() def gen_kymo(self): self.kymogen.rescaling = self.rescaling_value.get() self.kymogen.generate_kymograph() def gen_kymo_3px_average(self): self.kymogen.rescaling = self.rescaling_value.get() self.kymogen.generate_kymograph_3px_average() def load_stack(self): self.kymogen.img_stack = imread_collection(askopenfilename()) self.len_img_stack = len(self.kymogen.img_stack) self.draw_button.config(state="active") self.next_image_button.config(state="active") self.image_index_status.set( str(self.image_index) + " of " + str(self.len_img_stack)) self.show_image() def next_image(self): self.image_index += 1 self.previous_image_button.config(state="active") if self.image_index == self.len_img_stack: self.next_image_button.config(state="disabled") self.image_index_status.set( str(self.image_index) + " of " + str(self.len_img_stack)) self.show_image() def previous_image(self): self.image_index -= 1 self.next_image_button.config(state="active") if self.image_index == 1: self.previous_image_button.config(state="disabled") self.image_index_status.set( str(self.image_index) + " of " + str(self.len_img_stack)) self.show_image() def show_image(self): if self.images_loaded is True: xlim = self.ax.get_xlim() ylim = self.ax.get_ylim() self.ax.cla() self.ax.axis("off") self.ax.set_xlim(xlim) self.ax.set_ylim(ylim) else: self.images_loaded = True self.ax.cla() self.ax.axis("off") if self.overlay is False: image = self.kymogen.img_stack[self.image_index - 1] else: image = self.kymogen.img_stack_w_roi[self.image_index - 1] image = img_as_float(image) #image = rescale_intensity(image) self.ax.imshow(image) self.canvas.draw() self.canvas.show() def overlay_roi(self): self.kymogen.img_stack_w_roi = [] for img in self.kymogen.img_stack: img = gray2rgb(img) for coords in self.kymogen.roi: img[coords[0], coords[1]] = (255, 0, 255) self.kymogen.img_stack_w_roi.append(img) self.overlay = True self.show_overlay_button.config(state="active") self.hide_overlay_button.config(state="active") self.gen_kymo_button.config(state="active") self.gen_kymo_button_3px_av.config(state="active") self.show_image() def stop_points(self, event): if event.button == 3: self.canvas.mpl_disconnect(self.cid_press) self.canvas.mpl_disconnect(self.cid_motion) self.canvas.mpl_disconnect(self.cid_release) self.kymogen.roi = np.array( list(OrderedDict.fromkeys(self.store_datapoints))) x0, y0 = np.amin(self.kymogen.roi, axis=0) x1, y1 = np.amax(self.kymogen.roi, axis=0) self.kymogen.box = x0, y0, x1, y1 self.store_datapoints = [] self.overlay_roi() def start_points(self, event): if event.button == 3: self.cid_motion = self.canvas.mpl_connect("motion_notify_event", self.get_points_of_roi) def get_points_of_roi(self, event): self.store_datapoints.append((int(event.ydata), int(event.xdata))) def draw_region_of_interest(self): self.cid_press = self.canvas.mpl_connect("button_press_event", self.start_points) self.cid_release = self.canvas.mpl_connect("button_release_event", self.stop_points) def remove_coord(self, x, y): """"Hack" to remove the mpl coordinates""" return str(x) + " , " + str(y)
class Application(tk.Frame): def __init__(self, master=None): super().__init__(master) self.master = master self.f = Figure(figsize=(8, 6), dpi=100) self.mainPlot = self.f.add_subplot(111) self.canvas = FigureCanvasTkAgg(self.f, self.master) self.cid = self.canvas.mpl_connect("button_press_event", self.canvasClick) self.canvas.draw() self.canvas_tk = self.canvas.get_tk_widget().pack(side=tk.BOTTOM, fill=tk.BOTH, expand=True) def canvasClick(self, event): global CONFIG_VRANGE, CONFIG_SAMPLERATE, CONFIG_SAMPLECOUNT, CONFIG_FPREFIX, CONFIG_THRESHOLD, CONFIG_CAPTURES, CONFIG_BACKOFF, CONFIG_DONOTSAVE, CONFIG_SPECGRAM, CONFIG_DISPLAY_SAMPLES try: ps = ps2000a.PS2000a() self.canvas.mpl_disconnect(self.cid) print("Committing capture...") except: print("Failed to commit scope") return ps.setChannel('A', 'DC', VRange=CONFIG_VRANGE, VOffset=0.0, enabled=True, BWLimited=False, probeAttenuation=10.0) (freq, maxSamples) = ps.setSamplingFrequency(CONFIG_SAMPLERATE, CONFIG_SAMPLECOUNT) print(" > Asked for %d Hz, got %d Hz" % (CONFIG_SAMPLERATE, freq)) if CONFIG_FPREFIX is not None: print(" > Configured in training mode with prefix %s" % CONFIG_FPREFIX) ps.setSimpleTrigger('A', CONFIG_THRESHOLD, 'Rising', enabled=True) i = 0 nCount = 0 nMax = CONFIG_CAPTURES print("Capture is committed...") while True: if nMax == nCount: break ps.runBlock(pretrig=0.2) ps.waitReady() dataA = ps.getDataV("A", CONFIG_SAMPLECOUNT, returnOverflow=False) # print(len(dataA)) SLICE_START = int(CONFIG_SAMPLECOUNT * 0.2) SLICE_END = SLICE_START + 500 if float(max( dataA[SLICE_START:SLICE_END])) < float(CONFIG_THRESHOLD): # print("failed capture") continue if CONFIG_DONOTSAVE is False: if CONFIG_FPREFIX is None: print("Saving training capture...") np.save("floss/%d.npy" % nCount, dataA) else: print("Saving real capture...") np.save("toothpicks/%s-%d.npy" % (CONFIG_FPREFIX, nCount), dataA) else: print("No save mode specified, discarding save") self.mainPlot.clear() data_DISPLAY = dataA[SLICE_START - 5000:SLICE_START + 5000] if CONFIG_SPECGRAM: self.mainPlot.specgram(data_DISPLAY, NFFT=1024, Fs=CONFIG_SAMPLERATE, noverlap=900) else: self.mainPlot.plot( support.block_preprocess_function(data_DISPLAY)) self.canvas.draw() self.canvas.flush_events() time.sleep(CONFIG_BACKOFF) nCount += 1 print("Captured %d slices" % nCount)
class LocalizationPlot: def __init__(self, frame, tag_filter, tag_var,ran): self.frame = frame self.tag_filter = tag_filter self.tag_var = tag_var self.ran = ran self.fig = plt.figure(figsize=(20,10)) self.ax = self.fig.add_subplot(111) # Hollow out self.canvas = FigureCanvasTkAgg(self.fig, master=frame) self.canvas.draw() self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=False) self.canvas.mpl_connect("button_press_event", self.input_Motionx_y) self.canvas.mpl_connect("button_release_event", self.output_Motionx_y) self.canvas.mpl_connect("scroll_event", self.zoom) # We only want the latest 80 plot # update and cancel the previous one def update_plot(self, data, p_num): xs = [] ys = [] try: for i in range(self.tag_filter.counter): xs.append(data[self.tag_var.get()]['pos'][0]) ys.append(data[self.tag_var.get()]['pos'][1]) except: self.tag_var.set(list(data.keys())[0]) for i in range(self.tag_filter.counter): xs.append(data[self.tag_var.get()]['pos'][0]) ys.append(data[self.tag_var.get()]['pos'][1]) self.ax.cla() self.ax.set(xlabel='x Axis (m)', ylabel='y Axis (m)', title = "Number: %d" % p_num) # # Update the right label for i in range(len(xs)): self.ax.plot(xs[i], ys[i], '.', color='#00BFFF', visible=self.tag_var.get()) self.ax.set_xlim(self.ran[0], self.ran[1]) self.ax.set_ylim(self.ran[2], self.ran[3]) self.canvas.draw() def input_Motionx_y(self,event): self.c_list_x = [] self.c_list_y = [] self.c_list_x.append(event.x) self.c_list_y.append(event.y) self.move_f = self.canvas.mpl_connect("motion_notify_event", self.moveon) def output_Motionx_y(self,event): self.canvas.mpl_disconnect(self.move_f) def zoom(self,event): if event.button == "up": self.ran[0] += 0.05 self.ran[1] -= 0.05 self.ran[2] += 0.05 self.ran[3] -= 0.05 self.ax.set_xlim(self.ran[0], self.ran[1]) self.ax.set_ylim(self.ran[2], self.ran[3]) else: self.ran[0] -= 0.05 self.ran[1] += 0.05 self.ran[2] -= 0.05 self.ran[3] += 0.05 self.ax.set_xlim(self.ran[0], self.ran[1]) self.ax.set_ylim(self.ran[2], self.ran[3]) def getrange(self): return self.ran def moveon(self,event): self.c_list_x.append(event.x) self.c_list_y.append(event.y) try: if self.c_list_x[-1]-self.c_list_x[-2] >0: res_x = event.xdata x_c = eval(str(self.ran[1]) + "-" + str(self.ran[0])) / 50 self.ran[0] -= (res_x) * x_c self.ran[1] -= (res_x) * x_c else: res_x = event.xdata x_c = eval(str(self.ran[1]) + "-" + str(self.ran[0])) / 50 self.ran[0] -= (res_x) * x_c self.ran[1] -= (res_x) * x_c if self.c_list_y[-1]-self.c_list_y[-2] >0: res_y = event.ydata y_c = eval(str(self.ran[3]) + "-" + str(self.ran[2]))/5 self.ran[2] -= (res_y) * y_c self.ran[3] -= (res_y) * y_c self.ax.set_xlim(self.ran[0], self.ran[1]) self.ax.set_ylim(self.ran[2], self.ran[3]) elif self.c_list_y[-1]-self.c_list_y[-2] == 0: pass else: res_y = event.ydata y_c = eval(str(self.ran[3]) + "-" + str(self.ran[2]))/10 self.ran[2] -= (res_y) * y_c self.ran[3] -= (res_y) * y_c self.ax.set_xlim(self.ran[0], self.ran[1]) self.ax.set_ylim(self.ran[2], self.ran[3]) except: pass
class data_interpreter(tk.Frame): #single instrument def __init__(self, master, root, name, file, plot_index): tk.Frame.__init__(self, master) self.config(border = 2) self.master = master self.root = root self.plot_frame_row = -1 self.plot_frame_column = -1 self.grid_rowconfigure(0,weight = 1) self.grid_columnconfigure(0, weight = 1) self.name = name #this will be plot title self.file = file #open file not file path self.plot_index = plot_index #where to plot the data self.time = [] self.data = [] self.units = [] #read data self.read_data() #plot_data self.fig = plt.Figure()#figsize=(10,10)) self.ax = self.fig.add_subplot(1,1,1) self.line, = self.ax.plot(self.time, self.data) self.ax.set_title(self.name) self.canvas = FigureCanvasTkAgg(self.fig,self) self.canvas.show() self.canvas_widget = self.canvas.get_tk_widget() self.canvas_widget.pack(side=tk.TOP, fill=tk.BOTH, expand=True) self.canvas._tkcanvas.grid(row = 0, column = 0, sticky = 'nsew') self.selected = False self.selected_intervals = [] #times for the beginning and end of each interval self.selected_vlines = [] #lines to place at selected intervals self.selected_time = [] #times within interval to plot self.selected_data = [] #cooresponding data to plot self.mean_time = [] self.mean_data = [] self.std_dev = [] self.getting_coordinates = False self.connectid = None #select button self.select_btn = ttk.Button(self, text = 'Select Graph', command = lambda: self.select_graph()) self.select_btn.grid(row = 1, column = 0, sticky = 'nsew') def reinit_toselectframe(self, master): self.master = master #overwrite previous master frame tk.Frame.__init__(self, self.master) self.grid_rowconfigure(0, weight = 1) self.grid_columnconfigure(0, weight = 1) self.config(border = 2, relief = tk.GROOVE) self.fig = plt.Figure() self.ax = self.fig.add_subplot(1,1,1) self.line, = self.ax.plot(self.time, self.data) self.ax.set_title(self.name) self.canvas = FigureCanvasTkAgg(self.fig, self) self.canvas.show() self.canvas_widget = self.canvas.get_tk_widget() self.canvas_widget.grid(row = 0, column = 0, sticky = 'nsew')#pack(side=tk.TOP, fill=tk.BOTH, expand=True) self.canvas._tkcanvas.grid(row = 0, column = 0, sticky = 'nsew') self.navigator_frame = tk.Frame(self) self.navigator_frame.grid(row = 1, column = 0, sticky = 'nsew') self.toolbar = NavigationToolbar2TkAgg(self.canvas, self.navigator_frame) self.toolbar.update() self.get_coordinates_btn = ttk.Button(self, text = 'Select Intervals', command = lambda: self.get_coordinates()) self.get_coordinates_btn.grid(row = 2, column = 0, sticky = 'nsew') def reinit_toplotframe(self, master): self.master = master #overwrite previous master frame tk.Frame.__init__(self, self.master) self.grid_rowconfigure(0, weight = 1) self.grid_columnconfigure(0, weight = 1) self.config(border = 2, relief = tk.GROOVE) self.fig = plt.Figure() self.ax = self.fig.add_subplot(1,1,1) self.line, = self.ax.plot(self.time, self.data) self.ax.set_title(self.name) self.canvas = FigureCanvasTkAgg(self.fig, self) self.canvas.show() self.canvas_widget = self.canvas.get_tk_widget() self.canvas_widget.grid(row = 0, column = 0, sticky = 'nsew')#pack(side=tk.TOP, fill=tk.BOTH, expand=True) self.canvas._tkcanvas.grid(row = 0, column = 0, sticky = 'nsew') self.select_btn = ttk.Button(self, text = 'Select Graph', command = lambda: self.select_graph()) self.select_btn.grid(row = 1, column = 0, sticky = 'nsew') def read_data(self): for line in self.file: datalist = line.split(',') if len(datalist) > 1: try: self.time.append(float((datetime.datetime.strptime(datalist[0], '%Y-%m-%d %H:%M:%S.%f') - datetime.datetime(1970,1,1)).total_seconds())) self.data.append(float(datalist[1])) except ValueError: try: self.time.append(float((datetime.datetime.strptime(datalist[0], '%Y-%m-%d %H:%M:%S') - datetime.datetime(1970,1,1)).total_seconds())) self.data.append(float(datalist[1])) except ValueError: pass if len(datalist) > 2: self.units.append(datalist[2]) else: self.units.append(None) def select_graph(self): if self.selected: self.select_btn.config(text = 'Select Graph') self.config(relief = tk.FLAT) else: self.select_btn.config(text = 'Unselect Graph') self.config(relief = tk.GROOVE) self.selected = not self.selected def remove_plot(self): self.ax.lines.remove(self.line) def get_coordinates(self): if self.getting_coordinates is None: #then button has been pressed just after initilization and need to start getting interals self.get_coordinates_btn.config(text = 'Stop Selecting Intervals... ') self.connectid = self.canvas.mpl_connect('button_press_event', self.on_click) self.getting_coordinates = True else: if self.getting_coordinates: #then stop getting coordinates self.get_coordinates_btn.config(text = 'Updating Curves...') #send selected coordinates to root self.root.selected_intervals = self.selected_intervals try: self.canvas.mpl_disconnect(self.connectid) self.root.use_intervals() self.get_coordinates_btn.config(text = 'Select Intervals') except SystemError: pass else: #then something went wrong. Logic shouldn't allow this code to run self.get_coordinates_btn.config(text = 'Stop Selecting Intervals... ') self.connectid = self.canvas.mpl_connect('button_press_event', self.on_click) self.getting_coordinates = not self.getting_coordinates def on_click(self, event): #get the x and y coords, flip y from top to bottom x, y = event.x, event.y if event.button == 1: if event.inaxes is not None: print('data coords %f %f' % (event.xdata, event.ydata)) #self.root.selected_times.append(event.xdata) self.selected_intervals.append(event.xdata) #plot vertical lines on this interpreter line = self.ax.axvline(x = event.xdata, color = 'r') self.selected_vlines.append(line) #plot vertical lines on other selected interpreters for interpreter in self.root.selected_interpreter_list: if interpreter.name != self.name: #only do this for other selected interpreters other_line = interpreter.ax.axvline(x = event.xdata, color = 'r') interpreter.selected_vlines.append(other_line) interpreter.canvas.draw() if event.button == 3: if event.inaxes is not None: self.selected_intervals = self.selected_intervals[:-1] self.ax.lines.remove(self.selected_vlines[-1]) #remove vertical lines from other selected interpreters for interpreter in self.root.selected_interpreter_list: if interpreter.name != self.name: #only do this for other selected interpreters interpreter.ax.lines.remove(interpreter.selected_vlines[-1]) interpreter.canvas.draw() self.canvas.draw()