plover 5.0.0rc1__py3-none-any.whl → 5.2.0__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.
Files changed (84) hide show
  1. plover/__init__.py +5 -3
  2. plover/config.py +58 -14
  3. plover/dictionary/loading_manager.py +23 -6
  4. plover/engine.py +216 -48
  5. plover/exception.py +0 -12
  6. plover/formatting.py +5 -2
  7. plover/gui_qt/about_dialog_ui.py +1 -1
  8. plover/gui_qt/add_translation_dialog_ui.py +1 -1
  9. plover/gui_qt/add_translation_widget_ui.py +1 -1
  10. plover/gui_qt/appearance.py +49 -0
  11. plover/gui_qt/config_file_widget_ui.py +1 -1
  12. plover/gui_qt/config_keyboard_widget_ui.py +1 -1
  13. plover/gui_qt/config_plover_hid_widget_ui.py +110 -0
  14. plover/gui_qt/config_serial_widget_ui.py +2 -11
  15. plover/gui_qt/config_window.py +139 -33
  16. plover/gui_qt/config_window_ui.py +2 -2
  17. plover/gui_qt/console_widget.py +1 -1
  18. plover/gui_qt/console_widget_ui.py +1 -1
  19. plover/gui_qt/dictionaries_widget.py +9 -0
  20. plover/gui_qt/dictionaries_widget_ui.py +1 -1
  21. plover/gui_qt/dictionary_editor_ui.py +1 -1
  22. plover/gui_qt/engine.py +6 -1
  23. plover/gui_qt/lookup_dialog_ui.py +1 -1
  24. plover/gui_qt/machine_options.py +54 -20
  25. plover/gui_qt/main_window.py +13 -1
  26. plover/gui_qt/main_window_ui.py +15 -8
  27. plover/gui_qt/paper_tape_ui.py +1 -1
  28. plover/gui_qt/plugins_manager.py +9 -4
  29. plover/gui_qt/plugins_manager_ui.py +2 -2
  30. plover/gui_qt/resources_rc.py +29 -29
  31. plover/gui_qt/run_dialog_ui.py +1 -1
  32. plover/gui_qt/suggestions_dialog_ui.py +1 -1
  33. plover/gui_qt/trayicon.py +0 -2
  34. plover/gui_qt/utils.py +0 -1
  35. plover/i18n.py +1 -1
  36. plover/key_combo.py +3 -1
  37. plover/log.py +2 -2
  38. plover/machine/base.py +12 -2
  39. plover/machine/keyboard_capture/__init__.py +33 -10
  40. plover/machine/keymap.py +86 -10
  41. plover/machine/plover_hid.py +333 -0
  42. plover/machine/procat.py +1 -4
  43. plover/machine/stentura.py +5 -5
  44. plover/messages/es/LC_MESSAGES/plover.mo +0 -0
  45. plover/messages/es/LC_MESSAGES/plover.po +2 -2
  46. plover/messages/fr/LC_MESSAGES/plover.mo +0 -0
  47. plover/messages/fr/LC_MESSAGES/plover.po +2 -2
  48. plover/messages/it/LC_MESSAGES/plover.mo +0 -0
  49. plover/messages/it/LC_MESSAGES/plover.po +2 -2
  50. plover/messages/nl/LC_MESSAGES/plover.mo +0 -0
  51. plover/messages/nl/LC_MESSAGES/plover.po +2 -2
  52. plover/messages/plover.pot +1 -1
  53. plover/messages/zh_tw/LC_MESSAGES/plover.mo +0 -0
  54. plover/messages/zh_tw/LC_MESSAGES/plover.po +2 -2
  55. plover/orthography.py +2 -1
  56. plover/oslayer/controller.py +1 -1
  57. plover/oslayer/linux/keyboardcontrol.py +4 -2
  58. plover/oslayer/linux/keyboardcontrol_uinput.py +107 -343
  59. plover/oslayer/linux/keyboardcontrol_x11.py +22 -13
  60. plover/oslayer/linux/keyboardlayout_wayland.py +403 -0
  61. plover/oslayer/linux/log.py +3 -1
  62. plover/oslayer/linux/wayland_connection.py +293 -0
  63. plover/oslayer/osx/keyboardlayout.py +4 -1
  64. plover/oslayer/windows/log.py +28 -6
  65. plover/plugins_manager/registry.py +4 -3
  66. plover/registry.py +1 -1
  67. plover/scripts/main.py +6 -3
  68. plover/system/__init__.py +7 -7
  69. plover/system/english_stenotype.py +53 -0
  70. {plover-5.0.0rc1.dist-info → plover-5.2.0.dist-info}/METADATA +5 -3
  71. {plover-5.0.0rc1.dist-info → plover-5.2.0.dist-info}/RECORD +84 -79
  72. {plover-5.0.0rc1.dist-info → plover-5.2.0.dist-info}/WHEEL +1 -1
  73. {plover-5.0.0rc1.dist-info → plover-5.2.0.dist-info}/entry_points.txt +5 -3
  74. {plover-5.0.0rc1.dist-info → plover-5.2.0.dist-info}/licenses/LICENSE.txt +4 -5
  75. plover_build_utils/check_requirements.py +0 -3
  76. plover_build_utils/download.py +5 -2
  77. plover_build_utils/functions.sh +69 -17
  78. plover_build_utils/testing/__init__.py +9 -0
  79. plover_build_utils/testing/blackbox.py +3 -1
  80. plover_build_utils/testing/steno_dictionary.py +2 -2
  81. /plover/machine/{geminipr.py → gemini_pr.py} +0 -0
  82. /plover/machine/{txbolt.py → tx_bolt.py} +0 -0
  83. {plover-5.0.0rc1.dist-info → plover-5.2.0.dist-info}/top_level.txt +0 -0
  84. {plover-5.0.0rc1.dist-info → plover-5.2.0.dist-info}/zip-safe +0 -0
