Example #1
0
    def begin_session(self):
        set_state(self.root, 'disabled')
        set_state(self.switch_button, 'enabled')
        self.real_root.withdraw()
        # Try to find SUMA. If we can't, look for a config
        # file telling us where it is. If there is none,
        # ask the user where it is and create a config file.
        try:
            self.suma_file = subprocess.check_output(
                ["which", "suma"]).strip()
        except subprocess.CalledProcessError: 
            config_name = os.path.join(
               os.path.dirname(inspect.stack()[-1][1]),
               'suma.config')
            try:
                
                config_fi = open(config_name, 'r')
                config = config_fi.read()
                config_fi.close()
                self.suma_file = json.loads(config)["suma"]
                if not self.suma_file:
                    raise IOError()
                
                    
            except (IOError, KeyError):
                try:
                    self.configure()
                except ValueError:
                    # We can't continue without a path to SUMA.
                    sys.exit(1)
            
            
        # If we're not connected, we need to choose a local dataset or server
        init = None
        if self.net_thread and self.net_thread.authenticated:
            mode = 'server'
        else:
            init_dialog = tk.Toplevel()
            init_dialog.title("COVI: Choose data source")
            if self.net_thread:
                self.net_thread.job_q.put(["die"])
#             self.net_thread = NetworkThread()
            init = InitWindow(init_dialog, NetworkThread())
            center_window(init_dialog)
            root.wait_window(init_dialog)
            mode = init.mode

        if mode == 'server':
            if init:
                self.net_thread = init.net_thread
                dset_dialog = ServerDsetWindow(self.real_root,
                                            net_thread=self.net_thread,
                                            title="COVI: %s: Datasets"%init.user_var.get())
            else:
                dset_dialog = ServerDsetWindow(self.real_root,
                                            net_thread=self.net_thread,
                                            title="COVI: Server Datasets")
            if hasattr(dset_dialog, 'dset'):
                self.dset = dset_dialog.dset
                if type(self.dset) == list:
                    dset_name = '/'.join(self.dset)
                else:
                    dset_name = self.dset
                
                if type(self.dset) == list:
                    self.proc_thread = ProcessingThread(dset=self.dset[1], 
                        net_thread=self.net_thread, owner=self.dset[0],
                        suma_file = self.suma_file)
                else:
                    self.proc_thread = ProcessingThread(dset=self.dset, 
                        net_thread=self.net_thread,
                        suma_file = self.suma_file)
                    
                self.proc_thread.start()
            
        elif mode == 'local':
            self.dset = init.dset_var.get()
            # TODO: Load dataset & surfaces
            sv_window = LocalSpecAndVolWindow(self.real_root,
                                              title="COVI: Select Spec "
                                              +"and Volume Files",
                                              dset_path=self.dset)
            
            if sv_window.spec_file and sv_window.vol_file:
                self.spec_file = sv_window.spec_file
                self.vol_file = sv_window.vol_file
                self.annot_file = sv_window.annot_file
                self.proc_thread = ProcessingThread(
                    spec_file=self.spec_file,
                    surfvol_file=self.vol_file,
                    dset_path = self.dset,
                    annot_file = self.annot_file,
                    suma_file = self.suma_file)
                self.proc_thread.start()
                
                # Re-enable main window widgets
                set_state(self.real_root, 'enabled')
            
        
        
        self.real_root.deiconify()
Example #2
0
class MainWindow:
    def begin_session(self):
        set_state(self.root, 'disabled')
        set_state(self.switch_button, 'enabled')
        self.real_root.withdraw()
        # Try to find SUMA. If we can't, look for a config
        # file telling us where it is. If there is none,
        # ask the user where it is and create a config file.
        try:
            self.suma_file = subprocess.check_output(
                ["which", "suma"]).strip()
        except subprocess.CalledProcessError: 
            config_name = os.path.join(
               os.path.dirname(inspect.stack()[-1][1]),
               'suma.config')
            try:
                
                config_fi = open(config_name, 'r')
                config = config_fi.read()
                config_fi.close()
                self.suma_file = json.loads(config)["suma"]
                if not self.suma_file:
                    raise IOError()
                
                    
            except (IOError, KeyError):
                try:
                    self.configure()
                except ValueError:
                    # We can't continue without a path to SUMA.
                    sys.exit(1)
            
            
        # If we're not connected, we need to choose a local dataset or server
        init = None
        if self.net_thread and self.net_thread.authenticated:
            mode = 'server'
        else:
            init_dialog = tk.Toplevel()
            init_dialog.title("COVI: Choose data source")
            if self.net_thread:
                self.net_thread.job_q.put(["die"])
