Example #1
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__))
    args = parser.parse_args(args=sys.argv[1:])
    try:
        # Ensure only one instance of Plover is running at a time.
        with plover.oslayer.processlock.PloverLock():
            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
            gui = plover.gui.main.PloverGUI(config)
            gui.MainLoop()
            with open(config.target_file, 'wb') as f:
                config.save(f)
    except plover.oslayer.processlock.LockNotAcquiredException:
        show_error('Error', 'Another instance of Plover is already running.')
    except:
        show_error('Unexpected error', traceback.format_exc())
    os._exit(1)
Example #2
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('-l',
                        '--log-level',
                        choices=['debug', 'info', 'warning', 'error'],
                        default='warning',
                        help='set log level')
    args = parser.parse_args(args=sys.argv[1:])
    log.set_level(args.log_level.upper())
    try:
        # Ensure only one instance of Plover is running at a time.
        with plover.oslayer.processlock.PloverLock():
            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
            gui = plover.gui.main.PloverGUI(config)
            gui.MainLoop()
            with open(config.target_file, 'wb') as f:
                config.save(f)
    except plover.oslayer.processlock.LockNotAcquiredException:
        show_error('Error', 'Another instance of Plover is already running.')
    except:
        show_error('Unexpected error', traceback.format_exc())
    os._exit(1)
Example #3
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('-l', '--log-level', choices=['debug', 'info', 'warning', 'error'],
                        default=None, help='set log level')
    args = parser.parse_args(args=sys.argv[1:])
    if args.log_level is not None:
        log.set_level(args.log_level.upper())
    try:
        # 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()
            log.info('Plover %s', __version__)
            config = Config()
            config.target_file = CONFIG_FILE
            gui = plover.gui.main.PloverGUI(config)
            gui.MainLoop()
            with open(config.target_file, 'wb') as f:
                config.save(f)
    except plover.oslayer.processlock.LockNotAcquiredException:
        show_error('Error', 'Another instance of Plover is already running.')
    except:
        show_error('Unexpected error', traceback.format_exc())
    os._exit(1)
Example #4
0
 def run(self):
     self.run_command('bdist_app')
     args = '{out!r}, {name!r}, {settings!r}, lookForHiDPI=True'.format(
         out='dist/%s.dmg' % PACKAGE,
         name=__software_name__.capitalize(),
         settings='osx/dmg_resources/settings.py',
     )
     log.info('running dmgbuild(%s)', args)
     script = "__import__('dmgbuild').build_dmg(" + args + ')'
     subprocess.check_call((sys.executable, '-u', '-c', script))
Example #5
0
    def emit(self, record):
        # Notification Center has no levels or timeouts.
        notification = NSUserNotification.alloc().init()

        notification.setTitle_(__software_name__.capitalize())
        notification.setSubtitle_(record.levelname.title())
        notification.setInformativeText_(self.format(record))

        ns = NSUserNotificationCenter.defaultUserNotificationCenter()
        ns.setDelegate_(always_present_delegator)
        ns.deliverNotification_(notification)
Example #6
0
 def _show_about_dialog(self, event=None):
     """Called when the About... button is clicked."""
     info = wx.AboutDialogInfo()
     info.Name = __software_name__.capitalize()
     info.Version = __version__
     info.Copyright = __copyright__
     info.Description = __long_description__
     info.WebSite = __url__
     info.Developers = __credits__
     info.License = __license__
     wx.AboutBox(info)
Example #7
0
 def _show_about_dialog(self, event=None):
     """Called when the About... button is clicked."""
     info = wx.AboutDialogInfo()
     info.Name = __software_name__.capitalize()
     info.Version = __version__
     info.Copyright = __copyright__
     info.Description = __long_description__
     info.WebSite = __url__
     info.Developers = __credits__
     info.License = __license__
     wx.AboutBox(info)
Example #8
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('-l',
                        '--log-level',
                        choices=['debug', 'info', 'warning', 'error'],
                        default=None,
                        help='set log level')
    gui_choices = {
        'none': 'plover.gui_none.main',
        'qt': 'plover.gui_qt.main',
    }
    gui_default = 'qt'
    parser.add_argument('-g',
                        '--gui',
                        choices=gui_choices,
                        default=gui_default,
                        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())

    if not args.gui in gui_choices:
        raise ValueError('invalid gui: %r' % args.gui)
    gui = importlib.import_module(gui_choices[args.gui])

    try:
        # 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()
            log.info('Plover %s', __version__)
            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
    os._exit(code)
