wip
This commit is contained in:
41
Readme.md
41
Readme.md
@@ -1,3 +1,13 @@
|
||||
pyqtgraph examples
|
||||
------------------
|
||||
```
|
||||
ipython3
|
||||
import pyqtgraph.examples
|
||||
pyqtgraph.examples.run()
|
||||
|
||||
```
|
||||
|
||||
|
||||
EPICS simulator
|
||||
---------------
|
||||
```
|
||||
@@ -23,9 +33,38 @@ EPICS_CA_ADDR_LIST=localhost
|
||||
./camera.py -u -b SwissMxSim
|
||||
```
|
||||
|
||||
|
||||
test at ESC
|
||||
-----------
|
||||
```
|
||||
https://git.psi.ch/SwissMX/swissmx_cristallina/-/wikis/Instructions%20on%20how%20to%20use%20software
|
||||
|
||||
P=ESB_MX/python/SwissMX/
|
||||
ssh saresc-cons-02 mkdir -p /tmp/zamofing_t/$P
|
||||
rsync -vai ~/Documents/prj/SwissFEL/epics_ioc_modules/$P saresc-cons-02:/tmp/zamofing_t/$P
|
||||
cd /tmp/zamofing_t/ESB_MX/python/SwissMX
|
||||
/opt/gfa/python-3.7/2018.12/bin/python camera.py -u -p SARES30-CAMS156-SMX-OAV
|
||||
|
||||
caqtdm -macro 'NAME=SARES30-CAMS156-SMX-OAV,CAMNAME=SARES30-CAMS156-SMX-OAV' /sf/controls/config/qt/Camera/CameraExpert_RF.ui
|
||||
|
||||
```
|
||||
|
||||
try revive Zac code
|
||||
-------------------
|
||||
```
|
||||
cd /tmp/; git clone https://github.com/malcolmreynolds/transformations.git
|
||||
cd /tmp/transformations.git
|
||||
-> modify __init__.py -> .transformation (add dot)
|
||||
setup.py install --user
|
||||
|
||||
|
||||
cd /tmp/; git clone https://github.com/spyder-ide/qtawesome.git
|
||||
cd /tmp/qtawesome
|
||||
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
caQtDM -macro 'P=SAR-EXPMX' /photonics/home/gac-cristall/Documents/swissmx_cristallina/gui/SwissMX_ChipMotion.ui
|
||||
caQtDM -macro 'P=SAR-EXPMX' ESB_MX_exp.ui
|
||||
|
||||
161
camera.py
161
camera.py
@@ -1,4 +1,5 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
"""
|
||||
Hi Zac,
|
||||
|
||||
@@ -19,16 +20,21 @@ Best regards
|
||||
|
||||
import enum
|
||||
import logging
|
||||
_log = logging.getLogger(__name__)
|
||||
|
||||
import epics
|
||||
import numpy as np
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
class CamMode(enum.IntEnum):
|
||||
class Camera(enum.IntEnum):
|
||||
OFF = 0
|
||||
RUNNING = 1
|
||||
|
||||
class CameraStatus(enum.IntEnum):
|
||||
OFFLINE = 0
|
||||
IDLE = 1
|
||||
RUNNING = 2
|
||||
|
||||
|
||||
class epics_cam(object):
|
||||
|
||||
@@ -36,8 +42,6 @@ class epics_cam(object):
|
||||
self._pv=pv=dict()
|
||||
if prefix[-1]!=':': prefix+=':'
|
||||
self._prefix=prefix
|
||||
pv_w=self.getPv('WIDTH');pv_h=self.getPv('HEIGHT')
|
||||
self._sz=(int(pv_w.value), int(pv_h.value))
|
||||
|
||||
def getPv(self,name):
|
||||
try:
|
||||
@@ -101,6 +105,31 @@ class epics_cam(object):
|
||||
pic=pic[::trf[0,1],::trf[1,0]].T
|
||||
|
||||
self.pic=pic
|
||||
return pic
|
||||
|
||||
def stop(self,v=CameraStatus.IDLE):
|
||||
if 'pic' in self._pv:
|
||||
del self._pv['pic']
|
||||
if v is not None:
|
||||
pv_cs = self.getPv('CAMERASTATUS')
|
||||
pv_cs.put(v, wait=True)
|
||||
|
||||
def run(self,cb=None):
|
||||
pv_cam=self.getPv('CAMERA')
|
||||
if pv_cam.value==Camera.OFF:
|
||||
pv_cs = self.getPv('CAMERASTATUS')
|
||||
pv_cs.put(CameraStatus.RUNNING, wait=True)
|
||||
while pv_cam.value==Camera.OFF:
|
||||
print('wait...');time.sleep(.5)
|
||||
self.update_size()
|
||||
if cb is None:
|
||||
self._pv['pic'] = epics.PV(self._prefix + "FPICTURE")
|
||||
else:
|
||||
self._pv['pic'] = epics.PV(self._prefix + "FPICTURE", auto_monitor=True, callback=cb)
|
||||
|
||||
def update_size(self):
|
||||
pv_w=self.getPv('WIDTH');pv_h=self.getPv('HEIGHT')
|
||||
self._sz=(int(pv_w.value), int(pv_h.value))
|
||||
|
||||
def set_exposure(self,exp):
|
||||
pv_exp=self.getPv('EXPOSURE')
|
||||
@@ -110,7 +139,7 @@ class epics_cam(object):
|
||||
def set_roi(self,rxs,rxe,rys,rye):
|
||||
pv_rxs=self.getPv('REGIONX_START');pv_rxe=self.getPv('REGIONX_END')
|
||||
pv_rys=self.getPv('REGIONY_START');pv_rye=self.getPv('REGIONY_END')
|
||||
self.update_params((pv_rxs,rxs),(pv_rxe,rxe),(pv_rys,rys),(pv_ryerye))
|
||||
self.update_params((pv_rxs,rxs),(pv_rxe,rxe),(pv_rys,rys),(pv_rye,rye))
|
||||
|
||||
def set_binning(self,bx,by):
|
||||
pv_bx=self.getPv('BINX');pv_by=self.getPv('BINY')
|
||||
@@ -120,43 +149,71 @@ class epics_cam(object):
|
||||
def update_params(self, *args):
|
||||
"""update parameters on camera"""
|
||||
for pv, val in args:
|
||||
logging.debug("updating {} = {}".format(pv, v))
|
||||
if not pv.connected:
|
||||
_log.info('force connect {}'.format(pv))
|
||||
pv.force_connect() #force to connect pv
|
||||
_log.debug("updating {} = {}".format(pv.pvname, val))
|
||||
pv.put(val, wait=True)
|
||||
pv_camera=self.getPv('CAMERA')
|
||||
pv_set_param=self.getPv("SET_PARAM")
|
||||
|
||||
pv_camera.put(CamMode.OFF, wait=True)
|
||||
pv_set_param.put(1, wait=True)
|
||||
pv_camera.put(CamMode.RUNNING, wait=True)
|
||||
#TODO: update self._sz
|
||||
pv_cam=self.getPv('CAMERA')
|
||||
pv_cs = self.getPv('CAMERASTATUS')
|
||||
#pv_set_param=self.getPv("SET_PARAM")
|
||||
pv_cam.put(CameraStatus.IDLE, wait=True)
|
||||
#pv_set_param.put(1, wait=True)
|
||||
pv_cam.put(CameraStatus.RUNNING, wait=True)
|
||||
self.update_size()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import time, os, PIL
|
||||
|
||||
logging.basicConfig(format="%(asctime)s $(levelname)s %(message)s", level=logging.DEBUG)
|
||||
|
||||
import time, os, PIL.Image, platform, subprocess
|
||||
import argparse
|
||||
logging.basicConfig(level=logging.DEBUG,format='%(levelname)s:%(module)s:%(lineno)d:%(funcName)s:%(message)s ')
|
||||
|
||||
def default_app_open(file):
|
||||
if platform.system() == 'Darwin': # macOS
|
||||
subprocess.call(('open', file))
|
||||
elif platform.system() == 'Windows': # Windows
|
||||
os.startfile(file)
|
||||
else: # linux variants
|
||||
subprocess.call(('xdg-open', file))
|
||||
|
||||
|
||||
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument("--ui", "-u", help="qt test", action="store_true")
|
||||
parser.add_argument("--prefix","-p",help="PV prefix for images; default=ESB-MX-CAM",type=str,default="ESB-MX-CAM",)
|
||||
parser.add_argument("--ui", "-u", help="qt test", type=int, default=0)
|
||||
parser.add_argument("--prefix","-p",help="PV prefix for images: default=%(default)s",type=str,default="SARES30-CAMS156-SMX-OAV",)
|
||||
args = parser.parse_args()
|
||||
|
||||
print('STARTING')
|
||||
os.environ['EPICS_CA_ADDR_LIST']='localhost'
|
||||
_log.info('STARTING')
|
||||
if args.prefix=='SwissMxSim':
|
||||
os.environ['EPICS_CA_ADDR_LIST']='localhost'
|
||||
else:
|
||||
os.environ['EPICS_CA_ADDR_LIST'] ='129.129.244.255 sf-saresc-cagw.psi.ch:5062 sf-saresc-cagw.psi.ch:5066'
|
||||
|
||||
if not args.ui:
|
||||
cam = epics_cam(prefix=args.base_pv)
|
||||
cam = epics_cam(prefix=args.prefix)
|
||||
#sz=(2448,2048)
|
||||
#ctr=(1200,1400)
|
||||
#sz=(1200,1000)
|
||||
#cam.set_roi(int(ctr[0]-sz[0]/2),int(ctr[0]+sz[0]/2),int(ctr[1]-sz[1]/2),int(ctr[1]+sz[1]/2))
|
||||
#cam.set_exposure(3)
|
||||
#cam.set_binning(1,1)
|
||||
|
||||
cam.run()
|
||||
n = 1
|
||||
base = time.strftime("/tmp/image_%Y%m%d_%H%M%S_{:05d}.png")
|
||||
base = "/tmp/image_{:05d}.png"
|
||||
while True:
|
||||
fname = base.format(n)
|
||||
img=cam.get_image()
|
||||
print(type(img))
|
||||
type(img)
|
||||
mx= img.max()
|
||||
if img.dtype==np.uint16 and mx<256:
|
||||
_log.info('reformat to uint 8')
|
||||
img=np.uint8(img)
|
||||
print(img,img.dtype)
|
||||
PIL.Image.fromarray(img).save(fname)
|
||||
logging.info("acquired {}".format(fname))
|
||||
|
||||
fn = base.format(n)
|
||||
PIL.Image.fromarray(img).save(fn)
|
||||
_log.info('File {} shape:{} dtype:{} max:{}'.format(fn,img.shape,img.dtype,img.max()))
|
||||
default_app_open(fn)
|
||||
try:
|
||||
input("<enter> for next image, ctrl-c to abort")
|
||||
except KeyboardInterrupt:
|
||||
@@ -167,18 +224,16 @@ if __name__ == "__main__":
|
||||
|
||||
def __init__(self, prefix="ESB-MX-CAM"):
|
||||
epics_cam.__init__(self,prefix)
|
||||
self._pv['pic'] = epics.PV(self._prefix + "FPICTURE", auto_monitor=True, callback=self.new_frame_pv_cb)
|
||||
pass
|
||||
|
||||
def new_frame_pv_cb(self, **kwargs):
|
||||
pv = self._pv
|
||||
sz = self._sz
|
||||
if kwargs['count']==sz[0] * sz[1]:
|
||||
pic = kwargs['value'].reshape(sz[::-1])
|
||||
pic=kwargs['value'].reshape(sz[::-1])
|
||||
else:
|
||||
self._sz = (int(pv['w'].value), int(pv['h'].value))
|
||||
print('new_frame_pv_cb SIZE ERROR:',pic[-1][-1],kwargs['count'])
|
||||
return
|
||||
sz=self.update_size()
|
||||
pic=kwargs['value'].reshape(sz[::-1])
|
||||
print('new_frame_pv_cb',pic[-1][-1],kwargs['count'])
|
||||
if pic.dtype==np.int16:
|
||||
pic.dtype=np.uint16
|
||||
@@ -211,46 +266,20 @@ if __name__ == "__main__":
|
||||
win.show()
|
||||
win.setWindowTitle('pyqtgraph example: ImageView')
|
||||
|
||||
## Create random 3D data set with noisy signals
|
||||
img = pg.gaussianFilter(np.random.normal(size=(200, 200)), (5, 5)) * 20 + 100
|
||||
img = img[np.newaxis,:,:]
|
||||
decay = np.exp(-np.linspace(0,0.3,100))[:,np.newaxis,np.newaxis]
|
||||
data = np.random.normal(size=(100, 200, 200))
|
||||
data += img * decay
|
||||
data += 2
|
||||
|
||||
## Add time-varying signal
|
||||
sig = np.zeros(data.shape[0])
|
||||
sig[30:] += np.exp(-np.linspace(1,10, 70))
|
||||
sig[40:] += np.exp(-np.linspace(1,10, 60))
|
||||
sig[70:] += np.exp(-np.linspace(1,10, 30))
|
||||
|
||||
sig = sig[:,np.newaxis,np.newaxis] * 3
|
||||
data[:,50:60,30:40] += sig
|
||||
|
||||
|
||||
## Display the data and assign each frame a time value from 1.0 to 3.0
|
||||
imv.setImage(data, xvals=np.linspace(1., 3., data.shape[0]))
|
||||
cam = UIcamera(prefix=args.prefix)
|
||||
cam.get_image()
|
||||
imv.setImage(cam.pic)
|
||||
#cam.set_binning(4,4)
|
||||
#cam.run()
|
||||
#cam.get_image()
|
||||
#imv.setImage(cam.pic, autoRange=True, autoLevels=True)
|
||||
#cam.stop(None)
|
||||
cam.run(cam.new_frame_pv_cb)
|
||||
|
||||
## Set a custom color map
|
||||
colors = [
|
||||
(0, 0, 0),
|
||||
(45, 5, 61),
|
||||
(84, 42, 55),
|
||||
(150, 87, 60),
|
||||
(208, 171, 141),
|
||||
(255, 255, 255)
|
||||
]
|
||||
colors = [(0, 0, 0),(45, 5, 61),(84, 42, 55),(150, 87, 60),(208, 171, 141),(255, 255, 255)]
|
||||
cmap = pg.ColorMap(pos=np.linspace(0.0, 1.0, 6), color=colors)
|
||||
imv.setColorMap(cmap)
|
||||
|
||||
## Start Qt event loop unless running in interactive mode.
|
||||
if (sys.flags.interactive != 1) or not hasattr(QtCore, 'PYQT_VERSION'):
|
||||
QtGui.QApplication.instance().exec_()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
152
illumination.py
Executable file
152
illumination.py
Executable file
@@ -0,0 +1,152 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
import logging
|
||||
_log=logging.getLogger(__name__)
|
||||
import socket
|
||||
|
||||
'''
|
||||
Please do not leave the LEDs on longer than needed, especially the colored ones.
|
||||
I guess I have an issue with
|
||||
power dissipation on my board. For the white LEDs it should be ok.
|
||||
|
||||
The lights communicate in hexidecimal, two 2bytes:
|
||||
byte 1:
|
||||
0x00 get status
|
||||
0x01 off
|
||||
0x02 on
|
||||
0x04 out (unused?)
|
||||
|
||||
byte 2:
|
||||
0x01 front white
|
||||
0x02 front red
|
||||
0x04 front green
|
||||
0x08 front blue
|
||||
0x10 back white
|
||||
'''
|
||||
# commands
|
||||
stat= 0x00
|
||||
off = 0x01
|
||||
on = 0x02
|
||||
|
||||
# leds
|
||||
front = 0x01
|
||||
red = 0x02
|
||||
green = 0x04
|
||||
blue = 0x08
|
||||
back = 0x10
|
||||
all = 0xff
|
||||
|
||||
|
||||
class IlluminationControl(object):
|
||||
|
||||
def __init__(self,hostname: str,port: int):
|
||||
self._hostname = hostname
|
||||
self._port = port
|
||||
self.connect()
|
||||
|
||||
def connect(self):
|
||||
"""creates a socket and connects to illumination controller"""
|
||||
# connect to XT-PICO
|
||||
self._socket=s=socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
s.settimeout(1)
|
||||
s.connect((self._hostname, self._port))
|
||||
|
||||
def disconnect(self):
|
||||
try:
|
||||
self._socket.close()
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
def is_on(self, led_flags: int) -> bool:
|
||||
"""check status for light state, see CONSTANTS STATUS_BITS_*"""
|
||||
try:
|
||||
s = self.status()[0]
|
||||
except:
|
||||
print("error reading status")
|
||||
return False
|
||||
return bool(s & led_flags)
|
||||
|
||||
def send_command(self, cmd: bytes):
|
||||
try:
|
||||
self._socket.sendall(cmd)
|
||||
except (BrokenPipeError, OSError) as e:
|
||||
_log.warning("reconnecting lights")
|
||||
self.connect()
|
||||
try:
|
||||
self._socket.sendall(cmd)
|
||||
except Exception as e:
|
||||
raise IlluminationControlException("unrecoverable socket error: {}".format(e.args))
|
||||
|
||||
def status(self):
|
||||
self.send_command(bytes((stat,all)))
|
||||
resp = self._socket.recv(1024)
|
||||
return resp[0]&0x1f
|
||||
|
||||
def all(self,val):
|
||||
cmd = on if val else off
|
||||
self.send_command(bytes((cmd,all)))
|
||||
|
||||
def front(self,val):
|
||||
cmd = on if val else off
|
||||
self.send_command(bytes((cmd,front)))
|
||||
|
||||
def back(self,val):
|
||||
cmd = on if val else off
|
||||
self.send_command(bytes((cmd,back)))
|
||||
|
||||
def red(self,val):
|
||||
cmd = on if val else off
|
||||
self.send_command(bytes((cmd,red)))
|
||||
|
||||
def blue(self,val):
|
||||
cmd = on if val else off
|
||||
self.send_command(bytes((cmd,blue)))
|
||||
|
||||
def green(self,val):
|
||||
cmd = on if val else off
|
||||
self.send_command(bytes((cmd,green)))
|
||||
|
||||
if __name__ == "__main__":
|
||||
import argparse
|
||||
logging.basicConfig(level=logging.DEBUG,format='%(levelname)s:%(module)s:%(lineno)d:%(funcName)s:%(message)s ')
|
||||
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('-m', '--mode', help='mode string with characters: "frgbka" for front,red,green,blue,back,all')
|
||||
parser.add_argument("-t", "--test", help="test sequence", action="store_true")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
_log.debug(args)
|
||||
host = "129.129.221.92"
|
||||
port = 1003
|
||||
leds = IlluminationControl(host,port)
|
||||
if args.test:
|
||||
leds.all(False)
|
||||
print(bin(leds.status()));time.sleep(1)
|
||||
leds.front(True)
|
||||
print(bin(leds.status()));time.sleep(1)
|
||||
leds.front(False); leds.red(True)
|
||||
print(bin(leds.status()));time.sleep(1)
|
||||
leds.red(False); leds.green(True)
|
||||
print(bin(leds.status()));time.sleep(1)
|
||||
leds.green(False); leds.blue(True)
|
||||
print(bin(leds.status()));time.sleep(1)
|
||||
leds.blue(False); leds.back(True)
|
||||
print(bin(leds.status()));time.sleep(1)
|
||||
leds.back(False)
|
||||
print(bin(leds.status()));time.sleep(1)
|
||||
else:
|
||||
leds.all(False)
|
||||
_log.debug('turn off all')
|
||||
if args.mode is not None:
|
||||
m=args.mode
|
||||
l=0
|
||||
if 'f' in m: l|=front
|
||||
if 'r' in m: l|=red
|
||||
if 'g' in m: l|=green
|
||||
if 'b' in m: l|=blue
|
||||
if 'k' in m: l|=back
|
||||
if 'a' in m: l|=all
|
||||
leds.send_command(bytes((on,l)))
|
||||
_log.debug('turn on %s'%bin(l))
|
||||
|
||||
327
zoom.py
Executable file
327
zoom.py
Executable file
@@ -0,0 +1,327 @@
|
||||
#!/usr/bin/env python
|
||||
|
||||
|
||||
import json
|
||||
import logging
|
||||
_log=logging.getLogger(__name__)
|
||||
import os
|
||||
import qtawesome
|
||||
from PyQt5 import QtWidgets, uic
|
||||
import PyQt5.QtWidgets as qtw
|
||||
from PyQt5.QtCore import pyqtSignal
|
||||
from PyQt5.QtWidgets import (
|
||||
QApplication,
|
||||
QPushButton,
|
||||
QGroupBox,
|
||||
QGridLayout,
|
||||
QLabel,
|
||||
QDoubleSpinBox,
|
||||
QComboBox,
|
||||
QSpinBox,
|
||||
QVBoxLayout,
|
||||
QHBoxLayout,
|
||||
QCheckBox,
|
||||
)
|
||||
from PyQt5.uic import loadUiType
|
||||
import epics
|
||||
|
||||
Ui_Zoom, QWidget = loadUiType("epics_widgets/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 configure(self):
|
||||
keys = settings.allKeys()
|
||||
if "sample_viewing/zoom_buttons" not in keys:
|
||||
settings.setValue("sample_viewing/zoom_buttons",
|
||||
json.dumps([(1, "1"),(200, "200"),(400, "400"),(600, "600"),(800, "800"),(1000, "1000"),]),)
|
||||
buttons = json.loads(settings.value("sample_viewing/zoom_buttons"))
|
||||
|
||||
if "backlight/backlight_pv" not in keys:
|
||||
settings.setValue("backlight/backlight_pv", "SAR-EXPMX:MOT_BLGT")
|
||||
backlight_pv = settings.value("backlight/backlight_pv")
|
||||
|
||||
if "sample_viewing/zoom_api" not in keys:
|
||||
settings.setValue("sample_viewing/zoom_api", "rest://pc12818.psi.ch:9999")
|
||||
zoom_api = settings.value("sample_viewing/zoom_api")
|
||||
|
||||
#self.get_zoom_pv = PV(zoom_api + ":ZOOM-RBV", callback=self.zoom_update_cb)
|
||||
#self.status_pv = PV(zoom_api + ":ZOOM-STATUS", callback=self.zoom_status_cb)
|
||||
current_zoom_value = self.get_zoom()
|
||||
|
||||
# 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_SINGLE_STEP)
|
||||
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)
|
||||
#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)
|
||||
|
||||
#grid.addWidget(QLabel("Acq.Mode"), 1, 0)
|
||||
#cbox = QComboBox()
|
||||
#for mode in Camera.AcquisitionMode:
|
||||
# cbox.addItem(mode.name, mode)
|
||||
#cbox.setCurrentIndex(cam.pv_acqmode.get())
|
||||
#cbox.currentIndexChanged.connect(self.update_camera_acqmode)
|
||||
#grid.addWidget(cbox, 1, 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_in() or bl.is_diode():
|
||||
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))
|
||||
|
||||
def get_zoom(self) -> int:
|
||||
zoom=QApplication.instance()._zoom
|
||||
pos = zoom.get()
|
||||
_log.debug("get_zoom(epics) => {}".format(pos))
|
||||
return pos
|
||||
|
||||
def zoom_update_cb(self, pvname, value, char_value, **kwargs):
|
||||
self._zoom_spinner.blockSignals(True)
|
||||
self._zoom_spinner.setValue(value)
|
||||
self._zoom_spinner.blockSignals(False)
|
||||
|
||||
def zoom_status_cb(self, pvname, value, char_value, **kwargs):
|
||||
busy = bool(value)
|
||||
self.setDisabled(busy)
|
||||
|
||||
|
||||
class QopticZoom(object):
|
||||
|
||||
def __init__(self, prefix='SAR-EXPMX-FETURA'):
|
||||
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(self) -> int:
|
||||
pv=self.getPv('POS_RB')
|
||||
return pv.get()
|
||||
|
||||
def set(self, val: int):
|
||||
|
||||
pv=self.getPv('POS_SP')
|
||||
pv.put(val)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
import backlight
|
||||
import illumination
|
||||
import camera
|
||||
|
||||
from app_config import settings, appsconf
|
||||
|
||||
logging.basicConfig(level=logging.DEBUG,format='%(levelname)s:%(module)s:%(lineno)d:%(funcName)s:%(message)s ')
|
||||
|
||||
_log.debug('Start')
|
||||
app = QApplication(sys.argv)
|
||||
|
||||
app._backlight = backlight.Backlight()
|
||||
app._illumination = illumination.IlluminationControl("129.129.221.92", 1003)
|
||||
app._zoom = QopticZoom()
|
||||
app._camera = camera.camera_server('SARES30-CAMS156-SMX-OAV')
|
||||
|
||||
simulated = appsconf["microscope"]["zoom"].get("simulate", False)
|
||||
|
||||
obj=Zoom()
|
||||
obj.configure()
|
||||
obj.show()
|
||||
app.exec_()
|
||||
|
||||
Reference in New Issue
Block a user