Example #1
0
 def start_capture(self):
     """Begin listening for output from the stenotype machine."""
     self._initializing()
     try:
         self._keyboard_capture = KeyboardCapture()
         self._keyboard_capture.key_down = self._key_down
         self._keyboard_capture.key_up = self._key_up
         self._suppress()
         self._keyboard_capture.start()
     except:
         self._error()
         raise
     self._ready()
Example #2
0
 def start_capture(self):
     """Begin listening for output from the stenotype machine."""
     self._released_keys.clear()
     self._last_stroke_key_down_count = 0
     self._initializing()
     try:
         self._keyboard_capture = KeyboardCapture()
         self._keyboard_capture.key_down = self._key_down
         self._keyboard_capture.key_up = self._key_up
         self._keyboard_capture.start()
     except:
         self._error()
         raise
     self._ready()
Example #3
0
class EditKeysDialog(wx.Dialog):
    def __init__(self, parent, action, keys):
        super(EditKeysDialog, self).__init__(parent)
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        instructions = wx.StaticText(
            self, label='Press on the key you want to add/remove.')
        self.sizer.Add(instructions,
                       border=UI_BORDER,
                       flag=wx.ALL | wx.ALIGN_CENTER_HORIZONTAL)
        self.message = wx.StaticText(self)
        self.sizer.Add(self.message,
                       border=UI_BORDER,
                       flag=wx.ALL | wx.ALIGN_CENTER_HORIZONTAL)
        buttons = self.CreateButtonSizer(wx.OK | wx.CANCEL)
        self.sizer.Add(buttons,
                       border=UI_BORDER,
                       flag=wx.ALL | wx.ALIGN_CENTER_HORIZONTAL)
        self.SetSizer(self.sizer)
        self.sizer.Fit(self)
        self.action = action
        self.keys = set(keys)
        self.original_keys = self.keys.copy()
        self.capture = KeyboardCapture(KeyboardCapture.SUPPORTED_KEYS)
        self.capture.key_down = lambda key: wx.CallAfter(
            self.on_capture_key, key)

    def ShowModal(self):
        self.update_message()
        self.capture.start()
        self.capture.suppress_keyboard(True)
        try:
            code = super(EditKeysDialog, self).ShowModal()
        finally:
            self.capture.suppress_keyboard(False)
        self.capture.cancel()
        return code

    def update_message(self):
        message = '\nKeys for %s: ' % self.action
        message += ' '.join(sorted(self.keys)) if self.keys else 'None'
        message += '\n\nChanges: '
        changes = []
        for key in sorted(self.keys.union(self.original_keys)):
            if key not in self.original_keys:
                changes.append('+' + key)
            elif key not in self.keys:
                changes.append('-' + key)
        message += ' '.join(changes) if changes else 'None'
        message += '\n'
        self.message.SetLabelText(message)
        self.sizer.Fit(self)
        self.sizer.Layout()

    def on_capture_key(self, key):
        if key in self.keys:
            self.keys.remove(key)
        else:
            self.keys.add(key)
        self.update_message()
