From 58af6f3f74c38efefd8a01f9b62bfd4285be50a9 Mon Sep 17 00:00:00 2001 From: Simon Renblad Date: Mon, 22 Apr 2024 14:06:09 +0800 Subject: [PATCH] moninj: add load/save config --- artiq/dashboard/moninj.py | 88 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 2 deletions(-) diff --git a/artiq/dashboard/moninj.py b/artiq/dashboard/moninj.py index 899f5c1a0..c9778e931 100644 --- a/artiq/dashboard/moninj.py +++ b/artiq/dashboard/moninj.py @@ -1,15 +1,19 @@ import asyncio import logging import textwrap +import os from collections import namedtuple from PyQt5 import QtCore, QtWidgets, QtGui import pyqtgraph as pg +from sipyco import pyon + from artiq.coredevice.comm_moninj import CommMonInj, TTLOverride, TTLProbe from artiq.coredevice.ad9912_reg import AD9912_SER_CONF -from artiq.gui.tools import LayoutWidget +from artiq.gui.tools import LayoutWidget, get_open_file_name, get_save_file_name from artiq.gui.models import DictSyncTreeSepModel +from artiq.tools import exc_to_warning logger = logging.getLogger(__name__) @@ -964,6 +968,14 @@ class MonInjDock(QtWidgets.QDockWidget): layout = LayoutWidget() self.setWidget(layout) + + self._current_dir = os.getcwd() + self._menu_btn = QtWidgets.QPushButton() + self._menu_btn.setIcon( + QtWidgets.QApplication.style().standardIcon( + QtWidgets.QStyle.SP_FileDialogStart)) + layout.addWidget(self._menu_btn, 0, 0) + self._channel_model = Model({}) self.add_channel_dialog = _AddChannelDialog(self, self._channel_model) self.add_channel_dialog.accepted.connect( @@ -974,11 +986,22 @@ class MonInjDock(QtWidgets.QDockWidget): QtWidgets.QApplication.style().standardIcon( QtWidgets.QStyle.SP_FileDialogListView)) add_channel_btn.clicked.connect(self.add_channel_dialog.open) - layout.addWidget(add_channel_btn, 0, 0, colspan=1) + layout.addWidget(add_channel_btn, 0, 1) self.tree = MoninjTreeWidget() layout.addWidget(self.tree, 1, 0, colspan=10) + self._file_menu = QtWidgets.QMenu() + self._add_async_action("Open configuration...", self.load_configuration) + self._add_async_action("Save configuration...", self.save_configuration) + self._menu_btn.setMenu(self._file_menu) + + def _add_async_action(self, label, coro): + action = QtWidgets.QAction(label, self) + action.triggered.connect( + lambda: asyncio.ensure_future(exc_to_warning(coro()))) + self._file_menu.addAction(action) + def layout_widgets(self, handlers): for handler in handlers: handler.create_widget() @@ -988,6 +1011,67 @@ class MonInjDock(QtWidgets.QDockWidget): def set_channels(self, handlers): self._channel_model.update(handlers) + def _get_widget_from_handler(self, uid): + if uid in self.dm.handlers_by_uid: + handler = self.dm.handlers_by_uid[uid] + handler.create_widget() + return handler.widget + else: + logger.warning("skipping moninj widget with uid {}:" + " description incorrect/missing from device db".format(uid)) + return None + + def _connect_config(self, config): + out_config = [] + for item in config: + if isinstance(item, dict): + out_widgets = [] + for uid in item["widgets"]: + widget = self._get_widget_from_handler(uid) + if widget is not None: + out_widgets.append(widget) + item["widgets"] = out_widgets + out_config.append(item) + else: + widget = self._get_widget_from_handler(item) + if widget is not None: + out_config.append(widget) + return out_config + + async def load_configuration(self): + try: + filename = await get_open_file_name( + self, + "Load configuration", + self._current_dir, + "PYON files (*.pyon);;All files (*.*)") + except asyncio.CancelledError: + return + self._current_dir = os.path.dirname(filename) + try: + configuration = pyon.load_file(filename) + connected_config = self._connect_config(configuration) + self.tree.clear() + self.tree.extend(connected_config) + except Exception: + logger.error("Failed to load moninj configuration", exc_info=True) + + async def save_configuration(self): + try: + filename = await get_save_file_name( + self, + "Save configuration", + self._current_dir, + "PYON files (*.pyon);;All files (*.*)") + except asyncio.CancelledError: + return + self._current_dir = os.path.dirname(filename) + try: + configuration = self.tree.export_list() + pyon.store_file(filename, configuration) + except Exception: + logger.error("Failed to save moninj configuration", exc_info=True) + async def stop(self): if self.dm is not None: await self.dm.close()