#!/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="

(null)" v="(null)" else: v=f'{v:.5g} (-123)' 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()