Esempio n. 1
0
def _get_dictionary_module(filename):
    extension = splitext(filename)[1].lower()[1:]
    try:
        entrypoint = registry.get_plugin('dictionary', extension)
    except KeyError:
        raise DictionaryLoaderException(
            'Unsupported extension: %s. Supported extensions: %s' %
            (extension, ', '.join(sorted(registry.list_plugins('dictionary')))))
    return entrypoint.resolve()
Esempio n. 2
0
def _get_dictionary_class(filename):
    extension = splitext(filename)[1].lower()[1:]
    try:
        dict_module = registry.get_plugin('dictionary', extension).obj
    except KeyError:
        raise ValueError(
            'Unsupported extension: %s. Supported extensions: %s' %
            (extension, ', '.join(plugin.name for plugin in
                                  registry.list_plugins('dictionary'))))
    return dict_module
Esempio n. 3
0
def _get_dictionary_module(filename):
    extension = splitext(filename)[1].lower()[1:]
    try:
        entrypoint = registry.get_plugin('dictionary', extension)
    except KeyError:
        raise DictionaryLoaderException(
            'Unsupported extension: %s. Supported extensions: %s' %
            (extension, ', '.join(sorted(
                registry.list_plugins('dictionary')))))
    return entrypoint.resolve()
Esempio n. 4
0
def _get_dictionary_class(filename):
    extension = splitext(filename)[1].lower()[1:]
    try:
        dict_module = registry.get_plugin('dictionary', extension).obj
    except KeyError:
        raise ValueError(
            'Unsupported extension: %s. Supported extensions: %s' %
            (extension, ', '.join(plugin.name for plugin in
                                  registry.list_plugins('dictionary'))))
    return dict_module
Esempio n. 5
0
 def _machine_option(self, *args):
     machine_options = {
         plugin.name: plugin.obj
         for plugin in registry.list_plugins('gui.qt.machine_option')
     }
     machine_type = self._config['machine_type']
     machine_class = registry.get_plugin('machine', machine_type).obj
     for klass in machine_class.mro():
         # Look for `module_name:class_name` before `class_name`.
         for name in (
                 '%s:%s' % (klass.__module__, klass.__name__),
                 klass.__name__,
         ):
             opt_class = machine_options.get(name)
             if opt_class is not None:
                 return opt_class(*args)
     return NopeOption(*args)
Esempio n. 6
0
 def _machine_option(self, *args):
     machine_options = {
         plugin.name: plugin.obj
         for plugin in registry.list_plugins('gui.qt.machine_option')
     }
     machine_type = self._config['machine_type']
     machine_class = registry.get_plugin('machine', machine_type).obj
     for klass in machine_class.mro():
         # Look for `module_name:class_name` before `class_name`.
         for name in (
             '%s:%s' % (klass.__module__, klass.__name__),
             klass.__name__,
         ):
             opt_class = machine_options.get(name)
             if opt_class is not None:
                 return opt_class(*args)
     return NopeOption(*args)
