Пример #1
0
    def _iter_packets(self, packet_size):
        """Yield packets of <packets_size> bytes until the machine is stopped.

        N.B.: to workaround the fact that the Toshiba Bluetooth stack
        on Windows does not correctly handle the read timeout setting
        (returning immediately if some data is already available):
        - the effective timeout is re-configured to <timeout/packet_size>
        - multiple reads are  done (until a packet is complete)
        - an incomplete packet will only be discarded if one of
          those reads return no data (but not on short read)
        """
        self.serial_port.timeout = max(
            self.serial_params.get('timeout', 1.0) / packet_size,
            0.01,
        )
        packet = b''
        while not self.finished.isSet():
            raw = self.serial_port.read(packet_size - len(packet))
            if not raw:
                if packet:
                    log.error('discarding incomplete packet: %s',
                              binascii.hexlify(packet))
                packet = b''
                continue
            packet += raw
            if len(packet) != packet_size:
                continue
            yield packet
            packet = b''
Пример #2
0
 def load_plugins(self, plugins_dir=PLUGINS_DIR):
     log.info('loading plugins from %s', plugins_dir)
     working_set = pkg_resources.working_set
     environment = pkg_resources.Environment([plugins_dir])
     distributions, errors = working_set.find_plugins(environment)
     if errors:
         log.error("error(s) while loading plugins: %s", errors)
     list(map(working_set.add, distributions))
Пример #3
0
 def _trigger_hook(self, hook, *args, **kwargs):
     for callback in self._hooks[hook]:
         try:
             callback(*args, **kwargs)
         except Exception:
             log.error('hook %r callback %r failed',
                       hook, callback,
                       exc_info=True)
Пример #4
0
 def run(self):
     while True:
         func, args, kwargs = self._queue.get()
         try:
             with self._lock:
                 if func(*args, **kwargs):
                     break
         except Exception:
             log.error('engine %s failed', func.__name__[1:], exc_info=True)
Пример #5
0
 def load_config(self):
     try:
         with open(self._config.target_file, 'rb') as f:
             self._config.load(f)
     except Exception:
         log.error('loading configuration failed, reseting to default', exc_info=True)
         self._config.clear()
         return False
     return True
Пример #6
0
 def get_translation_frame_opacity(self):
     opacity = self._get_int(
         TRANSLATION_FRAME_SECTION, TRANSLATION_FRAME_OPACITY_OPTION, DEFAULT_TRANSLATION_FRAME_OPACITY
     )
     try:
         raise_if_invalid_opacity(opacity)
     except ValueError as e:
         log.error("translation %s, reset to %u", e, DEFAULT_TRANSLATION_FRAME_OPACITY)
         opacity = DEFAULT_TRANSLATION_FRAME_OPACITY
     return opacity
Пример #7
0
 def _update(self, event=None):
     # Refreshes the UI to reflect current data.
     machine_name = self.choice.GetStringSelection()
     try:
         options = self.config.get_machine_specific_options(machine_name)
     except Exception:
         log.error("could not get machine specific options", exc_info=True)
         options = {}
     self.advanced_options = options
     self.config_button.Enable(bool(options))
Пример #8
0
 def load_config(self):
     try:
         with open(self._config.target_file, 'rb') as f:
             self._config.load(f)
     except Exception:
         log.error('loading configuration failed, reseting to default',
                   exc_info=True)
         self._config.clear()
         return False
     return True
