def __init__(self, parent, controller): tk.Frame.__init__(self, parent) self.controller = controller name = self.controller.currentResultFolder if name != "abc": cur_dir_path = os.path.dirname(os.path.realpath(__file__)) path = Path(cur_dir_path) path = path.parent.parent reference = os.path.join( path, os.path.join('ZZoutput', os.path.join(name, 'results_' + name + '.txt'))) if controller.justEnteredViewParameter == 1: with open(reference) as ff: dataRef = json.load(ff) controller.dataRef = dataRef controller.justEnteredViewParameter = 0 else: dataRef = controller.dataRef nbWellsRef = len(dataRef["wellPoissMouv"]) numWell = self.controller.numWell numPoiss = self.controller.numPoiss numMouv = self.controller.numMouv nbWells = len(dataRef["wellPoissMouv"]) if numWell >= nbWells: if nbWells == 0: numWell = 0 else: numWell = nbWells - 1 nbPoiss = len(dataRef["wellPoissMouv"][numWell]) if numPoiss >= nbPoiss: if nbPoiss == 0: numPoiss = 0 else: numPoiss = nbPoiss - 1 nbMouv = len(dataRef["wellPoissMouv"][numWell][numPoiss]) if numMouv >= nbMouv: if nbMouv == 0: numMouv = 0 else: numMouv = nbMouv - 1 label = tk.Label(self, text=" ", font=LARGE_FONT) label.grid(row=1, column=6) ttk.Button(self, text="View video for all wells together", command=lambda: controller.showValidationVideo( -1, 0, -1)).grid(row=1, column=1) # ttk.Button(self, text="Verify wells detection", command=lambda: controller.showValidationVideo(-1,0,-1)).grid(row=1,column=2, columnspan=2) label = tk.Label(self, text=name, font="bold", justify=LEFT, pady=10) label.grid(sticky=W, row=0, column=0, columnspan=8) label = tk.Label(self, text="Well number:") label.grid(row=2, column=1) e1 = tk.Entry(self) e1.insert(0, numWell) e1.grid(row=2, column=2) tk.Button(self, text="-", command=lambda: controller.printSomeResults( int(e1.get()) - 1, e2.get(), e3.get())).grid( row=2, column=3) tk.Button(self, text="+", command=lambda: controller.printSomeResults( int(e1.get()) + 1, e2.get(), e3.get())).grid( row=2, column=4) ttk.Button(self, text="View zoomed video for well " + str(numWell), command=lambda: controller.showValidationVideo( e1.get(), 1, -1)).grid(row=3, column=2) def callback(url): webbrowser.open_new(url) link1 = tk.Button(self, text="Video viewing tips", cursor="hand2", bg='red') link1.grid(row=3, column=4) link1.bind( "<Button-1>", lambda e: callback( "https://zebrazoom.org/validationVideoReading.html")) label = tk.Label(self, text="Fish number:") label.grid(row=4, column=1) e2 = tk.Entry(self) e2.insert(0, numPoiss) e2.grid(row=4, column=2) tk.Button(self, text="-", command=lambda: controller.printSomeResults( e1.get(), int(e2.get()) - 1, e3.get())).grid(row=4, column=3) tk.Button(self, text="+", command=lambda: controller.printSomeResults( e1.get(), int(e2.get()) + 1, e3.get())).grid(row=4, column=4) label = tk.Label(self, text="Bout number:") label.grid(row=5, column=1) e3 = tk.Entry(self) e3.insert(0, numMouv) e3.grid(row=5, column=2) tk.Button(self, text="-", command=lambda: controller.printSomeResults( e1.get(), e2.get(), int(e3.get()) - 1)).grid(row=5, column=3) tk.Button(self, text="+", command=lambda: controller.printSomeResults( e1.get(), e2.get(), int(e3.get()) + 1)).grid(row=5, column=4) button1 = ttk.Button(self, text="View bout's angle", command=lambda: controller.printSomeResults( e1.get(), e2.get(), e3.get())) button1.grid(row=6, column=2) if (len(dataRef["wellPoissMouv"][numWell][numPoiss]) > 0): if ("flag" in dataRef["wellPoissMouv"][numWell][numPoiss] [numMouv]): if (dataRef["wellPoissMouv"][numWell][numPoiss][numMouv] ["flag"]): flagTxt = "UnFlag Movement" else: flagTxt = "Flag Movement" else: flagTxt = "Flag Movement" button3 = tk.Button(self, text=flagTxt, command=lambda: controller.flagMove( e1.get(), e2.get(), e3.get())) if flagTxt == "UnFlag Movement": button3.configure(bg='red') button3.grid(row=6, column=4) prev = ttk.Button( self, text="Previous Bout", command=lambda: controller.printPreviousResults( e1.get(), e2.get(), e3.get(), nbWells, nbPoiss, nbMouv)) prev.grid(row=7, column=1) next = ttk.Button( self, text="Next Bout", command=lambda: controller.printNextResults( e1.get(), e2.get(), e3.get(), nbWells, nbPoiss, nbMouv)) next.grid(row=7, column=2) tk.Button(self, text="Go to the previous page", command=lambda: controller.show_frame( "ResultsVisualization")).grid(row=8, column=1) tk.Button(self, text="Change Right Side Plot", command=lambda: controller.printSomeResults( e1.get(), e2.get(), e3.get(), True), bg="light green").grid(row=8, column=2, columnspan=2) if (controller.superstructmodified == 1): button1 = tk.Button(self, text="Save SuperStruct", command=lambda: controller.saveSuperStruct( e1.get(), e2.get(), e3.get())) button1.configure(bg='orange') button1.grid(row=7, column=4) if nbMouv > 0: begMove = dataRef["wellPoissMouv"][numWell][numPoiss][numMouv][ "BoutStart"] endMove = dataRef["wellPoissMouv"][numWell][numPoiss][numMouv][ "BoutEnd"] ttk.Button(self, text="View video for well " + str(numWell), command=lambda: controller.showValidationVideo( e1.get(), 0, begMove)).grid(row=3, column=1) ttk.Button(self, text="View bout's video", command=lambda: controller.showValidationVideo( e1.get(), 1, begMove)).grid(row=6, column=1) if controller.visualization == 0 and not ( "TailAngle_smoothed" in dataRef["wellPoissMouv"] [numWell][numPoiss][numMouv]): controller.visualization = 1 if controller.visualization == 1 and not ( "TailAngle_Raw" in dataRef["wellPoissMouv"][numWell] [numPoiss][numMouv]): controller.visualization = 2 if controller.visualization == 2 and not ( (len( np.unique(dataRef["wellPoissMouv"][numWell][numPoiss] [numMouv]["HeadX"])) > 1) and (len( np.unique(dataRef["wellPoissMouv"][numWell][numPoiss] [numMouv]["HeadY"])) > 1)): controller.visualization = 0 if controller.visualization == 0: label = tk.Label( self, text="Tail Angle Smoothed and amplitudes for well " + str(numWell) + ", fish " + str(numPoiss) + ", bout " + str(numMouv), font=LARGE_FONT) elif controller.visualization == 1: label = tk.Label(self, text="Tail Angle Raw for well " + str(numWell) + ", fish " + str(numPoiss) + ", bout " + str(numMouv) + " ", font=LARGE_FONT) else: label = tk.Label(self, text="Body Coordinates for well " + str(numWell) + " , fish " + str(numPoiss) + " , bout " + str(numMouv), font=LARGE_FONT) label.grid(row=1, column=7) if controller.visualization == 0: tailAngleSmoothed = dataRef["wellPoissMouv"][numWell][ numPoiss][numMouv]["TailAngle_smoothed"].copy() for ind, val in enumerate(tailAngleSmoothed): tailAngleSmoothed[ind] = tailAngleSmoothed[ind] * ( 180 / (math.pi)) if "Bend_Timing" in dataRef["wellPoissMouv"][numWell][ numPoiss][numMouv]: freqX = dataRef["wellPoissMouv"][numWell][numPoiss][ numMouv]["Bend_Timing"] freqY = dataRef["wellPoissMouv"][numWell][numPoiss][ numMouv]["Bend_Amplitude"] else: freqX = [] freqY = [] if type(freqY) == int or type(freqY) == float: freqY = freqY * (180 / (math.pi)) else: if "Bend_Timing" in dataRef["wellPoissMouv"][numWell][ numPoiss][numMouv]: freqX = dataRef["wellPoissMouv"][numWell][ numPoiss][numMouv]["Bend_Timing"].copy() freqY = dataRef["wellPoissMouv"][numWell][ numPoiss][numMouv]["Bend_Amplitude"].copy() else: freqX = [] freqY = [] for ind, val in enumerate(freqY): freqY[ind] = freqY[ind] * (180 / (math.pi)) fx = [begMove] fy = [0] if (type(freqX) is int) or (type(freqX) is float): freqX = [freqX] freqY = [freqY] for idx, x in enumerate(freqX): idx2 = idx - 1 fx.append(freqX[idx2] - 1 + begMove) fx.append(freqX[idx2] - 1 + begMove) fx.append(freqX[idx2] - 1 + begMove) fy.append(0) fy.append(freqY[idx2]) fy.append(0) f = Figure(figsize=(5, 5), dpi=100) a = f.add_subplot(111) if len(tailAngleSmoothed): a.plot([i for i in range(begMove, endMove + 1)], tailAngleSmoothed) a.plot(fx, fy) a.plot([i for i in range(begMove, endMove + 1)], [0 for i in range(0, len(tailAngleSmoothed))]) elif controller.visualization == 1: tailAngleSmoothed = dataRef["wellPoissMouv"][numWell][ numPoiss][numMouv]["TailAngle_Raw"].copy() for ind, val in enumerate(tailAngleSmoothed): tailAngleSmoothed[ind] = tailAngleSmoothed[ind] * ( 180 / (math.pi)) f = Figure(figsize=(5, 5), dpi=100) a = f.add_subplot(111) a.plot([i for i in range(begMove, endMove + 1)], tailAngleSmoothed) a.plot([i for i in range(begMove, endMove + 1)], [0 for i in range(0, len(tailAngleSmoothed))]) else: headX = dataRef["wellPoissMouv"][numWell][numPoiss][ numMouv]["HeadX"].copy() headY = dataRef["wellPoissMouv"][numWell][numPoiss][ numMouv]["HeadY"].copy() f = Figure(figsize=(5, 5), dpi=100) a = f.add_subplot(111) a.plot(headX, headY) canvas = FigureCanvasTkAgg(f, self) canvas.draw() canvas.get_tk_widget().grid(row=2, column=7, rowspan=7) else: tailAngleSmoothed = [i for i in range(0, 1)] f = Figure(figsize=(5, 5), dpi=100) a = f.add_subplot(111) a.plot([i for i in range(0, len(tailAngleSmoothed))], tailAngleSmoothed) canvas = FigureCanvasTkAgg(f, self) canvas.draw() canvas.get_tk_widget().grid(row=2, column=7, rowspan=7) canvas = Canvas(self) canvas.create_text(100, 10, text="No bout detected for well " + str(numWell)) canvas.grid(row=2, column=7, rowspan=7)
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="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.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) 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() #load data self.scdata = wishbone.wb.SCData.from_csv(os.path.expanduser(self.dataFileName), data_type='sc-seq', normalize=self.normalizeVar.get()) #get genes self.genes = self.scdata.data.columns.values #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') #set up visualization buttons tk.Label(self, text=u"Visualizations:", fg='black', bg='white').grid(column=0, row=1) 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.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 self.analysisMenu.entryconfig(0, state='normal') self.geometry('800x550') #destroy pop up menu self.fileInfo.destroy() 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") 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(5) tk.Entry(self.tsneOptions, textvariable=self.nCompVar).grid(column=1,row=0) tk.Button(self.tsneOptions, text="Run", command=self._runTSNE).grid(column=1, row=1) tk.Button(self.tsneOptions, text="Cancel", command=self.tsneOptions.destroy).grid(column=0, row=1) self.wait_window(self.tsneOptions) def _runTSNE(self): self.scdata.run_tsne(n_components=self.nCompVar.get()) #enable buttons 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') self.tsneOptions.destroy() def runDM(self): self.scdata.run_diffusion_map() #enable buttons self.analysisMenu.entryconfig(3, state='normal') self.analysisMenu.entryconfig(4, state='normal') self.visMenu.entryconfig(2, 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) #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) 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()) #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.component_menu.config(state='disabled') self.updateButton.config(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.component_menu.config(state='disabled') self.updateButton.config(state='disabled') 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.component_menu.config(state='disabled') self.updateButton.config(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.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.component_menu.config(state='disabled') self.updateButton.config(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.component_menu.config(state='disabled') self.updateButton.config(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.component_menu.config(state='disabled') self.updateButton.config(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.component_menu.config(state='disabled') self.updateButton.config(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 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 matplotlibSwitchGraphs: def __init__(self, master): self.master = master self.frame = Frame(self.master) self.fig, self.ax = config_plot() self.graphIndex = 0 self.canvas = FigureCanvasTkAgg(self.fig, self.master) self.config_window() self.draw_graph('janvier') self.frame.pack(expand=YES, fill=BOTH) def config_window(self): self.canvas.mpl_connect("key_press_event", self.on_key_press) toolbar = NavigationToolbar2Tk(self.canvas, self.master) toolbar.update() self.canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1) self.button = Button(self.master, text="Quit", command=self._quit) self.button.pack(side=BOTTOM) self.button_back = Button(self.master, text="Graphique précédent", command=self.back_graph) self.button_back.pack(side=BOTTOM) self.button_next = Button(self.master, text="Graphique suivant", command=self.next_graph) self.button_next.pack(side=BOTTOM) def draw_graph(self, month): if(month == 'année'): df_temp = pd.DataFrame(columns = ['Température']) for column in df: for value in df[column]: df_temp = df_temp.append({'Températeure':value},ignore_index=True) df_temp.dropna() self.ax.clear() self.ax.plot(df_temp['Température']) self.ax.set(title='Année') self.canvas.draw() else: self.ax.clear() self.ax.plot(df[month]) self.ax.set(title=month) self.canvas.draw() def on_key_press(event): key_press_handler(event, self.canvas, toolbar) def _quit(self): self.master.quit() def next_graph(self): if self.graphIndex == 0: self.draw_graph('février') self.graphIndex = 1 elif self.graphIndex == 1: self.draw_graph('mars') self.graphIndex = 2 elif self.graphIndex == 2: self.draw_graph('avril') self.graphIndex = 3 elif self.graphIndex == 3: self.draw_graph('mai') self.graphIndex = 4 elif self.graphIndex == 4: self.draw_graph('juin') self.graphIndex = 5 elif self.graphIndex == 5: self.draw_graph('juillet') self.graphIndex = 6 elif self.graphIndex == 6: self.draw_graph('août') self.graphIndex = 7 elif self.graphIndex == 7: self.draw_graph('septembre') self.graphIndex = 8 elif self.graphIndex == 8: self.draw_graph('octobre') self.graphIndex = 9 elif self.graphIndex == 9: self.draw_graph('novembre') self.graphIndex = 10 elif self.graphIndex == 10: self.draw_graph('décembre') self.graphIndex = 11 elif self.graphIndex == 11: self.draw_graph('janvier') self.graphIndex = 0 elif self.graphIndex == 12: self.draw_graph('année') self.graphIndex = 12 def back_graph(self): if self.graphIndex == 0: self.draw_graph('décembre') self.graphIndex = 11 elif self.graphIndex == 11: self.draw_graph('novembre') self.graphIndex = 10 elif self.graphIndex == 10: self.draw_graph('octobre') self.graphIndex = 9 elif self.graphIndex == 9: self.draw_graph('septembre') self.graphIndex = 8 elif self.graphIndex == 8: self.draw_graph('août') self.graphIndex = 7 elif self.graphIndex == 7: self.draw_graph('juillet') self.graphIndex = 6 elif self.graphIndex == 6: self.draw_graph('juin') self.graphIndex = 5 elif self.graphIndex == 5: self.draw_graph('mai') self.graphIndex = 4 elif self.graphIndex == 4: self.draw_graph('avril') self.graphIndex = 3 elif self.graphIndex == 3: self.draw_graph('mars') self.graphIndex = 2 elif self.graphIndex == 2: self.draw_graph('février') self.graphIndex = 1 elif self.graphIndex == 1: self.draw_graph('janvier') self.graphIndex = 0 elif self.graphIndex == 12: self.draw_graph('année') self.graphIndex = 12 def main(): root = Tk() matplotlibSwitchGraphs(root) root.mainloop() def show_graph(): main() fenetre = Tk() fenetre.title("Data tp 1") fenetre.geometry('800x800') canvas=Canvas(fenetre,bg='#FFFFFF',width=800,height=800,scrollregion=(0,0,1500,1500)) hbar=Scrollbar(fenetre,orient=HORIZONTAL) hbar.pack(side=BOTTOM,fill=X) hbar.config(command=canvas.xview) vbar=Scrollbar(fenetre,orient=VERTICAL) vbar.pack(side=RIGHT,fill=Y) vbar.config(command=canvas.yview) canvas.config(width=800,height=800) canvas.config(xscrollcommand=hbar.set, yscrollcommand=vbar.set) canvas.pack(side=LEFT,expand=True,fill=BOTH) for index, column in enumerate(df): canvas.create_text(100,(index*120)+20,fill="black",font="Times 15 bold", text="Mois : "+str(column)) canvas.create_text(100,(index*120)+40,fill="black",font="Times 10", text="Moyenne : "+str(df[column].mean())) canvas.create_text(100,(index*120)+60,fill="black",font="Times 10", text="Ecart type : "+str(df[column].std())) canvas.create_text(100,(index*120)+80,fill="black",font="Times 10", text="Minimum : "+str(df[column].min())) canvas.create_text(100,(index*120)+100,fill="black",font="Times 10", text="Maximum : "+str(df[column].max())) b = Button(fenetre, text="Afficher les graphiques", width=10, command=show_graph,background="white",foreground="black",activebackground="white",activeforeground="black") b.place(x=300, y=20, anchor="nw", width=150, height=30) fenetre.mainloop()
class LeftFrame(Frame): """ Classe pour la "frame" qui sera la partie gauche de l'insterface. Celle-ci accueillera les grilles et les plots des statistiques """ def __init__(self, parent): Frame.__init__(self, parent) self.parent = parent self.canvas = None self.nb_colonnes = 0 self.nb_lignes = 0 self.pas_colonne = 0 self.pas_ligne = 0 self.initialize() def initialize(self): self.affiche_grille(self.parent.grilles[0]) self.rescale() def clean(self): """ Vide le contenu de la frame en supprimant les objets contenus dans celle-ci, en vue de le remplacer par autre chose """ for i in self.winfo_children(): i.destroy() def rescale(self): """ Change l'échelle prise par la fenêtre pour un affichage plus pratique, on n'a alors plus qu'à spécifier les coordonnées comme dans la grille et cette fonction repositionne tout pour que l'affichage fasse la taille de la fenêtre """ if self.canvas is not None and self.pas_colonne != 0 and self.pas_ligne != 0: self.canvas.scale(ALL, -1, -1, self.pas_colonne, self.pas_ligne) def afficher_resultat(self, resultat, tps_creat=None, tps_calc=None): """ Affiche pour chaque instance du fichier d'entrée: la chaîne de caractère représentant le chemin emprunté par le robot, ainsi que le temps de création du graphe et le temps de calcul de ce résultat :param resultat: liste des chaines de caractères résultats du calcul du chemin le plus rapide des instances :param tps_creat: temps de création du graphe pour chaque instance :param tps_calc: temps de calcul du chemin le plus rapide pour chaque instance (sans la création du graphe) :type resultat: list :type tps_creat: list :type tps_calc: list """ self.clean() if tps_creat: label = Label(self, text="La moyenne du temps de création de graphe sur ce fichier est : " + str(np.mean(tps_creat)) + " secondes") label.pack() if tps_calc: label = Label(self, text="La moyenne du temps de calcul du chemin le plus rapide sur ce fichier est : " + str(np.mean(tps_calc)) + " secondes") label.pack() for i in range(len(resultat)): label = Label(self, text="Problème n°"+str(i)) label.pack() label_res = Label(self, text=resultat[i]) label_res.pack() if tps_creat: label_tps_creat = Label(self, text="La création du graphe a pris " + str(tps_creat[i]) + " secondes") label_tps_creat.pack() if tps_calc: label_tps_calc = Label(self, text="Le calcul de la solution a pris " + str(tps_calc[i]) + " secondes") label_tps_calc.pack() def affiche_grille(self, grille): # Ne pas oublier de faire un rescale après appel à cette fonction !!! """ Affiche la grille passée en paramètre dans la partie gauche de la fenêtre :param grille: grille représentant le dépôt constituée de 3 éléments : - un tuple pour le nombre de lignes et de colonnes - une liste représentant les lignes du dépôt avec les '0' et '1' - une liste pour les coordonnées du point de départ, d'arrivée, et l'orientation du robot au départ :type grille: list """ self.clean() # Récupération des éléments de la grille self.nb_lignes, self.nb_colonnes = grille[0] lignes = grille[1] ligne = grille[2] self.canvas = Canvas(self, width=leftframewidth, height=leftframeheight) # Récupération des données du problème self.pas_colonne = leftframewidth//(self.nb_colonnes + 2) self.pas_ligne = leftframeheight//(self.nb_lignes + 2) rayon = 1/2 # rayon des cercles du robot au départ et à l'arrivée # Dessin du quadrillage for i in range(0, self.nb_lignes): for j in range(0, self.nb_colonnes): if lignes[i][j] == '0': rectangle(self.canvas, j, i, j+1, i+1) else: rectangle(self.canvas, j, i, j+1, i+1, color=couleur_obstacles) # Dessin du point de départ du robot avec sa flèche dessine_depart(self.canvas, int(ligne[1]), int(ligne[0]), rayon, ligne[-1]) # Dessin du point d'arrivée du robot cercle(self.canvas, int(ligne[3]), int(ligne[2]), rayon) self.canvas.pack() def affiche_chemin(self, grille, chemin_list, chemin_str): """ Affiche la grille et le chemin le plus rapide du robot entre son point de départ et d'arrivée :param grille: grille représentant le dépôt constituée de 3 éléments (cf affiche_grille) :param chemin_list: liste de coordonnées des points empruntés par le robot dans sont chemin :param chemin_str: chaine de caractères résultat du calcul du chemin le plus rapide dans cette grille :type grille: list :type chemin_list: list :type chemin_str: str """ # Affichage de la grille elle-même self.affiche_grille(grille) # Ajout du chemin par des lignes for i in range(1, len(chemin_list)): self.canvas.create_line(chemin_list[i-1][1], chemin_list[i-1][0], chemin_list[i][1], chemin_list[i][0], width=3) # on redessine le point de départ pour que ce soit plus "joli" ligne = grille[2] rayon = 1/2 dessine_depart(self.canvas, int(ligne[1]), int(ligne[0]), rayon, ligne[-1]) self.canvas.create_text(0, self.nb_lignes+1/2, text=chemin_str, font=("Helvetica", 10), anchor=NW) self.rescale() def modifier_grille(self): """ Ajoute un lien entre clic gauche et les rectangles de la grille pour pouvoir modifier leur statut (obstacle ou non) """ self.canvas.tag_bind("case", "<Button-1>", self.toggle_obstacle) def toggle_obstacle(self, event): """ Changement du status (obstacle ou non) d'un rectangle de la grille lorsqu'on clique dessus :param event: clic gauche sur un rectangle """ # Récupération du rectangle sur lequel on a cliqué w = event.widget.find_closest(event.x, event.y) ligne = (w[0]-1)//self.nb_colonnes colonne = (w[0]-1) % self.nb_colonnes # Changement de la grille elle-même en changeant la case correspondante, # et changement de la couleur du rectangle if self.parent.grilles[0][1][ligne][colonne] == '1': self.parent.grilles[0][1][ligne][colonne] = '0' self.canvas.itemconfig(w, fill="white") else: self.parent.grilles[0][1][ligne][colonne] = '1' self.canvas.itemconfig(w, fill=couleur_obstacles) def affiche_patienter(self): """ Demande à l'utilisateur de patienter pendant le calcul des statistiques qu'il a demandées """ self.clean() label = Label(self, text="Patientez s'il vous plaît\nCalcul des statistiques en cours", font=("Helvetica", 50)) label.grid() def affiche_plot(self, figure): """ Affichage dans la fenêtre du plot correspondant aux statistiques demandées :param figure: élément figure matplotlib à afficher """ self.clean() self.canvas = FigureCanvasTkAgg(figure, master=self) self.canvas.show() self.canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1) toolbar = NavigationToolbar2TkAgg(self.canvas, self) toolbar.update() self.canvas._tkcanvas.pack(side=TOP, fill=BOTH, expand=1)
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 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 " u"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.draw() 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(), branch=self.branch.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(), branch=self.branch.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.draw() 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.draw() 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.draw() 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.draw() 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.draw() 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.draw() 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.draw() 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()