diff --git a/studio/Python/tinymovr/cli.py b/studio/Python/tinymovr/cli.py index df5f3303..3c70a9d7 100644 --- a/studio/Python/tinymovr/cli.py +++ b/studio/Python/tinymovr/cli.py @@ -12,7 +12,6 @@ """ import can -from canine import CANineBus import pkg_resources import IPython from traitlets.config import Config @@ -20,7 +19,7 @@ from tinymovr import init_tee, destroy_tee from tinymovr.discovery import Discovery -from tinymovr.constants import app_name, base_node_name +from tinymovr.constants import app_name from tinymovr.config import get_bus_config, configure_logging """ diff --git a/studio/Python/tinymovr/gui/__init__.py b/studio/Python/tinymovr/gui/__init__.py index 555bde3e..6bcc37c2 100644 --- a/studio/Python/tinymovr/gui/__init__.py +++ b/studio/Python/tinymovr/gui/__init__.py @@ -9,7 +9,6 @@ magnitude_of, hold_sema, TimedGetter, - RateLimitedFunction, check_selected_items, get_dynamic_attrs, is_dark_mode diff --git a/studio/Python/tinymovr/gui/helpers.py b/studio/Python/tinymovr/gui/helpers.py index d138b566..b06c5e8c 100644 --- a/studio/Python/tinymovr/gui/helpers.py +++ b/studio/Python/tinymovr/gui/helpers.py @@ -432,36 +432,6 @@ def get_value(self, getter): return val -class RateLimitedFunction: - """ - A class that limits the rate of calls to a passed - function f - """ - - def __init__(self, func, rate): - self.func = func - self.rate = rate - self.busy_dt = 0 - self.meas_dt = 0 - self.load = 0 - self.stop = False - - def call(self, *args, **kwargs): - if self.busy_dt > 0 and self.busy_dt < self.rate: - self.load = self.load * 0.99 + self.busy_dt / self.rate * 0.01 - time.sleep(self.rate - self.busy_dt) - self.meas_dt = self.rate - else: - self.load = 1 - self.meas_dt = self.busy_dt - start_time = time.time() - self.func() - self.busy_dt = time.time() - start_time - - def __call__(self, *args, **kwargs): - self.call(self, *args, **kwargs) - - def display_warning(title, text): """ Display a pop up message with a warning diff --git a/studio/Python/tinymovr/gui/window.py b/studio/Python/tinymovr/gui/window.py index 03fc42c1..c42b2d54 100644 --- a/studio/Python/tinymovr/gui/window.py +++ b/studio/Python/tinymovr/gui/window.py @@ -21,7 +21,7 @@ from contextlib import suppress import json from PySide6 import QtCore -from PySide6.QtCore import Signal +from PySide6.QtCore import Signal, QTimer from PySide6.QtWidgets import ( QMainWindow, QDialog, @@ -146,22 +146,29 @@ def __init__(self, app, arguments): else: params = {"bustype": buses[0], "channel": channel, "bitrate": bitrate} - self.thread = QtCore.QThread() self.worker = Worker(params, self.logger) + self.TreeItemCheckedSignal.connect(self.worker.update_active_attrs) - self.thread.started.connect(self.worker.run) - self.worker.moveToThread(self.thread) - self.worker.handle_error.connect(self.handle_worker_error) - self.worker.regen.connect(self.regen_tree) - self.worker.update_attrs.connect(self.attrs_updated) + self.worker.handleErrorSignal.connect( + self.handle_worker_error, QtCore.Qt.QueuedConnection + ) + self.worker.regenSignal.connect(self.regen_tree, QtCore.Qt.QueuedConnection) + self.worker.updateAttrsSignal.connect( + self.attrs_updated, QtCore.Qt.QueuedConnection + ) + self.worker.updateTimingsSignal.connect( + self.timings_updated, QtCore.Qt.QueuedConnection + ) app.aboutToQuit.connect(self.about_to_quit) - self.thread.start() + + self.timer = QTimer(self) + self.timer.timeout.connect(self.worker._update) + self.timer.start(40) @QtCore.Slot() def about_to_quit(self): + self.timer.stop() self.worker.stop() - self.thread.quit() - self.thread.wait() @QtCore.Slot() def handle_worker_error(self, e): @@ -173,7 +180,7 @@ def handle_worker_error(self, e): else: self.logger.warn("Timeout while getting value.") self.timeout_count += 1 - + else: raise e @@ -289,7 +296,12 @@ def item_changed(self, item): @QtCore.Slot() def attrs_updated(self, data): for attr_name, val in data.items(): - self.attr_widgets_by_id[attr_name]["widget"].setText(1, format_value(val)) + try: + self.attr_widgets_by_id[attr_name]["widget"].setText( + 1, format_value(val) + ) + except RuntimeError: + self.logger.warn("Attribute widget disappeared while updating") if attr_name in self.graphs_by_id: graph_info = self.graphs_by_id[attr_name] x = graph_info["data"]["x"] @@ -301,16 +313,16 @@ def attrs_updated(self, data): y.append(magnitude_of(val)) graph_info["data_line"].setData(x, y) graph_info["widget"].update() - meas_dt = self.worker._rate_limited_update.meas_dt - if meas_dt == 0: - meas_dt_str = "-" - else: - meas_dt_str = "{:.1f}Hz".format(meas_dt) + + @QtCore.Slot() + def timings_updated(self, timings_dict): + meas_freq = timings_dict["meas_freq"] + meas_freq_str = "-" if meas_freq == 0 else "{:.1f}Hz".format(meas_freq) self.status_label.setText( "{}\t CH:{:.0f}%\t RT:{:.1f}ms".format( - meas_dt_str, - self.worker._rate_limited_update.load * 100, - self.worker.timed_getter.dt * 1000, + meas_freq_str, + timings_dict["load"], + timings_dict["getter_dt"] * 1000, ) ) @@ -318,7 +330,6 @@ def attrs_updated(self, data): def f_call_clicked(self, f): args = [] - # Check if the function has any arguments if f.arguments: dialog = ArgumentInputDialog(f.arguments, self) if dialog.exec_() == QDialog.Accepted: @@ -326,11 +337,8 @@ def f_call_clicked(self, f): args = [input_values[arg.name] for arg in f.arguments] else: return # User cancelled, stop the entire process - - # Convert arguments as required using pint args = [get_registry()(arg) for arg in args] - # Call the function with the collected arguments f(*args) if "reload_data" in f.meta and f.meta["reload_data"]: self.worker.reset() diff --git a/studio/Python/tinymovr/gui/worker.py b/studio/Python/tinymovr/gui/worker.py index 29611452..24d4ea96 100644 --- a/studio/Python/tinymovr/gui/worker.py +++ b/studio/Python/tinymovr/gui/worker.py @@ -20,20 +20,17 @@ import can from PySide6 import QtCore from PySide6.QtCore import QObject -from PySide6.QtWidgets import ( - QApplication, -) -from tinymovr.gui import TimedGetter, RateLimitedFunction, get_dynamic_attrs +from tinymovr.gui import TimedGetter, get_dynamic_attrs from tinymovr.tee import init_tee, destroy_tee from tinymovr.discovery import Discovery from tinymovr.constants import base_node_name class Worker(QObject): - - update_attrs = QtCore.Signal(dict) - regen = QtCore.Signal(dict) - handle_error = QtCore.Signal(object) + updateAttrsSignal = QtCore.Signal(dict) + updateTimingsSignal = QtCore.Signal(dict) + regenSignal = QtCore.Signal(dict) + handleErrorSignal = QtCore.Signal(object) def __init__(self, busparams, logger): super().__init__() @@ -41,30 +38,25 @@ def __init__(self, busparams, logger): self.mutx = QtCore.QMutex() init_tee(can.Bus(**busparams)) self._init_containers() - self.dsc = Discovery(self._device_appeared, self._device_disappeared, self.logger) + self.dsc = Discovery( + self._device_appeared, self._device_disappeared, self.logger + ) self.timed_getter = TimedGetter() - self._rate_limited_update = RateLimitedFunction(lambda: self._update(), 0.040) - self.running = True - - def run(self): - while self.running: - self._rate_limited_update() # calls update() - destroy_tee() @QtCore.Slot() def stop(self): - self.running = False + destroy_tee() def force_regen(self): self.mutx.lock() - self.regen.emit(dict(self.devices_by_name)) + self.regenSignal.emit(dict(self.devices_by_name)) self.mutx.unlock() def reset(self): self.mutx.lock() self.dsc.reset() self._init_containers() - self.regen.emit(dict(self.devices_by_name)) + self.regenSignal.emit(dict(self.devices_by_name)) self.mutx.unlock() @QtCore.Slot(dict) @@ -80,7 +72,7 @@ def _get_attr_values(self): try: vals[attr.full_name] = self.timed_getter.get_value(attr.get_value) except Exception as e: - self.handle_error.emit(e) + self.handleErrorSignal.emit(e) start_time = time.time() self.dynamic_attrs.sort( key=lambda attr: self.dynamic_attrs_last_update[attr.full_name] @@ -98,7 +90,7 @@ def _get_attr_values(self): vals[attr.full_name] = self.timed_getter.get_value(attr.get_value) self.dynamic_attrs_last_update[attr.full_name] = start_time except Exception as e: - self.handle_error.emit(e) + self.handleErrorSignal.emit(e) break return vals @@ -113,9 +105,11 @@ def _update(self): self.mutx.lock() last_updated = self._get_attr_values() if len(last_updated) > 0: - self.update_attrs.emit(last_updated) + self.updateAttrsSignal.emit(last_updated) + self.updateTimingsSignal.emit( + {"meas_freq": 0, "load": 0, "getter_dt": self.timed_getter.dt} + ) self.mutx.unlock() - QApplication.processEvents() def _device_appeared(self, device, node_id): self.mutx.lock() @@ -125,7 +119,7 @@ def _device_appeared(self, device, node_id): device.name = display_name device.include_base_name = True self.dynamic_attrs = get_dynamic_attrs(self.devices_by_name) - self.regen.emit(dict(self.devices_by_name)) + self.regenSignal.emit(dict(self.devices_by_name)) self.mutx.unlock() def _device_disappeared(self, node_id): @@ -134,5 +128,5 @@ def _device_disappeared(self, node_id): del self.devices_by_name[display_name] del self.names_by_id[node_id] self.dynamic_attrs = get_dynamic_attrs(self.devices_by_name) - self.regen.emit(dict(self.devices_by_name)) + self.regenSignal.emit(dict(self.devices_by_name)) self.mutx.unlock()