Пример #9
0
 def _save(self, event=None):
     file_name_suggestion = 'steno-notes-%s.txt' % time.strftime('%Y-%m-%d-%H-%M')
     file_dialog = wx.FileDialog(self, SAVE_NOTES_DIALOG_TITLE, "", file_name_suggestion,
                                 SAVE_NOTES_FILE_DIALOG_TEXT_FILE_FILTER, wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
     if file_dialog.ShowModal() == wx.ID_OK:
         try:
             with open(file_dialog.GetPath(), 'w') as fd:
                 fd.write(self.listbox.GetValue()+'\n')
         except IOError:
             log.error("Cannot save notes in file '%s'." % file_dialog.GetPath(), exc_info=True)
Пример #10
0
 def register_plugin_from_entrypoint(self, plugin_type, entrypoint):
     log.info('%s: %s (from %s)', plugin_type,
              entrypoint.name, entrypoint.module_name)
     try:
         obj = entrypoint.resolve()
     except:
         log.error('error loading %s plugin: %s (from %s)', plugin_type,
                   entrypoint.name, entrypoint.module_name, exc_info=True)
     else:
         self.register_plugin(plugin_type, entrypoint.name, obj)
Пример #11
0
 def _start_extensions(self, extension_list):
     for extension_name in extension_list:
         log.info('starting `%s` extension', extension_name)
         try:
             extension = registry.get_plugin('extension', extension_name).obj(self)
             extension.start()
         except Exception:
             log.error('initializing extension `%s` failed', extension_name, exc_info=True)
         else:
             self._running_extensions[extension_name] = extension
Пример #12
0
 def _update(self, event=None):
     # Refreshes the UI to reflect current data.
     machine_name = self.choice.GetStringSelection()
     try:
         options = self.config.get_machine_specific_options(machine_name)
     except Exception:
         log.error('could not get machine specific options', exc_info=True)
         options = {}
     self.advanced_options = options
     self.config_button.Enable(bool(options))
Пример #13
0
 def _save(self, event=None):
     file_name_suggestion = 'steno-notes-%s.txt' % time.strftime('%Y-%m-%d-%H-%M')
     file_dialog = wx.FileDialog(self, SAVE_NOTES_DIALOG_TITLE, "", file_name_suggestion,
                                 SAVE_NOTES_FILE_DIALOG_TEXT_FILE_FILTER, wx.FD_SAVE | wx.FD_OVERWRITE_PROMPT)
     if file_dialog.ShowModal() == wx.ID_OK:
         try:
             with open(file_dialog.GetPath(), 'w') as fd:
                 fd.write(self.listbox.GetValue()+'\n')
         except IOError:
             log.error("Cannot save notes in file '%s'." % file_dialog.GetPath(), exc_info=True)
Пример #14
0
 def get_undo_levels(self):
     undo_levels = self._get_int(OUTPUT_CONFIG_SECTION,
                                 OUTPUT_CONFIG_UNDO_LEVELS,
                                 DEFAULT_OUTPUT_CONFIG_UNDO_LEVELS)
     if undo_levels < MINIMUM_OUTPUT_CONFIG_UNDO_LEVELS:
         log.error("Undo limit %d is below minimum %d, using %d",
                   undo_levels, MINIMUM_OUTPUT_CONFIG_UNDO_LEVELS,
                   DEFAULT_OUTPUT_CONFIG_UNDO_LEVELS)
         undo_levels = DEFAULT_OUTPUT_CONFIG_UNDO_LEVELS
     return undo_levels
Пример #15
0
 def _start_extensions(self, extension_list):
     for extension_name in extension_list:
         log.info('starting `%s` extension', extension_name)
         try:
             extension = registry.get_plugin('extension', extension_name).obj(self)
             extension.start()
         except Exception:
             log.error('initializing extension `%s` failed', extension_name, exc_info=True)
         else:
             self._running_extensions[extension_name] = extension
Пример #16
0
 def get_undo_levels(self):
     undo_levels = self._get_int(OUTPUT_CONFIG_SECTION, OUTPUT_CONFIG_UNDO_LEVELS, DEFAULT_OUTPUT_CONFIG_UNDO_LEVELS)
     if undo_levels < MINIMUM_OUTPUT_CONFIG_UNDO_LEVELS:
         log.error(
             "Undo limit %d is below minimum %d, using %d",
             undo_levels,
             MINIMUM_OUTPUT_CONFIG_UNDO_LEVELS,
             DEFAULT_OUTPUT_CONFIG_UNDO_LEVELS,
         )
         undo_levels = DEFAULT_OUTPUT_CONFIG_UNDO_LEVELS
     return undo_levels
Пример #17
0
 def get_translation_frame_opacity(self):
     opacity = self._get_int(TRANSLATION_FRAME_SECTION,
                             TRANSLATION_FRAME_OPACITY_OPTION,
                             DEFAULT_TRANSLATION_FRAME_OPACITY)
     try:
         raise_if_invalid_opacity(opacity)
     except ValueError as e:
         log.error("translation %s, reset to %u", e,
                   DEFAULT_TRANSLATION_FRAME_OPACITY)
         opacity = DEFAULT_TRANSLATION_FRAME_OPACITY
     return opacity
 def _request_finished(self, future):
     if not future.done():
         return
     try:
         resp = future.result()
     except Exception as exc:
         log.error("error fetching %s", exc.request.url, exc_info=True)
         return
     if not resp.ok:
         log.info("error fetching %s: %s", resp.url, resp.reason)
         return
     self._resource_downloaded.emit(resp.request.url, resp.content)
Пример #19
0
 def run(self):
     """Overrides base class run method. Do not call directly."""
     self._ready()
     for packet in self._iter_packets(BYTES_PER_STROKE):
         if (packet[0] & 0x80) or packet[3] != 0xff:
             log.error('discarding invalid packet: %s',
                       binascii.hexlify(packet))
             continue
         steno_keys = self.keymap.keys_to_actions(
             self.process_steno_packet(packet))
         if steno_keys:
             self._notify(steno_keys)
Пример #20
0
 def register_plugin_from_entrypoint(self, plugin_type, entrypoint):
     log.info('%s: %s (from %s)', plugin_type, entrypoint.name,
              entrypoint.module_name)
     try:
         obj = entrypoint.resolve()
     except:
         log.error('error loading %s plugin: %s (from %s)',
                   plugin_type,
                   entrypoint.name,
                   entrypoint.module_name,
                   exc_info=True)
     else:
         self.register_plugin(plugin_type, entrypoint.name, obj)
Пример #21
0
 def run(self):
     """Overrides base class run method. Do not call directly."""
     self._ready()
     for packet in self._iter_packets(BYTES_PER_STROKE):
         if (packet[0] & 0x80) or packet[3] != 0xff:
             log.error('discarding invalid packet: %s',
                       binascii.hexlify(packet))
             continue
         steno_keys = self.keymap.keys_to_actions(
             self.process_steno_packet(packet)
         )
         if steno_keys:
             self._notify(steno_keys)
Пример #22
0
 def __getitem__(self, key):
     key, opt = self._lookup(key)
     if key in self._cache:
         return self._cache[key]
     try:
         value = opt.validate(self, key, opt.getter(self, key))
     except (configparser.NoOptionError, KeyError):
         value = opt.default(self, key)
     except InvalidConfigOption as e:
         log.error('invalid value for %r option', opt.name, exc_info=True)
         value = e.fixed_value
     self._cache[key] = value
     return value
Пример #23
0
 def __getitem__(self, key):
     key, opt = self._lookup(key)
     if key in self._cache:
         return self._cache[key]
     try:
         value = opt.validate(self, key, opt.getter(self, key))
     except (configparser.NoOptionError, KeyError):
         value = opt.default(self, key)
     except InvalidConfigOption as e:
         log.error('invalid value for %r option', opt.name, exc_info=True)
         value = e.fixed_value
     self._cache[key] = value
     return value
Пример #24
0
 def get_enabled_extensions(self):
     extensions = ()
     value = self._get(PLUGINS_CONFIG_SECTION,
                       ENABLED_EXTENSIONS_OPTION,
                       None)
     if value is not None:
         try:
             extensions = set(json.loads(value))
         except ValueError:
             log.error("invalid enabled extensions set, resetting to default",
                       exc_info=True)
             self.set_enabled_extensions(None)
     return set(extensions)
Пример #25
0
 def get_machine_type(self):
     machine_type = self._get(MACHINE_CONFIG_SECTION, MACHINE_TYPE_OPTION,
                              None)
     if machine_type is not None:
         try:
             machine_registry.get(machine_type)
         except NoSuchMachineException:
             log.error("invalid machine type: %s", machine_type)
             self.set_machine_type(DEFAULT_MACHINE_TYPE)
             machine_type = None
     if machine_type is None:
         machine_type = DEFAULT_MACHINE_TYPE
     return machine_type
Пример #26
0
 def get_enabled_extensions(self):
     extensions = ()
     value = self._get(PLUGINS_CONFIG_SECTION, ENABLED_EXTENSIONS_OPTION,
                       None)
     if value is not None:
         try:
             extensions = set(json.loads(value))
         except ValueError:
             log.error(
                 "invalid enabled extensions set, resetting to default",
                 exc_info=True)
             self.set_enabled_extensions(None)
     return set(extensions)
Пример #27
0
 def get_system_name(self):
     system_name = self._get(BASE_SYSTEM_SECTION, SYSTEM_NAME_OPTION, None)
     if system_name is not None:
         try:
             system_name = registry.get_plugin('system', system_name).name
         except:
             log.error("invalid system name: %s",
                       system_name,
                       exc_info=True)
             self.set_system_name(DEFAULT_SYSTEM_NAME)
             system_name = None
     if system_name is None:
         system_name = DEFAULT_SYSTEM_NAME
     return system_name
Пример #28
0
 def get_machine_type(self):
     machine_type = self._get(MACHINE_CONFIG_SECTION,
                              MACHINE_TYPE_OPTION,
                              None)
     if machine_type is not None:
         try:
             machine_registry.get(machine_type)
         except NoSuchMachineException:
             log.error("invalid machine type: %s", machine_type)
             self.set_machine_type(DEFAULT_MACHINE_TYPE)
             machine_type = None
     if machine_type is None:
         machine_type = DEFAULT_MACHINE_TYPE
     return machine_type
Пример #29
0
 def get_machine_type(self):
     machine_type = self._get(MACHINE_CONFIG_SECTION,
                              MACHINE_TYPE_OPTION,
                              None)
     if machine_type is not None:
         try:
             machine_type = registry.get_plugin('machine', machine_type).name
         except:
             log.error("invalid machine type: %s",
                       machine_type, exc_info=True)
             self.set_machine_type(DEFAULT_MACHINE_TYPE)
             machine_type = None
     if machine_type is None:
         machine_type = DEFAULT_MACHINE_TYPE
     return machine_type
Пример #30
0
 def get_system_name(self):
     system_name = self._get(BASE_SYSTEM_SECTION,
                             SYSTEM_NAME_OPTION,
                             None)
     if system_name is not None:
         try:
             system_name = registry.get_plugin('system', system_name).name
         except:
             log.error("invalid system name: %s",
                       system_name, exc_info=True)
             self.set_system_name(DEFAULT_SYSTEM_NAME)
             system_name = None
     if system_name is None:
         system_name = DEFAULT_SYSTEM_NAME
     return system_name
Пример #31
0
 def get_machine_type(self):
     machine_type = self._get(MACHINE_CONFIG_SECTION, MACHINE_TYPE_OPTION,
                              None)
     if machine_type is not None:
         try:
             machine_type = registry.get_plugin('machine',
                                                machine_type).name
         except:
             log.error("invalid machine type: %s",
                       machine_type,
                       exc_info=True)
             self.set_machine_type(DEFAULT_MACHINE_TYPE)
             machine_type = None
     if machine_type is None:
         machine_type = DEFAULT_MACHINE_TYPE
     return machine_type
 def update(self):
     try:
         available_plugins = global_registry.list_plugins()
     except:
         log.error("failed to fetch list of available plugins from PyPI",
                   exc_info=True)
         return
     for name, metadata in available_plugins.items():
         pkg = self._packages.get(name)
         if pkg is None:
             pkg = PackageState(name, available=metadata)
             self._packages[name] = pkg
         else:
             pkg.available = metadata
             if pkg.current and pkg.current.parsed_version < pkg.latest.parsed_version:
                 pkg.status = 'outdated'
Пример #33
0
 def run(self):
     """Overrides base class run method. Do not call directly."""
     self._ready()
     for packet in self._iter_packets(BYTES_PER_STROKE):
         if not (packet[0] & 0x80) or sum(b & 0x80 for b in packet[1:]):
             log.error('discarding invalid packet: %s',
                       binascii.hexlify(packet))
             continue
         steno_keys = []
         for i, b in enumerate(packet):
             for j in range(1, 8):
                 if (b & (0x80 >> j)):
                     steno_keys.append(STENO_KEY_CHART[i * 7 + j - 1])
         steno_keys = self.keymap.keys_to_actions(steno_keys)
         if steno_keys:
             self._notify(steno_keys)
Пример #34
0
 def run(self):
     """Overrides base class run method. Do not call directly."""
     self._ready()
     for packet in self._iter_packets(BYTES_PER_STROKE):
         if not (packet[0] & 0x80) or sum(b & 0x80 for b in packet[1:]):
             log.error('discarding invalid packet: %s',
                       binascii.hexlify(packet))
             continue
         steno_keys = []
         for i, b in enumerate(packet):
             for j in range(1, 8):
                 if (b & (0x80 >> j)):
                     steno_keys.append(STENO_KEY_CHART[i * 7 + j - 1])
         steno_keys = self.keymap.keys_to_actions(steno_keys)
         if steno_keys:
             self._notify(steno_keys)
Пример #35
0
 def register_plugin_from_entrypoint(self, plugin_type, entrypoint):
     log.info('%s: %s (from %s)', plugin_type,
              entrypoint.name, entrypoint.dist)
     try:
         obj = entrypoint.resolve()
     except:
         log.error('error loading %s plugin: %s (from %s)', plugin_type,
                   entrypoint.name, entrypoint.module_name, exc_info=True)
     else:
         plugin = self.register_plugin(plugin_type, entrypoint.name, obj)
         # Keep track of distributions providing plugins.
         dist_id = str(entrypoint.dist)
         dist = self._distributions.get(dist_id)
         if dist is None:
             dist = PluginDistribution(entrypoint.dist, set())
             self._distributions[dist_id] = dist
         dist.plugins.add(plugin)
Пример #36
0
 def register_plugin_from_entrypoint(self, plugin_type, entrypoint):
     log.info('%s: %s (from %s)', plugin_type,
              entrypoint.name, entrypoint.dist)
     try:
         obj = entrypoint.resolve()
     except:
         log.error('error loading %s plugin: %s (from %s)', plugin_type,
                   entrypoint.name, entrypoint.module_name, exc_info=True)
         if not self._suppress_errors:
             reraise(*sys.exc_info())
     else:
         plugin = self.register_plugin(plugin_type, entrypoint.name, obj)
         # Keep track of distributions providing plugins.
         dist_id = str(entrypoint.dist)
         dist = self._distributions.get(dist_id)
         if dist is None:
             dist = PluginDistribution(entrypoint.dist, set())
             self._distributions[dist_id] = dist
         dist.plugins.add(plugin)
Пример #37
0
def main():
    description = 'Send a command to a running Plover instance.'
    parser = argparse.ArgumentParser(description=description)
    parser.add_argument('-l',
                        '--log-level',
                        choices=['debug', 'info', 'warning', 'error'],
                        default=None,
                        help='set log level')
    parser.add_argument('command',
                        metavar='COMMAND{:ARGS}',
                        type=str,
                        help='the command to send')
    args = parser.parse_args(args=sys.argv[1:])
    if args.log_level is not None:
        log.set_level(args.log_level.upper())
    log.setup_platform_handler()
    with Controller() as controller:
        if controller.is_owner:
            log.error('sending command failed: no running instance found')
            sys.exit(1)
        controller.send_command(args.command)
Пример #38
0
    def _save(self, event):

        self.machine_config.save()
        self.dictionary_config.save()
        self.logging_config.save()
        self.display_config.save()
        self.output_config.save()

        try:
            update_engine(self.engine, self.config)
        except Exception:
            log.error('updating engine configuration failed', exc_info=True)
            return

        with open(self.config.target_file, 'wb') as f:
            self.config.save(f)

        if self.IsModal():
            self.EndModal(wx.ID_SAVE)
        else:
            self.Close()
Пример #39
0
    def _save(self, event):

        self.machine_config.save()
        self.dictionary_config.save()
        self.logging_config.save()
        self.display_config.save()
        self.output_config.save()

        try:
            update_engine(self.engine, self.config)
        except Exception:
            log.error("updating engine configuration failed", exc_info=True)
            return

        with open(self.config.target_file, "wb") as f:
            self.config.save(f)

        if self.IsModal():
            self.EndModal(wx.ID_SAVE)
        else:
            self.Close()
Пример #40
0
 def get_dictionaries(self):
     system_name = self.get_system_name()
     try:
         system = registry.get_plugin('system', system_name).obj
     except:
         log.error("invalid system name: %s", system_name, exc_info=True)
         return []
     section = SYSTEM_CONFIG_SECTION % system_name
     option = SYSTEM_DICTIONARIES_OPTION
     dictionaries = self._get(section, option, None)
     if dictionaries is None:
         dictionaries = self._legacy_get_dictionary_file_names()
         if dictionaries is None:
             dictionaries = [DictionaryConfig(path)
                             for path in system.DEFAULT_DICTIONARIES]
         return dictionaries
     try:
         return [DictionaryConfig.from_dict(d)
                 for d in json.loads(dictionaries)]
     except:
         log.error("invalid system dictionaries, resetting to default", exc_info=True)
         self.set_dictionaries(None)
         return self.get_dictionaries()
Пример #41
0
 def get_dictionary_file_names(self):
     system_name = self.get_system_name()
     try:
         system = registry.get_plugin('system', system_name).resolve()
     except:
         log.error("invalid system name: %s", system_name, exc_info=True)
         return []
     section = SYSTEM_CONFIG_SECTION % system_name
     option = SYSTEM_DICTIONARIES_OPTION
     filenames = self._get(section, option, None)
     if filenames is None:
         filenames = self._legacy_get_dictionary_file_names()
         if filenames is None:
             filenames = system.DEFAULT_DICTIONARIES
     else:
         try:
             filenames = tuple(json.loads(filenames))
         except ValueError as e:
             log.error("invalid system dictionaries, resetting to default",
                       exc_info=True)
             self.set_dictionary_file_names(None)
             filenames = system.DEFAULT_DICTIONARIES
     return [expand_path(path) for path in filenames]
Пример #42
0
 def get_dictionary_file_names(self):
     system_name = self.get_system_name()
     try:
         system = registry.get_plugin('system', system_name).resolve()
     except:
         log.error("invalid system name: %s", system_name, exc_info=True)
         return []
     section = SYSTEM_CONFIG_SECTION % system_name
     option = SYSTEM_DICTIONARIES_OPTION
     filenames = self._get(section, option, None)
     if filenames is None:
         filenames = self._legacy_get_dictionary_file_names()
         if filenames is None:
             filenames = system.DEFAULT_DICTIONARIES
     else:
         try:
             filenames = tuple(json.loads(filenames))
         except ValueError as e:
             log.error("invalid system dictionaries, resetting to default",
                       exc_info=True)
             self.set_dictionary_file_names(None)
             filenames = system.DEFAULT_DICTIONARIES
     return [expand_path(path) for path in filenames]
Пример #43
0
 def _create_new_dictionary(self):
     new_filename = QFileDialog.getSaveFileName(
         self, _('New dictionary'), None,
         _dictionary_filters(include_readonly=False),
     )[0]
     if not new_filename:
         return
     new_filename = normalize_path(new_filename)
     try:
         d = create_dictionary(new_filename, threaded_save=False)
         d.save()
     except:
         log.error('creating dictionary %s failed', new_filename, exc_info=True)
         return
     dictionaries = self._config_dictionaries[:]
     for d in dictionaries:
         if d.path == new_filename:
             break
     else:
         dictionaries.insert(0, DictionaryConfig(new_filename))
     # Note: pass in `loaded_dictionaries` to force update (use case:
     # the user decided to overwrite an already loaded dictionary).
     self._update_dictionaries(dictionaries, keep_selection=False,
                               loaded_dictionaries=self._loaded_dictionaries)
Пример #44
0
 def get_system_keymap(self, machine_type=None):
     if machine_type is None:
         machine_type = self.get_machine_type()
     try:
         machine_class = machine_registry.get(machine_type)
     except:
         log.error("invalid machine type: %s", machine_type, exc_info=True)
         return None
     section = SYSTEM_CONFIG_SECTION % DEFAULT_SYSTEM
     option = SYSTEM_KEYMAP_OPTION % machine_type
     mappings = self._get(section, option, None)
     if mappings is None:
         mappings = system.KEYMAPS.get(machine_type)
     else:
         try:
             mappings = dict(json.loads(mappings))
         except ValueError as e:
             log.error("invalid machine keymap, resetting to default",
                       exc_info=True)
             mappings = system.KEYMAPS.get(machine_type)
             self.set_system_keymap(mappings, machine_type)
     keymap = Keymap(machine_class.get_keys(), system.KEYS + machine_class.get_actions())
     keymap.set_mappings(mappings)
     return keymap
Пример #45
0
 def get_system_keymap(self, machine_type=None, system_name=None):
     if machine_type is None:
         machine_type = self.get_machine_type()
     try:
         machine_class = registry.get_plugin('machine',
                                             machine_type).resolve()
     except:
         log.error("invalid machine type: %s", machine_type, exc_info=True)
         return None
     if system_name is None:
         system_name = self.get_system_name()
     try:
         system = registry.get_plugin('system', system_name).resolve()
     except:
         log.error("invalid system name: %s", system_name, exc_info=True)
         return None
     section = SYSTEM_CONFIG_SECTION % system_name
     option = SYSTEM_KEYMAP_OPTION % machine_type
     mappings = self._get(section, option, None)
     if mappings is None:
         # No user mappings, use system default.
         mappings = system.KEYMAPS.get(machine_type)
     else:
         try:
             mappings = dict(json.loads(mappings))
         except ValueError as e:
             log.error("invalid machine keymap, resetting to default",
                       exc_info=True)
             self.set_system_keymap(None, machine_type)
             mappings = system.KEYMAPS.get(machine_type)
     if mappings is None:
         if machine_class.KEYMAP_MACHINE_TYPE is not None:
             # Try fallback.
             return self.get_system_keymap(
                 machine_type=machine_class.KEYMAP_MACHINE_TYPE,
                 system_name=system_name)
         # No fallback...
         mappings = {}
     keymap = Keymap(machine_class.get_keys(),
                     system.KEYS + machine_class.get_actions())
     keymap.set_mappings(mappings)
     return keymap
Пример #46
0
 def get_system_keymap(self, machine_type=None, system_name=None):
     if machine_type is None:
         machine_type = self.get_machine_type()
     try:
         machine_class = registry.get_plugin('machine', machine_type).obj
     except:
         log.error("invalid machine type: %s", machine_type, exc_info=True)
         return None
     if system_name is None:
         system_name = self.get_system_name()
     try:
         system = registry.get_plugin('system', system_name).obj
     except:
         log.error("invalid system name: %s", system_name, exc_info=True)
         return None
     section = SYSTEM_CONFIG_SECTION % system_name
     option = SYSTEM_KEYMAP_OPTION % machine_type
     mappings = self._get(section, option, None)
     if mappings is None:
         # No user mappings, use system default.
         mappings = system.KEYMAPS.get(machine_type)
     else:
         try:
             mappings = dict(json.loads(mappings))
         except ValueError:
             log.error("invalid machine keymap, resetting to default",
                       exc_info=True)
             self.set_system_keymap(None, machine_type)
             mappings = system.KEYMAPS.get(machine_type)
     if mappings is None:
         if machine_class.KEYMAP_MACHINE_TYPE is not None:
             # Try fallback.
             return self.get_system_keymap(
                 machine_type=machine_class.KEYMAP_MACHINE_TYPE,
                 system_name=system_name
             )
         # No fallback...
         mappings = {}
     keymap = Keymap(machine_class.get_keys(), system.KEYS + machine_class.get_actions())
     keymap.set_mappings(mappings)
     return keymap
Пример #47
0
 def _xcall(self, fn, *args, **kwargs):
     try:
         fn(*args, **kwargs)
     except Exception:
         log.error('output failed', exc_info=True)
Пример #48
0
def main():
    """Launch plover."""
    description = "Run the plover stenotype engine. This is a graphical application."
    parser = argparse.ArgumentParser(description=description)
    parser.add_argument('--version',
                        action='version',
                        version='%s %s' %
                        (__software_name__.capitalize(), __version__))
    parser.add_argument(
        '-s',
        '--script',
        default=None,
        nargs=argparse.REMAINDER,
        help='use another plugin console script as main entrypoint, '
        'passing in the rest of the command line arguments, '
        'print list of available scripts when no argument is given')
    parser.add_argument('-l',
                        '--log-level',
                        choices=['debug', 'info', 'warning', 'error'],
                        default=None,
                        help='set log level')
    parser.add_argument('-g', '--gui', default=None, help='set gui')
    args = parser.parse_args(args=sys.argv[1:])
    if args.log_level is not None:
        log.set_level(args.log_level.upper())
    log.setup_platform_handler()

    log.info('Plover %s', __version__)
    log.info('configuration directory: %s', CONFIG_DIR)

    if PLATFORM == 'mac':
        # Fixes PyQt issue on macOS Big Sur.
        os.environ['QT_MAC_WANTS_LAYER'] = '1'

    registry.update()

    if args.gui is None:
        gui_priority = {
            'qt': 1,
            'none': -1,
        }
        gui_list = sorted(registry.list_plugins('gui'),
                          reverse=True,
                          key=lambda gui: gui_priority.get(gui.name, 0))
        gui = gui_list[0].obj
    else:
        gui = registry.get_plugin('gui', args.gui).obj

    try:
        if args.script is not None:
            if args.script:
                # Create a mapping of available console script,
                # with the following priorities (highest first):
                # - {project_name}-{version}:{script_name}
                # - {project_name}:{script_name}
                # - {script_name}
                console_scripts = {}
                for e in sorted(
                        pkg_resources.iter_entry_points('console_scripts'),
                        key=lambda e: (e.dist, e.name)):
                    for key in (
                            '%s-%s:%s' %
                        (e.dist.project_name, e.dist.version, e.name),
                            '%s:%s' % (e.dist.project_name, e.name),
                            e.name,
                    ):
                        console_scripts[key] = e
                entrypoint = console_scripts.get(args.script[0])
                if entrypoint is None:
                    log.error('no such script: %s', args.script[0])
                    code = 1
                else:
                    sys.argv = args.script
                    try:
                        code = entrypoint.load()()
                    except SystemExit as e:
                        code = e.code
                if code is None:
                    code = 0
            else:
                print('available script(s):')
                dist = None
                for e in sorted(
                        pkg_resources.iter_entry_points('console_scripts'),
                        key=lambda e: (str(e.dist), e.name)):
                    if dist != e.dist:
                        dist = e.dist
                        print('%s:' % dist)
                    print('- %s' % e.name)
                code = 0
            os._exit(code)

        # Ensure only one instance of Plover is running at a time.
        with Controller() as controller:
            if controller.is_owner:
                # Not other instance, regular startup.
                if PLATFORM == 'mac':
                    import appnope
                    appnope.nope()
                init_config_dir()
                # This must be done after calling init_config_dir, so
                # Plover's configuration directory actually exists.
                log.setup_logfile()
                config = Config(CONFIG_FILE)
                code = gui.main(config, controller)
            else:
                log.info(
                    'another instance is running, sending `focus` command')
                # Other instance? Try focusing the main window.
                try:
                    controller.send_command('focus')
                except ConnectionRefusedError:
                    log.error('connection to existing instance failed, '
                              'force cleaning before restart')
                    # Assume the previous instance died, leaving
                    # a stray socket, try cleaning it...
                    if not controller.force_cleanup():
                        raise
                    # ...and restart.
                    code = -1
                else:
                    code = 0
    except:
        gui.show_error('Unexpected error', traceback.format_exc())
        code = 2
    if code == -1:
        # Restart.
        args = sys.argv[:]
        if args[0].endswith('.py') or args[0].endswith('.pyc'):
            # We're running from source.
            spec = sys.modules['__main__'].__spec__
            assert sys.argv[0] == spec.origin
            args[0:1] = [sys.executable, '-m', spec.name]
        # Execute atexit handlers.
        atexit._run_exitfuncs()
        if PLATFORM == 'win':
            # Workaround https://bugs.python.org/issue19066
            subprocess.Popen(args, cwd=os.getcwd())
            code = 0
        else:
            os.execv(args[0], args)
    os._exit(code)
Пример #49
0
 def _xcall(self, fn, *args, **kwargs):
     try:
         fn(*args, **kwargs)
     except Exception:
         log.error('output failed', exc_info=True)
Пример #50
0
def main():
    """Launch plover."""
    description = "Run the plover stenotype engine. This is a graphical application."
    parser = argparse.ArgumentParser(description=description)
    parser.add_argument('--version',
                        action='version',
                        version='%s %s' %
                        (__software_name__.capitalize(), __version__))
    parser.add_argument(
        '-s',
        '--script',
        default=None,
        nargs=argparse.REMAINDER,
        help='use another plugin console script as main entrypoint, '
        'passing in the rest of the command line arguments, '
        'print list of available scripts when no argument is given')
    parser.add_argument('-l',
                        '--log-level',
                        choices=['debug', 'info', 'warning', 'error'],
                        default=None,
                        help='set log level')
    parser.add_argument('-g', '--gui', default=None, help='set gui')
    args = parser.parse_args(args=sys.argv[1:])
    if args.log_level is not None:
        log.set_level(args.log_level.upper())
    log.setup_platform_handler()

    log.info('Plover %s', __version__)
    log.info('configuration directory: %s', CONFIG_DIR)
    log.info('plugins directory: %s', PLUGINS_DIR)

    registry.update()

    if args.gui is None:
        gui_priority = {
            'qt': 1,
            'none': -1,
        }
        gui_list = sorted(registry.list_plugins('gui'),
                          reverse=True,
                          key=lambda gui: gui_priority.get(gui.name, 0))
        gui = gui_list[0].obj
    else:
        gui = registry.get_plugin('gui', args.gui).obj

    try:
        if args.script is not None:
            if args.script:
                # Create a mapping of available console script,
                # with the following priorities (highest first):
                # - {project_name}-{version}:{script_name}
                # - {project_name}:{script_name}
                # - {script_name}
                console_scripts = {}
                for e in sorted(
                        pkg_resources.iter_entry_points('console_scripts'),
                        key=lambda e: (e.dist, e.name)):
                    for key in (
                            '%s-%s:%s' %
                        (e.dist.project_name, e.dist.version, e.name),
                            '%s:%s' % (e.dist.project_name, e.name),
                            e.name,
                    ):
                        console_scripts[key] = e
                entrypoint = console_scripts.get(args.script[0])
                if entrypoint is None:
                    log.error('no such script: %s', args.script[0])
                    code = 1
                else:
                    sys.argv = args.script
                    try:
                        code = entrypoint.load()()
                    except SystemExit as e:
                        code = e.code
                if code is None:
                    code = 0
            else:
                print('available script(s):')
                dist = None
                for e in sorted(
                        pkg_resources.iter_entry_points('console_scripts'),
                        key=lambda e: (str(e.dist), e.name)):
                    if dist != e.dist:
                        dist = e.dist
                        print('%s:' % dist)
                    print('- %s' % e.name)
                code = 0
            os._exit(code)

        # Ensure only one instance of Plover is running at a time.
        with plover.oslayer.processlock.PloverLock():
            if sys.platform.startswith('darwin'):
                appnope.nope()
            init_config_dir()
            # This must be done after calling init_config_dir, so
            # Plover's configuration directory actually exists.
            log.setup_logfile()
            config = Config()
            config.target_file = CONFIG_FILE
            code = gui.main(config)
            with open(config.target_file, 'wb') as f:
                config.save(f)
    except plover.oslayer.processlock.LockNotAcquiredException:
        gui.show_error('Error',
                       'Another instance of Plover is already running.')
        code = 1
    except:
        gui.show_error('Unexpected error', traceback.format_exc())
        code = 2
    if code == -1:
        # Restart.
        args = sys.argv[:]
        if args[0].endswith('.py') or args[0].endswith('.pyc'):
            # We're running from source.
            assert args[0] == __file__
            args[0:1] = [sys.executable, '-m', __spec__.name]
        # Execute atexit handlers.
        atexit._run_exitfuncs()
        if sys.platform.startswith('win32'):
            # Workaround https://bugs.python.org/issue19066
            subprocess.Popen(args, cwd=os.getcwd())
            code = 0
        else:
            os.execv(args[0], args)
    os._exit(code)
Пример #51
0
    def __init__(self, config):
        self.config = config

        # Note: don't set position from config, since it's not yet loaded.
        wx.Frame.__init__(
            self,
            None,
            title=self.TITLE,
            style=wx.DEFAULT_FRAME_STYLE
            & ~(wx.RESIZE_BORDER | wx.RESIZE_BOX | wx.MAXIMIZE_BOX))
        root = wx.Panel(self, style=wx.DEFAULT_FRAME_STYLE)

        # Menu Bar
        MenuBar = wx.MenuBar()
        self.SetMenuBar(MenuBar)

        # Application icon
        icon = wx.Icon(self.PLOVER_ICON_FILE, wx.BITMAP_TYPE_ICO)
        self.SetIcon(icon)

        # Configure button.
        self.configure_button = wx.Button(root,
                                          label=self.CONFIGURE_BUTTON_LABEL)
        self.configure_button.Bind(wx.EVT_BUTTON, self._show_config_dialog)

        # About button.
        self.about_button = wx.Button(root, label=self.ABOUT_BUTTON_LABEL)
        self.about_button.Bind(wx.EVT_BUTTON, self._show_about_dialog)

        # Status radio buttons.
        self.radio_output_enable = wx.RadioButton(
            root, label=self.ENABLE_OUTPUT_LABEL)
        self.radio_output_enable.Bind(
            wx.EVT_RADIOBUTTON,
            lambda e: self.steno_engine.set_is_running(True))
        self.radio_output_disable = wx.RadioButton(
            root, label=self.DISABLE_OUTPUT_LABEL)
        self.radio_output_disable.Bind(
            wx.EVT_RADIOBUTTON,
            lambda e: self.steno_engine.set_is_running(False))

        # Machine status.
        # TODO: Figure out why spinner has darker gray background.
        self.spinner = wx.animate.GIFAnimationCtrl(root, -1, SPINNER_FILE)
        self.spinner.GetPlayer().UseBackgroundColour(True)
        # Need to call this so the size of the control is not
        # messed up (100x100 instead of 16x16) on Linux...
        self.spinner.InvalidateBestSize()
        self.spinner.Hide()

        self.connected_bitmap = wx.Bitmap(self.CONNECTED_IMAGE_FILE,
                                          wx.BITMAP_TYPE_PNG)
        self.disconnected_bitmap = wx.Bitmap(self.DISCONNECTED_IMAGE_FILE,
                                             wx.BITMAP_TYPE_PNG)
        self.connection_ctrl = wx.StaticBitmap(root,
                                               bitmap=self.disconnected_bitmap)

        border_flag = wx.SizerFlags(1).Border(wx.ALL, self.BORDER)

        # Create Settings Box
        settings_sizer = wx.BoxSizer(wx.HORIZONTAL)

        settings_sizer.AddF(self.configure_button, border_flag.Expand())
        settings_sizer.AddF(self.about_button, border_flag.Expand())

        # Create Output Status Box
        box = wx.StaticBox(root, label=self.HEADER_OUTPUT)
        status_sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)

        status_sizer.AddF(self.radio_output_enable, border_flag)
        status_sizer.AddF(self.radio_output_disable, border_flag)

        # Create Machine Status Box
        machine_sizer = wx.BoxSizer(wx.HORIZONTAL)
        center_flag =\
            wx.SizerFlags()\
              .Align(wx.ALIGN_CENTER_VERTICAL)\
              .Border(wx.LEFT | wx.RIGHT, self.BORDER)

        machine_sizer.AddF(self.spinner, center_flag)
        machine_sizer.AddF(self.connection_ctrl, center_flag)
        longest_machine = max(machine_registry.get_all_names(), key=len)
        longest_state = max((STATE_ERROR, STATE_INITIALIZING, STATE_RUNNING),
                            key=len)
        longest_machine_status = '%s: %s' % (longest_machine, longest_state)
        self.machine_status_text = wx.StaticText(root,
                                                 label=longest_machine_status)
        machine_sizer.AddF(self.machine_status_text, center_flag)
        refresh_bitmap = wx.Bitmap(self.REFRESH_IMAGE_FILE, wx.BITMAP_TYPE_PNG)
        self.reconnect_button = wx.BitmapButton(root, bitmap=refresh_bitmap)
        machine_sizer.AddF(self.reconnect_button, center_flag)

        # Assemble main UI
        global_sizer = wx.GridBagSizer(vgap=self.BORDER, hgap=self.BORDER)
        global_sizer.Add(settings_sizer,
                         flag=wx.EXPAND,
                         pos=(0, 0),
                         span=(1, 2))
        global_sizer.Add(status_sizer,
                         flag=wx.EXPAND | wx.LEFT | wx.RIGHT,
                         border=self.BORDER,
                         pos=(1, 0),
                         span=(1, 2))
        global_sizer.Add(machine_sizer,
                         flag=wx.CENTER | wx.ALIGN_CENTER | wx.EXPAND
                         | wx.LEFT,
                         pos=(2, 0),
                         border=self.BORDER,
                         span=(1, 2))
        self.machine_sizer = machine_sizer
        # Add a border around the entire sizer.
        border = wx.BoxSizer()
        border.AddF(global_sizer,
                    wx.SizerFlags(1).Border(wx.ALL, self.BORDER).Expand())
        root.SetSizerAndFit(border)
        border.SetSizeHints(self)

        self.Bind(wx.EVT_CLOSE, self._quit)
        self.Bind(wx.EVT_MOVE, self.on_move)
        self.reconnect_button.Bind(wx.EVT_BUTTON, lambda e: self._reconnect())

        try:
            with open(config.target_file, 'rb') as f:
                self.config.load(f)
        except Exception:
            log.error('loading configuration failed, reseting to default',
                      exc_info=True)
            self.config.clear()

        rect = wx.Rect(config.get_main_frame_x(), config.get_main_frame_y(),
                       *self.GetSize())
        self.SetRect(AdjustRectToScreen(rect))

        self.steno_engine = app.StenoEngine()
        self.steno_engine.add_callback(
            lambda s: wx.CallAfter(self._update_status, s))
        self.steno_engine.set_output(
            Output(self.consume_command, self.steno_engine))

        self.steno_engine.add_stroke_listener(
            StrokeDisplayDialog.stroke_handler)
        if self.config.get_show_stroke_display():
            StrokeDisplayDialog.display(self, self.config)

        self.steno_engine.formatter.add_listener(
            SuggestionsDisplayDialog.stroke_handler)
        if self.config.get_show_suggestions_display():
            SuggestionsDisplayDialog.display(self, self.config,
                                             self.steno_engine)

        try:
            app.init_engine(self.steno_engine, self.config)
        except Exception:
            log.error('engine initialization failed', exc_info=True)
            self._show_config_dialog()
