759 lines
25 KiB
Python
Executable File
759 lines
25 KiB
Python
Executable File
#!/usr/bin/env python
|
||
# *-----------------------------------------------------------------------*
|
||
# | |
|
||
# | Copyright (c) 2022 by Paul Scherrer Institute (http://www.psi.ch) |
|
||
# | |
|
||
# | Author Thierry Zamofing (thierry.zamofing@psi.ch) |
|
||
# *-----------------------------------------------------------------------*
|
||
|
||
from PyQt5.QtWidgets import QWidget, QLabel, QPushButton, QApplication
|
||
from PyQt5.QtGui import QPainter, QColor, QBrush
|
||
|
||
from pyqtgraph.Qt import QtCore, QtGui
|
||
import numpy as np
|
||
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 geometry
|
||
|
||
import sys
|
||
import logging
|
||
import epics
|
||
|
||
_log=logging.getLogger(__name__)
|
||
|
||
def obj_info(obj,p=''):
|
||
print(f"{p}obj_info:{obj}")
|
||
try:
|
||
pos=obj.pos()
|
||
print(f"{p}pos:({pos.x():.6g},{pos.y():.6g})") # in coordinate value on the scene (no change by zooming)
|
||
except AttributeError:
|
||
pass
|
||
try:
|
||
sz=obj.size()
|
||
print(f"{p}size:({sz.x():.6g},{sz.y():.6g})") # in coordinate value on the scene (no change by zooming)
|
||
except AttributeError:
|
||
pass
|
||
try:
|
||
for k, v in (('Viewport', obj.viewport()), ('Window', obj.window())):
|
||
print(
|
||
f"{p}{k} (x,y)(w,h):({v.x():.6g},{v.y():.6g})({v.width():.6g},{v.height():.6g})") # in coordinate value on the scene (no change by zooming)
|
||
except AttributeError:
|
||
pass
|
||
try:
|
||
scnPos=obj.scenePos()
|
||
print(f"{p}scenePos:({scnPos.x():.6g},{scnPos.y():.6g})") # in pixel on the scene (changes by zooming)
|
||
except AttributeError:
|
||
pass
|
||
try:
|
||
if type(obj)==QtGui.QTransform:
|
||
t=obj
|
||
else:
|
||
t=obj.transform()
|
||
print(f"{p}QTransform:{t.m11():8.5g} {t.m12():8.5g} {t.m13():8.5g}")
|
||
print(f"{p} {t.m21():8.5g} {t.m22():8.5g} {t.m23():8.5g}")
|
||
print(f"{p} {t.m31():8.5g} {t.m32():8.5g} {t.m33():8.5g}")
|
||
except AttributeError:
|
||
pass
|
||
|
||
class RIXSgirder(QWidget):
|
||
_lutColor={0:(255,0,0),1:(0,255,0),2:(255,255,0)}
|
||
def __init__(self):
|
||
super().__init__()
|
||
self._param={'ctr':(200, 300), # location of big chamber
|
||
'rCmb':80, # radius of chamber
|
||
'rTrg':10, # radius of target
|
||
'szSeal': (20,50),
|
||
'szArm': (80,350),
|
||
'aaSeal':70, # RIXS sliding angle
|
||
'aaArm':70, # RIXS arm angle
|
||
'airPads':0, # air pads (off=0 on=1 undef=2)
|
||
'defComp':0, # deformation compensation (off=0 on=1 undef=2)
|
||
}
|
||
|
||
self.initUI()
|
||
|
||
def initUI(self):
|
||
self.setGeometry(300, 300, 850, 800)
|
||
self.setWindowTitle('RIXS girder')
|
||
#label = QLabel('Python', self)
|
||
#label.move(50,50)
|
||
#b1 = QPushButton("Button1",self)
|
||
#b1.move(400,150)
|
||
self._wdGrpDraw=w=QtGui.QGroupBox("Drawing",self)
|
||
w.move(10,10)
|
||
l=QtGui.QVBoxLayout(w)
|
||
|
||
sld={}
|
||
for key,rng,tk in (('aaSeal',(0,180),5),('aaArm',(0,180),5),('airPads',(0,2),1),('defComp',(0,2),1),):
|
||
sl=QtGui.QSlider(QtCore.Qt.Horizontal,objectName=key)
|
||
sl.setFixedWidth(200);sl.setMinimum(rng[0]);sl.setMaximum(rng[1])
|
||
sl.setValue(self._param[key])
|
||
sl.setTickPosition(QtGui.QSlider.TicksBelow);sl.setTickInterval(tk)
|
||
l.addWidget(sl)
|
||
sl.valueChanged.connect(lambda val,key=key: self.sldChanged(key,val))
|
||
sld[key]=sl
|
||
|
||
self.show()
|
||
|
||
def sldChanged(self,key,val,*args,**kwargs):
|
||
p=self._param
|
||
#print(key,val)
|
||
if key=='aaArm':
|
||
p['aaSeal']=v=p['aaSeal']-p['aaArm']+val
|
||
wSl=self._wdGrpDraw.findChild(QtGui.QSlider, 'aaSeal')
|
||
wSl.blockSignals(True)
|
||
wSl.setValue(int(v))
|
||
wSl.blockSignals(False)
|
||
sl=QtGui.QSlider(QtCore.Qt.Horizontal,objectName=key)
|
||
|
||
p[key]=val
|
||
self.update()
|
||
|
||
def paintEvent(self, e):
|
||
p=self._param
|
||
ctr=p['ctr']
|
||
aaSeal=p['aaSeal']
|
||
aaArm=p['aaArm']
|
||
rCmb=p['rCmb']
|
||
rTrg=p['rTrg']
|
||
xa,ya=p['szArm']
|
||
xs,ys=p['szSeal']
|
||
ap=p['airPads']
|
||
dc=p['defComp']
|
||
|
||
|
||
qp = QPainter()
|
||
qp.begin(self)
|
||
qp.translate(ctr[0],ctr[1])
|
||
tf0=qp.transform()
|
||
qp.setPen(QtGui.QPen(QtCore.Qt.black, 2, QtCore.Qt.SolidLine))
|
||
qp.setBrush(QColor(155, 155, 155, 128))
|
||
br1=qp.brush()
|
||
|
||
|
||
qp.drawEllipse(-rCmb, -rCmb, 2*rCmb, 2*rCmb) # big chamber
|
||
qp.drawEllipse(-rTrg, -rTrg, 2*rTrg, 2*rTrg) # target
|
||
qp.drawLine(0,-rCmb-50,0,-rTrg) #beam
|
||
qp.rotate(-aaSeal)
|
||
qp.translate(0,rCmb)
|
||
tf1=qp.transform()
|
||
xs2=xs//2
|
||
xa2=xa//2
|
||
ya2=ya//2
|
||
d=int(abs(aaSeal-aaArm)*20)
|
||
r=min(155+d,255)
|
||
gb=max(155-d,0)
|
||
qp.setBrush(QColor(r, gb, gb, 255))
|
||
qp.drawRect(-xs2, 0, xs, ys) # seal bellow
|
||
|
||
qp.setBrush(br1)
|
||
|
||
qp.setTransform(tf0)
|
||
qp.rotate(-aaArm)
|
||
qp.translate(0,rCmb+ys)
|
||
tf2=qp.transform()
|
||
qp.drawRect(-xa2, 0, xa, ya) # girder
|
||
#qp.drawEllipse(-ya2, 100+150, 20, 20) #pusher left
|
||
#qp.drawEllipse( ya2-r, 100+150, 20, 20) #pusher right
|
||
|
||
#air pad
|
||
r,g,b=RIXSgirder._lutColor[ap]
|
||
rd=13
|
||
r3=int(rd*np.sqrt(3))
|
||
qp.setBrush(QColor(r, g, b, 255))
|
||
qp.drawEllipse(-xa2-rd, ya-4*rd, 2*rd, 2*rd) #left
|
||
qp.drawEllipse(-xa2-rd+r3,ya-3*rd, 2*rd, 2*rd) #left
|
||
qp.drawEllipse(-xa2-rd+r3,ya-5*rd, 2*rd, 2*rd) #left
|
||
|
||
qp.drawEllipse( xa2-rd, ya-4*rd, 2*rd, 2*rd) #right
|
||
qp.drawEllipse( xa2-rd-r3,ya-3*rd, 2*rd, 2*rd) #right
|
||
qp.drawEllipse( xa2-rd-r3,ya-5*rd, 2*rd, 2*rd) #right
|
||
|
||
qp.drawEllipse( -rd, 3*rd, 2*rd, 2*rd) #grating
|
||
qp.drawEllipse( 0, 3*rd-r3, 2*rd, 2*rd) #grating
|
||
qp.drawEllipse( -2*rd, 3*rd-r3, 2*rd, 2*rd) #grating
|
||
|
||
qp.setBrush(br1)
|
||
|
||
#deformation compensation
|
||
r,g,b=RIXSgirder._lutColor[dc]
|
||
rd=14
|
||
qp.setBrush(QColor(r, g, b, 255))
|
||
qp.drawEllipse(-xa2, ya2, 2*rd, 2*rd) #air pad left
|
||
qp.drawEllipse( xa2-2*rd, ya2, 2*rd, 2*rd) #air pad right
|
||
#qp.drawEllipse( -rd, rd, 2*rd, 2*rd) #air pad grating
|
||
qp.setBrush(br1)
|
||
|
||
|
||
|
||
|
||
qp.end()
|
||
|
||
|
||
|
||
|
||
#Ui_MainWindow, QMainWindow = loadUiType("graphEx1.ui")
|
||
#class RIXSgrating(QMainWindow, Ui_MainWindow):
|
||
# def __init__(self, ):
|
||
# super(RIXSgrating, self).__init__()
|
||
# self.setupUi(self)
|
||
|
||
class RIXSgrating(QWidget):
|
||
def __init__(self):
|
||
super().__init__()
|
||
|
||
self._param={'ctr':(500,500), # location of vlsg
|
||
'r1':314,#distance probe grating
|
||
'r2':447,#distance grating detector
|
||
'aa':18, #grating angle
|
||
'bb':18, #reflection angle
|
||
'cc':58, #detector angle
|
||
'szG':(200,5), #size VLS grating
|
||
'szD':(150,5), #size detector
|
||
'difrBeamPaint':4, # mode to plot the difracted beam
|
||
}
|
||
self._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),
|
||
)
|
||
|
||
|
||
# n=4;mode=2;alpha=190
|
||
# n=4;mode=1;alpha=200
|
||
# n=32;mode=1;alpha=120
|
||
# n=8;mode=1;alpha=196
|
||
n=32;
|
||
mode=0;
|
||
alpha=120;
|
||
width=3
|
||
# n=32;mode=2;alpha=255
|
||
|
||
self.initUI()
|
||
|
||
def initUI(self):
|
||
#p=self.palette()
|
||
#p.setColor(self.backgroundRole(), QtCore.Qt.black)
|
||
#self.setPalette(p)
|
||
self.setGeometry(300, 300, 1050, 700)
|
||
self.setWindowTitle('RIXS grating')
|
||
|
||
#w.move(400,100)
|
||
#w.setFixedSize(200,200)
|
||
|
||
self._wdGrpEnergy=w=QtGui.QGroupBox("Energy",self)
|
||
w.move(10,10)
|
||
#w.setFixedSize(200,42+32*2)
|
||
lv=QtGui.QVBoxLayout(w)
|
||
w=QtGui.QWidget() #QGroupBox("E2",self)
|
||
lv.addWidget(w)
|
||
lg=QtGui.QGridLayout(w)
|
||
|
||
i=0;t='Energy'
|
||
w=QLabel(t); lg.addWidget(w, i,0)
|
||
w=QtGui.QLineEdit(t,objectName=t); lg.addWidget(w, i,1)
|
||
w.returnPressed.connect(lambda w=w:self.OnEnergyChanged(w))
|
||
|
||
i+=1;t='Grating'
|
||
w=QLabel(t); lg.addWidget(w, i,0)
|
||
w=QtGui.QComboBox(objectName=t); lg.addWidget(w, i,1)
|
||
for i,t in enumerate(geometry.VLSgrating.vlsgTypes):
|
||
w.insertItem(i,t)
|
||
w.currentIndexChanged.connect(lambda val,w=w:self.OnEnergyChanged(w,val))
|
||
|
||
key,rng,tk,pos=('energy',(200,1500),50,60)
|
||
w=QtGui.QSlider(QtCore.Qt.Horizontal,objectName=key)
|
||
w.setFixedWidth(200);w.setMinimum(rng[0]);w.setMaximum(rng[1])
|
||
#w.setValue(self._param[key])
|
||
w.setTickPosition(QtGui.QSlider.TicksBelow);w.setTickInterval(tk)
|
||
lv.addWidget(w)
|
||
#sl.valueChanged.connect(lambda val,key=key: self.sldChanged(key,val))
|
||
w.valueChanged.connect(lambda val,w=w:self.OnEnergyChanged(w,val))
|
||
|
||
self._wdGrpGeometry=w=QtGui.QGroupBox("Geometry",self)
|
||
w.move(10,160)
|
||
#w.setFixedSize(200,42+32*4)
|
||
l=QtGui.QGridLayout(w)
|
||
lut={'aa':'\u03B1', 'bb':'\u03B2', 'cc':'\u03B3'}
|
||
for i,t in enumerate(('r1','r2','aa','bb','cc',)):
|
||
tl=lut.get(t,t)
|
||
w=QLabel(tl)
|
||
l.addWidget(w, i,0)
|
||
w=QtGui.QLineEdit(t,objectName=t)
|
||
l.addWidget(w, i,1)
|
||
|
||
self._wdGrpRaw=w=QtGui.QGroupBox("Motors",self)
|
||
w.move(250,10)
|
||
#w.setFixedSize(200,42+32*4)
|
||
l=QtGui.QGridLayout(w)
|
||
|
||
motors=(
|
||
('MT', 'Mask translation'),
|
||
('GTZ', 'Grating translation along Z-axis (along beam)'),
|
||
('GTY1', 'Grating translation along Y-axis (height) Wedge leveller 1'),
|
||
('GTY2', 'Grating translation along Y-axis (height) Wedge leveller 2'),
|
||
('GRX', 'Grating rotation around X-axis'),
|
||
('GTX', 'Grating translation along X-axis'),
|
||
('DTZ', 'Detector Translation along Z-axis'),
|
||
('DTY1', 'Detector Translation along Y-axis'),
|
||
('DTY2', 'Detector Translation along Y-axis'),
|
||
('DRX', 'Detector Rotation around X-axis (ɣ) '),
|
||
)
|
||
|
||
for i,m in enumerate(motors):
|
||
tl,inf=m
|
||
t=tl.lower()
|
||
w=QLabel(tl)
|
||
l.addWidget(w, i,0)
|
||
w=QtGui.QLineEdit(t,objectName=t)
|
||
w.setToolTip(inf)
|
||
l.addWidget(w, i,1)
|
||
w=QtGui.QPushButton('move all motors')
|
||
w.clicked.connect(self.btnMoveAllMotors)
|
||
l.addWidget(w, i+1, 1)
|
||
|
||
|
||
self._wdGrpDraw=w=QtGui.QGroupBox("Drawing",self)
|
||
w.move(470,10)
|
||
l=QtGui.QVBoxLayout(w)
|
||
|
||
sld={}
|
||
for key,rng,tk in (('r1',(50,800),50),('r2',(50,800),50),('aa',(0,90),5),('bb',(0,90),5),('cc',(0,180),5),
|
||
('difrBeamPaint',(0,len(self._lutDifrBeamPaint)-1),1),):
|
||
sl=QtGui.QSlider(QtCore.Qt.Horizontal,objectName=key)
|
||
sl.setFixedWidth(200);sl.setMinimum(rng[0]);sl.setMaximum(rng[1])
|
||
sl.setValue(self._param[key])
|
||
sl.setTickPosition(QtGui.QSlider.TicksBelow);sl.setTickInterval(tk)
|
||
l.addWidget(sl)
|
||
sl.valueChanged.connect(lambda val,key=key: self.sldChanged(key,val))
|
||
sld[key]=sl
|
||
|
||
self.show()
|
||
|
||
def OnEnergyChanged(self,w,*args):
|
||
#_log.debug(f'OnEnergyChanged: {w} {args}')
|
||
app=QApplication.instance()
|
||
#wCb=self._wdGrpEnergy.findChild(QtGui.QComboBox, 'Grating')
|
||
wLe=self._wdGrpEnergy.findChild(QtGui.QLineEdit, 'Energy')
|
||
wSl=self._wdGrpEnergy.findChild(QtGui.QSlider, 'energy')
|
||
t=type(w)
|
||
if t==QtGui.QLineEdit:
|
||
try:
|
||
e=float(w.text())
|
||
except ValueError as e:
|
||
return
|
||
wSl.blockSignals(True)
|
||
wSl.setValue(int(e))
|
||
wSl.blockSignals(False)
|
||
elif t==QtGui.QComboBox:
|
||
app=QApplication.instance()
|
||
vlsg=app._vlsg
|
||
vlsg.setup(w.currentText())
|
||
try:
|
||
e=float(wLe.text())
|
||
except ValueError as e:
|
||
return
|
||
elif t==QtGui.QSlider:
|
||
e=args[0]
|
||
wLe.setText(f'{e:.4g}')
|
||
else:
|
||
raise(TypeError(f'wrong type: {t}'))
|
||
self.setEnergy(e)
|
||
|
||
def OnEvent(self,*args,**kwargs):
|
||
#test event
|
||
print(f'OnEvent: {args} ,{kwargs}')
|
||
|
||
def setEnergy(self,val):
|
||
app=QApplication.instance()
|
||
p=self._param
|
||
wGrating=self._wdGrpEnergy.findChild(QtGui.QComboBox, 'Grating')
|
||
wEnergy=self._wdGrpEnergy.findChild(QtGui.QLineEdit, 'Energy')
|
||
wEnergy.setText(f'{val:.4g}')
|
||
|
||
vlsg=app._vlsg
|
||
vlsg.setup(wGrating.currentText())
|
||
self._vlsg_geo=geo=vlsg.energy2geometry(val)
|
||
p['r1']=geo['r1']/8
|
||
p['r2']=geo['r2']/8
|
||
p['aa']=(90-geo['aa'])*5
|
||
p['bb']=(90-geo['bb'])*5
|
||
p['cc']=geo['cc']
|
||
|
||
grp=self._wdGrpDraw
|
||
for k,v in p.items():
|
||
w=grp.findChild(QtGui.QSlider, k)
|
||
if w is not None:
|
||
w.blockSignals(True)
|
||
w.setValue(int(v))
|
||
w.blockSignals(False)
|
||
|
||
grp=self._wdGrpGeometry
|
||
for k,v in geo.items():
|
||
w=grp.findChild(QtGui.QLineEdit, k)
|
||
w.setText(f'{v:.6g}')
|
||
|
||
self._vlsg_raw=raw,err=vlsg.geometry2raw(**geo)
|
||
if err is not None:
|
||
_log.error(str(err))
|
||
self._vlsg_raw=raw
|
||
grp=self._wdGrpRaw
|
||
for k,v in raw.items():
|
||
w=grp.findChild(QtGui.QLineEdit, k)
|
||
if v is None:
|
||
w.setText('(null)')
|
||
else:
|
||
w.setText(f'{v:.6g}')
|
||
self.update()
|
||
|
||
def sldChanged(self,key,val,*args,**kwargs):
|
||
#print(key,val)
|
||
try:
|
||
self._vlsg_geo
|
||
except AttributeError:
|
||
pass
|
||
p=self._param
|
||
p[key]=val
|
||
self.update()
|
||
|
||
def btnMoveAllMotors(self):
|
||
p=self._param
|
||
wGr=self._wdGrpEnergy.findChild(QtGui.QComboBox, 'Grating')
|
||
wEn=self._wdGrpEnergy.findChild(QtGui.QLineEdit, 'Energy')
|
||
try:
|
||
mot=self._vlsg_raw
|
||
except AttributeError:
|
||
_log.error('no current energy set')
|
||
return
|
||
dlg=DlgMoveMotors()
|
||
dlg.initUI('prefix',mot)
|
||
#dlg.initUI('prefix',self._vlsg_raw)
|
||
if dlg.exec():
|
||
print("Success!")
|
||
else:
|
||
print("Cancel!")
|
||
|
||
|
||
#print(f'grating:{wGr.currentText()} energy:{wEn.text()}')
|
||
#print(f'geometry:{self._vlsg_geo}')
|
||
#print(f'raw motors:{self._vlsg_raw}')
|
||
|
||
def paintEvent(self, e):
|
||
p=self._param
|
||
r1=int(p['r1'])
|
||
r2=int(p['r2'])
|
||
aa=p['aa']
|
||
bb=p['bb']
|
||
cc=p['cc']
|
||
szG=p['szG']
|
||
szD=p['szD']
|
||
ctr=p['ctr']
|
||
qp = QPainter()
|
||
qp.begin(self)
|
||
qp.setRenderHints(QPainter.HighQualityAntialiasing)
|
||
|
||
#plot black background
|
||
w=int(max(szG[0],szD[0])/2)
|
||
ctr=(max(ctr[0],w+r1),max(w+r2*np.sin((aa+bb)*np.pi/180),ctr[1]))
|
||
qp.translate(ctr[0],ctr[1])
|
||
qp.setBrush(QColor(0, 0, 0))
|
||
qp.drawRect(-r1-w, -w, r1+w, 2*w)
|
||
#qp.drawEllipse(-r1-w, -w, 2*w, 2*w)
|
||
qp.drawEllipse(-w, -w, 2*w, 2*w)
|
||
qp.rotate(180-aa-bb)
|
||
qp.drawRect(-r2-w, -w, r2+w, 2*w)
|
||
#qp.drawEllipse(-r2-w, -w, 2*w, 2*w)
|
||
|
||
#plot beam path
|
||
qp.setTransform(QtGui.QTransform())
|
||
qp.translate(ctr[0],ctr[1])
|
||
qp.setCompositionMode(QtGui.QPainter.CompositionMode_Lighten)
|
||
|
||
tf0=qp.transform()
|
||
qp.setPen(QtGui.QPen(QtCore.Qt.white, 1, QtCore.Qt.SolidLine))
|
||
qp.setBrush(QColor(255, 80, 0, 128))
|
||
|
||
qp.drawEllipse(-r1-20, -10, 20, 20) # target
|
||
qp.drawLine(-r1,0,0,0) #central beam1
|
||
qp.rotate(-aa)
|
||
tf1=qp.transform()
|
||
qp.drawRect(int(-szG[0]/2), 0, szG[0], szG[1]) # grating
|
||
qp.rotate(-bb)
|
||
tf2=qp.transform()
|
||
qp.drawLine(0,0,int(r2+szD[0]/2),0) #central beam2
|
||
qp.translate(r2,0)
|
||
qp.rotate(-cc)
|
||
tf3=qp.transform()
|
||
qp.drawRect(int(-szD[0]/2),0 , szD[0], szD[1]) # detector
|
||
|
||
#beam target to vlsg
|
||
qp.setTransform(QtGui.QTransform())
|
||
p0=QtCore.QPointF(*tf0.map(-r1-10, 10))
|
||
p1=QtCore.QPointF(*tf1.map(-szG[0]/2, 0))
|
||
qp.drawLine(p0,p1)
|
||
p0=QtCore.QPointF(*tf0.map(-r1-10, -10))
|
||
p1=QtCore.QPointF(*tf1.map(szG[0]/2, 0))
|
||
qp.drawLine(p0,p1)
|
||
|
||
# beam vlsg to detector
|
||
#n=32;mode=0;alpha=120;width=3
|
||
n,mode,alpha,width=self._lutDifrBeamPaint[p['difrBeamPaint']]
|
||
scl=256/n
|
||
p0=QtCore.QPointF(*tf1.map(-szG[0]/2, 0))
|
||
p1=QtCore.QPointF(*tf1.map(szG[0]/2, 0))
|
||
|
||
if mode==0:
|
||
pen=QtGui.QPen(QtCore.Qt.black, width, QtCore.Qt.SolidLine)
|
||
col=QtGui.QColor()
|
||
for i in range(n):
|
||
p2=QtCore.QPointF(*tf3.map(szD[0]/2-szD[0]*i/(n-1),0))
|
||
col.setHsv(int(i*scl),255,255,alpha)
|
||
pen.setColor(col)
|
||
qp.setPen(pen)
|
||
qp.drawLine(p0,p2)
|
||
qp.drawLine(p1,p2)
|
||
if mode==1:
|
||
col=QtGui.QColor()
|
||
qp.setPen(QtGui.QPen(QtCore.Qt.black, 0, QtCore.Qt.SolidLine))
|
||
for i in range(n):
|
||
p2=QtCore.QPointF(*tf3.map(szD[0]/2-szD[0]*i/(n-1), 0))
|
||
col.setHsv(int(i*scl), 255, 255, alpha)
|
||
qp.setBrush(col)
|
||
qp.drawPolygon(p0, p1, p2)
|
||
if mode==2:
|
||
col=QtGui.QColor()
|
||
qp.setPen(QtGui.QPen(QtCore.Qt.black, 0, QtCore.Qt.SolidLine))
|
||
for i in range(n):
|
||
p2=QtCore.QPointF(*tf3.map(szD[0]/2-szD[0]*(i+1)/n, 0))
|
||
p3=QtCore.QPointF(*tf3.map(szD[0]/2-szD[0]*i/n, 0))
|
||
col.setHsv(int(i*scl), 255, 255, alpha)
|
||
qp.setBrush(col)
|
||
qp.drawPolygon(p0, p1, p2, p3)
|
||
|
||
qp.setCompositionMode(QtGui.QPainter.CompositionMode_SourceOver)
|
||
|
||
#draw central beam (if needed)
|
||
if mode!=0:
|
||
qp.setTransform(tf0)
|
||
qp.setPen(QtGui.QPen(QtGui.QColor(0x757780), 2, QtCore.Qt.SolidLine))
|
||
qp.drawLine(-r1,0,0,0) #central beam1
|
||
qp.setTransform(tf2)
|
||
qp.drawLine(0,0,int(r2+szD[0]/2),0) #central beam2
|
||
|
||
#draw dimensions
|
||
try:
|
||
d=self._vlsg_geo
|
||
except AttributeError:
|
||
vr1=r1; vr2=r2; vaa=aa; vbb=bb; vcc=cc
|
||
else:
|
||
vr1=d['r1']
|
||
vr2=d['r2']
|
||
vaa=90-d['aa']
|
||
vbb=90-d['bb']
|
||
vcc=d['cc']
|
||
|
||
qp.setTransform(QtGui.QTransform())
|
||
pen=QtGui.QPen(QtCore.Qt.red, 1, QtCore.Qt.SolidLine)
|
||
penTxt=QtGui.QPen(QtCore.Qt.yellow, 1, QtCore.Qt.SolidLine)
|
||
qp.setPen(pen)
|
||
p0=QtCore.QPointF(*tf0.map(-r1-10, +10))
|
||
p1=QtCore.QPointF(*tf0.map(-r1-10, 0+w))
|
||
qp.drawLine(p0,p1)
|
||
p2=QtCore.QPointF(*tf0.map(0, 0))
|
||
p3=QtCore.QPointF(*tf0.map(0, 0+w))
|
||
qp.drawLine(p2,p3)
|
||
p4=(p1+p3)/2 #text pos
|
||
p5=QtCore.QPointF(*tf2.map(0, 0+w))
|
||
qp.drawLine(p2,p5)
|
||
p6=QtCore.QPointF(*tf2.map(r2, 0))
|
||
p7=QtCore.QPointF(*tf2.map(r2, 0+w))
|
||
qp.drawLine(p6,p7)
|
||
p8=(p5+p7)/2 #text pos
|
||
|
||
p9=QtCore.QPointF(*tf1.map(0, -w))
|
||
qp.drawLine(p2,p9)
|
||
s=50
|
||
qp.drawArc(int(p2.x())-s, int(p2.y())-s, 2*s, 2*s, 180*16, int(aa*16))
|
||
s1=s*np.cos(aa*np.pi/180); s2=s*np.sin(aa*np.pi/180)
|
||
qp.drawArc(int(p2.x())-s, int(p2.y())-s, 2*s, 2*s, int(aa*16), int(bb*16))
|
||
|
||
qp.drawArc(int(p6.x())-s, int(p6.y())-s, 2*s, 2*s, int((aa+bb)*16), int(cc*16))
|
||
p10=QtCore.QPointF(*tf2.map(r2+20, 0)) #text pos
|
||
|
||
qp.setPen(penTxt)
|
||
qp.drawText(p4+QtCore.QPointF(-40,-10),f'R1={vr1:.5g}mm')
|
||
#qp.setTransform(QtGui.QTransform())
|
||
qp.translate(p8); qp.rotate(-aa-bb)
|
||
qp.drawText(-40,-10,f'R2={vr2:.5g}mm')
|
||
qp.setTransform(QtGui.QTransform())
|
||
qp.translate(p2); qp.rotate(-aa)
|
||
qp.drawText(20-int(szG[0]/2),20,f'aa={vaa:.5g}°')
|
||
qp.drawText(20,20,f'bb={vbb:.5g}°')
|
||
qp.setTransform(QtGui.QTransform())
|
||
qp.translate(p10); qp.rotate(-aa-bb)
|
||
qp.drawText(0,20,f'cc={vcc:.5g}°')
|
||
|
||
#for i,p in enumerate((p0,p1,p2,p3,p4,p5,p6,p7,p8,p9)):
|
||
# qp.drawText(p,f'P{i}')
|
||
#p5=QtCore.QPointF(*tf1.map(-r2-10, 50+w))
|
||
qp.end()
|
||
|
||
#https://www.pythonguis.com/tutorials/pyqt-dialogs/
|
||
class DlgMoveMotors(QtGui.QDialog):
|
||
def __init__(self):
|
||
super().__init__()
|
||
#self.initUI()
|
||
|
||
def initUI(self,prefix,motDst):
|
||
self._motDst=motDst
|
||
self.setWindowTitle("Move Motors:")
|
||
|
||
#QBtn=QtGui.QDialogButtonBox.Ok|QtGui.QDialogButtonBox.Cancel
|
||
#self.buttonBox=QtGui.QDialogButtonBox(QBtn)
|
||
#self.buttonBox.accepted.connect(self.accept)
|
||
#self.buttonBox.rejected.connect(self.reject)
|
||
|
||
self.buttonBox=QtGui.QDialogButtonBox()
|
||
btn=self.buttonBox.addButton('move all',QtGui.QDialogButtonBox.ActionRole)
|
||
btn.clicked.connect(self.OnMove)
|
||
self.buttonBox.addButton('stop all',QtGui.QDialogButtonBox.ActionRole)
|
||
|
||
self.layout=QtGui.QVBoxLayout()
|
||
message=QLabel("Something happened, is that OK?")
|
||
self.layout.addWidget(message)
|
||
|
||
self._wdGrpRaw=w=QtGui.QGroupBox("Motors", self)
|
||
self.layout.addWidget(w)
|
||
self._layoutGrid=lg=QtGui.QGridLayout(w)
|
||
|
||
# dev=epics.Device('SATES30-RIXS:MOT_ABL.', attrs=('VAL', 'RBV', 'DESC'))
|
||
app=QApplication.instance()
|
||
devs=app._devs
|
||
prefix='SATES30-RIXS:'
|
||
for i,(k,v) in enumerate(motDst.items()):
|
||
m='MOT_'+k.upper()
|
||
#devs[k]=dev=epics.Device(prefix+m+'.', attrs=('RBV', 'DESC'), timeout=.5) # for safty: removed 'VAL',
|
||
try:
|
||
dev=devs[k]
|
||
except KeyError:
|
||
devs[k]=dev=SimDevice(prefix+m+'.', attrs=('VAL', 'RBV', 'DESC'))
|
||
w=QtGui.QCheckBox(m,objectName=k)
|
||
lg.addWidget(w, i, 0)
|
||
if v is None:
|
||
#v="<P><b><font color='#808080'>(null)</i></b></font>"
|
||
v="<font color='#808080'>(null)</font>"
|
||
|
||
else:
|
||
rbv=dev.RBV
|
||
d=v-rbv
|
||
if d>0:
|
||
c='ff8080'
|
||
else:
|
||
c='60a060'
|
||
v=f"{v:.5g} <font color='#808080'>(old:{rbv:.5g} <font color='#{c}'> {d:.5g})</font></font>"
|
||
w.setChecked(True)
|
||
w=QLabel(v)
|
||
lg.addWidget(w, i, 1)
|
||
|
||
self.layout.addWidget(self.buttonBox)
|
||
self.setLayout(self.layout)
|
||
|
||
def OnMove(self):
|
||
app=QApplication.instance()
|
||
devs=app._devs
|
||
motDst=self._motDst
|
||
lg=self._layoutGrid
|
||
col=lg.columnCount()
|
||
for i,(k,v) in enumerate(motDst.items()):
|
||
cb=lg.itemAt(i*col).widget()
|
||
if cb.isChecked():
|
||
if v is not None:
|
||
dev=devs[k]
|
||
#print(dev)
|
||
dev.VAL=v # move motor
|
||
print(f'{cb.objectName()} {dev}')
|
||
|
||
|
||
# ->>>> https://pyepics.github.io/pyepics/devices.html >>> USE THIS
|
||
#[satesf-cons-03 ~]$ /opt/gfa/python-3.8/latest/bin/ipython3
|
||
#import CaChannel
|
||
#import epics
|
||
|
||
#capv=CaChannel.CaChannel('SATES30-RIXS:MOT_ABL.VAL') -> THIS IS REALLY BASIC...
|
||
#capv.state()
|
||
#m=epics.Motor('SATES30-RIXS:MOT_ABL')
|
||
#pv=pvm.PV('RBV')
|
||
#dev=epics.Device('SATES30-RIXS:MOT_ABL.', attrs=('VAL', 'RBV', 'DESC'))
|
||
#dev.RBV
|
||
class SimDevice():
|
||
def __init__(self, prefix='', attrs=None,
|
||
nonpvs=None, delim='', timeout=None,
|
||
mutable=True, aliases=None, with_poll=True):
|
||
self._prefix=prefix
|
||
self._pvs={}
|
||
for k in attrs:
|
||
self._pvs[k]=0
|
||
|
||
def __getattr__(self, attr):
|
||
#if self._pvs:
|
||
if '_pvs' in self.__dict__:
|
||
return self._pvs[attr]
|
||
else:
|
||
_log.debug(attr)
|
||
raise AttributeError
|
||
|
||
def __setattr__(self, attr, val):
|
||
if attr in ('_pvs','_prefix'):
|
||
self.__dict__[attr]=val
|
||
elif attr in self._pvs:
|
||
self._pvs[attr]= val
|
||
if attr=='VAL' and 'RBV' in self._pvs:
|
||
self._pvs['RBV']=val
|
||
else:
|
||
self.__dict__[attr]=val
|
||
|
||
def __repr__(self):
|
||
"string representation"
|
||
attr=[]
|
||
for k,v in self._pvs.items():
|
||
attr.append(f'{k}: {v}')
|
||
s=f"<Device: {self._prefix} ( "+', '.join(attr)+' )'
|
||
return s
|
||
|
||
|
||
# if attr in self._pvs:
|
||
# return self.get(attr)
|
||
# elif attr in self.__dict__:
|
||
# return self.__dict__[attr]
|
||
|
||
if __name__ == '__main__':
|
||
import argparse
|
||
logging.basicConfig(level=logging.DEBUG, 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=2)
|
||
parser.add_argument("--sim", "-s", type=lambda x: int(x,0), help="simulate devices (see bitmasks) default=0x%(default)x", default=0)
|
||
args=parser.parse_args()
|
||
_log.info('Arguments:{}'.format(args.__dict__))
|
||
|
||
app=QApplication(sys.argv)
|
||
vlsg=geometry.VLSgrating()
|
||
vlsg.setup('80meV')
|
||
app._vlsg=vlsg
|
||
app._devs=dict() # all device (grouped PVs)
|
||
if args.mode&0x01:
|
||
app._wndGirder= RIXSgirder()
|
||
if args.mode&0x02:
|
||
app._wndGrating=RIXSgrating()
|
||
sys.exit(app.exec_())
|
||
|
||
main() |