Example #4
0
class EditKeysDialog(wx.Dialog):

    def __init__(self, parent, action, keys):
        super(EditKeysDialog, self).__init__(parent)
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        instructions = wx.StaticText(self, label='Press on the key you want to add/remove.')
        self.sizer.Add(instructions, border=UI_BORDER, flag=wx.ALL|wx.ALIGN_CENTER_HORIZONTAL)
        self.message = wx.StaticText(self)
        self.sizer.Add(self.message, border=UI_BORDER, flag=wx.ALL|wx.ALIGN_CENTER_HORIZONTAL)
        buttons = self.CreateButtonSizer(wx.OK|wx.CANCEL)
        self.sizer.Add(buttons, border=UI_BORDER, flag=wx.ALL|wx.ALIGN_CENTER_HORIZONTAL)
        self.SetSizer(self.sizer)
        self.sizer.Fit(self)
        self.action = action
        self.keys = set(keys)
        self.original_keys = self.keys.copy()
        self.capture = KeyboardCapture(KeyboardCapture.SUPPORTED_KEYS)
        self.capture.key_down = lambda key: wx.CallAfter(self.on_capture_key, key)

    def ShowModal(self):
        self.update_message()
        self.capture.start()
        self.capture.suppress_keyboard(True)
        try:
            code = super(EditKeysDialog, self).ShowModal()
        finally:
            self.capture.suppress_keyboard(False)
        self.capture.cancel()
        return code

    def update_message(self):
        message = '\nKeys for %s: ' % self.action
        message += ' '.join(sorted(self.keys)) if self.keys else 'None'
        message += '\n\nChanges: '
        changes = []
        for key in sorted(self.keys.union(self.original_keys)):
            if key not in self.original_keys:
                changes.append('+' + key)
            elif key not in self.keys:
                changes.append('-' + key)
        message += ' '.join(changes) if changes else 'None'
        message += '\n'
        self.message.SetLabelText(message)
        self.sizer.Fit(self)
        self.sizer.Layout()

    def on_capture_key(self, key):
        if key in self.keys:
            self.keys.remove(key)
        else:
            self.keys.add(key)
        self.update_message()
Example #5
0
 def __init__(self, parent, action, keys):
     super(EditKeysDialog, self).__init__(parent)
     self.sizer = wx.BoxSizer(wx.VERTICAL)
     sizer_flags = wx.SizerFlags().Border(wx.ALL, UI_BORDER).Center()
     instructions = wx.StaticText(
         self, label='Press on the key you want to add/remove.')
     self.sizer.AddF(instructions, sizer_flags)
     self.message = wx.StaticText(self)
     self.sizer.AddF(self.message, sizer_flags)
     buttons = self.CreateButtonSizer(wx.OK | wx.CANCEL)
     clear_button = wx.Button(self, id=wx.ID_CLEAR)
     clear_button.Bind(wx.EVT_BUTTON, self.on_clear)
     buttons.InsertF(0, clear_button, sizer_flags.Left())
     self.sizer.AddF(buttons, sizer_flags.Expand())
     self.SetSizerAndFit(self.sizer)
     self.action = action
     self.keys = set(keys)
     self.original_keys = self.keys.copy()
     self.capture = KeyboardCapture()
     self.capture.key_down = lambda key: wx.CallAfter(
         self.on_capture_key, key)
class EditKeysDialog(wx.Dialog):

    def __init__(self, parent, action, keys):
        super(EditKeysDialog, self).__init__(parent)
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        sizer_flags = wx.SizerFlags().Border(wx.ALL, UI_BORDER).Center()
        instructions = wx.StaticText(self, label='Press on the key you want to add/remove.')
        self.sizer.AddF(instructions, sizer_flags)
        self.message = wx.StaticText(self)
        self.sizer.AddF(self.message, sizer_flags)
        buttons = self.CreateButtonSizer(wx.OK|wx.CANCEL)
        clear_button = wx.Button(self, id=wx.ID_CLEAR)
        clear_button.Bind(wx.EVT_BUTTON, self.on_clear)
        buttons.InsertF(0, clear_button, sizer_flags.Left())
        self.sizer.AddF(buttons, sizer_flags.Expand())
        self.SetSizerAndFit(self.sizer)
        self.action = action
        self.keys = set(keys)
        self.original_keys = self.keys.copy()
        self.capture = KeyboardCapture()
        self.capture.key_down = lambda key: wx.CallAfter(self.on_capture_key, key)

    def ShowModal(self):
        self.update_message()
        self.capture.start()
        try:
            # Prevent dialog from stealing some key events.
            self.capture.suppress_keyboard(('space', 'Escape', 'Return', 'Tab'))
            code = super(EditKeysDialog, self).ShowModal()
        finally:
            self.capture.cancel()
        return code

    def update_message(self):
        message = '\nKeys for %s: ' % self.action
        message += ' '.join(sorted(self.keys)) if self.keys else 'None'
        message += '\n\nChanges: '
        changes = []
        for key in sorted(self.keys.union(self.original_keys)):
            if key not in self.original_keys:
                changes.append('+' + key)
            elif key not in self.keys:
                changes.append('-' + key)
        message += ' '.join(changes) if changes else 'None'
        message += '\n'
        self.message.SetLabelText(message)
        self.sizer.Fit(self)
        self.sizer.Layout()

    def on_clear(self, event):
        self.keys.clear()
        self.update_message()

    def on_capture_key(self, key):
        if key in self.keys:
            self.keys.remove(key)
        else:
            self.keys.add(key)
        self.update_message()
