From 97a84abd60e85eb5d376b12f4269bbaaae6fb0d5 Mon Sep 17 00:00:00 2001 From: user202729 <25191436+user202729@users.noreply.github.com> Date: Mon, 17 Jul 2023 16:13:59 +0700 Subject: [PATCH] Implement dynamic status update of the dictionaries --- plover/dictionary/loading_manager.py | 24 +++++++++++++++++++----- plover/engine.py | 16 +++++++--------- plover/exception.py | 11 ----------- plover/gui_qt/dictionaries_widget.py | 9 +++++++++ plover/gui_qt/engine.py | 4 +++- 5 files changed, 38 insertions(+), 26 deletions(-) diff --git a/plover/dictionary/loading_manager.py b/plover/dictionary/loading_manager.py index f177752da..b94f4067a 100644 --- a/plover/dictionary/loading_manager.py +++ b/plover/dictionary/loading_manager.py @@ -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): @@ -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 @@ -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 @@ -89,8 +102,9 @@ 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) + 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() diff --git a/plover/engine.py b/plover/engine.py index bc47d669c..816253884 100644 --- a/plover/engine.py +++ b/plover/engine.py @@ -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 @@ -84,6 +84,7 @@ class StenoEngine: output_changed config_changed dictionaries_loaded + dictionary_state_changed send_string send_backspaces send_key_combination @@ -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 @@ -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) diff --git a/plover/exception.py b/plover/exception.py index fce4e814b..075a40723 100644 --- a/plover/exception.py +++ b/plover/exception.py @@ -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) diff --git a/plover/gui_qt/dictionaries_widget.py b/plover/gui_qt/dictionaries_widget.py index b1937cd03..bb86b2f5d 100644 --- a/plover/gui_qt/dictionaries_widget.py +++ b/plover/gui_qt/dictionaries_widget.py @@ -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 @@ -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) @@ -237,6 +239,13 @@ def _on_dictionaries_loaded(self, loaded_dictionaries): 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: diff --git a/plover/gui_qt/engine.py b/plover/gui_qt/engine.py index 1c2d338c5..05a9ad956 100644 --- a/plover/gui_qt/engine.py +++ b/plover/gui_qt/engine.py @@ -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)