Example #9
0
    def emit(self, record):
        # Notification Center has no levels or timeouts.
        notification = NSUserNotification.alloc().init()

        notification.setTitle_(__software_name__.capitalize())
        notification.setSubtitle_(record.levelname.title())
        notification.setInformativeText_(self.format(record))

        ns = NSUserNotificationCenter.defaultUserNotificationCenter()
        ns.setDelegate_(always_present_delegator)
        ns.deliverNotification_(notification)
Example #10
0
def _notify(level, message):
    nm = wx.NotificationMessage()
    nm.SetTitle(__software_name__.capitalize())
    nm.SetMessage(message)
    if level <= log.INFO:
        flags = wx.ICON_INFORMATION
    elif level <= log.WARNING:
        flags = wx.ICON_WARNING
    else:
        flags = wx.ICON_ERROR
    nm.SetFlags(flags)
    nm.Show()
Example #11
0
def _notify(level, message):
    nm = wx.NotificationMessage()
    nm.SetTitle(__software_name__.capitalize())
    nm.SetMessage(message)
    if level <= log.INFO:
        flags = wx.ICON_INFORMATION
    elif level <= log.WARNING:
        flags = wx.ICON_WARNING
    else:
        flags = wx.ICON_ERROR
    nm.SetFlags(flags)
    nm.Show()
Example #12
0
def pyinstaller(*args):
    py_args = [
        '--log-level=INFO', '--specpath=build',
        '--additional-hooks-dir=windows', '--paths=.',
        '--name=%s-%s' % (
            __software_name__.capitalize(),
            __version__,
        ), '--noconfirm', '--windowed', '--onefile'
    ]
    py_args.extend(args)
    py_args.append('windows/main.py')
    main = pkg_resources.load_entry_point('PyInstaller', 'console_scripts',
                                          'pyinstaller')
    main(py_args)
Example #13
0
 def run(self):
     self.run_command('bdist_app')
     subprocess.check_call((sys.executable, '-c',
                            textwrap.dedent('''
         __import__('dmgbuild').build_dmg(
             {output!r}, {name!r},
             {settings!r}, **{options!r},
         )
         ''').format(
                                output='dist/%s.dmg' % PACKAGE,
                                name=__software_name__.capitalize(),
                                settings='osx/dmg_resources/settings.py',
                                options=dict(lookForHiDPI=True),
                            )))
Example #14
0
 def run(self):
     self.run_command('bdist_app')
     subprocess.check_call((sys.executable, '-c', textwrap.dedent(
         '''
         __import__('dmgbuild').build_dmg(
             {output!r}, {name!r},
             {settings!r}, **{options!r},
         )
         '''
     ).format(
         output='dist/%s.dmg' % PACKAGE,
         name=__software_name__.capitalize(),
         settings='osx/dmg_resources/settings.py',
         options=dict(lookForHiDPI=True),
     )))
Example #15
0
 def emit(self, record):
     level = record.levelno
     message = self.format(record)
     if level <= log.INFO:
         timeout = 10
         urgency = 0
     elif level <= log.WARNING:
         timeout = 15
         urgency = 1
     else:
         timeout = 0
         urgency = 2
     n = pynotify.Notification(__software_name__.capitalize(), message)
     n.set_timeout(timeout * 1000)
     n.set_urgency(urgency)
     n.show()
Example #16
0
 def emit(self, record):
     level = record.levelno
     message = self.format(record)
     if level <= log.INFO:
         timeout = 10
         urgency = 0
     elif level <= log.WARNING:
         timeout = 15
         urgency = 1
     else:
         timeout = 0
         urgency = 2
     n = pynotify.Notification(__software_name__.capitalize(), message)
     n.set_timeout(timeout * 1000)
     n.set_urgency(urgency)
     n.show()
Example #17
0
class Launch(Command):

    description = 'run %s from source' % __software_name__.capitalize()
    command_consumes_arguments = True
    user_options = []

    def initialize_options(self):
        self.args = None

    def finalize_options(self):
        pass

    def run(self):
        with self.project_on_sys_path():
            from plover.main import main
            sys.argv = [' '.join(sys.argv[0:2]) + ' --'] + self.args
            sys.exit(main())
Example #18
0
class Launch(setuptools.Command):

    description = 'run %s from source' % __software_name__.capitalize()
    command_consumes_arguments = True
    user_options = []

    def initialize_options(self):
        self.args = None

    def finalize_options(self):
        pass

    def run(self):
        # Make sure metadata are up-to-date first.
        self.run_command('egg_info')
        reload(pkg_resources)
        from plover.main import main
        sys.argv = [' '.join(sys.argv[0:2]) + ' --'] + self.args
        main()
