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