318 lines
9.7 KiB
Python
Executable File
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_()
|
|
|