Example #19
0
    def __init__(self, config, controller, use_qt_notifications):

        # This is done dynamically so localization
        # support can be configure beforehand.
        from plover.gui_qt.main_window import MainWindow

        self._app = None
        self._win = None
        self._engine = None
        self._translator = None

        QCoreApplication.setApplicationName(__software_name__.capitalize())
        QCoreApplication.setApplicationVersion(__version__)
        QCoreApplication.setOrganizationName('Open Steno Project')
        QCoreApplication.setOrganizationDomain('openstenoproject.org')

        self._app = QApplication([])
        self._app.setAttribute(Qt.AA_UseHighDpiPixmaps)

        # Enable localization of standard Qt controls.
        log.info('setting language to: %s', _.lang)
        self._translator = QTranslator()
        translations_dir = QLibraryInfo.location(QLibraryInfo.TranslationsPath)
        self._translator.load('qtbase_' + _.lang, translations_dir)
        self._app.installTranslator(self._translator)

        QApplication.setQuitOnLastWindowClosed(False)

        self._app.engine = self._engine = Engine(config, controller,
                                                 KeyboardEmulation())
        # On macOS, quitting through the dock will result
        # in a direct call to `QCoreApplication.quit`.
        self._app.aboutToQuit.connect(self._app.engine.quit)

        signal.signal(signal.SIGINT, lambda signum, stack: self._engine.quit())

        # Make sure the Python interpreter runs at least every second,
        # so signals have a chance to be processed.
        self._timer = QTimer()
        self._timer.timeout.connect(lambda: None)
        self._timer.start(1000)

        self._win = MainWindow(self._engine, use_qt_notifications)
Example #20
0
class Launch(Command):

    description = 'run %s from source' % __software_name__.capitalize()
    command_consumes_arguments = True
    user_options = []

    def initialize_options(self):
        self.args = None

    def finalize_options(self):
        pass

    def run(self):
        self.run_command('egg_info')
        python_path = os.environ.get('PYTHONPATH', '').split(os.pathsep)
        python_path.insert(0, os.getcwd())
        os.environ['PYTHONPATH'] = os.pathsep.join(python_path)
        os.execv(sys.executable,
                 [sys.executable, '-m', 'plover.main'] + self.args)
Example #21
0
    def __init__(self, config, use_qt_notifications):

        # This is done dynamically so localization
        # support can be configure beforehand.
        from plover.gui_qt.main_window import MainWindow

        self._app = None
        self._win = None
        self._engine = None
        self._translator = None

        QCoreApplication.setApplicationName(__software_name__.capitalize())
        QCoreApplication.setApplicationVersion(__version__)
        QCoreApplication.setOrganizationName('Open Steno Project')
        QCoreApplication.setOrganizationDomain('openstenoproject.org')

        self._app = QApplication([])
        self._app.setAttribute(Qt.AA_UseHighDpiPixmaps)

        # Enable localization of standard Qt controls.
        self._translator = QTranslator()
        translations_dir = QLibraryInfo.location(QLibraryInfo.TranslationsPath)
        self._translator.load('qtbase_' + get_language(), translations_dir)
        self._app.installTranslator(self._translator)

        QApplication.setQuitOnLastWindowClosed(False)

        self._app.engine = self._engine = Engine(config, KeyboardEmulation())
        # On macOS, quitting through the dock will result
        # in a direct call to `QCoreApplication.quit`.
        self._app.aboutToQuit.connect(self._app.engine.quit)

        signal.signal(signal.SIGINT, lambda signum, stack: self._engine.quit())

        # Make sure the Python interpreter runs at least every second,
        # so signals have a chance to be processed.
        self._timer = QTimer()
        self._timer.timeout.connect(lambda: None)
        self._timer.start(1000)

        self._win = MainWindow(self._engine, use_qt_notifications)
Example #22
0
class Launch(Command):

    description = 'run %s from source' % __software_name__.capitalize()
    command_consumes_arguments = True
    user_options = []

    def initialize_options(self):
        self.args = None

    def finalize_options(self):
        pass

    def run(self):
        with self.project_on_sys_path():
            python_path = os.environ.get('PYTHONPATH', '').split(os.pathsep)
            python_path.insert(0, sys.path[0])
            os.environ['PYTHONPATH'] = os.pathsep.join(python_path)
            cmd = [sys.executable, '-m', 'plover.main'] + self.args
            if sys.platform.startswith('win32'):
                # Workaround https://bugs.python.org/issue19066
                subprocess.Popen(cmd, cwd=os.getcwd())
                sys.exit(0)
            os.execv(cmd[0], cmd)
