Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement dynamic status update of the dictionaries #1400

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions plover/dictionary/loading_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,20 @@
import time

from plover.dictionary.base import load_dictionary
from plover.exception import DictionaryLoaderException
from plover.resource import resource_timestamp
from plover import log


class DictionaryLoadingManager:

def __init__(self):
def __init__(self, state_change_callback):
"""
Parameters:
state_change_callback -- A function that will be called when any dictionary is loaded
with two parameters: the filename and the loaded StenoDictionary object
(or an instance of ErroredDictionary if the load fails).
"""
self._state_change_callback = state_change_callback
self.dictionaries = {}

def __len__(self):
Expand All @@ -31,7 +37,7 @@ def start_loading(self, filename):
if op is not None and not op.needs_reloading():
return op
log.info('%s dictionary: %s', 'loading' if op is None else 'reloading', filename)
op = DictionaryLoadingOperation(filename)
op = DictionaryLoadingOperation(filename, self._state_change_callback)
self.dictionaries[filename] = op
return op

Expand All @@ -54,7 +60,14 @@ def load(self, filenames):

class DictionaryLoadingOperation:

def __init__(self, filename):
def __init__(self, filename, state_change_callback):
"""
Parameters:
state_change_callback -- A function that will be called when the load is finished
with two parameters: the filename and the loaded StenoDictionary object
(or an instance of ErroredDictionary if the load fails).
"""
self._state_change_callback = state_change_callback
self.loading_thread = threading.Thread(target=self.load)
self.filename = filename
self.result = None
Expand Down Expand Up @@ -89,8 +102,10 @@ def load(self):
self.result = load_dictionary(self.filename)
except Exception as e:
log.debug('loading dictionary %s failed', self.filename, exc_info=True)
self.result = DictionaryLoaderException(self.filename, e)
from plover.engine import ErroredDictionary
self.result = ErroredDictionary(self.filename, e)
self.result.timestamp = timestamp
self._state_change_callback(self.filename, self.result)

def get(self):
self.loading_thread.join()
Expand Down
16 changes: 7 additions & 9 deletions plover/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
from collections import namedtuple, OrderedDict
from functools import wraps
from queue import Queue
import functools
import os
import shutil
import threading

from plover import log, system
from plover.dictionary.loading_manager import DictionaryLoadingManager
from plover.exception import DictionaryLoaderException
from plover.formatting import Formatter
from plover.misc import shorten_path
from plover.registry import registry
Expand Down Expand Up @@ -84,6 +84,7 @@ class StenoEngine:
output_changed
config_changed
dictionaries_loaded
dictionary_state_changed
send_string
send_backspaces
send_key_combination
Expand Down Expand Up @@ -116,7 +117,7 @@ def __init__(self, config, controller, keyboard_emulation):
self._translator.add_listener(log.translation)
self._translator.add_listener(self._formatter.format)
self._dictionaries = self._translator.get_dictionary()
self._dictionaries_manager = DictionaryLoadingManager()
self._dictionaries_manager = DictionaryLoadingManager(functools.partial(self._trigger_hook, "dictionary_state_changed"))
self._running_state = self._translator.get_state()
self._translator.clear_state()
self._keyboard_emulation = keyboard_emulation
Expand Down Expand Up @@ -271,15 +272,12 @@ def _update(self, config_update=None, full=False, reset_machine=False):
])
# And then (re)load all dictionaries.
dictionaries = []
for result in self._dictionaries_manager.load(config_dictionaries.keys()):
if isinstance(result, DictionaryLoaderException):
d = ErroredDictionary(result.path, result.exception)
for d in self._dictionaries_manager.load(config_dictionaries.keys()):
if isinstance(d, ErroredDictionary):
# Only show an error if it's new.
if d != self._dictionaries.get(result.path):
if d != self._dictionaries.get(d.path):
log.error('loading dictionary `%s` failed: %s',
shorten_path(result.path), str(result.exception))
else:
d = result
shorten_path(d.path), str(d.exception))
d.enabled = config_dictionaries[d.path].enabled
dictionaries.append(d)
self._set_dictionaries(dictionaries)
Expand Down
11 changes: 0 additions & 11 deletions plover/exception.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,3 @@
class InvalidConfigurationError(Exception):
"Raised when there is something wrong in the configuration."
pass

class DictionaryLoaderException(Exception):
"""Dictionary file could not be loaded."""

def __init__(self, path, exception):
super().__init__(path, exception)
self.path = path
self.exception = exception

def __str__(self):
return 'loading dictionary `%s` failed: %s' % (self.path, self.exception)
11 changes: 10 additions & 1 deletion plover/gui_qt/dictionaries_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
)

from plover import _
from plover.steno_dictionary import StenoDictionary
from plover.config import DictionaryConfig
from plover.dictionary.base import create_dictionary
from plover.engine import ErroredDictionary
Expand Down Expand Up @@ -118,6 +119,7 @@ def __init__(self, engine, icons, max_undo=20):
config = engine.config
engine.signal_connect('config_changed', self._on_config_changed)
engine.signal_connect('dictionaries_loaded', self._on_dictionaries_loaded)
engine.signal_connect('dictionary_state_changed', self._on_dictionary_state_changed)
self._reset_items(config['dictionaries'],
config['classic_dictionaries_display_order'],
backup=False, publish=False)
Expand Down Expand Up @@ -232,11 +234,18 @@ def _on_dictionaries_loaded(self, loaded_dictionaries):
continue
item.loaded = loaded
updated_rows.add(item.row)
updated_rows.update(self._update_favorite())
if not updated_rows:
return
updated_rows.update(self._update_favorite())
self._updated_rows(updated_rows)

def _on_dictionary_state_changed(self, filename, d):
[item] = [item for item in self._from_row if item.path == filename]
# item is of type DictionaryItem
if item.loaded != d:
item.loaded = d
self._updated_rows([item.row])

def _move(self, index_list, step):
row_list = sorted(self._normalized_row_list(index_list))
if not row_list:
Expand Down
4 changes: 3 additions & 1 deletion plover/gui_qt/engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@
class Engine(StenoEngine, QThread):

# Signals.
# Each signal is emitted when the correspondingly-named hook in the engine is triggered.
signal_stroked = pyqtSignal(QVariant)
signal_translated = pyqtSignal(QVariant, QVariant)
signal_machine_state_changed = pyqtSignal(str, str)
signal_output_changed = pyqtSignal(bool)
signal_config_changed = pyqtSignal(QVariant)
signal_dictionaries_loaded = pyqtSignal(QVariant)
signal_dictionary_state_changed = pyqtSignal(str, QVariant) # Some dictionary has finished loading. Refer to class DictionaryLoadingManager for argument description.
signal_dictionaries_loaded = pyqtSignal(QVariant) # All dictionaries are loaded. Argument is a StenoDictionaryCollection instance.
signal_send_string = pyqtSignal(str)
signal_send_backspaces = pyqtSignal(int)
signal_send_key_combination = pyqtSignal(str)
Expand Down
Loading