Пример #52
0
 def _reconnect(self):
     try:
         app.reset_machine(self.steno_engine, self.config)
     except Exception:
         log.error('machine reset failed', exc_info=True)
Пример #53
0
import sys
from plover import log

handler = None

try:
    if sys.platform.startswith('linux'):
        from plover.oslayer.log_dbus import DbusNotificationHandler
        handler_class = DbusNotificationHandler
    elif sys.platform.startswith('darwin'):
        from plover.oslayer.log_osx import OSXNotificationHandler
        handler_class = OSXNotificationHandler
except Exception:
    log.warning('could not import platform gui log', exc_info=True)
else:
    try:
        handler = handler_class()
    except Exception:
        log.error('could not initialize platform gui log', exc_info=True)

if handler is None:
    from plover.gui.log_wx import WxNotificationHandler
    handler = WxNotificationHandler()

log.add_handler(handler)
Пример #54
0
 def _reconnect(self):
     try:
         app.reset_machine(self.steno_engine, self.config)
     except Exception:
         log.error('machine reset failed', exc_info=True)
Пример #55
0
 def _update(self, config_update=None, full=False, reset_machine=False):
     original_config = self._config.as_dict()
     # Update configuration.
     if config_update is not None:
         self._config.update(**config_update)
         config = self._config.as_dict()
     else:
         config = original_config
     # Create configuration update.
     if full:
         config_update = config
     else:
         config_update = {
             option: value
             for option, value in config.items()
             if value != original_config[option]
         }
         if 'machine_type' in config_update:
             for opt in (
                 'machine_specific_options',
                 'system_keymap',
             ):
                 config_update[opt] = config[opt]
     # Update logging.
     log.set_stroke_filename(config['log_file_name'])
     log.enable_stroke_logging(config['enable_stroke_logging'])
     log.enable_translation_logging(config['enable_translation_logging'])
     # Update output.
     self._formatter.set_space_placement(config['space_placement'])
     self._formatter.start_attached = config['start_attached']
     self._formatter.start_capitalized = config['start_capitalized']
     self._translator.set_min_undo_length(config['undo_levels'])
     # Update system.
     system_name = config['system_name']
     if system.NAME != system_name:
         log.info('loading system: %s', system_name)
         system.setup(system_name)
     # Update machine.
     update_keymap = False
     start_machine = False
     machine_params = MachineParams(config['machine_type'],
                                    config['machine_specific_options'],
                                    config['system_keymap'])
     # Do not reset if only the keymap changed.
     if self._machine_params is None or \
        self._machine_params.type != machine_params.type or \
        self._machine_params.options != machine_params.options:
         reset_machine = True
     if reset_machine:
         if self._machine is not None:
             self._machine.stop_capture()
             self._machine = None
         machine_type = config['machine_type']
         machine_options = config['machine_specific_options']
         try:
             machine_class = registry.get_plugin('machine', machine_type).obj
         except Exception as e:
             raise InvalidConfigurationError(str(e))
         log.info('setting machine: %s', machine_type)
         self._machine = machine_class(machine_options)
         self._machine.set_suppression(self._is_running)
         self._machine.add_state_callback(self._machine_state_callback)
         self._machine.add_stroke_callback(self._machine_stroke_callback)
         self._machine_params = machine_params
         update_keymap = True
         start_machine = True
     elif self._machine is not None:
         update_keymap = 'system_keymap' in config_update
     if update_keymap:
         machine_keymap = config['system_keymap']
         if machine_keymap is not None:
             self._machine.set_keymap(machine_keymap)
     if start_machine:
         self._machine.start_capture()
     # Update running extensions.
     enabled_extensions = config['enabled_extensions']
     running_extensions = set(self._running_extensions)
     self._stop_extensions(running_extensions - enabled_extensions)
     self._start_extensions(enabled_extensions - running_extensions)
     # Trigger `config_changed` hook.
     if config_update:
         self._trigger_hook('config_changed', config_update)
     # Update dictionaries.
     config_dictionaries = OrderedDict(
         (d.path, d)
         for d in config['dictionaries']
     )
     copy_default_dictionaries(config_dictionaries.keys())
     # Start by unloading outdated dictionaries.
     self._dictionaries_manager.unload_outdated()
     self._set_dictionaries([
         d for d in self._dictionaries.dicts
         if d.path in config_dictionaries and \
            d.path in self._dictionaries_manager
     ])
     # And then (re)load all dictionaries.
     dictionaries = []
     for result in self._dictionaries_manager.load(config_dictionaries.keys()):
         if isinstance(result, DictionaryLoaderException):
             d = ErroredDictionary(result.path, result.exception)
             # Only show an error if it's new.
             if d != self._dictionaries.get(result.path):
                 log.error('loading dictionary `%s` failed: %s',
                           shorten_path(result.path), str(result.exception))
         else:
             d = result
         d.enabled = config_dictionaries[d.path].enabled
         dictionaries.append(d)
     self._set_dictionaries(dictionaries)