Example #23
0
 def log(self, level, message):
     if self._enabled:
         if level <= log.INFO:
             icon = QSystemTrayIcon.Information
             timeout = 10
         elif level <= log.WARNING:
             icon = QSystemTrayIcon.Warning
             timeout = 15
         else:
             icon = QSystemTrayIcon.Critical
             timeout = 25
         self._trayicon.showMessage(__software_name__.capitalize(),
                                    message, icon, timeout * 1000)
     else:
         if level <= log.INFO:
             icon = QMessageBox.Information
         elif level <= log.WARNING:
             icon = QMessageBox.Warning
         else:
             icon = QMessageBox.Critical
         msgbox = QMessageBox()
         msgbox.setText(message)
         msgbox.setIcon(icon)
         msgbox.exec_()
Example #24
0
 def log(self, level, message):
     if self._enabled:
         if level <= log.INFO:
             icon = QSystemTrayIcon.Information
             timeout = 10
         elif level <= log.WARNING:
             icon = QSystemTrayIcon.Warning
             timeout = 15
         else:
             icon = QSystemTrayIcon.Critical
             timeout = 25
         self._trayicon.showMessage(__software_name__.capitalize(),
                                    message, icon, timeout * 1000)
     else:
         if level <= log.INFO:
             icon = QMessageBox.Information
         elif level <= log.WARNING:
             icon = QMessageBox.Warning
         else:
             icon = QMessageBox.Critical
         msgbox = QMessageBox()
         msgbox.setText(message)
         msgbox.setIcon(icon)
         msgbox.exec_()
Example #25
0
class MainFrame(wx.Frame):
    """The top-level GUI element of the Plover application."""

    # Class constants.
    TITLE = __software_name__.capitalize()
    ALERT_DIALOG_TITLE = TITLE
    CONNECTED_IMAGE_FILE = os.path.join(ASSETS_DIR, 'connected.png')
    DISCONNECTED_IMAGE_FILE = os.path.join(ASSETS_DIR, 'disconnected.png')
    REFRESH_IMAGE_FILE = os.path.join(ASSETS_DIR, 'refresh.png')
    PLOVER_ICON_FILE = os.path.join(ASSETS_DIR, 'plover.ico')
    BORDER = 5
    STATUS_DISCONNECTED, STATUS_OUTPUT_DISABLED, STATUS_OUTPUT_ENABLED = range(
        -1, 2)
    ENABLE_OUTPUT_LABEL = "Enable"
    DISABLE_OUTPUT_LABEL = "Disable"
    HEADER_OUTPUT = "Output"
    HEADER_MACHINE = "Machine"
    HEADER_SETTINGS = "Settings"
    CONFIGURE_BUTTON_LABEL = u"Configure…"
    ABOUT_BUTTON_LABEL = u"About…"
    RECONNECT_BUTTON_LABEL = u"Reconnect…"
    COMMAND_SUSPEND = 'SUSPEND'
    COMMAND_ADD_TRANSLATION = 'ADD_TRANSLATION'
    COMMAND_LOOKUP = 'LOOKUP'
    COMMAND_RESUME = 'RESUME'
    COMMAND_TOGGLE = 'TOGGLE'
    COMMAND_CONFIGURE = 'CONFIGURE'
    COMMAND_FOCUS = 'FOCUS'
    COMMAND_QUIT = 'QUIT'

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

    def _reconnect(self):
        try:
            app.reset_machine(self.steno_engine, self.config)
        except Exception:
            log.error('machine reset failed', exc_info=True)

    def consume_command(self, command):
        # The first commands can be used whether plover has output enabled or not.
        if command == self.COMMAND_RESUME:
            wx.CallAfter(self.steno_engine.set_is_running, True)
            return True
        elif command == self.COMMAND_TOGGLE:
            wx.CallAfter(self.steno_engine.set_is_running,
                         not self.steno_engine.is_running)
            return True
        elif command == self.COMMAND_QUIT:
            wx.CallAfter(self._quit)
            return True

        if not self.steno_engine.is_running:
            return False

        # These commands can only be run when plover has output enabled.
        if command == self.COMMAND_SUSPEND:
            wx.CallAfter(self.steno_engine.set_is_running, False)
            return True
        elif command == self.COMMAND_CONFIGURE:
            wx.CallAfter(self._show_config_dialog)
            return True
        elif command == self.COMMAND_FOCUS:

            def f():
                self.Raise()
                self.Iconize(False)

            wx.CallAfter(f)
            return True
        elif command == self.COMMAND_ADD_TRANSLATION:
            wx.CallAfter(plover.gui.add_translation.Show, self,
                         self.steno_engine, self.config)
            return True
        elif command == self.COMMAND_LOOKUP:
            wx.CallAfter(plover.gui.lookup.Show, self, self.steno_engine,
                         self.config)
            return True

        return False

    def _update_status(self, state):
        if state:
            machine_name = machine_registry.resolve_alias(
                self.config.get_machine_type())
            self.machine_status_text.SetLabel('%s: %s' % (machine_name, state))
            self.spinner.Show(state == STATE_INITIALIZING)
            self.connection_ctrl.Show(state != STATE_INITIALIZING)
            if state == STATE_INITIALIZING:
                self.spinner.Play()
            else:
                self.spinner.Stop()
            if state == STATE_RUNNING:
                self.connection_ctrl.SetBitmap(self.connected_bitmap)
            elif state == STATE_ERROR:
                self.connection_ctrl.SetBitmap(self.disconnected_bitmap)
            self.machine_sizer.Layout()
        if not self.steno_engine.machine or self.steno_engine.machine.state is not STATE_RUNNING:
            status = self.STATUS_DISCONNECTED
        elif self.steno_engine.is_running:
            status = self.STATUS_OUTPUT_ENABLED
        else:
            status = self.STATUS_OUTPUT_DISABLED
        self._show_status(status)

    def _show_status(self, status):
        if status is self.STATUS_DISCONNECTED:
            controls_enabled = False
            output_enabled = False
        elif status is self.STATUS_OUTPUT_DISABLED:
            controls_enabled = True
            output_enabled = False
        elif status is self.STATUS_OUTPUT_ENABLED:
            controls_enabled = True
            output_enabled = True
        else:
            raise ValueError('invalid status: %s' % str(status))
        # Enable / Disable controls
        self.radio_output_disable.Enable(controls_enabled)
        self.radio_output_enable.Enable(controls_enabled)
        # Set selected values
        self.radio_output_disable.SetValue(not output_enabled)
        self.radio_output_enable.SetValue(output_enabled)

    def _quit(self, event=None):
        if self.steno_engine:
            self.steno_engine.destroy()
        self.Destroy()

    def _show_config_dialog(self, event=None):
        dlg = ConfigurationDialog(self.steno_engine, self.config, parent=self)
        dlg.Show()

    def _show_about_dialog(self, event=None):
        """Called when the About... button is clicked."""
        info = wx.AboutDialogInfo()
        info.Name = __software_name__.capitalize()
        info.Version = __version__
        info.Copyright = __copyright__
        info.Description = __long_description__
        info.WebSite = __url__
        info.Developers = __credits__
        info.License = __license__
        wx.AboutBox(info)

    def on_move(self, event):
        pos = self.GetScreenPositionTuple()
        self.config.set_main_frame_x(pos[0])
        self.config.set_main_frame_y(pos[1])
        event.Skip()