Example #7
0
 def start_capture(self):
     """Begin listening for output from the stenotype machine."""
     self._initializing()
     try:
         self._keyboard_capture = KeyboardCapture()
         self._keyboard_capture.key_down = self._key_down
         self._keyboard_capture.key_up = self._key_up
         self._suppress()
         self._keyboard_capture.start()
     except:
         self._error()
         raise
     self._ready()
Example #8
0
 def start_capture(self):
     """Begin listening for output from the stenotype machine."""
     self._released_keys.clear()
     self._last_stroke_key_down_count = 0
     self._initializing()
     try:
         self._keyboard_capture = KeyboardCapture()
         self._keyboard_capture.key_down = self._key_down
         self._keyboard_capture.key_up = self._key_up
         self._keyboard_capture.start()
     except:
         self._error()
         raise
     self._ready()
Example #9
0
 def __init__(self, parent, action, keys):
     super(EditKeysDialog, self).__init__(parent)
     self.sizer = wx.BoxSizer(wx.VERTICAL)
     instructions = wx.StaticText(
         self, label='Press on the key you want to add/remove.')
     self.sizer.Add(instructions,
                    border=UI_BORDER,
                    flag=wx.ALL | wx.ALIGN_CENTER_HORIZONTAL)
     self.message = wx.StaticText(self)
     self.sizer.Add(self.message,
                    border=UI_BORDER,
                    flag=wx.ALL | wx.ALIGN_CENTER_HORIZONTAL)
     buttons = self.CreateButtonSizer(wx.OK | wx.CANCEL)
     self.sizer.Add(buttons,
                    border=UI_BORDER,
                    flag=wx.ALL | wx.ALIGN_CENTER_HORIZONTAL)
     self.SetSizer(self.sizer)
     self.sizer.Fit(self)
     self.action = action
     self.keys = set(keys)
     self.original_keys = self.keys.copy()
     self.capture = KeyboardCapture(KeyboardCapture.SUPPORTED_KEYS)
     self.capture.key_down = lambda key: wx.CallAfter(
         self.on_capture_key, key)