Esempio n. 7
0
 def __init__(self, engine, use_qt_notifications):
     super(MainWindow, self).__init__()
     self.setupUi(self)
     if hasattr(self, 'setUnifiedTitleAndToolBarOnMac'):
         self.setUnifiedTitleAndToolBarOnMac(True)
     self._engine = engine
     self._active_dialogs = {}
     self._dialog_class = {
         'about'             : AboutDialog,
         'configuration'     : ConfigWindow,
     }
     all_actions = find_menu_actions(self.menubar)
     # Dictionaries.
     self.dictionaries = self.scroll_area.widget()
     self.dictionaries.add_translation.connect(self._add_translation)
     self.dictionaries.setFocus()
     edit_menu = all_actions['menu_Edit'].menu()
     edit_menu.addAction(self.dictionaries.action_Undo)
     edit_menu.addSeparator()
     edit_menu.addMenu(self.dictionaries.menu_AddDictionaries)
     edit_menu.addAction(self.dictionaries.action_EditDictionaries)
     edit_menu.addAction(self.dictionaries.action_RemoveDictionaries)
     edit_menu.addSeparator()
     edit_menu.addAction(self.dictionaries.action_MoveDictionariesUp)
     edit_menu.addAction(self.dictionaries.action_MoveDictionariesDown)
     # Tray icon.
     self._trayicon = TrayIcon()
     self._trayicon.enable()
     self._trayicon.clicked.connect(self._engine.toggle_output)
     if use_qt_notifications:
         handler = NotificationHandler()
         handler.emitSignal.connect(self._trayicon.log)
         log.add_handler(handler)
     popup_menu = QMenu()
     for action_name in (
         'action_ToggleOutput',
         'action_Reconnect',
         '',
         'menu_Tools',
         '',
         'action_Configure',
         '',
         'menu_Help',
         '',
         'action_Show',
         'action_Quit',
     ):
         if action_name:
             popup_menu.addAction(all_actions[action_name])
         else:
             popup_menu.addSeparator()
     self._trayicon.set_menu(popup_menu)
     engine.signal_connect('machine_state_changed', self._trayicon.update_machine_state)
     engine.signal_connect('quit', self.on_quit)
     self.action_Quit.triggered.connect(engine.quit)
     # Populate tools bar/menu.
     tools_menu = all_actions['menu_Tools'].menu()
     # Toolbar popup menu for selecting which tools are shown.
     self.toolbar_menu = QMenu()
     self.toolbar.setContextMenuPolicy(Qt.CustomContextMenu)
     self.toolbar.customContextMenuRequested.connect(
         lambda: self.toolbar_menu.popup(QCursor.pos())
     )
     for tool_plugin in registry.list_plugins('gui.qt.tool'):
         tool = tool_plugin.obj
         action_parameters = []
         if tool.ICON is not None:
             icon = tool.ICON
             # Internal QT resources start with a `:`.
             if not icon.startswith(':'):
                 icon = resource_filename(icon)
             action_parameters.append(QIcon(icon))
         action_parameters.append(tool.TITLE)
         toolbar_action = None
         for parent in (tools_menu, self.toolbar, self.toolbar_menu):
             action = parent.addAction(*action_parameters)
             action.setObjectName(tool_plugin.name)
             if tool.__doc__ is not None:
                 action.setToolTip(tool.__doc__)
             if tool.SHORTCUT is not None:
                 action.setShortcut(QKeySequence.fromString(tool.SHORTCUT))
             if parent == self.toolbar_menu:
                 action.setCheckable(True)
                 action.setChecked(True)
                 assert toolbar_action is not None
                 action.toggled.connect(toolbar_action.setVisible)
             else:
                 if parent == self.toolbar:
                     toolbar_action = action
                 action.triggered.connect(partial(self._activate_dialog,
                                                  tool_plugin.name,
                                                  args=()))
         self._dialog_class[tool_plugin.name] = tool
     engine.signal_connect('output_changed', self.on_output_changed)
     # Machine.
     self.machine_type.addItems(
         _(plugin.name)
         for plugin in registry.list_plugins('machine')
     )
     engine.signal_connect('config_changed', self.on_config_changed)
     engine.signal_connect('machine_state_changed',
         lambda machine, state:
         self.machine_state.setText(_(state.capitalize()))
     )
     self.restore_state()
     # Commands.
     engine.signal_connect('add_translation', partial(self._add_translation, manage_windows=True))
     engine.signal_connect('focus', self._focus)
     engine.signal_connect('configure', partial(self._configure, manage_windows=True))
     engine.signal_connect('lookup', partial(self._activate_dialog, 'lookup',
                                             manage_windows=True))
     # Load the configuration (but do not start the engine yet).
     if not engine.load_config():
         self.on_configure()
     # Apply configuration settings.
     config = self._engine.config
     self.machine_type.setCurrentText(config['machine_type'])
     self._configured = False
     self.dictionaries.on_config_changed(config)
     self.set_visible(not config['start_minimized'])
     # Start the engine.
     engine.start()
