Example #1
0
    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)
Example #2
0
    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)
Example #3
0
    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)
Example #4
0
    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
Example #5
0
    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
Example #6
0
 def qasync_init(self):
     app = QtWidgets.QApplication([])
     self.loop = QEventLoop(app)
     asyncio.set_event_loop(self.loop)
Example #7
0
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_())