Skip to content

Commit

Permalink
Merge pull request #97 from balshetzer/master
Browse files Browse the repository at this point in the history
Add real time dictionary updates
  • Loading branch information
balshetzer committed Jul 9, 2013
2 parents aee8132 + 5582117 commit efc6bcc
Show file tree
Hide file tree
Showing 13 changed files with 433 additions and 95 deletions.
2 changes: 1 addition & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ There is no package yet so an installation requires installing all dependencies.
run the following commands::

cd
sudo apt-get install python-xlib python-serial python-wxgtk2.8 python-pip
sudo apt-get install python-xlib python-serial python-wxgtk2.8 wmctrl python-pip
sudo pip install -U appdirs simplejson
wget https://github.com/plover/plover/archive/v2.3.1.tar.gz
tar -zxf v2.3.1.tar.gz
Expand Down
2 changes: 1 addition & 1 deletion plover/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

"""Plover: Open Source Stenography Software"""

__version__ = '2.3.1'
__version__ = '2.4.0'
__copyright__ = '(C) 2010-2011 Joshua Harlan Lifton'
__url__ = 'http://stenoknight.com/plover'
__download_url__ = 'https://github.com/plover/plover'
Expand Down
4 changes: 3 additions & 1 deletion plover/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,6 @@ class plus a hook to the application allows output to the screen and control
def __init__(self):
"""Creates and configures a single steno pipeline."""
self.subscribers = []
self.machine_status_subscribers = []
self.is_running = False
self.machine = None

Expand Down Expand Up @@ -182,6 +181,9 @@ def set_machine(self, machine):
def set_dictionary(self, d):
self.translator.set_dictionary(d)

def get_dictionary(self):
return self.translator.get_dictionary()

def set_is_running(self, value):
self.is_running = value
if self.is_running:
Expand Down
54 changes: 46 additions & 8 deletions plover/dictionary/base.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,26 @@
# Copyright (c) 2013 Hesky Fisher
# See LICENSE.txt for details.

# TODO: maybe move this code into the StenoDictionary itself. The current saver
# structure is odd and awkward.
# TODO: write tests for this file

"""Common elements to all dictionary formats."""

from os.path import join, splitext
import shutil
import threading

from plover.dictionary.json_dict import load_dictionary as json_loader
from plover.dictionary.rtfcre_dict import load_dictionary as rtfcre_loader
import plover.dictionary.json_dict as json_dict
import plover.dictionary.rtfcre_dict as rtfcre_dict
from plover.config import JSON_EXTENSION, RTF_EXTENSION, CONFIG_DIR
from plover.exception import DictionaryLoaderException