Esempio n. 8
0
 def list_plugins(self, plugin_type):
     return sorted(registry.list_plugins(plugin_type))
Esempio n. 9
0
def _dictionary_formats():
    return set(registry.list_plugins('dictionary'))
Esempio n. 10
0
 def __init__(self, output, engine):
     sub_commands = [
         EnableDisableExtension(p.name, output, engine)
         for p in registry.list_plugins("extension")
     ]
     super().__init__("extensions", output, sub_commands)
Esempio n. 11
0
def _dictionary_formats():
    return set(registry.list_plugins('dictionary'))
Esempio n. 12
0
def _dictionary_formats(include_readonly=True):
    return set(plugin.name
               for plugin in registry.list_plugins('dictionary')
               if include_readonly or not plugin.obj.readonly)
Esempio n. 13
0
def _dictionary_formats():
    return set(plugin.name for plugin in registry.list_plugins('dictionary'))
Esempio n. 14
0
 def list_plugins(self, plugin_type):
     return sorted(registry.list_plugins(plugin_type))
Esempio n. 15
0
 def __init__(self, output, engine) -> None:
     sub_commands = [MachineOptions(output, engine)] + [
         SetMachine(p.name, output, engine)
         for p in registry.list_plugins("machine")
     ]
     super().__init__("machine", output, sub_commands)
Esempio n. 16
0
 def __init__(self, output, engine) -> None:
     sub_commands = [
         SetSystem(p.name, output, engine)
         for p in registry.list_plugins("system")
     ]
     super().__init__("system", output, sub_commands)
Esempio n. 17
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)

    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 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 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)
