|
| 1 | +import time |
| 2 | + |
| 3 | +from PyQt5.QtCore import Qt, QTimer, pyqtSlot |
| 4 | + |
| 5 | +from plover.gui_qt.tool import Tool |
| 6 | +from textstat.textstat import textstat |
| 7 | + |
| 8 | +from plover_wpm_meter.wpm_meter_ui import Ui_WpmMeter |
| 9 | + |
| 10 | +_NUM_SYLLABLES_PER_WORD = 1.44 |
| 11 | + |
| 12 | + |
| 13 | +class PloverWpmMeter(Tool, Ui_WpmMeter): |
| 14 | + TITLE = "WPM Meter" |
| 15 | + |
| 16 | + _TIMEOUTS = { |
| 17 | + "wpm1": 10, |
| 18 | + "wpm2": 60, |
| 19 | + } |
| 20 | + |
| 21 | + def __init__(self, engine): |
| 22 | + super().__init__(engine) |
| 23 | + self.setupUi(self) |
| 24 | + |
| 25 | + self._timer = QTimer() |
| 26 | + self._timer.setInterval(100) |
| 27 | + self._timer.setTimerType(Qt.PreciseTimer) |
| 28 | + self._timer.timeout.connect(self._update_wpms) |
| 29 | + self._timer.start() |
| 30 | + |
| 31 | + self._chars = [] |
| 32 | + engine.signal_connect("translated", self._on_translation) |
| 33 | + |
| 34 | + def _on_translation(self, old, new): |
| 35 | + for action in old: |
| 36 | + remove = len(action.text) |
| 37 | + if remove > 0: |
| 38 | + self._chars = self._chars[:-remove] |
| 39 | + self._chars += _timestamp_chars(action.replace) |
| 40 | + |
| 41 | + for action in new: |
| 42 | + remove = len(action.replace) |
| 43 | + if remove > 0: |
| 44 | + self._chars = self._chars[:-remove] |
| 45 | + self._chars += _timestamp_chars(action.text) |
| 46 | + |
| 47 | + self._update_wpms() |
| 48 | + |
| 49 | + @pyqtSlot() |
| 50 | + def _update_wpms(self): |
| 51 | + max_timeout = max(self._TIMEOUTS.values()) |
| 52 | + self._chars = _filter_old_chars(self._chars, max_timeout) |
| 53 | + for name, timeout in self._TIMEOUTS.items(): |
| 54 | + chars = _filter_old_chars(self._chars, timeout) |
| 55 | + wpm = _wpm_of_chars(chars, timeout) |
| 56 | + getattr(self, name).display(str(wpm)) |
| 57 | + |
| 58 | + |
| 59 | +def _timestamp_chars(chars): |
| 60 | + current_time = time.time() |
| 61 | + return [(c, current_time) for c in chars] |
| 62 | + |
| 63 | + |
| 64 | +def _filter_old_chars(chars, timeout): |
| 65 | + current_time = time.time() |
| 66 | + return [(c, t) for c, t in chars |
| 67 | + if (current_time - t) <= timeout] |
| 68 | + |
| 69 | + |
| 70 | +def _wpm_of_chars(chars, timeout): |
| 71 | + text = "".join(c for c, _ in chars) |
| 72 | + num_words = textstat.syllable_count(text) / _NUM_SYLLABLES_PER_WORD |
| 73 | + num_minutes = timeout / 60 |
| 74 | + num_words_per_minute = num_words / num_minutes |
| 75 | + return int(round(num_words_per_minute)) |
0 commit comments