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)
Example #2
0
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()
Example #3
0
class matplotlibSwitchGraphs:
    def __init__(self, master):
        self.master = master
        self.frame = Frame(self.master)
        self.fig, self.ax = config_plot()
        self.graphIndex = 0
        self.canvas = FigureCanvasTkAgg(self.fig, self.master)  
        self.config_window()
        self.draw_graph('janvier')
        self.frame.pack(expand=YES, fill=BOTH)

    def config_window(self):
        self.canvas.mpl_connect("key_press_event", self.on_key_press)
        toolbar = NavigationToolbar2Tk(self.canvas, self.master)
        toolbar.update()
        self.canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)
        self.button = Button(self.master, text="Quit", command=self._quit)
        self.button.pack(side=BOTTOM)
        self.button_back = Button(self.master, text="Graphique précédent", command=self.back_graph)
        self.button_back.pack(side=BOTTOM)
        self.button_next = Button(self.master, text="Graphique suivant", command=self.next_graph)
        self.button_next.pack(side=BOTTOM)
    def draw_graph(self, month):

        if(month == 'année'):
            df_temp = pd.DataFrame(columns = ['Température'])
            for column in df:
            for value in df[column]:
                df_temp = df_temp.append({'Températeure':value},ignore_index=True)

            df_temp.dropna()
            self.ax.clear()
            self.ax.plot(df_temp['Température'])
            self.ax.set(title='Année')
            self.canvas.draw()
        else:
            self.ax.clear()
            self.ax.plot(df[month])
            self.ax.set(title=month)
            self.canvas.draw()
        

    def on_key_press(event):
        key_press_handler(event, self.canvas, toolbar)

    def _quit(self):
        self.master.quit() 

    def next_graph(self):
        if self.graphIndex == 0:
            self.draw_graph('février')
            self.graphIndex = 1
        elif self.graphIndex == 1:
            self.draw_graph('mars')
            self.graphIndex = 2
        elif self.graphIndex == 2:
            self.draw_graph('avril')
            self.graphIndex = 3
        elif self.graphIndex == 3:
            self.draw_graph('mai')
            self.graphIndex = 4
        elif self.graphIndex == 4:
            self.draw_graph('juin')
            self.graphIndex = 5
        elif self.graphIndex == 5:
            self.draw_graph('juillet')
            self.graphIndex = 6
        elif self.graphIndex == 6:
            self.draw_graph('août')
            self.graphIndex = 7
        elif self.graphIndex == 7:
            self.draw_graph('septembre')
            self.graphIndex = 8
        elif self.graphIndex == 8:
            self.draw_graph('octobre')
            self.graphIndex = 9
        elif self.graphIndex == 9:
            self.draw_graph('novembre')
            self.graphIndex = 10
        elif self.graphIndex == 10:
            self.draw_graph('décembre')
            self.graphIndex = 11
        elif self.graphIndex == 11:
            self.draw_graph('janvier')
            self.graphIndex = 0
        elif self.graphIndex == 12:
            self.draw_graph('année')
            self.graphIndex = 12

    def back_graph(self):
        if self.graphIndex == 0:
            self.draw_graph('décembre')
            self.graphIndex = 11
        elif self.graphIndex == 11:
            self.draw_graph('novembre')
            self.graphIndex = 10
        elif self.graphIndex == 10:
            self.draw_graph('octobre')
            self.graphIndex = 9
        elif self.graphIndex == 9:
            self.draw_graph('septembre')
            self.graphIndex = 8
        elif self.graphIndex == 8:
            self.draw_graph('août')
            self.graphIndex = 7
        elif self.graphIndex == 7:
            self.draw_graph('juillet')
            self.graphIndex = 6
        elif self.graphIndex == 6:
            self.draw_graph('juin')
            self.graphIndex = 5
        elif self.graphIndex == 5:
            self.draw_graph('mai')
            self.graphIndex = 4
        elif self.graphIndex == 4:
            self.draw_graph('avril')
            self.graphIndex = 3
        elif self.graphIndex == 3:
            self.draw_graph('mars')
            self.graphIndex = 2
        elif self.graphIndex == 2:
            self.draw_graph('février')
            self.graphIndex = 1
        elif self.graphIndex == 1:
            self.draw_graph('janvier')
            self.graphIndex = 0
        elif self.graphIndex == 12:
            self.draw_graph('année')
            self.graphIndex = 12


def main():
    root = Tk()
    matplotlibSwitchGraphs(root)
    root.mainloop()

def show_graph():
    main()

