def build_context_menu(self, builder): if self.model.context.is_online_master(): # If no new data points are coming in, setting the history size wouldn't do # anything. # TODO: is_online_master() should really be something like # SinglePointModel.ever_updates(). num_history_box = QtWidgets.QSpinBox() num_history_box.setMinimum(1) num_history_box.setMaximum(2**16) num_history_box.setValue(self._history_length) num_history_box.valueChanged.connect(self.set_history_length) container = QtWidgets.QWidget() layout = QtWidgets.QHBoxLayout() container.setLayout(layout) label = QtWidgets.QLabel("N: ") layout.addWidget(label) layout.addWidget(num_history_box) action = builder.append_widget_action() action.setDefaultWidget(container) builder.ensure_separator() super().build_context_menu(builder)
def __init__(self, roots, context): super().__init__() self.root_widgets = OrderedDict((label, RootWidget(root, context)) for label, root in roots.items()) self.layout = QtWidgets.QVBoxLayout() self.layout.setContentsMargins(0, 0, 0, 0) self.setLayout(self.layout) self.tab_widget = QtWidgets.QTabWidget() for label, widget in self.root_widgets.items(): self.tab_widget.addTab(widget, label) self.layout.addWidget(self.tab_widget)
def build_context_menu(self, builder): if self.model.context.is_online_master(): x_datasets = extract_linked_datasets(self.x_schema["param"]) y_datasets = extract_linked_datasets(self.y_schema["param"]) for d, axis in chain(zip(x_datasets, repeat("x")), zip(y_datasets, repeat("y"))): action = builder.append_action( "Set '{}' from crosshair".format(d)) action.triggered.connect(lambda *a, axis=axis, d=d: ( self._set_dataset_from_crosshair(d, axis))) if len(x_datasets) == 1 and len(y_datasets) == 1: action = builder.append_action("Set both from crosshair") def set_both(): self._set_dataset_from_crosshair(x_datasets[0], "x") self._set_dataset_from_crosshair(y_datasets[0], "y") action.triggered.connect(set_both) builder.ensure_separator() self.channel_menu_group = QtWidgets.QActionGroup(self) for name in self.data_names: action = builder.append_action(name) action.setCheckable(True) action.setActionGroup(self.channel_menu_group) action.setChecked(name == self.plot.active_channel_name) action.triggered.connect( lambda *a, name=name: self.plot.activate_channel(name)) builder.ensure_separator() super().build_context_menu(builder)
def __init__(self, root: Root, context: Context): super().__init__() self.root = root self.root.model_changed.connect(self._update_plot) self.context = context self.context.title_changed.connect(self._set_window_title) self.layout = QtWidgets.QVBoxLayout() self.layout.setContentsMargins(0, 0, 0, 0) self.setLayout(self.layout) # TODO: Use context info/… to identify plot to user in message. self.message_label = QtWidgets.QLabel("No data.") self.widget_stack = QtWidgets.QStackedWidget() self.widget_stack.addWidget(self.message_label) self.layout.addWidget(self.widget_stack) self.plot_container = None
def __init__(self, model: Model): super().__init__() self._alternate_plots = OrderedDict() self.model = model self.layout = QtWidgets.QVBoxLayout() self.layout.setContentsMargins(0, 0, 0, 0) self.setLayout(self.layout) self.widget_stack = QtWidgets.QStackedWidget() self.message_label = QtWidgets.QLabel( "Waiting for channel metadata for scan…") self.widget_stack.addWidget(self.message_label) self.layout.addWidget(self.widget_stack) if isinstance(self.model, SinglePointModel): self.plot = Rolling1DPlotWidget(self.model, self._get_alternate_names) self.model.channel_schemata_changed.connect( self._create_subscan_roots) else: try: self.plot = _make_dimensional_plot(self.model, self._get_alternate_names) except NotImplementedError as err: self._show_error(str(err)) return self.widget_stack.addWidget(self.plot) self._alternate_plots["main plot"] = self.plot self.plot.error.connect(self._show_error) self.plot.ready.connect(lambda: self._show(self.plot)) self.plot.alternate_plot_requested.connect(self._show_alternate_plot) # All subscan roots, to keep the objects alive. self._subscan_roots = None
def qasync_init(self): app = QtWidgets.QApplication([]) self.loop = QEventLoop(app) asyncio.set_event_loop(self.loop)
def main(): args = get_argparser().parse_args() app = QtWidgets.QApplication(sys.argv) loop = QEventLoop(app) asyncio.set_event_loop(loop) magic_spec = results.parse_magic(args.path) if magic_spec is not None: paths = results.find_results(day="auto", **magic_spec) if len(paths) != 1: QtWidgets.QMessageBox.critical( None, "Unable to resolve experiment path", f"Could not resolve '{args.path}: {paths}'") sys.exit(1) path = next(iter(paths.values())).path else: path = args.path try: file = h5py.File(path, "r") except Exception as e: QtWidgets.QMessageBox.critical(None, "Unable to load file", str(e)) sys.exit(1) try: datasets = file["datasets"] except KeyError: QtWidgets.QMessageBox.critical( None, "Not an ARTIQ results file", "No ARTIQ dataset records found in file: '{}'".format(args.path)) sys.exit(1) prefix = fetch_explicit_prefix(args) if prefix is not None: try: # 'axes' existed since the earliest schema revisions, so we can use it to # detect whether the file/prefix the user specified vaguely looks like it # has been generated by ndscan. datasets[prefix + "axes"][()] except KeyError: QtWidgets.QMessageBox.critical( None, "Not an ndscan file", "Datasets '{}*' in file '{}' do not look like ndscan results.". format(prefix, args.path)) sys.exit(1) prefixes = [prefix] else: prefixes = find_ndscan_roots(datasets) if not prefixes: QtWidgets.QMessageBox.critical( None, "Not an ndscan file", "No ndscan result datasets found in file: '{}'".format( args.path)) sys.exit(1) try: schema = extract_param_schema( pyon.decode(file["expid"][()])["arguments"]) except Exception as e: print("No ndscan parameter arguments found:", e) schema = None if schema is not None: print("Scan settings") print("=============") print() for s in dump_scan(schema): print(s) print() print() print("Overrides") print("=========") print() for s in dump_overrides(schema): print(s) print() try: context = Context() context.set_title(os.path.basename(args.path)) # Take source_id from first prefix. This is pretty arbitrary, but for # experiment-generated files, they will all be the same anyway. if (prefixes[0] + "source_id") in datasets: source = datasets[prefixes[0] + "source_id"][()] if isinstance(source, bytes): # h5py 3+ – can use datasets[…].asstr() as soon as we don't support # version 2 any longer. source = source.decode("utf-8") context.set_source_id(source) else: # Old ndscan versions had a rid dataset instead of source_id. context.set_source_id("rid_{}".format(datasets[prefixes[0] + "rid"][()])) roots = [HDF5Root(datasets, p, context) for p in prefixes] except Exception as e: QtWidgets.QMessageBox.critical( None, "Error parsing ndscan file", "Error parsing datasets in '{}': {}".format(args.path, e)) sys.exit(2) if len(roots) == 1: widget = PlotContainerWidget(roots[0].get_model()) else: label_map = shorten_to_unambiguous_suffixes( prefixes, lambda fqn, n: ".".join(fqn.split(".")[-(n + 1):])) widget = MultiRootWidget( OrderedDict( zip((strip_suffix(label_map[p], ".") for p in prefixes), roots)), context) widget.setWindowTitle(f"{context.get_title()} – ndscan.show") widget.show() widget.resize(800, 600) sys.exit(app.exec_())