def main(argv): print(__doc__) with gui_qt("Example App"): app = ExampleApp() # 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, ) address = argv[1] dispatcher = RemoteDispatcher(address) dispatcher.subscribe(stream_documents_into_runs( app.viewer.add_run)) dispatcher.start() # We can access and modify the model as in... len(app.searches) app.searches[0] app.searches.active # i.e. current tab app.searches.active.input.since # time range app.searches.active.input.until app.searches.active.results app.searches.active.selection_as_catalog app.searches.active.selected_uids
def listen_for_data(app, address): # Optional: Receive live streaming data. from bluesky_widgets.jupyter.zmq_dispatcher import RemoteDispatcher from bluesky_widgets.utils.streaming import stream_documents_into_runs dispatcher = RemoteDispatcher(address) dispatcher.subscribe(stream_documents_into_runs(app.model.add_run)) dispatcher.start() # launches process and thread return dispatcher.stop
def stream_to_figures(fig, axes_list, start_at=0): fig.patch.set_alpha(0.5) axes_list = axes_list.ravel() section = np.zeros((8, 8)) section[:4, :4] = 1 section[4:, 4:] = 1 init_data = np.tile(section, (1 + SHAPE[0] // 8, 1 + SHAPE[1] // 8))[ : SHAPE[0], : SHAPE[1] ] ims = [ax.imshow(init_data) for ax in axes_list] if len(axes_list) > 1: sample_text = "S" # abbreviate for space else: sample_text = "Sample " for j, ax in enumerate(axes_list): ax.set_title(f"{sample_text}{j + start_at} N_shots: 0") ax.axis("off") counts = Counter() last_seen = None def update_plot(event): nonlocal last_seen run = event.run (sample,) = run.primary.read()["sample_selector"] sample = int(sample) img = run.primary.read()["detector_image"].mean(axis=0) img -= img.min() img /= img.max() if len(ims) == 1: (im,) = ims if sample != last_seen: counts.clear() else: im = ims[int(sample) - start_at] prev_count = counts[sample] old_data = im.get_array() new_data = (old_data * prev_count + img) / (prev_count + 1) counts[sample] += 1 im.set_data(new_data) im.axes.set_title(f"{sample_text}{sample} N_shots: {counts[sample]}") last_seen = sample fig.canvas.draw_idle() def update_plot_on_stop(run): run.events.completed.connect(update_plot) return stream_documents_into_runs(update_plot_on_stop)
def __init__(self, *, show=True, title="Demo App"): # TODO Where does title thread through? super().__init__() if SETTINGS.subscribe_to: from bluesky_widgets.qt.zmq_dispatcher import RemoteDispatcher from bluesky_widgets.utils.streaming import ( stream_documents_into_runs, ) for address in SETTINGS.subscribe_to: dispatcher = RemoteDispatcher(address) dispatcher.subscribe(stream_documents_into_runs(self.auto_plot_builder.add_run)) dispatcher.start() widget = QtViewer(self) self._window = Window(widget, show=show)
import tempfile from bluesky import RunEngine from bluesky.plans import scan from ophyd.sim import motor, det 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 model = AutoLines(max_runs=3) view = HeadlessFigures(model.figures) RE = RunEngine() RE.subscribe(stream_documents_into_runs(model.add_run)) catalog = get_catalog() scans = catalog.search({"plan_name": "scan"}) model.add_run(scans[-1], pinned=True) def plan(): for i in range(1, 5): yield from scan([det], motor, -1, 1, 1 + 2 * i) RE(plan())
def test_publisher_and_qt_remote_dispatcher( kafka_bootstrap_servers, temporary_topics, publisher_factory, qapp, hw ): """Test publishing and dispatching bluesky documents in Kafka messages. Messages will be "dispatched" by a `bluesky_kafka.RemoteDispatcher`. Parameters ---------- kafka_bootstrap_servers: str (pytest fixture) comma-delimited string of Kafka broker host:port, for example "localhost:9092" temporary_topics: context manager (pytest fixture) creates and cleans up temporary Kafka topics for testing publisher_factory: pytest fixture fixture-as-a-factory for creating Publishers qapp: pytest-qt fixture needed to force processing of Qt events hw: pytest fixture ophyd simulated hardware objects """ with temporary_topics(topics=["test.qt.remote.dispatcher"]) as (topic,): bluesky_publisher = publisher_factory( topic=topic, key=f"{topic}.key", flush_on_stop_doc=True, ) # trying to test _waiting_for_start bluesky_publisher("descriptor", {}) bluesky_publisher("event", {}) # bluesky_publisher("stop", {}) published_bluesky_documents = [] # this function will store all documents # published by the RunEngine in a list def store_published_document(name, document): published_bluesky_documents.append((name, document)) RE = RunEngine() RE.subscribe(bluesky_publisher) RE.subscribe(store_published_document) RE(count([hw.det])) # it is assumed that RE(count()) will produce four # documents: start, descriptor, event, stop assert len(published_bluesky_documents) == 4 lines_model = Lines(x="time", ys=["det"]) assert len(lines_model.runs) == 0 qt_remote_dispatcher = QtRemoteDispatcher( topics=[topic], bootstrap_servers=kafka_bootstrap_servers, group_id=f"{topic}.consumer.group", consumer_config={ # this consumer is intended to read messages that # were published before it starts, so it is necessary # to specify "earliest" here "auto.offset.reset": "earliest", }, polling_duration=1.0, ) qt_remote_dispatcher.subscribe(stream_documents_into_runs(lines_model.add_run)) dispatched_bluesky_documents = [] # the QtRemoteDispatcher will use this function # to build a list of documents delivered by Kafka def store_dispatched_document(name, document): test_logger.debug("store_dispatched_document name='%s'", name) dispatched_bluesky_documents.append((name, document)) qt_remote_dispatcher.subscribe(store_dispatched_document) # this function will be given to QtRemoteDispatcher.start() # it returns False to end the polling loop # as soon as it sees one stop document def until_first_stop_document(): dispatched_bluesky_document_names = [ name for name, _ in dispatched_bluesky_documents ] test_logger.debug( "until_first_stop_document %s", dispatched_bluesky_document_names ) if "stop" in dispatched_bluesky_document_names: return False else: return True # start() will return when 'until_first_stop_document' returns False qt_remote_dispatcher.start( continue_polling=until_first_stop_document, ) while len(dispatched_bluesky_documents) < 3: # waiting for all Kafka messages to land test_logger.debug("processing Qt Events") qapp.processEvents() time.sleep(1.0) test_logger.debug( "waiting for all Kafka messages, %d so far", len(dispatched_bluesky_documents), ) assert len(published_bluesky_documents) == len(dispatched_bluesky_documents) assert len(lines_model.runs) == 1
def main(): with gui_qt("Example App"): worker_address, message_bus_address = sys.argv[1:] dispatcher = RemoteDispatcher(message_bus_address) client = ZMQCommSendThreads(zmq_server_address=worker_address) # NOTE: this example starts only if RE Manager is idle and the queue is cleared. # Those are optional steps that ensure that the code in this example is executed correctly. # Check if RE Worker environment already exists and RE manager is idle. status = client.send_message(method="status") if status["manager_state"] != "idle": raise RuntimeError( f"RE Manager state must be 'idle': current state: {status['manager_state']}" ) # Clear the queue. response = client.send_message(method="queue_clear") if not response["success"]: raise RuntimeError( f"Failed to clear the plan queue: {response['msg']}") # Open the new environment only if it does not exist. if not status["worker_environment_exists"]: # Initiate opening of RE Worker environment response = client.send_message(method="environment_open") if not response["success"]: raise RuntimeError( f"Failed to open RE Worker environment: {response['msg']}") # Wait for the environment to be created. t_timeout = 10 t_stop = time.time() + t_timeout while True: status2 = client.send_message(method="status") if (status2["worker_environment_exists"] and status2["manager_state"] == "idle"): break if time.time() > t_stop: raise RuntimeError( "Failed to start RE Worker: timeout occurred") time.sleep(0.5) # Add plan to queue response = client.send_message( method="queue_item_add", params={ "plan": { "name": "scan", "args": [["det"], "motor", -5, 5, 11] }, "user": "", "user_group": "admin", }, ) if not response["success"]: raise RuntimeError( f"Failed to add plan to the queue: {response['msg']}") model = Lines("motor", ["det"], max_runs=3) dispatcher.subscribe(stream_documents_into_runs(model.add_run)) view = QtFigure(model.figure) view.show() dispatcher.start() response = client.send_message(method="queue_start") if not response["success"]: raise RuntimeError(f"Failed to start the queue: {response['msg']}")
def export(*args): filenames = view.export_all(directory) print("\n".join(f'"{filename}"' for filename in filenames)) view.close() if run_is_live_and_not_completed(run): run.events.new_data.connect(export) else: export() if __name__ == "__main__": bootstrap_servers = "127.0.0.1:9092" kafka_deserializer = partial(msgpack.loads, object_hook=mpn.decode) topics = ["widgets_test.bluesky.documents"] consumer_config = { "auto.commit.interval.ms": 100, "auto.offset.reset": "latest" } dispatcher = RemoteDispatcher( topics=topics, bootstrap_servers=bootstrap_servers, group_id="widgets_test", consumer_config=consumer_config, ) dispatcher.subscribe( stream_documents_into_runs(export_thumbnails_when_complete)) dispatcher.start()
def main(): # First, some boilerplate to make a super-minimal Qt application that we want # to add some bluesky-widgets components into. app = QApplication(["Some App"]) window = QMainWindow() central_widget = QWidget(window) window.setCentralWidget(central_widget) central_widget.setLayout(QVBoxLayout()) central_widget.layout().addWidget(QLabel("This is part of the 'original' app.")) window.show() # *** INTEGRATION WITH BLUESKY-WIDGETS STARTS HERE. *** # Ensure that any background workers started by bluesky-widgets stop # gracefully when the application closes. from bluesky_widgets.qt.threading import wait_for_workers_to_quit app.aboutToQuit.connect(wait_for_workers_to_quit) # Model a list of figures. # This will generate line plot automatically based on the structure (shape) # of the data and its hints. Other models could be used for more explicit # control of what gets plotted. See the examples in # http://blueskyproject.io/bluesky-widgets/reference.html#plot-builders from bluesky_widgets.models.auto_plot_builders import AutoLines model = AutoLines(max_runs=3) # Feed it data from the RunEngine. In actual practice, the RunEngine should # be in a separate process and we should be receiving these documents # 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 RE = RunEngine() RE.subscribe(stream_documents_into_runs(model.add_run)) # Add a tabbed pane of figures to the app. from bluesky_widgets.qt.figures import QtFigures view = QtFigures(model.figures) # view is a QWidget central_widget.layout().addWidget(view) # When the model receives data or is otherwise updated, any changes to # model.figures will be reflected in changes to the view. # Just for this example, generate some data before starting this app. # Again, in practice, this should happen in a separate process and send # the results over a network. from bluesky.plans import scan from ophyd.sim import motor, det def plan(): for i in range(1, 5): yield from scan([det], motor, -1, 1, 1 + 2 * i) RE(plan()) # *** INTEGRATION WITH BLUESKY-WIDGETS ENDS HERE. *** # Run the app. app.exec_()
def __init__(self, callback): self.callback = callback self._run = None # self._document_collector = stream_documents_into_runs(self.add_new_run)
# TODO This does not seem to have any effect. # Am I using it wrong, or are ophyd's simulated motors # not implementing enough of the optional API to engage it? # Register bluesky IPython magics. from bluesky.magics import BlueskyMagics get_ipython().register_magics(BlueskyMagics) # Set up plots with bluesky_widgets. from bluesky_widgets.models.auto_plot_builders import AutoLines from bluesky_widgets.utils.streaming import stream_documents_into_runs from bluesky_widgets.jupyter.figures import JupyterFigures auto_plot_model = AutoLines(max_runs=10) RE.subscribe(stream_documents_into_runs(auto_plot_model.add_run)) auto_plot_view = JupyterFigures(auto_plot_model.figures) # Use BestEffortCallback just for the table # TODO: Retire our use of BestEffortCallback, using a table from # bluesky_widgets once one is available. from bluesky.callbacks.best_effort import BestEffortCallback bec = BestEffortCallback() bec.disable_plots() RE.subscribe(bec) # Import matplotlib and put it in interactive mode. import matplotlib.pyplot as plt plt.ion()
def start_dichro_plot(monitor='Ion Ch 4', detector='Ion Ch 5', fluo=True): model = AutoDichroPlot(monitor=monitor, detector=detector, fluo=fluo) view = QtFigures(model.figures) view.show() RE.subscribe(stream_documents_into_runs(model.add_run)) return model, view