Пример #56
0
 def _update(self, config_update=None, full=False, reset_machine=False):
     original_config = self._config.as_dict()
     # Update configuration.
     if config_update is not None:
         self._config.update(**config_update)
         config = self._config.as_dict()
     else:
         config = original_config
     # Create configuration update.
     if full:
         config_update = config
     else:
         config_update = {
             option: value
             for option, value in config.items()
             if value != original_config[option]
         }
     # Update logging.
     log.set_stroke_filename(config['log_file_name'])
     log.enable_stroke_logging(config['enable_stroke_logging'])
     log.enable_translation_logging(config['enable_translation_logging'])
     # Update output.
     self._formatter.set_space_placement(config['space_placement'])
     self._formatter.start_attached = config['start_attached']
     self._formatter.start_capitalized = config['start_capitalized']
     self._translator.set_min_undo_length(config['undo_levels'])
     # Update system.
     system_name = config['system_name']
     if system.NAME != system_name:
         log.info('loading system: %s', system_name)
         system.setup(system_name)
     # Update machine.
     update_keymap = False
     start_machine = False
     machine_params = MachineParams(config['machine_type'],
                                    config['machine_specific_options'],
                                    config['system_keymap'])
     # Do not reset if only the keymap changed.
     if self._machine_params is None or \
        self._machine_params.type != machine_params.type or \
        self._machine_params.options != machine_params.options:
         reset_machine = True
     if reset_machine:
         if self._machine is not None:
             self._machine.stop_capture()
             self._machine = None
         machine_type = config['machine_type']
         machine_options = config['machine_specific_options']
         machine_class = registry.get_plugin('machine', machine_type).obj
         log.info('setting machine: %s', machine_type)
         self._machine = machine_class(machine_options)
         self._machine.set_suppression(self._is_running)
         self._machine.add_state_callback(self._machine_state_callback)
         self._machine.add_stroke_callback(self._machine_stroke_callback)
         self._machine_params = machine_params
         update_keymap = True
         start_machine = True
     elif self._machine is not None:
         update_keymap = 'system_keymap' in config_update
     if update_keymap:
         machine_keymap = config['system_keymap']
         if machine_keymap is not None:
             self._machine.set_keymap(machine_keymap)
     if start_machine:
         self._machine.start_capture()
     # Update running extensions.
     enabled_extensions = config['enabled_extensions']
     running_extensions = set(self._running_extensions)
     self._stop_extensions(running_extensions - enabled_extensions)
     self._start_extensions(enabled_extensions - running_extensions)
     # Trigger `config_changed` hook.
     if config_update:
         self._trigger_hook('config_changed', config_update)
     # Update dictionaries.
     config_dictionaries = OrderedDict(
         (d.path, d) for d in config['dictionaries'])
     copy_default_dictionaries(config_dictionaries.keys())
     # Start by unloading outdated dictionaries.
     self._dictionaries_manager.unload_outdated()
     self._set_dictionaries([
         d for d in self._dictionaries.dicts
         if d.path in config_dictionaries and \
            d.path in self._dictionaries_manager
     ])
     # And then (re)load all dictionaries.
     dictionaries = []
     for result in self._dictionaries_manager.load(
             config_dictionaries.keys()):
         if isinstance(result, DictionaryLoaderException):
             d = ErroredDictionary(result.path, result.exception)
             # Only show an error if it's new.
             if d != self._dictionaries.get(result.path):
                 log.error('loading dictionary `%s` failed: %s',
                           shorten_path(result.path), str(result.exception))
         else:
             d = result
         d.enabled = config_dictionaries[d.path].enabled
         dictionaries.append(d)
     self._set_dictionaries(dictionaries)
