From 763563ba1d1fc75086032d7a37ebc69d8d169db8 Mon Sep 17 00:00:00 2001 From: Dmitri Gavrilov Date: Sat, 16 Mar 2024 18:38:34 -0400 Subject: [PATCH 1/4] ENH: menu items for saving plan history (queue-monitor app) --- bluesky_widgets/_matplotlib_axes.py | 2 +- bluesky_widgets/apps/queue_monitor/main.py | 2 +- bluesky_widgets/apps/queue_monitor/viewer.py | 48 +++++++++++++++-- bluesky_widgets/apps/queue_monitor/widgets.py | 24 ++++----- .../advanced/qt_viewer_with_search.py | 18 +++---- .../examples/advanced/qt_with_RE_worker.py | 15 +++--- bluesky_widgets/examples/headless_figures.py | 8 +-- bluesky_widgets/examples/ipy_qt_figure.py | 6 +-- bluesky_widgets/examples/ipy_qt_figures.py | 6 +-- bluesky_widgets/examples/jupyter_viewer.py | 2 +- bluesky_widgets/examples/kafka_figures.py | 6 +-- .../examples/napari_dock_widgets.py | 4 +- bluesky_widgets/examples/pyFAI_dialog.py | 23 ++++----- .../examples/qt_app_integration.py | 7 +-- .../examples/qt_app_search_integration.py | 13 ++--- bluesky_widgets/examples/qt_run_tree_view.py | 5 +- bluesky_widgets/examples/qt_search.py | 9 ++-- .../examples/utils/generate_mongo_data.py | 11 ++-- .../examples/utils/generate_msgpack_data.py | 11 ++-- .../examples/utils/stream_data_kafka.py | 7 ++- .../examples/utils/stream_data_zmq.py | 15 +++--- .../headless/_tests/test_closing.py | 3 +- bluesky_widgets/headless/figures.py | 3 +- bluesky_widgets/jupyter/_search_input.py | 16 ++---- bluesky_widgets/jupyter/figures.py | 4 +- bluesky_widgets/jupyter/zmq_dispatcher.py | 6 +-- bluesky_widgets/models/_tests/test_figures.py | 14 ++--- bluesky_widgets/models/_tests/test_image.py | 2 +- bluesky_widgets/models/_tests/test_lines.py | 3 +- .../models/_tests/test_rastered_image.py | 6 +-- .../models/_tests/test_search_input.py | 2 +- bluesky_widgets/models/_tests/test_utils.py | 4 +- .../models/auto_plot_builders/__init__.py | 2 +- .../models/auto_plot_builders/_base.py | 2 +- .../models/auto_plot_builders/_lines.py | 4 +- .../_tests/test_auto_images.py | 4 +- .../_tests/test_auto_lines.py | 3 +- bluesky_widgets/models/plot_builders.py | 9 +--- bluesky_widgets/models/plot_specs.py | 2 +- bluesky_widgets/models/run_engine_client.py | 51 +++++++++++++++++-- bluesky_widgets/models/run_tree.py | 2 +- bluesky_widgets/models/search.py | 6 +-- bluesky_widgets/models/utils.py | 2 +- bluesky_widgets/qt/__init__.py | 2 +- bluesky_widgets/qt/_event_loop.py | 1 - bluesky_widgets/qt/_main_window.py | 12 ++--- bluesky_widgets/qt/_search_input.py | 10 ++-- bluesky_widgets/qt/_search_results.py | 9 +--- bluesky_widgets/qt/_searches.py | 11 +--- bluesky_widgets/qt/_tests/conftest.py | 10 ++-- .../qt/_tests/test_kafka_dispatcher.py | 4 +- .../qt/_tests/test_qt_search_example.py | 2 + .../qt/_tests/test_qt_search_input.py | 8 +-- .../test_qt_viewer_with_search_example.py | 1 + bluesky_widgets/qt/figures.py | 19 +++---- bluesky_widgets/qt/kafka_dispatcher.py | 5 +- bluesky_widgets/qt/run_engine_client.py | 44 ++++++++-------- bluesky_widgets/qt/run_tree.py | 9 +--- bluesky_widgets/qt/search.py | 2 +- bluesky_widgets/qt/zmq_dispatcher.py | 5 +- bluesky_widgets/utils/dict_view.py | 3 +- bluesky_widgets/utils/streaming.py | 2 +- setup.py | 7 +-- 63 files changed, 285 insertions(+), 273 deletions(-) diff --git a/bluesky_widgets/_matplotlib_axes.py b/bluesky_widgets/_matplotlib_axes.py index 4de14a29..bace4d81 100644 --- a/bluesky_widgets/_matplotlib_axes.py +++ b/bluesky_widgets/_matplotlib_axes.py @@ -5,7 +5,7 @@ import logging -from .models.plot_specs import Axes, Line, Image +from .models.plot_specs import Axes, Image, Line class MatplotlibAxes: diff --git a/bluesky_widgets/apps/queue_monitor/main.py b/bluesky_widgets/apps/queue_monitor/main.py index b47f361e..9c03902c 100644 --- a/bluesky_widgets/apps/queue_monitor/main.py +++ b/bluesky_widgets/apps/queue_monitor/main.py @@ -3,8 +3,8 @@ from bluesky_widgets.qt import gui_qt -from .viewer import Viewer from .settings import SETTINGS +from .viewer import Viewer def main(argv=None): diff --git a/bluesky_widgets/apps/queue_monitor/viewer.py b/bluesky_widgets/apps/queue_monitor/viewer.py index 8cc96080..8ebd3a29 100644 --- a/bluesky_widgets/apps/queue_monitor/viewer.py +++ b/bluesky_widgets/apps/queue_monitor/viewer.py @@ -1,10 +1,12 @@ +import os + +from qtpy.QtWidgets import QAction, QFileDialog + from bluesky_widgets.models.run_engine_client import RunEngineClient from bluesky_widgets.qt import Window -from .widgets import QtViewer from .settings import SETTINGS - -from qtpy.QtWidgets import QAction +from .widgets import QtViewer class ViewerModel: @@ -32,6 +34,8 @@ def __init__(self, *, show=True, title="Demo App"): # TODO Where does title thread through? super().__init__() + self._work_dir = os.path.expanduser("~") + self._widget = QtViewer(self) self._window = Window(self._widget, show=show) @@ -43,6 +47,17 @@ def __init__(self, *, show=True, title="Demo App"): self.action_activate_env_destroy.triggered.connect(self._activate_env_destroy_triggered) menu_item_control.addAction(self.action_activate_env_destroy) + menu_item_control = menu_bar.addMenu("Save/Restore") + self.action_save_history_as_txt = QAction("Save Plan History (as .txt)", self._window._qt_window) + self.action_save_history_as_txt.triggered.connect(self._save_history_as_txt_triggered) + menu_item_control.addAction(self.action_save_history_as_txt) + self.action_save_history_as_json = QAction("Save Plan History (as .json)", self._window._qt_window) + self.action_save_history_as_json.triggered.connect(self._save_history_as_json_triggered) + menu_item_control.addAction(self.action_save_history_as_json) + self.action_save_history_as_yaml = QAction("Save Plan History (as .yaml)", self._window._qt_window) + self.action_save_history_as_yaml.triggered.connect(self._save_history_as_yaml_triggered) + menu_item_control.addAction(self.action_save_history_as_yaml) + self._widget.model.run_engine.events.status_changed.connect(self.on_update_widgets) def _update_action_env_destroy_state(self): @@ -53,6 +68,33 @@ def _activate_env_destroy_triggered(self): env_destroy_activated = self._widget.model.run_engine.env_destroy_activated self._widget.model.run_engine.activate_env_destroy(not env_destroy_activated) + def _save_history_as_txt_triggered(self): + self._save_history_to_file("txt") + + def _save_history_as_json_triggered(self): + self._save_history_to_file("json") + + def _save_history_as_yaml_triggered(self): + self._save_history_to_file("yaml") + + def _save_history_to_file(self, file_format): + try: + fln_pattern = f"{file_format.upper()} (*.{file_format.lower()});; All (*)" + file_path_init = os.path.join(self._work_dir, "plan_history." + file_format.lower()) + file_path_tuple = QFileDialog.getSaveFileName( + self._widget, "Save Plan History to File", file_path_init, fln_pattern + ) + file_path = file_path_tuple[0] + if file_path: + file_path = file_path_tuple[0] + self._work_dir = os.path.dirname(file_path) + self._widget.model.run_engine.save_plan_history_to_file( + file_path=file_path, file_format=file_format + ) + print(f"Plan history was successfully saved to file {file_path!r}") + except Exception as ex: + print(f"Failed to save data to file: {ex}") + def on_update_widgets(self, event): self._update_action_env_destroy_state() diff --git a/bluesky_widgets/apps/queue_monitor/widgets.py b/bluesky_widgets/apps/queue_monitor/widgets.py index 22cd0e83..54a16541 100644 --- a/bluesky_widgets/apps/queue_monitor/widgets.py +++ b/bluesky_widgets/apps/queue_monitor/widgets.py @@ -2,27 +2,21 @@ Extendeding and supplementing the widgets import bluesky-widgets """ +from qtpy.QtCore import Qt +from qtpy.QtWidgets import QFrame, QHBoxLayout, QSplitter, QTabWidget, QVBoxLayout, QWidget + from bluesky_widgets.qt.run_engine_client import ( + QtReConsoleMonitor, QtReEnvironmentControls, - QtReManagerConnection, - QtReQueueControls, QtReExecutionControls, - QtReStatusMonitor, - QtRePlanQueue, + QtReManagerConnection, + QtRePlanEditor, QtRePlanHistory, + QtRePlanQueue, + QtReQueueControls, QtReRunningPlan, - QtRePlanEditor, - QtReConsoleMonitor, -) -from qtpy.QtWidgets import ( - QWidget, - QHBoxLayout, - QVBoxLayout, - QTabWidget, - QFrame, - QSplitter, + QtReStatusMonitor, ) -from qtpy.QtCore import Qt class QtOrganizeQueueWidgets(QSplitter): diff --git a/bluesky_widgets/examples/advanced/qt_viewer_with_search.py b/bluesky_widgets/examples/advanced/qt_viewer_with_search.py index 1fd4825f..33387909 100644 --- a/bluesky_widgets/examples/advanced/qt_viewer_with_search.py +++ b/bluesky_widgets/examples/advanced/qt_viewer_with_search.py @@ -13,16 +13,16 @@ python -m bluesky_widgets.examples.advanced.qt_viewer_with_search localhost:XXXXX """ -from bluesky_widgets.qt import Window -from bluesky_widgets.qt import gui_qt -from bluesky_widgets.models.search import SearchList, Search +from qtpy.QtWidgets import QHBoxLayout, QPushButton, QVBoxLayout, QWidget + +from bluesky_widgets.examples.utils.add_search_mixin import columns +from bluesky_widgets.examples.utils.generate_msgpack_data import get_catalog from bluesky_widgets.models.auto_plot_builders import AutoLines -from bluesky_widgets.qt.search import QtSearches +from bluesky_widgets.models.search import Search, SearchList +from bluesky_widgets.qt import Window, gui_qt from bluesky_widgets.qt.figures import QtFigures +from bluesky_widgets.qt.search import QtSearches from bluesky_widgets.utils.event import Event -from bluesky_widgets.examples.utils.generate_msgpack_data import get_catalog -from bluesky_widgets.examples.utils.add_search_mixin import columns -from qtpy.QtWidgets import QWidget, QPushButton, QHBoxLayout, QVBoxLayout class SearchListWithButton(SearchList): @@ -121,9 +121,7 @@ def main(argv): # Optional: Receive live streaming data. if len(argv) > 1: from bluesky_widgets.qt.zmq_dispatcher import RemoteDispatcher - from bluesky_widgets.utils.streaming import ( - stream_documents_into_runs, - ) + from bluesky_widgets.utils.streaming import stream_documents_into_runs address = argv[1] dispatcher = RemoteDispatcher(address) diff --git a/bluesky_widgets/examples/advanced/qt_with_RE_worker.py b/bluesky_widgets/examples/advanced/qt_with_RE_worker.py index 64e7fe32..841faf68 100644 --- a/bluesky_widgets/examples/advanced/qt_with_RE_worker.py +++ b/bluesky_widgets/examples/advanced/qt_with_RE_worker.py @@ -12,16 +12,17 @@ python -m bluesky_widgets.examples.advanced.qt_with_RE_worker tcp://localhost:60615 localhost:5578 """ # noqa E501 -from bluesky_widgets.utils.streaming import stream_documents_into_runs -from bluesky_widgets.qt.zmq_dispatcher import RemoteDispatcher -from bluesky_widgets.models.plot_builders import Lines -from bluesky_widgets.qt.figures import QtFigure -from bluesky_widgets.qt import gui_qt +import sys +import time + from bluesky_queueserver.manager.comms import ZMQCommSendThreads from bluesky_queueserver_api._defaults import default_user_group -import sys -import time +from bluesky_widgets.models.plot_builders import Lines +from bluesky_widgets.qt import gui_qt +from bluesky_widgets.qt.figures import QtFigure +from bluesky_widgets.qt.zmq_dispatcher import RemoteDispatcher +from bluesky_widgets.utils.streaming import stream_documents_into_runs def main(): diff --git a/bluesky_widgets/examples/headless_figures.py b/bluesky_widgets/examples/headless_figures.py index 731e7007..112176c6 100644 --- a/bluesky_widgets/examples/headless_figures.py +++ b/bluesky_widgets/examples/headless_figures.py @@ -10,12 +10,12 @@ from bluesky import RunEngine from bluesky.plans import scan -from ophyd.sim import motor, det +from ophyd.sim import det, motor -from bluesky_widgets.utils.streaming import stream_documents_into_runs -from bluesky_widgets.models.auto_plot_builders import AutoLines -from bluesky_widgets.headless.figures import HeadlessFigures from bluesky_widgets.examples.utils.generate_msgpack_data import get_catalog +from bluesky_widgets.headless.figures import HeadlessFigures +from bluesky_widgets.models.auto_plot_builders import AutoLines +from bluesky_widgets.utils.streaming import stream_documents_into_runs model = AutoLines(max_runs=3) view = HeadlessFigures(model.figures) diff --git a/bluesky_widgets/examples/ipy_qt_figure.py b/bluesky_widgets/examples/ipy_qt_figure.py index aa9753cc..0b3c6955 100644 --- a/bluesky_widgets/examples/ipy_qt_figure.py +++ b/bluesky_widgets/examples/ipy_qt_figure.py @@ -8,12 +8,12 @@ from bluesky import RunEngine from bluesky.plans import scan -from ophyd.sim import motor, det +from ophyd.sim import det, motor -from bluesky_widgets.utils.streaming import stream_documents_into_runs +from bluesky_widgets.examples.utils.generate_msgpack_data import get_catalog from bluesky_widgets.models.plot_builders import Lines from bluesky_widgets.qt.figures import QtFigure -from bluesky_widgets.examples.utils.generate_msgpack_data import get_catalog +from bluesky_widgets.utils.streaming import stream_documents_into_runs model = Lines("motor", ["det"], max_runs=3) view = QtFigure(model.figure) diff --git a/bluesky_widgets/examples/ipy_qt_figures.py b/bluesky_widgets/examples/ipy_qt_figures.py index 1bdf537f..be58388c 100644 --- a/bluesky_widgets/examples/ipy_qt_figures.py +++ b/bluesky_widgets/examples/ipy_qt_figures.py @@ -8,12 +8,12 @@ from bluesky import RunEngine from bluesky.plans import scan -from ophyd.sim import motor, det +from ophyd.sim import det, motor -from bluesky_widgets.utils.streaming import stream_documents_into_runs +from bluesky_widgets.examples.utils.generate_msgpack_data import get_catalog from bluesky_widgets.models.auto_plot_builders import AutoLines from bluesky_widgets.qt.figures import QtFigures -from bluesky_widgets.examples.utils.generate_msgpack_data import get_catalog +from bluesky_widgets.utils.streaming import stream_documents_into_runs model = AutoLines(max_runs=3) view = QtFigures(model.figures) diff --git a/bluesky_widgets/examples/jupyter_viewer.py b/bluesky_widgets/examples/jupyter_viewer.py index 39c2f614..3877d27f 100644 --- a/bluesky_widgets/examples/jupyter_viewer.py +++ b/bluesky_widgets/examples/jupyter_viewer.py @@ -13,8 +13,8 @@ python -m bluesky_widgets.examples.qt_viewer_with_search localhost:XXXXX """ -from bluesky_widgets.models.auto_plot_builders import AutoLines from bluesky_widgets.jupyter.figures import JupyterFigures +from bluesky_widgets.models.auto_plot_builders import AutoLines class ExampleApp: diff --git a/bluesky_widgets/examples/kafka_figures.py b/bluesky_widgets/examples/kafka_figures.py index b1caa507..91f09696 100644 --- a/bluesky_widgets/examples/kafka_figures.py +++ b/bluesky_widgets/examples/kafka_figures.py @@ -7,18 +7,18 @@ directory. The filepaths will be printed to the stdout, one per line. """ -from functools import partial import os import tempfile +from functools import partial import msgpack import msgpack_numpy as mpn from bluesky_kafka import RemoteDispatcher -from bluesky_widgets.utils.streaming import stream_documents_into_runs -from bluesky_widgets.models.plot_builders import AutoLines from bluesky_widgets.headless.figures import HeadlessFigures +from bluesky_widgets.models.plot_builders import AutoLines from bluesky_widgets.models.utils import run_is_live_and_not_completed +from bluesky_widgets.utils.streaming import stream_documents_into_runs def export_thumbnails_when_complete(run): diff --git a/bluesky_widgets/examples/napari_dock_widgets.py b/bluesky_widgets/examples/napari_dock_widgets.py index f224c81e..919e05d2 100644 --- a/bluesky_widgets/examples/napari_dock_widgets.py +++ b/bluesky_widgets/examples/napari_dock_widgets.py @@ -1,12 +1,12 @@ import napari +from qtpy.QtWidgets import QPushButton, QVBoxLayout, QWidget from bluesky_widgets.examples.utils.add_search_mixin import extract_results_row_from_run from bluesky_widgets.examples.utils.generate_msgpack_data import get_catalog from bluesky_widgets.examples.utils.get_run_images import generate_thumbnail from bluesky_widgets.models.search import Search, SearchList -from bluesky_widgets.utils.event import Event -from qtpy.QtWidgets import QWidget, QPushButton, QVBoxLayout from bluesky_widgets.qt.search import QtSearches +from bluesky_widgets.utils.event import Event class SearchListWithButton(SearchList): diff --git a/bluesky_widgets/examples/pyFAI_dialog.py b/bluesky_widgets/examples/pyFAI_dialog.py index c0ba619e..d7adea19 100755 --- a/bluesky_widgets/examples/pyFAI_dialog.py +++ b/bluesky_widgets/examples/pyFAI_dialog.py @@ -40,13 +40,12 @@ __date__ = "21/01/2020" __status__ = "production" +import datetime +import logging import os import sys -import datetime from argparse import ArgumentParser -import logging - logging.basicConfig(level=logging.INFO) logging.captureWarnings(True) logger = logging.getLogger(__name__) @@ -57,10 +56,10 @@ logger_uncaught = logging.getLogger("pyFAI-calib2.UNCAUGHT") -import pyFAI.resources import pyFAI.calibrant import pyFAI.detectors import pyFAI.io.image +import pyFAI.resources from pyFAI.io.ponifile import PoniFile try: @@ -568,10 +567,9 @@ def setup_model(model, options): args = options.args # The module must not import the GUI - from pyFAI.gui.utils import units - # TODO: This should be removed import pyFAI.gui.cli_calibration + from pyFAI.gui.utils import units # Settings settings = model.experimentSettingsModel() @@ -793,8 +791,8 @@ def setup_model(model, options): if options.npt: try: - from pyFAI.gui.helper import model_transform from pyFAI.control_points import ControlPoints + from pyFAI.gui.helper import model_transform controlPoints = ControlPoints(filename=options.npt) peakSelectionModel = model.peakSelectionModel() @@ -842,8 +840,8 @@ def main(): # Make sure matplotlib is loaded first by silx import silx.gui.plot.matplotlib - from pyFAI.gui.CalibrationWindow import CalibrationWindow from pyFAI.gui.CalibrationContext import CalibrationContext + from pyFAI.gui.CalibrationWindow import CalibrationWindow sys.excepthook = logUncaughtExceptions if options.qtargs is None: @@ -863,13 +861,12 @@ def main(): # Begin modifications for bluesky_widgets - from qtpy.QtWidgets import QDialog + from qtpy.QtWidgets import QAction, QDialog, QHBoxLayout, QPushButton, QVBoxLayout, QWidget + + from bluesky_widgets.examples.utils.add_search_mixin import columns + from bluesky_widgets.examples.utils.generate_msgpack_data import get_catalog from bluesky_widgets.models.search import Search from bluesky_widgets.qt.search import QtSearch - from bluesky_widgets.examples.utils.generate_msgpack_data import get_catalog - from bluesky_widgets.examples.utils.add_search_mixin import columns - - from qtpy.QtWidgets import QAction, QHBoxLayout, QPushButton, QVBoxLayout, QWidget example_catalog = get_catalog() diff --git a/bluesky_widgets/examples/qt_app_integration.py b/bluesky_widgets/examples/qt_app_integration.py index 0f3e8391..2a079d1b 100644 --- a/bluesky_widgets/examples/qt_app_integration.py +++ b/bluesky_widgets/examples/qt_app_integration.py @@ -4,7 +4,7 @@ python -m bluesky_widgets.examples.qt_app_integration """ -from qtpy.QtWidgets import QApplication, QVBoxLayout, QLabel, QMainWindow, QWidget +from qtpy.QtWidgets import QApplication, QLabel, QMainWindow, QVBoxLayout, QWidget def main(): @@ -40,9 +40,10 @@ def main(): # over a network via publish--subscribe. See # bluesky_widgets.examples.advanced.qt_viewer_with_search for an example of # that. Here, we keep it simple. - from bluesky_widgets.utils.streaming import stream_documents_into_runs from bluesky import RunEngine + from bluesky_widgets.utils.streaming import stream_documents_into_runs + RE = RunEngine() RE.subscribe(stream_documents_into_runs(model.add_run)) @@ -60,7 +61,7 @@ def main(): # the results over a network. from bluesky.plans import scan - from ophyd.sim import motor, det + from ophyd.sim import det, motor def plan(): for i in range(1, 5): diff --git a/bluesky_widgets/examples/qt_app_search_integration.py b/bluesky_widgets/examples/qt_app_search_integration.py index 007bd8bc..cc113935 100644 --- a/bluesky_widgets/examples/qt_app_search_integration.py +++ b/bluesky_widgets/examples/qt_app_search_integration.py @@ -4,18 +4,11 @@ python -m bluesky_widgets.examples.qt_app_integration """ -from qtpy.QtWidgets import ( - QApplication, - QPushButton, - QVBoxLayout, - QLabel, - QMainWindow, - QWidget, -) -from bluesky_widgets.models.search import Search -from bluesky_widgets.qt.search import QtSearch from bluesky_live.event import Event +from qtpy.QtWidgets import QApplication, QLabel, QMainWindow, QPushButton, QVBoxLayout, QWidget +from bluesky_widgets.models.search import Search +from bluesky_widgets.qt.search import QtSearch # Extend the search widget with a single button. In your application, you might # want multiple buttons that do different things. diff --git a/bluesky_widgets/examples/qt_run_tree_view.py b/bluesky_widgets/examples/qt_run_tree_view.py index 33ff48bb..a9c60bd2 100644 --- a/bluesky_widgets/examples/qt_run_tree_view.py +++ b/bluesky_widgets/examples/qt_run_tree_view.py @@ -4,11 +4,10 @@ on the command line to explore runs. """ +from bluesky_widgets.examples.utils.generate_msgpack_data import get_catalog from bluesky_widgets.models.run_tree import RunTree -from bluesky_widgets.qt import Window -from bluesky_widgets.qt import gui_qt +from bluesky_widgets.qt import Window, gui_qt from bluesky_widgets.qt.run_tree import QtTreeView -from bluesky_widgets.examples.utils.generate_msgpack_data import get_catalog class RunTree(RunTree): diff --git a/bluesky_widgets/examples/qt_search.py b/bluesky_widgets/examples/qt_search.py index 322f0c9c..11766dd0 100644 --- a/bluesky_widgets/examples/qt_search.py +++ b/bluesky_widgets/examples/qt_search.py @@ -4,12 +4,13 @@ or visualization. """ +from qtpy.QtWidgets import QPushButton, QVBoxLayout, QWidget + +from bluesky_widgets.examples.utils.add_search_mixin import columns +from bluesky_widgets.examples.utils.generate_mongo_data import get_catalog +from bluesky_widgets.models.search import Search, SearchList from bluesky_widgets.qt import Window, gui_qt -from bluesky_widgets.models.search import SearchList, Search from bluesky_widgets.qt.search import QtSearches -from bluesky_widgets.examples.utils.generate_mongo_data import get_catalog -from bluesky_widgets.examples.utils.add_search_mixin import columns -from qtpy.QtWidgets import QWidget, QPushButton, QVBoxLayout class SearchListWithButton(SearchList): diff --git a/bluesky_widgets/examples/utils/generate_mongo_data.py b/bluesky_widgets/examples/utils/generate_mongo_data.py index cd85f3f2..bb6a9938 100644 --- a/bluesky_widgets/examples/utils/generate_mongo_data.py +++ b/bluesky_widgets/examples/utils/generate_mongo_data.py @@ -1,13 +1,12 @@ # Based on databroker/examples/generate_mongo_data.py -from suitcase.mongo_normalized import Serializer -from bluesky import RunEngine -from bluesky.plans import count, scan -from ophyd.sim import det, motor, SynSignal -import numpy as np import uuid +import numpy as np +from bluesky import RunEngine +from bluesky.plans import count, scan from databroker._drivers.mongo_normalized import BlueskyMongoCatalog - +from ophyd.sim import SynSignal, det, motor +from suitcase.mongo_normalized import Serializer random_img = SynSignal(func=lambda: np.random.random((5, 10, 10)), name="random_img") diff --git a/bluesky_widgets/examples/utils/generate_msgpack_data.py b/bluesky_widgets/examples/utils/generate_msgpack_data.py index cba76ec2..45a522cd 100644 --- a/bluesky_widgets/examples/utils/generate_msgpack_data.py +++ b/bluesky_widgets/examples/utils/generate_msgpack_data.py @@ -1,12 +1,11 @@ import tempfile -from suitcase.msgpack import Serializer -from bluesky import RunEngine -from bluesky.plans import count, scan, grid_scan -from ophyd.sim import det, motor, SynSignal, det4, motor1, motor2 -import numpy as np +import numpy as np +from bluesky import RunEngine +from bluesky.plans import count, grid_scan, scan from databroker._drivers.msgpack import BlueskyMsgpackCatalog - +from ophyd.sim import SynSignal, det, det4, motor, motor1, motor2 +from suitcase.msgpack import Serializer random_img = SynSignal(func=lambda: np.random.random((5, 10, 10)), name="random_img") diff --git a/bluesky_widgets/examples/utils/stream_data_kafka.py b/bluesky_widgets/examples/utils/stream_data_kafka.py index 03fc0d85..90f596a9 100644 --- a/bluesky_widgets/examples/utils/stream_data_kafka.py +++ b/bluesky_widgets/examples/utils/stream_data_kafka.py @@ -13,12 +13,11 @@ import msgpack import msgpack_numpy as mpn - from bluesky import RunEngine -from bluesky_kafka import Publisher -from bluesky.plans import scan from bluesky.plan_stubs import sleep -from ophyd.sim import motor, det +from bluesky.plans import scan +from bluesky_kafka import Publisher +from ophyd.sim import det, motor log = logging.getLogger(__name__) diff --git a/bluesky_widgets/examples/utils/stream_data_zmq.py b/bluesky_widgets/examples/utils/stream_data_zmq.py index 10907f9f..a5fb78b2 100644 --- a/bluesky_widgets/examples/utils/stream_data_zmq.py +++ b/bluesky_widgets/examples/utils/stream_data_zmq.py @@ -1,19 +1,18 @@ import asyncio import logging +import tempfile from multiprocessing import Process, Queue from pathlib import Path -import tempfile -from bluesky.callbacks.zmq import Publisher -from bluesky.callbacks import LiveTable -from bluesky.preprocessors import SupplementalData -from suitcase.jsonl import Serializer from bluesky import RunEngine -from ophyd.sim import det, motor, motor1, motor2 +from bluesky.callbacks import LiveTable +from bluesky.callbacks.zmq import Publisher +from bluesky.plan_stubs import sleep from bluesky.plans import scan +from bluesky.preprocessors import SupplementalData from event_model import RunRouter -from bluesky.plan_stubs import sleep - +from ophyd.sim import det, motor, motor1, motor2 +from suitcase.jsonl import Serializer log = logging.getLogger(__name__) diff --git a/bluesky_widgets/headless/_tests/test_closing.py b/bluesky_widgets/headless/_tests/test_closing.py index ac3e2f4c..8382f293 100644 --- a/bluesky_widgets/headless/_tests/test_closing.py +++ b/bluesky_widgets/headless/_tests/test_closing.py @@ -1,7 +1,6 @@ import matplotlib.pyplot as plt - -from ...models.plot_specs import Figure, Axes +from ...models.plot_specs import Axes, Figure from ..figures import HeadlessFigure diff --git a/bluesky_widgets/headless/figures.py b/bluesky_widgets/headless/figures.py index 5e96927a..7dd4f63e 100644 --- a/bluesky_widgets/headless/figures.py +++ b/bluesky_widgets/headless/figures.py @@ -1,9 +1,10 @@ import collections.abc from pathlib import Path + import matplotlib -from ..models.plot_specs import Figure, FigureList from .._matplotlib_axes import MatplotlibAxes +from ..models.plot_specs import Figure, FigureList from ..utils.dict_view import DictView diff --git a/bluesky_widgets/jupyter/_search_input.py b/bluesky_widgets/jupyter/_search_input.py index 65c01d1c..8b689af6 100644 --- a/bluesky_widgets/jupyter/_search_input.py +++ b/bluesky_widgets/jupyter/_search_input.py @@ -1,18 +1,10 @@ import contextlib +from datetime import datetime, timedelta -from ipywidgets.widgets import ( - Text, - VBox, - RadioButtons, - HBox, - Label, - Button, - Layout, - GridBox, -) -from traitlets import All from ipydatetime import DatetimePicker -from datetime import datetime, timedelta +from ipywidgets.widgets import Button, GridBox, HBox, Label, Layout, RadioButtons, Text, VBox +from traitlets import All + from bluesky_widgets.models.search import LOCAL_TIMEZONE diff --git a/bluesky_widgets/jupyter/figures.py b/bluesky_widgets/jupyter/figures.py index 224031d9..1dd8db01 100644 --- a/bluesky_widgets/jupyter/figures.py +++ b/bluesky_widgets/jupyter/figures.py @@ -1,9 +1,9 @@ -from ipywidgets import widgets import ipympl.backend_nbagg import matplotlib.figure +from ipywidgets import widgets -from ..models.plot_specs import Figure, FigureList from .._matplotlib_axes import MatplotlibAxes as _MatplotilbAxes +from ..models.plot_specs import Figure, FigureList from ..utils.dict_view import DictView diff --git a/bluesky_widgets/jupyter/zmq_dispatcher.py b/bluesky_widgets/jupyter/zmq_dispatcher.py index a7db8a52..20cd75aa 100644 --- a/bluesky_widgets/jupyter/zmq_dispatcher.py +++ b/bluesky_widgets/jupyter/zmq_dispatcher.py @@ -1,10 +1,10 @@ -import pickle import multiprocessing -from queue import Empty +import pickle import threading +from queue import Empty -from bluesky.run_engine import Dispatcher, DocumentNames import zmq +from bluesky.run_engine import Dispatcher, DocumentNames class RemoteDispatcher: diff --git a/bluesky_widgets/models/_tests/test_figures.py b/bluesky_widgets/models/_tests/test_figures.py index 492f75b9..eb6dac4a 100644 --- a/bluesky_widgets/models/_tests/test_figures.py +++ b/bluesky_widgets/models/_tests/test_figures.py @@ -1,16 +1,8 @@ -from bluesky_live.run_builder import RunBuilder -from bluesky_live.event import CallbackException import pytest +from bluesky_live.event import CallbackException +from bluesky_live.run_builder import RunBuilder -from ...models.plot_specs import ( - AxesAlreadySet, - Figure, - FigureList, - Axes, - Line, - Image, -) - +from ...models.plot_specs import Axes, AxesAlreadySet, Figure, FigureList, Image, Line # Generate example data. with RunBuilder() as builder: diff --git a/bluesky_widgets/models/_tests/test_image.py b/bluesky_widgets/models/_tests/test_image.py index ed7d4f7b..cfd4c8c6 100644 --- a/bluesky_widgets/models/_tests/test_image.py +++ b/bluesky_widgets/models/_tests/test_image.py @@ -1,5 +1,5 @@ -from bluesky_live.run_builder import build_simple_run import numpy +from bluesky_live.run_builder import build_simple_run from ..plot_builders import Images from ..plot_specs import Axes, Figure diff --git a/bluesky_widgets/models/_tests/test_lines.py b/bluesky_widgets/models/_tests/test_lines.py index 95b5062e..2a257ec6 100644 --- a/bluesky_widgets/models/_tests/test_lines.py +++ b/bluesky_widgets/models/_tests/test_lines.py @@ -1,10 +1,9 @@ -from bluesky_live.run_builder import build_simple_run import pytest +from bluesky_live.run_builder import build_simple_run from ..plot_builders import Lines from ..plot_specs import Axes, Figure - # Make some runs to use. runs = [ build_simple_run( diff --git a/bluesky_widgets/models/_tests/test_rastered_image.py b/bluesky_widgets/models/_tests/test_rastered_image.py index d9718a2d..9f8b4fea 100644 --- a/bluesky_widgets/models/_tests/test_rastered_image.py +++ b/bluesky_widgets/models/_tests/test_rastered_image.py @@ -1,9 +1,9 @@ -from bluesky_live.run_builder import RunBuilder -import pytest import numpy +import pytest +from bluesky_live.run_builder import RunBuilder from ..plot_builders import RasteredImages -from ..plot_specs import Axes, Image, Figure +from ..plot_specs import Axes, Figure, Image @pytest.fixture diff --git a/bluesky_widgets/models/_tests/test_search_input.py b/bluesky_widgets/models/_tests/test_search_input.py index 0c70fadd..5b0266fd 100644 --- a/bluesky_widgets/models/_tests/test_search_input.py +++ b/bluesky_widgets/models/_tests/test_search_input.py @@ -2,7 +2,7 @@ import pytest -from ..search import SearchInput, LOCAL_TIMEZONE +from ..search import LOCAL_TIMEZONE, SearchInput def test_instantiation(): diff --git a/bluesky_widgets/models/_tests/test_utils.py b/bluesky_widgets/models/_tests/test_utils.py index 6de07652..5addd70d 100644 --- a/bluesky_widgets/models/_tests/test_utils.py +++ b/bluesky_widgets/models/_tests/test_utils.py @@ -1,7 +1,7 @@ -from bluesky_live.run_builder import build_simple_run, RunBuilder import numpy -import xarray import pytest +import xarray +from bluesky_live.run_builder import RunBuilder, build_simple_run from ..utils import call_or_eval, construct_namespace diff --git a/bluesky_widgets/models/auto_plot_builders/__init__.py b/bluesky_widgets/models/auto_plot_builders/__init__.py index e779ed82..c00b0b62 100644 --- a/bluesky_widgets/models/auto_plot_builders/__init__.py +++ b/bluesky_widgets/models/auto_plot_builders/__init__.py @@ -1,3 +1,3 @@ from ._base import * # noqa: F401, F403 -from ._lines import * # noqa: F401, F403 from ._images import * # noqa: F401, F403 +from ._lines import * # noqa: F401, F403 diff --git a/bluesky_widgets/models/auto_plot_builders/_base.py b/bluesky_widgets/models/auto_plot_builders/_base.py index 995d598d..52e8a77b 100644 --- a/bluesky_widgets/models/auto_plot_builders/_base.py +++ b/bluesky_widgets/models/auto_plot_builders/_base.py @@ -1,8 +1,8 @@ import abc +from ...utils.list import EventedList from ..plot_specs import FigureList from ..utils import run_is_live_and_not_completed -from ...utils.list import EventedList class AutoPlotter(abc.ABC): diff --git a/bluesky_widgets/models/auto_plot_builders/_lines.py b/bluesky_widgets/models/auto_plot_builders/_lines.py index ba243b23..893499dc 100644 --- a/bluesky_widgets/models/auto_plot_builders/_lines.py +++ b/bluesky_widgets/models/auto_plot_builders/_lines.py @@ -1,8 +1,8 @@ from warnings import warn -from ..plot_builders import Lines -from ..plot_specs import Figure, Axes from .._heuristics import hinted_fields +from ..plot_builders import Lines +from ..plot_specs import Axes, Figure from ._base import AutoPlotter diff --git a/bluesky_widgets/models/auto_plot_builders/_tests/test_auto_images.py b/bluesky_widgets/models/auto_plot_builders/_tests/test_auto_images.py index fba98d86..eb715492 100644 --- a/bluesky_widgets/models/auto_plot_builders/_tests/test_auto_images.py +++ b/bluesky_widgets/models/auto_plot_builders/_tests/test_auto_images.py @@ -1,8 +1,8 @@ -from bluesky_live.run_builder import build_simple_run import numpy +from bluesky_live.run_builder import build_simple_run -from .. import AutoImages from ....headless.figures import HeadlessFigures +from .. import AutoImages def test_images(): diff --git a/bluesky_widgets/models/auto_plot_builders/_tests/test_auto_lines.py b/bluesky_widgets/models/auto_plot_builders/_tests/test_auto_lines.py index 2f35da0d..6b360304 100644 --- a/bluesky_widgets/models/auto_plot_builders/_tests/test_auto_lines.py +++ b/bluesky_widgets/models/auto_plot_builders/_tests/test_auto_lines.py @@ -1,8 +1,7 @@ from bluesky_live.run_builder import build_simple_run -from .. import AutoLines from ....headless.figures import HeadlessFigures - +from .. import AutoLines # Make some runs to use. runs = [ diff --git a/bluesky_widgets/models/plot_builders.py b/bluesky_widgets/models/plot_builders.py index d4c17e88..0155c9c6 100644 --- a/bluesky_widgets/models/plot_builders.py +++ b/bluesky_widgets/models/plot_builders.py @@ -4,16 +4,11 @@ import numpy -from .plot_specs import ( - Figure, - Axes, - Image, - Line, -) -from .utils import auto_label, call_or_eval, RunManager, run_is_live_and_not_completed from ..utils.dict_view import DictView from ..utils.event import EmitterGroup, Event from ..utils.list import EventedList +from .plot_specs import Axes, Figure, Image, Line +from .utils import RunManager, auto_label, call_or_eval, run_is_live_and_not_completed class Lines: diff --git a/bluesky_widgets/models/plot_specs.py b/bluesky_widgets/models/plot_specs.py index 88230d46..76d90f9a 100644 --- a/bluesky_widgets/models/plot_specs.py +++ b/bluesky_widgets/models/plot_specs.py @@ -10,9 +10,9 @@ import collections import uuid as uuid_module +from ..utils.dict_view import DictView, UpdateOnlyDict from ..utils.event import EmitterGroup, Event from ..utils.list import EventedList -from ..utils.dict_view import UpdateOnlyDict, DictView class BaseSpec: diff --git a/bluesky_widgets/models/run_engine_client.py b/bluesky_widgets/models/run_engine_client.py index 1b84da4d..7f5fff18 100644 --- a/bluesky_widgets/models/run_engine_client.py +++ b/bluesky_widgets/models/run_engine_client.py @@ -1,17 +1,19 @@ import collections import copy +import datetime +import importlib +import json import os.path import pprint import time -import importlib +import yaml from bluesky_live.event import EmitterGroup, Event from bluesky_queueserver import bind_plan_arguments from bluesky_queueserver.manager.conversions import spreadsheet_to_plan_list -from bluesky_queueserver_api.zmq import REManagerAPI as REManagerAPI_ZMQ -from bluesky_queueserver_api.http import REManagerAPI as REManagerAPI_HTTP - from bluesky_queueserver_api._defaults import default_user_group +from bluesky_queueserver_api.http import REManagerAPI as REManagerAPI_HTTP +from bluesky_queueserver_api.zmq import REManagerAPI as REManagerAPI_ZMQ class RunEngineClient: @@ -315,6 +317,47 @@ def load_plan_history(self): except Exception as ex: print(f"Exception: {ex}") + def save_plan_history_to_file(self, *, file_path, file_format): + """ + Save plan history to the file on locally mounted disk. The function + does not download the plan history from the server and can be called + when the client is disconnected from the server. + + Parameters + ---------- + file_path : str + Full path to the file + file_format : str + File format: "txt", "json" or "yaml" + """ + file_format = file_format.lower() + history = copy.copy(self._plan_history_items) + + if file_format == "txt": + with open(file_path, "wt") as f: + for n, plan in enumerate(history): + t_start = plan.get("result", {}).get("time_start", None) + t_stop = plan.get("result", {}).get("time_stop", None) + + s = f"PLAN {n + 1}" + if t_start: + s += f": {datetime.datetime.fromtimestamp(t_start)}" + if t_stop: + s += f" - {datetime.datetime.fromtimestamp(t_stop)}" + + f.write("=" * 80) + f.write(f"\n{s: ^80}\n") + f.write("=" * 80) + f.write(f"\n{pprint.pformat(plan, width=80)}\n") + elif file_format == "json": + with open(file_path, "wt") as f: + json.dump(history, f, indent=2) + elif file_format == "yaml": + with open(file_path, "w") as f: + yaml.dump(history, f) + else: + raise ValueError(f"Unsupported output format: {file_format!r}") + # ============================================================================ # Useful functions def get_allowed_plan_parameters(self, *, name): diff --git a/bluesky_widgets/models/run_tree.py b/bluesky_widgets/models/run_tree.py index 787a9da4..7fcc4d9e 100644 --- a/bluesky_widgets/models/run_tree.py +++ b/bluesky_widgets/models/run_tree.py @@ -1,4 +1,4 @@ -from ..utils.event import Event, EmitterGroup +from ..utils.event import EmitterGroup, Event class RunTree: diff --git a/bluesky_widgets/models/search.py b/bluesky_widgets/models/search.py index b17ab39f..93e36dda 100644 --- a/bluesky_widgets/models/search.py +++ b/bluesky_widgets/models/search.py @@ -1,13 +1,13 @@ import abc import collections.abc -from datetime import datetime, timedelta import itertools +from datetime import datetime, timedelta import dateutil.tz -from ..utils.list import EventedList -from ..utils.event import EmitterGroup, Event from ..utils.dict_view import UpdateOnlyDict +from ..utils.event import EmitterGroup, Event +from ..utils.list import EventedList LOCAL_TIMEZONE = dateutil.tz.tzlocal() _epoch = datetime(1970, 1, 1, 0, 0, tzinfo=LOCAL_TIMEZONE) diff --git a/bluesky_widgets/models/utils.py b/bluesky_widgets/models/utils.py index 057c0134..437111cb 100644 --- a/bluesky_widgets/models/utils.py +++ b/bluesky_widgets/models/utils.py @@ -5,8 +5,8 @@ import numpy -from ..utils.list import EventedList from ..utils.event import EmitterGroup, Event +from ..utils.list import EventedList class RunList(EventedList): diff --git a/bluesky_widgets/qt/__init__.py b/bluesky_widgets/qt/__init__.py index e997ef2d..2e5dcc0a 100644 --- a/bluesky_widgets/qt/__init__.py +++ b/bluesky_widgets/qt/__init__.py @@ -1,2 +1,2 @@ -from ._main_window import Window # noqa: F401 from ._event_loop import gui_qt # noqa: F401 +from ._main_window import Window # noqa: F401 diff --git a/bluesky_widgets/qt/_event_loop.py b/bluesky_widgets/qt/_event_loop.py index 8cc81823..1c4cdaf4 100644 --- a/bluesky_widgets/qt/_event_loop.py +++ b/bluesky_widgets/qt/_event_loop.py @@ -5,7 +5,6 @@ from .threading import wait_for_workers_to_quit - _our_app_name = None diff --git a/bluesky_widgets/qt/_main_window.py b/bluesky_widgets/qt/_main_window.py index 14d59523..02ade8f7 100644 --- a/bluesky_widgets/qt/_main_window.py +++ b/bluesky_widgets/qt/_main_window.py @@ -5,16 +5,9 @@ import time -from qtpy.QtWidgets import ( # noqa: E402 - QApplication, - QMainWindow, - QWidget, - QHBoxLayout, - QLabel, - QStatusBar, - QFileDialog, -) from qtpy.QtCore import Qt # noqa: E402 +from qtpy.QtWidgets import QFileDialog # noqa: E402 +from qtpy.QtWidgets import QApplication, QHBoxLayout, QLabel, QMainWindow, QStatusBar, QWidget from ._event_loop import get_our_app_name from .threading import wait_for_workers_to_quit @@ -172,6 +165,7 @@ def screenshot(self, path=None): img = self._qt_window.grab().toImage() if path is not None: from skimage.io import imsave + from .utils import QImg2array # noqa: E402 imsave(path, QImg2array(img)) diff --git a/bluesky_widgets/qt/_search_input.py b/bluesky_widgets/qt/_search_input.py index b8f07fb4..df0939ed 100644 --- a/bluesky_widgets/qt/_search_input.py +++ b/bluesky_widgets/qt/_search_input.py @@ -1,18 +1,20 @@ import contextlib from datetime import datetime, timedelta + from qtpy.QtCore import QDateTime from qtpy.QtWidgets import ( QButtonGroup, QDateTimeEdit, - QWidget, - QPushButton, QFormLayout, - QRadioButton, QGridLayout, - QLineEdit, QHBoxLayout, QLabel, + QLineEdit, + QPushButton, + QRadioButton, + QWidget, ) + from ..models.search import LOCAL_TIMEZONE, secs_since_epoch diff --git a/bluesky_widgets/qt/_search_results.py b/bluesky_widgets/qt/_search_results.py index 26972c36..ce627034 100644 --- a/bluesky_widgets/qt/_search_results.py +++ b/bluesky_widgets/qt/_search_results.py @@ -2,18 +2,11 @@ import logging from qtpy import QtCore -from qtpy.QtCore import ( - QAbstractTableModel, - # QItemSelection, - # QItemSelectionModel, - QTimer, - Qt, -) +from qtpy.QtCore import QAbstractTableModel, Qt, QTimer # QItemSelection,; QItemSelectionModel, from qtpy.QtWidgets import QAbstractItemView, QHeaderView, QTableView from .threading import create_worker - logger = logging.getLogger(__name__) LOADING_PLACEHOLDER = "..." CHUNK_SIZE = 5 # max rows to fetch at once diff --git a/bluesky_widgets/qt/_searches.py b/bluesky_widgets/qt/_searches.py index 02117cc0..ff2fe777 100644 --- a/bluesky_widgets/qt/_searches.py +++ b/bluesky_widgets/qt/_searches.py @@ -1,15 +1,8 @@ import logging from qtpy.QtCore import QStringListModel -from qtpy.QtWidgets import ( - QComboBox, - QPushButton, - QTabWidget, - QVBoxLayout, - QWidget, - QSpacerItem, - QSizePolicy, -) +from qtpy.QtWidgets import QComboBox, QPushButton, QSizePolicy, QSpacerItem, QTabWidget, QVBoxLayout, QWidget + from ._search_input import QtSearchInput from ._search_results import QtSearchResults diff --git a/bluesky_widgets/qt/_tests/conftest.py b/bluesky_widgets/qt/_tests/conftest.py index 371c0057..b7d79515 100644 --- a/bluesky_widgets/qt/_tests/conftest.py +++ b/bluesky_widgets/qt/_tests/conftest.py @@ -1,15 +1,13 @@ import pytest - -from qtpy.QtWidgets import QApplication - -from bluesky_kafka.tests.conftest import ( # noqa +from bluesky_kafka.tests.conftest import ( # noqa F401 + broker_authorization_config, hw, - pytest_addoption, kafka_bootstrap_servers, - broker_authorization_config, publisher_factory, + pytest_addoption, temporary_topics, ) +from qtpy.QtWidgets import QApplication @pytest.fixture diff --git a/bluesky_widgets/qt/_tests/test_kafka_dispatcher.py b/bluesky_widgets/qt/_tests/test_kafka_dispatcher.py index 7999a890..19bfb4d7 100644 --- a/bluesky_widgets/qt/_tests/test_kafka_dispatcher.py +++ b/bluesky_widgets/qt/_tests/test_kafka_dispatcher.py @@ -5,10 +5,8 @@ from bluesky.plans import count from bluesky_widgets.models.plot_builders import Lines -from bluesky_widgets.utils.streaming import stream_documents_into_runs - from bluesky_widgets.qt.kafka_dispatcher import QtRemoteDispatcher - +from bluesky_widgets.utils.streaming import stream_documents_into_runs test_logger = logging.getLogger("bluesky_widgets.qt.tests") diff --git a/bluesky_widgets/qt/_tests/test_qt_search_example.py b/bluesky_widgets/qt/_tests/test_qt_search_example.py index 27ba3f0d..56b04b4a 100644 --- a/bluesky_widgets/qt/_tests/test_qt_search_example.py +++ b/bluesky_widgets/qt/_tests/test_qt_search_example.py @@ -1,5 +1,7 @@ from datetime import datetime, timedelta + import pytest + from ...examples.qt_search import ExampleApp diff --git a/bluesky_widgets/qt/_tests/test_qt_search_input.py b/bluesky_widgets/qt/_tests/test_qt_search_input.py index 5baa1125..077563d6 100644 --- a/bluesky_widgets/qt/_tests/test_qt_search_input.py +++ b/bluesky_widgets/qt/_tests/test_qt_search_input.py @@ -1,9 +1,11 @@ -from datetime import datetime, timedelta import time +from datetime import datetime, timedelta + import pytest from qtpy.QtCore import QDateTime -from ...qt.search import QtSearchInput, ADA_LOVELACE_BIRTHDAY, as_qdatetime -from ...models.search import SearchInput, LOCAL_TIMEZONE + +from ...models.search import LOCAL_TIMEZONE, SearchInput +from ...qt.search import ADA_LOVELACE_BIRTHDAY, QtSearchInput, as_qdatetime def as_datetime(qdatetime): diff --git a/bluesky_widgets/qt/_tests/test_qt_viewer_with_search_example.py b/bluesky_widgets/qt/_tests/test_qt_viewer_with_search_example.py index c92386b0..e01efc4b 100644 --- a/bluesky_widgets/qt/_tests/test_qt_viewer_with_search_example.py +++ b/bluesky_widgets/qt/_tests/test_qt_viewer_with_search_example.py @@ -1,4 +1,5 @@ import pytest + from ...examples.advanced.qt_viewer_with_search import ExampleApp diff --git a/bluesky_widgets/qt/figures.py b/bluesky_widgets/qt/figures.py index e1608a0e..9d3594f4 100644 --- a/bluesky_widgets/qt/figures.py +++ b/bluesky_widgets/qt/figures.py @@ -1,22 +1,15 @@ import gc -from qtpy.QtWidgets import ( - QSizePolicy, - QTabWidget, - QWidget, - QVBoxLayout, -) -from qtpy.QtCore import Signal, QObject -from matplotlib.backends.backend_qt5agg import ( - FigureCanvasQTAgg as FigureCanvas, - NavigationToolbar2QT as NavigationToolbar, -) import matplotlib.figure +from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas +from matplotlib.backends.backend_qt5agg import NavigationToolbar2QT as NavigationToolbar +from qtpy.QtCore import QObject, Signal +from qtpy.QtWidgets import QSizePolicy, QTabWidget, QVBoxLayout, QWidget -from ..models.plot_specs import Figure, FigureList from .._matplotlib_axes import MatplotlibAxes -from ..utils.event import Event +from ..models.plot_specs import Figure, FigureList from ..utils.dict_view import DictView +from ..utils.event import Event def _initialize_matplotlib(): diff --git a/bluesky_widgets/qt/kafka_dispatcher.py b/bluesky_widgets/qt/kafka_dispatcher.py index 679f24a7..a93c4c7e 100644 --- a/bluesky_widgets/qt/kafka_dispatcher.py +++ b/bluesky_widgets/qt/kafka_dispatcher.py @@ -1,10 +1,9 @@ import functools import logging -import msgpack -from qtpy.QtCore import QTimer, QObject - import bluesky_kafka +import msgpack +from qtpy.QtCore import QObject, QTimer from ..qt.threading import create_worker diff --git a/bluesky_widgets/qt/run_engine_client.py b/bluesky_widgets/qt/run_engine_client.py index 40979430..c272080d 100644 --- a/bluesky_widgets/qt/run_engine_client.py +++ b/bluesky_widgets/qt/run_engine_client.py @@ -1,39 +1,39 @@ import ast -import inspect -import time -import pprint import copy +import inspect import os +import pprint +import time +from bluesky_queueserver import construct_parameters, format_text_descriptions +from qtpy.QtCore import Qt, QTimer, Signal, Slot +from qtpy.QtGui import QBrush, QColor, QFontMetrics, QIntValidator, QPalette from qtpy.QtWidgets import ( - QWidget, - QGroupBox, - QPushButton, - QVBoxLayout, - QHBoxLayout, - QLabel, - QTableWidget, - QTableWidgetItem, - QTableView, - QHeaderView, QAbstractItemView, - QTextEdit, - QTabWidget, - QRadioButton, QButtonGroup, - QComboBox, - QLineEdit, QCheckBox, + QComboBox, QDialog, - QFileDialog, QDialogButtonBox, + QFileDialog, QFormLayout, + QGroupBox, + QHBoxLayout, + QHeaderView, + QLabel, + QLineEdit, + QPushButton, + QRadioButton, + QTableView, + QTableWidget, + QTableWidgetItem, + QTabWidget, + QTextEdit, + QVBoxLayout, + QWidget, ) -from qtpy.QtCore import Qt, Signal, Slot, QTimer -from qtpy.QtGui import QFontMetrics, QPalette, QBrush, QColor, QIntValidator from bluesky_widgets.qt.threading import FunctionWorker -from bluesky_queueserver import construct_parameters, format_text_descriptions class LineEditExtended(QLineEdit): diff --git a/bluesky_widgets/qt/run_tree.py b/bluesky_widgets/qt/run_tree.py index 1d47a8cd..10b4f9d3 100644 --- a/bluesky_widgets/qt/run_tree.py +++ b/bluesky_widgets/qt/run_tree.py @@ -1,15 +1,10 @@ from collections import abc +from databroker.core import BlueskyEventStream from qtpy import QtCore -from qtpy.QtCore import ( - QAbstractItemModel, - QModelIndex, - Qt, -) +from qtpy.QtCore import QAbstractItemModel, QModelIndex, Qt from qtpy.QtWidgets import QAbstractItemView, QTreeView -from databroker.core import BlueskyEventStream - class RunTree: """Lazily populate the tree as data is requested.""" diff --git a/bluesky_widgets/qt/search.py b/bluesky_widgets/qt/search.py index 845d9f2e..b4768b84 100644 --- a/bluesky_widgets/qt/search.py +++ b/bluesky_widgets/qt/search.py @@ -1,3 +1,3 @@ from ._search_input import * # noqa: F401, F403 -from ._searches import * # noqa: F401, F403 from ._search_results import * # noqa: F401, F403 +from ._searches import * # noqa: F401, F403 diff --git a/bluesky_widgets/qt/zmq_dispatcher.py b/bluesky_widgets/qt/zmq_dispatcher.py index 612ad70c..7bf7f388 100644 --- a/bluesky_widgets/qt/zmq_dispatcher.py +++ b/bluesky_widgets/qt/zmq_dispatcher.py @@ -1,12 +1,11 @@ import pickle import time -from bluesky.run_engine import Dispatcher, DocumentNames import zmq +from bluesky.run_engine import Dispatcher, DocumentNames +from qtpy.QtCore import QObject, QTimer from ..qt.threading import create_worker -from qtpy.QtCore import QTimer, QObject - LOADING_LATENCY = 0.01 # sec diff --git a/bluesky_widgets/utils/dict_view.py b/bluesky_widgets/utils/dict_view.py index fb5c8bd5..2423c695 100644 --- a/bluesky_widgets/utils/dict_view.py +++ b/bluesky_widgets/utils/dict_view.py @@ -1,5 +1,6 @@ import collections.abc -from .event import Event, EmitterGroup + +from .event import EmitterGroup, Event class DictView(collections.abc.Mapping): diff --git a/bluesky_widgets/utils/streaming.py b/bluesky_widgets/utils/streaming.py index b53117c2..861b4d78 100644 --- a/bluesky_widgets/utils/streaming.py +++ b/bluesky_widgets/utils/streaming.py @@ -1,5 +1,5 @@ -from bluesky_live.bluesky_run import BlueskyRun, DocumentCache import event_model +from bluesky_live.bluesky_run import BlueskyRun, DocumentCache def stream_documents_into_runs(add_run): diff --git a/setup.py b/setup.py index 36e13f98..535eece5 100644 --- a/setup.py +++ b/setup.py @@ -1,8 +1,9 @@ -from os import path -from setuptools import setup, find_packages import sys -import versioneer +from os import path +from setuptools import find_packages, setup + +import versioneer # NOTE: This file must remain Python 2 compatible for the foreseeable future, # to ensure that we error out properly for people with outdated setuptools From 2d0b64d79b6666cbcf473bf2b9c35b4937f7ce06 Mon Sep 17 00:00:00 2001 From: Dmitri Gavrilov Date: Sat, 16 Mar 2024 18:48:54 -0400 Subject: [PATCH 2/4] CI: add isort config file and workflow --- .github/workflows/isort.yml | 21 +++++++++++++++++++++ .isort.cfg | 5 +++++ 2 files changed, 26 insertions(+) create mode 100644 .github/workflows/isort.yml create mode 100644 .isort.cfg diff --git a/.github/workflows/isort.yml b/.github/workflows/isort.yml new file mode 100644 index 00000000..53589da7 --- /dev/null +++ b/.github/workflows/isort.yml @@ -0,0 +1,21 @@ +name: Check Code Style - ISORT + +on: [push, pull_request] + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: actions/setup-python@v2 + - name: Install Dependencies + run: | + # These packages are installed in the base environment but may be older + # versions. Explicitly upgrade them because they often create + # installation problems if out of date. + python -m pip install --upgrade pip setuptools numpy + + pip install isort + - name: Run ISort + run: | + isort . -c diff --git a/.isort.cfg b/.isort.cfg new file mode 100644 index 00000000..ac9c74a5 --- /dev/null +++ b/.isort.cfg @@ -0,0 +1,5 @@ +[settings] +line_length = 115 +multi_line_output = 3 +include_trailing_comma = True +profile = black From 477c18c928a99f76130a1d40a010fa1ae34bc637 Mon Sep 17 00:00:00 2001 From: Dmitri Gavrilov Date: Sun, 17 Mar 2024 22:22:42 -0400 Subject: [PATCH 3/4] ENH: rename Save/Restore menu to Save and Backup --- bluesky_widgets/apps/queue_monitor/viewer.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/bluesky_widgets/apps/queue_monitor/viewer.py b/bluesky_widgets/apps/queue_monitor/viewer.py index 8ebd3a29..ba55ae52 100644 --- a/bluesky_widgets/apps/queue_monitor/viewer.py +++ b/bluesky_widgets/apps/queue_monitor/viewer.py @@ -40,23 +40,23 @@ def __init__(self, *, show=True, title="Demo App"): self._window = Window(self._widget, show=show) menu_bar = self._window._qt_window.menuBar() - menu_item_control = menu_bar.addMenu("Control Actions") + menu_item_save = menu_bar.addMenu("Control Actions") self.action_activate_env_destroy = QAction("Activate 'Destroy Environment'", self._window._qt_window) self.action_activate_env_destroy.setCheckable(True) self._update_action_env_destroy_state() self.action_activate_env_destroy.triggered.connect(self._activate_env_destroy_triggered) - menu_item_control.addAction(self.action_activate_env_destroy) + menu_item_save.addAction(self.action_activate_env_destroy) - menu_item_control = menu_bar.addMenu("Save/Restore") + menu_item_save = menu_bar.addMenu("Save and Backup") self.action_save_history_as_txt = QAction("Save Plan History (as .txt)", self._window._qt_window) self.action_save_history_as_txt.triggered.connect(self._save_history_as_txt_triggered) - menu_item_control.addAction(self.action_save_history_as_txt) + menu_item_save.addAction(self.action_save_history_as_txt) self.action_save_history_as_json = QAction("Save Plan History (as .json)", self._window._qt_window) self.action_save_history_as_json.triggered.connect(self._save_history_as_json_triggered) - menu_item_control.addAction(self.action_save_history_as_json) + menu_item_save.addAction(self.action_save_history_as_json) self.action_save_history_as_yaml = QAction("Save Plan History (as .yaml)", self._window._qt_window) self.action_save_history_as_yaml.triggered.connect(self._save_history_as_yaml_triggered) - menu_item_control.addAction(self.action_save_history_as_yaml) + menu_item_save.addAction(self.action_save_history_as_yaml) self._widget.model.run_engine.events.status_changed.connect(self.on_update_widgets) From f405a9cf96c63533a8d392cdb900a45e4bf25016 Mon Sep 17 00:00:00 2001 From: Dmitri Gavrilov Date: Mon, 18 Mar 2024 10:48:42 -0400 Subject: [PATCH 4/4] ENH: restored correct name for 'menu_item_control' --- bluesky_widgets/apps/queue_monitor/viewer.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bluesky_widgets/apps/queue_monitor/viewer.py b/bluesky_widgets/apps/queue_monitor/viewer.py index ba55ae52..e1acedd4 100644 --- a/bluesky_widgets/apps/queue_monitor/viewer.py +++ b/bluesky_widgets/apps/queue_monitor/viewer.py @@ -40,12 +40,12 @@ def __init__(self, *, show=True, title="Demo App"): self._window = Window(self._widget, show=show) menu_bar = self._window._qt_window.menuBar() - menu_item_save = menu_bar.addMenu("Control Actions") + menu_item_control = menu_bar.addMenu("Control Actions") self.action_activate_env_destroy = QAction("Activate 'Destroy Environment'", self._window._qt_window) self.action_activate_env_destroy.setCheckable(True) self._update_action_env_destroy_state() self.action_activate_env_destroy.triggered.connect(self._activate_env_destroy_triggered) - menu_item_save.addAction(self.action_activate_env_destroy) + menu_item_control.addAction(self.action_activate_env_destroy) menu_item_save = menu_bar.addMenu("Save and Backup") self.action_save_history_as_txt = QAction("Save Plan History (as .txt)", self._window._qt_window)