Esempio n. 18
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)
Esempio n. 19
0
 def __init__(self, engine, use_qt_notifications):
     super().__init__()
     self.setupUi(self)
     if hasattr(self, 'setUnifiedTitleAndToolBarOnMac'):
         self.setUnifiedTitleAndToolBarOnMac(True)
     self._engine = engine
     self._active_dialogs = {}
     self._dialog_class = {
         'about': AboutDialog,
         'configuration': ConfigWindow,
     }
     all_actions = find_menu_actions(self.menubar)
     # Dictionaries.
     self.dictionaries.add_translation.connect(self._add_translation)
     self.dictionaries.setup(engine)
     # Populate edit menu from dictionaries' own.
     edit_menu = all_actions['menu_Edit'].menu()
     for action in self.dictionaries.edit_menu.actions():
         edit_menu.addAction(action)
     # Tray icon.
     self._trayicon = TrayIcon()
     self._trayicon.enable()
     self._trayicon.clicked.connect(self._engine.toggle_output)
     if use_qt_notifications:
         handler = NotificationHandler()
         handler.emitSignal.connect(self._trayicon.log)
         log.add_handler(handler)
     popup_menu = QMenu()
     for action_name in (
             'action_ToggleOutput',
             'action_Reconnect',
             '',
             'menu_Tools',
             '',
             'action_Configure',
             'action_OpenConfigFolder',
             '',
             'menu_Help',
             '',
             'action_Show',
             'action_Quit',
     ):
         if action_name:
             popup_menu.addAction(all_actions[action_name])
         else:
             popup_menu.addSeparator()
     self._trayicon.set_menu(popup_menu)
     engine.signal_connect('machine_state_changed',
                           self._trayicon.update_machine_state)
     engine.signal_connect('quit', self.on_quit)
     self.action_Quit.triggered.connect(engine.quit)
     # Toolbar popup menu for selecting which tools are shown.
     self.toolbar_menu = QMenu()
     self.toolbar.setContextMenuPolicy(Qt.CustomContextMenu)
     self.toolbar.customContextMenuRequested.connect(
         lambda: self.toolbar_menu.popup(QCursor.pos()))
     # Populate tools bar/menu.
     tools_menu = all_actions['menu_Tools'].menu()
     for tool_plugin in registry.list_plugins('gui.qt.tool'):
         tool = tool_plugin.obj
         menu_action = tools_menu.addAction(tool.TITLE)
         if tool.SHORTCUT is not None:
             menu_action.setShortcut(QKeySequence.fromString(tool.SHORTCUT))
         if tool.ICON is not None:
             icon = tool.ICON
             # Internal QT resources start with a `:`.
             if not icon.startswith(':'):
                 icon = resource_filename(icon)
             menu_action.setIcon(QIcon(icon))
         menu_action.triggered.connect(
             partial(self._activate_dialog, tool_plugin.name, args=()))
         toolbar_action = self.toolbar.addAction(menu_action.icon(),
                                                 menu_action.text())
         if tool.__doc__ is not None:
             toolbar_action.setToolTip(tool.__doc__)
         toolbar_action.triggered.connect(menu_action.trigger)
         toggle_action = self.toolbar_menu.addAction(
             menu_action.icon(), menu_action.text())
         toggle_action.setObjectName(tool_plugin.name)
         toggle_action.setCheckable(True)
         toggle_action.setChecked(True)
         toggle_action.toggled.connect(toolbar_action.setVisible)
         self._dialog_class[tool_plugin.name] = tool
     engine.signal_connect('output_changed', self.on_output_changed)
     # Machine.
     for plugin in registry.list_plugins('machine'):
         self.machine_type.addItem(_(plugin.name), plugin.name)
     engine.signal_connect('config_changed', self.on_config_changed)
     engine.signal_connect(
         'machine_state_changed',
         lambda machine, state: self.machine_state.setText(state.capitalize(
         )))
     self.restore_state()
     # Commands.
     engine.signal_connect(
         'add_translation',
         partial(self._add_translation, manage_windows=True))
     engine.signal_connect('focus', self._focus)
     engine.signal_connect('configure',
                           partial(self._configure, manage_windows=True))
     engine.signal_connect(
         'lookup',
         partial(self._activate_dialog, 'lookup', manage_windows=True))
     engine.signal_connect(
         'suggestions',
         partial(self._activate_dialog, 'suggestions', manage_windows=True))
     # Load the configuration (but do not start the engine yet).
     if not engine.load_config():
         self.on_configure()
     # Apply configuration settings.
     config = self._engine.config
     self._warn_on_hide_to_tray = not config['start_minimized']
     self._update_machine(config['machine_type'])
     self._configured = False
     self.set_visible(not config['start_minimized'])
     # Process events before starting the engine
     # (to avoid display lag at window creation).
     QCoreApplication.processEvents()
     # Start the engine.
     engine.start()