Пример #57
0
def default_excepthook(*exc_info):
    log.error('Qt GUI error', exc_info=exc_info)
Пример #58
0
import sys
from plover import log


handler = None

try:
    if sys.platform.startswith('linux'):
        from plover.oslayer.log_dbus import DbusNotificationHandler
        handler_class = DbusNotificationHandler
    elif sys.platform.startswith('darwin'):
        from plover.oslayer.log_osx import OSXNotificationHandler
        handler_class = OSXNotificationHandler
except Exception:
    log.warning('could not import platform gui log', exc_info=True)
else:
    try:
        handler = handler_class()
    except Exception:
        log.error('could not initialize platform gui log', exc_info=True)

if handler is None:
    from plover.gui.log_wx import WxNotificationHandler
    handler = WxNotificationHandler()

log.add_handler(handler)
Пример #59
0
def parse_rtfcre(text, normalize=lambda s: s, skip_errors=True):
    not_text = r'\{}'
    style_rx = re.compile('s[0-9]+')
    tokenizer = RtfTokenizer(text)
    next_token = tokenizer.next_token
    rewind_token = tokenizer.rewind_token
    # Check header.
    if next_token() != '{' or next_token() != r'\rtf1':
        raise BadRtfError('invalid header')
    # Parse header/document.
    g_destination, g_text = 'rtf1', ''
    group_stack = deque()
    stylesheet = {}
    steno = None
    while True:
        token = next_token()
        # EOF.
        if token is None:
            err = RtfParseError(tokenizer.lnum, tokenizer.cnum,
                                'unexpected end of file')
            if not skip_errors:
                raise err
            log.error('%s', err)
            break
        # Group start.
        if token == '{':
            # Always rewind the last token?
            rewind = False
            # Is it an ignored group?
            is_ignored = False
            destination = None
            token = next_token()
            # Ignored?
            if token == r'\*':
                token = next_token()
                is_ignored = True
            # Destination?
            if token[0] == '\\':
                destination = token[1:]
                # Steno.
                if destination == 'cxs':
                    if group_stack:
                        err = RtfParseError(
                            tokenizer.lnum, tokenizer.cnum,
                            'starting new mapping, but previous is unfinished')
                        if not skip_errors:
                            raise err
                        log.error('%s', err)
                        # Simulate missing group end(s).
                        assert group_stack[0][0] == 'rtf1'
                        rewind_token(token)
                        if is_ignored:
                            rewind_token(r'\*')
                        rewind_token('{')
                        for __ in range(len(group_stack)):
                            rewind_token('}')
                        continue
                    if steno is not None:
                        yield normalize(steno), finalize_translation(g_text)
                        steno = None
                    is_ignored = False
                    # Reset text.
                    g_text = ''
                elif destination in {
                        # Fingerspelling.
                        'cxfing',
                        # Stenovations extensions...
                        'cxsvatdictflags',
                        # Plover macro.
                        'cxplovermacro',
                        # Plover meta.
                        'cxplovermeta',
                }:
                    is_ignored = False
                elif style_rx.fullmatch(destination):
                    pass
                else:
                    # In the case of e.g. `{\par...`,
                    # `\par` must be handled as a
                    # control word.
                    rewind = True
            else:
                rewind = True
            if is_ignored:
                # Skip ignored content.
                stack_depth = 1
                while True:
                    token = next_token()
                    if token is None:
                        err = RtfParseError(tokenizer.lnum, tokenizer.cnum,
                                            'unexpected end of file')
                        if not skip_errors:
                            raise err
                        log.error('%s', err)
                        break
                    if token == '{':
                        stack_depth += 1
                    elif token == '}':
                        stack_depth -= 1
                        if not stack_depth:
                            break
                if stack_depth:
                    break
                continue
            group_stack.append((g_destination, g_text))
            g_destination, g_text = destination, ''
            if rewind:
                rewind_token(token)
            continue
        # Group end.
        if token == '}':
            if not group_stack:
                token = next_token()
                if token is None:
                    # The end...
                    break
                err = RtfParseError(tokenizer.lnum, tokenizer.cnum,
                                    'expected end of file, got: %r', token[0])
                if not skip_errors:
                    raise err
                log.error('%s', err)
                rewind_token(token)
                continue
            # Steno.
            if g_destination == 'cxs':
                steno = g_text
                text = ''
            # Punctuation.
            elif g_destination == 'cxp':
                text = g_text.strip()
                if text in {'.', '!', '?', ',', ';', ':'}:
                    text = '{' + text + '}'
                elif text == "'":
                    text = "{^'}"
                elif text in ('-', '/'):
                    text = '{^' + text + '^}'
                else:
                    # Show unknown punctuation as given.
                    text = '{^' + g_text + '^}'
            # Stenovations extensions...
            elif g_destination == 'cxsvatdictflags':
                if 'N' in g_text:
                    text = '{-|}'
                else:
                    text = ''
            # Fingerspelling.
            elif g_destination == 'cxfing':
                text = '{&' + g_text + '}'
            # Plover macro.
            elif g_destination == 'cxplovermacro':
                text = '=' + g_text
            # Plover meta.
            elif g_destination == 'cxplovermeta':
                text = '{' + g_text + '}'
            # Style declaration.
            elif (g_destination is not None
                  and style_rx.fullmatch(g_destination)
                  and group_stack[-1][0] == 'stylesheet'):
                stylesheet[g_destination] = g_text
            else:
                text = g_text
            g_destination, g_text = group_stack.pop()
            g_text += text
            continue
        # Control char/word.
        if token[0] == '\\':
            ctrl = token[1:]
            text = {
                # Ignore.
                '*': '',
                # Hard space.
                '~': '{^ ^}',
                # Non-breaking hyphen.
                '_': '{^-^}',
                # Escaped newline: \par.
                '': '\n\n',
                '\n': '\n\n',
                '\r': '\n\n',
                # Escaped characters.
                '\\': '\\',
                '{': '{',
                '}': '}',
                '-': '-',
                # Line break.
                'line': '\n',
                # Paragraph break.
                'par': '\n\n',
                # Tab.
                'tab': '\t',
                # Force Cap.
                'cxfc': '{-|}',
                # Force Lower Case.
                'cxfl': '{>}',
            }.get(ctrl)
            if text is not None:
                g_text += text
            # Delete Spaces.
            elif ctrl == 'cxds':
                token = next_token()
                if token is None or token[0] in not_text:
                    g_text += '{^}'
                    rewind_token(token)
                else:
                    text = token
                    token = next_token()
                    if token == r'\cxds':
                        # Infix
                        g_text += '{^' + text + '^}'
                    else:
                        # Prefix.
                        g_text += '{^' + text + '}'
                        rewind_token(token)
            # Delete Last Stroke.
            elif ctrl == 'cxdstroke':
                g_text = '=undo'
            # Fingerspelling.
            elif ctrl == 'cxfing':
                token = next_token()
                if token is None or token[0] in not_text:
                    err = RtfParseError(tokenizer.lnum, tokenizer.cnum,
                                        'expected text, got: %r', token)
                    if not skip_errors:
                        raise err
                    log.error('%s', err)
                    rewind_token(token)
                else:
                    g_text += '{&' + token + '}'
            elif style_rx.fullmatch(ctrl):
                # Workaround for caseCATalyst declaring
                # new styles without a preceding \par.
                if not g_text.endswith('\n\n'):
                    g_text += '\n\n'
                # Indent continuation styles.
                if stylesheet.get(ctrl, '').startswith('Contin'):
                    g_text += '    '
            continue
        # Text.
        text = token
        token = next_token()
        if token == r'\cxds':
            # Suffix.
            text = '{' + text + '^}'
        else:
            rewind_token(token)
        g_text += text
    if steno is not None:
        yield normalize(steno), finalize_translation(g_text)
