plover 5.0.0.dev2__py3-none-any.whl → 5.0.0.dev3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- plover/__init__.py +8 -7
- plover/__main__.py +1 -1
- plover/command/set_config.py +7 -4
- plover/config.py +177 -74
- plover/dictionary/base.py +21 -9
- plover/dictionary/helpers.py +6 -4
- plover/dictionary/json_dict.py +10 -12
- plover/dictionary/loading_manager.py +8 -10
- plover/dictionary/rtfcre_dict.py +46 -43
- plover/dictionary/rtfcre_parse.py +116 -100
- plover/engine.py +112 -96
- plover/exception.py +4 -1
- plover/formatting.py +198 -140
- plover/gui_none/add_translation.py +17 -16
- plover/gui_none/engine.py +1 -3
- plover/gui_none/main.py +2 -2
- plover/gui_qt/about_dialog.py +17 -17
- plover/gui_qt/add_translation_dialog.py +8 -9
- plover/gui_qt/add_translation_widget.py +66 -63
- plover/gui_qt/config_window.py +274 -154
- plover/gui_qt/console_widget.py +9 -13
- plover/gui_qt/dictionaries_widget.py +140 -119
- plover/gui_qt/dictionary_editor.py +60 -61
- plover/gui_qt/engine.py +3 -3
- plover/gui_qt/info_browser.py +50 -4
- plover/gui_qt/log_qt.py +3 -2
- plover/gui_qt/lookup_dialog.py +9 -9
- plover/gui_qt/machine_options.py +38 -37
- plover/gui_qt/main.py +19 -19
- plover/gui_qt/main_window.py +88 -71
- plover/gui_qt/paper_tape.py +69 -52
- plover/gui_qt/paper_tape_ui.py +0 -3
- plover/gui_qt/plugins_manager.py +47 -43
- plover/gui_qt/resources_rc.py +28 -28
- plover/gui_qt/run_dialog.py +7 -6
- plover/gui_qt/steno_validator.py +2 -3
- plover/gui_qt/suggestions_dialog.py +34 -38
- plover/gui_qt/suggestions_widget.py +22 -16
- plover/gui_qt/tool.py +1 -3
- plover/gui_qt/trayicon.py +19 -19
- plover/gui_qt/utils.py +19 -9
- plover/i18n.py +9 -8
- plover/key_combo.py +130 -130
- plover/log.py +26 -26
- plover/machine/base.py +40 -35
- plover/machine/geminipr.py +7 -7
- plover/machine/keyboard.py +16 -14
- plover/machine/keyboard_capture/__init__.py +0 -1
- plover/machine/keymap.py +24 -20
- plover/machine/passport.py +6 -5
- plover/machine/procat.py +12 -16
- plover/machine/stentura.py +45 -29
- plover/machine/txbolt.py +5 -3
- plover/macro/repeat.py +0 -1
- plover/macro/retro.py +9 -9
- plover/macro/undo.py +4 -3
- plover/meta/attach.py +12 -10
- plover/meta/case.py +2 -1
- plover/meta/conditional.py +3 -3
- plover/meta/currency.py +4 -4
- plover/meta/mode.py +15 -15
- plover/meta/punctuation.py +1 -0
- plover/misc.py +21 -17
- plover/orthography.py +12 -10
- plover/oslayer/__init__.py +7 -5
- plover/oslayer/config.py +16 -15
- plover/oslayer/controller.py +13 -13
- plover/oslayer/linux/i18n.py +2 -1
- plover/oslayer/linux/keyboardcontrol.py +2 -2
- plover/oslayer/linux/keyboardcontrol_uinput.py +48 -3
- plover/oslayer/linux/keyboardcontrol_x11.py +860 -851
- plover/oslayer/linux/log.py +1 -1
- plover/oslayer/linux/log_dbus.py +97 -65
- plover/oslayer/linux/serial.py +2 -2
- plover/oslayer/linux/wmctrl_x11.py +11 -15
- plover/oslayer/osx/keyboardcontrol.py +194 -99
- plover/oslayer/osx/keyboardlayout.py +138 -119
- plover/oslayer/osx/log.py +14 -8
- plover/oslayer/osx/serial.py +1 -1
- plover/oslayer/osx/wmctrl.py +6 -1
- plover/oslayer/windows/keyboardcontrol.py +195 -89
- plover/oslayer/windows/keyboardlayout.py +367 -334
- plover/oslayer/windows/log.py +5 -3
- plover/oslayer/windows/serial.py +4 -5
- plover/oslayer/windows/wmctrl.py +1 -1
- plover/output/__init__.py +1 -2
- plover/output/keyboard.py +15 -15
- plover/plugins_manager/__main__.py +42 -39
- plover/plugins_manager/global_registry.py +2 -5
- plover/plugins_manager/local_registry.py +11 -13
- plover/plugins_manager/package_index.py +12 -18
- plover/plugins_manager/pip_wrapper.py +5 -5
- plover/plugins_manager/plugin_metadata.py +17 -9
- plover/plugins_manager/registry.py +39 -28
- plover/plugins_manager/requests.py +2 -4
- plover/plugins_manager/utils.py +9 -7
- plover/registry.py +28 -26
- plover/resource.py +13 -10
- plover/scripts/dist_main.py +7 -7
- plover/scripts/main.py +57 -34
- plover/scripts/send_command.py +13 -7
- plover/steno.py +16 -9
- plover/steno_dictionary.py +47 -21
- plover/suggestions.py +11 -11
- plover/system/__init__.py +39 -23
- plover/system/english_stenotype.py +231 -229
- plover/translation.py +67 -51
- {plover-5.0.0.dev2.dist-info → plover-5.0.0.dev3.dist-info}/METADATA +1 -1
- plover-5.0.0.dev3.dist-info/RECORD +216 -0
- plover_build_utils/check_requirements.py +5 -6
- plover_build_utils/download.py +10 -8
- plover_build_utils/get_pip.py +10 -5
- plover_build_utils/install_wheels.py +33 -31
- plover_build_utils/pyqt.py +24 -22
- plover_build_utils/setup.py +64 -54
- plover_build_utils/source_less.py +6 -6
- plover_build_utils/testing/blackbox.py +31 -31
- plover_build_utils/testing/dict.py +3 -3
- plover_build_utils/testing/output.py +5 -6
- plover_build_utils/testing/parametrize.py +5 -3
- plover_build_utils/testing/steno_dictionary.py +145 -124
- plover_build_utils/tree.py +21 -19
- plover_build_utils/trim.py +5 -5
- plover_build_utils/zipdir.py +3 -3
- plover-5.0.0.dev2.dist-info/RECORD +0 -216
- {plover-5.0.0.dev2.dist-info → plover-5.0.0.dev3.dist-info}/WHEEL +0 -0
- {plover-5.0.0.dev2.dist-info → plover-5.0.0.dev3.dist-info}/entry_points.txt +0 -0
- {plover-5.0.0.dev2.dist-info → plover-5.0.0.dev3.dist-info}/licenses/LICENSE.txt +0 -0
- {plover-5.0.0.dev2.dist-info → plover-5.0.0.dev3.dist-info}/top_level.txt +0 -0
- {plover-5.0.0.dev2.dist-info → plover-5.0.0.dev3.dist-info}/zip-safe +0 -0
plover/__init__.py
CHANGED
|
@@ -3,8 +3,9 @@
|
|
|
3
3
|
|
|
4
4
|
"""Plover: Open Source Stenography Software"""
|
|
5
5
|
|
|
6
|
-
if __name__ ==
|
|
6
|
+
if __name__ == "plover":
|
|
7
7
|
from plover.i18n import Translator
|
|
8
|
+
|
|
8
9
|
_ = Translator(__package__)
|
|
9
10
|
else:
|
|
10
11
|
# exec from `setup.py`, package data
|
|
@@ -12,10 +13,10 @@ else:
|
|
|
12
13
|
# want to translate anyway.
|
|
13
14
|
_ = lambda s: s
|
|
14
15
|
|
|
15
|
-
__version__ =
|
|
16
|
-
__copyright__ =
|
|
17
|
-
__url__ =
|
|
18
|
-
__download_url__ =
|
|
16
|
+
__version__ = "5.0.0.dev3"
|
|
17
|
+
__copyright__ = "(C) Open Steno Project"
|
|
18
|
+
__url__ = "http://www.openstenoproject.org/"
|
|
19
|
+
__download_url__ = "http://www.openstenoproject.org/plover"
|
|
19
20
|
__credits__ = _("""\
|
|
20
21
|
Founded by stenographer Mirabai Knight.
|
|
21
22
|
|
|
@@ -30,9 +31,9 @@ Martin Koerner
|
|
|
30
31
|
|
|
31
32
|
and many more on GitHub:
|
|
32
33
|
<https://github.com/openstenoproject/plover>""")
|
|
33
|
-
__license__ =
|
|
34
|
+
__license__ = "GNU General Public License v2 or later (GPLv2+)"
|
|
34
35
|
# i18n: Short description for Plover, currently not used in the interface.
|
|
35
|
-
__description__ = _(
|
|
36
|
+
__description__ = _("Open Source Stenography Software")
|
|
36
37
|
__long_description__ = _("""\
|
|
37
38
|
Plover is a free open source program intended to bring realtime
|
|
38
39
|
stenographic technology not just to stenographers, but also to
|
plover/__main__.py
CHANGED
plover/command/set_config.py
CHANGED
|
@@ -22,11 +22,14 @@ def set_config(engine, cmdline):
|
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
def _cmdline_to_dict(cmdline):
|
|
25
|
-
"""
|
|
25
|
+
"""Add braces and parse the entire command line as a Python dict literal."""
|
|
26
26
|
try:
|
|
27
|
-
opt_dict = ast.literal_eval(
|
|
27
|
+
opt_dict = ast.literal_eval("{" + cmdline + "}")
|
|
28
28
|
assert isinstance(opt_dict, dict)
|
|
29
29
|
return opt_dict
|
|
30
30
|
except (AssertionError, SyntaxError, ValueError) as e:
|
|
31
|
-
raise ValueError(
|
|
32
|
-
|
|
31
|
+
raise ValueError(
|
|
32
|
+
'Bad command string "%s" for PLOVER:SET_CONFIG.\n' % cmdline
|
|
33
|
+
+ "See for reference:\n\n"
|
|
34
|
+
+ set_config.__doc__
|
|
35
|
+
) from e
|
plover/config.py
CHANGED
|
@@ -17,26 +17,25 @@ from plover import log
|
|
|
17
17
|
|
|
18
18
|
|
|
19
19
|
# General configuration sections, options and defaults.
|
|
20
|
-
MACHINE_CONFIG_SECTION =
|
|
20
|
+
MACHINE_CONFIG_SECTION = "Machine Configuration"
|
|
21
21
|
|
|
22
|
-
LEGACY_DICTIONARY_CONFIG_SECTION =
|
|
22
|
+
LEGACY_DICTIONARY_CONFIG_SECTION = "Dictionary Configuration"
|
|
23
23
|
|
|
24
|
-
LOGGING_CONFIG_SECTION =
|
|
24
|
+
LOGGING_CONFIG_SECTION = "Logging Configuration"
|
|
25
25
|
|
|
26
|
-
OUTPUT_CONFIG_SECTION =
|
|
26
|
+
OUTPUT_CONFIG_SECTION = "Output Configuration"
|
|
27
27
|
DEFAULT_UNDO_LEVELS = 100
|
|
28
28
|
MINIMUM_UNDO_LEVELS = 1
|
|
29
29
|
DEFAULT_TIME_BETWEEN_KEY_PRESSES = 0
|
|
30
30
|
MINIMUM_TIME_BETWEEN_KEY_PRESSES = 0
|
|
31
31
|
|
|
32
|
-
DEFAULT_SYSTEM_NAME =
|
|
32
|
+
DEFAULT_SYSTEM_NAME = "English Stenotype"
|
|
33
33
|
|
|
34
|
-
SYSTEM_CONFIG_SECTION =
|
|
35
|
-
SYSTEM_KEYMAP_OPTION =
|
|
34
|
+
SYSTEM_CONFIG_SECTION = "System: %s"
|
|
35
|
+
SYSTEM_KEYMAP_OPTION = "keymap[%s]"
|
|
36
36
|
|
|
37
37
|
|
|
38
|
-
class DictionaryConfig(namedtuple(
|
|
39
|
-
|
|
38
|
+
class DictionaryConfig(namedtuple("DictionaryConfig", "path enabled")):
|
|
40
39
|
def __new__(cls, path, enabled=True):
|
|
41
40
|
return super().__new__(cls, expand_path(path), enabled)
|
|
42
41
|
|
|
@@ -48,8 +47,8 @@ class DictionaryConfig(namedtuple('DictionaryConfig', 'path enabled')):
|
|
|
48
47
|
# Note: do not use _asdict because of
|
|
49
48
|
# https://bugs.python.org/issue24931
|
|
50
49
|
return {
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
"path": self.short_path,
|
|
51
|
+
"enabled": self.enabled,
|
|
53
52
|
}
|
|
54
53
|
|
|
55
54
|
def replace(self, **kwargs):
|
|
@@ -60,17 +59,20 @@ class DictionaryConfig(namedtuple('DictionaryConfig', 'path enabled')):
|
|
|
60
59
|
return DictionaryConfig(**d)
|
|
61
60
|
|
|
62
61
|
def __repr__(self):
|
|
63
|
-
return
|
|
62
|
+
return "DictionaryConfig(%r, %r)" % (self.short_path, self.enabled)
|
|
64
63
|
|
|
65
64
|
|
|
66
|
-
ConfigOption = namedtuple(
|
|
65
|
+
ConfigOption = namedtuple(
|
|
66
|
+
"ConfigOption",
|
|
67
|
+
"""
|
|
67
68
|
name default
|
|
68
69
|
getter setter
|
|
69
70
|
validate full_key
|
|
70
|
-
|
|
71
|
+
""",
|
|
72
|
+
)
|
|
71
73
|
|
|
72
|
-
class InvalidConfigOption(ValueError):
|
|
73
74
|
|
|
75
|
+
class InvalidConfigOption(ValueError):
|
|
74
76
|
def __init__(self, raw_value, fixed_value, message=None):
|
|
75
77
|
super().__init__(raw_value)
|
|
76
78
|
self.raw_value = raw_value
|
|
@@ -83,118 +85,161 @@ class InvalidConfigOption(ValueError):
|
|
|
83
85
|
|
|
84
86
|
def raw_option(name, default, section, option, validate):
|
|
85
87
|
option = option or name
|
|
88
|
+
|
|
86
89
|
def getter(config, key):
|
|
87
90
|
return config._config[section][option]
|
|
91
|
+
|
|
88
92
|
def setter(config, key, value):
|
|
89
93
|
config._set(section, option, value)
|
|
94
|
+
|
|
90
95
|
return ConfigOption(name, lambda c, k: default, getter, setter, validate, None)
|
|
91
96
|
|
|
97
|
+
|
|
92
98
|
def json_option(name, default, section, option, validate):
|
|
93
99
|
option = option or name
|
|
100
|
+
|
|
94
101
|
def getter(config, key):
|
|
95
102
|
value = config._config[section][option]
|
|
96
103
|
try:
|
|
97
104
|
return json.loads(value)
|
|
98
105
|
except json.JSONDecodeError as e:
|
|
99
106
|
raise InvalidConfigOption(value, default) from e
|
|
107
|
+
|
|
100
108
|
def setter(config, key, value):
|
|
101
109
|
if isinstance(value, set):
|
|
102
110
|
# JSON does not support sets.
|
|
103
111
|
value = list(sorted(value))
|
|
104
|
-
config._set(
|
|
112
|
+
config._set(
|
|
113
|
+
section, option, json.dumps(value, sort_keys=True, ensure_ascii=False)
|
|
114
|
+
)
|
|
115
|
+
|
|
105
116
|
return ConfigOption(name, default, getter, setter, validate, None)
|
|
106
117
|
|
|
118
|
+
|
|
107
119
|
def int_option(name, default, minimum, maximum, section, option=None):
|
|
108
120
|
option = option or name
|
|
121
|
+
|
|
109
122
|
def getter(config, key):
|
|
110
123
|
return config._config[section][option]
|
|
124
|
+
|
|
111
125
|
def setter(config, key, value):
|
|
112
126
|
config._set(section, option, str(value))
|
|
127
|
+
|
|
113
128
|
def validate(config, key, value):
|
|
114
129
|
try:
|
|
115
130
|
value = int(value)
|
|
116
131
|
except ValueError as e:
|
|
117
132
|
raise InvalidConfigOption(value, default) from e
|
|
118
|
-
if (minimum is not None and value < minimum) or
|
|
119
|
-
|
|
120
|
-
|
|
133
|
+
if (minimum is not None and value < minimum) or (
|
|
134
|
+
maximum is not None and value > maximum
|
|
135
|
+
):
|
|
136
|
+
message = "%s not in [%s, %s]" % (value, minimum or "-∞", maximum or "∞")
|
|
121
137
|
raise InvalidConfigOption(value, default, message)
|
|
122
138
|
return value
|
|
139
|
+
|
|
123
140
|
return ConfigOption(name, lambda c, k: default, getter, setter, validate, None)
|
|
124
141
|
|
|
142
|
+
|
|
125
143
|
def boolean_option(name, default, section, option=None):
|
|
126
144
|
option = option or name
|
|
145
|
+
|
|
127
146
|
def getter(config, key):
|
|
128
147
|
return config._config[section][option]
|
|
148
|
+
|
|
129
149
|
def setter(config, key, value):
|
|
130
150
|
config._set(section, option, str(value))
|
|
151
|
+
|
|
131
152
|
def validate(config, key, value):
|
|
132
153
|
try:
|
|
133
154
|
return boolean(value)
|
|
134
155
|
except ValueError as e:
|
|
135
156
|
raise InvalidConfigOption(value, default) from e
|
|
157
|
+
|
|
136
158
|
return ConfigOption(name, lambda c, k: default, getter, setter, validate, None)
|
|
137
159
|
|
|
160
|
+
|
|
138
161
|
def choice_option(name, choices, section, option=None):
|
|
139
162
|
default = choices[0]
|
|
163
|
+
|
|
140
164
|
def validate(config, key, value):
|
|
141
165
|
if value not in choices:
|
|
142
166
|
raise InvalidConfigOption(value, default)
|
|
143
167
|
return value
|
|
168
|
+
|
|
144
169
|
return raw_option(name, default, section, option, validate)
|
|
145
170
|
|
|
171
|
+
|
|
146
172
|
def plugin_option(name, plugin_type, default, section, option=None):
|
|
147
173
|
def validate(config, key, value):
|
|
148
174
|
try:
|
|
149
175
|
return registry.get_plugin(plugin_type, value).name
|
|
150
176
|
except KeyError as e:
|
|
151
177
|
raise InvalidConfigOption(value, default) from e
|
|
178
|
+
|
|
152
179
|
return raw_option(name, default, section, option, validate)
|
|
153
180
|
|
|
181
|
+
|
|
154
182
|
def opacity_option(name, section, option=None):
|
|
155
183
|
return int_option(name, 100, 0, 100, section, option)
|
|
156
184
|
|
|
185
|
+
|
|
157
186
|
def path_option(name, default, section, option=None):
|
|
158
187
|
option = option or name
|
|
188
|
+
|
|
159
189
|
def getter(config, key):
|
|
160
190
|
return expand_path(config._config[section][option])
|
|
191
|
+
|
|
161
192
|
def setter(config, key, value):
|
|
162
193
|
config._set(section, option, shorten_path(value))
|
|
194
|
+
|
|
163
195
|
def validate(config, key, value):
|
|
164
196
|
if not isinstance(value, str):
|
|
165
197
|
raise InvalidConfigOption(value, default)
|
|
166
198
|
return value
|
|
199
|
+
|
|
167
200
|
return ConfigOption(name, lambda c, k: default, getter, setter, validate, None)
|
|
168
201
|
|
|
202
|
+
|
|
169
203
|
def enabled_extensions_option():
|
|
170
204
|
def validate(config, key, value):
|
|
171
205
|
if not isinstance(value, (list, set, tuple)):
|
|
172
206
|
raise InvalidConfigOption(value, ())
|
|
173
207
|
return set(value)
|
|
174
|
-
|
|
208
|
+
|
|
209
|
+
return json_option(
|
|
210
|
+
"enabled_extensions",
|
|
211
|
+
lambda c, k: set(),
|
|
212
|
+
"Plugins",
|
|
213
|
+
"enabled_extensions",
|
|
214
|
+
validate,
|
|
215
|
+
)
|
|
216
|
+
|
|
175
217
|
|
|
176
218
|
def machine_specific_options():
|
|
177
219
|
def full_key(config, key):
|
|
178
220
|
if isinstance(key, tuple):
|
|
179
221
|
assert len(key) == 2
|
|
180
222
|
return key
|
|
181
|
-
return (key, config[
|
|
223
|
+
return (key, config["machine_type"])
|
|
224
|
+
|
|
182
225
|
def default(config, key):
|
|
183
|
-
machine_class = registry.get_plugin(
|
|
226
|
+
machine_class = registry.get_plugin("machine", key[1]).obj
|
|
184
227
|
return {
|
|
185
|
-
name: params[0]
|
|
186
|
-
for name, params in machine_class.get_option_info().items()
|
|
228
|
+
name: params[0] for name, params in machine_class.get_option_info().items()
|
|
187
229
|
}
|
|
230
|
+
|
|
188
231
|
def getter(config, key):
|
|
189
232
|
return config._config[key[1]]
|
|
233
|
+
|
|
190
234
|
def setter(config, key, value):
|
|
191
235
|
config._config[key[1]] = value
|
|
236
|
+
|
|
192
237
|
def validate(config, key, raw_options):
|
|
193
238
|
if not isinstance(raw_options, (dict, configparser.SectionProxy)):
|
|
194
239
|
raise InvalidConfigOption(raw_options, default(config, key))
|
|
195
240
|
machine_options = OrderedDict()
|
|
196
241
|
invalid_options = OrderedDict()
|
|
197
|
-
machine_class = registry.get_plugin(
|
|
242
|
+
machine_class = registry.get_plugin("machine", key[1]).obj
|
|
198
243
|
for name, params in sorted(machine_class.get_option_info().items()):
|
|
199
244
|
fallback, convert = params
|
|
200
245
|
try:
|
|
@@ -211,78 +256,102 @@ def machine_specific_options():
|
|
|
211
256
|
if invalid_options:
|
|
212
257
|
raise InvalidConfigOption(invalid_options, machine_options)
|
|
213
258
|
return machine_options
|
|
214
|
-
|
|
259
|
+
|
|
260
|
+
return ConfigOption(
|
|
261
|
+
"machine_specific_options", default, getter, setter, validate, full_key
|
|
262
|
+
)
|
|
263
|
+
|
|
215
264
|
|
|
216
265
|
def system_keymap_option():
|
|
217
266
|
def full_key(config, key):
|
|
218
267
|
if isinstance(key, tuple):
|
|
219
268
|
assert len(key) == 3
|
|
220
269
|
return key
|
|
221
|
-
return (key, config[
|
|
270
|
+
return (key, config["system_name"], config["machine_type"])
|
|
271
|
+
|
|
222
272
|
def location(config, key):
|
|
223
273
|
return SYSTEM_CONFIG_SECTION % key[1], SYSTEM_KEYMAP_OPTION % key[2]
|
|
274
|
+
|
|
224
275
|
def build_keymap(config, key, mappings=None):
|
|
225
|
-
system = registry.get_plugin(
|
|
226
|
-
machine_class = registry.get_plugin(
|
|
227
|
-
keymap = Keymap(
|
|
276
|
+
system = registry.get_plugin("system", key[1]).obj
|
|
277
|
+
machine_class = registry.get_plugin("machine", key[2]).obj
|
|
278
|
+
keymap = Keymap(
|
|
279
|
+
machine_class.get_keys(), system.KEYS + machine_class.get_actions()
|
|
280
|
+
)
|
|
228
281
|
if mappings is None:
|
|
229
282
|
mappings = system.KEYMAPS.get(key[2])
|
|
230
283
|
if mappings is None:
|
|
231
284
|
if machine_class.KEYMAP_MACHINE_TYPE is not None:
|
|
232
285
|
# Try fallback.
|
|
233
|
-
return build_keymap(
|
|
286
|
+
return build_keymap(
|
|
287
|
+
config, (key[0], key[1], machine_class.KEYMAP_MACHINE_TYPE)
|
|
288
|
+
)
|
|
234
289
|
# No fallback...
|
|
235
290
|
mappings = {}
|
|
236
291
|
keymap.set_mappings(mappings)
|
|
237
292
|
return keymap
|
|
293
|
+
|
|
238
294
|
def default(config, key):
|
|
239
295
|
return build_keymap(config, key)
|
|
296
|
+
|
|
240
297
|
def getter(config, key):
|
|
241
298
|
section, option = location(config, key)
|
|
242
299
|
return config._config[section][option]
|
|
300
|
+
|
|
243
301
|
def setter(config, key, keymap):
|
|
244
302
|
section, option = location(config, key)
|
|
245
303
|
config._set(section, option, str(keymap))
|
|
304
|
+
|
|
246
305
|
def validate(config, key, value):
|
|
247
306
|
try:
|
|
248
307
|
return build_keymap(config, key, value)
|
|
249
308
|
except (TypeError, ValueError) as e:
|
|
250
309
|
raise InvalidConfigOption(value, default(config, key)) from e
|
|
251
|
-
|
|
310
|
+
|
|
311
|
+
return ConfigOption("system_keymap", default, getter, setter, validate, full_key)
|
|
312
|
+
|
|
252
313
|
|
|
253
314
|
def dictionaries_option():
|
|
254
315
|
def full_key(config, key):
|
|
255
316
|
if isinstance(key, tuple):
|
|
256
317
|
assert len(key) == 2
|
|
257
318
|
return key
|
|
258
|
-
return (key, config[
|
|
319
|
+
return (key, config["system_name"])
|
|
320
|
+
|
|
259
321
|
def location(config, key):
|
|
260
322
|
return (
|
|
261
323
|
SYSTEM_CONFIG_SECTION % key[1],
|
|
262
|
-
|
|
324
|
+
"dictionaries",
|
|
263
325
|
)
|
|
326
|
+
|
|
264
327
|
def default(config, key):
|
|
265
|
-
system = registry.get_plugin(
|
|
328
|
+
system = registry.get_plugin("system", key[1]).obj
|
|
266
329
|
return [DictionaryConfig(path) for path in system.DEFAULT_DICTIONARIES]
|
|
330
|
+
|
|
267
331
|
def legacy_getter(config):
|
|
268
332
|
options = config._config[LEGACY_DICTIONARY_CONFIG_SECTION].items()
|
|
269
333
|
return [
|
|
270
|
-
{
|
|
334
|
+
{"path": value}
|
|
271
335
|
for name, value in reversed(sorted(options))
|
|
272
|
-
if re.match(r
|
|
336
|
+
if re.match(r"dictionary_file\d*$", name) is not None
|
|
273
337
|
]
|
|
338
|
+
|
|
274
339
|
def getter(config, key):
|
|
275
340
|
section, option = location(config, key)
|
|
276
341
|
value = config._config.get(section, option, fallback=None)
|
|
277
342
|
if value is None:
|
|
278
343
|
return legacy_getter(config)
|
|
279
344
|
return json.loads(value)
|
|
345
|
+
|
|
280
346
|
def setter(config, key, dictionaries):
|
|
281
347
|
section, option = location(config, key)
|
|
282
|
-
config._set(
|
|
283
|
-
|
|
284
|
-
|
|
348
|
+
config._set(
|
|
349
|
+
section,
|
|
350
|
+
option,
|
|
351
|
+
json.dumps([d.to_dict() for d in dictionaries], sort_keys=True),
|
|
352
|
+
)
|
|
285
353
|
config._config.remove_section(LEGACY_DICTIONARY_CONFIG_SECTION)
|
|
354
|
+
|
|
286
355
|
def validate(config, key, value):
|
|
287
356
|
dictionaries = []
|
|
288
357
|
for d in value:
|
|
@@ -294,11 +363,11 @@ def dictionaries_option():
|
|
|
294
363
|
d = DictionaryConfig.from_dict(d)
|
|
295
364
|
dictionaries.append(d)
|
|
296
365
|
return dictionaries
|
|
297
|
-
return ConfigOption('dictionaries', default, getter, setter, validate, full_key)
|
|
298
366
|
|
|
367
|
+
return ConfigOption("dictionaries", default, getter, setter, validate, full_key)
|
|
299
368
|
|
|
300
|
-
class Config:
|
|
301
369
|
|
|
370
|
+
class Config:
|
|
302
371
|
def __init__(self, path=None):
|
|
303
372
|
self._config = None
|
|
304
373
|
self._cache = {}
|
|
@@ -308,7 +377,7 @@ class Config:
|
|
|
308
377
|
|
|
309
378
|
def load(self):
|
|
310
379
|
self.clear()
|
|
311
|
-
with open(self.path, encoding=
|
|
380
|
+
with open(self.path, encoding="utf-8") as fp:
|
|
312
381
|
try:
|
|
313
382
|
self._config.read_file(fp)
|
|
314
383
|
except configparser.Error as e:
|
|
@@ -320,7 +389,7 @@ class Config:
|
|
|
320
389
|
|
|
321
390
|
def save(self):
|
|
322
391
|
with resource_update(self.path) as temp_path:
|
|
323
|
-
with open(temp_path, mode=
|
|
392
|
+
with open(temp_path, mode="w", encoding="utf-8") as fp:
|
|
324
393
|
self._config.write(fp)
|
|
325
394
|
|
|
326
395
|
def _set(self, section, option, value):
|
|
@@ -331,35 +400,69 @@ class Config:
|
|
|
331
400
|
# Note: order matters, e.g. machine_type comes before
|
|
332
401
|
# machine_specific_options and system_keymap because
|
|
333
402
|
# the latter depend on the former.
|
|
334
|
-
_OPTIONS = OrderedDict(
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
403
|
+
_OPTIONS = OrderedDict(
|
|
404
|
+
(opt.name, opt)
|
|
405
|
+
for opt in [
|
|
406
|
+
# Output.
|
|
407
|
+
choice_option(
|
|
408
|
+
"space_placement",
|
|
409
|
+
("Before Output", "After Output"),
|
|
410
|
+
OUTPUT_CONFIG_SECTION,
|
|
411
|
+
),
|
|
412
|
+
boolean_option("start_attached", False, OUTPUT_CONFIG_SECTION),
|
|
413
|
+
boolean_option("start_capitalized", False, OUTPUT_CONFIG_SECTION),
|
|
414
|
+
int_option(
|
|
415
|
+
"undo_levels",
|
|
416
|
+
DEFAULT_UNDO_LEVELS,
|
|
417
|
+
MINIMUM_UNDO_LEVELS,
|
|
418
|
+
None,
|
|
419
|
+
OUTPUT_CONFIG_SECTION,
|
|
420
|
+
),
|
|
421
|
+
int_option(
|
|
422
|
+
"time_between_key_presses",
|
|
423
|
+
DEFAULT_TIME_BETWEEN_KEY_PRESSES,
|
|
424
|
+
MINIMUM_TIME_BETWEEN_KEY_PRESSES,
|
|
425
|
+
None,
|
|
426
|
+
OUTPUT_CONFIG_SECTION,
|
|
427
|
+
),
|
|
428
|
+
choice_option(
|
|
429
|
+
"keyboard_layout",
|
|
430
|
+
("qwerty", "qwertz", "colemak", "colemak-dh", "dvorak"),
|
|
431
|
+
OUTPUT_CONFIG_SECTION,
|
|
432
|
+
),
|
|
433
|
+
# Logging.
|
|
434
|
+
path_option(
|
|
435
|
+
"log_file_name",
|
|
436
|
+
expand_path("strokes.log"),
|
|
437
|
+
LOGGING_CONFIG_SECTION,
|
|
438
|
+
"log_file",
|
|
439
|
+
),
|
|
440
|
+
boolean_option("enable_stroke_logging", False, LOGGING_CONFIG_SECTION),
|
|
441
|
+
boolean_option("enable_translation_logging", False, LOGGING_CONFIG_SECTION),
|
|
442
|
+
# GUI.
|
|
443
|
+
boolean_option("start_minimized", False, "Startup", "Start Minimized"),
|
|
444
|
+
boolean_option("show_stroke_display", False, "Stroke Display", "show"),
|
|
445
|
+
boolean_option(
|
|
446
|
+
"show_suggestions_display", False, "Suggestions Display", "show"
|
|
447
|
+
),
|
|
448
|
+
opacity_option("translation_frame_opacity", "Translation Frame", "opacity"),
|
|
449
|
+
boolean_option("classic_dictionaries_display_order", False, "GUI"),
|
|
450
|
+
# Plugins.
|
|
451
|
+
enabled_extensions_option(),
|
|
452
|
+
# Machine.
|
|
453
|
+
boolean_option("auto_start", False, MACHINE_CONFIG_SECTION),
|
|
454
|
+
plugin_option(
|
|
455
|
+
"machine_type", "machine", "Keyboard", MACHINE_CONFIG_SECTION
|
|
456
|
+
),
|
|
457
|
+
machine_specific_options(),
|
|
458
|
+
# System.
|
|
459
|
+
plugin_option(
|
|
460
|
+
"system_name", "system", DEFAULT_SYSTEM_NAME, "System", "name"
|
|
461
|
+
),
|
|
462
|
+
system_keymap_option(),
|
|
463
|
+
dictionaries_option(),
|
|
464
|
+
]
|
|
465
|
+
)
|
|
363
466
|
|
|
364
467
|
def _lookup(self, key):
|
|
365
468
|
name = key[0] if isinstance(key, tuple) else key
|
|
@@ -377,7 +480,7 @@ class Config:
|
|
|
377
480
|
except (configparser.NoOptionError, KeyError):
|
|
378
481
|
value = opt.default(self, key)
|
|
379
482
|
except InvalidConfigOption as e:
|
|
380
|
-
log.error(
|
|
483
|
+
log.error("invalid value for %r option", opt.name, exc_info=True)
|
|
381
484
|
value = e.fixed_value
|
|
382
485
|
self._cache[key] = value
|
|
383
486
|
return value
|
plover/dictionary/base.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Copyright (c) 2013 Hesky Fisher
|
|
2
2
|
# See LICENSE.txt for details.
|
|
3
3
|
|
|
4
|
-
# TODO: maybe move this code into the StenoDictionary itself. The current saver
|
|
4
|
+
# TODO: maybe move this code into the StenoDictionary itself. The current saver
|
|
5
5
|
# structure is odd and awkward.
|
|
6
6
|
# TODO: write tests for this file
|
|
7
7
|
|
|
@@ -17,47 +17,59 @@ from plover.registry import registry
|
|
|
17
17
|
def _get_dictionary_class(filename):
|
|
18
18
|
extension = splitext(filename)[1].lower()[1:]
|
|
19
19
|
try:
|
|
20
|
-
dict_module = registry.get_plugin(
|
|
20
|
+
dict_module = registry.get_plugin("dictionary", extension).obj
|
|
21
21
|
except KeyError:
|
|
22
22
|
raise ValueError(
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
"Unsupported extension: %s. Supported extensions: %s"
|
|
24
|
+
% (
|
|
25
|
+
extension,
|
|
26
|
+
", ".join(
|
|
27
|
+
plugin.name for plugin in registry.list_plugins("dictionary")
|
|
28
|
+
),
|
|
29
|
+
)
|
|
30
|
+
)
|
|
26
31
|
return dict_module
|
|
27
32
|
|
|
33
|
+
|
|
28
34
|
def _locked(fn):
|
|
29
35
|
lock = threading.Lock()
|
|
36
|
+
|
|
30
37
|
@functools.wraps(fn)
|
|
31
38
|
def wrapper(*args, **kwargs):
|
|
32
39
|
with lock:
|
|
33
40
|
fn(*args, **kwargs)
|
|
41
|
+
|
|
34
42
|
return wrapper
|
|
35
43
|
|
|
44
|
+
|
|
36
45
|
def _threaded(fn):
|
|
37
46
|
@functools.wraps(fn)
|
|
38
47
|
def wrapper(*args, **kwargs):
|
|
39
48
|
t = threading.Thread(target=fn, args=args, kwargs=kwargs)
|
|
40
49
|
t.start()
|
|
50
|
+
|
|
41
51
|
return wrapper
|
|
42
52
|
|
|
53
|
+
|
|
43
54
|
def create_dictionary(resource, threaded_save=True):
|
|
44
|
-
|
|
55
|
+
"""Create a new dictionary.
|
|
45
56
|
|
|
46
57
|
The format is inferred from the extension.
|
|
47
58
|
|
|
48
59
|
Note: the file is not created! The resulting dictionary save
|
|
49
60
|
method must be called to finalize the creation on disk.
|
|
50
|
-
|
|
61
|
+
"""
|
|
51
62
|
d = _get_dictionary_class(resource).create(resource)
|
|
52
63
|
if threaded_save:
|
|
53
64
|
d.save = _threaded(_locked(d.save))
|
|
54
65
|
return d
|
|
55
66
|
|
|
67
|
+
|
|
56
68
|
def load_dictionary(resource, threaded_save=True):
|
|
57
|
-
|
|
69
|
+
"""Load a dictionary from a file.
|
|
58
70
|
|
|
59
71
|
The format is inferred from the extension.
|
|
60
|
-
|
|
72
|
+
"""
|
|
61
73
|
d = _get_dictionary_class(resource).load(resource)
|
|
62
74
|
if not d.readonly and threaded_save:
|
|
63
75
|
d.save = _threaded(_locked(d.save))
|
plover/dictionary/helpers.py
CHANGED
|
@@ -4,7 +4,6 @@ from plover.steno import normalize_steno
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
class StenoNormalizer:
|
|
7
|
-
|
|
8
7
|
def __init__(self, dictionary_path):
|
|
9
8
|
self._dictionary_path = dictionary_path
|
|
10
9
|
self._errors_count = 0
|
|
@@ -14,12 +13,15 @@ class StenoNormalizer:
|
|
|
14
13
|
return normalize_steno(steno)
|
|
15
14
|
except ValueError:
|
|
16
15
|
self._errors_count += 1
|
|
17
|
-
return tuple(steno.split(
|
|
16
|
+
return tuple(steno.split("/"))
|
|
18
17
|
|
|
19
18
|
def __enter__(self):
|
|
20
19
|
return self.normalize
|
|
21
20
|
|
|
22
21
|
def __exit__(self, exc_type, exc_value, traceback):
|
|
23
22
|
if exc_type is None and self._errors_count:
|
|
24
|
-
log.warning(
|
|
25
|
-
|
|
23
|
+
log.warning(
|
|
24
|
+
_("dictionary `%s` loaded with %u invalid steno errors"),
|
|
25
|
+
shorten_path(self._dictionary_path),
|
|
26
|
+
self._errors_count,
|
|
27
|
+
)
|