Example #10
0
 def __init__(self, parent, action, keys):
     super(EditKeysDialog, self).__init__(parent)
     self.sizer = wx.BoxSizer(wx.VERTICAL)
     instructions = wx.StaticText(self, label='Press on the key you want to add/remove.')
     self.sizer.Add(instructions, border=UI_BORDER, flag=wx.ALL|wx.ALIGN_CENTER_HORIZONTAL)
     self.message = wx.StaticText(self)
     self.sizer.Add(self.message, border=UI_BORDER, flag=wx.ALL|wx.ALIGN_CENTER_HORIZONTAL)
     buttons = self.CreateButtonSizer(wx.OK|wx.CANCEL)
     self.sizer.Add(buttons, border=UI_BORDER, flag=wx.ALL|wx.ALIGN_CENTER_HORIZONTAL)
     self.SetSizer(self.sizer)
     self.sizer.Fit(self)
     self.action = action
     self.keys = set(keys)
     self.original_keys = self.keys.copy()
     self.capture = KeyboardCapture()
     self.capture.key_down = lambda key: wx.CallAfter(self.on_capture_key, key)
 def __init__(self, parent, action, keys):
     super(EditKeysDialog, self).__init__(parent)
     self.sizer = wx.BoxSizer(wx.VERTICAL)
     sizer_flags = wx.SizerFlags().Border(wx.ALL, UI_BORDER).Center()
     instructions = wx.StaticText(self, label='Press on the key you want to add/remove.')
     self.sizer.AddF(instructions, sizer_flags)
     self.message = wx.StaticText(self)
     self.sizer.AddF(self.message, sizer_flags)
     buttons = self.CreateButtonSizer(wx.OK|wx.CANCEL)
     clear_button = wx.Button(self, id=wx.ID_CLEAR)
     clear_button.Bind(wx.EVT_BUTTON, self.on_clear)
     buttons.InsertF(0, clear_button, sizer_flags.Left())
     self.sizer.AddF(buttons, sizer_flags.Expand())
     self.SetSizerAndFit(self.sizer)
     self.action = action
     self.keys = set(keys)
     self.original_keys = self.keys.copy()
     self.capture = KeyboardCapture()
     self.capture.key_down = lambda key: wx.CallAfter(self.on_capture_key, key)
Example #12
0
 def __init__(self, params):
     """Monitor the keyboard's events."""
     StenotypeBase.__init__(self)
     self.arpeggiate = params['arpeggiate']
     self.keymap = params['keymap'].to_dict()
     self._arpeggiate_key = None
     for key, mapping in self.keymap.items():
         if 'no-op' == mapping:
             self.keymap[key] = None
         if 'arpeggiate' == mapping:
             if self.arpeggiate:
                 self.keymap[key] = None
                 self._arpeggiate_key = key
             else:
                 # Don't suppress arpeggiate key if it's not used.
                 del self.keymap[key]
     self._down_keys = set()
     self._released_keys = set()
     self._keyboard_capture = KeyboardCapture()
     self._keyboard_capture.key_down = self._key_down
     self._keyboard_capture.key_up = self._key_up
     self._last_stroke_key_down_count = 0
