Files
EsfRixsApps/ARESvis/ARESvis.py

741 lines
22 KiB
Python
Executable File

#!/usr/bin/env python
# *-----------------------------------------------------------------------*
# | |
# | Copyright (c) 2024 by Paul Scherrer Institute (http://www.psi.ch) |
# | |
# | Author Thierry Zamofing (thierry.zamofing@psi.ch) |
# *-----------------------------------------------------------------------*
"""
Furka ARES chamber visualization
For simulated motor IOC:
/home/zamofing_t/Documents/prj/SwissFEL/test_ioc/MotorSim/iocBoot/ARESvis/ARESvis.cmd
For motor ui:
caQtDM ~/Documents/prj/SwissFEL/test_ioc/MotorSim/iocBoot/ARESvis/ARESvis.ui&
self.pv_angles = [epics.PV("SATES30-ARES:MOT_SRY.RBV"), epics.PV("SATES30-ARES:MOT_DRY.RBV"), epics.PV("SATES30-ARES:MOT_2TRY.RBV")]
SATES30-RIXS:MOT_RY.RBV # sliding seal
SATES30-ARES:MOT_JFRY.RBV # jungfrau detector angle:
SATES30-ARES:MOT_2TRY.RBV # 2thetha angle: foc.mirror Diode2 Diode3
SATES30-ARES:MOT_DRY.RBV # detector angle: Diode1 Mirror
SATES30-ARES:MOT_SRY.RBV # sample rotation
bitmask for simulation:
0x01: EPICS motors
0x02:
0x04:
0x08:
0x10:
0x20:
0x40:
0x80:
"""
from PyQt5.QtWidgets import QApplication, QWidget, QLabel, QPushButton, QSlider, QLineEdit,\
QCheckBox, QHBoxLayout, QVBoxLayout, QGroupBox, QGridLayout, QComboBox
from PyQt5.QtGui import QPainter, QColor, QPen, QBrush, QPolygon, QPolygonF, QTransform, QPainterPath
from PyQt5.QtCore import QPoint, QPointF, Qt,pyqtSignal
import PyQt5.QtGui as QtGui
import PyQt5.QtCore as QtCore
import PyQt5.QtWidgets as QtW
from PyQt5.uic import loadUiType
import numpy as np
import sys, logging, copy
import epics
if sys.version_info[0] < 3 or sys.version_info[1] < 6:
print(f"Must be using Python 3.6 or newer. Try e.g. /opt/gfa/python-3.8/latest/bin/python /sf/furka/bin/ARESvis")
_log=logging.getLogger(__name__)
import logging
class col:
d = '\033[0m' #default
r = '\033[31m' #red
g = '\033[32m' #green
y = '\033[33m' #yellow
rr= '\033[91m' #red(bright)
gg= '\033[92m' #green(bright)
yy= '\033[93m' #yellow(bright)
b = '\033[1m' #bold
u = '\033[4m' #underline
R = '\033[1;31m' #bold, red
G = '\033[1;32m' #bold, green
Y = '\033[1;33m' #bold, yellow
class logHandler(logging.StreamHandler):
def __init__(self):
logging.StreamHandler.__init__(self)
def emit(self, record):
'''override function of base class'''
try:
msg=self.format(record)
# print(record.__dict__)
if record.levelno<=10:
c=col.g
elif record.levelno<=20:
c=col.y
elif record.levelno<=30:
c=col.yy
elif record.levelno<=40:
c=col.r
else:
c=col.rr+col.b
msg=c+msg+col.d
stream=self.stream
stream.write(msg+self.terminator)
self.flush()
except RecursionError:
raise
except Exception:
self.handleError(record)
class ARESdevice():
_lutDifrBeamPaint=( # (number of difr beam,draw mode,alpha,width)
(4, 2, 190, 0),
(4, 1, 200, 0),
(32, 1, 120, 0),
(8, 1, 196, 0),
(32, 0, 120, 3),
(32, 2, 255, 0),
)
def __init__(self,name,**kwargs):
self._name=name
self._paint=p={
'ofs':(820, 350), # location of device
'rArm':int(540), # 540mm inner radius of chamber (RIXS arm)
'r2Th':int(375), # 375mm radius 2Theta platfform
'rJFr':int(270), # 270mm radius of Jungfrau
'rDet':int(207.5), # 207.5mm radius of detector table
'rTrg':int(168.5), # 168.5mm radius of target
'szArm':(20, 50),
'aArm':100, # angle RIXS arm
'aJFr':140, # angle detector
'a2Th':30, # angle 2thetha
'aDet':-20, # angle detector
'aTrg':10, # angle target
'mode':4, #difraction beam paint mode
'szG':(200, 5), # size VLS grating
'szD':(150, 5), # size detector
'sclTrf':(2**(6/2), 2**(-2/2)), # scaling transfformation [angle, distance]
}
# 2thetha angle SATES30-ARES:MOT_2TRY
# detector angle SATES30-ARES:MOT_DRY
# sliding seal SATES30-RIXS:MOT_RY
p.update(kwargs)
self._geo=g={
'r1':2000, # distance probe grating
'r2':3500, # distance grating detector
'aa':88, # grating angle
'bb':87, # reflection angle
'cc':22, # detector angle
}
self.setGeometry(g)
def setGeometry(self,geo):
self._geo=geo
p=self._paint
sclA,sclD=p['sclTrf']
p.update({
'r1':int(geo['r1']*sclD),
'r2':int(geo['r2']*sclD),
'aa':int((90-geo['aa'])*sclA),
'bb':int((90-geo['bb'])*sclA),
'cc':int(geo['cc']),
})
def geometry2motor(self):
# returns raw motor positions
# offset detector plane to deflected beam: 34deg
geo=self._geo
r1,r2,aa,bb,cc=geo['r1'],geo['r2'],geo['aa'],geo['bb'],geo['cc']
mt=gtz=gty1=gty2=grx=gtx=dtz=dty1=dty2=drx=None
degArm=90-aa+90-bb
radArm=np.deg2rad(degArm)
gtz=r1
grx=90-aa
dtz=np.cos(radArm)*r2
dty1=dty2=np.sin(radArm)*r2
drx=90-aa+90-bb+cc-34
dd=cc-34 # angle of bellow to detector
geo.update({
'mt':mt,
'gtz':gtz,
'gty1':gty1,
'gty2': gty2,
'grx':grx,
'gtx':gtx,
'dtz':dtz,
'dty1':dty1,
'dty2':dty2,
'drx':drx})
if degArm>10:
raise(ValueError('angle arm > 10deg'))
elif degArm<1:
raise(ValueError('angle arm < 1deg'))
elif abs(dd)>15:
raise(ValueError('angle bellow to detector > 15deg'))
def containsPoint(self,point):
try:
pg=self._polygon
except AttributeError:
return False
return pg.containsPoint(point,Qt.OddEvenFill)
@staticmethod
def plotOrig(qp):
penR=QPen(QtCore.Qt.red, 2, QtCore.Qt.SolidLine)
penG=QPen(QtCore.Qt.green, 2, QtCore.Qt.SolidLine)
pOrig=qp.pen()
qp.setPen(penR)
qp.drawLine(-20, 0, 20, 0)
qp.drawLine( 20, 0, 16, 2)
qp.setPen(penG)
qp.drawLine(0, -20, 0, 20)
qp.drawLine(0, 20, 2, 16)
qp.setPen(pOrig)
def paint(self,qp):
# qp QPainter to paint on
# ofs caanter to draw
# scl scaling for x and y translation of coordinate systems
# paintMode: mode how to paint the diffraction beam
p=self._paint
ofs=p['ofs']
rArm=p['rArm']
r2Th=p['r2Th']
rJFr=p['rJFr']
rDet=p['rDet']
rTrg=p['rTrg']
aArm=p['aArm']
a2Th=p['a2Th']
aJFr=p['aJFr']
aDet=p['aDet']
aTrg=p['aTrg']
sclTrf=p['sclTrf']
tickW,tickL=3,20 # tick px-width, tick len
diW,diH=40,20 # diodes
miW,miH=50,10 # mirrors
tgW,tgH=80,30 # target
# --- prepare transformations ---
# tf0: target not rotated
# tfArm: target center rotated angle aaArm
# tf2Th: target center rotated angle tf2Th
# tfDet: target center rotated angle aaDet
# tfTrg: target center rotated angle aaTrg
tf0=QTransform()
tf0.translate(ofs[0], ofs[1])
tf0.scale(sclTrf[1],sclTrf[1])
tfArm=copy.copy(tf0) # center
tfArm.rotate(-aArm)
tfJFr=copy.copy(tf0)
tfJFr.rotate(-aJFr)
tf2Th=copy.copy(tf0)
tf2Th.rotate(-a2Th)
tfDet=copy.copy(tf0)
tfDet.rotate(-aDet)
tfTrg=copy.copy(tf0)
tfTrg.rotate(-aTrg)
#tfd.translate(r2,0).rotate(-cc)
penBk=QPen(QtCore.Qt.black, 0, QtCore.Qt.SolidLine)
penWt=QPen(QtCore.Qt.white, 1, QtCore.Qt.SolidLine)
penYl=QPen(QtCore.Qt.yellow, 1, QtCore.Qt.SolidLine)
penBl=QPen(QtCore.Qt.blue, 1, QtCore.Qt.SolidLine)
penRd=QPen(QtCore.Qt.red, 1, QtCore.Qt.SolidLine)
# --- visualize ---
#qp.setRenderHints(QPainter.HighQualityAntialiasing)
# setup and plot dragable region
self._polygon=QPolygon([
QPoint(*tf0.map(-rArm ,-rArm)),
QPoint(*tf0.map(-rArm ,+rArm+100)),
QPoint(*tf0.map(+rArm,+rArm+100)),
QPoint(*tf0.map(+rArm,-rArm)),])
qp.setBrush(QColor(0, 0, 0,64))
qp.drawPolygon(self._polygon)
# plot beam path
qp.setTransform(tf0)
qp.setPen(penBk)
qp.setBrush(QColor(128, 128, 128, 128)) #r,g,b,a
#circles of rotation
#qp.drawEllipse(-rArm, -rArm, 2*rArm, 2*rArm) # ARES chamber
#qp.drawEllipse(-r2Th, -r2Th, 2*r2Th, 2*r2Th) # 2theta
#qp.drawEllipse(-rJFr, -rJFr, 2*rJFr, 2*rJFr) # jungfrau
#qp.drawEllipse(-rDet, -rDet, 2*rDet, 2*rDet) # detector
#qp.drawEllipse(-rTrg, -rTrg, 2*rTrg, 2*rTrg) # target
for r1,r2,col in (
(rArm,r2Th,QColor(255, 0, 0, 32)), #2theta
(r2Th,rDet,QColor( 0,255, 0, 32)), #Jungfrau
#(r2Th,rJFr,QColor( 0,255, 0, 32)), #Jungfrau
#(rJFr,rDet,QColor(255, 0,255, 32)), #detector
(rDet,rTrg,QColor( 0, 0,255, 32)), #target
):
path=QPainterPath()
qp.setBrush(col) #r,g,b,a
path.addEllipse(-r1, -r1, 2*r1, 2*r1)
path.addEllipse(-r2, -r2, 2*r2, 2*r2) # Jungfrau
qp.drawPath(path)
qp.setBrush(Qt.NoBrush)
qp.drawEllipse(-rJFr, -rJFr, 2*rJFr, 2*rJFr) # jungfrau
qp.setBrush(QColor(0,255,255,32)) # r,g,b,a
qp.drawEllipse(-rTrg, -rTrg, 2*rTrg, 2*rTrg) # target
#beam arrow
qp.setPen(QPen(QtCore.Qt.black, 3, QtCore.Qt.SolidLine))
qp.drawLine(0,+rArm+100,0,rArm)
qp.drawPolygon(QPolygon([QPoint(0,rArm),QPoint(-5,rArm+20),QPoint(+5,rArm+20),]))
#crosshair
qp.setPen(penBk)
qp.drawLine(0,-rArm,0,rArm)
qp.drawLine(-rArm,0,rArm,0)
#self.plotOrig(qp)
#qp.setPen(penRd)
#qp.drawRect(-10, -10+rArm, 20, 20)
#--- RIXS-arm devices ---
qp.setTransform(tfArm)
qp.setPen(QPen(QtCore.Qt.red, tickW, QtCore.Qt.SolidLine))
qp.drawLine(0,-rArm,0,-rArm+tickL) #tick
qp.setPen(penBk)
qp.setBrush(QColor(255,0,0,128))
qp.drawRect(-60, -150-rArm, 120, 150) # 2th mount sample
qp.drawRect(-300, -600-rArm, 600, 450) # 2th mount sample
#--- 2-theta devices ---
qp.setTransform(tf2Th)
qp.setPen(QPen(QtCore.Qt.green, tickW, QtCore.Qt.SolidLine))
qp.drawLine(0,-r2Th,0,-r2Th+tickL) #tick
qp.setPen(penBk)
qp.setBrush(QColor(0,255,0,192))
#mirror:310x30mm, 10-40mm dist, 40mm outside 2th
qp.translate(20,-r2Th-40);qp.rotate(2)
qp.drawRect(0, 0, 30, 310) # foc. mirror 1
qp.setTransform(tf2Th);qp.translate(-20,-r2Th-40);qp.rotate(-2)
qp.drawRect(-30, 0, 30, 310) # foc. mirror 2
qp.setTransform(tf2Th);qp.rotate(20);qp.translate(-diW/2,-r2Th);
qp.drawRect(0, 0, diW, diH) # diode2
qp.setTransform(tf2Th);qp.rotate(50);qp.translate(-diW/2,-r2Th);
qp.drawRect(0, 0, diW, diH) # diode3
#--- Jungfrau devices ---
qp.setTransform(tfJFr)
qp.setPen(QPen(QtCore.Qt.magenta, tickW, QtCore.Qt.SolidLine))
qp.drawLine(0,-rJFr,0,-rJFr+tickL) #tick
qp.setPen(penBk)
qp.setBrush(QColor(255,0,255,192))
jfW,jfH=80,20 # jungfrau detector
qp.setTransform(tfJFr);qp.translate(-jfW/2,-rJFr);
qp.drawRect(0, -jfH, jfW, jfH) # detector mount sample
#--- detector devices ---
qp.setTransform(tfDet)
qp.setPen(QPen(QtCore.Qt.blue, tickW, QtCore.Qt.SolidLine))
qp.drawLine(0,-rDet,0,-rDet+tickL) #tick
qp.setPen(penBk)
qp.setBrush(QColor(0,0,255,192))
qp.setTransform(tfDet);qp.rotate(10);qp.translate(-diW/2,-rDet);
qp.drawRect(0, 0, diW, diH) # diode1
qp.setTransform(tfDet);qp.rotate(40);qp.translate(0,-rDet+miW/2);qp.rotate(-45)
qp.drawRect(-int(miW/2), 0, miW, miH) # mirror1
#--- target devices ---
qp.setTransform(tfTrg)
qp.setPen(QPen(QtCore.Qt.cyan, tickW, QtCore.Qt.SolidLine))
qp.drawLine(0,-rTrg,0,-rTrg+tickL) #tick
qp.setPen(penBk)
qp.setBrush(QColor(0,255,255,192))
qp.drawRect(int(-(tgW/2)), -tgH, tgW, tgH) # target mount sample
# --- fixed objects ---
#parabola mirror
qp.setTransform(tf0)
qp.setPen(penBk)
qp.setBrush(QColor(80,80,80,128))
a,b=50.8,12
path=QPainterPath()
path.moveTo(-b-a/2,a)
path.lineTo(-b-a/2,2*a)
path.lineTo( a/2,2*a)
path.lineTo( a/2,2*a)
path.cubicTo(a/2-20,2*a, -a/2,a+20, -a/2,a)
path.lineTo(-b-a/2,a)
qp.drawPath(path)
#qp.drawPolygon(QPolygonF([QPointF(-b-a/2,a),
# QPointF(-b-a/2,2*a),
# QPointF( a/2,2*a),
# QPointF( -a/2,a),]))
qp.setPen(penBl)
qp.drawLine(0 ,0 ,int(a/2) ,int(2*a))
qp.drawLine(int(rArm),int(2*a),int(a/2) ,int(2*a))
qp.drawLine(0 ,0 ,int(-a/2),int(a ))
qp.drawLine(int(rArm),int(a) ,int(-a/2),int(a ))
#qp.setPen(penBk)
#qp.drawRect(-10, -10+rArm, 20, 20)
#self.plotOrig(qp)
#qp.setTransform(tf2Th)
#qp.setPen(penYl)
#qp.drawRect(-10, -10+r2Th, 20, 20)
#self.plotOrig(qp)
#qp.setTransform(tfDet)
#qp.setPen(penBl)
#qp.drawRect(-10, -10+r2Th+20, 20, 20)
#qp.setTransform(tfTrg)
#qp.setPen(penBl)
#qp.drawRect(-10, -10+r2Th+20, 20, 20)
#self.plotOrig(qp)
#qp.setCompositionMode(QtGui.QPainter.CompositionMode_Lighten)
#qp.setCompositionMode(QtGui.QPainter.CompositionMode_SourceOver)
#mouse move polygon
#qp.setTransform(QTransform())
#qp.setPen(penRd)
#qp.drawPolygon(self._polygon)
#origin crosses
#for tf in (tf0,tfa,tfab,tfc):#,tfg,tfs):#,tfc,tfs):
# qp.setTransform(tf);self.plotOrig(qp)
class WndVisualize(QWidget):
_pv2key={
'SATES30-RIXS:MOT_RY.RBV' :'aArm',
'SATES30-ARES:MOT_2TRY.RBV':'a2Th',
'SATES30-ARES:MOT_JFRY.RBV':'aJFr',
'SATES30-ARES:MOT_DRY.RBV' :'aDet',
'SATES30-ARES:MOT_SRY.RBV' :'aTrg'
}
def __init__(self):
super().__init__()
self.initUI()
self.connectEPICS()
def initUI(self):
self.setGeometry(560, 100, 1300, 800)
self.setWindowTitle('Visualize')
app=QApplication.instance()
dev=app._dev
self._wdGrpDraw=w=QGroupBox(dev._name,self)
w.move(10,10)
row=0
lg=QGridLayout(w)
pDev=dev._paint
for key, rng, tk in (
('aArm',(0,360,), 30), # angle ARES sliding seal
('a2Th',(0,360,), 30), # angle 2thetha
('aJFr' ,(0,360,), 30), # angle Jungfrau
('aDet',(0,360,), 30), # angle detector
('aTrg',(0,360,), 30), # angle target
('sclA', ( -8, 8), 1),
('sclD', (-8, 8), 1),):
wLb=QLabel(f"<font>{key}</font>",objectName=key) #MOST BE with <font> as it is changed later and else creates a seg fault
wSl=QSlider(QtCore.Qt.Horizontal,objectName=key)
wSl.setFixedWidth(200);wSl.setMinimum(rng[0]);wSl.setMaximum(rng[1])
if key.startswith('scl'):
if key[-1]=='A':
v=pDev['sclTrf'][0]
else:
v=pDev['sclTrf'][1]
v=int(round(np.log2(v)*2))
else:
v=pDev[key]
wSl.setValue(v)
wSl.setTickPosition(QSlider.TicksBelow);wSl.setTickInterval(tk)
wSl.valueChanged.connect(lambda val,key=key: self.sldChanged(key,val))
lg.addWidget(wLb, row, 0)
lg.addWidget(wSl, row, 1);row+=1
#self.event_update.connect(self.cb_update)
self.show()
def cb_update(self,*args,**kwargs):
_log.debug(f'{args} {kwargs}')
def connectEPICS(self):
_log.info('connect PVs')
self._pvDict=pvd=dict()
self._pvConnected=0
for pvn in self._pv2key.keys():
pv=epics.get_pv(pvn,connection_callback=self.OnConnectionChange,callback=self.OnValueChange)
pvd[pvn]=pv
_log.info(f'{pv}')
#epics.Motor checks the record type and will fail if the record is not online
#therefore use epics.Device
#epics.Device will force to connect the PV in Device.add_callback
#therefore use epics.PV.add_callback to acc callback
#as soon as the devices are online, they are connected
#but with epics.Device creating PV is not fully flexible. epics.get_pv proviles connection and value change callbacks that is way more flexible.
#therefore the lowest level of the library (only pvs is the best suited
#m=epics.Motor(rec_name)
#m=epics.Device(rec_name, delim='.',with_poll=False,attrs=('VAL', 'RBV', 'DESC', 'RVAL','LVIO', 'HLS', 'LLS'))
#m.add_callback('RBV', self.OnChangedRBV)
#pv=m.PV('RBV',connect=False)
#pv.add_callback(self.OnChangedRBV)
#pv.connection_callbacks
#if not pv.connected:
# disconnected.add(rec_name)
#print(pv.connected)
#devs.add(m)
def OnConnectionChange(self, pvname=None, conn=None, **kws):
pvc=self._pvConnected
if conn:
pvc+=1
else:
if pvc>0: pvc-=1
_log.info(f'PV connection {pvc}/{len(self._pvDict)}: {pvname} {conn}')
self._pvConnected=pvc
app=QApplication.instance()
wGrp=app._wndVisualize._wdGrpDraw
key=self._pv2key[pvname]
wLb=wGrp.findChild(QLabel, key)
if not conn:
v=f"<font color='#a00000'>{key}</font>"
wLb.setText(v)
else:
v=f"<font color='#00a000'>{key}</font>"
wLb.setText(v)
def OnValueChange(self, pvname, value, **kw):
#_log.info(kw) #{
# 'pvname': 'SATES30-ARES:MOT_2TRY.RBV', 'value': 102.507, 'char_value': '102.5070', 'status': 0, 'ftype': 20,
# 'chid': 26100744, 'host': 'localhost:5064', 'count': 1, 'access': 'read-only', 'write_access': False, 'read_access': True,
# 'severity': 0, 'timestamp': 1724915455.46745, 'posixseconds': 1724915455.0, 'nanoseconds': 467450100, 'precision': 4,
# 'units': 'deg', 'enum_strs': None, 'upper_disp_limit': 0.0, 'lower_disp_limit': 0.0, 'upper_alarm_limit': nan,
#'lower_alarm_limit': nan, 'lower_warning_limit': nan, 'upper_warning_limit': nan, 'upper_ctrl_limit': 0.0,
#'lower_ctrl_limit': 0.0, 'nelm': 1, 'type': 'time_double', 'typefull': 'time_double',
#'cb_info': (1, <PV 'SATES30-ARES:MOT_2TRY.RBV', count=1, type=time_double, access=read-only>)
#}
_log.info(f"PV val:{pvname}:{value}")
try:
key=self._pv2key[pvname]
except KeyError as e:
_log.warning(f"can't handle PV: {pvname}:{value}")
return
self.vis_update(key,value,'008000')
def vis_update(self,key,value,col='000000'):
app=QApplication.instance()
wGrp=app._wndVisualize._wdGrpDraw
wSl=wGrp.findChild(QSlider, key)
wSl.blockSignals(True)
wSl.setValue(int(value)) # move the slider without emiting a signal
wSl.blockSignals(False)
self.sldChanged(key,value) # emit the signal
#self.event_update.emit(pvname=pvname, value=None)
wLb=wGrp.findChild(QLabel, key)
v=f"<font color='#{col}'>{key}</font>"
wLb.setText(v)
def liveView(self):
# try to live update all PVs
_log.info('')
p2k=self._pv2key
for pv in self._pvDict.values():
pvn=pv.pvname
key=p2k[pvn]
if pv.connected:
value=pv.get()
self.vis_update(key,value,'008000')
print (pvn,key,value)
else:
print (pvn,key)
pass
def destroy(self, destroyWindow, destroySubWindows): #overloaded function
_log.info('destroy')
def closeEvent(self, event): #overloaded function
_log.info('closeEvent')
def sldChanged(self,key,val,*args,**kwargs):
app=QApplication.instance()
dev=app._dev
p=dev._paint
if key.startswith('scl'):
wGrp=self._wdGrpDraw
if key[-1]=='A':
p['sclTrf']=(2**(val/2),p['sclTrf'][1])
else:
p['sclTrf']=(p['sclTrf'][0],2**(val/2))
print(p['sclTrf'])
dev.setGeometry(dev._geo)
else:
p[key]=val
g=dev._geo
if key in ('r1','r2'):
sclD=p['sclTrf'][1]
g[key]=val/sclD
elif key in ('aa', 'bb'):
sclA=p['sclTrf'][0]
g[key]=90-val/sclA
else:
g[key]=val
wGrp=app._wndVisualize._wdGrpDraw
wLb=wGrp.findChild(QLabel, key)
v=f"<font color='#0000ff'>{key}</font>"
wLb.setText(v)#print(wLb,v)
self.update()
def mouseReleaseEvent(self, a0):
try:
del self._mouseDrag
except AttributeError:
pass
def mousePressEvent(self, a0):
app=QApplication.instance()
mousePos=a0.pos()
print(a0.type)
if a0.type()!=QtGui.QMouseEvent.MouseButtonPress:
return
wGrp=self._wdGrpDraw
#if wGrp.underMouse(): #draging sliders?
if wGrp.geometry().contains(mousePos):
self.liveView()
self._mouseDrag={'obj':wGrp, 'start':mousePos}
return
dev=app._dev
if dev.containsPoint(mousePos):
self._devSel=dev
wGrp=self._wdGrpDraw
wGrp.setTitle(dev._name)
devP=dev._paint
for wSl in wGrp.findChildren(QSlider):
#_log.info(wSl)
key=wSl.objectName()
if key.startswith('scl'):
if key[-1]=='A':
v=devP['sclTrf'][0]
else:
v=devP['sclTrf'][1]
v=int(round(np.log2(v)*2))
else:
v=devP[key]
wSl.setValue(int(v))
self._mouseDrag={'obj':dev,'start':(mousePos,dev._paint['ofs'])}
try:
_log.info(f'{self._mouseDrag}')
except AttributeError:
_log.info(f'no object to drag')
def mouseMoveEvent(self, a0):
try:
md=self._mouseDrag
except AttributeError:
return
obj=md['obj']
s=md['start']
if obj==self._wdGrpDraw:
p=a0.pos()
md['start']=p
p=obj.geometry().topLeft()+p-s
obj.move(p)
return
p=a0.pos()
ofs=QPoint(*s[1])+p-s[0]
_log.info(f'{p} {ofs}')
obj._paint['ofs']=(ofs.x(),ofs.y())
self.update()
def paintEvent(self, e):
qp = QPainter()
qp.begin(self)
qp.setRenderHints(QPainter.HighQualityAntialiasing)
app=QApplication.instance()
dev=app._dev
app._dev.paint(qp)
qp.end()
def updateDevice(self, dev):
self._devSel=dev
devP=dev._paint
wGrp=self._wdGrpDraw
wGrp.setTitle(dev._name)
for wSl in wGrp.findChildren(QSlider):
# _log.info(wSl)
key=wSl.objectName()
if key.startswith('scl'):
if key[-1]=='A':
v=devP['sclTrf'][0]
else:
v=devP['sclTrf'][1]
v=int(round(np.log2(v)*2))
else:
v=devP[key]
wSl.blockSignals(True)
wSl.setValue(v)
wSl.blockSignals(False)
self.update()
def OnEvent(self,*args,**kwargs):
#test event
print(f'OnEvent: {args} ,{kwargs}')
if __name__ == '__main__':
import argparse
logging.basicConfig(level=logging.DEBUG, handlers=[logHandler()], format='%(levelname)s:%(module)s:%(lineno)d:%(funcName)s:%(message)s ')
def main():
epilog=__doc__ # +'\nExamples:'+''.join(map(lambda s:cmd+s, exampleCmd))+'\n'
parser=argparse.ArgumentParser(epilog=epilog, formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('--mode', '-m', type=lambda x:int(x, 0), help='mode (see bitmasks) default=0x%(default)x', default=1)
parser.add_argument("--sim", "-s", type=lambda x: int(x,0), help="simulate devices (see bitmasks) default=0x%(default)x", default=0x01)
args=parser.parse_args()
_log.info('Arguments:{}'.format(args.__dict__))
app=QApplication(sys.argv)
app._args=args
app._dev=dev=ARESdevice('Furka-ARES')
if args.mode&0x01:
app._wndVisualize=wnd=WndVisualize()
wnd.show()
sys.exit(app.exec_())
main()