Esempio n. 20
0
 def __init__(self, engine):
     super().__init__()
     self.setupUi(self)
     self._engine = engine
     machines = {
         plugin.name: _(plugin.name)
         for plugin in registry.list_plugins('machine')
     }
     mappings = (
         # i18n: Widget: “ConfigWindow”.
         (_('Interface'), (
             ConfigOption(
                 _('Start minimized:'), 'start_minimized', BooleanOption,
                 _('Minimize the main window to systray on startup.')),
             ConfigOption(_('Show paper tape:'),
                          'show_stroke_display', BooleanOption,
                          _('Open the paper tape on startup.')),
             ConfigOption(_('Show suggestions:'),
                          'show_suggestions_display', BooleanOption,
                          _('Open the suggestions dialog on startup.')),
             ConfigOption(
                 _('Add translation dialog opacity:'),
                 'translation_frame_opacity',
                 partial(IntOption, maximum=100, minimum=0),
                 _('Set the translation dialog opacity:\n'
                   '- 0 makes the dialog invisible\n'
                   '- 100 is fully opaque')),
             ConfigOption(
                 _('Dictionaries display order:'),
                 'classic_dictionaries_display_order',
                 partial(BooleanAsDualChoiceOption, _('top-down'),
                         _('bottom-up')),
                 _('Set the display order for dictionaries:\n'
                   '- top-down: match the search order; highest priority first\n'
                   '- bottom-up: reverse search order; lowest priority first\n'
                   )),
         )),
         # i18n: Widget: “ConfigWindow”.
         (_('Logging'), (
             ConfigOption(
                 _('Log file:'), 'log_file_name',
                 partial(FileOption, _('Select a log file'),
                         _('Log files (*.log)')),
                 _('File to use for logging strokes/translations.')),
             ConfigOption(_('Log strokes:'), 'enable_stroke_logging',
                          BooleanOption, _('Save strokes to the logfile.')),
             ConfigOption(_('Log translations:'),
                          'enable_translation_logging', BooleanOption,
                          _('Save translations to the logfile.')),
         )),
         # i18n: Widget: “ConfigWindow”.
         (_('Machine'), (
             ConfigOption(
                 _('Machine:'),
                 'machine_type',
                 partial(ChoiceOption, choices=machines),
                 dependents=(
                     ('machine_specific_options',
                      self._update_machine_options),
                     ('system_keymap',
                      lambda v: self._update_keymap(machine_type=v)),
                 )),
             ConfigOption(_('Options:'), 'machine_specific_options',
                          self._machine_option),
             ConfigOption(_('Keymap:'), 'system_keymap', KeymapOption),
         )),
         # i18n: Widget: “ConfigWindow”.
         (_('Output'), (
             ConfigOption(_('Enable at start:'), 'auto_start',
                          BooleanOption, _('Enable output on startup.')),
             ConfigOption(
                 _('Start attached:'), 'start_attached', BooleanOption,
                 _('Disable preceding space on first output.\n'
                   '\n'
                   'This option is only applicable when spaces are placed before.'
                   )),
             ConfigOption(_('Start capitalized:'), 'start_capitalized',
                          BooleanOption, _('Capitalize the first word.')),
             ConfigOption(
                 _('Space placement:'), 'space_placement',
                 partial(ChoiceOption,
                         choices={
                             'Before Output': _('Before Output'),
                             'After Output': _('After Output'),
                         }),
                 _('Set automatic space placement: before or after each word.'
                   )),
             ConfigOption(
                 _('Undo levels:'), 'undo_levels',
                 partial(IntOption,
                         maximum=10000,
                         minimum=MINIMUM_UNDO_LEVELS),
                 _('Set how many preceding strokes can be undone.\n'
                   '\n'
                   'Note: the effective value will take into account the\n'
                   'dictionaries entry with the maximum number of strokes.')
             ),
         )),
         # i18n: Widget: “ConfigWindow”.
         (_('Plugins'), (ConfigOption(
             _('Extension:'), 'enabled_extensions',
             partial(MultipleChoicesOption,
                     choices={
                         plugin.name: plugin.name
                         for plugin in registry.list_plugins('extension')
                     },
                     labels=(_('Name'), _('Enabled'))),
             _('Configure enabled plugin extensions.')), )),
         # i18n: Widget: “ConfigWindow”.
         (_('System'), (ConfigOption(
             _('System:'),
             'system_name',
             partial(ChoiceOption,
                     choices={
                         plugin.name: plugin.name
                         for plugin in registry.list_plugins('system')
                     }),
             dependents=(
                 ('system_keymap',
                  lambda v: self._update_keymap(system_name=v)), )), )),
     )
     # Only keep supported options, to avoid messing with things like
     # dictionaries, that are handled by another (possibly concurrent)
     # dialog.
     self._supported_options = set()
     for section, option_list in mappings:
         self._supported_options.update(option.option_name
                                        for option in option_list)
     self._update_config()
     # Create and fill tabs.
     options = {}
     for section, option_list in mappings:
         layout = QFormLayout()
         for option in option_list:
             widget = self._create_option_widget(option)
             options[option.option_name] = option
             option.tab_index = self.tabs.count()
             option.layout = layout
             option.widget = widget
             label = QLabel(option.display_name)
             label.setToolTip(option.help_text)
             layout.addRow(label, widget)
         frame = QFrame()
         frame.setLayout(layout)
         scroll_area = QScrollArea()
         scroll_area.setWidgetResizable(True)
         scroll_area.setWidget(frame)
         self.tabs.addTab(scroll_area, section)
     # Update dependents.
     for option in options.values():
         option.dependents = [
             (options[option_name], update_fn)
             for option_name, update_fn in option.dependents
         ]
     buttons = self.findChild(QWidget, 'buttons')
     buttons.button(QDialogButtonBox.Ok).clicked.connect(self.on_apply)
     buttons.button(QDialogButtonBox.Apply).clicked.connect(self.on_apply)
     self.restore_state()
     self.finished.connect(self.save_state)