Example #13
0
class Keyboard(StenotypeBase):
    """Standard stenotype interface for a computer keyboard.

    This class implements the three methods necessary for a standard
    stenotype interface: start_capture, stop_capture, and
    add_callback.

    """

    KEYS_LAYOUT = KeyboardCapture.SUPPORTED_KEYS_LAYOUT
    ACTIONS = ('arpeggiate',)

    def __init__(self, params):
        """Monitor the keyboard's events."""
        super(Keyboard, self).__init__()
        self._arpeggiate = params['arpeggiate']
        self._is_suppressed = False
        self._bindings = {}
        self._down_keys = set()
        self._released_keys = set()
        self._keyboard_capture = None
        self._last_stroke_key_down_count = 0
        self._stroke_key_down_count = 0
        self._update_bindings()

    def _suppress(self):
        if self._keyboard_capture is None:
            return
        suppressed_keys = self._bindings.keys() if self._is_suppressed else ()
        self._keyboard_capture.suppress_keyboard(suppressed_keys)

    def _update_bindings(self):
        self._bindings = dict(self.keymap.get_bindings())
        for key, mapping in list(self._bindings.items()):
            if 'no-op' == mapping:
                self._bindings[key] = None
            elif 'arpeggiate' == mapping:
                if self._arpeggiate:
                    self._bindings[key] = None
                    self._arpeggiate_key = key
                else:
                    # Don't suppress arpeggiate key if it's not used.
                    del self._bindings[key]
        self._suppress()

    def set_keymap(self, keymap):
        super(Keyboard, self).set_keymap(keymap)
        self._update_bindings()

    def start_capture(self):
        """Begin listening for output from the stenotype machine."""
        self._released_keys.clear()
        self._stroke_key_down_count = 0
        self._initializing()
        try:
            self._keyboard_capture = KeyboardCapture()
            self._keyboard_capture.key_down = self._key_down
            self._keyboard_capture.key_up = self._key_up
            self._suppress()
            self._keyboard_capture.start()
        except:
            self._error()
            raise
        self._ready()

    def stop_capture(self):
        """Stop listening for output from the stenotype machine."""
        if self._keyboard_capture is not None:
            self._is_suppressed = False
            self._suppress()
            self._keyboard_capture.cancel()
            self._keyboard_capture = None
        self._stopped()

    def set_suppression(self, enabled):
        self._is_suppressed = enabled
        self._suppress()

    def suppress_last_stroke(self, send_backspaces):
        send_backspaces(self._last_stroke_key_down_count)
        self._last_stroke_key_down_count = 0

    def _key_down(self, key):
        """Called when a key is pressed."""
        assert key is not None
        if key in self._bindings:
            self._stroke_key_down_count += 1
        steno_key = self._bindings.get(key)
        if steno_key is not None:
            self._down_keys.add(steno_key)

    def _key_up(self, key):
        """Called when a key is released."""
        assert key is not None
        steno_key = self._bindings.get(key)
        if steno_key is not None:
            # Process the newly released key.
            self._released_keys.add(steno_key)
            # Remove invalid released keys.
            self._released_keys = self._released_keys.intersection(self._down_keys)

        # A stroke is complete if all pressed keys have been released.
        # If we are in arpeggiate mode then only send stroke when spacebar is pressed.
        send_strokes = bool(self._down_keys and
                            self._down_keys == self._released_keys)
        if self._arpeggiate:
            send_strokes &= key == self._arpeggiate_key
        if send_strokes:
            self._last_stroke_key_down_count = self._stroke_key_down_count
            steno_keys = list(self._down_keys)
            self._down_keys.clear()
            self._released_keys.clear()
            self._stroke_key_down_count = 0
            self._notify(steno_keys)

    @classmethod
    def get_option_info(cls):
        bool_converter = lambda s: s == 'True'
        return {
            'arpeggiate': (False, bool_converter),
        }
