Exemple #1
0
class CameraReadoutDock(QtWidgets.QDockWidget):
    def __init__(self, acxn):
        QtWidgets.QDockWidget.__init__(self, "Camera Readout")
        self.acxn = acxn
        self.setObjectName("CameraReadoutHistogram")
        self.setFeatures(QtWidgets.QDockWidget.DockWidgetMovable
                         | QtWidgets.QDockWidget.DockWidgetFloatable)
        self.image = None
        self.image_region = None
        self.run_time = None
        self.main_widget = QtWidgets.QWidget()
        self.setWidget(self.main_widget)
        self.make_GUI()
        self.connect_GUI()
        self.connect_asyncio_server()

    def connect_asyncio_server(self):
        self.loop = asyncio.get_event_loop()
        self.asyncio_server = Server(
            {"camera_reference_image": self.RemotePlotting(self)}, None, True)
        self.task = self.loop.create_task(
            self.asyncio_server.start("::1", 3288))

    class RemotePlotting:
        def __init__(self, plt):
            self.plt = plt

        def plot(self, image, image_region, run_time=None):
            self.plt.image = image
            self.plt.image_region = image_region
            if run_time is None:
                self.run_time = dt.now().strftime("%Y%m%d_%H%M.%S")
            try:
                cxn = labrad.connect()
                p = cxn.parametervault
            except:
                logger.error("Couldn't connect to parametervault",
                             exc_info=True)
            N = int(p.get_parameter("IonsOnCamera", "ion_number"))
            x_axis = np.arange(image_region[2], image_region[3] + 1,
                               image_region[0])
            y_axis = np.arange(image_region[4], image_region[5] + 1,
                               image_region[1])
            xx, yy = np.meshgrid(x_axis, y_axis)

            fitter = ion_state_detector(N)
            result, params = fitter.guess_parameters_and_fit(xx, yy, image)
            p.set_parameter("IonsOnCamera", "fit_background_level",
                            params["background_level"].value)
            p.set_parameter("IonsOnCamera", "fit_amplitude",
                            params["amplitude"].value)
            p.set_parameter("IonsOnCamera", "fit_rotation_angle",
                            params["rotation_angle"].value)
            p.set_parameter("IonsOnCamera", "fit_center_horizontal",
                            params["center_x"].value)
            p.set_parameter("IonsOnCamera", "fit_center_vertical",
                            params["center_y"].value)
            p.set_parameter("IonsOnCamera", "fit_spacing",
                            params["spacing"].value)
            p.set_parameter("IonsOnCamera", "fit_sigma", params["sigma"].value)

            self.plt.ax.clear()
            with suppress(Exception):
                self.plt.cb.remove()
            I = self.plt.ax.imshow(image,
                                   cmap="cividis",
                                   interpolation="spline16",
                                   extent=[
                                       x_axis.min(),
                                       x_axis.max(),
                                       y_axis.max(),
                                       y_axis.min()
                                   ])
            self.plt.cb = self.plt.fig.colorbar(I, fraction=0.046, pad=0.04)
            x_axis_fit = np.linspace(x_axis.min(), x_axis.max(),
                                     x_axis.size * 10)
            y_axis_fit = np.linspace(y_axis.min(), y_axis.max(),
                                     y_axis.size * 10)
            xx, yy = np.meshgrid(x_axis_fit, y_axis_fit)
            fit = fitter.ion_model(params, xx, yy)
            self.plt.ax.contour(x_axis_fit,
                                y_axis_fit,
                                fit,
                                3,
                                colors=[(1., .49, 0., .75)])

            if result is not None:
                # print(lmfit.fit_report(result, show_correl=False))
                results_text = lmfit.fit_report(result, show_correl=False)
                param_results = results_text.split("\n")[-7:]
                for i, param_result in enumerate(param_results):
                    param_result = param_result.split("(")[0]
                    param_result = param_result.replace(" +/- ", "(")[:-1]
                    param_result = param_result.split(".")
                    param_result1 = param_result[1].split("(")
                    param_result[1] = param_result1[0][:3] + "("
                    try:
                        param_result[2] = param_result[2][:3]
                    except IndexError:
                        pass
                    param_result = ".".join(param_result)
                    param_result += ")"
                    param_results[i] = param_result
                results_text = "\n".join(param_results)
                results_text += "\n    chi_red = {:.2f}".format(result.redchi)
                results_text += "\n    runtime = " + str(self.run_time)
                self.plt.ax.annotate(results_text, (0.5, 0.75),
                                     xycoords="axes fraction",
                                     color=(1., .49, 0., 1.))

            self.plt.canvas.draw()
            self.plt.ax.relim()
            self.plt.ax.autoscale(enable=True, axis="both")

            cxn.disconnect()

        def enable_button(self):
            self.plt.reference_image_button.setDisabled(False)

    def closeEvent(self, event):
        self.task.cancel()
        self.loop.create_task(self.asyncio_server.stop())
        super(CameraReadoutDock, self).closeEvent(event)

    def make_GUI(self):
        layout = QtWidgets.QGridLayout()

        self.fig = Figure(figsize=(10, 10), tight_layout=True)
        self.fig.patch.set_facecolor((.97, .96, .96))
        self.canvas = FigureCanvasQTAgg(self.fig)
        self.canvas.setParent(self)
        # self.canvas.setMinimumSize(800, 800)
        self.ax = self.fig.add_subplot(111)
        self.ax.set_facecolor((.97, .96, .96))
        self.ax.tick_params(top=False,
                            bottom=False,
                            left=False,
                            right=False,
                            labeltop=True,
                            labelbottom=True,
                            labelleft=True,
                            labelright=False)
        self.mpl_toolbar = NavigationToolbar2QT(self.canvas, self)
        self.canvas.setSizePolicy(QtWidgets.QSizePolicy.Expanding,
                                  QtWidgets.QSizePolicy.Expanding)
        self.ax.tick_params(axis="both", direction="in")
        self.reference_image_button = QtWidgets.QPushButton("reference image")

        try:
            cxn = labrad.connect()
            p = cxn.parametervault
        except:
            pass
        accessed_params = set()
        parameters = p.get_parameter_names("IonsOnCamera")
        for parameter in parameters:
            accessed_params.update({"IonsOnCamera." + parameter})

        d_accessed_parameter_editor = ParameterEditorDock(
            acxn=None, name="Camera Options", accessed_params=accessed_params)
        d_accessed_parameter_editor.setFeatures(
            QtGui.QDockWidget.NoDockWidgetFeatures)
        d_accessed_parameter_editor.setTitleBarWidget(QtGui.QWidget())
        d_accessed_parameter_editor.table.setMaximumWidth(390)

        layout.addWidget(self.mpl_toolbar, 0, 0, 1, 1)
        layout.addWidget(self.reference_image_button, 0, 2, 1, 1)
        layout.addWidget(d_accessed_parameter_editor, 1, 0, 1, 1)
        layout.addWidget(self.canvas, 1, 1, 1, 2)

        self.main_widget.setLayout(layout)

    def connect_GUI(self):
        self.scheduler = Client("::1", 3251, "master_schedule")
        self.reference_image_button.clicked.connect(self.get_reference_image)

    def get_reference_image(self):
        self.reference_image_button.setDisabled(True)
        expid = {
            "arguments": {},
            "class_name": "ReferenceImage",
            "file": "misc/reference_image.py",
            "log_level": 30,
            "repo_rev": None,
            "priority": 2
        }
        self.scheduler.submit("main", expid, 2)

    def save_state(self):
        return {
            "image": self.image,
            "image_region": self.image_region,
            "run_time": self.run_time
        }

    def restore_state(self, state):
        if state["image"] is not None and state["image_region"] is not None:
            r = self.RemotePlotting(self)
            r.plot(state["image"],
                   state["image_region"],
                   run_time=state["run_time"])
