예제 #1
0
class WorkflowTest(unittest.TestCase):
    def setUp(self):

        # communications channels
        parent_workflow_conn, child_workflow_conn = multiprocessing.Pipe()
        parent_mpl_conn, child_matplotlib_conn = multiprocessing.Pipe()
        running_event = multiprocessing.Event()

        # logging
        log_q = multiprocessing.Queue()

        def handle(record):
            logger = logging.getLogger(record.name)
            if logger.isEnabledFor(record.levelno):
                logger.handle(record)

        handler = CallbackHandler(handle)
        self.queue_listener = QueueListener(log_q, handler)
        self.queue_listener.start()

        remote_process = multiprocessing.Process(
            target=remote_main,
            name="remote process",
            args=[parent_workflow_conn, parent_mpl_conn, log_q, running_event])

        remote_process.daemon = True
        remote_process.start()
        running_event.wait()

        self.workflow = Workflow((child_workflow_conn, child_matplotlib_conn))
        self.remote_process = remote_process

    def tearDown(self):
        self.workflow.shutdown_remote_process(self.remote_process)
        self.queue_listener.stop()
예제 #2
0
class WorkflowTest(unittest.TestCase):
    def setUp(self):
        def remote_main(parent_workflow_conn, parent_mpl_conn, log_q,
                        running_event):
            running_event.set()
            RemoteWorkflow().run(parent_workflow_conn, parent_mpl_conn, log_q)

        # communications channels
        parent_workflow_conn, child_workflow_conn = multiprocessing.Pipe()
        parent_mpl_conn, child_matplotlib_conn = multiprocessing.Pipe()
        log_q = multiprocessing.Queue()
        running_event = multiprocessing.Event()

        remote_process = multiprocessing.Process(
            target=remote_main,
            name="remote process",
            args=[parent_workflow_conn, parent_mpl_conn, log_q, running_event])

        remote_process.daemon = True
        remote_process.start()
        running_event.wait()

        self.workflow = Workflow(
            (child_workflow_conn, child_matplotlib_conn, log_q))
        self.remote_process = remote_process

    def tearDown(self):
        self.workflow.shutdown_remote_process()
        self.remote_process.join()
예제 #3
0
class CytoflowApplication(TasksApplication):
    """ The cytoflow Tasks application.
    """

    # The application's globally unique identifier.
    id = 'edu.mit.synbio.cytoflow'

    # The application's user-visible name.
    name = 'Cytoflow'

    # Override two traits from TasksApplication so we can provide defaults, below

    # The default window-level layout for the application.
    default_layout = List(TaskWindowLayout)

    # Whether to restore the previous application-level layout when the
    # applicaton is started.
    always_use_default_layout = Property(Bool)

    # are we debugging? at the moment, just for sending logs to the console
    debug = Bool

    # did we get a filename on the command line?
    filename = File

    # if there's an ERROR-level log message, drop it here     
    application_error = Str
    
    # keep the application log in memory
    application_log = Instance(io.StringIO, ())
    
    # local process's central model
    remote_connection = Any
    model = Instance(Workflow)
    
    # the shared task pane
    plot_pane = Instance(FlowTaskPane)
            
    def run(self):
 
        # Clear the default logging so we can use our own format
        rootLogger = logging.getLogger()
        list(map(rootLogger.removeHandler, rootLogger.handlers[:]))
        list(map(rootLogger.removeFilter, rootLogger.filters[:]))
        
        logging.getLogger().setLevel(logging.DEBUG)
        
        ## send the log to STDERR
        try:
            console_handler = logging.StreamHandler()
            console_handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s:%(name)s:%(message)s"))
            console_handler.setLevel(logging.DEBUG if self.debug else logging.ERROR)
            logging.getLogger().addHandler(console_handler)
        except:
            # if there's no console, this fails
            pass
          
        ## capture log in memory
        mem_handler = logging.StreamHandler(self.application_log)
        mem_handler.setFormatter(logging.Formatter("%(asctime)s %(levelname)s:%(name)s:%(message)s"))
        mem_handler.setLevel(logging.DEBUG)
        logging.getLogger().addHandler(mem_handler)
         
        ## and display gui messages for exceprions
        gui_handler = multiprocess_logging.CallbackHandler( lambda msg, app = self: gui_handler_callback(msg, app))
        gui_handler.setLevel(logging.ERROR)
        logging.getLogger().addHandler(gui_handler)
         
        # must redirect to the gui thread
        self.on_trait_change(self.show_error, 'application_error', dispatch = 'ui')
                
        # set up the model
        self.model = Workflow(remote_connection = self.remote_connection,
                              debug = self.debug)
        
        # and the shared central pane
        self.plot_pane = FlowTaskPane(model = self.model)

        # run the GUI
        super(CytoflowApplication, self).run()
        
    def show_error(self, error_string):
        error(None, "An exception has occurred.  Please report a problem from the Help menu!\n\n"
                    "Afterwards, may need to restart Cytoflow to continue working.\n\n" 
                    + error_string)
        
    def stop(self):
        super().stop()
        self.model.shutdown_remote_process()
        

    preferences_helper = Instance(CytoflowPreferences)

    ###########################################################################
    # Private interface.
    ###########################################################################
     
    def _load_state(self):
        """ 
        Loads saved application state, if possible.  Overload the envisage-
        defined one to fix a py3k bug and increment the TasksApplicationState
        version.
         
        """
        state = TasksApplicationState(version = 2)
        filename = os.path.join(self.state_location, 'application_memento')
        if os.path.exists(filename):
            # Attempt to unpickle the saved application state.
            try:
                with open(filename, 'rb') as f:
                    restored_state = pickle.load(f)
                if state.version == restored_state.version:
                    state = restored_state
                     
                    # make sure the active task is the main window
                    state.previous_window_layouts[0].active_task = 'edu.mit.synbio.cytoflowgui.flow_task'
                else:
                    logger.warn('Discarding outdated application layout')
            except:
                # If anything goes wrong, log the error and continue.
                logger.exception('Had a problem restoring application layout from %s',
                                 filename)
                  
        self._state = state
     
    def _save_state(self):
        """
        Saves the application window size, position, panel locations, etc
        """

        # Grab the current window layouts.
        window_layouts = [w.get_window_layout() for w in self.windows]
        self._state.previous_window_layouts = window_layouts
     
        # Attempt to pickle the application state.
        filename = os.path.join(self.state_location, 'application_memento')
        try:
            with open(filename, 'wb') as f:
                pickle.dump(self._state, f)
        except Exception as e:
            # If anything goes wrong, log the error and continue.
            logger.exception('Had a problem saving application layout: {}'.format(str(e)))
    #### Trait initializers ###################################################

    def _default_layout_default(self):
        active_task = self.preferences_helper.default_task
        tasks = [ factory.id for factory in self.task_factories ]
        return [ TaskWindowLayout(*tasks,
                                  active_task = active_task,
                                  size = (1280, 800)) ]

    def _preferences_helper_default(self):
        return CytoflowPreferences(preferences = self.preferences)

    #### Trait property getter/setters ########################################
 
    def _get_always_use_default_layout(self):
        return self.preferences_helper.always_use_default_layout