#!/usr/bin/env python # *-----------------------------------------------------------------------* # | | # | Copyright (c) 2022 by Paul Scherrer Institute (http://www.psi.ch) | # | Based on Zac great first implementation | # | Author Thierry Zamofing (thierry.zamofing@psi.ch) | # *-----------------------------------------------------------------------* ''' ''' import logging _log=logging.getLogger(__name__) import os from PyQt5.QtCore import pyqtSignal from PyQt5.QtWidgets import ( QApplication, QPushButton, QGroupBox, QGridLayout, QLabel, QDoubleSpinBox, QSpinBox, QVBoxLayout, QHBoxLayout, QCheckBox, ) from PyQt5.uic import loadUiType import backlight, illumination, camera import epics Ui_Zoom, QWidget = loadUiType(os.path.join(os.path.dirname(__file__),"zoom.ui")) MIN_ZOOM = 1 MAX_ZOOM = 1000 SPINNER_SINGLE_STEP = 50 SPINNER_LARGE_STEP = 200 class Zoom(QGroupBox, Ui_Zoom): zoomChanged = pyqtSignal(float) moveBacklight = pyqtSignal(str) def __init__(self, parent=None): super(Zoom, self).__init__(parent=parent) self.setupUi(self) self.setTitle("Sample Viewing") def init_settings(self): app=QApplication.instance() buttons=((1, "1"), (200, "200"), (400, "400"), (600, "600"), (800, "800"), (1000, "1000"),) zoom=QApplication.instance()._zoom current_zoom_value = zoom.get_val() # zoom widgets layout = QVBoxLayout() layout.setSpacing(0) layout.setContentsMargins(0, 0, 0, 0) self._button_box.setLayout(layout) self._zoom_spinner = QSpinBox() self._zoom_spinner.setRange(MIN_ZOOM, MAX_ZOOM) self._zoom_spinner.setValue(current_zoom_value) self._zoom_spinner.editingFinished.connect(self.move_zoom_a) self._zoom_spinner.setSingleStep(SPINNER_LARGE_STEP) _box = QWidget() _box.setLayout(QHBoxLayout()) _box.layout().setSpacing(0) _box.layout().setContentsMargins(0, 0, 0, 0) _box.layout().addWidget(QLabel("Zoom Level")) _box.layout().addWidget(self._zoom_spinner) layout.addWidget(_box) _box = QWidget() _box.setLayout(QGridLayout()) _box.layout().setContentsMargins(0, 0, 0, 0) for n, b in enumerate(buttons): row, col = n // 2, n % 2 value, label = b but = QPushButton(label) but.setCheckable(True) but.setChecked(value == current_zoom_value) but.setAutoExclusive(True) but.setAccessibleName("zoom_button") but.setObjectName("zoom_{}".format(value)) # used with findChild() but.pressed.connect(lambda x=value: self.move_zoom(x)) _box.layout().addWidget(but, row, col, 1, 1) self._zoom_buttonbox = _box layout.addWidget(_box) # backlight self._top_grid.setLayout(QVBoxLayout()) lbox = QWidget() lbox.setLayout(QHBoxLayout()) lbox.layout().setSpacing(0) lbox.layout().setContentsMargins(0, 0, 0, 0) self._top_grid.layout().addWidget(lbox) #TODO: self.blgt_button = QPushButton(qtawesome.icon("material.lightbulb_outline"), "Backlight") self.blgt_button = QPushButton( "Backlight") self.blgt_button.clicked.connect(self.toggle_backlight) lbox = QWidget() lbox.setLayout(QHBoxLayout()) lbox.layout().setSpacing(0) lbox.layout().setContentsMargins(0, 0, 0, 0) self._top_grid.layout().addWidget(lbox) lbox.layout().addWidget(self.blgt_button) leds=QApplication.instance()._illumination but = QCheckBox("Back") but.setChecked(leds.is_on(illumination.back)) but.stateChanged.connect(self.back_toggle) lbox.layout().addWidget(but) but = QCheckBox("Front") but.setChecked(leds.is_on(illumination.front)) but.stateChanged.connect(self.front_toggle) lbox.layout().addWidget(but) lbox = QWidget() lbox.setLayout(QHBoxLayout()) lbox.layout().setSpacing(0) lbox.layout().setContentsMargins(0, 0, 0, 0) self._top_grid.layout().addWidget(lbox) but = QCheckBox("Red") but.setChecked(leds.is_on(illumination.red)) but.stateChanged.connect(self.red_toggle) lbox.layout().addWidget(but) but = QCheckBox("Green") but.setChecked(leds.is_on(illumination.green)) but.stateChanged.connect(self.green_toggle) lbox.layout().addWidget(but) but = QCheckBox("Blue") but.setChecked(leds.is_on(illumination.blue)) but.stateChanged.connect(self.blue_toggle) lbox.layout().addWidget(but) lbox = QWidget() lbox.setLayout(QHBoxLayout()) lbox.layout().setSpacing(0) lbox.layout().setContentsMargins(0, 0, 0, 0) self._top_grid.layout().addWidget(lbox) self.slid = None cam=QApplication.instance()._camera grp = QGroupBox("Camera settings") grid = QGridLayout() grp.setLayout(grid) lab = QLabel("Exposure (ms)") grid.addWidget(lab, 0, 0) self.slid = QDoubleSpinBox() self.slid.setValue(cam.get_exposure()) self.slid.setMinimum(0.001) self.slid.setMaximum(1000.000) self.slid.setDecimals(3) self.slid.setSuffix(" ms") grid.addWidget(self.slid, 0, 1) lbox.layout().addWidget(grp) self.slid.editingFinished.connect(lambda: self.update_exposure(self.slid.value())) def back_toggle(self, state): leds=QApplication.instance()._illumination leds.back(state) def front_toggle(self, state): leds=QApplication.instance()._illumination leds.front(state) def red_toggle(self, state): leds=QApplication.instance()._illumination leds.red(state) def green_toggle(self, state): leds=QApplication.instance()._illumination leds.green(state) def blue_toggle(self, state): leds=QApplication.instance()._illumination leds.blue(state) def toggle_backlight(self): bl=QApplication.instance()._backlight if bl.is_pos('in'): self.moveBacklight.emit("out") self.blgt_button.setText("Move Backlight IN") else: self.moveBacklight.emit("in") self.blgt_button.setText("Move Backlight OUT") def update_camera_exposure(self, nv): cam=QApplication.instance()._camera cam.set_exposure(nv) def update_exposure(self, nv): _log.debug("update_exposure: {} {}".format(nv, self.slid.singleStep())) if nv > 100: self.slid.setSingleStep(50) elif 100 > nv > 50: self.slid.setSingleStep(10) elif 50 > nv > 10: self.slid.setSingleStep(5) elif 10 > nv > 2: self.slid.setSingleStep(1) elif 2 > nv > 1.19: self.slid.setSingleStep(0.2) elif 1 > nv > 0.1: self.slid.setSingleStep(0.1) else: self.slid.setSingleStep(0.01) _log.debug("update_exposure: {} {}".format(nv, self.slid.singleStep())) if nv > 0.001: cam = QApplication.instance()._camera cam.set_exposure(nv) def update_camera_acqmode(self, index): _log.debug("changing camera mode: {}".format(index)) cam=QApplication.instance()._camera cam.auto(index) def move_zoom_a(self): nval = self._zoom_spinner.value() self.move_zoom(nval) def move_zoom(self, value: int): zoom=QApplication.instance()._zoom _log.debug("zooming to : {}".format(value)) self._zoom_spinner.blockSignals(True) zoom.set(value) but = "zoom_{:d}".format(value) _log.debug("looking for button: {}".format(but)) for bbut in self._zoom_buttonbox.findChildren(QPushButton): bbut.blockSignals(True) curbut = bbut.objectName() checked = curbut == but bbut.setChecked(checked) # FIXME this is not working bbut.blockSignals(False) self._zoom_spinner.setValue(int(value)) self._zoom_spinner.blockSignals(False) self.zoomChanged.emit(float(value)) class QopticZoom(object): def __init__(self, prefix='SAR-EXPMX-FETURA'): if prefix is None: self._val=1 return #simulated mode self._pv=pv=dict() if prefix[-1]!=':': prefix+=':' self._prefix=prefix def getPv(self,name): try: pv=self._pv[name] except KeyError: prefix=self._prefix pv=epics.PV(prefix+name) self._pv[name]=pv return pv def get_rbv(self) -> int: # get_rbv lags. therefore get_val is often the better choicewill get the last set_point without usage of pvs # default do not get the RBV, because this might be delayed try: pv = self.getPv('POS_RB') except AttributeError: val=self._val; _log.debug('simulated mode:{}'.format(val)) return val else: return pv.get() def get_val(self) -> int: try: val=self._val except AttributeError: pv=self.getPv('POS_SP') self._val=val=pv.get() return val def set(self, val: int): try: pv=self.getPv('POS_SP') except AttributeError: _log.debug('simulated mode:{}'.format(val)) else: pv.put(val) self._val=val if __name__ == "__main__": import sys, os import argparse logging.basicConfig(level=logging.DEBUG,format='%(levelname)s:%(module)s:%(lineno)d:%(funcName)s:%(message)s ') parser = argparse.ArgumentParser() parser.add_argument("--sim", "-s", help="simulate all devices", action='store_true') args = parser.parse_args() _log.info('Arguments:{}'.format(args.__dict__)) os.environ['EPICS_CA_ADDR_LIST'] = '129.129.244.255 sf-saresc-cagw.psi.ch:5062 sf-saresc-cagw.psi.ch:5066' app = QApplication(sys.argv) from app_config import appsconf if args.sim: app._backlight = backlight.Backlight(None) app._illumination = illumination.IlluminationControl(None) app._zoom = QopticZoom(None) app._camera = camera.epics_cam(None) else: app._backlight = backlight.Backlight() app._illumination = illumination.IlluminationControl() app._zoom = QopticZoom() app._camera = camera.epics_cam() simulated = appsconf["microscope"]["zoom"].get("simulate", False) obj=Zoom() obj.configure() obj.show() app.exec_()