Exemple #2
0
class PMTControlDock(QtWidgets.QDockWidget):
    def __init__(self, acxn):
        QtWidgets.QDockWidget.__init__(self, "Manual Controls")
        self.setObjectName("pmt_control")
        self.setFeatures(QtWidgets.QDockWidget.DockWidgetMovable |
                         QtWidgets.QDockWidget.DockWidgetFloatable)
        self.pv = None
        self.pm = None
        self.bb = None
        self.acxn = acxn
        self.setup_listeners()

        self.dset_ctl = Client("::1", 3251, "master_dataset_db")
        self.scheduler = Client("::1", 3251, "master_schedule")
        self.dataset_db = Client("::1", 3251, "master_dataset_db")
        self.rid = None
        self.pulsed = False
        self.expid_continuous = {
            "arguments": {},
            "class_name": "pmt_collect_continuously",
            "file": "run_continuously/run_pmt_continuously.py",
            "log_level": 30,
            "repo_rev": None,
            "priority": 0
        }

        self.expid_pulsed = {
            "arguments": {},
            "class_name": "pmt_collect_pulsed",
            "file": "run_continuously/run_pmt_pulsed.py",
            "log_level": 30,
            "repo_rev": None,
            "priority": 0
        }

        self.expid_ttl = {
            "class_name": "change_ttl",
            "file": "misc/manual_ttl_control.py",
            "log_level": 30,
            "repo_rev": None,
            "priority": 1
        }

        self.expid_dds = {
            "arguments": {},
            "class_name": "change_cw",
            "file": "misc/manual_dds_control.py",
            "log_level": 30,
            "repo_rev": None,
            "priority": 1
        }

        self.expid_dc = {
            "arguments": {},
            "class_name": "set_dopplercooling_and_statereadout",
            "file": "misc/set_dopplercooling_and_statereadout.py",
            "log_level": 30,
            "repo_rev": None,
            "priority": 2
        }

        frame = QtWidgets.QFrame()
        layout = QtWidgets.QVBoxLayout()
        pmt_frame = self.create_pmt_frame()
        linetrigger_frame = self.create_linetrigger_frame()
        dds_frame = self.create_dds_frame()
        picomotor_frame = self.create_picomotor_frame()
        layout.addWidget(pmt_frame)
        layout.addWidget(dds_frame)
        layout.addWidget(linetrigger_frame)
        layout.addWidget(picomotor_frame)
        layout.setSpacing(50)
        layout.setContentsMargins(0, 50, 0, 50)
        frame.setLayout(layout)

        scroll = QtWidgets.QScrollArea()
        scroll.setVerticalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
        scroll.setHorizontalScrollBarPolicy(QtCore.Qt.ScrollBarAsNeeded)
        scroll.setWidgetResizable(False)
        scroll.setWidget(frame)
        scroll.setAlignment(QtCore.Qt.AlignHCenter | QtCore.Qt.AlignVCenter)
        scroll.setSizePolicy(QtWidgets.QSizePolicy.MinimumExpanding,
                             QtWidgets.QSizePolicy.MinimumExpanding)
        self.setWidget(scroll)

        self.connect_servers()

    def create_pmt_frame(self):
        pmtLabel = boldLabel("PMT")
        self.onButton = QtWidgets.QPushButton("On")
        self.onButton.setCheckable(True)
        self.onButton.clicked[bool].connect(self.set_state)
        self.setDCButton = QtWidgets.QPushButton("set")
        self.setDCButton.clicked.connect(self.set_dc_and_state_readout)
        self.clearPMTPlotButton = QtWidgets.QPushButton("clear")
        self.clearPMTPlotButton.clicked.connect(self.clear_pmt_plot)
        self.autoLoadButton = QtWidgets.QPushButton("On")
        self.autoLoadButton.setCheckable(True)
        self.autoLoadButton.clicked[bool].connect(self.toggle_autoload)
        self.autoLoadSpin = customIntSpinBox(0, (0, 1000000))
        self.autoLoadCurrentSpin = customSpinBox(0, (0, 10), " A")
        self.countDisplay = QtWidgets.QLCDNumber()
        self.countDisplay.setSegmentStyle(2)
        self.countDisplay.display(0)
        self.timer = QtCore.QTimer()
        self.timer.timeout.connect(self.updateLCD)
        self.timer.start(250)
        self.countDisplay.setStyleSheet("background-color: lightGray;"
                                        "color: green;")
        self.unitsLabel = QtWidgets.QLabel("kcounts / s")

        self.durationLabel = QtWidgets.QLabel("Duration (ms): ")
        self.duration = QtWidgets.QLineEdit("100")
        try:
            with labrad.connection() as cxn:
                p = cxn.parametervault
                p.set_parameter(["PmtReadout", "duration", U(100, "ms")])
        except:
            logger.error("Failed to initially connect to labrad.")
            self.duration.setDisabled(True)
        validator = QtGui.QDoubleValidator()
        self.duration.setValidator(validator)
        self.duration.returnPressed.connect(self.duration_changed)
        self.duration.setStyleSheet("QLineEdit { background-color:  #c4df9b}" )
        self.modeLabel = QtWidgets.QLabel("Mode: ")
        self.setMode = customComboBox(["continuous", "pulsed"])
        self.setMode.currentIndexChanged.connect(self.set_mode)
        layout = QtWidgets.QGridLayout()
        frame = QtWidgets.QFrame()
        frame.setFrameStyle(QtWidgets.QFrame.Panel | QtWidgets.QFrame.Sunken)
        frame.setLineWidth(2)
        frame.setMidLineWidth(3)
        layout.addWidget(pmtLabel, 0, 0, 1, 3)
        layout.addWidget(self.onButton, 1, 0)
        layout.addWidget(self.countDisplay, 1, 1)
        layout.addWidget(self.unitsLabel, 1, 2)
        layout.addWidget(self.durationLabel, 2, 0)
        layout.addWidget(self.duration, 2, 1, 1, 2)
        layout.addWidget(self.modeLabel, 3, 0)
        layout.addWidget(self.setMode, 3, 1, 1, 2)
        layout.addWidget(QtWidgets.QLabel("Autoload: "), 4, 0)
        layout.addWidget(self.autoLoadButton, 4, 1)
        layout.addWidget(self.autoLoadSpin, 4, 2)
        layout.addWidget(QtWidgets.QLabel("Current: "), 5, 0)
        layout.addWidget(self.autoLoadCurrentSpin, 5, 1)
        dcLabel = QtWidgets.QLabel("Set Doppler cooling and state readout: ")
        layout.addWidget(dcLabel, 6, 0, 1, 2)
        layout.addWidget(self.setDCButton, 6, 2)
        # clearLabel = QtWidgets.QLabel("Reset PMT plot: ")
        # layout.addWidget(clearLabel, 7, 0)
        # layout.addWidget(self.clearPMTPlotButton, 7, 1, 1, 2)
        frame.setLayout(layout)
        return frame

    def create_linetrigger_frame(self):
        linetriggerLabel = boldLabel("LINETRIGGER")
        self.linetriggerButton = QtWidgets.QPushButton("Off")
        self.linetriggerButton.setCheckable(True)
        self.linetriggerButton.setChecked(True)
        self.linetriggerButton.clicked[bool].connect(self.toggle_linetrigger)
        self.linetriggerLineEdit = QtWidgets.QLineEdit("0")
        self.linetriggerLineEdit.returnPressed.connect(self.linetrigger_duration_changed)
        layout = QtWidgets.QGridLayout()
        frame = QtWidgets.QFrame()
        frame.setFrameStyle(QtWidgets.QFrame.Panel | QtWidgets.QFrame.Sunken)
        frame.setLineWidth(2)
        frame.setMidLineWidth(3)
        layout.addWidget(linetriggerLabel, 0, 0, 1, 3)
        layout.addWidget(self.linetriggerButton, 1, 0, 1, 3)
        layout.addWidget(QtWidgets.QLabel("Offset duration (us): "), 2, 0)
        layout.addWidget(self.linetriggerLineEdit, 2, 1, 1, 2)
        frame.setLayout(layout)
        return frame

    def create_picomotor_frame(self):
        layout = QtWidgets.QGridLayout()
        frame = QtWidgets.QFrame()
        frame.setFrameStyle(QtWidgets.QFrame.Panel | QtWidgets.QFrame.Sunken)
        frame.setLineWidth(2)
        frame.setMidLineWidth(3)
        piezoLabel = boldLabel("PICOMOTOR")
        ctls = ["Local X", "Local Y", "Global X", "Global Y"]
        self.piezoStepSize = dict()
        self.piezoCurrentPos = dict()
        self.piezoLastPos = dict()
        layout.addWidget(piezoLabel, 0, 0, 1, 3)
        for i, ctl in enumerate(ctls):
            layout.addWidget(QtWidgets.QLabel(ctl + ": "), i + 1, 0)
            self.piezoStepSize[ctl] = customIntSpinBox(0, (0, 300))
            self.piezoStepSize[ctl].setToolTip("Set step size.")
            self.piezoStepSize[ctl].setObjectName(ctl)
            self.piezoStepSize[ctl].setKeyboardTracking(False)
            self.piezoStepSize[ctl].valueChanged.connect(self.piezo_step_size_changed)
            self.piezoStepSize[ctl].setRange(0, 300)
            layout.addWidget(self.piezoStepSize[ctl], i + 1, 1)
            self.piezoCurrentPos[ctl] = QtWidgets.QSpinBox()
            self.piezoCurrentPos[ctl].setSingleStep(0)
            self.piezoCurrentPos[ctl].setToolTip("Current position.")
            self.piezoCurrentPos[ctl].setRange(-100000, 100000)
            self.piezoCurrentPos[ctl].setObjectName(str(i + 1))
            self.piezoLastPos[i + 1] = 0
            self.piezoCurrentPos[ctl].setKeyboardTracking(False)
            self.piezoCurrentPos[ctl].valueChanged.connect(self.piezo_changed)
            layout.addWidget(self.piezoCurrentPos[ctl], i + 1, 2)
        frame.setLayout(layout)
        return frame

    def create_dds_frame(self):
        layout = QtWidgets.QGridLayout()
        frame = QtWidgets.QFrame()
        frame.setFrameStyle(QtWidgets.QFrame.Panel | QtWidgets.QFrame.Sunken)
        frame.setLineWidth(2)
        frame.setMidLineWidth(3)
        ddsLabel = boldLabel("DDS Control")
        layout.addWidget(ddsLabel, 0, 0, 1, 2)
        home_dir = os.path.expanduser("~")
        dir_ = os.path.join(home_dir, "artiq-master/HardwareConfiguration.py")
        settings = run_path(dir_)
        dds_dict = settings["dds_config"]
        self.all_dds_specs = dict()
        self.dds_widgets = dict()

        try:
            cxn = labrad.connect()
            p = cxn.parametervault
            for (name, specs) in dds_dict.items():
                try:
                    params = p.get_parameter(["dds_cw_parameters", name])
                    freq, amp, state, att = params[1]
                except:
                    freq = str(specs.default_freq)
                    att = str(specs.default_att)
                    amp = str(1.)
                    state = str(0)
                    p.new_parameter("dds_cw_parameters", name,
                                    ("cw_settings", [freq, amp, state, att]))
                self.all_dds_specs[name] = {"cpld": int(specs.urukul),
                                            "frequency": float(freq) * 1e6,
                                            "att": float(att),
                                            "state": bool(int(state)),
                                            "amplitude": float(amp)}
            for i, (name, specs) in enumerate(sorted(dds_dict.items())):
                widget = ddsControlWidget(name, specs, self.scheduler, self)
                layout.addWidget(widget, i // 2 + 1 , i % 2)
                self.dds_widgets[name] = widget
            frame.setLayout(layout)
            cxn.disconnect()
        except:
            logger.error("Failed to initially connect to labrad.")

        return frame

    def set_state(self, override=False):
        if override:
            flag = True
        else:
            flag = self.onButton.isChecked()

        if flag:
            if self.rid is None:
                if self.setMode.currentText() == "continuous":
                    self.rid = self.scheduler.submit("main", self.expid_continuous, 0)
                else:
                    self.rid = self.scheduler.submit("main", self.expid_pulsed, 0)
            self.onButton.setText("Off")

        else:
            if self.rid is None:
                return  # Shouldn't happen
            else:
                self.scheduler.request_termination(self.rid)
                self.rid = None
            self.onButton.setText("On")

    def set_dc_and_state_readout(self):
        self.scheduler.submit("main", self.expid_dc, 2)

    def clear_pmt_plot(self):
        self.dataset_db.set("clear_pmt_plot", True)

    @inlineCallbacks
    def duration_changed(self, *args, **kwargs):
        # connect to parametervault here
        if self.pv is None:
            self.pv = yield self.acxn.get_server("ParameterVault")
        sender = self.sender()
        validator = sender.validator()
        state = validator.validate(sender.text(), 0)[0]
        if state == QtGui.QValidator.Acceptable:
            color = "#c4df9b" # green
        elif state == QtGui.QValidator.Intermediate:
            color = "#fff79a" # yellow
        else:
            color = "#f6989d" # red
        sender.setStyleSheet("QLineEdit { background-color: %s }" %color)
        try:
            min = 1e-3 # 1 us
            raw_duration = float(sender.text())
            duration = raw_duration if raw_duration >= min else min
            yield self.pv.set_parameter(["PmtReadout", "duration", U(duration, "ms")])
            a = yield self.pv.get_parameter(["PmtReadout", "duration"])
            if self.rid is None:
                return
            else:
                self.scheduler.request_termination(self.rid)
                self.rid = None
                self.set_state(True)
        except ValueError:
            # Shouldn't happen
            yield print("")
            logger.warning("Problem trying to update duration", exc_info=True)

    def set_mode(self):
        txt = str(self.setMode.currentText())
        if self.rid is None:
            self.pulsed = txt == "pulsed"
        elif  txt == "continuous":
            if not self.pulsed:
                return
            else:
                self.pulsed = False
                self.scheduler.request_termination(self.rid)
                self.rid = self.scheduler.submit("main", self.expid_continuous, 0)
        else:
            if self.pulsed:
                return
            else:
                self.pulsed = True
                self.scheduler.request_termination(self.rid)
                self.rid = self.scheduler.submit("main", self.expid_pulsed, 0)

    def updateLCD(self):
        if not self.onButton.isChecked():
            self.countDisplay.display(0)
            return
        try:
            raw_val = self.dset_ctl.get("pmt_counts")[-1]
            try:
                # duration in mseconds
                duration = float(self.duration.text())
            except ValueError:
                # picked up a backspace or something
                logger.warning("Failed to update LCD", exc_info=True)
                return
            val = raw_val / duration  # kcounts / second
            self.countDisplay.display(val)
        except KeyError:
            # dataset doesn't exist
            logger.info("dataset doesn't exist yet")
            self.countDisplay.display(0)
        except IndexError:
            # timer too fast
            pass

    @inlineCallbacks
    def toggle_linetrigger(self, *args):
        sender = self.sender()
        flag = sender.isChecked()
        if flag:
            sender.setText("Off")
            yield self.pv.set_parameter(["line_trigger_settings", "enabled", True])
        else:
            sender.setText("On")
            yield self.pv.set_parameter(["line_trigger_settings", "enabled", False])

    @inlineCallbacks
    def linetrigger_duration_changed(self, *args):
        value = float(self.sender().text())
        yield self.pv.set_parameter(["line_trigger_settings", "offset_duration", value])

    def piezo_step_size_changed(self):
        sender = self.sender()
        step = int(sender.value())
        ctl = sender.objectName()
        self.piezoCurrentPos[ctl].setSingleStep(step)

    @inlineCallbacks
    def piezo_changed(self, *args):
        if self.pm is None:
            yield print("not connected to picomotor")
        sender = self.sender()
        piezo = int(sender.objectName())
        current_pos = int(sender.value())
        last_pos = self.piezoLastPos[piezo]
        self.piezoLastPos[piezo] = current_pos
        move = current_pos - last_pos
        yield self.pm.relative_move(piezo, move)

    @inlineCallbacks
    def toggle_autoload(self, *args):
        sender = self.autoLoadButton
        flag = sender.isChecked()
        if flag:
            try:
                self.check_pmt_data_length = len(self.dataset_db.get("pmt_counts"))
            except KeyError:
                sender.setChecked(False)
                return
            sender.setText("Off")
            self.expid_ttl.update({"arguments": {"device": "blue_PIs",
                                                 "state": True}})
            if not hasattr(self, "check_pmt_timer"):
                self.check_pmt_timer = QtCore.QTimer()
                self.check_pmt_timer.timeout.connect(self.check_pmt_counts)
            self.check_pmt_timer.start(100)
            yield self.bb.connect()
            yield self.bb.set_current(self.autoLoadCurrentSpin.value())
            yield self.bb.on()
        else:
            sender.setText("On")
            if not hasattr(self, "check_pmt_timer"):
                return
            self.check_pmt_timer.stop()
            self.expid_ttl.update({"arguments": {"device": "blue_PIs",
                                                 "state": False}})
            yield self.bb.off()
        self.scheduler.submit("main", self.expid_ttl, priority=1)

    def check_pmt_counts(self):
        try:
            counts = self.dataset_db.get("pmt_counts")[self.check_pmt_data_length:]
        except KeyError:
            return
        if len(counts) == 0:
            return
        if max(counts) > int(self.autoLoadSpin.value()):
            self.autoLoadButton.setChecked(False)
            self.autoLoadButton.clicked.emit()

    def save_state(self):
        return {"ctl": {ctl: self.piezoStepSize[ctl].value()
                        for ctl in self.piezoStepSize.keys()},
                "offset":  self.linetriggerLineEdit.text(),
                "autoload": self.autoLoadSpin.value(),
                "mode": self.setMode.currentText(),
                "ltrigger": self.linetriggerButton.isChecked(),
                "current": self.autoLoadCurrentSpin.value()}

    def restore_state(self, state):
        for ctl, value in state["ctl"].items():
            self.piezoStepSize[ctl].setValue(value)
            self.piezoCurrentPos[ctl].setSingleStep(int(value))
        self.linetriggerLineEdit.setText(state["offset"])
        self.autoLoadSpin.setValue(int(state["autoload"]))
        self.setMode.setCurrentText(state["mode"])
        self.linetriggerButton.setChecked(state["ltrigger"])
        d = {False: "On", True: "Off"}
        self.linetriggerButton.setText(d[state["ltrigger"]])
        self.autoLoadCurrentSpin.setValue(state["current"])

    def setup_listeners(self):
        self.acxn.add_on_connect("ParameterVault", self.parameter_vault_connect)
        self.acxn.add_on_disconnect("ParameterVault", self.parameter_vault_disconnect)
        self.acxn.add_on_connect("picomotorserver", self.picomotor_connect)
        self.acxn.add_on_disconnect("picomotorserver", self.picomotor_disconnect)
        self.acxn.add_on_connect("barebonese3663a", self.barebones_connect)
        self.acxn.add_on_disconnect("barebonese3663a", self.barebones_disconnect)

    def parameter_vault_connect(self):
        self.duration.setDisabled(False)

    def parameter_vault_disconnect(self):
        self.duration.setDisabled(True)

    def picomotor_connect(self):
        for spinbox in self.piezoStepSize.values():
            spinbox.setDisabled(False)
        for spinbox in self.piezoCurrentPos.values():
            spinbox.setDisabled(False)

    def picomotor_disconnect(self):
        for spinbox in self.piezoStepSize.values():
            spinbox.setDisabled(True)
        for spinbox in self.piezoCurrentPos.values():
            spinbox.setDisabled(True)

    def barebones_connect(self):
        self.autoLoadCurrentSpin.setDisabled(False)

    def barebones_disconnect(self):
        self.autoLoadCurrentSpin.setDisabled(True)

    @inlineCallbacks
    def connect_servers(self):
        if self.pv is None:
            try:
                self.pv = yield self.acxn.get_server("ParameterVault")
            except:
                self.parameter_vault_disconnect()
        if self.pm is None:
            try:
                self.pm = yield self.acxn.get_server("picomotorserver")
            except:
                self.picomotor_disconnect()
        if self.bb is None:
            try:
                self.bb = yield self.acxn.get_server("barebonese3663a")
            except:
                self.barebones_disconnect()