Esempio n. 21
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)
Esempio n. 22
0
 def __init__(self, engine, use_qt_notifications):
     super(MainWindow, self).__init__()
     self.setupUi(self)
     if hasattr(self, 'setUnifiedTitleAndToolBarOnMac'):
         self.setUnifiedTitleAndToolBarOnMac(True)
     self._engine = engine
     self._active_dialogs = {}
     self._dialog_class = {
         'about'             : AboutDialog,
         'configuration'     : ConfigWindow,
     }
     all_actions = find_menu_actions(self.menubar)
     # Dictionaries.
     self.dictionaries = DictionariesWidget(engine)
     self.dictionaries.add_translation.connect(self._add_translation)
     self.scroll_area.setWidget(self.dictionaries)
     self.dictionaries.setFocus()
     edit_menu = all_actions['menu_Edit'].menu()
     edit_menu.addAction(self.dictionaries.action_Undo)
     edit_menu.addSeparator()
     edit_menu.addAction(self.dictionaries.action_AddDictionaries)
     edit_menu.addAction(self.dictionaries.action_EditDictionaries)
     edit_menu.addAction(self.dictionaries.action_RemoveDictionaries)
     edit_menu.addSeparator()
     edit_menu.addAction(self.dictionaries.action_MoveDictionariesUp)
     edit_menu.addAction(self.dictionaries.action_MoveDictionariesDown)
     # Tray icon.
     self._trayicon = TrayIcon()
     self._trayicon.enable()
     self._trayicon.clicked.connect(self._engine.toggle_output)
     if use_qt_notifications:
         handler = NotificationHandler()
         handler.emitSignal.connect(self._trayicon.log)
         log.add_handler(handler)
     popup_menu = QMenu()
     for action_name in (
         'action_ToggleOutput',
         'action_Reconnect',
         '',
         'menu_Tools',
         '',
         'action_Configure',
         '',
         'menu_Help',
         '',
         'action_Show',
         'action_Quit',
     ):
         if action_name:
             popup_menu.addAction(all_actions[action_name])
         else:
             popup_menu.addSeparator()
     self._trayicon.set_menu(popup_menu)
     engine.signal_connect('machine_state_changed', self._trayicon.update_machine_state)
     engine.signal_connect('quit', self.on_quit)
     self.action_Quit.triggered.connect(engine.quit)
     # Populate tools bar/menu.
     tools_menu = all_actions['menu_Tools'].menu()
     # Toolbar popup menu for selecting which tools are shown.
     self.toolbar_menu = QMenu()
     self.toolbar.setContextMenuPolicy(Qt.CustomContextMenu)
     self.toolbar.customContextMenuRequested.connect(
         lambda: self.toolbar_menu.popup(QCursor.pos())
     )
     for tool_plugin in registry.list_plugins('gui.qt.tool'):
         tool = tool_plugin.obj
         action_parameters = []
         if tool.ICON is not None:
             icon = tool.ICON
             # Internal QT resources start with a `:`.
             if not icon.startswith(':'):
                 icon = resource_filename(icon)
             action_parameters.append(QIcon(icon))
         action_parameters.append(tool.TITLE)
         toolbar_action = None
         for parent in (tools_menu, self.toolbar, self.toolbar_menu):
             action = parent.addAction(*action_parameters)
             action.setObjectName(tool_plugin.name)
             if tool.__doc__ is not None:
                 action.setToolTip(tool.__doc__)
             if tool.SHORTCUT is not None:
                 action.setShortcut(QKeySequence.fromString(tool.SHORTCUT))
             if parent == self.toolbar_menu:
                 action.setCheckable(True)
                 action.setChecked(True)
                 assert toolbar_action is not None
                 action.toggled.connect(toolbar_action.setVisible)
             else:
                 if parent == self.toolbar:
                     toolbar_action = action
                 action.triggered.connect(partial(self._activate_dialog,
                                                  tool_plugin.name,
                                                  args=()))
         self._dialog_class[tool_plugin.name] = tool
     engine.signal_connect('output_changed', self.on_output_changed)
     # Machine.
     self.machine_type.addItems(
         _(plugin.name)
         for plugin in registry.list_plugins('machine')
     )
     engine.signal_connect('config_changed', self.on_config_changed)
     engine.signal_connect('machine_state_changed',
         lambda machine, state:
         self.machine_state.setText(_(state.capitalize()))
     )
     self.restore_state()
     # Commands.
     engine.signal_connect('add_translation', partial(self._add_translation, manage_windows=True))
     engine.signal_connect('focus', self._focus)
     engine.signal_connect('configure', partial(self._configure, manage_windows=True))
     engine.signal_connect('lookup', partial(self._activate_dialog, 'lookup',
                                             manage_windows=True))
     # Load the configuration (but do not start the engine yet).
     if not engine.load_config():
         self.on_configure()
     # Apply configuration settings.
     config = self._engine.config
     self.machine_type.setCurrentText(config['machine_type'])
     self._configured = False
     self.dictionaries.on_config_changed(config)
     self.set_visible(not config['start_minimized'])
     # Start the engine.
     engine.start()