#             self.net_thread = NetworkThread()
            init = InitWindow(init_dialog, NetworkThread())
            center_window(init_dialog)
            root.wait_window(init_dialog)
            mode = init.mode

        if mode == 'server':
            if init:
                self.net_thread = init.net_thread
                dset_dialog = ServerDsetWindow(self.real_root,
                                            net_thread=self.net_thread,
                                            title="COVI: %s: Datasets"%init.user_var.get())
            else:
                dset_dialog = ServerDsetWindow(self.real_root,
                                            net_thread=self.net_thread,
                                            title="COVI: Server Datasets")
            if hasattr(dset_dialog, 'dset'):
                self.dset = dset_dialog.dset
                if type(self.dset) == list:
                    dset_name = '/'.join(self.dset)
                else:
                    dset_name = self.dset
                
                if type(self.dset) == list:
                    self.proc_thread = ProcessingThread(dset=self.dset[1], 
                        net_thread=self.net_thread, owner=self.dset[0],
                        suma_file = self.suma_file)
                else:
                    self.proc_thread = ProcessingThread(dset=self.dset, 
                        net_thread=self.net_thread,
                        suma_file = self.suma_file)
                    
                self.proc_thread.start()
            
        elif mode == 'local':
            self.dset = init.dset_var.get()
            # TODO: Load dataset & surfaces
            sv_window = LocalSpecAndVolWindow(self.real_root,
                                              title="COVI: Select Spec "
                                              +"and Volume Files",
                                              dset_path=self.dset)
            
            if sv_window.spec_file and sv_window.vol_file:
                self.spec_file = sv_window.spec_file
                self.vol_file = sv_window.vol_file
                self.annot_file = sv_window.annot_file
                self.proc_thread = ProcessingThread(
                    spec_file=self.spec_file,
                    surfvol_file=self.vol_file,
                    dset_path = self.dset,
                    annot_file = self.annot_file,
                    suma_file = self.suma_file)
                self.proc_thread.start()
                
                # Re-enable main window widgets
                set_state(self.real_root, 'enabled')
            
        
        
        self.real_root.deiconify()
        
    def configure(self):
        config_name = os.path.join(
            os.path.dirname(inspect.stack()[-1][1]),
            'suma.config')
        config_dialog = ConfigDialog(parent=self.root, 
            title='COVI Configuration')
        self.suma_file = config_dialog.suma_file
        if self.suma_file:
            config = {"suma":self.suma_file}
            config_fi = open(config_name, 'w')
            json.dump(config, config_fi, indent=4, separators=(',',':'))
            if self.proc_thread and self.proc_thread.ready():
                self.proc_thread.job_q.put_nowait(["suma_path", 
                    self.suma_file])
            
            return True
        else:
            raise ValueError("The user did not specify the path to SUMA.")
        
    
    def __init__(self, real_root):
        self.proc_thread = False
        self.net_thread = False
        self.real_root = real_root
        self.real_root.resizable(0,0)
        self.real_root.title("COVI")
        self.menu_vars = []
        
        self.real_root.protocol("WM_DELETE_WINDOW", self.cleanup)
        
        # Put all of our widgets inside a frame, not the Toplevel object
        self.root = ttk.Frame(real_root)
        root = self.root
        root.pack(fill=tk.BOTH, expand=tk.YES)
        self.body()
        #rootlabel = ttk.Label(root, 
        #    text="I'm the main window!\nWhen I grow up, I'll be full of widgets!")
        #rootlabel.pack()
        center_window(real_root)
        set_state(self.root, 'disabled')
        set_state(self.switch_button, 'enabled')

        self.begin_session()
        set_state(self.root, 'disabled')
        set_state(self.switch_button, 'enabled')
        self.poll()
    
    def poll(self):
        '''
        Check for events from SUMA.
        '''
        if self.proc_thread:
            if self.proc_thread not in threading.enumerate():
                tkMessageBox.showwarning("Connection to SUMA lost", 
                      "The connection to SUMA has been lost. "+
                      "Reload  your dataset to continue using COVI.")
                self.proc_not_ready()
                self.proc_thread = False
            if self.proc_thread and self.proc_thread.ready():
                try:
                    res = self.proc_thread.res_q.get_nowait()
                    self.proc_thread.res_q.task_done()
                    if res:
                        if res[0] == 'node':
                            self.node_number_label['text'] = '%i'%res[1]
                        elif res[0] == 'cluster':
                            self.cluster_number_label['text'] = '%i'%res[1]
                        elif res[0] == 'area':
                            self.curr_area_label['text'] = res[1]
                        elif res[0] == 'ready':
                            # Re-enable main window widgets
                            set_state(self.real_root, 'enabled')
                except Empty:
                    pass
        if self.net_thread and self.net_thread not in threading.enumerate():
            tkMessageBox.showwarning("Connection to server lost", 
                  "The connection to the server has been lost. "+
                  "Reload  your dataset to continue using COVI.")
            self.proc_not_ready()
            self.net_thread = False
        
        self.root.after(100, self.poll)
        
    def cleanup(self):
        if self.net_thread:
            self.net_thread.job_q.put_nowait(["close"])
            self.net_thread.job_q.put_nowait(["die"])
        if self.proc_thread:
            self.proc_thread.job_q.put_nowait(["die"])
        self.real_root.quit()
                                            
    def proc_not_ready(self):
        '''
        What to do if no dataset is loaded into SUMA.
        Disable interface widgets that won't work without SUMA 
        and display a message.
        '''
        set_state(self.root, 'disabled')
        set_state(self.switch_button, 'enabled')
        """
        tkMessageBox.showwarning("Warning", 
             "A dataset needs to be loaded into SUMA to do that.")"""
        
    def scale_command(self, event=None):
        threshold = self.threshold.get()
        self.threshold_label['text'] = str(threshold)
        
    def switch_command(self):
        '''
        Switch datasets
        '''
        answer = True
        if self.proc_thread and self.proc_thread.ready():
            answer = tkMessageBox.askyesno("Warning", 
                "This will end your current COVI session. Continue?")
            
        if answer:
            if self.proc_thread:
                self.proc_thread.job_q.put_nowait(["die"])
                self.proc_thread = False
                self.proc_not_ready()
            self.begin_session()
            
    
    def open_command(self):
        pass
    
    def mode_command(self, mode):
        if self.proc_thread and self.proc_thread.ready():
            self.proc_thread.job_q.put_nowait(["shape", mode])
        else:
            self.proc_not_ready()
            
    
    def color_command(self, color):
        if self.proc_thread and self.proc_thread.ready():
            if color == 'annot' and not self.proc_thread.annot:
                tkMessageBox.showerror("Coloring Error", 
                    "Can't use brain area-based coloring without an annot file.")
                self.color_var.set(self.last_color)
                return
            elif color == 'brightness' and self.proc_thread.no_surface:
                tkMessageBox.showerror("Coloring Error", 
                    "Coordinate-based coloring can't be used, since "+
                    "COVI could not find a valid FreeSurfer ASCII surface "+
                    "file for the current surface. You can use the AFNI "+
                    "program ConvertSurface to create a FreeSurfer ASCII "+
                    "file for the current surface.")
                self.color_var.set(self.last_color)
                return
            else:
                self.proc_thread.job_q.put_nowait(["color", color])
                self.last_color = color
            
        else:
            self.proc_not_ready()
    def redraw_command(self):
        if self.proc_thread and self.proc_thread.ready():
            threshold = float(self.threshold.get())
            # Avoid divide by zero error
            threshold = max(threshold, 10**-9)
            self.proc_thread.job_q.put_nowait(["threshold", 
                float(self.threshold.get())/100.])
            self.proc_thread.job_q.put_nowait(["redraw"])
        else:
            self.proc_not_ready()
            
    def relaunch_command(self):
        if self.proc_thread and self.proc_thread.ready():
            self.proc_thread.job_q.put_nowait(['suma'])
        else:
            self.proc_not_ready()

    def disconnect_command(self):
        '''
        End a network session and disconnect from the server
        '''
        answer = tkMessageBox.askyesno("Warning", 
            "This will end your current COVI session. Continue?")
        if not answer:
            return

        if self.net_thread:
            if self.net_thread in threading.enumerate():
                self.net_thread.job_q.put(["close"])
                self.net_thread.job_q.put(["die"])
            self.net_thread = False
            if self.proc_thread and self.proc_thread.ready():
                self.proc_thread.job_q.put(["die"])
        else:
            tkMessageBox.showinfo("Can't disconnect from server",
                "COVI Client is not connected to a server.")


            
    def make_mode_menu(self, root_menu):
        '''
        Make a menu to choose among graph modes.
        '''
        if not hasattr(self, "mode_var"):
            self.mode_var = tk.StringVar()
            self.mode_var.set("sized spheres")
        mode_menu = tk.Menu(root_menu, tearoff=0)
        
        mode_menu.add_radiobutton(label="Paths", variable=self.mode_var,
                command=lambda: self.mode_command("paths"),
                value="paths")
        
        mode_menu.add_radiobutton(label="Spheres", variable=self.mode_var,
                command=lambda: self.mode_command("spheres"),
                value="spheres")
        mode_menu.add_radiobutton(label="Sized Spheres", variable=self.mode_var,
                command=lambda: self.mode_command("sized spheres"),
                value="sized spheres")
        return mode_menu
        
    def make_color_menu(self, root_menu):
        '''
        Make a menu to choose among color modes.
        '''
        if not hasattr(self, "mode_var"):
            self.mode_var = tk.StringVar()
        
        if not hasattr(self, "color_var"):
            self.color_var = tk.StringVar()
            self.color_var.set("heat")
        color_menu = tk.Menu(self.edit_menu,tearoff=0)
        color_menu.add_radiobutton(label="Heatmap", 
                command=lambda: self.color_command("heat"),
                variable=self.color_var, value="heat")
        color_menu.add_radiobutton(label="Coordinate-based", 
                command=lambda: self.color_command("brightness"),
                variable=self.color_var, value="brightness")
        color_menu.add_radiobutton(label=" Brain Area-based", 
                command=lambda: self.color_command("annot"),
                variable=self.color_var, value="annot")
        return color_menu
        
    def body(self):
        self.threshold = tk.IntVar(0)
        self.menu = tk.Menu()
        
        self.file_menu = tk.Menu(self.menu, tearoff=0)
        self.file_menu.add_command(label="Open dataset/Connect to server",
                                   command=self.begin_session)
        self.file_menu.add_command(label="Disconnect from server",
                                   command=self.disconnect_command)
        self.file_menu.add_separator()
        self.file_menu.add_command(label="Quit", command=self.cleanup)
        self.menu.add_cascade(label="File", menu=self.file_menu)
        
        self.edit_menu = tk.Menu(self.menu, tearoff=0)
        
        
        self.edit_menu.add_cascade(label="Graph mode", 
                menu=self.make_mode_menu(self.edit_menu))
        
        self.last_color = 'heat'
        
        self.edit_menu.add_cascade(label="Color mode", 
                menu=self.make_color_menu(self.edit_menu)) 
        
        
        self.edit_menu.add_command(label="Relaunch SUMA", 
                                   command=self.relaunch_command)
        self.edit_menu.add_command(label="Reconfigure", 
                                   command=self.configure)
        self.menu.add_cascade(label="Edit", menu=self.edit_menu)
        
        self.real_root.config(menu=self.menu)
        
        
        self.scale = ttk.Scale(self.root, from_=100, to=0, 
                               variable=self.threshold,
                               orient=tk.VERTICAL,
                               command=self.scale_command)
        self.threshold.set(50)
        self.scale.grid(column=0, row=0, rowspan=8, sticky=(tk.N+tk.S),
                        padx='3m', pady='1m')
        self.threshold_label = ttk.Label(self.root, text="0", justify=tk.LEFT)
        self.threshold_label.grid(column=0, row=8)
        self.scale_command()
        
        
        self.node_label = ttk.Label(self.root, text="Node")
        self.node_label.grid(column=1, row=0, sticky=tk.W+tk.E, padx='1m')
        
        self.node_number_label = ttk.Label(self.root, text='%i'%0, width='6',
                                      relief=tk.SUNKEN, justify=tk.RIGHT)
        self.node_number_label.grid(column=1, row=1, sticky=tk.W+tk.E, padx='1m')
        
        
        self.cluster_label = ttk.Label(self.root, text="Cluster")
        self.cluster_label.grid(column=1, row=2, sticky=tk.W+tk.E, padx='1m')
         
        self.cluster_number_label = ttk.Label(self.root, text='%i'%0, width='6',
                                      relief=tk.SUNKEN, justify=tk.RIGHT)
        self.cluster_number_label.grid(column=1, row=3, sticky=tk.W+tk.E, padx='1m')
        
        self.area_label = ttk.Label(self.root, text="Area") 
        self.area_label.grid(column=1, row=4, sticky=tk.W+tk.E, padx='1m')
        self.curr_area_label = ttk.Label(self.root, text='',
                                      relief=tk.SUNKEN, justify=tk.RIGHT)
        self.curr_area_label.grid(column=1, row=5, sticky=tk.W+tk.E, padx='1m')

        """
        self.open_button = ttk.Button(self.root, text= "Open\nDataset",
                                        command=self.open_command)
        self.open_button.grid(column=1, row=3, sticky=tk.W+tk.E, padx='1m')
        """
        self.switch_button = ttk.Button(self.root, text= "Switch\nDataset",
                                        command=self.switch_command)
        self.switch_button.grid(column=1, row=6, sticky=tk.W+tk.E, padx='1m')
        self.mode_button = ttk.Menubutton(self.root, text= "Graph\nMode")
        self.mode_button['menu'] = self.make_mode_menu(self.mode_button)
        self.mode_button.grid(column=1, row=7, sticky=tk.W+tk.E, padx='1m')
        self.color_button = ttk.Menubutton(self.root, text= "Color\nMode")
        self.color_button['menu'] = self.make_color_menu(self.color_button)
        self.color_button.grid(column=1, row=8, sticky=tk.W+tk.E, padx='1m')
        self.redraw_button = ttk.Button(self.root, text= "Redraw",
                                        command=self.redraw_command)
        self.redraw_button.grid(column=0, row=9, sticky=(tk.W+tk.E), 
                                columnspan=2, padx='1m')
        add_padding(self.root)