Example #14
0
class Keyboard(StenotypeBase):
    """Standard stenotype interface for a computer keyboard.

    This class implements the three methods necessary for a standard
    stenotype interface: start_capture, stop_capture, and
    add_callback.

    """

    KEYS_LAYOUT = KeyboardCapture.SUPPORTED_KEYS_LAYOUT
    ACTIONS = ('arpeggiate',)

    def __init__(self, params):
        """Monitor the keyboard's events."""
        super(Keyboard, self).__init__()
        self._arpeggiate = params['arpeggiate']
        self._is_suppressed = False
        # Currently held keys.
        self._down_keys = set()
        # All keys part of the stroke.
        self._stroke_keys = set()
        self._keyboard_capture = None
        self._last_stroke_key_down_count = 0
        self._stroke_key_down_count = 0
        self._update_bindings()
        self._key_timeout = None

    def _suppress(self):
        if self._keyboard_capture is None:
            return
        suppressed_keys = self._bindings.keys() if self._is_suppressed else ()
        self._keyboard_capture.suppress_keyboard(suppressed_keys)

    def _update_bindings(self):
        self._arpeggiate_key = None
        self._bindings = dict(self.keymap.get_bindings())
        for key, mapping in list(self._bindings.items()):
            if 'no-op' == mapping:
                self._bindings[key] = None
            elif 'arpeggiate' == mapping:
                if self._arpeggiate:
                    self._bindings[key] = None
                    self._arpeggiate_key = key
                else:
                    # Don't suppress arpeggiate key if it's not used.
                    del self._bindings[key]
        self._suppress()

    def set_keymap(self, keymap):
        super(Keyboard, self).set_keymap(keymap)
        self._update_bindings()

    def start_capture(self):
        """Begin listening for output from the stenotype machine."""
        self._initializing()
        try:
            self._keyboard_capture = KeyboardCapture()
            self._keyboard_capture.key_down = self._key_down
            self._keyboard_capture.key_up = self._key_up
            self._suppress()
            self._keyboard_capture.start()
        except:
            self._error()
            raise
        self._ready()

    def stop_capture(self):
        """Stop listening for output from the stenotype machine."""
        if self._keyboard_capture is not None:
            self._is_suppressed = False
            self._suppress()
            self._keyboard_capture.cancel()
            self._keyboard_capture = None
        self._stopped()

    def set_suppression(self, enabled):
        self._is_suppressed = enabled
        self._suppress()

    def suppress_last_stroke(self, send_backspaces):
        send_backspaces(self._last_stroke_key_down_count)
        self._last_stroke_key_down_count = 0

    def _key_down(self, key):
        """Called when a key is pressed."""
        assert key is not None
        self._stroke_key_down_count += 1
        self._stroke_keys.add(key)
        #
        # HACK emit per keydown
        if key not in self._down_keys:
            steno_keys = set(self._bindings.get(k) for k in self._stroke_keys)
            steno_keys -= {None}
            if steno_keys:  self._notify(steno_keys)
        self._down_keys.add(key)
        #
        # pause = respect keyups
        if self._key_timeout: self._key_timeout.cancel()

    def timeout_keys(self):
        """Keys have been held released for a while"""
        print('Timing out {} to {}'.format(self._stroke_keys, self._down_keys))
        self._stroke_keys = set(self._down_keys)
        steno_keys = set(self._bindings.get(k) for k in self._stroke_keys)
        steno_keys -= {None}
        if steno_keys:  self._notify(steno_keys)

    def _key_up(self, key):
        """Called when a key is released."""
        assert key is not None
        self._down_keys.discard(key)
        #
        # pause = respect keyups
        if self._key_timeout: self._key_timeout.cancel()
        # A stroke is complete if all pressed keys have been released,
        # and — when arpeggiate mode is enabled — the arpeggiate key
        # is part of it.
        if (
            self._down_keys or
            not self._stroke_keys or
            (self._arpeggiate and self._arpeggiate_key not in self._stroke_keys)
        ):
            # TODO configure timeout length
            self._key_timeout = Timer(0.5, self.timeout_keys).start()
            return
        self._last_stroke_key_down_count = self._stroke_key_down_count
        steno_keys = set(self._bindings.get(k) for k in self._stroke_keys)
        steno_keys -= {None}
        if steno_keys:
            steno_keys.add('!')
            self._notify(steno_keys)
        self._stroke_keys.clear()
        self._stroke_key_down_count = 0

    @classmethod
    def get_option_info(cls):
        bool_converter = lambda s: s == 'True'
        return {
            'arpeggiate': (False, bool_converter),
        }
