#!/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"{key}",objectName=key) #MOST BE with 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"{key}" wLb.setText(v) else: v=f"{key}" 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, ) #} _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"{key}" 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"{key}" 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()