Пример #60
0
    def __init__(self, config):
        self.config = config

        # Note: don't set position from config, since it's not yet loaded.
        wx.Frame.__init__(self, None, title=self.TITLE,
                          style=wx.DEFAULT_FRAME_STYLE & ~(wx.RESIZE_BORDER |
                                                           wx.RESIZE_BOX |
                                                           wx.MAXIMIZE_BOX))
        root = wx.Panel(self, style=wx.DEFAULT_FRAME_STYLE)

        # Menu Bar
        MenuBar = wx.MenuBar()
        self.SetMenuBar(MenuBar)

        # Application icon
        icon = wx.Icon(self.PLOVER_ICON_FILE,
                       wx.BITMAP_TYPE_ICO)
        self.SetIcon(icon)

        # Configure button.
        self.configure_button = wx.Button(root,
                                          label=self.CONFIGURE_BUTTON_LABEL)
        self.configure_button.Bind(wx.EVT_BUTTON, self._show_config_dialog)

        # About button.
        self.about_button = wx.Button(root, label=self.ABOUT_BUTTON_LABEL)
        self.about_button.Bind(wx.EVT_BUTTON, self._show_about_dialog)

        # Status radio buttons.
        self.radio_output_enable = wx.RadioButton(root,
                                                  label=self.ENABLE_OUTPUT_LABEL)
        self.radio_output_enable.Bind(wx.EVT_RADIOBUTTON,
                                      lambda e: self.steno_engine.set_is_running(True))
        self.radio_output_disable = wx.RadioButton(root,
                                                   label=self.DISABLE_OUTPUT_LABEL)
        self.radio_output_disable.Bind(wx.EVT_RADIOBUTTON,
                                       lambda e: self.steno_engine.set_is_running(False))

        # Machine status.
        # TODO: Figure out why spinner has darker gray background.
        self.spinner = wx.animate.GIFAnimationCtrl(root, -1, SPINNER_FILE)
        self.spinner.GetPlayer().UseBackgroundColour(True)
        # Need to call this so the size of the control is not
        # messed up (100x100 instead of 16x16) on Linux...
        self.spinner.InvalidateBestSize()
        self.spinner.Hide()

        self.connected_bitmap = wx.Bitmap(self.CONNECTED_IMAGE_FILE,
                                          wx.BITMAP_TYPE_PNG)
        self.disconnected_bitmap = wx.Bitmap(self.DISCONNECTED_IMAGE_FILE, 
                                             wx.BITMAP_TYPE_PNG)
        self.connection_ctrl = wx.StaticBitmap(root,
                                               bitmap=self.disconnected_bitmap)

        border_flag = wx.SizerFlags(1).Border(wx.ALL, self.BORDER)

        # Create Settings Box
        settings_sizer = wx.BoxSizer(wx.HORIZONTAL)


        settings_sizer.AddF(self.configure_button, border_flag.Expand())
        settings_sizer.AddF(self.about_button, border_flag.Expand())

        # Create Output Status Box
        box = wx.StaticBox(root, label=self.HEADER_OUTPUT)
        status_sizer = wx.StaticBoxSizer(box, wx.HORIZONTAL)

        status_sizer.AddF(self.radio_output_enable, border_flag)
        status_sizer.AddF(self.radio_output_disable, border_flag)

        # Create Machine Status Box
        machine_sizer = wx.BoxSizer(wx.HORIZONTAL)
        center_flag =\
            wx.SizerFlags()\
              .Align(wx.ALIGN_CENTER_VERTICAL)\
              .Border(wx.LEFT | wx.RIGHT, self.BORDER)

        machine_sizer.AddF(self.spinner, center_flag)
        machine_sizer.AddF(self.connection_ctrl, center_flag)
        longest_machine = max(machine_registry.get_all_names(), key=len)
        longest_state = max((STATE_ERROR, STATE_INITIALIZING, STATE_RUNNING),
                            key=len)
        longest_machine_status = '%s: %s' % (longest_machine, longest_state)
        self.machine_status_text = wx.StaticText(root, label=longest_machine_status)
        machine_sizer.AddF(self.machine_status_text, center_flag)
        refresh_bitmap = wx.Bitmap(self.REFRESH_IMAGE_FILE, wx.BITMAP_TYPE_PNG)          
        self.reconnect_button = wx.BitmapButton(root, bitmap=refresh_bitmap)
        machine_sizer.AddF(self.reconnect_button, center_flag)

        # Assemble main UI
        global_sizer = wx.GridBagSizer(vgap=self.BORDER, hgap=self.BORDER)
        global_sizer.Add(settings_sizer,
                         flag=wx.EXPAND,
                         pos=(0, 0),
                         span=(1, 2))
        global_sizer.Add(status_sizer,
                         flag=wx.EXPAND | wx.LEFT | wx.RIGHT,
                         border=self.BORDER,
                         pos=(1, 0),
                         span=(1, 2))
        global_sizer.Add(machine_sizer,
                         flag=wx.CENTER | wx.ALIGN_CENTER | wx.EXPAND | wx.LEFT,
                         pos=(2, 0),
                         border=self.BORDER,
                         span=(1, 2))
        self.machine_sizer = machine_sizer
        # Add a border around the entire sizer.
        border = wx.BoxSizer()
        border.AddF(global_sizer,
                    wx.SizerFlags(1).Border(wx.ALL, self.BORDER).Expand())
        root.SetSizerAndFit(border)
        border.SetSizeHints(self)

        self.Bind(wx.EVT_CLOSE, self._quit)
        self.Bind(wx.EVT_MOVE, self.on_move)
        self.reconnect_button.Bind(wx.EVT_BUTTON, lambda e: self._reconnect())

        try:
            with open(config.target_file, 'rb') as f:
                self.config.load(f)
        except Exception:
            log.error('loading configuration failed, reseting to default', exc_info=True)
            self.config.clear()

        rect = wx.Rect(config.get_main_frame_x(), config.get_main_frame_y(), *self.GetSize())
        self.SetRect(AdjustRectToScreen(rect))

        self.steno_engine = app.StenoEngine()
        self.steno_engine.add_callback(
            lambda s: wx.CallAfter(self._update_status, s))
        self.steno_engine.set_output(
            Output(self.consume_command, self.steno_engine))

        self.steno_engine.add_stroke_listener(
            StrokeDisplayDialog.stroke_handler)
        if self.config.get_show_stroke_display():
            StrokeDisplayDialog.display(self, self.config)

        self.steno_engine.formatter.add_listener(
            SuggestionsDisplayDialog.stroke_handler)
        if self.config.get_show_suggestions_display():
            SuggestionsDisplayDialog.display(self, self.config, self.steno_engine)

        try:
            app.init_engine(self.steno_engine, self.config)
        except Exception:
            log.error('engine initialization failed', exc_info=True)
            self._show_config_dialog()