def plot_line_graph(data_length): lists = sorted(data_length.items()) line_graphs = [] protocols, data_lengths = zip(*lists) i = 0 while i < len(data_lengths): r = random.random() b = random.random() g = random.random() color = (r, g, b) figure = plt.Figure(figsize=(4, 5), dpi=100) ax = figure.add_subplot(111) ax.set_title("Lengths of packets in " + protocols[i] + " protocol") line = FigureCanvasTkAgg(figure, root) line = line.get_tk_widget() line.grid(row=5, column=i) line_graphs.append(line) data = { 'Indices': range(0, len(data_lengths[i])), 'Data lengths': data_lengths[i] } data_frame = DataFrame(data, columns=['Indices', 'Data lengths']) data_frame = data_frame[['Indices', 'Data lengths']].groupby('Indices').sum() data_frame.plot(kind='line', legend=True, ax=ax, color=color) i += 1 return line_graphs
def __init__(self, *args, **kwargs): tk.Tk.__init__(self, *args, **kwargs) global window window = self for button in buttons: row, column, text, press, release = button b = ttk.Button(self, text=text) b.bind("<ButtonPress-1>", (lambda press: (lambda event: press()))(press)) b.bind("<ButtonRelease-1>", (lambda release: (lambda event: release()))(release)) b.grid(row=row, column=column) for checkbox in checkboxes: row, column, text, command = checkbox variable = tk.IntVar() checkbox_variables.append(variable) ttk.Checkbutton(self, text=text, variable=variable, command=command).grid(row=row, column=column) for radio_button_group in radio_button_groups: radio_buttons, command = radio_button_group variable = tk.IntVar() radio_button_variables.append(variable) for radio_button in radio_buttons: row, column, text, value = radio_button ttk.Radiobutton(self, text=text, variable=variable, value=value, command=command).grid(row=row, column=column) for message in messages: row, column, columnspan = message variable = tk.StringVar() message_variables.append(variable) m = tk.Label(self) m.config(font=("Verdana", 14), textvariable=variable) m.grid(row=row, column=column, columnspan=columnspan) global canvas if use_matplotlib: canvas = FigureCanvasTkAgg(figure, self) if fixed_size: axes.set_xlim(0, 1) axes.set_ylim(0, 1) canvas.show() if click_command: canvas.mpl_connect('button_press_event', lambda event: click_command(event.xdata, event.ydata)) canvas.get_tk_widget().grid(row=button_rows, columnspan=button_columns) else: canvas = tk.Canvas(self, width=window_width, height=window_height) canvas.grid(row=button_rows, columnspan=button_columns) self.bind("<Control-c>", lambda event: done()) self.bind("<Escape>", lambda event: done())
def plot_bar_graph(dictionary, key_title, value_title, title): lists = sorted(dictionary.items()) x, y = zip(*lists) data = {key_title: x, value_title: y} data_frame = DataFrame(data, columns=[key_title, value_title]) figure = plt.Figure(figsize=(5, 4), dpi=100) ax = figure.add_subplot(111) ax.set_title(title) bar = FigureCanvasTkAgg(figure, root) bar = bar.get_tk_widget() bar.grid(row=4, column=1) df = data_frame[[key_title, value_title]].groupby(key_title).sum() df.plot(kind='bar', legend=True, ax=ax) return bar
class Main(MainFrame): def content(self, master): # self.button = tk.Button( # master, # text = "Run attack (running run.sh)", # command = self.on_click_hello # ) # self.button.pack(padx=2, pady=2, fill=tk.X) '''Initializing the console inside a scrollbar area''' self.console = '''Initializing and displaying graph inside a canvas''' self.canvas = FigureCanvasTkAgg(calc_and_parse_graph(), master=self.parent) # A tk.DrawingArea. self.canvas.draw() self.canvas.grid(column=0, row=0, colspan=2, rowspan=2) self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=1) #self.canvas.toolbar = NavigationToolbar2Tk(self.canvas, self.parent) #self.canvas.toolbar.update() def on_click_hello(self): print("hello world!")
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 BaseTab(ttk.Frame, ABC): def __init__(self, master=None, **kw): super().__init__(master=master, **kw) self.graph = None @abstractmethod def add_menu(self): pass def add_vertical_separator(self, column): ttk.Separator(self, orient='vertical')\ .grid(row=0, column=column, pady=5, sticky='NS') def add_text_frame(self, row, column): frame = ScrollableFrame(self) frame.grid(row=row, column=column, sticky='NSWE') frame.grid_propagate(False) self.result = InfoLabel(frame.scrollable_frame, font=("Helvetica", 16)) self.result.grid(row=1, column=0) frame.bind_vertical_scroll('<MouseWheel>', self) frame.bind_horizontal_scroll('<MouseWheel>', frame.scrollbar_x) def add_canvas(self, row, column, for_networkX=False): frame = ttk.Frame(self) frame.grid(row=row, column=column, sticky='NSWE') frame.grid_propagate(False) if not for_networkX: self.canvas = ResizingSquareCanvas(frame, width=1, height=1) self.canvas.grid(row=0, column=0) else: class CustomToolbar(NavigationToolbar2Tk): # only display the buttons we need toolitems = [ t for t in NavigationToolbar2Tk.toolitems if t[0] in ('Home', 'Pan', 'Zoom', 'Save') ] figure = Figure(figsize=(5, 4), constrained_layout=True, frameon=False) self.axis = figure.add_subplot(111) self.axis.axis(False) self.canvas = FigureCanvasTkAgg(figure, master=frame) self.toolbar = CustomToolbar(self.canvas, frame) self.toolbar.update() self.canvas.get_tk_widget().pack(side=tk.TOP, fill=tk.BOTH, expand=True) self.canvas.draw() frame.grid_columnconfigure(0, weight=1) frame.grid_rowconfigure(0, weight=1) def draw_graph(self): pass def clear_graph(self): self.graph = None self.canvas.delete('all') def print_graph(self): if self.graph is not None: self.result.show_normal(str(self.graph)) def clear_text(self): self.result.clear() def append_text(self, text): self.result.append(text)
class ImageSession: def __init__(self, master, filename, hdu=0): # filename should be a string! self.filename = filename self.hdu = hdu # The master window? self.master = master if not os.path.exists(self.filename): tkMessageBox.showerror("File not found", "File {0} doesn't exist!".format(file)) return self.openFile() self.createWindow() def openFile(self): # Try to open the file and read in the data from HDU try: hdulist = pf.open(self.filename) try: if hdulist[self.hdu].header['XTENSION'] == 'BINTABLE': tkMessageBox.showerror("HDU Error", "The HDU {0} is not an ImageHDU!".format(self.hdu)) raise util.NotAnImageHDUError("The HDU {0} is not an ImageHDU!".format(self.hdu)) # HDU is a table -- throw an error! except KeyError: # HDU is an image, carry on! pass self.rawData = hdulist[self.hdu].data self.rawData.shape except IOError: tkMessageBox.showerror("FITS file error ", "File {0} does not appear to be a valid FITS file!".format(self.filename)) raise util.ImageSessionError("File {0} does not appear to be a valid FITS file!".format(self.filename)) except AttributeError: tkMessageBox.showerror("FITS file error ", "File {0} does not appear to have data in HDU 0.".format(self.filename)) raise util.ImageSessionError("File {0} does not appear to have data in HDU 0.".format(self.filename)) def createWindow(self): # Create the actual window to draw the image and scale controls self.ImageWindow = Tk.Toplevel(self.master) self.ImageWindow.title(self.filename) #self.ImageWindow.protocol("WM_DELETE_WINDOW", self.closeImage) # TODO: THIS SHOULD DETECT SCREEN RESOLUTION screenSize = (1050/1.5, 1680/1.5) if self.rawData.shape[0] > screenSize[0] and self.rawData.shape[1] > screenSize[1]: factor1 = screenSize[0] / self.rawData.shape[0] factor2 = screenSize[1] / self.rawData.shape[1] self.zoomFactor = min([factor1, factor2]) elif self.rawData.shape[1] > screenSize[0]: self.zoomFactor = screenSize[0]/ self.rawData.shape[1] elif self.rawData.shape[0] > screenSize[1]: self.zoomFactor = screenSize[1] / self.rawData.shape[0] else: self.zoomFactor = 1. # Create the image tools scaleTypeLabel = Tk.Label(self.ImageWindow, text="Scaling: ") scaleTypeLabel.grid(row=0, column=0) self.scalingName = Tk.StringVar() self.scalingName.set("arcsinh") self.scalingOption = Tk.OptionMenu(self.ImageWindow, self.scalingName, "arcsinh","linear","sqrt", command=self.setRescaler) self.scalingOption.grid(row=0, column=1) rescaleLabel = Tk.Label(self.ImageWindow, text="Rescale: ") rescaleLabel.grid(row=1, column=0) self.scaleValue = Tk.Scale(self.ImageWindow, from_=-4, to=6, resolution=0.05, orient=Tk.HORIZONTAL, showvalue=1, length=300) self.scaleValue.set(1.0) self.scaleValue.grid(row=1, column=1) minLabel = Tk.Label(self.ImageWindow, text="Min Pixel Value: ") minLabel.grid(row=2, column=0) self.minValue = Tk.Scale(self.ImageWindow, resolution=0.05, orient=Tk.HORIZONTAL, showvalue=1, length=300) self.minValue.set(1.0) self.minValue.grid(row=2, column=1) maxLabel = Tk.Label(self.ImageWindow, text="Max Pixel Value: ") maxLabel.grid(row=3, column=0) self.maxValue = Tk.Scale(self.ImageWindow, resolution=0.05, orient=Tk.HORIZONTAL, showvalue=1, length=300) self.maxValue.set(1.0) self.maxValue.grid(row=3, column=1) # Set the min/max slider boundaries self.minValue.config(from_=self.rawData.min(), to=self.rawData.max()) self.minValue.set(self.rawData.min()) self.maxValue.config(from_=self.rawData.min(), to=self.rawData.max()) self.maxValue.set(self.rawData.max()) # Set the default rescaler self.rescaler = util.arcsinhStretch self.scaleImage() self.drawImage() # Bind the sliders to the scaleImage() method self.scaleValue.bind("<ButtonRelease-1>", self.redraw) self.minValue.bind("<ButtonRelease-1>", self.redraw) self.maxValue.bind("<ButtonRelease-1>", self.redraw) self.scalingOption.bind("<ButtonRelease-1>", self.redraw) def setRescaler(self, event): self.rescaler = scalingTypes[self.scalingName.get()] def redraw(self, event=None): """ This rescales and redraws the image using the current values for scaling """ self.scaleImage() self.drawImage() def updateThumbnail(self, event): """ """ self.lastMousePosition = (event.x, event.y) self.pilThumbnail = self.pilImage.transform(self.pilImage.size, Image.EXTENT, (event.x-25,event.y-25,event.x+25,event.y+25)) self.thumbnailImage = ImageTk.PhotoImage(self.pilThumbnail.resize((200,200))) self.thumbnailImageLabel.configure(image=self.thumbnailImage) def scaleImage(self): """ This method re-scales the image data """ self.scaledData = 255.0*self.rescaler(self.rawData, beta=10.**self.scaleValue.get(), min=self.minValue.get(), max=self.maxValue.get(), clip=True) def plotContour(self, event): """ If the 'c' key is pressed, generate a contour plot of whatever is in the thumbnail zoom box """ self.contourPlot = Tk.Toplevel(self.master) self.contourPlot.title("Contour plot for: {0}".format(self.filename)) rawShape = self.rawData.shape lastx, lasty = self.lastMousePosition lastx = round(lastx/self.zoomFactor) lasty = round(lasty/self.zoomFactor) boxHalfSize = 25/self.zoomFactor x1,y1,x2,y2 = [x for x in map(round, (lastx-boxHalfSize,lasty-boxHalfSize,lastx+boxHalfSize,lasty+boxHalfSize))] if x1 < 0: x1 = 0 x2 = boxHalfSize*2 if x2 > rawShape[1]: x2 = rawShape[1] x1 = x2 - boxHalfSize*2 if y1 < 0: y1 = 0 y2 = boxHalfSize*2 if y2 > rawShape[0]: y2 = rawShape[0] y1 = y2 - boxHalfSize*2 thumbData = self.rawData[y1:y2, x1:x2] shp = thumbData.shape x,y = np.meshgrid(range(shp[0]), range(shp[1])) self.fig = Figure(figsize=(5,5)) ax = self.fig.add_subplot(111) ax.contour(x, y, thumbData) ax.set_ylim(ax.get_ylim()[::-1]) ax.get_xaxis().set_ticks([]) ax.get_yaxis().set_ticks([]) self.canvas = FigureCanvasTkAgg(self.fig, master=self.contourPlot) self.canvas.get_tk_widget().pack(side='top', fill='both', expand=1) def drawImage(self): """ This method will draw the image into a PhotoImage object in the new image window """ self.pilImage = Image.fromarray(self.scaledData.astype(np.uint8)) newSize = (int(self.pilImage.size[0]*self.zoomFactor), int(self.pilImage.size[1]*self.zoomFactor)) self.pilImage = self.pilImage.resize(newSize) self.tkImage = ImageTk.PhotoImage(self.pilImage) self.canvas = Tk.Canvas(self.ImageWindow, width=newSize[0], height=newSize[1], bd=0) self.canvas.create_image(0, 0, image=self.tkImage, anchor="nw") self.canvas.grid(row=0, column=2, rowspan=5, sticky="nswe") self.canvas.bind("<Motion>", self.updateThumbnail) self.canvas.bind("c", self.plotContour) self.canvas.focus_set() """ # Code for source detection: numSigma = 2. labels, num = snd.label(self.rawData > (numSigma*np.std(self.rawData)), np.ones((3,3))) coords = snd.center_of_mass(self.rawData, labels, range(1,num+1)) rad = 5. for coord in coords: y,x = coord x = x*self.zoomFactor y = y*self.zoomFactor circ1 = self.canvas.create_oval(x-rad,y-rad,x+rad,y+rad, outline='red') """ self.pilThumbnail = self.pilImage.transform(self.pilImage.size, Image.EXTENT, (0,0,50,50)) self.pilThumbnail = self.pilThumbnail.resize((200,200)) self.thumbnailImage = ImageTk.PhotoImage(self.pilThumbnail) self.thumbnailImageLabel = Tk.Label(self.ImageWindow, image=self.thumbnailImage, command=None) self.thumbnailImageLabel.grid(row=4, column=0, columnspan=2, rowspan=5)
def start(m_text_initial, p_text_initial, nfd_text_initial, pfd_text_initial, S_text_initial, explain_initial, options, solver_name, time_limit): matplotlib.use("TkAgg") root = tk.Tk() actions_lookup = [] def quit(): # Close matplotlib figure so program is not left hanging plt.close('all') root.quit() def random_problem(): # Generate random problem m_text, p_text, nfd_text, pfd_text = interface.random_problem() # Set textboxes spinbox_set(m_spinbox, m_text) textbox_set(p_textbox, p_text) textbox_set(nfd_textbox, nfd_text) textbox_set(pfd_textbox, pfd_text) def load_problem(): # Locate file filename = tk.filedialog.askopenfilename(title='Open problem', filetypes=[('Problem files', '*.problem'), ('Any files', '*')]) # Handle closed dialog if not filename: return # Read file success, texts = interface.load_problem(filename) if success: [m_text, p_text, nfd_text, pfd_text] = texts spinbox_set(m_spinbox, m_text) textbox_set(p_textbox, p_text) textbox_set(nfd_textbox, nfd_text) textbox_set(pfd_textbox, pfd_text) else: E_listbox_set(texts) def save_problem(): # Locate file filename = tk.filedialog.asksaveasfilename(title='Save problem as', filetypes=[('Problem files', '*.problem'), ('Any files', '*')]) # Handle closed dialog if not filename: return # Write file success, error = interface.save_problem( filename, m_spinbox.get(), textbox_get(p_textbox), textbox_get(nfd_textbox), textbox_get(pfd_textbox)) if not success: E_listbox_set(error) def optimal_schedule(): # Generate schedule success, text = interface.optimal_schedule( m_spinbox.get(), textbox_get(p_textbox), textbox_get(nfd_textbox), textbox_get(pfd_textbox), solver_name, time_limit) if success: textbox_set(S_textbox, text) else: E_listbox_set(text) def random_schedule(): # Generate schedule success, text = interface.random_schedule( m_spinbox.get(), textbox_get(p_textbox), textbox_get(nfd_textbox), textbox_get(pfd_textbox)) if success: textbox_set(S_textbox, text) else: E_listbox_set(text) def load_schedule(): # Locate file filename = tk.filedialog.askopenfilename(title='Open schedule', filetypes=[('Schedule files', '*.schedule'), ('Any files', '*')]) # Handle closed dialog if not filename: return # Read file success, text = interface.load_text(filename) if success: textbox_set(S_textbox, text) else: E_listbox_set(text) def save_file(text, title, filetypes): # Locate file filename = tk.filedialog.asksaveasfilename(title=title, filetypes=filetypes) # Handle closed dialog if not filename: return # Write file success, error = interface.save_text(filename, text) if not success: E_listbox_set(error) def save_schedule(): save_file(textbox_get(S_textbox), 'Save schedule as', [('Schedule files', '*.schedule'), ('Any files', '*')]) def explain(options=options): success, result = interface.explain( m_spinbox.get(), textbox_get(p_textbox), textbox_get(nfd_textbox), textbox_get(pfd_textbox), textbox_get(S_textbox), options) if success: # Setup reasons E_listbox_set([reason for reason, _ in result]) # Updates actions nonlocal actions_lookup actions_lookup = [actions for _, actions in result] # Select first appliable reason for index, (_, actions) in enumerate(result): if actions: E_listbox.select_set(index) on_select_reason() break fig.canvas.draw() else: E_listbox_set(result) def save_explanation(): save_file(listbox_get(E_listbox), 'Save output as', [('Text files', '*.txt'), ('Any files', '*')]) # Set E_listbox's items while clearing actions def E_listbox_set(lines): actions_lookup = [] listbox_set(E_listbox, lines) listbox_set(action_listbox, []) # Shows suggested actions for a selected reason def on_select_reason(_=None): # If anything has been explained if actions_lookup: # Show suggested actions readable_actions = actions_lookup[E_listbox.curselection()[0]] actions = [action for action, _ in readable_actions] listbox_set(action_listbox, actions) # Select if possible if actions: action_listbox.select_set(0) # Apply selected action to schedule def apply(_=None): # If action is selected indices = list(action_listbox.curselection()) if indices: # Find internal representation of action action_index = indices[0] reason_index = E_listbox.curselection()[0] _, action = actions_lookup[reason_index][action_index] # Apply action success, result = interface.apply( m_spinbox.get(), textbox_get(p_textbox), textbox_get(nfd_textbox), textbox_get(pfd_textbox), textbox_get(S_textbox), action, options) if success: # Unpack results nfd_text, pfd_text, S_text = result # Update textboxes textbox_set(nfd_textbox, nfd_text) textbox_set(pfd_textbox, pfd_text) textbox_set(S_textbox, S_text) # Automate next step, but do not draw again for explanation draw_options = dict(options) draw_options['graphical'] = False explain(draw_options) else: E_listbox_set([result]) # When tab is pressed def focus_next_window(event): event.widget.tk_focusNext().focus() return('break') root.protocol('WM_DELETE_WINDOW', quit) root.title('Schedule Explainer') input_textbox_width = 30 # Create widgets left_frame = tk.Frame(root) problem_frame = tk.LabelFrame(left_frame, text='Problem') problem_command_frame = tk.Frame(problem_frame) random_problem_button = tk.Button(problem_command_frame, text='Randomise', command=random_problem) load_problem_button = tk.Button(problem_command_frame, text='Load', command=load_problem) save_problem_button = tk.Button(problem_command_frame, text='Save', command=save_problem) m_label = tk.Label(problem_frame, text='Number of machines', anchor=tk.E) m_spinbox = tk.Spinbox(problem_frame, from_=0, to_=tk.sys.maxsize) p_label = tk.Label(problem_frame, text='Processing times', anchor=tk.E) p_textbox = tk.Text(problem_frame, height=3, width=input_textbox_width) p_scrollbar = attach_scrollbar(problem_frame, p_textbox) p_textbox.bind('<Tab>', focus_next_window) nfd_label = tk.Label(problem_frame, text='Negative fixed decisions', anchor=tk.E) nfd_textbox = tk.Text(problem_frame, height=3, width=input_textbox_width) nfd_scrollbar = attach_scrollbar(problem_frame, nfd_textbox) nfd_textbox.bind('<Tab>', focus_next_window) pfd_label = tk.Label(problem_frame, text='Positive fixed decisions', anchor=tk.E) pfd_textbox = tk.Text(problem_frame, height=3, width=input_textbox_width) pfd_scrollbar = attach_scrollbar(problem_frame, pfd_textbox) pfd_textbox.bind('<Tab>', focus_next_window) S_frame = tk.LabelFrame(left_frame, text='Schedule') S_command_frame = tk.Frame(S_frame) S_optimal_schedule_button = tk.Button(S_command_frame, text='Optimise', command=optimal_schedule) S_random_button = tk.Button(S_command_frame, text='Randomise', command=random_schedule) load_S_button = tk.Button(S_command_frame, text='Load', command=load_schedule) save_S_button = tk.Button(S_command_frame, text='Save', command=save_schedule) S_textbox = tk.Text(S_frame, height=3, width=input_textbox_width) S_textbox.bind('<Tab>', focus_next_window) S_scrollbar = attach_scrollbar(S_frame, S_textbox) explain_button = tk.Button(S_frame, text='Explain', command=explain) right_frame = tk.Frame(root) fig = plt.figure(0) S_figure = FigureCanvasTkAgg(fig, master=right_frame).get_tk_widget() E_frame = tk.LabelFrame(right_frame, text='Explanation') E_listbox = tk.Listbox(E_frame, exportselection=False) E_listbox.bind('<<ListboxSelect>>', on_select_reason) E_listbox.bind('<Return>', apply) E_scrollbar = attach_scrollbar(E_frame, E_listbox) save_E_button = tk.Button(E_frame, text='Save', command=save_explanation) action_frame = tk.LabelFrame(right_frame, text='Actions') action_listbox = tk.Listbox(action_frame) action_listbox.bind('<Return>', apply) action_scrollbar = attach_scrollbar(action_frame, action_listbox) apply_action_button = tk.Button(action_frame, text='Apply', command=apply) # Geometry padding = 8 h_fill = tk.W + tk.E v_fill = tk.N + tk.S fill = h_fill + v_fill root.geometry("1280x720") root.rowconfigure(0, weight=1) root.columnconfigure(1, weight=1) left_frame.grid(row=0, column=0, sticky=v_fill) left_frame.rowconfigure(0, weight=1) left_frame.rowconfigure(1, weight=1) problem_frame.grid(row=0, column=0, padx=padding, pady=padding, sticky=v_fill) problem_command_frame.grid(row=0, column=0, columnspan=3, padx=padding, pady=padding, sticky=h_fill) problem_command_frame.columnconfigure(0, weight=1) problem_command_frame.columnconfigure(1, weight=1) problem_command_frame.columnconfigure(2, weight=1) random_problem_button.grid(row=0, column=0, sticky=h_fill) load_problem_button.grid(row=0, column=1, padx=padding, sticky=h_fill) save_problem_button.grid(row=0, column=2, sticky=h_fill) problem_frame.rowconfigure(2, weight=1) problem_frame.rowconfigure(3, weight=1) problem_frame.rowconfigure(4, weight=1) m_label.grid(row=1, column=0, padx=padding, sticky=h_fill) m_spinbox.grid(row=1, column=1, columnspan=2, padx=(0, padding), sticky=h_fill) p_label.grid(row=2, column=0, padx=padding, pady=padding, sticky=h_fill) p_textbox.grid(row=2, column=1, pady=padding, sticky=v_fill) p_scrollbar.grid(row=2, column=2, padx=(0, padding), pady=padding, sticky=v_fill) nfd_label.grid(row=3, column=0, padx=padding, sticky=h_fill) nfd_textbox.grid(row=3, column=1, sticky=v_fill) nfd_scrollbar.grid(row=3, column=2, padx=(0, padding), sticky=v_fill) pfd_label.grid(row=4, column=0, padx=padding, pady=padding, sticky=h_fill) pfd_textbox.grid(row=4, column=1, pady=padding, sticky=v_fill) pfd_scrollbar.grid(row=4, column=2, padx=(0, padding), pady=padding, sticky=v_fill) S_frame.grid(row=1, column=0, padx=padding, pady=(0, padding), ipadx=50, sticky=fill) S_frame.rowconfigure(1, weight=1) S_frame.columnconfigure(0, weight=1) S_command_frame.grid(row=0, column=0, columnspan=2, padx=padding, pady=padding, sticky=h_fill) S_command_frame.columnconfigure(0, weight=1) S_command_frame.columnconfigure(1, weight=1) S_command_frame.columnconfigure(2, weight=1) S_command_frame.columnconfigure(3, weight=1) S_optimal_schedule_button.grid(row=0, column=0, padx=(0, padding), sticky=h_fill) S_random_button.grid(row=0, column=1, padx=(0, padding), sticky=h_fill) load_S_button.grid(row=0, column=2, padx=(0, padding), sticky=h_fill) save_S_button.grid(row=0, column=3, sticky=h_fill) S_textbox.grid(row=1, column=0, padx=(padding, 0), sticky=fill) S_scrollbar.grid(row=1, column=1, padx=(0, padding), sticky=v_fill) explain_button.grid(row=2, column=0, columnspan=2, padx=padding, pady=padding, sticky=h_fill) right_frame.grid(row=0, column=1, sticky=fill) right_frame.rowconfigure(1, weight=1) right_frame.columnconfigure(0, weight=1) right_frame.columnconfigure(1, weight=1) S_figure.grid(row=0, column=0, columnspan=2, sticky=h_fill, padx=padding, pady=padding) E_frame.grid(row=1, column=0, sticky=fill, padx=padding, pady=(0, padding)) E_frame.rowconfigure(0, weight=1) E_frame.columnconfigure(0, weight=1) E_listbox.grid(row=0, column=0, sticky=fill, padx=(padding, 0), pady=(padding, 0)) E_scrollbar.grid(row=0, column=1, sticky=v_fill, padx=(0, padding), pady=(padding, 0)) save_E_button.grid(row=1, column=0, columnspan=2, padx=padding, pady=padding, sticky=h_fill) action_frame.grid(row=1, column=1, sticky=fill, padx=padding, pady=(0, padding)) action_frame.rowconfigure(0, weight=1) action_frame.columnconfigure(0, weight=1) action_frame.rowconfigure(0, weight=1) action_listbox.grid(row=0, column=0, sticky=fill, padx=(padding, 0), pady=(padding, 0)) action_scrollbar.grid(row=0, column=1, sticky=v_fill, padx=(0, padding), pady=(padding, 0)) apply_action_button.grid(row=1, column=0, columnspan=2, padx=padding, pady=padding, sticky=h_fill) # Initial state from command line spinbox_set(m_spinbox, m_text_initial) textbox_set(p_textbox, p_text_initial) textbox_set(nfd_textbox, nfd_text_initial) textbox_set(pfd_textbox, pfd_text_initial) textbox_set(S_textbox, S_text_initial) if explain_initial: explain() root.mainloop()
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()
def intial(self): self.note.add(self.tab1, text="SPI graph") #,image=scheduledimage, compound=TOP) #self.note.pack() label = tk.Label(self.tab1, text="Graph Page!") label.grid(row=1) #,pady=3,padx=3) exit_btn = tk.Button(self.tab1, text='Go back to main page', command=self.close, activebackground='grey', activeforeground='#AB78F1', bg='#58F0AB', highlightcolor='red', padx='10px', pady='3px') exit_btn.grid(row=2, column=2) import matplotlib.pyplot as plt from matplotlib.figure import Figure from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg, NavigationToolbar2TkAgg ## fig = Figure(figsize=(7,5), dpi=200) fig = plt.figure() num = int("%i11" % len(self.df.columns)) i = 0 ax = [] for col in self.df.columns: ax1 = fig.add_subplot(num + i) ax.append(ax1) spi_pos = self.df[col].clip(lower=0).to_frame(col) spi_neg = self.df[col].clip(upper=0).to_frame(col) ## print(spi) self.df[col].plot(ax=ax[i], color="black") plt.fill_between(spi_neg.index, spi_neg[col].values, color="r") plt.fill_between(spi_pos.index, spi_pos[col].values, color="b") ax1.legend(loc='right', bbox_to_anchor=(1.15, 0.9), shadow=True, fontsize=10) i += 1 #plt.show() self.frame = ttk.Frame(self.tab1) self.frame.grid(row=2, sticky=tk.W + tk.E) canvas = FigureCanvasTkAgg(fig, self.frame) canvas.draw() canvas = canvas.get_tk_widget() canvas.grid(row=1, column=0, rowspan=10, columnspan=4, sticky=tk.W + tk.E + tk.N + tk.S) f = [] for col in self.df.columns: tab = ttk.Frame(self.note) f.append(tab) self.note.add(tab, text=col) df2 = self.df[col].to_frame(col) df2.dropna(inplace=True) #print (df2.head()) import statsmodels.api as sm decomposition = sm.tsa.seasonal_decompose(df2, model='additive') fig2 = decomposition.plot() plt.figure(figsize=(15, 5)) self.frame = ttk.Frame(tab) self.frame.grid(row=2, sticky=tk.W + tk.E) canvas = FigureCanvasTkAgg(fig2, self.frame) canvas.draw() canvas = canvas.get_tk_widget() canvas.grid(row=1, column=0, rowspan=10, columnspan=4, sticky=tk.W + tk.E + tk.N + tk.S) self.tab2 = ttk.Frame(self.note) self.note.add(self.tab2, text="Frequency") fig2 = plt.figure() num = int("%i11" % len(self.df.columns)) i = 0 ax = [] for col in self.df.columns: ax2 = fig2.add_subplot(num + i) ax.append(ax2) SPI = self.df[col].to_frame(col) SPI.dropna(inplace=True) print(SPI.head()) SPI["Class"] = SPI[col].apply(reclass) SPIgroup = SPI.groupby(by="Class") count = SPIgroup.count() d = SPIclasses.join(count) print(d.head()) plt.style.use('ggplot') color_palette_list = [ '#009ACD', '#ADD8E6', '#63D1F4', '#0EBFE9', '#C1F0F6', '#0099CC', '#909090' ] d.plot(kind='barh', figsize=(12, 7), color=color_palette_list, fontsize=6, ax=ax[i]) #plt.set_title('Amount Frequency of %s'%col) ax2.set_xlabel('Frequency') ax2.set_ylabel('Classes') ## rects = fig2.patches autolabel(ax2, "left") i += 1 self.frame1 = ttk.Frame(self.tab2) self.frame1.grid(row=2, sticky=tk.W + tk.E) canvas = FigureCanvasTkAgg(fig2, self.frame1) canvas.draw() canvas = canvas.get_tk_widget() canvas.grid(row=1, column=0, rowspan=10, columnspan=4, sticky=tk.W + tk.E + tk.N + tk.S) ## #print(self.note[col]) ## tab_names = [] ## for i in self.note.tabs(): ## print (i) ## tab_names.append(self.note.tab(i, "text")) ## print (tab_names) ## #### #### plt.show() ## ## ## #fig.savefig(out[:-3]+"png") ## a = f.add_subplot(111) ## a.plot([1,2,3,4,5,6,7,8],[5,6,1,3,8,9,3,5]) self.note.pack()
y2 = [i**2 for i in x] plt.xlabel("x") plt.ylabel("y") graf=plt.plot(x,y,x,y2) #fig=plt.figure() lbl=FigureCanvasTkAgg(fig,root) #lbl.get_tk_widget().grid(row=11,column=2,columnspan=10) lbl.get_tk_widget().grid(column=0,row=9,columnspan=10) lbl.draw() FileName=Entry(root,width=30,textvariable=name) FileName.grid(row=0, column=1, columnspan=3,pady=10) FileName.insert(0,'img_1.jpg') lbl = Label(root, text=" X1 ") lbl.grid(column=0, row=5,pady=10) txt11 = Entry(root,width=10) txt11.grid(column=1, row=5,pady=10) lbl = Label(root, text=" X2 ") lbl.grid(column=2, row=5,pady=10) txt12 = Entry(root,width=10) txt12.grid(column=3, row=5,pady=10) lbl = Label(root, text=" Y1 ") lbl.grid(column=0, row=7,pady=10) txt21 = Entry(root,width=10) txt21.grid(column=1, row=7,pady=10) lbl = Label(root, text=" Y2 ") lbl.grid(column=2, row=7,pady=10) txt22 = Entry(root,width=10) txt22.grid(column=3, row=7,pady=10) render=((img_rows, img_cols))
class GUI(): def __init__(self, tempDir): self.tempDir = tempDir self.done = False self.bg = "#f0f0f0" self.btnCol = "light grey" self.files = [] self.scales = [1 for x in range(10)] self.empty = [True for x in range(10)] self.data = [0 for x in range(10)] self.figures = [0 for x in range(10)] self.views = [-1 for x in range(10)] self.addresses = [0 for x in range(10)] self.resultTypes = [0 for x in range(10)] self.current = 0 def main_menu(self): #Displays the main menu page #========== Setup root = tk.Tk() root.configure(bg=self.bg) root.state("zoomed") h = root.winfo_screenheight() #========== Weighting root.rowconfigure(1, weight=1) root.rowconfigure(3, weight=1) root.rowconfigure(5, weight=1) root.rowconfigure(7, weight=1) root.rowconfigure(9, weight=1) root.columnconfigure(0, weight=1) #========== Widgets #===== Row 0, 1, 2 tk.Frame(root, bg=self.bg, height=round(h * 0.1)).grid(row=0, column=0) tk.Label(root, bg=self.bg, text="Microscope image analyser", justify="center", font=("Arial", round(h * 0.06))).grid(row=1, column=0) tk.Frame(root, bg=self.bg, height=round(h * 0.1)).grid(row=2, column=0) #===== Row 3, 4 tk.Button(root, bg=self.btnCol, text="Image analysis", justify="center", font=("Calibri", round(h * 0.03)), width=30, command=partial(self.select_file, root, "Select image to analyse", ("Png files", "*.png"), 0)).grid(row=3, column=0) tk.Frame(root, bg=self.bg).grid(row=4, column=0) #===== Row 5, 6 tk.Button(root, bg=self.btnCol, text="Load saved data", justify="center", font=("Calibri", round(h * 0.03)), width=30, command=partial(self.select_file, root, "Select results file", ("Text files", "*.txt"), 1)).grid(row=5, column=0) tk.Frame(root, bg=self.bg).grid(row=6, column=0) #===== Row 7, 8 tk.Button( root, bg=self.btnCol, text="View results", justify="center", font=("Calibri", round(h * 0.03)), width=30, command=lambda: [root.destroy(), self.results()]).grid(row=7, column=0) tk.Frame(root, bg=self.bg).grid(row=8, column=0) #===== Row 9 tk.Button(root, bg=self.btnCol, text="Quit", justify="center", font=("Calibri", round(h * 0.03)), width=30, command=root.destroy).grid(row=9, column=0) #===== Row 10 tk.Frame(root, height=round(h * 0.2)).grid(row=10, column=0) #========== Mainloop root.mainloop() def plot_histogram(self): #Creates a figure containing a histogram fig = Figure() fig.patch.set_facecolor(self.bg) plot = fig.gca() #Creates plot for histogram to be on plot.hist(self.data[self.current], 30) #Plot histogram with 30 bins plot.yaxis.set_major_locator( MaxNLocator(integer=True)) #Make ticks integers plot.set_xlabel("Diameter", fontsize=20) #Set label size plot.set_ylabel("Frequency", fontsize=20) plot.tick_params(labelsize=15) #Set tick label size self.figures[self.current] = fig #Record new figure def remove_entry(self, index, refresh, root=None): #Removes an item from the stored results, given the index def remove(array, index): #Removes 1 item from a list given its index return [array[x] for x in range(len(array)) if x != index] self.files = remove(self.files, index) self.scales = remove(self.scales, index) self.scales.append(1) self.empty = remove(self.empty, index) self.empty.append(True) self.data = remove(self.data, index) self.data.append(0) self.figures = remove(self.figures, index) self.figures.append(0) self.views = remove(self.views, index) self.views.append(-1) self.addresses = remove(self.addresses, index) self.addresses.append(0) self.resultTypes = remove(self.resultTypes, index) self.resultTypes.append(0) if refresh: if self.current > index: self.current -= 1 if self.current >= len(self.files) and len(self.files) > 0: self.current = len(self.files) - 1 root.destroy() self.results() def results(self): #Displays analysis results page #========== Setup root = tk.Tk() root.configure(bg=self.bg) root.state("zoomed") w = root.winfo_screenwidth() h = root.winfo_screenheight() #========== Weighting / padding root.rowconfigure(0, pad=h * 0.05) root.rowconfigure(12, pad=h * 0.05) root.columnconfigure(1, weight=1) root.columnconfigure(2, weight=1) root.columnconfigure(3, weight=1) root.columnconfigure(4, weight=1) #========== Widgets #===== Row 0 tk.Button( root, bg=self.btnCol, text="Main menu", justify="center", font=("Calibri", round(h * 0.02)), command=lambda: [root.destroy(), self.main_menu()]).grid( row=0, column=0, sticky="NW") self.resultsTitle = tk.StringVar() tk.Label(root, bg=self.bg, textvariable=self.resultsTitle, justify="center", font=("Arial", round(h * 0.04))).grid(row=0, column=0, columnspan=6) #===== Other results tk.Label(root, bg=self.bg, text="Other results:", justify="left", font=("Calibri", round(h * 0.03))).grid(row=1, column=5) self.others = [] for x in range(10): try: self.others.append( tk.Button(root, bg=self.btnCol, text=self.files[x], justify="left", font=("Calibri", round(h * 0.02)), width=20, command=partial(self.set_results, x, root))) except IndexError: self.others.append( tk.Button(root, bg=self.btnCol, text="", justify="left", font=("Calibri", round(h * 0.02)), width=20, command=partial(self.set_results, x, root))) self.others[x].grid(row=x + 2, column=5) tk.Button(root, bg=self.btnCol, text="X", justify="center", font=("Arial", round(h * 0.02)), width=2, command=partial(self.remove_entry, x, True, root)).grid(row=x + 2, column=6) #===== Row 12 tk.Label(root, bg=self.bg, text="Scale:", font=("Calibri", round(h * 0.03))).grid(row=12, column=0, sticky="E") root.bind("<Return>", partial(self.update_scale, root)) self.scaleEntry = tk.StringVar() tk.Entry(root, textvariable=self.scaleEntry, font=("Calibri", round(h * 0.03)), width=10).grid(row=12, column=1, sticky="W") tk.Button(root, bg=self.btnCol, text="Original image", width=15, font=("Calibri", round(h * 0.03)), command=partial(self.set_view, root, 1)).grid(row=12, column=2) tk.Button(root, bg=self.btnCol, text="Analysed image", width=15, font=("Calibri", round(h * 0.03)), command=partial(self.set_view, root, 2)).grid(row=12, column=3) tk.Button(root, bg=self.btnCol, text="Histogram", width=15, font=("Calibri", round(h * 0.03)), command=partial(self.set_view, root, 0)).grid(row=12, column=4) tk.Button(root, bg=self.btnCol, text="Save", width=10, font=("Calibri", round(h * 0.03)), command=self.save).grid(row=12, column=5) self.set_results(self.current, root) self.set_view(root, 0) root.mainloop() def save(self): #Writes the scale adjusted diameters to txt file in csv format name = self.files[self.current] #Get shorter identifier name = name[:name.rindex(".")] + "-results.txt" address = filedialog.asksaveasfilename(title="Save results file", initialfile=name, filetype=(("Text files", "*.txt"), ("All files", "*.*"))) file = open(address, "w+") file.write(','.join([str(X) for X in self.data[self.current]])) file.close( ) #Store diameters including scale adjustment, fstring used so it is 1 argument def select_file(self, root, title, filetype, resultType): #Allows the user to select an image file from a directory file = filedialog.askopenfilename(title=title, filetypes=(filetype, ("All files", "*.*"))) if file != "": #If operation not cancelled self.files.append(file[file.rindex("/") + 1:]) #Get filename only if len(self.files) > 10: #If list full, replace oldest item self.remove_entry(0, False) self.current = 9 self.current = len(self.files) - 1 self.addresses[self.current] = file self.resultTypes[self.current] = resultType root.destroy() if resultType == 0: #Image analysis pic = image(file, self.tempDir) #Do overall analysis particles = pic.create_objects() #Do particle analysis self.data[self.current] = [P.diameter for P in particles] elif resultType == 1: #Reading results file text = open(file, "r").read() self.data[self.current] = text.split(",") self.data[self.current] = [ float(X) for X in self.data[self.current] ] self.plot_histogram() #Create histogram figure self.results() def set_results(self, new, root): #Changes to a new set of analysis results if len(self.files) > new: #If results exist self.others[self.current].config( relief="raised") #Deselect previous self.current = new self.others[self.current].config(relief="sunken") #select current self.resultsTitle.set( f"Results - {self.files[self.current]}") #Change title if self.empty[self.current]: self.scaleEntry.set("") #If no scale set don't display scale else: self.scaleEntry.set(str(float(self.scales[self.current] * 127))) #Else do display scale self.set_view(root) def set_view(self, root, view=None): #Changes to viewing histogram, original image, or analysed image w = root.winfo_screenwidth() h = root.winfo_screenheight() if self.views[self.current] == 0: try: self.histogram.destroy() #Destroy histogram except Exception: pass elif self.views[self.current] == 1 or self.views[ self.current] == 2: #If previous was image try: self.picWidget.destroy() #Destroy image except Exception: pass if view != None: self.views[self.current] = view #Update to new view if self.views[self.current] == 0: self.histogram = FigureCanvasTkAgg( self.figures[self.current], master=root).get_tk_widget() #Get widget self.histogram.config(width=round(w * 0.75), height=round(h * 0.7)) #Resize cavas self.histogram.grid(row=1, column=0, rowspan=11, columnspan=5) #Place canvas elif self.views[self.current] == 1 or self.views[self.current] == 2: if self.resultTypes[self.current] == 0: if self.views[self.current] == 1: file = self.addresses[ self.current] #Get shorter version of file name pic = ImageTk.PhotoImage( Image.open(file)) #Get original image else: file = self.files[self.current] file = self.tempDir + "\\" + file[:file.rindex( '.')] + "-analysed" + file[file.rindex('.'):] pic = ImageTk.PhotoImage( Image.open(file)) #Get analysed image self.picWidget = tk.Label(root, image=pic, width=round(w * 0.75), height=round(h * 0.7)) #Image label self.picWidget.image = pic #Stops the image being discarded by memory manager self.picWidget.grid(row=1, column=0, rowspan=11, columnspan=5) else: self.views[self.current] = 0 self.set_view(root, 0) def update_scale(self, root, key): #Records the scale if it is valid old = self.scales[self.current] try: self.scales[self.current] = float( self.scaleEntry.get()) / 127 #Get scale if self.scales[self.current] > 0: for x in range(len(self.data[ self.current])): #Adjust each item to new scale self.data[ self.current][x] *= self.scales[self.current] / old self.plot_histogram() #Replot histogram self.empty[ self.current] = False #Record that scale has been set self.set_results(self.current, root) #Redraw canvas else: self.scales[self.current] = old except ValueError: pass #Unless not valid
class DataAquisitionUI(UserInterface): def __init__( self, layout_path, data_src=[DummyDataSource()], vis_data=100 ): super().__init__(layout_path) self.plt_config = self.ui_config['plot config'] self.running=True self.collect = False self.vis_data = vis_data self.data_src = data_src self.protocol("WM_DELETE_WINDOW", self.quit_app) self.init_plot() self.cleanup() def init_plot(self): """ Sets up plot with colors and formats. Turns on interactive mode and places the plot in the grid of the parent. """ pltconf = self.plt_config["global config"] bg_color, fg_color = pltconf['bg'], pltconf['fg'] self.figure, self.ax1 = subplots(facecolor=bg_color) self.ax2 = self.ax1.twinx() self.ax1.set_xlabel(pltconf['x label']) self.ax1.set_ylabel(pltconf['y1 label']) self.ax2.set_ylabel(pltconf['y2 label']) self.lines = [self.ax1.add_line( mlplines.Line2D([], [], label=label, **vals) ) for label, vals in self.plt_config['lines axis 1'].items() ] self.lines += [self.ax2.add_line( mlplines.Line2D([], [], label=label, **vals) ) for label, vals in self.plt_config['lines axis 2'].items() ] for line in self.lines: line.xdata, line.ydata = [], [] labels = [l.get_label() for l in self.lines] legend = self.ax2.legend( self.lines, labels, loc='upper left', facecolor=bg_color, edgecolor=bg_color, framealpha=1.0 ) for text in legend.get_texts(): text.set_color(fg_color) for axis in [self.ax1, self.ax2]: axis.set_facecolor(bg_color) for side in ['bottom', 'top', 'right', 'left']: axis.spines[side].set_color(fg_color) axis.tick_params(axis='x', colors=fg_color) axis.tick_params(axis='y', colors=fg_color) axis.yaxis.label.set_color(fg_color) axis.xaxis.label.set_color(fg_color) axis.grid(color=fg_color) axis.set_autoscaley_on(True) axis.set_autoscalex_on(True) tight_layout() ion() self.data_plot = PltCanvas(self.figure, master=self).get_tk_widget() self.data_plot.grid(**pltconf['grid']) def cleanup(self): """ Re-initializes the parameters for data collection and clears the console screen. """ for line in self.lines: line.xdata = [] line.ydata = [] self.data = [] def daq_loop(self): """ Part of the mainloop. This function will run every 0.5 seconds. If the "collect" parameter is set to True it will append the data it collects to an array to be saved to the output file. The loop functions with these instructions: - Get data as a list from some source - Save the data to an array if self.collect == True - Update the graph with the latest data point - Loop This will execute on startup and continue doing while mainloop is still running. """ data_point = [] for src in self.data_src: data_point += src.get_data() if not self.valid_data(data_point): self.after(0, self.daq_loop) return self.process_data(data_point) if self.collect: self.data_collect(data_point) self.graph_data(data_point) if self.running: self.after(0, self.daq_loop) def graph_data(self, data1): """ Update the data to be graphed and then update the graph """ for i, line in enumerate(self.lines): assert len(line.xdata) == len(line.ydata) if len(line.xdata) > self.vis_data: line.xdata.pop(0) line.ydata.pop(0) line.xdata.append(data1[0]) line.ydata.append(data1[i+1]) line.set_xdata(line.xdata) line.set_ydata(line.ydata) self.ax1.relim() self.ax2.relim() self.ax1.autoscale_view() self.ax2.autoscale_view() self.figure.canvas.draw() self.figure.canvas.flush_events() def valid_data(self, data_point): """ Check that data is valid and do any preprocessing on the data. Must return True or False. """ print(data_point) return True def process_data(self, data_point): """ Do any processing needed before data collection and graphing. """ pass def data_collect(data_point): """ Processing for data collection. """ pass def quit_app(self, event=None): """Safely exits the program.""" if self.collect: if not askyesno( "Quit Warning", "DAQ is still running!\nAre you sure you want to quit?" ): return self.running = False self.destroy() raise SystemExit