Example #15
0
class Stenotype(StenotypeBase):
    """Standard stenotype interface for a computer keyboard.

    This class implements the three methods necessary for a standard
    stenotype interface: start_capture, stop_capture, and
    add_callback.

    """

    KEYS_LAYOUT = KeyboardCapture.SUPPORTED_KEYS_LAYOUT
    ACTIONS = StenotypeBase.ACTIONS + ('arpeggiate', )

    def __init__(self, params):
        """Monitor the keyboard's events."""
        super(Stenotype, self).__init__()
        self.arpeggiate = params['arpeggiate']
        self._bindings = {}
        self._down_keys = set()
        self._released_keys = set()
        self._keyboard_capture = None
        self._last_stroke_key_down_count = 0
        self._update_bindings()

    def _update_bindings(self):
        self._bindings = dict(self.keymap.get_bindings())
        for key, mapping in self._bindings.items():
            if 'no-op' == mapping:
                self._bindings[key] = None
            elif 'arpeggiate' == mapping:
                if self.arpeggiate:
                    self._bindings[key] = None
                    self._arpeggiate_key = key
                else:
                    # Don't suppress arpeggiate key if it's not used.
                    del self._bindings[key]

    def set_mappings(self, mappings):
        super(Stenotype, self).set_mappings(mappings)
        self._update_bindings()

    def start_capture(self):
        """Begin listening for output from the stenotype machine."""
        self._released_keys.clear()
        self._last_stroke_key_down_count = 0
        self._initializing()
        try:
            self._keyboard_capture = KeyboardCapture()
            self._keyboard_capture.key_down = self._key_down
            self._keyboard_capture.key_up = self._key_up
            self._keyboard_capture.start()
        except:
            self._error()
            raise
        self._ready()

    def stop_capture(self):
        """Stop listening for output from the stenotype machine."""
        if self._keyboard_capture is not None:
            self._keyboard_capture.cancel()
            self._keyboard_capture = None
        self._stopped()

    def set_suppression(self, enabled):
        suppressed_keys = self._bindings.keys() if enabled else ()
        self._keyboard_capture.suppress_keyboard(suppressed_keys)

    def suppress_last_stroke(self, send_backspaces):
        send_backspaces(self._last_stroke_key_down_count)

    def _key_down(self, key):
        """Called when a key is pressed."""
        assert key is not None
        if key in self._bindings:
            self._last_stroke_key_down_count += 1
        steno_key = self._bindings.get(key)
        if steno_key is not None:
            self._down_keys.add(steno_key)

    def _key_up(self, key):
        """Called when a key is released."""
        assert key is not None
        steno_key = self._bindings.get(key)
        if steno_key is not None:
            # Process the newly released key.
            self._released_keys.add(steno_key)
            # Remove invalid released keys.
            self._released_keys = self._released_keys.intersection(
                self._down_keys)

        # A stroke is complete if all pressed keys have been released.
        # If we are in arpeggiate mode then only send stroke when spacebar is pressed.
        send_strokes = bool(self._down_keys
                            and self._down_keys == self._released_keys)
        if self.arpeggiate:
            send_strokes &= key == self._arpeggiate_key
        if send_strokes:
            steno_keys = list(self._down_keys)
            if steno_keys:
                self._down_keys.clear()
                self._released_keys.clear()
                self._notify(steno_keys)
            self._last_stroke_key_down_count = 0

    @classmethod
    def get_option_info(cls):
        bool_converter = lambda s: s == 'True'
        return {
            'arpeggiate': (False, bool_converter),
        }
Example #16
0
class EditKeysDialog(wx.Dialog):
    def __init__(self, parent, action, keys):
        super(EditKeysDialog, self).__init__(parent)
        self.sizer = wx.BoxSizer(wx.VERTICAL)
        sizer_flags = wx.SizerFlags().Border(wx.ALL, UI_BORDER).Center()
        instructions = wx.StaticText(
            self, label='Press on the key you want to add/remove.')
        self.sizer.AddF(instructions, sizer_flags)
        self.message = wx.StaticText(self)
        self.sizer.AddF(self.message, sizer_flags)
        buttons = self.CreateButtonSizer(wx.OK | wx.CANCEL)
        clear_button = wx.Button(self, id=wx.ID_CLEAR)
        clear_button.Bind(wx.EVT_BUTTON, self.on_clear)
        buttons.InsertF(0, clear_button, sizer_flags.Left())
        self.sizer.AddF(buttons, sizer_flags.Expand())
        self.SetSizerAndFit(self.sizer)
        self.action = action
        self.keys = set(keys)
        self.original_keys = self.keys.copy()
        self.capture = KeyboardCapture()
        self.capture.key_down = lambda key: wx.CallAfter(
            self.on_capture_key, key)

    def ShowModal(self):
        self.update_message()
        self.capture.start()
        try:
            # Prevent dialog from stealing some key events.
            self.capture.suppress_keyboard(
                ('space', 'Escape', 'Return', 'Tab'))
            code = super(EditKeysDialog, self).ShowModal()
        finally:
            self.capture.cancel()
        return code

    def update_message(self):
        message = '\nKeys for %s: ' % self.action
        message += ' '.join(sorted(self.keys)) if self.keys else 'None'
        message += '\n\nChanges: '
        changes = []
        for key in sorted(self.keys.union(self.original_keys)):
            if key not in self.original_keys:
                changes.append('+' + key)
            elif key not in self.keys:
                changes.append('-' + key)
        message += ' '.join(changes) if changes else 'None'
        message += '\n'
        self.message.SetLabelText(message)
        self.sizer.Fit(self)
        self.sizer.Layout()

    def on_clear(self, event):
        self.keys.clear()
        self.update_message()

    def on_capture_key(self, key):
        if key in self.keys:
            self.keys.remove(key)
        else:
            self.keys.add(key)
        self.update_message()