Example #26
0
import setuptools

from plover import (
    __name__ as __software_name__,
    __version__,
    __description__,
    __long_description__,
    __url__,
    __download_url__,
    __license__,
    __copyright__,
)

from utils.metadata import copy_metadata

package_name = __software_name__.capitalize()


def get_version():
    if not os.path.exists('.git'):
        return None
    version = subprocess.check_output(
        'git describe --tags --match=v[0-9]*'.split()).strip()
    m = re.match(r'^v(\d[\d.]*)(-\d+-g[a-f0-9]*)?$', version)
    assert m is not None, version
    version = m.group(1)
    if m.group(2) is not None:
        version += '+' + m.group(2)[1:].replace('-', '.')
    return version

Example #27
0
import sys
import setuptools

setup_requires = []
options = {}
kwargs = {}

if sys.platform.startswith('darwin'):
    setup_requires.append('py2app')
    options['py2app'] = {
        'argv_emulation': False,
        'iconfile': 'osx/plover.icns',
        'resources': 'plover/assets/',
        'plist': {
            'CFBundleName': __software_name__.capitalize(),
            'CFBundleShortVersionString': __version__,
            'CFBundleVersion': __version__,
            'CFBundleIdentifier': 'org.openstenoproject.plover',
            'NSHumanReadableCopyright': __copyright__,
            'CFBundleDevelopmentRegion': 'English',
        }
    }
    # Py2app will not look at entry_points.
    kwargs['app'] = 'launch.py',

