Files
EsfRixsApps/graphExample.py
2022-11-08 15:51:18 +01:00

564 lines
18 KiB
Python
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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 CaChannel,time
_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):
def __init__(self):
super().__init__()
self._param={'ctr':(200, 300), # location of big chamber
'aa':45, # RIXS arm angle
'cc':45, # UNSUSED
}
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)
w=QtGui.QGroupBox("Drawing",self)
w.move(10,10)
l=QtGui.QVBoxLayout(w)
sld={}
for key,rng,tk,pos in (('aa',(0,180),5,20),('cc',(0,180),5,40),):
sl=QtGui.QSlider(QtCore.Qt.Horizontal)
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):
print(key,val)
self._param[key]=val
self.update()
def paintEvent(self, e):
p=self._param
ctr=p['ctr']
aa=p['aa']
qp = QPainter()
qp.begin(self)
qp.translate(ctr[0],ctr[1])
qp.setPen(QtGui.QPen(QtCore.Qt.black, 2, QtCore.Qt.SolidLine))
qp.setBrush(QColor(255, 80, 0, 128))
rCmb=100 # radius of chamber
rTrg=10 # radius of target
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(-aa)
qp.drawRect(-50, 100, 100, 300) # girder
qp.drawEllipse(-50, 100+150, 20, 20) #pusher left
qp.drawEllipse( 50-20, 100+150, 20, 20) #pusher right
qp.drawEllipse(-50, 100+250, 20, 20) #air pad left
qp.drawEllipse( 50-20, 100+250, 20, 20) #air pad right
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
}
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(('80meV','gratingTest1','gratingTest2')):
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)
w=QtGui.QGroupBox("Drawing",self)
w.move(470,10)
l=QtGui.QVBoxLayout(w)
sld={}
for key,rng,tk,pos in (('r1',(50,800),50,60),('r2',(50,800),50,80),('aa',(0,90),5,20),('bb',(0,90),5,20),('cc',(0,180),5,40),):
sl=QtGui.QSlider(QtCore.Qt.Horizontal)
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']=(90-geo['cc'])
wGeo=self._wdGrpGeometry
for k,v in geo.items():
w=wGeo.findChild(QtGui.QLineEdit, k)
w.setText(f'{v:.6g}')
self._vlsg_raw=raw=vlsg.geometry2raw(**geo)
wRaw=self._wdGrpRaw
for k,v in raw.items():
w=wRaw.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)
p=self._param
if key=='energy':
self.setEnergy(val)
else:
p[key]=val
if 'geo' in p:
del p['geo']
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:
mot={'mt': None,
'gtz': 1089.7275273601558,
'gty1': None,
'gty2': None,
'grx': 1.9320525786473723,
'gtx': None,
'dtz': 4355.976416183402,
'dty1': 683.4429089462491,
'dty2': 683.4429089462491,
'drx': 41.4374216118389}
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)
#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) #beam
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) #beam
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=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
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(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(i*scl, 255, 255, alpha)
qp.setBrush(col)
qp.drawPolygon(p0, p1, p2, p3)
#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=90-d['cc']
qp.setCompositionMode(QtGui.QPainter.CompositionMode_SourceOver)
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,motDict):
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()
self.buttonBox.addButton('move all',QtGui.QDialogButtonBox.ActionRole)
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)
lg=QtGui.QGridLayout(w)
for i,(k,v) in enumerate(motDict.items()):
m='MOT_'+k.upper()
w=QtGui.QCheckBox(m)
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:
v=f'{v:.5g}<font color="#ff0000"> (-123)</font>'
w.setChecked(True)
w=QLabel(v)
lg.addWidget(w, i, 1)
self.layout.addWidget(self.buttonBox)
self.setLayout(self.layout)
if __name__ == '__main__':
logging.basicConfig(level=logging.DEBUG, format='%(levelname)s:%(module)s:%(lineno)d:%(funcName)s:%(message)s ')
def main():
app=QApplication(sys.argv)
vlsg=geometry.VLSgrating()
vlsg.setup('80meV')
app._vlsg=vlsg
#app._wndGirder= RIXSgirder()
app._wndGrating=RIXSgrating()
sys.exit(app.exec_())
main()