fenetre = Tk()
fenetre.title("Data tp 1")
fenetre.geometry('800x800')
canvas=Canvas(fenetre,bg='#FFFFFF',width=800,height=800,scrollregion=(0,0,1500,1500))
hbar=Scrollbar(fenetre,orient=HORIZONTAL)
hbar.pack(side=BOTTOM,fill=X)
hbar.config(command=canvas.xview)
vbar=Scrollbar(fenetre,orient=VERTICAL)
vbar.pack(side=RIGHT,fill=Y)
vbar.config(command=canvas.yview)
canvas.config(width=800,height=800)
canvas.config(xscrollcommand=hbar.set, yscrollcommand=vbar.set)
canvas.pack(side=LEFT,expand=True,fill=BOTH)

for index, column in enumerate(df):
    canvas.create_text(100,(index*120)+20,fill="black",font="Times 15 bold",
                        text="Mois : "+str(column))
    canvas.create_text(100,(index*120)+40,fill="black",font="Times 10",
                            text="Moyenne : "+str(df[column].mean()))
    canvas.create_text(100,(index*120)+60,fill="black",font="Times 10",
    text="Ecart type : "+str(df[column].std()))
    canvas.create_text(100,(index*120)+80,fill="black",font="Times 10",
    text="Minimum : "+str(df[column].min()))
    canvas.create_text(100,(index*120)+100,fill="black",font="Times 10",
    text="Maximum : "+str(df[column].max()))

b = Button(fenetre, text="Afficher les graphiques", width=10, command=show_graph,background="white",foreground="black",activebackground="white",activeforeground="black")
b.place(x=300, y=20, anchor="nw", width=150, height=30)