Esempio n. 23
0
 def __init__(self, engine):
     super().__init__()
     self.setupUi(self)
     self._engine = engine
     machines = {
         plugin.name: _(plugin.name)
         for plugin in registry.list_plugins('machine')
     }
     mappings = (
         (_('Interface'), (
             ConfigOption(_('Start minimized:'), 'start_minimized', BooleanOption,
                          _('Minimize the main window to systray on startup.')),
             ConfigOption(_('Show paper tape:'), 'show_stroke_display', BooleanOption,
                          _('Open the paper tape on startup.')),
             ConfigOption(_('Show suggestions:'), 'show_suggestions_display', BooleanOption,
                          _('Open the suggestions dialog on startup.')),
             ConfigOption(_('Add translation dialog opacity:'), 'translation_frame_opacity',
                          partial(IntOption, maximum=100, minimum=0),
                          _('Set the translation dialog opacity:\n'
                            '- 0 makes the dialog invisible\n'
                            '- 100 is fully opaque')),
             ConfigOption(_('Dictionaries display order:'), 'classic_dictionaries_display_order',
                          partial(BooleanAsDualChoiceOption,
                                  _('top-down'), _('bottom-up')),
                          _('Set the display order for dictionaries:\n'
                            '- top-down: match the search order; highest priority first\n'
                            '- bottom-up: reverse search order; lowest priority first\n')),
         )),
         (_('Logging'), (
             ConfigOption(_('Log file:'), 'log_file_name',
                          partial(FileOption,
                                  _('Select a log file'),
                                  _('Log files') + ' (*.log)'),
                          _('File to use for logging strokes/translations.')),
             ConfigOption(_('Log strokes:'), 'enable_stroke_logging', BooleanOption,
                          _('Save strokes to the logfile.')),
             ConfigOption(_('Log translations:'), 'enable_translation_logging', BooleanOption,
                          _('Save translations to the logfile.')),
         )),
         (_('Machine'), (
             ConfigOption(_('Machine:'), 'machine_type', partial(ChoiceOption, choices=machines),
                          dependents=(
                              ('machine_specific_options', self._update_machine_options),
                              ('system_keymap', lambda v: self._update_keymap(machine_type=v)),
                          )),
             ConfigOption(_('Options:'), 'machine_specific_options', self._machine_option),
             ConfigOption(_('Keymap:'), 'system_keymap', KeymapOption),
         )),
         (_('Output'), (
             ConfigOption(_('Enable at start:'), 'auto_start', BooleanOption,
                          _('Enable output on startup.')),
             ConfigOption(_('Start attached:'), 'start_attached', BooleanOption,
                          _('Disable preceding space on first output.\n'
                            '\n'
                            'This option is only applicable when spaces are placed before.')),
             ConfigOption(_('Start capitalized:'), 'start_capitalized', BooleanOption,
                          _('Capitalize the first word.')),
             ConfigOption(_('Space placement:'), 'space_placement',
                          partial(ChoiceOption, choices={
                              'Before Output': _('Before Output'),
                              'After Output': _('After Output'),
                          }),
                          _('Set automatic space placement: before or after each word.')),
             ConfigOption(_('Undo levels:'), 'undo_levels',
                          partial(IntOption,
                                  maximum=10000,
                                  minimum=MINIMUM_UNDO_LEVELS),
                          _('Set how many preceding strokes can be undone.\n'
                            '\n'
                            'Note: the effective value will take into account the\n'
                            'dictionaries entry with the maximum number of strokes.')),
         )),
         (_('Plugins'), (
             ConfigOption(_('Extension:'), 'enabled_extensions',
                          partial(MultipleChoicesOption, choices={
                              plugin.name: plugin.name
                              for plugin in registry.list_plugins('extension')
                          }, labels=(_('Name'), _('Enabled'))),
                          _('Configure enabled plugin extensions.')),
         )),
         (_('System'), (
             ConfigOption(_('System:'), 'system_name',
                          partial(ChoiceOption, choices={
                              plugin.name: plugin.name
                              for plugin in registry.list_plugins('system')
                          }),
                          dependents=(
                              ('system_keymap', lambda v: self._update_keymap(system_name=v)),
                          )),
         )),
     )
     # Only keep supported options, to avoid messing with things like
     # dictionaries, that are handled by another (possibly concurrent)
     # dialog.
     self._supported_options = set()
     for section, option_list in mappings:
         self._supported_options.update(option.option_name for option in option_list)
     self._update_config()
     # Create and fill tabs.
     options = {}
     for section, option_list in mappings:
         layout = QFormLayout()
         for option in option_list:
             widget = self._create_option_widget(option)
             options[option.option_name] = option
             option.tab_index = self.tabs.count()
             option.layout = layout
             option.widget = widget
             label = QLabel(option.display_name)
             label.setToolTip(option.help_text)
             layout.addRow(label, widget)
         frame = QFrame()
         frame.setLayout(layout)
         scroll_area = QScrollArea()
         scroll_area.setWidgetResizable(True)
         scroll_area.setWidget(frame)
         self.tabs.addTab(scroll_area, section)
     # Update dependents.
     for option in options.values():
         option.dependents = [
             (options[option_name], update_fn)
             for option_name, update_fn in option.dependents
         ]
     buttons = self.findChild(QWidget, 'buttons')
     buttons.button(QDialogButtonBox.Ok).clicked.connect(self.on_apply)
     buttons.button(QDialogButtonBox.Apply).clicked.connect(self.on_apply)
     self.restore_state()
     self.finished.connect(self.save_state)
Esempio n. 24
0
def _dictionary_formats(include_readonly=True):
    return {
        plugin.name
        for plugin in registry.list_plugins('dictionary')
        if include_readonly or not plugin.obj.readonly
    }
Esempio n. 25
0
def _dictionary_formats():
    return set(plugin.name for plugin in registry.list_plugins('dictionary'))