#!/usr/bin/env python # *-----------------------------------------------------------------------* # | | # | Copyright (c) 2022 by Paul Scherrer Institute (http://www.psi.ch) | # | Based on Zac great first implementation | # | Author Thierry Zamofing (thierry.zamofing@psi.ch) | # *-----------------------------------------------------------------------* ''' User pyqtgraph objects and functions. TODO: Dimension of pyqtgraph is in um not pixel (as now) ''' #import initExample ## Add path to library (just for examples; you do not need this) import logging _log=logging.getLogger(__name__) import pyqtgraph as pg from pyqtgraph.Qt import QtCore, QtGui import numpy as np from PyQt5.QtGui import QPolygon,QPolygonF from PyQt5.QtCore import Qt,QPointF,QLineF from PyQt5.QtWidgets import QMenu def itr2str(itr): return '('+', '.join(tuple(map(lambda x:f'{x:.6g}', itr)))+')' def obj_tree(obj,p=''): obj_info(obj,p) for o in obj.childItems(): obj_tree(o,p+'.') 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 UsrROI(pg.ROI): def __init__(self, pos, size, **kargs): pg.ROI.__init__(self, pos, size, **kargs) def cb_toggle_movable(self): self.translatable=not self.translatable def contextMenuEvent(self, event): #pg.ROI.contextMenuEvent(self,event) _log.debug('ContextMenu') menu = QMenu("ctx") act=menu.addAction('locked') act.setCheckable(True) act.setChecked(not self.translatable) act.triggered.connect(self.cb_toggle_movable) #menu.addAction('center in view') #menu.addAction('delete') menu.exec(event.screenPos()) class Marker(UsrROI): """A crosshair ROI whose position is at the center of the crosshairs. By default, it is scalable, rotatable and translatable.""" def __init__(self, pos, size, mode, **kargs): UsrROI.__init__(self, pos, size, **kargs) self._mode=mode #widget.signal.connect(slot_function) #self.sigRegionChangeFinished.connect(self.OnRgnChanged) #def OnRgnChanged(self, event): # print(event) # obj_info(self) def paint(self, p, *args): #pg.ROI.paint(self, p, *args) r=QtCore.QRectF(0, 0, self.state['size'][0], self.state['size'][1]).normalized() p.setRenderHint(QtGui.QPainter.Antialiasing) p.setPen(self.currentPen) p.translate(r.left(), r.top()) #p.scale(r.width(), r.height()) # -> values x,y 0 to 1 #f=p.font(); #f.setPixelSize(50) #p.setFont(f) # p.drawText(0, 0, '{:.0f}x{:.0f}'.format(*tuple(self.size()))) #p.drawText(0, 0, 'Thierry') p.scale(.01*r.width(), .01*r.height()) # -> values x,y 0 to 100 m=self._mode if m==0: p.drawEllipse(0, 0, 100, 100) p.drawRect(0, 0, 100, 100) p.drawRect(0, 0, 5, 5) p.setPen(pg.mkPen(width=3, color=[200, 100, 100])) p.drawLine(pg.Point(50, 0), pg.Point(50, 100)) p.drawLine(pg.Point( 0,50), pg.Point(100, 50)) ofx,ofy=Marker.txtTrf(p) f=p.font() f.setPixelSize(10) p.setFont(f) p.drawText(ofx+24, ofy+20, 'beam marker') ctr=tuple((self.pos()+self.size()/2)*1000) sz=tuple(self.size()*1000) p.drawText(ofx+5, ofy+45, '{:.1f}x{:.1f} um'.format(*sz)) #p.drawText(5, -35, '{:.1f}'.format(ctr[0])) p.drawText(ofx+5, ofy+55,42,30,Qt.AlignRight, '{:.1f}'.format(ctr[0])) p.drawText(ofx+55, ofy+65, '{:.1f} um'.format(ctr[1])) elif m==1: p.drawEllipse(20,20,60,60) p.drawRect(0, 0, 5, 5) p.drawRect(0, 0, 100, 100) p.setPen(pg.mkPen(width=2, color=[10, 255, 0])) p.drawLine(pg.Point(50, 0), pg.Point( 50,100)) p.drawLine(pg.Point( 0,50), pg.Point(100, 50)) ofx,ofy=Marker.txtTrf(p) f=p.font(); f.setPixelSize(10) p.setFont(f) #p.drawText(ofx+0, ofy+0, 'FFF') #p.drawText(ofx+100, ofy+100, 'GGG') px=tuple(self.pos()+self.size()/2) p.drawText(ofx+18, ofy+10, 'optical center') p.drawText(ofx+5, ofy+80,42,30,Qt.AlignRight, '{:.1f}'.format(px[0])) p.drawText(ofx+55,ofy+90, '{:.1f}'.format(px[1])) @staticmethod def txtTrf(p): tr=p.transform() assert (p.transform()==p.worldTransform()) m11, m12, m13, m21, m22, m23, m31, m32, m33=tr.m11(), tr.m12(), tr.m13(), tr.m21(), tr.m22(), tr.m23(), tr.m31(), tr.m32(), tr.m33() ofx=ofy=0 if m11<0: m11=-m11; m12=-m12; ofx-=100 if m22<0: m22=-m22; m21=-m21; ofy=-100 tr.setMatrix(m11, m12, m13, m21, m22, m23, m31, m32, m33) p.setTransform(tr) return ofx,ofy class Fiducial(UsrROI): def __init__(self, pos, size, z:float, **kargs): UsrROI.__init__(self, pos, size, **kargs) self._z=z def paint(self, p, *args): #pg.ROI.paint(self, p, *args) r=QtCore.QRectF(0, 0, self.state['size'][0], self.state['size'][1]).normalized() p.setRenderHint(QtGui.QPainter.Antialiasing) p.setPen(self.currentPen) p.translate(r.left(), r.top()) p.scale(.01*r.width(), .01*r.height()) # -> values x,y 0 to 100 p.drawRect(0, 0, 100, 100) p.drawEllipse(45, 45, 10, 10) p.setPen(pg.mkPen(width=1, color=[255, 0, 0])) p.drawLine(pg.Point(50,10), pg.Point(50, 90)) p.drawLine(pg.Point(10,50), pg.Point(90, 50)) #tr=p.transform() #tr.setMatrix(tr.m11(), tr.m12(), tr.m13(), tr.m21(), -tr.m22(), tr.m23(), tr.m31(), tr.m32(), tr.m33()) #p.setTransform(tr) f=p.font() f.setPixelSize(10) p.setFont(f) #p.drawText(24, 20, 'beam marker') ctr=tuple(self.pos()+self.size()/2) sz=tuple(self.size()) #print((*ctr,self._z)) #p.drawText(5, 25, 'x{:.4g}\ny{:.4g}\nz{:.4g}'.format(*ctr,self._z)) p.drawText(52, 10,40,40,Qt.TextWordWrap, 'x{:.5g}\ny{:.5g}\nz{:.5g}'.format(*ctr,self._z)) def ctr(self): ctr=tuple(self.pos()+self.size()/2)+(self._z,) return ctr def __repr__(self): ctr=self.ctr() s=f'{self.__class__.__name__}:(ctr:{itr2str(ctr)}, size:{itr2str(self.size())}}}' return s def obj2json(self,encoder): jsn= { '__class__':self.__class__.__name__, 'pos':tuple(self.pos()), 'size':tuple(self.size()), 'z':self._z, } return jsn class Grid(UsrROI): '''a grid''' def __init__( self, pos=(0,0), size=(30,20), cnt=(6,4), fiducialSize=.2, **kargs): pg.ROI.__init__(self, pos, size, **kargs) self._cnt=cnt self._fidSz=fiducialSize self.addScaleHandle([1, 1], [0, 0]) self.addScaleHandle([0, 0], [1, 1]) self.addScaleRotateHandle([1, 0], [0, 0]) def paint(self, p, *args): #pg.ROI.paint(self, p, *args) sz=self.state['size'] nx, ny=self._cnt px, py=sz[0]/(nx-1), sz[1]/(ny-1) r=QtCore.QRectF(0, 0, sz[0], sz[1]).normalized() p.setRenderHint(QtGui.QPainter.Antialiasing) p.setPen(self.currentPen) for i in range(nx): x=i*px p.drawLine(pg.Point(x, 0), pg.Point(x, sz[1])) for i in range(ny): y=i*py p.drawLine(pg.Point(0, y), pg.Point(sz[0] ,y )) fidSz=self._fidSz rx=ry=fidSz/2 p.setPen(pg.mkPen(width=1, color=(255, 0, 0))) for j in range(ny): y=j*py for i in range(nx): x=i*px p.drawEllipse(QPointF(x,y), rx, ry) #p.translate(r.left(), r.top()) #p.scale(r.width(), r.height()) #p.drawEllipse(0, 0, 1, 1) #p.drawRect(0, 0, 1, 1) def __repr__(self): s=f'{self.__class__.__name__}:(pos:{itr2str(self.pos())}, size:{itr2str(self.size())}, cnt:{self._cnt}, fidSize:{self._fidSz}}}' return s def obj2json(self,encoder): jsn= { '__class__':self.__class__.__name__, 'pos':tuple(self.pos()), 'size':tuple(self.size()), 'cnt':self._cnt, 'fiducialSize':self._fidSz } return jsn def get_scan_param(self,mode=0x2): 'returns scan parameters for scanning with deltatau. the format is as used for shapepath' #pos=np.array(self.pos()) cnt=np.array(self._cnt,np.int32) sz=np.array(self.size()) pitch=sz/cnt xx, yy=np.meshgrid(range(cnt[0]), range(cnt[1])) if mode&0x01: #modify x scaning forward backward each line for i in range(1,cnt[1],2): xx[i]=xx[i][::-1] if mode&0x02: # modify y scaning forward backward each line xx=xx.T yy=yy.T for i in range(1, cnt[0], 2): yy[i]=yy[i][::-1] pts=np.array([xx.reshape(-1), yy.reshape(-1)], dtype=np.float).transpose()*pitch #pts+=pos return pts class Path(UsrROI): ''' a path object with fiducials path=np.array(n,2) of path points ficucialScale=size for fiducials in dimensions of path fiducial = np.array(n,2) of fiducial points a circle is plot at the path positions a cross is plot at the fiducial positions ''' def __init__( self, pos=(0,0), size=(30,20), path=np.array(((6,4),(16,24),(-5,7),(3,12))), fiducial=None, ficucialScale=5, **kwargs): self.code_gen=kwargs.pop('code_gen',0) trf=kwargs.pop('trf',None) pg.ROI.__init__(self, pos, size, **kwargs) self.szOrig=size if type(path)==list: path=np.array(path) if fiducial is None: all=path else: if type(fiducial)==list: fiducial=np.array(fiducial) if trf is not None: t=self.transform() t.setMatrix(trf[0][0], trf[0][1], 0, trf[1][0], trf[1][1], 0, 0, 0, 1) self.setTransform(t) self._fiducial=fiducial self._path=path self._fidScl=ficucialScale self._rect=r=QtCore.QRectF(0, 0, size[0], size[1]) self._qpath=qPth=QPolygonF() for pt in path: qPth.append(QPointF(*pt)) self.addFreeHandle([.1, .1]) self.addScaleHandle([1, 1], [0, 0]) self.addScaleHandle([0, 0], [1, 1]) self.addScaleRotateHandle([1, 0], [0, 0]) def paint(self, p, *args): #pg.ROI.paint(self, p, *args) pos=self.state['pos'] sz=self.state['size'] r=self._rect p.setRenderHint(QtGui.QPainter.Antialiasing) p.setPen(self.currentPen) #p.drawEllipse(0, 0, int(sz[0]), int(sz[1])) #p.drawRect(0, 0, int(sz[0]), int(sz[1])) p.drawRect(QtCore.QRectF(0, 0, sz[0], sz[1]).normalized()) #obj_info(p) p.scale(sz[0]/r.width(), sz[1]/r.height()) p.translate(-r.left(), -r.top()) qpath=self._qpath p.drawPolyline(qpath) fidScl=self._fidScl rx=fidScl*r.width()/sz[0] ry=fidScl*r.height()/sz[1] p.setPen(pg.mkPen(width=1, color=(255, 0, 0))) for ctr in qpath: p.drawEllipse(ctr, rx, ry) fid=self._fiducial for i in range(fid.shape[0]): x,y=fid[i,:] #;print(x,y) lh=QLineF(x-5*rx,y,x+5*rx,y) lv=QLineF(x, y-5*ry, x, y+5*ry) p.drawLines(lh,lv) def __repr__(self): s=f'{self.__class__.__name__}:(pos:{itr2str(self.pos())}, size:{itr2str(self.size())}, numFid:{self._fiducial.shape[0]}, numPts:{self._path.shape[0]}, ficucialScale:{self._fidScl}}}' return s def obj2json(self,encoder): jsn= { '__class__':self.__class__.__name__, 'pos':tuple(self.pos()), 'size':tuple(self.szOrig), 'code_gen':self.code_gen, 'fiducial': self._fiducial.tolist(), 'ficucialScale':self._fidScl, 'path':self._path.tolist(), } trf=self.transform() if not trf.isIdentity(): #obj_info(trf) trf=((trf.m11(),trf.m12()),(trf.m21(),trf.m22())) jsn['trf']=trf return jsn def get_scan_param(self,mode=0x2): 'returns scan parameters for scanning with deltatau. the format is as used for shapepath' #mode=pvt #param={'points':pts, 'mode':mode} param={'points':self._path, 'code_gen': self.code_gen} t=self.transform() p=np.array(self.pos()) s=self.size()/self.szOrig trf=np.array(((t.m11(),t.m12()),(t.m21(),t.m22()),(0,0))) trf[2,:]=p # shift origin #trf[:2, 0]*=s[0];trf[:2, 1]*=s[1] #scaling (before rotation shear) trf[:2,:]=(trf[:2,:].T*s).T # same as np.asmatrix(np.diag(s))*trf[:2,:], trf[:2,:]*=s not working, scale before rot / shear # trf*'gridpos in um' -> motor pos in mm param['trf']=trf return param class FixTargetFrame(UsrROI): '''fixed target frame''' tpl={ 'test':{ 'size':(120*15, 120*11), 'fiducial':{ 'type':0, 'pos':((120*2, 120*2), (120*13, 120*2), (120*2, 120*9), (120*13, 120*9)) }, 'grid':{ 'pos':(120*4, 120*3), 'pitch':(120, 120), 'count':(8, 6) } }, '12.5x12.5':{ 'size':(12500+120*4, 12500+120*4), 'fiducial':{ 'type':0, 'pos':((240, 240), (240+12500, 240), (240, 240+12500), (240+12500, 240+12500)) }, 'grid':{ # 240+(12500-78*120)/2=1810 'pos':(1810, 1810), 'pitch':(120, 120), 'count':(78, 78) #(84,84)... but sone covered } }, '23.0x23.0':{ 'size':(23000+120*4, 23000+120*4), 'fiducial':{ 'type':0, 'pos':((240, 240), (240+23000, 240), (240, 240+23000), (240+23000, 240+23000)) }, 'grid':{ # 240+(23000-162*120)/2+60?=2020+60? 'pos':(2080, 2080), 'pitch':(120, 120), 'count':(162, 162) #(172,172)... but sone covered } } } def __init__( self, pos=(0,0), size=(100,100), tpl='test', dscr=None, **kwargs): self.code_gen=kwargs.pop('code_gen',0) trf=kwargs.pop('trf',None) assert(kwargs==dict()) pg.ROI.__init__(self, pos, size)#, **kwargs) if trf is not None: t=self.transform() t.setMatrix(trf[0][0], trf[0][1], 0, trf[1][0], trf[1][1], 0, 0, 0, 1) self.setTransform(t) #fiducial type 0: 5 squares with pitch 120 um if dscr is not None: self._dscr=dscr else: self._dscr=FixTargetFrame.tpl[tpl] self.addScaleHandle([1, 1], [0, 0]) self.addScaleHandle([0, 0], [1, 1]) self.addScaleRotateHandle([1, 0], [0, 0]) #self.sigHoverEvent.connect(self.hover) # def hover(self): # _log.debug(f'hover {self}') def paint(self, p, *args): #pg.ROI.paint(self, p, *args) sz=self.state['size'] #nx, ny=self._cnt #px, py=sz[0]/(nx-1), sz[1]/(ny-1) r=QtCore.QRectF(0, 0, sz[0], sz[1]).normalized() p.setRenderHint(QtGui.QPainter.Antialiasing) p.setPen(self.currentPen) #p.drawRect(0, 0, int(sz[0]), int(sz[1])) p.drawRect(r) dscr=self._dscr objSz=dscr['size'] p.translate(r.left(), r.top()) p.scale(r.width()/objSz[0], r.height()/objSz[1]) # -> values x,y 0 to 13000 dscr=self._dscr g=dscr['grid'] ox,oy=g['pos'] px,py=g['pitch'] nx,ny=g['count'] #x0=ox; x1=ox+(ny-1)*py #y0=oy; y1=oy+(nx-1)*px x0=ox-.5*px; x1=ox+(nx-.5)*px y0=oy-.5*py; y1=oy+(ny-.5)*py for i in range(nx): x=ox+i*px p.drawLine(pg.Point(x, y0), pg.Point(x, y1)) for i in range(ny): y=oy+i*py p.drawLine(pg.Point(x0, y), pg.Point(x1 ,y )) f=dscr['fiducial'] rx=50 ry=50 p.setPen(pg.mkPen(width=1, color=(255, 0, 0))) if f['type']==0: for p0 in f['pos']: p0=np.array(p0) for p1 in ((-120,-120),(120,-120),(0,0),(-120,120),(120,120),): p1=np.array(p1) x, y=p0+p1 #;print(x,y) lh=QLineF(x-rx, y, x+rx, y) lv=QLineF(x, y-ry, x, y+ry) p.drawLines(lh, lv) #plot center of all feducials ctr=np.array(f['pos']).mean(axis=0) x, y=ctr # ;print(x,y) lh=QLineF(x-rx, y, x+rx, y) lv=QLineF(x, y-ry, x, y+ry) p.drawLines(lh, lv) p.drawEllipse(x-rx/2, y-ry/2, rx, ry) else: assert('unknown feducial type') def __repr__(self): s=f'{self.__class__.__name__}:(pos:{itr2str(self.pos())}, size:{itr2str(self.size())}, dscr:{self._dscr}}}' return s def obj2json(self,encoder): jsn= { '__class__':self.__class__.__name__, 'pos':tuple(self.pos()), 'size':tuple(self.size()), 'code_gen':self.code_gen, 'dscr': self._dscr } trf=self.transform() if not trf.isIdentity(): #obj_info(trf) trf=((trf.m11(),trf.m12()),(trf.m21(),trf.m22())) jsn['trf']=trf return jsn def get_scan_param(self): 'returns scan parameters for scanning with deltatau. the format is as used for shapepath' scan=0x2 #default fast y axis grid=self._dscr['grid'] self._dscr['size'] #pos=np.array(self.pos()) cnt =np.array(grid['count'],np.int32) #pos =np.array(grid['pos'],np.float) #pitch=np.array(grid['pitch'],np.float) xx, yy=np.meshgrid(range(cnt[0]), range(cnt[1])) if scan&0x01: #modify x scaning forward backward each line for i in range(1,cnt[1],2): xx[i]=xx[i][::-1] if scan&0x02: # modify y scaning forward backward each line xx=xx.T yy=yy.T for i in range(1, cnt[0], 2): yy[i]=yy[i][::-1] pts=np.array([xx.reshape(-1), yy.reshape(-1)], dtype=np.float).transpose() #*pitch param={'grid':grid, 'points':pts, 'code_gen': self.code_gen} # TODO: simplify !!! t=self.transform() p=np.array(self.pos()) s=self.size()/self._dscr['size'] trf=np.array(((t.m11(),t.m12()),(t.m21(),t.m22()),(0,0))) trf[2,:]=p # shift origin trf[:2,:]=(trf[:2,:].T*s).T # same as np.asmatrix(np.diag(s))*trf[:2,:], trf[:2,:]*=s not working, scale before rot / shear # trf*'gridpos in um' -> motor pos in mm pos=np.array(param['grid']['pos']) # in um pitch=np.array(param['grid']['pitch']) # in um trf2=np.asmatrix(np.identity(3)) trf2[:, :2]=trf trf2*=np.asmatrix(((-1000, 0, 0), (0, 1000, 0), (0, 0, 1))) trf3=np.asmatrix(((pitch[0], 0, 0), (0, pitch[1], 0), (pos[0], pos[1], 1))) trf=(trf3*trf2)[:, :2] param['trf']=trf return param ## Start Qt event loop unless running in interactive mode or using pyside. if __name__=='__main__': def set_fiducial(pic): # fiducial test f=np.array(((0, 0, 0, 0, 0), (0, 1, 1, 1, 0), (0, 1, 0, 0, 0), (0, 1, 1, 0, 0), (0, 1, 0, 0, 0), (0, 0, 0, 0, 0),), pic.dtype) pic[0:6, 0:5]=f*pic.max() # TODO: pg.ItemGroup does not support bounding box and therefore vb.autoRange() does not work class ItemGroup(pg.ItemGroup): # own item group that supports bounding rect def __init__(self, *args, **kargs): pg.ItemGroup.__init__(self, *args, **kargs) def boundingRect(self): # tr=self.transform() boRects=[] for item in self.childItems(): boRects.append(item.boundingRect()) if boRects: # r=QtCore.QRectF(tr.map(boRects[0].bottomRight()),tr.map(boRects[0].topLeft())) return boRects[0] else: return pg.ItemGroup.boundingRect(self) def addItem(self, *args, **kargs): self.setFlag(self.ItemHasNoContents, False) pg.ItemGroup.addItem(self, *args, **kargs) class TxtROI(pg.ROI): def __init__(self, pos, size, **kargs): pg.ROI.__init__(self, pos, size, **kargs) def paint(self, p, *args): # pg.ROI.paint(self, p, *args) r=QtCore.QRectF(0, 0, self.state['size'][0], self.state['size'][1]).normalized() p.setRenderHint(QtGui.QPainter.Antialiasing) p.setPen(self.currentPen) # p.translate(r.left(), r.top()) # p.scale(r.width(), r.height()) # p.drawRect(0, 0, 1, 1) p.drawRect(r) tr=p.worldTransform() obj_info(tr) tr.setMatrix(tr.m11(), tr.m12(), tr.m13(), tr.m21(), -tr.m22(), tr.m23(), tr.m31(), tr.m32(), tr.m33()) p.setWorldTransform(tr) obj_info(tr) obj_info(p.transform()) obj_info(p.worldTransform()) f=p.font(); f.setPixelSize(15) p.setFont(f) p.drawText(0, 5, 'Thierry') import sys import argparse #(h, t)=os.path.split(sys.argv[0]);cmd='\n '+(t if len(h)>20 else sys.argv[0])+' ' #exampleCmd=('', '-m0xf -v0') 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 default=0x%(default)x", default=0) args = parser.parse_args() pg.setConfigOptions(imageAxisOrder='row-major') def pt2str(p): return (f'({p.x():.5g},{p.y():.5g})') def childTree(obj, lvl=0): print('+'*lvl+str(obj)) for child in obj.childItems(): childTree(child, lvl+1) def gen_swissmx_points(flipx=False,flipy=False,ofs=(-200,0),width=1000): 'generathe a path that writes swissfel' #string from inkscape path of the drawing d="m 524.7061,637.31536 3.54883,0 3.54882,0 3.54883,0 0,-4.20801 0,-4.20801 0,-4.208 0,-4.20801 4.22949,0 4.22949,0 4.2295,0 4.22949,0 0,-3.55957 0,-3.55957 0,-3.55957 0,-3.55957 -4.22949,0 -4.2295,0 -4.22949,0 -4.22949,0 0,-4.22949 0,-4.2295 0,-4.22949 0,-4.22949 -3.54883,0 -3.54882,0 -3.54883,0 -3.54883,0 0,4.22949 0,4.22949 0,4.2295 0,4.22949 -4.20752,0 -4.20752,0 -4.20752,0 -4.20752,0 0,3.55957 0,3.55957 0,3.55957 0,3.55957 4.20752,0 4.20752,0 4.20752,0 4.20752,0 0,4.20801 0,4.208 0,4.20801 0,4.20801 -11.87126,0.36152 -12.12171,-0.13934 -2.52941,3.93977 -2.57238,3.94369 -2.50854,3.88614 -2.50731,3.91767 -2.49035,3.88268 -2.50987,3.91244 -2.50453,3.88732 -2.51897,3.9189 -6.39782,5.72802 -6.63782,6.70894 -3.21517,5.11464 -3.3404,5.32333 -3.08995,5.11464 -3.17343,5.15637 -16.69223,0.0698 5.55908,0 5.55909,0 5.55908,0 3.18604,-5.17432 3.18603,-5.17431 3.18604,-5.17432 3.18603,-5.17431 3.17481,5.17431 3.1748,5.17432 3.17481,5.17431 3.1748,5.17432 5.59229,0 5.59228,0 5.59229,0 5.59228,0 -2.74121,-4.15283 -2.74121,-4.15283 -2.74121,-4.15283 -2.74121,-4.15284 -2.74122,-4.15283 -2.74121,-4.15283 -2.74121,-4.15283 -2.74121,-4.15283 2.50488,-3.90015 2.50489,-3.90015 2.50488,-3.90014 2.50488,-3.90015 2.50488,-3.90015 2.50489,-3.90015 2.50488,-3.90014 2.50488,-3.90015 -5.42724,0 -5.42725,0 -5.42724,0 -5.42725,0 -2.76855,4.95508 -2.76856,4.95508 -2.76855,4.95508 -2.76856,4.95508 -2.85644,-4.95508 -2.85645,-4.95508 -2.85644,-4.95508 -2.85645,-4.95508 -5.48193,0 -5.48194,0 -5.48194,0 -5.48193,0 2.52686,3.8562 2.52685,3.8562 2.52686,3.8562 2.52686,3.85621 2.52685,3.8562 2.52686,3.8562 2.52685,3.8562 2.52686,3.8562 -2.77954,4.19678 -2.77954,4.19678 -2.77954,4.19677 -2.77955,4.19678 -2.77954,4.19678 -2.77954,4.19678 -2.77954,4.19677 -2.77954,4.19678 -4.91638,0 -4.91638,0 -4.91639,0 -4.91638,0 -4.91638,0 -4.91638,0 -4.91638,0 -4.91638,0 -4.91639,0 -4.91638,0 -4.91638,0 -4.91638,0 -4.91638,0 -4.91639,0 -4.91638,0 -4.91638,0 4.07568,0 4.07568,0 4.07569,0 4.07568,0 0,-6.14136 0,-6.14135 0,-6.14136 0,-6.14136 0,-6.14136 0,-6.14135 0,-6.14136 0,-6.14136 1.57105,6.14136 1.57104,6.14136 1.57104,6.14135 1.57105,6.14136 1.57105,6.14136 1.57104,6.14136 1.57104,6.14135 1.57105,6.14136 3.68066,0 3.68067,0 3.68067,0 3.68066,0 1.57642,-6.14136 1.57641,-6.14135 1.57642,-6.14136 1.57641,-6.14136 1.57642,-6.14136 1.57642,-6.14135 1.57641,-6.14136 1.57642,-6.14136 0,6.14136 0,6.14136 0,6.14135 0,6.14136 0,6.14136 0,6.14136 0,6.14135 0,6.14136 4.06494,0 4.06494,0 4.06494,0 4.06494,0 0,-8.05298 0,-8.05298 0,-8.05298 0,-8.05297 0,-8.05298 0,-8.05298 0,-8.05298 0,-8.05298 -6.52588,0 -6.52588,0 -6.52587,0 -6.52588,0 -1.25781,4.8999 -1.25782,4.89991 -1.25781,4.8999 -1.25781,4.8999 -1.25781,4.8999 -1.25782,4.89991 -1.25781,4.8999 -1.25781,4.8999 -1.26343,-4.8999 -1.26343,-4.8999 -1.26343,-4.89991 -1.26343,-4.8999 -1.26342,-4.8999 -1.26343,-4.8999 -1.26343,-4.89991 -1.26343,-4.8999 -6.54785,0 -6.54785,0 -6.54785,0 -6.54785,0 0,8.05298 0,8.05298 0,8.05298 0,8.05298 0,8.05297 0,8.05298 0,8.05298 -4.25755,8.13646 -8.40743,0.19687 -8.40743,0.19687 -8.40743,0.19687 -8.40743,0.19687 5.93521,0.22812 8.09742,-0.56079 6.18579,-1.6814 4.55883,-2.66919 3.13062,-3.43823 1.84571,-3.87866 0.61523,-3.98853 -0.58179,-3.83373 -1.74634,-3.50416 -2.802,-2.95581 -3.83472,-2.18676 -5.49316,-1.60401 -7.77832,-1.20849 -7.64649,-1.58204 -1.75781,-2.59179 1.36328,-2.59375 4.4375,-1.09766 5.09766,1.40625 2.19727,3.29492 4.24072,-0.41748 4.24073,-0.41748 4.24072,-0.41748 4.24072,-0.41748 -1.98804,-4.09741 -2.44946,-3.15259 -2.97778,-2.3291 -3.65894,-1.62598 -5.05371,-0.95629 -7.25098,-0.3191 -7.10766,0.41748 -5.50367,1.25244 -4.19677,2.05494 -3.18604,2.91186 -2.01099,3.65796 -0.67065,4.29517 0.61523,3.98852 1.84571,3.5271 2.78002,2.823 3.32935,1.87817 5.06421,1.42822 7.89868,1.56006 7.69141,1.84571 2.02148,2.98828 -1.53906,2.85742 -5.58008,1.53711 -5.27344,-1.36133 -3.07617,-4.52734 -4.43847,0.41748 -4.43848,0.41748 -4.43848,0.41748 -4.43847,0.41748 2.50488,5.95459 4.43848,4.4165 3.18313,1.59592 4.10031,1.14017 -3.65979,0.0939 -5.9713,6e-5 -5.97131,5e-5 -5.9713,6e-5 -5.9713,6e-5 -5.9713,5e-5 -5.97131,6e-5 -5.9713,5e-5 -5.9713,6e-5 5.34491,0.81842 8.09742,-0.56079 6.18579,-1.6814 4.55883,-2.66919 3.13062,-3.43823 1.84571,-3.87866 0.61523,-3.98853 -0.58179,-3.83373 -1.74634,-3.50416 -2.802,-2.95581 -3.83472,-2.18676 -5.49316,-1.60401 -7.77832,-1.20849 -7.64649,-1.58204 -1.75781,-2.59179 1.36328,-2.59375 4.4375,-1.09766 5.09766,1.40625 2.19727,3.29492 4.24072,-0.41748 4.24073,-0.41748 4.24072,-0.41748 4.24072,-0.41748 -1.98804,-4.09741 -2.44946,-3.15259 -2.97778,-2.3291 -3.65894,-1.62598 -5.05371,-0.95629 -7.25098,-0.3191 -7.10766,0.41748 -5.50367,1.25244 -4.19677,2.05494 -3.18604,2.91186 -2.01099,3.65796 -0.67065,4.29517 0.61523,3.98852 1.84571,3.5271 2.78002,2.823 3.32935,1.87817 5.06421,1.42822 7.89868,1.56006 7.69141,1.84571 2.02148,2.98828 -1.53906,2.85742 -5.58008,1.53711 -5.27344,-1.36133 -3.07617,-4.52734 -4.43847,0.41748 -4.43848,0.41748 -4.43848,0.41748 -4.43847,0.41748 2.50488,5.95459 4.43848,4.4165 3.18313,1.59592 4.10031,1.14017 -3.06953,-0.0416 -3.06952,-0.0416 -8.58102,-0.0261 -10.12782,-0.0261 -7.03422,-0.0261 -8.58102,-0.0261 4.47168,0 6.6151,0 2.32826,0 4.47168,0 0,-5.83374 0,-5.83374 0,-5.83374 0,-5.83374 0,-5.83374 0,-5.83374 0,-5.83374 0,-5.83374 -4.47168,0 -4.47168,0 -4.47168,0 0,-5.5796 4.47168,0 4.47168,0 4.47168,0 0,-6.08691 0,-6.08692 -4.47168,0 -4.47168,0 -4.47168,0 -4.47168,0 0,6.08692 0,6.08691 0,5.5796 0,5.83374 0,5.83374 0,5.83374 0,5.83374 0,5.83374 0,5.83374 0,5.83374 -3.67318,5.83374 -8.7308,0 -10.73079,0 -6.7308,0 -9.10563,0 -2.25201,0.007 -8.72971,0.0266 -7.53755,-0.0442 -9.68477,0.0107 -6.3443,0 3.99902,0 3.99902,0 3.99903,0 3.99902,0 2.28516,-7.02002 2.28516,-7.02002 2.28516,-7.02002 2.28516,-7.02002 2.36181,7.02002 2.36182,7.02002 2.36181,7.02002 2.36182,7.02002 3.97705,0 3.97705,0 3.97705,0 3.97705,0 2.14795,-5.83374 2.14795,-5.83374 2.14795,-5.83374 2.14795,-5.83374 2.14795,-5.83374 2.14795,-5.83374 2.14795,-5.83374 2.14795,-5.83374 -4.2959,0 -4.2959,0 -4.2959,0 -4.2959,0 -0.93921,3.67505 -0.93921,3.67505 -0.93921,3.67505 -0.93921,3.67505 -0.9392,3.67504 -0.93921,3.67505 -0.93921,3.67505 -0.93921,3.67505 -1.23047,-3.67505 -1.23047,-3.67505 -1.23047,-3.67505 -1.23047,-3.67504 -1.23046,-3.67505 -1.23047,-3.67505 -1.23047,-3.67505 -1.23047,-3.67505 -4.03223,0 -4.03222,0 -4.03223,0 -4.03223,0 -1.18652,3.67505 -1.18653,3.67505 -1.18652,3.67505 -1.18653,3.67505 -1.18652,3.67504 -1.18652,3.67505 -1.18653,3.67505 -1.18652,3.67505 -0.93921,-3.67505 -0.93921,-3.67505 -0.93921,-3.67505 -0.93921,-3.67504 -0.9392,-3.67505 -0.93921,-3.67505 -0.93921,-3.67505 -0.93921,-3.67505 -4.32862,0 -4.32861,0 -4.32862,0 -4.32861,0 2.16431,5.83374 2.1643,5.83374 2.16431,5.83374 2.16431,5.83374 2.16431,5.83374 2.1643,5.83374 2.16431,5.83374 -3.84635,5.83374 -5.60781,0.003 -5.6078,0.003 -5.60781,0.003 -5.6078,0.003 -5.4839,-1.59358 0,0 5.47119,-3.35034 4.10888,-4.60278 2.5708,-5.4712 0.85694,-5.95459 -0.64868,-5.02123 -1.94507,-4.51587 -3.32837,-3.91114 -4.88843,-3.20801 -7.482173,-2.87842 -5.1337,-1.42273 -6.06186,-1.41174 -6.67969,-2.37304 -1.44922,-2.76758 1.75782,-3.56055 5.22851,-1.49414 6.5918,1.97852 1.99951,2.5708 1.16455,3.75732 4.69141,-0.2749 4.691403,-0.2749 4.6914,-0.27491 4.69141,-0.2749 -0.94483,-4.66918 -1.604,-3.98804 -2.26318,-3.30688 -2.92236,-2.62574 -3.59802,-2.01858 -4.334103,-1.44162 -5.0702,-0.86484 -5.80627,-0.28824 -4.76547,0.1593 -4.23282,0.47791 -6.86695,1.91162 -5.04223,2.98828 -3.61401,3.95507 -2.14283,4.53687 -0.7146,4.82251 1.40625,6.88892 4.21875,5.54858 3.26035,2.31812 4.19986,2.07641 5.13919,1.83472 6.07834,1.59302 6.54785,1.81226 3.64746,1.92211 2.19727,4.48242 -2.33008,4.65821 -6.54688,1.97851 -5.05371,-0.97827 -3.73535,-2.93384 -1.57153,-2.9663 -0.93433,-4.06495 -4.73486,0.29688 -4.73487,0.29687 -4.73486,0.29688 -4.73486,0.29687 0.76065,4.6637 1.44711,4.23523 2.13376,3.80676 2.82059,3.3783 3.79577,2.76855 5.0592,1.97754 6.32264,1.18652 7.58606,0.39551 9.481626,-0.95145 -7.224723,-0.043 -7.224724,-0.043 -7.224723,-0.043 -7.224723,-0.043 -7.224723,-0.043 -7.224723,-0.043 -7.224724,-0.043 -7.224723,-0.043" d=d.split() pts=np.ndarray((len(d)-1,2),dtype=np.float) for i in range(pts.shape[0]): pts[i,:]=tuple(map(float,d[i+1].split(','))) pts[0,:]=(0,0) pts=pts.cumsum(0) pts=pts[::-1,:] pts=pts-pts[0] pts*=width/pts[:,0].max() if flipx: pts[:,0]=-pts[:,0] if not flipy: pts[:,1]=-pts[:,1] pts+=ofs return pts def mouse_click_event(event): #event.pos(): return Point(self.currentItem.mapFromScene(self._scenePos)) pos=event.pos() scn=event.scenePos() # there is a small black border, that makes the difference print(f'mouse-> scene pos:{pt2str(scn)} currentItem pos:{pt2str(pos)}') #img=viImg.mapFromScene(scn) #roi=viUsrRoi.mapFromScene(scn) #print(f'mouse-> img:{pt2str(img)} roi:{pt2str(roi)}') #childTree(vb) #obj_info(viImg) m=int(event.modifiers()) o=event.currentItem if m&Qt.ShiftModifier: o.setPos((100,200)) o.setSize((200,100)) o.rotate(10) pass elif m&Qt.ControlModifier: tr=o.transform() obj_info(tr) #tr.setMatrix(tr.m11(),tr.m12(),tr.m13()+100,tr.m21(),tr.m22(),tr.m23(),tr.m31(),tr.m32(),tr.m33()) #tr.setMatrix(tr.m11(),tr.m12(),tr.m13(),tr.m21(),tr.m22(),tr.m23(),tr.m31(),tr.m32()+20,tr.m33()) #tr.setMatrix(tr.m11(),tr.m12(),tr.m13(),tr.m21(),-tr.m22(),tr.m23(),tr.m31(),tr.m32(),tr.m33()) tr.setMatrix(-tr.m11(),tr.m12(),tr.m13(),tr.m21(),-tr.m22(),tr.m23(),tr.m31(),tr.m32(),tr.m33()) obj_info(tr) o.setTransform(tr) elif m&Qt.AltModifier: import matplotlib.pyplot as plt import numpy as np x=np.linspace(0.1, 2*np.pi, 41) y=np.exp(np.sin(x)) plt.stem(x, y) plt.show() pass else: obj_info(o) #print(o.state) ## Create image to display arr=np.ones((100, 100), dtype=float) arr[45:55, 45:55]=0 arr[25, :]=5 arr[:, 25]=5 arr[75, :]=5 arr[:, 75]=5 arr[50, :]=10 arr[:, 50]=10 arr+=np.sin(np.linspace(0, 20, 100)).reshape(1, 100) arr+=np.random.normal(size=(100, 100)) set_fiducial(arr) # add an arrow for asymmetry arr[10, :50]=10 arr[9:12, 44:48]=10 arr[8:13, 44:46]=10 ## create GUI app=QtGui.QApplication([]) w=pg.GraphicsLayoutWidget(show=True, size=(1000, 800), border=True) w.setWindowTitle('pyqtgraph example: ROI Examples') vb=w.addViewBox(row=1, col=0, lockAspect=True,invertY=False,enableMenu=False) vb.enableAutoRange(enable=False) try: g=pg.GridItem(pen=(0, 255, 0), textPen=(0, 255, 0)) # green grid and labels except: g=pg.GridItem() vb.addItem(g) viImg=pg.ImageItem(arr, border='y') vb.addItem(viImg) # Custom ROI for selecting an image region #viRoi=pg.ROI([20, -50], [60, 40]) #viRoi=TxtROI([20, -50], [60, 40]) #viRoi.addScaleHandle([1, 1], [0, 0]) #viRoi.addScaleHandle([.7, .5], [0, 0]) #vb.addItem(viRoi) viUsrRoi=Marker([50, 120], [30, 20],mode=0) vb.addItem(viUsrRoi) #obj=Marker([250, 220], [30, 20],mode=1) #vb.addItem(obj) vi=Grid( (120,-100), (200,150), (30,20),2) tr=QtGui.QTransform() # prepare ImageItem transformation: tr.setMatrix(1, -.1, 0, .2, 1, 0, 10, 10, 1) vi.setTransform(tr) # assign transform #vi=Grid( (50,10), (200,150), (6,4)) vb.addItem(vi) #vi= visual item grp=pg.ItemGroup() vb.addItem(grp) tr.setMatrix(-1, -.1, 0, .2, -1, 0, 10, 10, 1) grp.setTransform(tr) # assign transform obj=Marker([20, 40], [30, 20],mode=1) grp.addItem(obj) obj=Marker([20, 40], [30, 20],mode=1) vb.addItem(obj) fidScl=.5 fiducial=np.array(((18, 7), (25, 16), (70, 20))) path=gen_swissmx_points(ofs=(10, 5), width=200) vi=Path((120,100),path,fiducial,fidScl) #tr=QtGui.QTransform() # prepare ImageItem transformation: #tr.setMatrix(1, 0, 0, # 0, 1, 0, # 10, 10, 1) #vi.setTransform(tr) # assign transform vb.addItem(vi) vi=FixTargetFrame((100,300),(100,100),tpl='test') vb.addItem(vi) vi=FixTargetFrame((400,-200),(400,400),tpl='12.5x12.5') vb.addItem(vi) vi=Fiducial((0,200),(40,40),3,movable=False,removable=True) vb.addItem(vi) vi=pg.PolyLineROI([(22, -19), (40, -30), (23, -10), (22, -19)], closed=True) vb.addItem(vi) viRoi=pg.ROI([-200, -200], [100, 80],movable=True, rotatable=True, resizable=True) viRoi.addFreeHandle(pos=[.7, .5], axes=None, item=None, name=None, index=None) # rechteck , frei beweglich ??? verschwinden anch bewegung #viRoi.addRotateFreeHandle([.7, .5], [0, 0], axes=None, item=None, name=None, index=None) # kreis ??? verschwinden anch erstem gebrauch #viRoi.addRotateHandle([.7, .5], [0, 0], item=None, name=None, index=None) # kreis, nur rot #viRoi.addScaleHandle([.7, .5], [0, 0], axes=None, item=None, name=None, lockAspect=False, index=None) # raute scale x,y #viRoi.addScaleRotateHandle([0, .5], [1, .5], item=None, name=None, index=None) # kreis #viRoi.addTranslateHandle([.7, .5], axes=None, item=None, name=None, index=None) #quadrat vb.addItem(viRoi) #autorange not working, as all rois are in ItemGroup vb.setRange(QtCore.QRectF(-300, -400, 900+300, 500+400)) childTree(vb) w.scene().sigMouseClicked.connect(mouse_click_event) #viImg.sigImageChanged #print(vb.pos()) #vb.setPos(50,50) if (sys.flags.interactive!=1) or not hasattr(QtCore, 'PYQT_VERSION'): QtGui.QApplication.instance().exec_()