Example #17
0
class Stenotype(StenotypeBase):
    """Standard stenotype interface for a computer keyboard.

    This class implements the three methods necessary for a standard
    stenotype interface: start_capture, stop_capture, and
    add_callback.

    """

    def __init__(self, params):
        """Monitor the keyboard's events."""
        StenotypeBase.__init__(self)
        self.arpeggiate = params['arpeggiate']
        self.keymap = params['keymap'].to_dict()
        self._arpeggiate_key = None
        for key, mapping in self.keymap.items():
            if 'no-op' == mapping:
                self.keymap[key] = None
            if 'arpeggiate' == mapping:
                if self.arpeggiate:
                    self.keymap[key] = None
                    self._arpeggiate_key = key
                else:
                    # Don't suppress arpeggiate key if it's not used.
                    del self.keymap[key]
        self._down_keys = set()
        self._released_keys = set()
        self._keyboard_capture = KeyboardCapture()
        self._keyboard_capture.key_down = self._key_down
        self._keyboard_capture.key_up = self._key_up
        self._last_stroke_key_down_count = 0

    def start_capture(self):
        """Begin listening for output from the stenotype machine."""
        self._keyboard_capture.start()
        self._ready()

    def stop_capture(self):
        """Stop listening for output from the stenotype machine."""
        self._keyboard_capture.cancel()
        self._stopped()

    def set_suppression(self, enabled):
        suppressed_keys = self.keymap.keys() if enabled else ()
        self._keyboard_capture.suppress_keyboard(suppressed_keys)

    def suppress_last_stroke(self, send_backspaces):
        send_backspaces(self._last_stroke_key_down_count)

    def _key_down(self, key):
        """Called when a key is pressed."""
        assert key is not None
        if key in self.keymap:
            self._last_stroke_key_down_count += 1
        steno_key = self.keymap.get(key)
        if steno_key is not None:
            self._down_keys.add(steno_key)

    def _key_up(self, key):
        """Called when a key is released."""
        assert key is not None
        steno_key = self.keymap.get(key)
        if steno_key is not None:
            # Process the newly released key.
            self._released_keys.add(steno_key)
            # Remove invalid released keys.
            self._released_keys = self._released_keys.intersection(self._down_keys)

        # A stroke is complete if all pressed keys have been released.
        # If we are in arpeggiate mode then only send stroke when spacebar is pressed.
        send_strokes = bool(self._down_keys and
                            self._down_keys == self._released_keys)
        if self.arpeggiate:
            send_strokes &= key == self._arpeggiate_key
        if send_strokes:
            steno_keys = list(self._down_keys)
            if steno_keys:
                self._down_keys.clear()
                self._released_keys.clear()
                self._notify(steno_keys)
            self._last_stroke_key_down_count = 0

    @staticmethod
    def get_option_info():
        bool_converter = lambda s: s == 'True'
        keymap_converter = lambda s: Keymap.from_string(s)
        return {
            'arpeggiate': (False, bool_converter),
            'keymap':     (Keymap.default(), keymap_converter),
        }