def on_set_dstat_params_file(self, widget, data=None): options = self.get_step_options() form = Form.of(Filepath.named('dstat_params_file') .using(default=options.get('dstat_params_file', ''), optional=True, properties={'patterns': [('Dstat parameters file (*.yml)', ('*.yml', ))]})) dialog = FormViewDialog(form, 'Set DStat parameters file') valid, response = dialog.run() if valid: options['dstat_params_file'] = response['dstat_params_file'] self.set_step_values(options)
def __init__(self): window = gtk.Window(gtk.WINDOW_TOPLEVEL) window.set_title("Mpeg2-Player") window.set_default_size(640, 500) window.connect("destroy", self.on_destroy) vbox = gtk.VBox() window.add(vbox) hbox = gtk.HBox() vbox.pack_start(hbox, expand=False) video_mode_enum = Enum.named('video_mode').valued(*self.video_mode_keys) form = Form.of( video_mode_enum.using(default=self.video_mode_keys[0]), Filepath.named('output_path').using(default=''), Integer.named('bitrate').using(default=150, validators=[ValueAtLeast( minimum=25)], properties={'step': 25, 'label': 'Bitrate (KB/s)', }), String.named('transform_string').using(default='1,0,0,0,1,0,0,0,1'), Boolean.named('draw_cairo').using(default=False), ) self.video_mode_form_view = create_form_view(form) for field in ['video_mode', 'output_path', 'bitrate', 'transform_string', 'draw_cairo']: setattr(self, '%s_field' % field, self.video_mode_form_view.form\ .fields[field]) self.video_mode_field.proxy.connect('changed', self._on_mode_changed) self.video_source = None hbox.add(self.video_mode_form_view.widget) self.button = gtk.Button("Start") hbox.pack_start(self.button, False) self.button.connect("clicked", self.start_stop) self.aframe = gtk.AspectFrame(xalign=0.5, yalign=1.0, ratio=4.0 / 3.0, obey_child=False) self.pipeline = None self._proxy = None vbox.pack_start(self.aframe, expand=True) self.movie_view = GtkVideoView() self.movie_window = self.movie_view.widget self.aframe.add(self.movie_window) window.show_all() self.window = window
def on_select_script(self, widget, data=None): """ Handler called when the user clicks on "PSTrace step config..." in the "Tools" menu. """ app = get_app() options = self.get_step_options() form = Form.of(Filepath.named('script').using(default=options.script, optional=True)) dialog = FormViewDialog() valid, response = dialog.run(form) step_options_changed = False if valid and (response['script'] and response['script'] != options.script): options.script = response['script'] step_options_changed = True if step_options_changed: emit_signal('on_step_options_changed', [self.name, app.protocol .current_step_number], interface=IPlugin)
class App(SingletonPlugin, AppDataController): implements(IPlugin) ''' INFO: <Plugin App 'microdrop.app'> INFO: <Plugin ConfigController 'microdrop.gui.config_controller'> INFO: <Plugin DmfDeviceController 'microdrop.gui.dmf_device_controller'> INFO: <Plugin ExperimentLogController 'microdrop.gui.experiment_log_controller'> INFO: <Plugin MainWindowController 'microdrop.gui.main_window_controller'> INFO: <Plugin ProtocolController 'microdrop.gui.protocol_controller'> INFO: <Plugin ProtocolGridController 'microdrop.gui.protocol_grid_controller'> ''' core_plugins = [ 'microdrop.app', 'microdrop.gui.config_controller', 'microdrop.gui.dmf_device_controller', 'microdrop.gui.experiment_log_controller', 'microdrop.gui.main_window_controller', 'microdrop.gui.protocol_controller', 'microdrop.gui.protocol_grid_controller', 'microdrop.zmq_hub_plugin', 'microdrop.electrode_controller_plugin', 'microdrop.device_info_plugin' ] AppFields = Form.of( Integer.named('x').using(default=None, optional=True, properties={'show_in_gui': False}), Integer.named('y').using(default=None, optional=True, properties={'show_in_gui': False}), Integer.named('width').using(default=400, optional=True, properties={'show_in_gui': False}), Integer.named('height').using(default=500, optional=True, properties={'show_in_gui': False}), String.named('server_url').using( default='http://microfluidics.utoronto.ca/update', optional=True, properties=dict(show_in_gui=False)), Boolean.named('realtime_mode').using( default=False, optional=True, properties=dict(show_in_gui=False)), Filepath.named('log_file').using( default='', optional=True, properties={'action': gtk.FILE_CHOOSER_ACTION_SAVE}), Boolean.named('log_enabled').using(default=False, optional=True), Enum.named('log_level').using(default='info', optional=True).valued( 'debug', 'info', 'warning', 'error', 'critical')) def __init__(self): ''' .. versionchanged:: 2.11.2 Add :attr:`gtk_thread` attribute, holding a reference to the thread that the GTK main loop is executing in. .. versionchanged:: 2.17 Remove :attr:`version` attribute. Use :attr:`microdrop.__version__` instead. ''' args = parse_args() print 'Arguments: %s' % args self.name = "microdrop.app" #: .. versionadded:: 2.11.2 self.gtk_thread = None self.realtime_mode = False self.running = False self.builder = gtk.Builder() self.signals = {} self.plugin_data = {} # these members are initialized by plugins self.experiment_log_controller = None self.config_controller = None self.dmf_device_controller = None self.protocol_controller = None self.main_window_controller = None # Enable custom logging handler logging.getLogger().addHandler(CustomHandler()) self.log_file_handler = None # config model try: self.config = Config(args.config) except IOError: logging.error( 'Could not read configuration file, `%s`. Make sure' ' it exists and is readable.', args.config) raise SystemExit(-1) # set the log level if self.name in self.config.data and ('log_level' in self.config.data[self.name]): self._set_log_level(self.config.data[self.name]['log_level']) _L().info('MicroDrop version: %s', __version__) _L().info('Running in working directory: %s', os.getcwd()) # dmf device self.dmf_device = None # protocol self.protocol = None def get_data(self, plugin_name): data = self.plugin_data.get(plugin_name) if data: return data else: return {} def set_data(self, plugin_name, data): ''' .. versionchanged:: 2.20 Log data and plugin name to debug level. ''' logger = _L() # use logger with method context if logger.getEffectiveLevel() >= logging.DEBUG: caller = caller_name(skip=2) logger.debug('%s -> plugin_data:', caller) map(logger.debug, pprint.pformat(data).splitlines()) self.plugin_data[plugin_name] = data def on_app_options_changed(self, plugin_name): if plugin_name == self.name: data = self.get_data(self.name) if 'realtime_mode' in data: if self.realtime_mode != data['realtime_mode']: self.realtime_mode = data['realtime_mode'] if self.protocol_controller: self.protocol_controller.run_step() if 'log_file' in data and 'log_enabled' in data: self.apply_log_file_config(data['log_file'], data['log_enabled']) if 'log_level' in data: self._set_log_level(data['log_level']) if 'width' in data and 'height' in data: self.main_window_controller.view.resize( data['width'], data['height']) # allow window to resize before other signals are processed while gtk.events_pending(): gtk.main_iteration() if data.get('x') is not None and data.get('y') is not None: self.main_window_controller.view.move(data['x'], data['y']) # allow window to resize before other signals are processed while gtk.events_pending(): gtk.main_iteration() def apply_log_file_config(self, log_file, enabled): if enabled and not log_file: _L().error('Log file can only be enabled if a path is selected.') return False self.update_log_file() return True @property def plugins(self): return set(self.plugin_data.keys()) def plugin_name_lookup(self, name, re_pattern=False): if not re_pattern: return name for plugin_name in self.plugins: if re.search(name, plugin_name): return plugin_name return None def update_plugins(self): ''' .. versionchanged:: 2.16.2 Method was deprecated. ''' raise DeprecationWarning('The `update_plugins` method was deprecated ' 'in version 2.16.2.') def gtk_thread_active(self): ''' Returns ------- bool ``True`` if the currently active thread is the GTK thread. .. versionadded:: 2.11.2 ''' if self.gtk_thread is not None and (threading.current_thread().ident == self.gtk_thread.ident): return True else: return False def run(self): ''' .. versionchanged:: 2.11.2 Set :attr:`gtk_thread` attribute, holding a reference to the thread that the GTK main loop is executing in. .. versionchanged:: 2.16.2 Do not attempt to update plugins. ''' logger = _L() # use logger with method context self.gtk_thread = threading.current_thread() # set realtime mode to false on startup if self.name in self.config.data and \ 'realtime_mode' in self.config.data[self.name]: self.config.data[self.name]['realtime_mode'] = False plugin_manager.emit_signal('on_plugin_enable') log_file = self.get_app_values()['log_file'] if not log_file: self.set_app_values({ 'log_file': ph.path(self.config['data_dir']).joinpath('microdrop.log') }) pwd = ph.path(os.getcwd()).realpath() if '' in sys.path and pwd.joinpath('plugins').isdir(): logger.info( '[warning] Removing working directory `%s` from Python' ' import path.', pwd) sys.path.remove('') # Import enabled plugins from Conda environment. conda_plugins_dir = mpm.api.MICRODROP_CONDA_ETC.joinpath( 'plugins', 'enabled') if conda_plugins_dir.isdir(): plugin_manager.load_plugins(conda_plugins_dir, import_from_parent=False) self.update_log_file() logger.info('User data directory: %s', self.config['data_dir']) logger.info('Plugins directory: %s', conda_plugins_dir) logger.info('Devices directory: %s', self.get_device_directory()) FormViewDialog.default_parent = self.main_window_controller.view self.builder.connect_signals(self.signals) observers = {} plugins_to_disable_by_default = [] # Enable plugins according to schedule requests for package_name in self.config['plugins']['enabled']: try: service = plugin_manager. \ get_service_instance_by_package_name(package_name) observers[service.name] = service except KeyError: logger.warning('No plugin found registered with name `%s`', package_name) # Mark plugin to be removed from "enabled" list to prevent # trying to enable it on future launches. plugins_to_disable_by_default.append(package_name) except Exception, exception: logger.error(exception, exc_info=True) # Remove marked plugins from "enabled" list to prevent trying to enable # it on future launches. for package_name_i in plugins_to_disable_by_default: self.config['plugins']['enabled'].remove(package_name_i) schedule = plugin_manager.get_schedule(observers, "on_plugin_enable") # Load optional plugins marked as enabled in config for p in schedule: try: plugin_manager.enable(p) except KeyError: logger.warning('Requested plugin (%s) is not available.\n\n' 'Please check that it exists in the plugins ' 'directory:\n\n %s' % (p, self.config['plugins']['directory']), exc_info=True) plugin_manager.log_summary() self.experiment_log = None # save the protocol name from the config file because it is # automatically overwritten when we load a new device protocol_name = self.config['protocol']['name'] # if there is no device specified in the config file, try choosing one # from the device directory by default device_directory = ph.path(self.get_device_directory()) if not self.config['dmf_device']['name']: try: self.config['dmf_device']['name'] = \ device_directory.dirs()[0].name except Exception: pass # load the device from the config file if self.config['dmf_device']['name']: if device_directory: device_path = os.path.join(device_directory, self.config['dmf_device']['name'], DEVICE_FILENAME) self.dmf_device_controller.load_device(device_path) # if we successfully loaded a device if self.dmf_device: # reapply the protocol name to the config file self.config['protocol']['name'] = protocol_name # load the protocol if self.config['protocol']['name']: directory = self.get_device_directory() if directory: filename = os.path.join(directory, self.config['dmf_device']['name'], "protocols", self.config['protocol']['name']) self.protocol_controller.load_protocol(filename) data = self.get_data("microdrop.app") x = data.get('x', None) y = data.get('y', None) width = data.get('width', 400) height = data.get('height', 600) self.main_window_controller.view.resize(width, height) if x is not None and y is not None: self.main_window_controller.view.move(x, y) plugin_manager.emit_signal('on_gui_ready') self.main_window_controller.main()
class App(SingletonPlugin, AppDataController): implements(IPlugin) ''' INFO: <Plugin App 'microdrop.app'> INFO: <Plugin ConfigController 'microdrop.gui.config_controller'> INFO: <Plugin DmfDeviceController 'microdrop.gui.dmf_device_controller'> INFO: <Plugin ExperimentLogController 'microdrop.gui.experiment_log_controller'> INFO: <Plugin MainWindowController 'microdrop.gui.main_window_controller'> INFO: <Plugin ProtocolController 'microdrop.gui.protocol_controller'> INFO: <Plugin ProtocolGridController 'microdrop.gui.protocol_grid_controller'> ''' core_plugins = [ 'microdrop.app', 'microdrop.gui.config_controller', 'microdrop.gui.dmf_device_controller', 'microdrop.gui.experiment_log_controller', 'microdrop.gui.main_window_controller', 'microdrop.gui.protocol_controller', 'microdrop.gui.protocol_grid_controller', 'wheelerlab.zmq_hub_plugin', 'wheelerlab.electrode_controller_plugin', 'wheelerlab.device_info_plugin' ] AppFields = Form.of( Integer.named('x').using(default=None, optional=True, properties={'show_in_gui': False}), Integer.named('y').using(default=None, optional=True, properties={'show_in_gui': False}), Integer.named('width').using(default=400, optional=True, properties={'show_in_gui': False}), Integer.named('height').using(default=500, optional=True, properties={'show_in_gui': False}), Enum.named('update_automatically' #pylint: disable-msg=E1101,E1120 ).using(default=1, optional=True).valued( 'auto-update', 'check for updates, but ask before installing', '''don't check for updates'''), String.named('server_url').using( #pylint: disable-msg=E1120 default='http://microfluidics.utoronto.ca/update', optional=True, properties=dict(show_in_gui=False)), Boolean.named('realtime_mode').using( #pylint: disable-msg=E1120 default=False, optional=True, properties=dict(show_in_gui=False)), Filepath.named('log_file').using( #pylint: disable-msg=E1120 default='', optional=True, properties={'action': gtk.FILE_CHOOSER_ACTION_SAVE}), Boolean.named('log_enabled').using( #pylint: disable-msg=E1120 default=False, optional=True), Enum.named('log_level').using( #pylint: disable-msg=E1101, E1120 default='info', optional=True).valued('debug', 'info', 'warning', 'error', 'critical'), ) def __init__(self): args = parse_args() print 'Arguments: %s' % args self.name = "microdrop.app" # get the version number self.version = "" try: raise Exception version = subprocess.Popen( ['git', 'describe'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE).communicate()[0].rstrip() m = re.match('v(\d+)\.(\d+)-(\d+)', version) self.version = "%s.%s.%s" % (m.group(1), m.group(2), m.group(3)) branch = subprocess.Popen( ['git', 'rev-parse', '--abbrev-ref', 'HEAD'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE).communicate()[0].rstrip() if branch.strip() != 'master': self.version += "-%s" % branch except: import pkg_resources version = pkg_resources.get_distribution('microdrop').version dev = ('dev' in version) self.version = re.sub('\.dev.*', '', re.sub('post', '', version)) if dev: self.version += "-dev" self.realtime_mode = False self.running = False self.builder = gtk.Builder() self.signals = {} self.plugin_data = {} # these members are initialized by plugins self.experiment_log_controller = None self.config_controller = None self.dmf_device_controller = None self.protocol_controller = None self.main_window_controller = None # Enable custom logging handler logging.getLogger().addHandler(CustomHandler()) self.log_file_handler = None # config model try: self.config = Config(args.config) except IOError: logging.error( 'Could not read configuration file, `%s`. Make sure' ' it exists and is readable.', args.config) raise SystemExit(-1) # set the log level if self.name in self.config.data and ('log_level' in self.config.data[self.name]): self._set_log_level(self.config.data[self.name]['log_level']) logger.info('MicroDrop version: %s', self.version) logger.info('Running in working directory: %s', os.getcwd()) # Run post install hooks for freshly installed plugins. # It is necessary to delay the execution of these hooks here due to # Windows file locking preventing the deletion of files that are in use. post_install_queue_path = \ path(self.config.data['plugins']['directory']) \ .joinpath('post_install_queue.yml') if post_install_queue_path.isfile(): post_install_queue = yaml.load(post_install_queue_path.bytes()) post_install_queue = map(path, post_install_queue) logger.info('[App] processing post install hooks.') for p in post_install_queue[:]: try: info = get_plugin_info(p) logger.info(" running post install hook for %s" % info.plugin_name) plugin_manager.post_install(p) except Exception: logging.info(''.join(traceback.format_exc())) logging.error('Error running post-install hook for %s.', p.name, exc_info=True) finally: post_install_queue.remove(p) post_install_queue_path.write_bytes(yaml.dump(post_install_queue)) # Delete paths that were marked during the uninstallation of a plugin. # It is necessary to delay the deletion until here due to Windows file # locking preventing the deletion of files that are in use. deletions_path = path(self.config.data['plugins']['directory'])\ .joinpath('requested_deletions.yml') if deletions_path.isfile(): requested_deletions = yaml.load(deletions_path.bytes()) requested_deletions = map(path, requested_deletions) logger.info('[App] processing requested deletions.') for p in requested_deletions[:]: try: if p != p.abspath(): logger.info( ' (warning) ignoring path %s since it ' 'is not absolute', p) continue if p.isdir(): info = get_plugin_info(p) if info: logger.info(' deleting %s' % p) cwd = os.getcwd() os.chdir(p.parent) try: path(p.name).rmtree() #ignore_errors=True) except Exception, why: logger.warning('Error deleting path %s (%s)', p, why) raise os.chdir(cwd) requested_deletions.remove(p) else: # if the directory doesn't exist, remove it from the # list requested_deletions.remove(p) except (AssertionError, ): logger.info(' NOT deleting %s' % (p)) continue except (Exception, ): logger.info(' NOT deleting %s' % (p)) continue