plover/__init__.py CHANGED
@@ -11,9 +11,11 @@ else:
11
11
  # exec from `setup.py`, package data
12
12
  # may not be available, and we don't
13
13
  # want to translate anyway.
14
- _ = lambda s: s
14
+ def _(s):
15
+ return s
15
16
 
16
- __version__ = "5.0.0rc1"
17
+
18
+ __version__ = "5.2.0"
17
19
  __copyright__ = "(C) Open Steno Project"
18
20
  __url__ = "http://www.openstenoproject.org/"
19
21
  __download_url__ = "http://www.openstenoproject.org/plover"
@@ -24,7 +26,7 @@ Developers:
24
26
 
25
27
  Joshua Lifton
26
28
  Hesky Fisher
27
- Ted Morin
29
+ Thea Morin
28
30
  Benoit Pierre
29
31
  Sammi Ta
30
32
  Martin Koerner
plover/config.py CHANGED
@@ -1,12 +1,14 @@
1
1
  # Copyright (c) 2010-2011 Joshua Harlan Lifton.
2
2
  # See LICENSE.txt for details.
3
3
 
4
- """Configuration management."""
4
+ """This modules handles reading and writing Plover's configuration files, as well
5
+ as updating the configuration on-the-fly while Plover is running."""
5
6
 
6
7
  from collections import ChainMap, namedtuple, OrderedDict
7
8
  import configparser
8
9
  import json
9
10
  import re
11
+ from typing import Any, Dict
10
12
 
11
13
  from plover.exception import InvalidConfigurationError
12
14
  from plover.machine.keymap import Keymap
@@ -17,6 +19,7 @@ from plover import log
17
19
 
18
20
 
19
21
  # General configuration sections, options and defaults.
22
+ APPEARANCE_CONFIG_SECTION = "Appearance"
20
23
  MACHINE_CONFIG_SECTION = "Machine Configuration"
21
24
 
22
25
  LEGACY_DICTIONARY_CONFIG_SECTION = "Dictionary Configuration"
@@ -36,14 +39,23 @@ SYSTEM_KEYMAP_OPTION = "keymap[%s]"
36
39
 
37
40
 
38
41
  class DictionaryConfig(namedtuple("DictionaryConfig", "path enabled")):
42
+ """Represents the configuration for one dictionary.
43
+
44
+ Attributes:
45
+ path (str): The fully qualified path to the dictionary file.
46
+ enabled (bool): Whether the dictionary is enabled.
47
+ """
48
+
39
49
  def __new__(cls, path, enabled=True):
40
50
  return super().__new__(cls, expand_path(path), enabled)
41
51
 
42
52
  @property
43
- def short_path(self):
53
+ def short_path(self) -> str:
54
+ """The shortened path to the dictionary file. This is automatically calculated from :attr:`path`."""
44
55
  return shorten_path(self.path)
45
56
 
