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="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 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()