Files
SwissMX/zoom.py

318 lines
9.7 KiB
Python
Executable File

#!/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_()