setuptools.setup(
    name=__software_name__.capitalize(),
    version=__version__,
    description=__description__,
    long_description=__long_description__,
Example #28
0
class MainFrame(wx.Frame):
    """The top-level GUI element of the Plover application."""

    # Class constants.
    TITLE = __software_name__.capitalize()
    ALERT_DIALOG_TITLE = TITLE
    ON_IMAGE_FILE = os.path.join(ASSETS_DIR, 'plover_on.png')
    OFF_IMAGE_FILE = os.path.join(ASSETS_DIR, 'plover_off.png')
    CONNECTED_IMAGE_FILE = os.path.join(ASSETS_DIR, 'connected.png')
    DISCONNECTED_IMAGE_FILE = os.path.join(ASSETS_DIR, 'disconnected.png')
    REFRESH_IMAGE_FILE = os.path.join(ASSETS_DIR, 'refresh.png')
    BORDER = 5
    RUNNING_MESSAGE = "running"
    STOPPED_MESSAGE = "stopped"
    ERROR_MESSAGE = "error"
    CONFIGURE_BUTTON_LABEL = "Configure..."
    ABOUT_BUTTON_LABEL = "About..."
    RECONNECT_BUTTON_LABEL = "Reconnect..."
    COMMAND_SUSPEND = 'SUSPEND'
    COMMAND_ADD_TRANSLATION = 'ADD_TRANSLATION'
    COMMAND_LOOKUP = 'LOOKUP'
    COMMAND_RESUME = 'RESUME'
    COMMAND_TOGGLE = 'TOGGLE'
    COMMAND_CONFIGURE = 'CONFIGURE'
    COMMAND_FOCUS = 'FOCUS'
    COMMAND_QUIT = 'QUIT'

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

        # Status button.
        self.on_bitmap = wx.Bitmap(self.ON_IMAGE_FILE, wx.BITMAP_TYPE_PNG)
        self.off_bitmap = wx.Bitmap(self.OFF_IMAGE_FILE, wx.BITMAP_TYPE_PNG)
        self.status_button = wx.BitmapButton(self, bitmap=self.on_bitmap)
        self.status_button.Bind(wx.EVT_BUTTON, self._toggle_steno_engine)

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

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

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

        # Machine status.
        # TODO: Figure out why spinner has darker gray background.
        self.spinner = wx.animate.GIFAnimationCtrl(self, -1, SPINNER_FILE)
        self.spinner.GetPlayer().UseBackgroundColour(True)
        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(self,
                                               bitmap=self.disconnected_bitmap)

        # Layout.
        global_sizer = wx.BoxSizer(wx.VERTICAL)

        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(self.status_button,
                  flag=wx.ALL | wx.ALIGN_CENTER_VERTICAL,
                  border=self.BORDER)
        sizer.Add(self.configure_button,
                  flag=wx.TOP | wx.BOTTOM | wx.RIGHT
                  | wx.ALIGN_CENTER_VERTICAL,
                  border=self.BORDER)
        sizer.Add(self.about_button,
                  flag=wx.TOP | wx.BOTTOM | wx.RIGHT
                  | wx.ALIGN_CENTER_VERTICAL,
                  border=self.BORDER)
        global_sizer.Add(sizer)

        sizer = wx.BoxSizer(wx.HORIZONTAL)
        sizer.Add(self.spinner,
                  flag=(wx.LEFT | wx.BOTTOM | wx.RIGHT
                        | wx.ALIGN_CENTER_VERTICAL),
                  border=self.BORDER)
        sizer.Add(self.connection_ctrl,
                  flag=(wx.LEFT | wx.BOTTOM | wx.RIGHT
                        | wx.ALIGN_CENTER_VERTICAL),
                  border=self.BORDER)
        longest_machine = max(machine_registry.get_all_names(), key=len)
        longest_state = max((STATE_ERROR, STATE_INITIALIZING, STATE_RUNNING),
                            key=len)
        longest_status = '%s: %s' % (longest_machine, longest_state)
        self.machine_status_text = wx.StaticText(self, label=longest_status)
        sizer.Add(self.machine_status_text,
                  flag=wx.BOTTOM | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL,
                  border=self.BORDER)
        refresh_bitmap = wx.Bitmap(self.REFRESH_IMAGE_FILE, wx.BITMAP_TYPE_PNG)
        self.reconnect_button = wx.BitmapButton(self, bitmap=refresh_bitmap)
        sizer.Add(self.reconnect_button,
                  flag=wx.BOTTOM | wx.RIGHT | wx.ALIGN_CENTER_VERTICAL,
                  border=self.BORDER)
        self.machine_status_sizer = sizer
        global_sizer.Add(sizer)
        self.SetSizer(global_sizer)
        global_sizer.Fit(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: app.reset_machine(self.steno_engine, self.config))

        try:
            with open(config.target_file, 'rb') as f:
                self.config.load(f)
        except InvalidConfigurationError as e:
            self._show_alert(unicode(e))
            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))

        while True:
            try:
                app.init_engine(self.steno_engine, self.config)
                break
            except InvalidConfigurationError as e:
                self._show_alert(unicode(e))
                dlg = ConfigurationDialog(self.steno_engine,
                                          self.config,
                                          parent=self)
                ret = dlg.ShowModal()
                if ret == wx.ID_CANCEL:
                    self._quit()
                    return

        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)

    def consume_command(self, command):
        # The first commands can be used whether plover is active or not.
        if command == self.COMMAND_RESUME:
            wx.CallAfter(self.steno_engine.set_is_running, True)
            return True
        elif command == self.COMMAND_TOGGLE:
            wx.CallAfter(self.steno_engine.set_is_running,
                         not self.steno_engine.is_running)
            return True
        elif command == self.COMMAND_QUIT:
            wx.CallAfter(self._quit)
            return True

        if not self.steno_engine.is_running:
            return False

        # These commands can only be run when plover is active.
        if command == self.COMMAND_SUSPEND:
            wx.CallAfter(self.steno_engine.set_is_running, False)
            return True
        elif command == self.COMMAND_CONFIGURE:
            wx.CallAfter(self._show_config_dialog)
            return True
        elif command == self.COMMAND_FOCUS:

            def f():
                self.Raise()
                self.Iconize(False)

            wx.CallAfter(f)
            return True
        elif command == self.COMMAND_ADD_TRANSLATION:
            wx.CallAfter(plover.gui.add_translation.Show, self,
                         self.steno_engine, self.config)
            return True
        elif command == self.COMMAND_LOOKUP:
            wx.CallAfter(plover.gui.lookup.Show, self, self.steno_engine,
                         self.config)
            return True

        return False

    def _update_status(self, state):
        if state:
            machine_name = machine_registry.resolve_alias(
                self.config.get_machine_type())
            self.machine_status_text.SetLabel('%s: %s' % (machine_name, state))
            self.reconnect_button.Show(state == STATE_ERROR)
            self.spinner.Show(state == STATE_INITIALIZING)
            self.connection_ctrl.Show(state != STATE_INITIALIZING)
            if state == STATE_INITIALIZING:
                self.spinner.Play()
            else:
                self.spinner.Stop()
            if state == STATE_RUNNING:
                self.connection_ctrl.SetBitmap(self.connected_bitmap)
            elif state == STATE_ERROR:
                self.connection_ctrl.SetBitmap(self.disconnected_bitmap)
            self.machine_status_sizer.Layout()
        if self.steno_engine.machine:
            self.status_button.Enable()
            if self.steno_engine.is_running:
                self.status_button.SetBitmapLabel(self.on_bitmap)
                self.SetTitle("%s: %s" % (self.TITLE, self.RUNNING_MESSAGE))
            else:
                self.status_button.SetBitmapLabel(self.off_bitmap)
                self.SetTitle("%s: %s" % (self.TITLE, self.STOPPED_MESSAGE))
        else:
            self.status_button.Disable()
            self.status_button.SetBitmapLabel(self.off_bitmap)
            self.SetTitle("%s: %s" % (self.TITLE, self.ERROR_MESSAGE))

    def _quit(self, event=None):
        if self.steno_engine:
            self.steno_engine.destroy()
        self.Destroy()

    def _toggle_steno_engine(self, event=None):
        """Called when the status button is clicked."""
        self.steno_engine.set_is_running(not self.steno_engine.is_running)

    def _show_config_dialog(self, event=None):
        dlg = ConfigurationDialog(self.steno_engine, self.config, parent=self)
        dlg.Show()

    def _show_about_dialog(self, event=None):
        """Called when the About... button is clicked."""
        info = wx.AboutDialogInfo()
        info.Name = __software_name__
        info.Version = __version__
        info.Copyright = __copyright__
        info.Description = __long_description__
        info.WebSite = __url__
        info.Developers = __credits__
        info.License = __license__
        wx.AboutBox(info)

    def _show_alert(self, message):
        alert_dialog = wx.MessageDialog(self, message, self.ALERT_DIALOG_TITLE,
                                        wx.OK | wx.ICON_INFORMATION)
        alert_dialog.ShowModal()
        alert_dialog.Destroy()

    def on_move(self, event):
        pos = self.GetScreenPositionTuple()
        self.config.set_main_frame_x(pos[0])
        self.config.set_main_frame_y(pos[1])
        event.Skip()