46
- def to_dict(self):
57
+ def to_dict(self) -> Dict[str, Any]:
58
+ """Returns the ``dict`` representation of the dictionary configuration."""
47
59
  # Note: do not use _asdict because of
48
60
  # https://bugs.python.org/issue24931
49
61
  return {
@@ -51,11 +63,13 @@ class DictionaryConfig(namedtuple("DictionaryConfig", "path enabled")):
51
63
  "enabled": self.enabled,
52
64
  }
53
65
 
54
- def replace(self, **kwargs):
66
+ def replace(self, **kwargs) -> "DictionaryConfig":
67
+ """Replaces the values of :attr:`path` and :attr:`enabled` with those in ``kwargs``."""
55
68
  return self._replace(**kwargs)
56
69
 
57
70
  @staticmethod
58
- def from_dict(d):
71
+ def from_dict(d: Dict[str, Any]) -> "DictionaryConfig":
72
+ """Returns a :class:`DictionaryConfig` constructed from its ``dict`` representation."""
59
73
  return DictionaryConfig(**d)
60
74
 
61
75
  def __repr__(self):
@@ -73,6 +87,11 @@ ConfigOption = namedtuple(
73
87
 
74
88
 
75
89
  class InvalidConfigOption(ValueError):
90
+ """An exception raised when a configuration option has been set to an invalid
91
+ value, such as one of the wrong type. ``fixed_value`` is the value that
92
+ Plover is falling back on if ``raw_value`` can't be parsed correctly.
93
+ """
94
+
76
95
  def __init__(self, raw_value, fixed_value, message=None):
77
96
  super().__init__(raw_value)
78
97
  self.raw_value = raw_value
@@ -368,14 +387,22 @@ def dictionaries_option():
368
387
 
369
388
 
370
389
  class Config:
371
- def __init__(self, path=None):
390
+ """An object containing the entire Plover configuration. The config object
391
+ maintains a cache for any changes that are made while Plover is running.
392
+ """
393
+
394
+ def __init__(self, path=None) -> None:
372
395
  self._config = None
373
396
  self._cache = {}
374
397
  # A convenient place for other code to store a file name.
375
398
  self.path = path
376
399
  self.clear()
377
400
 
378
- def load(self):
401
+ def load(self) -> None:
402
+ """Reads and parses the configuration from the configuration file. Raises an
403
+ :exc:`InvalidConfigurationError<plover.exception.InvalidConfigurationError>`
404
+ if the configuration could not be parsed correctly.
405
+ """
379
406
  self.clear()
380
407
  with open(self.path, encoding="utf-8") as fp:
381
408
  try:
@@ -383,11 +410,13 @@ class Config:
383
410
  except configparser.Error as e:
384
411
  raise InvalidConfigurationError(str(e))
385
412
 
386
- def clear(self):
413
+ def clear(self) -> None:
414
+ """Clears the configuration and returns to the base state."""
387
415
  self._config = configparser.RawConfigParser()
388
416
  self._cache.clear()
389
417
 
390
- def save(self):
418
+ def save(self) -> None:
419
+ """Writes the current state of the configuration to the configuration file."""
391
420
  with resource_update(self.path) as temp_path:
392
421
  with open(temp_path, mode="w", encoding="utf-8") as fp:
393
422
  self._config.write(fp)
@@ -427,7 +456,7 @@ class Config:
427
456
  ),
428
457
  choice_option(
429
458
  "keyboard_layout",
430
- ("qwerty", "qwertz", "colemak", "colemak-dh", "dvorak"),
459
+ ("qwerty", "qwertz", "colemak", "colemak-dh", "dvorak", "wayland-auto"),
431
460
  OUTPUT_CONFIG_SECTION,
432
461
  ),
433
462
  # Logging.
@@ -439,6 +468,13 @@ class Config:
439
468
  ),
440
469
  boolean_option("enable_stroke_logging", False, LOGGING_CONFIG_SECTION),
441
470
  boolean_option("enable_translation_logging", False, LOGGING_CONFIG_SECTION),
471
+ # Appearance.
472
+ choice_option(
473
+ "appearance_mode",
474
+ ("system", "light", "dark"),
475
+ APPEARANCE_CONFIG_SECTION,
476
+ "mode",
477
+ ),
442
478
  # GUI.
443
479
  boolean_option("start_minimized", False, "Startup", "Start Minimized"),
444
480
  boolean_option("show_stroke_display", False, "Stroke Display", "show"),
@@ -471,7 +507,10 @@ class Config:
471
507
  key = opt.full_key(self, key)
