def file_menu(self): """ File menu schema. """ return SMenu( TaskAction( name="Open Workflow...", method="setup_task.open_workflow", enabled_name="setup_task.save_load_enabled", accelerator="Ctrl+O", ), TaskAction( id="Save", name="Save Workflow", method="setup_task.save_workflow", enabled_name="setup_task.save_load_enabled", accelerator="Ctrl+S", ), TaskAction( name="Save Workflow as...", method="setup_task.save_workflow_as", enabled_name="setup_task.save_load_enabled", accelerator="Shift+Ctrl+S", ), TaskAction(name="Plugins...", method="setup_task.open_plugins"), name="&File", )
class SecondTask(ExampleTask): """ A simple task for opening a blank editor. """ # Task interface ------------------------------------------------------- id = "example.second_task" name = "Second Multi-Tab Editor" tool_bars = [ SToolBar( TaskAction( method="new", tooltip="New file", image=ImageResource("document_new"), ), TaskAction( method="new", tooltip="New file", image=ImageResource("document_new"), ), image_size=(32, 32), ) ] # ------------------------------------------------------------------------ # 'Task' interface. # ------------------------------------------------------------------------ def _default_layout_default(self): return TaskLayout(left=VSplitter( HSplitter(PaneItem("steps.pane1"), PaneItem("steps.pane2"))))
class SecondTask(ExampleTask): """ A simple task for opening a blank editor. """ #### Task interface ####################################################### id = 'example.second_task' name = 'Second Multi-Tab Editor' menu_bar = SMenuBar( SMenu(TaskAction(name='New', method='new', accelerator='Ctrl+N'), id='File', name='&File'), SMenu(DockPaneToggleGroup(), TaskToggleGroup(), id='View', name='&View')) tool_bars = [ SToolBar(TaskAction(method='new', tooltip='New file', image=ImageResource('document_new')), image_size=(32, 32)), ] ########################################################################### # 'Task' interface. ########################################################################### def _default_layout_default(self): return TaskLayout(left=Tabbed(PaneItem('steps.first_pane'), PaneItem('steps.second_pane'), PaneItem('steps.third_pane')))
class ThirdTask(ExampleTask): """ A simple task for opening a blank editor. """ #### Task interface ####################################################### id = 'example.third_task' name = 'Third Multi-Tab Editor' tool_bars = [ SToolBar(TaskAction(method='new', tooltip='New file', image=ImageResource('document_new')), TaskAction(method='new', tooltip='New file', image=ImageResource('document_new')), TaskAction(method='new', tooltip='New file', image=ImageResource('document_new')), TaskAction(method='new', tooltip='New file', image=ImageResource('document_new')), image_size = (32, 32)), ] ########################################################################### # 'Task' interface. ########################################################################### def _default_layout_default(self): return TaskLayout( right=VSplitter( HSplitter( PaneItem('steps.pane1'), PaneItem('steps.pane2')), ))
def _menu_bar_default(self): return SMenuBar(SMenu(TaskAction(name='New', method='new', accelerator='Ctrl+N'), id='File', name='&File'), SMenu(DockPaneToggleGroup(), TaskToggleGroup(), id='View', name='&View'), SMenu(TaskAction(name='Item 1', method='item1'), TaskAction(name='Item 2', method='item2'), id='Task2', name='&Task2'),)
class ExampleTask(Task): """ A simple task for opening a blank editor. """ #### Task interface ####################################################### id = 'example.example_task' name = 'Multi-Tab Editor' active_editor = Property(Instance(IEditor), depends_on='editor_area.active_editor') editor_area = Instance(IEditorAreaPane) menu_bar = SMenuBar( SMenu(TaskAction(name='New', method='new', accelerator='Ctrl+N'), id='File', name='&File'), SMenu(DockPaneToggleGroup(), id='View', name='&View')) tool_bars = [ SToolBar(TaskAction(method='new', tooltip='New file', image=ImageResource('document_new')), image_size=(32, 32)), ] ########################################################################### # 'Task' interface. ########################################################################### def create_central_pane(self): """ Create the central pane: the script editor. """ self.editor_area = EditorAreaPane() return self.editor_area ########################################################################### # 'ExampleTask' interface. ########################################################################### def new(self): """ Opens a new empty window """ editor = Editor() self.editor_area.add_editor(editor) self.editor_area.activate_editor(editor) self.activated() #### Trait property getter/setters ######################################## def _get_active_editor(self): if self.editor_area is not None: return self.editor_area.active_editor return None
def help_menu(self): """ Help menu schema. """ return SMenu(TaskAction(name="About WorkflowManager...", method="setup_task.open_about"), TaskAction(name="Online Documentation", method="setup_task.open_documentation"), TaskAction(name="BDSS Tutorial", method="setup_task.open_tutorial"), name="&Help", id='Help')
class SecondTask(ExampleTask): """ A simple task for opening a blank editor. """ # Task interface ------------------------------------------------------- id = "example.second_task" name = "Second Multi-Tab Editor" menu_bar = SMenuBar( SMenu( TaskAction(name="New", method="new", accelerator="Ctrl+N"), id="File", name="&File", ), SMenu( DockPaneToggleGroup(), TaskToggleGroup(), id="View", name="&View" ), ) tool_bars = [ SToolBar( TaskAction( method="new", tooltip="New file", image=ImageResource("document_new"), ), image_size=(32, 32), ) ] # ------------------------------------------------------------------------ # 'Task' interface. # ------------------------------------------------------------------------ def _default_layout_default(self): return TaskLayout( left=VSplitter( HSplitter( PaneItem("steps.pane1"), PaneItem("steps.pane2"), PaneItem("steps.pane3"), ), HSplitter( PaneItem("steps.pane4"), PaneItem("steps.pane5"), PaneItem("steps.pane6"), ), ) )
def create_contents(self, parent): """ Create and return the toolkit-specific contents of the dock pane. """ self.toolbar = ToolBarManager(orientation='vertical', show_tool_names=False, image_size=(32, 32)) for plugin in self.plugins: # don't include the import plugin if plugin.id == 'edu.mit.synbio.cytoflowgui.op_plugins.import': continue task_action = TaskAction( name=plugin.short_name, on_perform=lambda pid=plugin.id: self.task.add_operation(pid), image=plugin.get_icon()) self.toolbar.append(task_action) window = QtGui.QMainWindow() window.addToolBar(QtCore.Qt.LeftToolBarArea, self.toolbar.create_tool_bar(window)) self.ui = self.model.edit_traits(view='operations_traits', kind='subpanel', parent=window) window.setCentralWidget(self.ui.control) window.setParent(parent) parent.setWidget(window) return window
class SecondTask(ExampleTask): """ A simple task for opening a blank editor. """ #### Task interface ####################################################### id = 'example.second_task' name = 'Second Multi-Tab Editor' tool_bars = [ SToolBar(TaskAction(method='new', tooltip='New file', image=ImageResource('document_new')), image_size = (32, 32)), ] ########################################################################### # 'Task' interface. ########################################################################### def _menu_bar_default(self): return SMenuBar(SMenu(TaskAction(name='New', method='new', accelerator='Ctrl+N'), id='File', name='&File'), SMenu(DockPaneToggleGroup(), TaskToggleGroup(), id='View', name='&View'), SMenu(TaskAction(name='Item 1', method='item1'), TaskAction(name='Item 2', method='item2'), id='Task2', name='&Task2'),)
def create_contents(self, parent): """ Create and return the toolkit-specific contents of the dock pane. """ dpi = self.control.physicalDpiX() image_size = (int(self.image_size[0] * dpi), int(self.image_size[1] * dpi)) self.toolbar = ToolBarManager(orientation='vertical', show_tool_names=False, image_size=image_size) self._default_action = TaskAction( name="Setup View", on_perform=lambda s=self: self.trait_set(selected_view=s. default_view), image=ImageResource('setup'), style='toggle', visible=False) self.toolbar.append(self._default_action) for plugin in self.plugins: task_action = TaskAction(name=plugin.short_name, on_perform=lambda view_id=plugin.view_id: self.trait_set(selected_view=view_id), image=plugin.get_icon(), style='toggle') self._actions[plugin.view_id] = task_action self.toolbar.append(task_action) self._window = window = HintedMainWindow() window.addToolBar(QtCore.Qt.RightToolBarArea, self.toolbar.create_tool_bar(window)) self.ui = self.model.edit_traits(view='selected_view_traits', kind='subpanel', parent=window) window.setCentralWidget(self.ui.control) window.setParent(parent) parent.setWidget(window) window.setEnabled(False) return window
class PythonShellTask(Task): """ A task which provides a simple Python Shell to the user. """ # Task Interface id = "pyface.tasks.contrib.python_shell" name = "Python Shell" # The list of bindings for the shell bindings = List(Dict) # The list of commands to run on shell startup commands = List(Str) # the IPythonShell instance that we are interacting with pane = Instance(PythonShellPane) # Task Interface menu_bar = SMenuBar( SMenu( TaskAction(name="Open...", method="open", accelerator="Ctrl+O"), id="File", name="&File", ), SMenu(id="View", name="&View"), ) def create_central_pane(self): """ Create a view pane with a Python shell """ logger.debug("Creating Python shell pane in central pane") self.pane = PythonShellPane(bindings=self.bindings, commands=self.commands) return self.pane # PythonShellTask API def open(self): """ Shows a dialog to open a file. """ logger.debug("PythonShellTask: opening file") dialog = FileDialog(parent=self.window.control, wildcard="*.py") if dialog.open() == OK: self._open_file(dialog.path) # Private API def _open_file(self, path): """ Execute the selected file in the editor's interpreter """ logger.debug('PythonShellTask: executing file "%s"' % path) self.pane.editor.execute_file(path)
def _menu_bar_default(self): return SMenuBar( SMenu( TaskAction(name="New", method="new", accelerator="Ctrl+N"), id="File", name="&File", ), SMenu( DockPaneToggleGroup(), TaskToggleGroup(), id="View", name="&View", ), SMenu( TaskAction(name="Item 1", method="item1"), TaskAction(name="Item 2", method="item2"), id="Task2", name="&Task2", ), )
def run_tools(self): """ Run/pause/stop workflow tools schema. """ return SToolBar( TaskAction( name="Run", tooltip="Run Workflow", image=ImageResource("baseline_play_arrow_black_48dp"), method="setup_task.run_bdss", enabled_name="setup_task.run_enabled", image_size=(64, 64), ), TaskAction( name="Stop", tooltip="Stop Workflow", method="setup_task.stop_bdss", enabled_name='setup_task.computation_running', image=ImageResource("baseline_stop_black_18dp"), image_size=(64, 64), ), TaskAction( name="Pause", tooltip="Pause Workflow", method="setup_task.pause_bdss", enabled_name='setup_task.computation_running', visible_name='setup_task._not_paused', image=ImageResource("baseline_pause_black_18dp"), image_size=(64, 64), ), TaskAction( name="Resume", tooltip="Resume Workflow", method="setup_task.pause_bdss", enabled_name='setup_task.computation_running', visible_name='setup_task._paused', image=ImageResource("baseline_skip_next_black_18dp"), image_size=(64, 64), ), )
def _tool_bars_default(self): toolbars = [ SToolBar(TaskAction(name="Import", method='on_import', image=ImageResource('import')), TaskAction(name="Open", method='on_open', image=ImageResource('survey')), TaskAction(name="Save", method='on_save', enabled_name='dirty', image=ImageResource('save')), id='File', name="File", show_tool_names=False, image_size=(24, 24)), SToolBar(TaskCommandAction(name='New Group', method='on_new_group', command_stack_name='command_stack', image=ImageResource('new-group')), TaskCommandAction(name='Delete Group', method='on_delete_group', enabled_name='have_current_group', command_stack_name='command_stack', image=ImageResource('delete-group')), TaskAction(name='Previous Line', method='on_previous_line', enabled_name='survey.survey_lines', image=ImageResource("arrow-left")), TaskAction(name='Next Line', method='on_next_line', enabled_name='survey.survey_lines', image=ImageResource("arrow-right")), id='Survey', name="Survey", show_tool_names=False, image_size=(24, 24)), ] return toolbars
def create_contents(self, parent): """ Create and return the toolkit-specific contents of the dock pane. """ self.toolbar = ToolBarManager(orientation='vertical', show_tool_names=False, image_size=(32, 32)) self._default_action = TaskAction( name="Setup View", on_perform=lambda: self.task.set_current_view("default"), image=ImageResource('setup'), style='toggle', visible=False) self._actions["default"] = self._default_action self.toolbar.append(self._default_action) for plugin in self.plugins: task_action = TaskAction(name=plugin.short_name, on_perform=lambda id=plugin.view_id: self. task.set_current_view(id), image=plugin.get_icon(), style='toggle') self._actions[plugin.view_id] = task_action self.toolbar.append(task_action) window = QtGui.QMainWindow() window.addToolBar(QtCore.Qt.RightToolBarArea, self.toolbar.create_tool_bar(window)) self.ui = self.edit_traits(kind='subpanel', parent=window) window.setCentralWidget(self.ui.control) window.setParent(parent) parent.setWidget(window) return window
def _tool_bars_default(self): tool_bars = [ SToolBar( TaskAction( name="Run", tooltip="Run PyFibre", image=ImageResource("baseline_play_arrow_black_48dp"), method="_run_pyfibre", image_size=(64, 64), enabled_name='run_enabled'), TaskAction(name="Stop", tooltip="Stop PyFibre run", image=ImageResource("baseline_stop_black_18dp"), method="stop_run", image_size=(64, 64), enabled_name='stop_enabled'), TaskAction(name="Save Database", tooltip="Save database containing " "image metrics", image=ImageResource("baseline_save_black_48dp"), method="save_database_as")) ] return tool_bars
def _tool_bars_default(self): return [ SToolBar( TaskAction( name="Setup Workflow", tooltip="Setup Workflow", image=ImageResource("outline_build_black_48dp"), method="switch_task", image_size=(64, 64), ), ), SToolBar( TaskAction( name="Open Project", tooltip="Open a project containing a workflow and results", image=ImageResource("baseline_folder_open_black_48dp"), method="open_project", image_size=(64, 64), ), TaskAction( name="Save Project As", tooltip="Save results and workflow together as JSON", image=ImageResource("outline_save_black_48dp"), method="save_project_as", enabled_name="export_results_enabled", image_size=(64, 64), ), TaskAction( name="Export Results", tooltip="Export results table to a JSON or CSV file", image=ImageResource("baseline_save_black_48dp"), method="export_analysis_model_as", enabled_name="export_results_enabled", image_size=(64, 64), ), ), ]
def create_contents(self, parent): """ Create and return the toolkit-specific contents of the dock pane. """ dpi = self.control.physicalDpiX() image_size = (int(self.image_size[0] * dpi), int(self.image_size[1] * dpi)) self.toolbar = ToolBarManager(orientation='vertical', show_tool_names=False, image_size=image_size) for plugin in self.plugins: # don't include the import plugin if plugin.id == 'edu.mit.synbio.cytoflowgui.op_plugins.import': continue task_action = TaskAction( name=plugin.short_name, on_perform=lambda plugin_id=plugin.operation_id: self.handler. add_operation(plugin_id), image=plugin.get_icon()) self.toolbar.append(task_action) # see the comment in cytoflowgui.view_pane for an explanation of this # HintedMainWindow business. window = HintedMainWindow() window.addToolBar( QtCore.Qt.LeftToolBarArea, # @UndefinedVariable self.toolbar.create_tool_bar(window)) # construct the view self.ui = self.handler.edit_traits(view='workflow_traits_view', context=self.model, kind='subpanel', parent=window) window.setCentralWidget(self.ui.control) window.setParent(parent) parent.setWidget(window) return window
class SecondTask(ExampleTask): """ A simple task for opening a blank editor. """ # Task interface ------------------------------------------------------- id = "example.second_task" name = "Second Multi-Tab Editor" tool_bars = [ SToolBar( TaskAction( method="new", tooltip="New file", image=ImageResource("document_new"), ), image_size=(32, 32), ) ] # ------------------------------------------------------------------------ # 'Task' interface. # ------------------------------------------------------------------------ def _menu_bar_default(self): return SMenuBar( SMenu( TaskAction(name="New", method="new", accelerator="Ctrl+N"), id="File", name="&File", ), SMenu( DockPaneToggleGroup(), TaskToggleGroup(), id="View", name="&View", ), SMenu( TaskAction(name="Item 1", method="item1"), TaskAction(name="Item 2", method="item2"), id="Task2", name="&Task2", ), )
class FlowTask(Task): """ classdocs """ id = "edu.mit.synbio.cytoflowgui.flow_task" name = "Cytometry analysis" # the main workflow instance. model = Instance(Workflow) # the center pane workflow_pane = Instance(WorkflowDockPane) view_pane = Instance(ViewDockPane) help_pane = Instance(HelpDockPane) plot_params_pane = Instance(PlotParamsPane) # plugin lists, to setup the interface op_plugins = List(IOperationPlugin) view_plugins = List(IViewPlugin) menu_bar = SMenuBar(SMenu(TaskAction(name='Open...', method='on_open', accelerator='Ctrl+O'), TaskAction(name='Save', #image='save', method='on_save', accelerator='Ctrl+S'), TaskAction(name='Save As...', method='on_save_as', accelerator='Ctrl+e'), TaskAction(name='Save Plot...', method='on_export', accelerator='Ctrl+x'), TaskAction(name='Export Jupyter notebook...', method='on_notebook', accelerator='Ctrl+I'), # TaskAction(name='Preferences...', # method='on_prefs', # accelerator='Ctrl+P'), id='File', name='&File'), SMenu(TaskToggleGroup(), id = 'View', name = '&View'), SMenu(TaskAction(name = 'Online documentation...', method = 'on_docs'), TaskAction(name = 'Report a problem....', method = 'on_problem'), TaskAction(name='About...', method='on_about'), id="Help", name ="&Help")) tool_bars = [ SToolBar(TaskAction(method='on_new', name = "New", tooltip='New workflow', image=ImageResource('new')), TaskAction(method='on_open', name = "Open", tooltip='Open a file', image=ImageResource('open')), TaskAction(method='on_save', name = "Save", tooltip='Save the current file', image=ImageResource('save')), TaskAction(method='on_export', name = "Save Plot", tooltip='Save the current plot', image=ImageResource('export')), TaskAction(method='on_notebook', name='Notebook', tooltip="Export to an Jupyter notebook...", image=ImageResource('jupyter')), TaskAction(method = "on_calibrate", name = "Calibrate FCS...", tooltip = "Calibrate FCS files", image = ImageResource('tasbe')), TaskAction(method = 'on_problem', name = "Report a bug...", tooltib = "Report a bug", image = ImageResource('bug')))] # TaskAction(method='on_prefs', # name = "Prefs", # tooltip='Preferences', # image=ImageResource('prefs')), # the file to save to if the user clicks "save" and has already clicked # "open" or "save as". filename = Str def initialized(self): if self.filename: self.open_file(self.filename) def activated(self): # if we're coming back from the TASBE task, re-load the saved # workflow if self.model.backup_workflow: self.model.workflow = self.model.backup_workflow self.model.backup_workflow = [] return # else, set up a new workflow # add the import op if not self.model.workflow: self.add_operation(ImportPlugin().id) self.model.selected = self.model.workflow[0] self.model.modified = False def _default_layout_default(self): return TaskLayout(left = VSplitter(PaneItem("edu.mit.synbio.cytoflowgui.workflow_pane", width = 350), PaneItem("edu.mit.synbio.cytoflowgui.help_pane", width = 350, height = 350)), right = VSplitter(PaneItem("edu.mit.synbio.cytoflowgui.view_traits_pane", width = 350), PaneItem("edu.mit.synbio.cytoflowgui.params_pane", width = 350, height = 350)), top_left_corner = 'left', bottom_left_corner = 'left', top_right_corner = 'right', bottom_right_corner = 'right') def create_central_pane(self): # set the toolbar image size # this isn't really the right place for this, but it's the only # place control passes back to user code before the toolbar # is created. dpi = self.window.control.physicalDpiX() self.tool_bars[0].image_size = (int(0.4 * dpi), int(0.4 * dpi)) return self.application.plot_pane def create_dock_panes(self): self.workflow_pane = WorkflowDockPane(model = self.model, plugins = self.op_plugins, task = self) self.view_pane = ViewDockPane(model = self.model, plugins = self.view_plugins, task = self) self.help_pane = HelpDockPane(view_plugins = self.view_plugins, op_plugins = self.op_plugins, task = self) self.plot_params_pane = PlotParamsPane(model = self.model, task = self) return [self.workflow_pane, self.view_pane, self.help_pane, self.plot_params_pane] def on_new(self): if self.model.modified: ret = confirm(parent = None, message = "Are you sure you want to discard the current workflow?", title = "Clear workflow?") if ret != YES: return self.filename = "" self.window.title = "Cytoflow" # clear the workflow self.model.workflow = [] # add the import op self.add_operation(ImportPlugin().id) # and select the operation self.model.selected = self.model.workflow[0] self.model.modified = False def on_open(self): """ Shows a dialog to open a file. """ if self.model.modified: ret = confirm(parent = None, message = "Are you sure you want to discard the current workflow?", title = "Clear workflow?") if ret != YES: return dialog = FileDialog(parent = self.window.control, action = 'open', wildcard = (FileDialog.create_wildcard("Cytoflow workflow", "*.flow") + ';' + #@UndefinedVariable FileDialog.create_wildcard("All files", "*"))) #@UndefinedVariable if dialog.open() == OK: self.open_file(dialog.path) self.filename = dialog.path self.window.title = "Cytoflow - " + self.filename def open_file(self, path): try: new_workflow = load_yaml(path) # a few things to take care of when reloading. # we do this in the try block to catch people who # load valid YAML files that aren't from cytoflow. for wi_idx, wi in enumerate(new_workflow): # get wi lock wi.lock.acquire() # clear the wi status wi.status = "loading" # re-link the linked list. if wi_idx > 0: wi.previous_wi = new_workflow[wi_idx - 1] if wi_idx < len(new_workflow) - 1: wi.next_wi = new_workflow[wi_idx + 1] except yaml.parser.ParserError as e: error(None, "Parser error loading {} -- is it a Cytoflow file?\n\n{}" .format(path, str(e))) return except Exception as e: error(None, "{} loading {} -- is it a Cytoflow file?\n\n{}" .format(e.__class__.__name__, path, str(e))) return # are we just running a smoke test? if 'startup_test' in new_workflow[0].metadata: def quit_app(app): app.exit(force = True) from pyface.timer.api import do_after do_after(5*1000, quit_app, self.application) return # check that the FCS files are all there wi = new_workflow[0] assert(wi.operation.id == "edu.mit.synbio.cytoflow.operations.import") missing_tubes = 0 for tube in wi.operation.tubes: file = pathlib.Path(tube.file) if not file.exists(): missing_tubes += 1 if missing_tubes == len(wi.operation.tubes): warning(self.window.control, "Cytoflow couldn't find any of the FCS files from that " "workflow. If they've been moved, please open one FCS " "file to show Cytoflow where they've been moved to.") dialog = FileDialog(parent = self.window.control, action = 'open', wildcard = (FileDialog.create_wildcard("FCS files", "*.fcs *.lmd"))) # @UndefinedVariable if dialog.open() == OK: # find the "best" file match -- ie, the one with the longest # tail match fcs_path = pathlib.Path(dialog.path).parts best_path_len = -1 for tube in wi.operation.tubes: tube_path = pathlib.Path(tube.file).parts for i in range(len(fcs_path)): if list(reversed(fcs_path))[:i] == list(reversed(tube_path))[:i] and i > best_path_len: best_path_len = i if best_path_len >= 0: for tube in wi.operation.tubes: tube_path = pathlib.Path(tube.file).parts new_path = fcs_path[:-1 * best_path_len] + tube_path[-1 * best_path_len :] tube.file = str(pathlib.Path(*new_path)) elif missing_tubes > 0: warning(self.window.control, "Cytoflow couldn't find some of the FCS files from that " "workflow. You'll need to re-load them from the Import " "operation.") # replace the current workflow with the one we just loaded if False: # for debugging the loading of things from .event_tracer import record_events with record_events() as container: self.model.workflow = new_workflow container.save_to_directory(os.getcwd()) else: self.model.workflow = new_workflow self.model.modified = False for wi in self.model.workflow: wi.lock.release() if self.model.debug: self.model.run_all() else: ret = confirm(parent = None, message = "Do you want to execute the workflow now?", title = "Run workflow?") if ret == YES: self.model.run_all() def on_save(self): """ Save the file to the previous filename """ if self.filename: save_yaml(self.model.workflow, self.filename) self.model.modified = False else: self.on_save_as() def on_save_as(self): dialog = DefaultFileDialog(parent = self.window.control, action = 'save as', default_suffix = "flow", wildcard = (FileDialog.create_wildcard("Cytoflow workflow", "*.flow") + ';' + #@UndefinedVariable FileDialog.create_wildcard("All files", "*"))) #@UndefinedVariable if dialog.open() == OK: save_yaml(self.model.workflow, dialog.path) self.filename = dialog.path self.model.modified = False self.window.title = "Cytoflow - " + self.filename @on_trait_change('model.modified', post_init = True) def _on_model_modified(self, val): if val: if not self.window.title.endswith("*"): self.window.title += "*" else: if self.window.title.endswith("*"): self.window.title = self.window.title[:-1] def on_export(self): task = next(x for x in self.window.tasks if x.id == 'edu.mit.synbio.cytoflowgui.export_task') self.window.activate_task(task) def on_calibrate(self): task = next(x for x in self.window.tasks if x.id == 'edu.mit.synbio.cytoflowgui.tasbe_task') self.window.activate_task(task) def on_notebook(self): """ Shows a dialog to export the workflow to an Jupyter notebook """ dialog = DefaultFileDialog(parent = self.window.control, action = 'save as', default_suffix = "ipynb", wildcard = (FileDialog.create_wildcard("Jupyter notebook", "*.ipynb") + ';' + #@UndefinedVariable FileDialog.create_wildcard("All files", "*"))) # @UndefinedVariable if dialog.open() == OK: save_notebook(self.model.workflow, dialog.path) def on_prefs(self): pass def on_docs(self): webbrowser.open_new_tab("https://cytoflow.readthedocs.io/en/stable/manual.html") def on_problem(self): log = str(self._get_package_versions()) + "\n" + self.application.application_log.getvalue() msg = "The best way to report a problem is send an application log to " \ "the developers. If you click 'Yes' below, you will be given then " \ "opportunity to save the log to a file and then file a " \ "new issue on GitHub at " \ "https://github.com/cytoflow/cytoflow/issues/new" dialog = ConfirmationDialog(message = msg, informative = "Would you like to report an issue to the developers?") if dialog.open() == YES: dialog = DefaultFileDialog(parent = self.window.control, action = 'save as', default_suffix = "log", wildcard = (FileDialog.create_wildcard("Log files", "*.log") + ';' + #@UndefinedVariable FileDialog.create_wildcard("All files", "*"))) #@UndefinedVariable if dialog.open() == OK: with open(dialog.path, 'w') as f: f.write(log) webbrowser.open_new_tab("https://github.com/cytoflow/cytoflow/issues/new") return def _get_package_versions(self): from cytoflow import __version__ as cf_version from fcsparser import __version__ as fcs_version from pandas import __version__ as pd_version from numpy import __version__ as np_version from numexpr import __version__ as nxp_version from bottleneck import __version__ as btl_version from seaborn import __version__ as sns_version from matplotlib import __version__ as mpl_version from scipy import __version__ as scipy_version from sklearn import __version__ as skl_version from statsmodels import __version__ as stats_version from pyface import __version__ as pyf_version from envisage import __version__ as env_version from traits import __version__ as trt_version from traitsui import __version__ as trt_ui_version from yapf import __version__ as yapf_version from nbformat import __version__ as nb_version from yaml import __version__ as yaml_version return {"python" : sys.version, "cytoflow" : cf_version, "fcsparser" : fcs_version, "pandas" : pd_version, "numpy" : np_version, "numexpr" : nxp_version, "bottleneck" : btl_version, "seaborn" : sns_version, "matplotlib" : mpl_version, "scipy" : scipy_version, "scikit-learn" : skl_version, "statsmodels" : stats_version, "pyface" : pyf_version, "envisage" : env_version, "traits" : trt_version, "traitsui" : trt_ui_version, "nbformat" : nb_version, "yapf" : yapf_version, "yaml" : yaml_version} def on_about(self): versions = self._get_package_versions() text = ["<b>Cytoflow {0}</b>".format(versions['cytoflow']), "<p>"] ver_text = ["{0} {1}".format(key, value) for key, value in versions.items()] text.extend(ver_text) text.extend(["Icons from the <a href=http://tango.freedesktop.org>Tango Desktop Project</a>", "<a href=https://thenounproject.com/search/?q=setup&i=14287>Settings icon</a> by Paulo Sa Ferreira from <a href=https://thenounproject.com>The Noun Project</a>", "<a href=https://thenounproject.com/search/?q=processing&i=849831>Processing icon</a> by Gregor Cresnar from <a href=https://thenounproject.com>The Noun Project</a>", "<a href=http://www.freepik.com/free-photos-vectors/background>App icon from Starline - Freepik.com</a>", "Cuvette image from Wikimedia Commons user <a href=http://commons.wikimedia.org/wiki/File:Hellma_Large_cone_cytometry_cell.JPG>HellmaUSA</a>"]) dialog = AboutDialog(text = text, parent = self.window.control, title = "About", image = ImageResource('cuvette'), additions = text) dialog.open() @on_trait_change('model.selected', post_init = True) def _on_select_op(self, selected): if selected: self.view_pane.enabled = (selected is not None) self.view_pane.default_view = selected.default_view.id if selected.default_view else "" self.view_pane.selected_view = selected.current_view.id if selected.current_view else "" self.help_pane.help_id = selected.operation.id else: self.view_pane.enabled = False @on_trait_change('view_pane.selected_view', post_init = True) def _on_select_view(self, view_id): if not view_id: return # if we already have an instantiated view object, find it try: self.model.selected.current_view = next((x for x in self.model.selected.views if x.id == view_id)) except StopIteration: # else make the new view plugin = next((x for x in self.view_plugins if x.view_id == view_id)) view = plugin.get_view() self.model.selected.views.append(view) self.model.selected.current_view = view self.help_pane.help_id = view_id def add_operation(self, op_id): # first, find the matching plugin plugin = next((x for x in self.op_plugins if x.id == op_id)) # next, get an operation op = plugin.get_operation() # make a new workflow item wi = WorkflowItem(operation = op, deletable = (op_id != 'edu.mit.synbio.cytoflowgui.op_plugins.import')) # if the op has a default view, add it to the wi try: wi.default_view = op.default_view() wi.views.append(wi.default_view) wi.current_view = wi.default_view except AttributeError: pass # figure out where to add it if self.model.selected: idx = self.model.workflow.index(self.model.selected) + 1 else: idx = len(self.model.workflow) # the add_remove_items handler takes care of updating the linked list self.model.workflow.insert(idx, wi) # and make sure to actually select the new wi self.model.selected = wi
class FlowTask(Task): """ classdocs """ id = "edu.mit.synbio.cytoflow.flow_task" name = "Cytometry analysis" # the main workflow instance. # THIS IS WHERE IT'S INITIALLY INSTANTIATED (note the args=()) model = Instance(Workflow, args=()) # the center pane view = Instance(FlowTaskPane) # plugin lists, to setup the interface op_plugins = List(IOperationPlugin) view_plugins = List(IViewPlugin) menu_bar = SMenuBar( SMenu( TaskAction(name='Open...', method='on_open', accelerator='Ctrl+O'), TaskAction( name='Save', #image='save', method='on_save', accelerator='Ctrl+S'), TaskAction(name='Save As...', method='on_save_as', accelerator='Ctrl+e'), TaskAction(name='Export image...', method='on_export', accelerator='Ctrl+x'), TaskAction(name='Export IPython notebook...', method='on_ipython', accelerator='Ctrl+I'), TaskAction(name='Preferences...', method='on_prefs', accelerator='Ctrl+P'), id='File', name='&File'), SMenu(TaskAction(name='About...', method='on_about', accelerator="Ctrl+A"), id="Help", name="&Help")) tool_bars = [ SToolBar(TaskAction(method='on_new', name="New", tooltip='New workflow', image=ImageResource('new')), TaskAction(method='on_open', name="Open", tooltip='Open a file', image=ImageResource('open')), TaskAction(method='on_save', name="Save", tooltip='Save the current file', image=ImageResource('save')), TaskAction(method='on_export', name="Export", tooltip='Export the current plot', image=ImageResource('export')), TaskAction(method='on_ipython', name='IPython', tooltip="Export to an IPython notebook...", image=ImageResource('ipython')), TaskAction(method='on_prefs', name="Prefs", tooltip='Preferences', image=ImageResource('prefs')), image_size=(32, 32)) ] # are we debugging? ie, do we need a default setup? debug = Bool worker = Instance(threading.Thread) to_update = Instance(UniquePriorityQueue, ()) worker_flag = Instance(threading.Event, args=()) worker_lock = Instance(threading.Lock, args=()) def initialized(self): # make sure that when the result changes we get notified # can't use a static notifier because selected.result gets updated # on the worker thread, but we need to dispatch on the UI thread self.model.on_trait_change(self._result_updated, "selected:result", dispatch='ui') def activated(self): # add an import plugin plugin = ImportPlugin() wi = WorkflowItem(task=self) wi.operation = plugin.get_operation() self.model.workflow.append(wi) self.model.selected = wi # if we're debugging, add a few data bits if self.debug: from cytoflow import Tube wi.operation.conditions["Dox"] = "log" tube1 = Tube(file="../cytoflow/tests/data/Plate01/CFP_Well_A4.fcs", conditions={"Dox": 0.1}) tube2 = Tube(file="../cytoflow/tests/data/Plate01/RFP_Well_A3.fcs", conditions={"Dox": 1.0}) wi.operation.tubes.append(tube1) wi.operation.tubes.append(tube2) self.add_operation( 'edu.mit.synbio.cytoflowgui.op_plugins.threshold') self.model.selected.operation.channel = "Y2-A" self.model.selected.operation.threshold = 2000 self.model.selected.operation.name = "T" def prepare_destroy(self): self.model = None def _default_layout_default(self): return TaskLayout(left=PaneItem("edu.mit.synbio.workflow_pane"), right=PaneItem("edu.mit.synbio.view_traits_pane")) def create_central_pane(self): self.view = FlowTaskPane(model=self.model) return self.view def create_dock_panes(self): return [ WorkflowDockPane(model=self.model, plugins=self.op_plugins, task=self), ViewDockPane(model=self.model, plugins=self.view_plugins, task=self) ] def on_new(self): self.model.workflow = [] # add an import plugin plugin = ImportPlugin() wi = WorkflowItem(task=self) wi.operation = plugin.get_operation() self.model.workflow.append(wi) self.model.selected = wi def on_open(self): """ Shows a dialog to open a file. """ dialog = FileDialog(parent=self.window.control, action='open', wildcard='*.flow') if dialog.open() == OK: self.open_file(dialog.path) def open_file(self, path): f = open(path, 'r') unpickler = pickle.Unpickler(f) new_model = unpickler.load() # update the link back to the controller (ie, self) for wi in new_model.workflow: wi.task = self # and set up the view handlers for view in wi.views: view.handler = view.handler_factory(model=view, wi=wi) # replace the current workflow with the one we just loaded if False: from event_tracer import record_events with record_events() as container: self.model.workflow[:] = new_model.workflow self.model.selected = new_model.selected container.save_to_directory(os.getcwd()) else: self.model.workflow[:] = new_model.workflow self.model.selected = new_model.selected wi = self.model.workflow[0] while True: wi.status = "invalid" with self.worker_lock: self.to_update.put_nowait((self.model.workflow.index(wi), wi)) if wi.next: wi = wi.next else: break # check to see if we have a worker thread around if not self.worker or not self.worker.is_alive(): self.worker = threading.Thread(target=update_model, args=(self.worker_flag, self.worker_lock, self.to_update)) self.worker.daemon = True self.worker.start() # start the worker thread processing with self.worker_lock: if not self.to_update.empty(): self.worker_flag.set() def on_save(self): """ Shows a dialog to open a file. """ dialog = FileDialog(parent=self.window.control, action='save as', wildcard='*.flow') if dialog.open() == OK: self.save_file(dialog.path) def on_save_as(self): pass def save_file(self, path): # TODO - error handling f = open(path, 'w') pickler = pickle.Pickler(f, 0) # text protocol for now pickler.dump(self.model) def on_export(self): """ Shows a dialog to export a file """ dialog = FileDialog(parent=self.window.control, action='save as') if dialog.open() == OK: self.view.export(dialog.path) def on_ipython(self): """ Shows a dialog to export the workflow to an IPython notebook """ dialog = FileDialog(parent=self.window.control, action='save as', wildcard='*.ipynb') if dialog.open() == OK: writer = IPythonNotebookWriter(file=dialog.path) writer.export(self.workflow) def on_prefs(self): pass def on_about(self): from cytoflow import __version__ as cf_version from fcsparser import __version__ as fcs_version from pandas.version import __version__ as pd_version from numpy.version import version as np_version from numexpr import __version__ as numexp_version from seaborn import __version__ as sns_version from matplotlib import __version__ as mpl_version from pyface import __version__ as py_version from envisage import __version__ as env_version from traits import __version__ as trt_version from traitsui import __version__ as trt_ui_version text = [ "<b>Cytoflow {0}</b>".format(cf_version), "<p>", "fcsversion {0}".format(fcs_version), "pandas {0}".format(pd_version), "numpy {0}".format(np_version), "numexpr {0}".format(numexp_version), "seaborn {0}".format(sns_version), "matplotlib {0}".format(mpl_version), "pyface {0}".format(py_version), "envisage {0}".format(env_version), "traits {0}".format(trt_version), "traitsui {0}".format(trt_ui_version), "Icons from the <a href=http://tango.freedesktop.org>Tango Desktop Project</a>", "<a href=https://thenounproject.com/search/?q=setup&i=14287>Settings icon</a> by Paulo Sa Ferreira from <a href=https://thenounproject.com>The Noun Project</a>", "Cuvette image from Wikimedia Commons user <a href=http://commons.wikimedia.org/wiki/File:Hellma_Large_cone_cytometry_cell.JPG>HellmaUSA</a>" ] dialog = AboutDialog(parent=self.window.control, title="About", image=ImageResource('cuvette'), additions=text) dialog.open() def add_operation(self, op_id): # first, find the matching plugin plugin = next((x for x in self.op_plugins if x.id == op_id)) # default to inserting at the end of the list if none selected after = self.model.selected if after is None: after = self.model.workflow[-1] idx = self.model.workflow.index(after) wi = WorkflowItem(task=self) wi.operation = plugin.get_operation() wi.next = after.next after.next = wi wi.previous = after if wi.next: wi.next.previous = wi self.model.workflow.insert(idx + 1, wi) # set up the default view wi.default_view = plugin.get_default_view() if wi.default_view is not None: wi.default_view.op = wi.operation wi.default_view.handler = \ wi.default_view.handler_factory(model = wi.default_view, wi = wi.previous) wi.views.append(wi.default_view) # select (open) the new workflow item self.model.selected = wi if wi.default_view: wi.current_view = wi.default_view # invalidate everything following self.operation_parameters_updated() @on_trait_change("model:workflow[]") def _on_remove_operation(self, obj, name, old, new): if name == "workflow_items" and len(new) == 0 and len(old) > 0: assert len(old) == 1 wi = old[0] if self.model.selected == wi: self.model.selected = wi.previous wi.previous.next = wi.next if wi.next: wi.next.previous = wi.previous del wi.default_view del wi.views del wi self.operation_parameters_updated() @on_trait_change("model:selected:operation:+") def operation_parameters_updated(self): # invalidate this workflow item and all the ones following it wi = self.model.selected while True: wi.status = "invalid" with self.worker_lock: self.to_update.put_nowait((self.model.workflow.index(wi), wi)) if wi.next: wi = wi.next else: break # check to see if we have a worker thread around if not self.worker or not self.worker.is_alive(): self.worker = threading.Thread(target=update_model, args=(self.worker_flag, self.worker_lock, self.to_update)) self.worker.daemon = True self.worker.start() # start the worker thread processing with self.worker_lock: if not self.to_update.empty(): self.worker_flag.set() def set_current_view(self, view_id): """ called by the view pane """ wi = self.model.selected if view_id == "default": view_id = self.model.selected.default_view.id view = next((x for x in wi.views if x.id == view_id), None) if not view: plugin = next( (x for x in self.view_plugins if x.view_id == view_id)) view = plugin.get_view() view.handler = view.handler_factory(model=view, wi=wi) wi.views.append(view) wi.current_view = view @on_trait_change("model:selected.current_view") def _current_view_changed(self, obj, name, old, new): # we get notified if *either* the currently selected workflowitem # *or* the current view changes. if name == 'selected': new = new.current_view if new else None old = old.current_view if old else None # remove the notifications from the old view if old: old.on_trait_change(self.view_parameters_updated, remove=True) # and if the old view was interactive, turn off its interactivity # to remove the matplotlib event handlers if "interactive" in old.traits(): old.interactive = False # whenever the view parameters change, we need to know so we can # update the plot(s) if new: new.on_trait_change(self.view_parameters_updated) if self.model.selected: self.view.plot(self.model.selected) else: self.view.clear_plot() else: self.view.clear_plot() def _result_updated(self, obj, name, old, new): print "result updated" if self.model.selected: self.view.plot(self.model.selected) else: self.view.clear_plot() def view_parameters_updated(self, obj, name, new): # i should be able to specify the metadata i want in the listener, # but there's an odd interaction (bug) between metadata, dynamic # trait listeners and instance traits. so, check for 'transient' # here instead, if obj.trait(name).transient: return print "view parameters updated: {0}".format(name) wi = self.model.selected if wi is None: wi = self.model.workflow[-1] self.view.plot(wi)
class PythonEditorTask(Task): """ A simple task for editing Python code. """ # 'Task' traits ----------------------------------------------------------- #: The unique id of the task. id = 'example.python_editor_task' #: The human-readable name of the task. name = u"Python Editor" #: The currently active editor in the editor area, if any. active_editor = Property(Instance(IEditor), depends_on='editor_area.active_editor') #: The editor area for this task. editor_area = Instance(IEditorAreaPane) #: The menu bar for the task. menu_bar = SMenuBar( SMenu( SGroup( TaskAction(name='New', method='new', accelerator='Ctrl+N'), id='new_group', ), SGroup( TaskAction(name='Open...', method='open', accelerator='Ctrl+O'), id='open_group', ), SGroup( TaskAction(name='Save', method='save', accelerator='Ctrl+S', enabled_name='active_editor.dirty'), TaskAction(name='Save As...', method='save_as', accelerator='Ctrl+Shift+S'), id='save_group', ), SGroup( TaskAction( name='Close Editor', method='close_editor', accelerator='Ctrl+W', ), id='close_group', ), id='File', name='&File', ), SMenu( SGroup( EditorAction( name='Undo', method='undo', enabled_name='can_undo', accelerator='Ctrl+Z', ), EditorAction( name='Redo', method='redo', enabled_name='can_redo', accelerator='Ctrl+Shift+Z', ), id='undo_group', ), SGroup( EditorAction( name='Go to Line...', method='go_to_line', accelerator='Ctrl+G', ), id='search_group', ), id='Edit', name='&Edit', ), SMenu( DockPaneToggleGroup(), id='View', name='&View', ), SMenu( SGroup( OpenURLAction( name='Python Documentation', id='python_docs', url=PYTHON_DOCS, ), id="documentation_group", ), id='Help', name='&Help', )) #: The tool bars for the task. tool_bars = [ SToolBar(TaskAction( method='new', tooltip='New file', image=ImageResource('document_new'), ), TaskAction(method='open', tooltip='Open a file', image=ImageResource('document_open')), TaskAction(method='save', tooltip='Save the current file', image=ImageResource('document_save'), enabled_name='active_editor.dirty'), image_size=(16, 16), show_tool_names=False), ] #: The status bar for the window when this task is active. status_bar = Instance(StatusBarManager, ()) # ------------------------------------------------------------------------- # 'PythonEditorTask' interface. # ------------------------------------------------------------------------- def create_editor(self, path=''): """ Create a new editor in the editor pane. Parameters ---------- path : path or '' The path to the file to edit, or '' for an empty editor. """ if path: path = os.path.abspath(path) use_existing = (path != '') self.editor_area.edit( path, factory=PythonEditor, use_existing=use_existing, ) if path: self.active_editor.load() def close_editor(self): """ Close the active editor, or if no editors, close the Task window. """ if self.editor_area.active_editor is not None: self.editor_area.remove_editor(self.editor_area.active_editor) else: self.window.close() def new(self): """ Open a new empty window """ self.create_editor() def open(self): """ Shows a dialog to open a Python file. """ dialog = FileDialog(parent=self.window.control, wildcard='*.py') if dialog.open() == OK: self.create_editor(dialog.path) def save(self): """ Save the current file. If needed, this code prompts for a path. Returns ------- saved : bool Whether or not the file was saved. """ editor = self.active_editor try: editor.save() except IOError: # If you are trying to save to a file that doesn't exist, open up a # FileDialog with a 'save as' action. dialog = FileDialog( parent=self.window.control, action='save as', wildcard='*.py', ) if dialog.open() == OK: editor.save(dialog.path) else: return False return True # ------------------------------------------------------------------------- # 'Task' interface. # ------------------------------------------------------------------------- def _default_layout_default(self): """ The default layout with the browser pane on the left. """ return TaskLayout( left=PaneItem('example.python_browser_pane', width=200)) def create_central_pane(self): """ Create the central pane: the script editor. """ self.editor_area = EditorAreaPane() return self.editor_area def create_dock_panes(self): """ Create the file browser and connect to its double click event. """ browser = PythonBrowserPane() def handler(): return self.create_editor(browser.selected_file) browser.on_trait_change(handler, 'activated') return [browser] # ------------------------------------------------------------------------- # Protected interface. # ------------------------------------------------------------------------- def _prompt_for_save(self): """ Prompts the user to save if necessary. Returns whether the dialog was cancelled. """ dirty_editors = { editor.name: editor for editor in self.editor_area.editors if editor.dirty and (editor.obj or editor.code) } if not dirty_editors: return True message = 'You have unsaved files. Would you like to save them?' dialog = ConfirmationDialog(parent=self.window.control, message=message, cancel=True, default=CANCEL, title='Save Changes?') result = dialog.open() if result == CANCEL: return False elif result == YES: for name, editor in dirty_editors.items(): editor.save(editor.path) return True # Trait change handlers -------------------------------------------------- @on_trait_change('window:closing') def _prompt_on_close(self, event): """ Prompt the user to save when exiting. """ close = self._prompt_for_save() event.veto = not close @on_trait_change('active_editor.name') def _change_title(self): """ Update the window title when the active editor changes. """ if self.window.active_task == self: if self.active_editor is not None: self.window.title = self.active_editor.name else: self.window.title = self.name @on_trait_change('active_editor.[line,column,selection_length]') def _update_status(self): if self.active_editor is not None: editor = self.active_editor if editor.selection_length: self.status_bar.messages = [ "Ln {}, Col {} ({} selected)".format( editor.line, editor.column, editor.selection_length) ] else: self.status_bar.messages = [ "Ln {}, Col {}".format(editor.line, editor.column) ] else: self.status_bar.messages = [] # Trait property getter/setters ------------------------------------------ @cached_property def _get_active_editor(self): if self.editor_area is not None: return self.editor_area.active_editor return None
class ExampleTask(Task): """ A simple task for opening a blank editor. """ # Task interface ------------------------------------------------------- id = "example.example_task" name = "Multi-Tab Editor" active_editor = Property(Instance(IEditor), observe="editor_area.active_editor") editor_area = Instance(IEditorAreaPane) tool_bars = [ SToolBar( TaskAction( method="new", tooltip="New file", image=ImageResource("document_new"), ), image_size=(32, 32), ) ] # ------------------------------------------------------------------------ # 'Task' interface. # ------------------------------------------------------------------------ def _menu_bar_default(self): return SMenuBar( SMenu( TaskAction(name="New", method="new", accelerator="Ctrl+N"), id="File", name="&File", ), SMenu( DockPaneToggleGroup(), TaskToggleGroup(), id="View", name="&View", ), ) def create_central_pane(self): """ Create the central pane: the script editor. """ self.editor_area = EditorAreaPane() return self.editor_area # ------------------------------------------------------------------------ # 'ExampleTask' interface. # ------------------------------------------------------------------------ def new(self): """ Opens a new empty window """ editor = Editor() self.editor_area.add_editor(editor) self.editor_area.activate_editor(editor) self.activated() # Trait property getter/setters ---------------------------------------- def _get_active_editor(self): if self.editor_area is not None: return self.editor_area.active_editor return None
class ExampleTask(Task): """ A simple task for editing Python code. """ #### Task interface ####################################################### id = 'example.example_task' name = 'Multi-Tab Editor' active_editor = Property(Instance(IEditor), depends_on='editor_area.active_editor') editor_area = Instance(IEditorAreaPane) menu_bar = SMenuBar( SMenu(TaskAction(name='New', method='new', accelerator='Ctrl+N'), TaskAction(name='Open...', method='open', accelerator='Ctrl+O'), TaskAction(name='Save', method='save', accelerator='Ctrl+S'), id='File', name='&File'), SMenu(DockPaneToggleGroup(), id='View', name='&View')) tool_bars = [ SToolBar(TaskAction(method='new', tooltip='New file', image=ImageResource('document_new')), TaskAction(method='open', tooltip='Open a file', image=ImageResource('document_open')), TaskAction(method='save', tooltip='Save the current file', image=ImageResource('document_save')), image_size=(32, 32), show_tool_names=False), ] ########################################################################### # 'Task' interface. ########################################################################### def _default_layout_default(self): return TaskLayout(left=PaneItem('example.python_script_browser_pane')) def activated(self): """ Overriden to set the window's title. """ return filename = self.active_editor.path if self.active_editor else '' self.window.title = filename if filename else 'Untitled' def create_central_pane(self): """ Create the central pane: the script editor. """ self.editor_area = EditorAreaPane() return self.editor_area def create_dock_panes(self): """ Create the file browser and connect to its double click event. """ browser = PythonScriptBrowserPane() handler = lambda: self._open_file(browser.selected_file) browser.on_trait_change(handler, 'activated') return [browser] ########################################################################### # 'ExampleTask' interface. ########################################################################### def new(self): """ Opens a new empty window """ editor = PythonEditor() self.editor_area.add_editor(editor) self.editor_area.activate_editor(editor) self.activated() def open(self): """ Shows a dialog to open a file. """ dialog = FileDialog(parent=self.window.control, wildcard='*.py') if dialog.open() == OK: self._open_file(dialog.path) def save(self): """ Attempts to save the current file, prompting for a path if necessary. Returns whether the file was saved. """ editor = self.active_editor try: editor.save() except IOError: # If you are trying to save to a file that doesn't exist, open up a # FileDialog with a 'save as' action. dialog = FileDialog(parent=self.window.control, action='save as', wildcard='*.py') if dialog.open() == OK: editor.save(dialog.path) else: return False return True ########################################################################### # Protected interface. ########################################################################### def _open_file(self, filename): """ Opens the file at the specified path in the editor. """ editor = PythonEditor(path=filename) self.editor_area.add_editor(editor) self.editor_area.activate_editor(editor) self.activated() def _prompt_for_save(self): """ Prompts the user to save if necessary. Returns whether the dialog was cancelled. """ dirty_editors = dict([(editor.name, editor) for editor in self.editor_area.editors if editor.dirty]) if not list(dirty_editors.keys()): return True message = 'You have unsaved files. Would you like to save them?' dialog = ConfirmationDialog(parent=self.window.control, message=message, cancel=True, default=CANCEL, title='Save Changes?') result = dialog.open() if result == CANCEL: return False elif result == YES: for name, editor in list(dirty_editors.items()): editor.save(editor.path) return True #### Trait change handlers ################################################ @on_trait_change('window:closing') def _prompt_on_close(self, event): """ Prompt the user to save when exiting. """ close = self._prompt_for_save() event.veto = not close #### Trait property getter/setters ######################################## def _get_active_editor(self): if self.editor_area is not None: return self.editor_area.active_editor return None
class S4LVisualizationTask(Task): # pylint: disable=too-many-instance-attributes """ A task for visualizing Sim4Life EM fields from scES simulations """ #: The task's identifier. id = "s4l.main_task" #: The task's user-visible name. name = "S4L Visualization" #: Configuration parser. configuration = Instance(ConfigParser) #: Current participant's ID participant_id = Str() #: Temporary dictionary for editing user configuration _to_edit = Dict() #: Plane attributes dock pane. plane_attributes_pane = Instance(PlaneAttributes) #: Line attributes dock pane. line_attributes_pane = Instance(LineAttributes) #: Participant ID dock pane. part_id_pane = Instance(ParticipantIDPane) #: The currently active editor. active_editor = Property(Instance(IEditor), depends_on="editor_area.active_editor") #: The editor area in which the editor belongs. editor_area = Instance(IEditorAreaPane) #: The opening page's editor. start_page = Instance(StartPage) #: The object containing the field data. fields_model = Instance(EMFields) #: The 3D view panel. mayavi_scene = Instance(Mayavi3DScene) #: The slice figure panel. slice_figure = Instance(SliceFigureModel) #: The line figure panel. line_figure = Instance(LineFigureModel) #: Has the main window been initialized? model_initialized = Bool(False) #: Action to run :py:meth:`toggle_full_model`. toggle_model_action = TaskAction(name='Full Model', method='toggle_full_model', style='toggle', enabled_name='model_initialized') #: Action to run :py:meth:`change_cord_model`. new_cord_action = TaskAction(name='New Cord Model', method='change_cord_model', enabled_name='model_initialized') #: Action to run :py:meth:`toggle_log_scale`. toggle_scale_action = TaskAction(name='Log Scale', method='toggle_log_scale', style='toggle', checked=True, enabled_name='model_initialized') #: Action to run :py:meth:`toggle_line_cross_marker`. toggle_line_cross_action = TaskAction(name='Line Cross Marker', method='toggle_line_cross_marker', style='toggle', checked=True, enabled_name='model_initialized') #: The task's menu bar. menu_bar = SMenuBar( SMenu( TaskAction(name="&Open...", method="open", accelerator="Ctrl+O"), SMenu( TaskAction(name="Export &Slice", method="export_slice"), TaskAction(name="Export &Line", method="export_line"), id="File.Export", name="&Export", ), TaskAction(name="&Settings", method="edit_configuration"), id="File", name="&File", ), SMenu(SMenu(DockPaneToggleGroup(), id='View.Panes', name='&Panes'), toggle_model_action, toggle_scale_action, toggle_line_cross_action, id="View", name="&View"), SMenu( new_cord_action, SMenu(FieldSelectionGroup(), id='Edit.Fields', name='&Choose Field'), id='Edit', name='&Edit', ), ) # ------------------------------------------------------------------------ # 'Task' interface. # ------------------------------------------------------------------------ @observe('editor_area.active_tabwidget') def _update_tabwidgets(self, event): # pylint: disable=unused-argument try: self.editor_area.active_tabwidget.setTabsClosable(False) except AttributeError: pass def initialized(self): for tabwidget in self.editor_area.tabwidgets(): tabwidget.setTabsClosable(False) self.start_page = StartPage(task=self) self.editor_area.add_editor(self.start_page) self.editor_area.activate_editor(self.start_page) self.activated() def activated(self): self.editor_area.active_tabwidget.setTabsClosable(False) def create_central_pane(self): self.editor_area = SplitEditorAreaPane( callbacks={'open': self._new_file}) return self.editor_area def create_dock_panes(self): """ Create the attribute editor panes. """ self.plane_attributes_pane = PlaneAttributes( configuration=self.configuration) self.plane_attributes_pane.sync_trait('participant_id', self) self.line_attributes_pane = LineAttributes( configuration=self.configuration) self.line_attributes_pane.sync_trait('participant_id', self) self.part_id_pane = ParticipantIDPane() self.part_id_pane.sync_trait('participant_id', self) return [ self.plane_attributes_pane, self.line_attributes_pane, self.part_id_pane ] # ------------------------------------------------------------------------ # 'S4L_Visualization_task' interface. # ------------------------------------------------------------------------ def open(self): """ Show a dialog to open a new data source. """ dialog = FileDialog( title='Choose Data File', parent=self.window.control, wildcard=FileDialog.create_wildcard('Data Files', ['*.mat']) + FileDialog.WILDCARD_ALL) if dialog.open() == OK: if not self.model_initialized: self._new_file(dialog.path) def export_slice(self): """ Export data for current slice. """ dialog = FileDialog( title='Export Slice Plane', action='save as', parent=self.window.control, wildcard='' + FileDialog.create_wildcard('Excel Files', ['*.xlsx']) + FileDialog.create_wildcard('CSV Files', ['*.csv', '*.txt']) + FileDialog.WILDCARD_ALL) if dialog.open() == OK: self.slice_figure.export_slice(dialog.path) def export_line(self): """ Export data for current line. """ dialog = FileDialog( title='Export Line Data', action='save as', parent=self.window.control, wildcard='' + FileDialog.create_wildcard('Excel Files', ['*.xlsx']) + FileDialog.create_wildcard('CSV Files', ['*.csv', '*.txt']) + FileDialog.WILDCARD_ALL) if dialog.open() == OK: self.line_figure.export_line(dialog.path) def toggle_full_model(self): """ Toggle between showing the full spinal cord model and showing only below the cut plane. """ self.mayavi_scene.show_full_model = not self.mayavi_scene.show_full_model def toggle_log_scale(self): """ Toggle between using a logarithmic scale and a linear scale. """ self.mayavi_scene.log_scale = not self.mayavi_scene.log_scale self.slice_figure.log_scale = not self.slice_figure.log_scale def toggle_line_cross_marker(self): """ Toggle visibility of the line cross marker on the slice figure. """ self.slice_figure.draw_cross = not self.slice_figure.draw_cross def change_cord_model(self): """ Change the spinal cord model file used for the 3D display. """ dialog = FileDialog( title='Choose Spinal Cord Model', parent=self.window.control, wildcard=FileDialog.create_wildcard('VTK Model', ['*.vtk']) + FileDialog.WILDCARD_ALL) if dialog.open() == OK: self.mayavi_scene.csf_model = dialog.path def reset_camera(self): """ Set the camera for the Mayavi scene to a pre-determined perspective. """ self.mayavi_scene.initialize_camera() def edit_configuration(self): preferences = PreferenceDialog(configuration=self.configuration, title="S4L Visualization Preferences") ui = preferences.edit_traits(kind='modal') if ui.result: self.configuration.remove_section('') with open('config.ini', 'w') as out_file: self.configuration.write(out_file) def update_participant_id(self): if self.model_initialized: self.line_attributes_pane.set_participant_defaults() self.mayavi_scene.reset_participant_defaults() # ------------------------------------------------------------------------ # Protected interface. # ------------------------------------------------------------------------ def _new_file(self, filename): """ Change the data source to the file at the specified path Parameters ---------- filename : :py:class:`os.PathLike` Path to data source file """ GUI.set_busy(True) if m := re.search(r"\D(\d{3})\D", filename): self.participant_id = m.group(1) if self.participant_id is not None: if self.participant_id not in self.configuration: self.configuration[self.participant_id] = {} default_points = self.configuration[self.participant_id]['points'] self.line_attributes_pane.set_points(default_points) self.part_id_pane.participant_id = self.participant_id self.editor_area.remove_editor(self.start_page) self.window.set_layout( TaskLayout(bottom=PaneItem('s4l.plane_attributes'), left=VSplitter(PaneItem('s4l.line_attributes'), PaneItem('s4l.participant_id_pane')), top_left_corner='top', top_right_corner='top', bottom_left_corner='left', bottom_right_corner='right')) self.fields_model = EMFields(configuration=self.configuration, data_path=filename) self.fields_model.sync_trait('participant_id', self) self.plane_attributes_pane.fields_model = self.fields_model self.mayavi_scene = Mayavi3DScene(fields_model=self.fields_model, configuration=self.configuration) self.mayavi_scene.sync_trait('participant_id', self) self.mayavi_scene.sync_trait('normal', self.plane_attributes_pane) self.mayavi_scene.sync_trait('origin', self.plane_attributes_pane) self.line_attributes_pane.sync_trait('points', self.mayavi_scene, mutual=False) self.mayavi_scene.create_plot() editor = self.mayavi_scene self.editor_area.add_editor(editor) self.editor_area.activate_editor(editor) self.editor_area.active_tabwidget.setTabsClosable(False) self.activated() self.slice_figure = SliceFigureModel(fields_model=self.fields_model, mayavi_scene=self.mayavi_scene, configuration=self.configuration) self.slice_figure.sync_trait('participant_id', self) self.slice_figure.create_plot() self.editor_area.add_editor(self.slice_figure) self.editor_area.activate_editor(self.slice_figure) self.editor_area.active_tabwidget.setTabsClosable(False) self.activated() self.line_figure = LineFigureModel(fields_model=self.fields_model) self.line_figure.sync_trait('points', self.line_attributes_pane) self.line_figure.create_plot(None) self.editor_area.add_editor(self.line_figure) self.editor_area.activate_editor(self.line_figure) self.editor_area.active_tabwidget.setTabsClosable(False) self.activated() self.mayavi_scene.disable_widgets() while self.editor_area.active_tabwidget.parent().is_collapsible(): self.editor_area.active_tabwidget.parent().collapse() layout = Splitter( Tabbed(PaneItem(1), PaneItem(2), active_tab=0), Tabbed(PaneItem(0), active_tab=0), ) self.editor_area.set_layout(layout) self.editor_area.control.setSizes([900, 295]) self.editor_area.activate_editor(self.slice_figure) self.editor_area.active_tabwidget.setTabsClosable(False) for tabwidget in self.editor_area.tabwidgets(): tabwidget.setTabsClosable(False) self.model_initialized = True
class ImageViewerTask(Task): """ A simple task for viewing images. """ #### Task interface ####################################################### id = 'example.image_viewer_task' name = 'Image Viewer' #default_layout = TaskLayout( # left=PaneItem('example.python_script_browser_pane')) menu_bar = SMenuBar( SMenu(TaskAction(name='Open...', method='open', accelerator='Ctrl+O'), id='File', name='&File'), SMenu(DockPaneToggleGroup(), id='View', name='&View')) tool_bars = [ SToolBar( TaskAction(method='open', tooltip='Open a file', image=ImageResource('document_open'))) ] ########################################################################### # 'Task' interface. ########################################################################### def _default_layout_default(self): return TaskLayout(left=PaneItem('image_viewer.file_browser_pane')) def activated(self): """ Overriden to set the window's title. """ filename = self.window.central_pane.model.path self.window.title = filename if filename else 'Untitled' def create_central_pane(self): """ Create the central pane: the image viewer. """ return ImageViewerPane() def create_dock_panes(self): """ Create the file browser and connect to its double click event. """ browser = FileBrowserPane(filters=["*.jpg"]) handler = lambda: self._open_file(browser.selected_file) browser.on_trait_change(handler, 'activated') return [browser] ########################################################################### # 'ExampleTask' interface. ########################################################################### def open(self): """ Shows a dialog to open a file. """ dialog = FileDialog(parent=self.window.control, wildcard='*.jpg') if dialog.open() == OK: self._open_file(dialog.path) ########################################################################### # Protected interface. ########################################################################### def _open_file(self, filename): """ Opens the file at the specified path in the editor. """ self.window.central_pane.model.path = filename self.activated()
class ExampleTask(Task): """ A simple task for opening a blank editor. """ # Task interface ------------------------------------------------------- id = "example.example_task" name = "Multi-Tab Editor" active_editor = Property(Instance(IEditor), observe="editor_area.active_editor") editor_area = Instance(IEditorAreaPane) menu_bar = SMenuBar( SMenu( TaskAction(name="New", method="new", accelerator="Ctrl+N"), TaskAction(name="Add Task", method="add_task", accelerator="Ctrl+A"), TaskAction(name="Remove Task", method="remove_task", accelerator="Ctrl+R"), id="File", name="&File", ), SMenu(DockPaneToggleGroup(), TaskToggleGroup(), id="View", name="&View"), ) tool_bars = [ SToolBar( TaskAction( method="new", tooltip="New file", image=ImageResource("document_new"), ), image_size=(32, 32), ) ] # ------------------------------------------------------------------------ # 'Task' interface. # ------------------------------------------------------------------------ def _default_layout_default(self): return TaskLayout(top=VSplitter( HSplitter(PaneItem("steps.pane1"), PaneItem("steps.pane2")))) def create_central_pane(self): """ Create the central pane: the script editor. """ self.editor_area = EditorAreaPane() return self.editor_area def create_dock_panes(self): """ Create the file browser and connect to its double click event. """ return [Pane1(), Pane2()] # ------------------------------------------------------------------------ # 'ExampleTask' interface. # ------------------------------------------------------------------------ def new(self): """ Opens a new empty window """ editor = Editor() self.editor_area.add_editor(editor) self.editor_area.activate_editor(editor) self.activated() def add_task(self): """ Opens a new empty window """ task3 = ThirdTask() self.window.add_task(task3) self.window.activate_task(task3) def remove_task(self): """ Opens a new empty window """ task = self.window.tasks[0] window = self.window window.remove_task(self) window.activate_task(task) # Trait property getter/setters ---------------------------------------- def _get_active_editor(self): if self.editor_area is not None: return self.editor_area.active_editor return None
class FlowTask(Task): """ classdocs """ id = "edu.mit.synbio.cytoflowgui.flow_task" name = "Cytometry analysis" # the main workflow instance. model = Instance(Workflow) # the center pane workflow_pane = Instance(WorkflowDockPane) view_pane = Instance(ViewDockPane) help_pane = Instance(HelpDockPane) plot_params_pane = Instance(PlotParamsPane) # plugin lists, to setup the interface op_plugins = List(IOperationPlugin) view_plugins = List(IViewPlugin) menu_bar = SMenuBar(SMenu(TaskAction(name='Open...', method='on_open', accelerator='Ctrl+O'), TaskAction(name='Save', #image='save', method='on_save', accelerator='Ctrl+S'), TaskAction(name='Save As...', method='on_save_as', accelerator='Ctrl+e'), TaskAction(name='Save Plot...', method='on_export', accelerator='Ctrl+x'), TaskAction(name='Export Jupyter notebook...', method='on_notebook', accelerator='Ctrl+I'), # TaskAction(name='Preferences...', # method='on_prefs', # accelerator='Ctrl+P'), id='File', name='&File'), SMenu(TaskToggleGroup(), # TaskWindowToggleGroup(), id = 'View', name = '&View'), SMenu(TaskAction(name = 'Report a problem....', method = 'on_problem'), TaskAction(name='About...', method='on_about'), id="Help", name ="&Help")) tool_bars = [ SToolBar(TaskAction(method='on_new', name = "New", tooltip='New workflow', image=ImageResource('new')), TaskAction(method='on_open', name = "Open", tooltip='Open a file', image=ImageResource('open')), TaskAction(method='on_save', name = "Save", tooltip='Save the current file', image=ImageResource('save')), TaskAction(method='on_export', name = "Save Plot", tooltip='Save the current plot', image=ImageResource('export')), TaskAction(method='on_notebook', name='Notebook', tooltip="Export to an Jupyter notebook...", image=ImageResource('ipython')), TaskAction(method = "on_calibrate", name = "Calibrate FCS...", tooltip = "Calibrate FCS files", image = ImageResource('tasbe')), TaskAction(method = 'on_problem', name = "Report a bug...", tooltib = "Report a bug", image = ImageResource('bug')), # TaskAction(method='on_prefs', # name = "Prefs", # tooltip='Preferences', # image=ImageResource('prefs')), image_size = (32, 32))] # the file to save to if the user clicks "save" and has already clicked # "open" or "save as". filename = Unicode def activated(self): # if we're coming back from the TASBE task, re-load the saved # workflow if self.model.backup_workflow: self.model.workflow = self.model.backup_workflow self.model.backup_workflow = [] return # else, set up a new workflow # add the import op if not self.model.workflow: self.add_operation(ImportPlugin().id) self.model.selected = self.model.workflow[0] # if we're debugging, add a few data bits if self.model.debug: from cytoflow import Tube import_op = self.model.workflow[0].operation import_op.conditions = {"Dox" : "float", "Well" : "category"} tube1 = Tube(file = "../cytoflow/tests/data/Plate01/CFP_Well_A4.fcs", conditions = {"Dox" : 0.0, "Well" : 'A'}) tube2 = Tube(file = "../cytoflow/tests/data/Plate01/RFP_Well_A3.fcs", conditions = {"Dox" : 10.0, "Well" : 'A'}) tube3 = Tube(file = "../cytoflow/tests/data/Plate01/CFP_Well_B4.fcs", conditions = {"Dox" : 0.0, "Well" : 'B'}) tube4 = Tube(file = "../cytoflow/tests/data/Plate01/RFP_Well_A6.fcs", conditions = {"Dox" : 10.0, "Well" : 'B'}) import_op.tubes = [tube1, tube2, tube3, tube4] # from cytoflowgui.op_plugins import ChannelStatisticPlugin # self.add_operation(ChannelStatisticPlugin().id) # stat_op = self.model.workflow[1].operation # stat_op.name = "Test" # stat_op.channel = "Y2-A" # stat_op.statistic_name = "Geom.Mean" # stat_op.by = ["Dox", "Well"] # self.model.selected = self.model.workflow[1] self.model.modified = False def _default_layout_default(self): return TaskLayout(left = VSplitter(PaneItem("edu.mit.synbio.cytoflowgui.workflow_pane"), PaneItem("edu.mit.synbio.cytoflowgui.help_pane")), right = VSplitter(PaneItem("edu.mit.synbio.cytoflowgui.view_traits_pane"), PaneItem("edu.mit.synbio.cytoflowgui.params_pane")), top_left_corner = 'left', bottom_left_corner = 'left', top_right_corner = 'right', bottom_right_corner = 'right') def create_central_pane(self): return self.application.plot_pane def create_dock_panes(self): self.workflow_pane = WorkflowDockPane(model = self.model, plugins = self.op_plugins, task = self) self.view_pane = ViewDockPane(model = self.model, plugins = self.view_plugins, task = self) self.help_pane = HelpDockPane(view_plugins = self.view_plugins, op_plugins = self.op_plugins, task = self) self.plot_params_pane = PlotParamsPane(model = self.model, task = self) return [self.workflow_pane, self.view_pane, self.help_pane, self.plot_params_pane] def on_new(self): if self.model.modified: ret = confirm(parent = None, message = "Are you sure you want to discard the current workflow?", title = "Clear workflow?") if ret != YES: return self.filename = "" self.window.title = "Cytoflow" # clear the workflow self.model.workflow = [] # add the import op self.add_operation(ImportPlugin().id) # and select the operation self.model.selected = self.model.workflow[0] self.model.modified = False def on_open(self): """ Shows a dialog to open a file. """ if self.model.modified: ret = confirm(parent = None, message = "Are you sure you want to discard the current workflow?", title = "Clear workflow?") if ret != YES: return dialog = FileDialog(parent = self.window.control, action = 'open', wildcard = (FileDialog.create_wildcard("Cytoflow workflow", "*.flow") + ';' + #@UndefinedVariable FileDialog.create_wildcard("All files", "*"))) #@UndefinedVariable if dialog.open() == OK: self.open_file(dialog.path) self.filename = dialog.path self.window.title = "Cytoflow - " + self.filename def open_file(self, path): new_workflow = load_yaml(path) # a few things to take care of when reloading for wi_idx, wi in enumerate(new_workflow): # get wi lock wi.lock.acquire() # clear the wi status wi.status = "loading" # re-link the linked list. if wi_idx > 0: wi.previous_wi = new_workflow[wi_idx - 1] if wi_idx < len(new_workflow) - 1: wi.next_wi = new_workflow[wi_idx + 1] # replace the current workflow with the one we just loaded if False: # for debugging the loading of things from .event_tracer import record_events with record_events() as container: self.model.workflow = new_workflow container.save_to_directory(os.getcwd()) else: self.model.workflow = new_workflow self.model.modified = False for wi in self.model.workflow: wi.lock.release() def on_save(self): """ Save the file to the previous filename """ if self.filename: save_yaml(self.model.workflow, self.filename) self.model.modified = False else: self.on_save_as() def on_save_as(self): dialog = DefaultFileDialog(parent = self.window.control, action = 'save as', default_suffix = "flow", wildcard = (FileDialog.create_wildcard("Cytoflow workflow", "*.flow") + ';' + #@UndefinedVariable FileDialog.create_wildcard("All files", "*"))) #@UndefinedVariable if dialog.open() == OK: save_yaml(self.model.workflow, dialog.path) self.filename = dialog.path self.model.modified = False self.window.title = "Cytoflow - " + self.filename @on_trait_change('model.modified', post_init = True) def _on_model_modified(self, val): if val: if not self.window.title.endswith("*"): self.window.title += "*" else: if self.window.title.endswith("*"): self.window.title = self.window.title[:-1] def on_export(self): task = next(x for x in self.window.tasks if x.id == 'edu.mit.synbio.cytoflowgui.export_task') self.window.activate_task(task) def on_calibrate(self): task = next(x for x in self.window.tasks if x.id == 'edu.mit.synbio.cytoflowgui.tasbe_task') self.window.activate_task(task) def on_notebook(self): """ Shows a dialog to export the workflow to an Jupyter notebook """ dialog = FileDialog(parent = self.window.control, action = 'save as', default_suffix = "ipynb", wildcard = (FileDialog.create_wildcard("Jupyter notebook", "*.ipynb") + ';' + #@UndefinedVariable FileDialog.create_wildcard("All files", "*"))) # @UndefinedVariable if dialog.open() == OK: save_notebook(self.model.workflow, dialog.path) def on_prefs(self): pass def on_problem(self): log = str(self._get_package_versions()) + "\n" + self.application.application_log.getvalue() msg = "The best way to report a problem is send an application log to " \ "the developers. You can do so by either sending us an email " \ "with the log in it, or saving the log to a file and filing a " \ "new issue on GitHub at " \ "https://github.com/bpteague/cytoflow/issues/new" dialog = ConfirmationDialog(message = msg, informative = "Which would you like to do?", yes_label = "Send an email...", no_label = "Save to a file...") if dialog.open() == NO: dialog = DefaultFileDialog(parent = self.window.control, action = 'save as', default_suffix = "log", wildcard = (FileDialog.create_wildcard("Log files", "*.log") + ';' + #@UndefinedVariable FileDialog.create_wildcard("All files", "*"))) #@UndefinedVariable if dialog.open() == OK: with open(dialog.path, 'w') as f: f.write(log) webbrowser.open_new_tab("https://github.com/bpteague/cytoflow/issues/new") return information(None, "I'll now try to open your email client and create a " "new message to the developer. Debugging logs are " "attached. Please fill out the template bug report and " "send -- thank you for reporting a bug!") log = self.application.application_log.getvalue() versions = ["{0} {1}".format(key, value) for key, value in self._get_package_versions().items()] body = """ Thank you for your bug report! Please fill out the following template. PLATFORM (Mac, PC, Linux, other): OPERATING SYSTEM (eg OSX 10.7, Windows 8.1): SEVERITY (Critical? Major? Minor? Enhancement?): DESCRIPTION: - What were you trying to do? - What happened? - What did you expect to happen? PACKAGE VERSIONS: {0} DEBUG LOG: {1} """.format(versions, log) mailto("*****@*****.**", subject = "Cytoflow bug report", body = body) def _get_package_versions(self): import sys from cytoflow import __version__ as cf_version from fcsparser import __version__ as fcs_version from pandas import __version__ as pd_version from numpy import __version__ as np_version from numexpr import __version__ as nxp_version from bottleneck import __version__ as btl_version from seaborn import __version__ as sns_version from matplotlib import __version__ as mpl_version from scipy import __version__ as scipy_version from sklearn import __version__ as skl_version from statsmodels import __version__ as stats_version from pyface import __version__ as pyf_version from envisage import __version__ as env_version from traits import __version__ as trt_version from traitsui import __version__ as trt_ui_version from yapf import __version__ as yapf_version from nbformat import __version__ as nb_version from yaml import __version__ as yaml_version return {"python" : sys.version, "cytoflow" : cf_version, "fcsparser" : fcs_version, "pandas" : pd_version, "numpy" : np_version, "numexpr" : nxp_version, "bottleneck" : btl_version, "seaborn" : sns_version, "matplotlib" : mpl_version, "scipy" : scipy_version, "scikit-learn" : skl_version, "statsmodels" : stats_version, "pyface" : pyf_version, "envisage" : env_version, "traits" : trt_version, "traitsui" : trt_ui_version, "nbformat" : nb_version, "yapf" : yapf_version, "yaml" : yaml_version} def on_about(self): versions = self._get_package_versions() text = ["<b>Cytoflow {0}</b>".format(versions['cytoflow']), "<p>"] ver_text = ["{0} {1}".format(key, value) for key, value in versions.items()] text.extend(ver_text) text.extend(["Icons from the <a href=http://tango.freedesktop.org>Tango Desktop Project</a>", "<a href=https://thenounproject.com/search/?q=setup&i=14287>Settings icon</a> by Paulo Sa Ferreira from <a href=https://thenounproject.com>The Noun Project</a>", "<a href=http://www.freepik.com/free-photos-vectors/background>App icon from Starline - Freepik.com</a>", "Cuvette image from Wikimedia Commons user <a href=http://commons.wikimedia.org/wiki/File:Hellma_Large_cone_cytometry_cell.JPG>HellmaUSA</a>"]) dialog = AboutDialog(text = text, parent = self.window.control, title = "About", image = ImageResource('cuvette'), additions = text) dialog.open() @on_trait_change('model.selected', post_init = True) def _on_select_op(self, selected): if selected: self.view_pane.enabled = (selected is not None) self.view_pane.default_view = selected.default_view.id if selected.default_view else "" self.view_pane.selected_view = selected.current_view.id if selected.current_view else "" self.help_pane.help_id = selected.operation.id else: self.view_pane.enabled = False @on_trait_change('view_pane.selected_view', post_init = True) def _on_select_view(self, view_id): if not view_id: return # if we already have an instantiated view object, find it try: self.model.selected.current_view = next((x for x in self.model.selected.views if x.id == view_id)) except StopIteration: # else make the new view plugin = next((x for x in self.view_plugins if x.view_id == view_id)) view = plugin.get_view() self.model.selected.views.append(view) self.model.selected.current_view = view self.help_pane.help_id = view_id def add_operation(self, op_id): # first, find the matching plugin plugin = next((x for x in self.op_plugins if x.id == op_id)) # next, get an operation op = plugin.get_operation() # make a new workflow item wi = WorkflowItem(operation = op, deletable = (op_id != 'edu.mit.synbio.cytoflowgui.op_plugins.import')) # if the op has a default view, add it to the wi try: wi.default_view = op.default_view() wi.views.append(wi.default_view) wi.current_view = wi.default_view except AttributeError: pass # figure out where to add it if self.model.selected: idx = self.model.workflow.index(self.model.selected) + 1 else: idx = len(self.model.workflow) # the add_remove_items handler takes care of updating the linked list self.model.workflow.insert(idx, wi) # and make sure to actually select the new wi self.model.selected = wi
class ExampleTask(Task): """ A simple task for opening a blank editor. """ #### Task interface ####################################################### id = 'example.example_task' name = 'Multi-Tab Editor' active_editor = Property(Instance(IEditor), depends_on='editor_area.active_editor') editor_area = Instance(IEditorAreaPane) menu_bar = SMenuBar( SMenu(TaskAction(name='New', method='new', accelerator='Ctrl+N'), id='File', name='&File'), SMenu(DockPaneToggleGroup(), TaskToggleGroup(), id='View', name='&View')) tool_bars = [ SToolBar(TaskAction(method='new', tooltip='New file', image=ImageResource('document_new')), image_size=(32, 32)), ] ########################################################################### # 'Task' interface. ########################################################################### def _default_layout_default(self): return TaskLayout(top=Tabbed(PaneItem('steps.first_pane'), PaneItem('steps.second_pane'), PaneItem('steps.third_pane'))) def create_central_pane(self): """ Create the central pane: the script editor. """ self.editor_area = EditorAreaPane() return self.editor_area def create_dock_panes(self): """ Create the file browser and connect to its double click event. """ return [FirstPane(), SecondPane(), ThirdPane()] ########################################################################### # 'ExampleTask' interface. ########################################################################### def new(self): """ Opens a new empty window """ editor = Editor() self.editor_area.add_editor(editor) self.editor_area.activate_editor(editor) self.activated() #### Trait property getter/setters ######################################## def _get_active_editor(self): if self.editor_area is not None: return self.editor_area.active_editor return None