Example #29
0
TOP_DIR = os.path.dirname(WIN_DIR)
NULL = open(os.devnull, 'r+b')
TEMP_DIR = r'C:\Temp'
PROG_DIR = r'C:\Progs'


sys.path.insert(0, TOP_DIR)
os.chdir(TOP_DIR)


from plover import (
    __name__ as __software_name__,
    __version__,
)

APPNAME = __software_name__.capitalize()
VERSION = __version__
ICON = os.path.join(TOP_DIR, __software_name__, 'assets', '%s.ico' % __software_name__)


if sys.stdout.isatty() and not sys.platform.startswith('win32'):

    def info(fmt, *args):
        s = fmt % args
        print '' + s + ''

else:

    def info(fmt, *args):
        s = fmt % args
        print s
Example #30
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)
Example #31
0
 def show_message(self, message,
                  icon=QSystemTrayIcon.Information,
                  timeout=10000):
     self._trayicon.showMessage(__software_name__.capitalize(),
                                message, icon, timeout)
Example #32
0
from plover import log, __name__ as __software_name__
import pynotify
import logging


pynotify.init(__software_name__.capitalize())



class DbusNotificationHandler(logging.Handler):
    """ Handler using DBus notifications to show messages. """

    def __init__(self):
        super(DbusNotificationHandler, self).__init__()
        self.setLevel(log.WARNING)
        self.setFormatter(logging.Formatter('%(levelname)s: %(message)s'))

    def emit(self, record):
        level = record.levelno
        message = self.format(record)
        if level <= log.INFO:
            timeout = 10
            urgency = 0
        elif level <= log.WARNING:
            timeout = 15
            urgency = 1
        else:
            timeout = 0
            urgency = 2
        n = pynotify.Notification(__software_name__.capitalize(), message)