fenetre.mainloop()
Example #4
0
class LeftFrame(Frame):
    """
    Classe pour la "frame" qui sera la partie gauche de l'insterface.
    Celle-ci accueillera les grilles et les plots des statistiques
    """
    def __init__(self, parent):
        Frame.__init__(self, parent)
        self.parent = parent
        self.canvas = None
        self.nb_colonnes = 0
        self.nb_lignes = 0
        self.pas_colonne = 0
        self.pas_ligne = 0
        self.initialize()

    def initialize(self):
        self.affiche_grille(self.parent.grilles[0])
        self.rescale()

    def clean(self):
        """
        Vide le contenu de la frame en supprimant les objets contenus dans celle-ci,
        en vue de le remplacer par autre chose
        """
        for i in self.winfo_children():
            i.destroy()

    def rescale(self):
        """
        Change l'échelle prise par la fenêtre pour un affichage plus pratique,
        on n'a alors plus qu'à spécifier les coordonnées comme dans la grille et cette fonction repositionne tout
        pour que l'affichage fasse la taille de la fenêtre
        """
        if self.canvas is not None and self.pas_colonne != 0 and self.pas_ligne != 0:
            self.canvas.scale(ALL, -1, -1, self.pas_colonne, self.pas_ligne)

    def afficher_resultat(self, resultat, tps_creat=None, tps_calc=None):
        """
        Affiche pour chaque instance du fichier d'entrée:
        la chaîne de caractère représentant le chemin emprunté par le robot,
        ainsi que le temps de création du graphe et le temps de calcul de ce résultat
        :param resultat: liste des chaines de caractères résultats du calcul du chemin le plus rapide des instances
        :param tps_creat: temps de création du graphe pour chaque instance
        :param tps_calc: temps de calcul du chemin le plus rapide pour chaque instance (sans la création du graphe)
        :type resultat: list
        :type tps_creat: list
        :type tps_calc: list
        """
        self.clean()
        if tps_creat:
            label = Label(self,
                          text="La moyenne du temps de création de graphe sur ce fichier est : " +
                               str(np.mean(tps_creat)) + " secondes")
            label.pack()
        if tps_calc:
            label = Label(self,
                          text="La moyenne du temps de calcul du chemin le plus rapide sur ce fichier est : " +
                               str(np.mean(tps_calc)) + " secondes")
            label.pack()
        for i in range(len(resultat)):
            label = Label(self, text="Problème n°"+str(i))
            label.pack()
            label_res = Label(self, text=resultat[i])
            label_res.pack()
            if tps_creat:
                label_tps_creat = Label(self, text="La création du graphe a pris " + str(tps_creat[i]) + " secondes")
                label_tps_creat.pack()
            if tps_calc:
                label_tps_calc = Label(self, text="Le calcul de la solution a pris " + str(tps_calc[i]) + " secondes")
                label_tps_calc.pack()

    def affiche_grille(self, grille):  # Ne pas oublier de faire un rescale après appel à cette fonction !!!
        """
        Affiche la grille passée en paramètre dans la partie gauche de la fenêtre
        :param grille: grille représentant le dépôt constituée de 3 éléments :
            - un tuple pour le nombre de lignes et de colonnes
            - une liste représentant les lignes du dépôt avec les '0' et '1'
            - une liste pour les coordonnées du point de départ, d'arrivée, et l'orientation du robot au départ
        :type grille: list
        """
        self.clean()
        # Récupération des éléments de la grille
        self.nb_lignes, self.nb_colonnes = grille[0]
        lignes = grille[1]
        ligne = grille[2]
        self.canvas = Canvas(self, width=leftframewidth, height=leftframeheight)
        # Récupération des données du problème
        self.pas_colonne = leftframewidth//(self.nb_colonnes + 2)
        self.pas_ligne = leftframeheight//(self.nb_lignes + 2)
        rayon = 1/2  # rayon des cercles du robot au départ et à l'arrivée
        # Dessin du quadrillage
        for i in range(0, self.nb_lignes):
            for j in range(0, self.nb_colonnes):
                if lignes[i][j] == '0':
                    rectangle(self.canvas, j, i, j+1, i+1)
                else:
                    rectangle(self.canvas, j, i, j+1, i+1, color=couleur_obstacles)
        # Dessin du point de départ du robot avec sa flèche
        dessine_depart(self.canvas, int(ligne[1]), int(ligne[0]), rayon, ligne[-1])
        # Dessin du point d'arrivée du robot
        cercle(self.canvas, int(ligne[3]), int(ligne[2]), rayon)
        self.canvas.pack()

    def affiche_chemin(self, grille, chemin_list, chemin_str):
        """
        Affiche la grille et le chemin le plus rapide du robot entre son point de départ et d'arrivée
        :param grille: grille représentant le dépôt constituée de 3 éléments (cf affiche_grille)
        :param chemin_list: liste de coordonnées des points empruntés par le robot dans sont chemin
        :param chemin_str: chaine de caractères résultat du calcul du chemin le plus rapide dans cette grille
        :type grille: list
        :type chemin_list: list
        :type chemin_str: str
        """
        # Affichage de la grille elle-même
        self.affiche_grille(grille)
        # Ajout du chemin par des lignes
        for i in range(1, len(chemin_list)):
            self.canvas.create_line(chemin_list[i-1][1], chemin_list[i-1][0],
                                    chemin_list[i][1], chemin_list[i][0], width=3)
        # on redessine le point de départ pour que ce soit plus "joli"
        ligne = grille[2]
        rayon = 1/2
        dessine_depart(self.canvas, int(ligne[1]), int(ligne[0]), rayon, ligne[-1])
        self.canvas.create_text(0, self.nb_lignes+1/2, text=chemin_str, font=("Helvetica", 10), anchor=NW)
        self.rescale()

    def modifier_grille(self):
        """
        Ajoute un lien entre clic gauche et les rectangles de la grille
        pour pouvoir modifier leur statut (obstacle ou non)
        """
        self.canvas.tag_bind("case", "<Button-1>", self.toggle_obstacle)

    def toggle_obstacle(self, event):
        """
        Changement du status (obstacle ou non) d'un rectangle de la grille lorsqu'on clique dessus
        :param event: clic gauche sur un rectangle
        """
        # Récupération du rectangle sur lequel on a cliqué
        w = event.widget.find_closest(event.x, event.y)
        ligne = (w[0]-1)//self.nb_colonnes
        colonne = (w[0]-1) % self.nb_colonnes
        # Changement de la grille elle-même en changeant la case correspondante,
        # et changement de la couleur du rectangle
        if self.parent.grilles[0][1][ligne][colonne] == '1':
            self.parent.grilles[0][1][ligne][colonne] = '0'
            self.canvas.itemconfig(w, fill="white")
        else:
            self.parent.grilles[0][1][ligne][colonne] = '1'
            self.canvas.itemconfig(w, fill=couleur_obstacles)

    def affiche_patienter(self):
        """
        Demande à l'utilisateur de patienter pendant le calcul des statistiques qu'il a demandées
        """
        self.clean()
        label = Label(self, text="Patientez s'il vous plaît\nCalcul des statistiques en cours", font=("Helvetica", 50))
        label.grid()

    def affiche_plot(self, figure):
        """
        Affichage dans la fenêtre du plot correspondant aux statistiques demandées
        :param figure: élément figure matplotlib à afficher
        """
        self.clean()
        self.canvas = FigureCanvasTkAgg(figure, master=self)
        self.canvas.show()
        self.canvas.get_tk_widget().pack(side=TOP, fill=BOTH, expand=1)
        toolbar = NavigationToolbar2TkAgg(self.canvas, self)
        toolbar.update()
        self.canvas._tkcanvas.pack(side=TOP, fill=BOTH, expand=1)
Example #5
0
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()
Example #6
0
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()