472
508
  return key, opt
473
509
 
474
- def __getitem__(self, key):
510
+ def __getitem__(self, key: str) -> Any:
511
+ """Returns the value of the specified ``key`` in the cache, or in the
512
+ full configuration if not available.
513
+ """
475
514
  key, opt = self._lookup(key)
476
515
  if key in self._cache:
477
516
  return self._cache[key]
@@ -485,16 +524,21 @@ class Config:
485
524
  self._cache[key] = value
486
525
  return value
487
526
 
488
- def __setitem__(self, key, value):
527
+ def __setitem__(self, key: str, value: Any) -> None:
528
+ """Sets the property ``key`` in the configuration to the specified value."""
489
529
  key, opt = self._lookup(key)
490
530
  value = opt.validate(self._config, key, value)
491
531
  opt.setter(self, key, value)
492
532
  self._cache[key] = value
493
533
 
494
- def as_dict(self):
534
+ def as_dict(self) -> Dict[str, Any]:
535
+ """Returns the ``dict`` representation of the current state of the
536
+ configuration.
537
+ """
495
538
  return {opt.name: self[opt.name] for opt in self._OPTIONS.values()}
496
539
 
497
- def update(self, **kwargs):
540
+ def update(self, **kwargs) -> None:
541
+ """Update the cache to reflect the contents of the full configuration."""
498
542
  new_settings = []
499
543
  new_config = ChainMap({}, self)
500
544
  for opt in self._OPTIONS.values():
@@ -7,13 +7,19 @@ import threading
7
7
  import time
8
8
 
9
9
  from plover.dictionary.base import load_dictionary
10
- from plover.exception import DictionaryLoaderException
11
10
  from plover.resource import resource_timestamp
12
11
  from plover import log
13
12
 
14
13
 
15
14
  class DictionaryLoadingManager:
16
- def __init__(self):
15
+ def __init__(self, state_change_callback):
16
+ """
17
+ Parameters:
18
+ state_change_callback -- A function that will be called when any dictionary is loaded
19
+ with two parameters: the filename and the loaded StenoDictionary object
20
+ (or an instance of ErroredDictionary if the load fails).
21
+ """
22
+ self._state_change_callback = state_change_callback
17
23
  self.dictionaries = {}
18
24
 
19
25
  def __len__(self):
@@ -32,7 +38,7 @@ class DictionaryLoadingManager:
32
38
  log.info(
33
39
  "%s dictionary: %s", "loading" if op is None else "reloading", filename
34
40
  )
35
- op = DictionaryLoadingOperation(filename)
41
+ op = DictionaryLoadingOperation(filename, self._state_change_callback)
36
42
  self.dictionaries[filename] = op
37
43
  return op
38
44
 
@@ -52,7 +58,15 @@ class DictionaryLoadingManager:
52
58
 
53
59
 
54
60
  class DictionaryLoadingOperation:
55
- def __init__(self, filename):
61
+ def __init__(self, filename, state_change_callback):
62
+ """
63
+ Parameters:
64
+ filename -- Path to dictionary file.
65
+ state_change_callback -- A function that will be called when the load is finished
66
+ with two parameters: the filename and the loaded StenoDictionary object
67
+ (or an instance of ErroredDictionary if the load fails).
68
+ """
69
+ self._state_change_callback = state_change_callback
56
70
  self.loading_thread = threading.Thread(target=self.load)
57
71
  self.filename = filename
58
72
  self.result = None
@@ -61,7 +75,7 @@ class DictionaryLoadingOperation:
61
75
  def needs_reloading(self):
62
76
  try:
63
77
  new_timestamp = resource_timestamp(self.filename)
64
- except:
78
+ except Exception:
65
79
  # Bad resource name, permission denied, path
66
80
  # does not exist, ...
67
81
  new_timestamp = None
@@ -87,8 +101,11 @@ class DictionaryLoadingOperation:
87
101
  self.result = load_dictionary(self.filename)
88
102
  except Exception as e:
89
103
  log.debug("loading dictionary %s failed", self.filename, exc_info=True)
90
- self.result = DictionaryLoaderException(self.filename, e)
104
+ from plover.engine import ErroredDictionary
105
+
106
+ self.result = ErroredDictionary(self.filename, e)
91
107
  self.result.timestamp = timestamp
108
+ self._state_change_callback(self.filename, self.result)
92
109
 
93
110
  def get(self):
94
111
  self.loading_thread.join()