Example #33
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)
Example #34
0
import os
import logging

from plyer import notification

from plover import log, __name__ as __software_name__
from plover.oslayer.config import ASSETS_DIR, PLATFORM

APPNAME = __software_name__.capitalize()

if PLATFORM == 'win':
    APPICON = os.path.join(ASSETS_DIR, 'plover.ico')
else:
    APPICON = os.path.join(ASSETS_DIR, 'plover_32x32.png')


class PlyerNotificationHandler(logging.Handler):
    """ Handler using Plyer's notifications to show messages. """
    def __init__(self):
        super().__init__()
        self.setLevel(log.WARNING)
        self.setFormatter(
            log.NoExceptionTracebackFormatter('%(levelname)s: %(message)s'))

    def emit(self, record):
        level = record.levelno
        message = self.format(record)
        if message.endswith('\n'):
            message = message[:-1]
        if level <= log.INFO:
            timeout = 10
Example #35
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)
Example #36
0
        with open(version_file, 'w') as fp:
            fp.write('\n'.join(contents))


setup_requires = []
options = {}
kwargs = {}

if sys.platform.startswith('darwin'):
    setup_requires.append('py2app')
    options['py2app'] = {
        'argv_emulation': False,
        'iconfile': 'osx/plover.icns',
        'resources': 'plover/assets/',
        'plist': {
            'CFBundleName': __software_name__.capitalize(),
            'CFBundleShortVersionString': __version__,
            'CFBundleVersion': __version__,
            'CFBundleIdentifier': 'org.openstenoproject.plover',
            'NSHumanReadableCopyright': __copyright__,
            'CFBundleDevelopmentRegion': 'English',
            }
        }
    # Py2app will not look at entry_points.
    kwargs['app'] = 'launch.py',

if sys.platform.startswith('win32'):
    setup_requires.append('PyInstaller==3.1.1')

setup_requires.extend(('pytest-runner', 'pytest'))
options['aliases'] = {
Example #37
0
from plover import log, __name__ as __software_name__
import pynotify
import logging

pynotify.init(__software_name__.capitalize())


class DbusNotificationHandler(logging.Handler):
    """ Handler using DBus notifications to show messages. """
    def __init__(self):
        super(DbusNotificationHandler, self).__init__()
        self.setLevel(log.WARNING)
        self.setFormatter(
            log.NoExceptionTracebackFormatter('%(levelname)s: %(message)s'))

    def emit(self, record):
        level = record.levelno
        message = self.format(record)
        if level <= log.INFO:
            timeout = 10
            urgency = 0
        elif level <= log.WARNING:
            timeout = 15
            urgency = 1
        else:
            timeout = 0
            urgency = 2
        n = pynotify.Notification(__software_name__.capitalize(), message)
        n.set_timeout(timeout * 1000)
        n.set_urgency(urgency)
        n.show()