loaders = {
JSON_EXTENSION.lower(): json_loader,
RTF_EXTENSION.lower(): rtfcre_loader,
dictionaries = {
JSON_EXTENSION.lower(): json_dict,
RTF_EXTENSION.lower(): rtfcre_dict,
}


def load_dictionary(filename):
"""Load a dictionary from a file."""
# The dictionary path can be either absolute or relative to the
Expand All @@ -24,14 +29,47 @@ def load_dictionary(filename):
extension = splitext(path)[1].lower()

try:
loader = loaders[extension]
dict_type = dictionaries[extension]
except KeyError:
raise DictionaryLoaderException(
'Unsupported extension %s. Supported extensions: %s',
(extension, ', '.join(loaders.keys())))

loader = dict_type.load_dictionary

try:
with open(path, 'rb') as f:
return loader(f.read())
d = loader(f.read())
except IOError as e:
raise DictionaryLoaderException(unicode(e))

d.save = ThreadedSaver(d, filename, dict_type.save_dictionary)
return d

def save_dictionary(d, filename, saver):
# Write the new file to a temp location.
tmp = filename + '.tmp'
with open(tmp, 'wb') as fp:
saver(d, fp)

# Then move the new file to the final location.
shutil.move(tmp, filename)

class ThreadedSaver(object):
"""A callable that saves a dictionary in the background.
Also makes sure that there is only one active call at a time.
"""
def __init__(self, d, filename, saver):
self.d = d
self.filename = filename
self.saver = saver
self.lock = threading.Lock()

def __call__(self):
t = threading.Thread(target=self.save)
t.start()

def save(self):
with self.lock:
save_dictionary(self.d, self.filename, self.saver)
7 changes: 6 additions & 1 deletion plover/dictionary/json_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,4 +27,9 @@ def h(pairs):
except UnicodeDecodeError:
return json.loads(data, 'latin-1', object_pairs_hook=h)
except ValueError:
raise DictionaryLoaderException('Dictionary is not valid json.')
raise DictionaryLoaderException('Dictionary is not valid json.')

# TODO: test this
def save_dictionary(d, fp):
d = dict(('/'.join(k), v) for k, v in d.iteritems())
json.dump(d, fp, sort_keys=True, indent=0, separators=(',', ': '))
32 changes: 32 additions & 0 deletions plover/dictionary/rtfcre_dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,3 +239,35 @@ def load_dictionary(s):
if converted is not None:
d[steno] = converted
return StenoDictionary(d)

HEADER = ("{\\rtf1\\ansi{\\*\\cxrev100}\\cxdict{\\*\\cxsystem Plover}" +
"{\\stylesheet{\\s0 Normal;}}\n")

# TODO: test this
def save_dictionary(d, fp):
fp.write(HEADER)

for s, t in d.items():
s = '/'.join(s)

t = re.sub(r'{\.}', '{\\cxp. }', t)
t = re.sub(r'{!}', '{\\cxp! }', t)
t = re.sub(r'{\?}', '{\\cxp? }', t)
t = re.sub(r'{\,}', '{\\cxp, }', t)
t = re.sub(r'{:}', '{\\cxp: }', t)
t = re.sub(r'{;}', '{\\cxp; }', t)
t = re.sub(r'{\^}', '\\cxds ', t)
t = re.sub(r'{\^([^^}]*)}', '\\cxds \\1', t)
t = re.sub(r'{([^^}]*)\^}', '\\1\\cxds ', t)
t = re.sub(r'{\^([^^}]*)\^}', '\\cxds \\1\\cxds ', t)
t = re.sub(r'{-\|}', '\\cxfc ', t)
t = re.sub(r'{ }', ' ', t)
t = re.sub(r'{&([^}]+)}', '{\\cxfing \\1}', t)
t = re.sub(r'{#([^}]+)}', '\\{#\\1\\}', t)
t = re.sub(r'{PLOVER:([a-zA-Z]+)}', '\\{PLOVER:\\1\\}', t)
t = re.sub(r'\\"', '"', t)

entry = "{\\*\\cxs %s}%s\r\n" % (s, t)
fp.write(entry)

fp.write("}\n")
22 changes: 1 addition & 21 deletions plover/formatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ def __init__(self):
def set_output(self, output):
"""Set the output class."""
noop = lambda x: None
output_type = type(self).output_type
output_type = self.output_type
fields = output_type._fields
self._output = output_type(*[getattr(output, f, noop) for f in fields])

Expand Down Expand Up @@ -145,26 +145,6 @@ def _get_last_action(actions):
"""Return last action in actions if possible or return a blank action."""
return actions[-1] if actions else _Action()

def _undo(actions, output):
"""Send instructions to output to undo actions."""
for a in reversed(actions):
if a.text:
output.send_backspaces(len(a.text))
if a.replace:
output.send_string(a.replace)

def _render_actions(actions, output):
"""Send instructions to output to render new actions."""
for a in actions:
if a.replace:
output.send_backspaces(len(a.replace))
if a.text:
output.send_string(a.text)
if a.combo:
output.send_key_combination(a.combo)
if a.command:
output.send_engine_command(a.command)

class _Action(object):
"""A hybrid class that stores instructions and resulting state.
Expand Down
Loading

0 comments on commit efc6bcc

Please sign in to comment.