#!/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 pyqtgraph as pg from pyqtgraph.Qt import QtCore, QtGui import numpy as np from PyQt5.QtGui import QPolygon,QPolygonF from PyQt5.QtCore import QPointF class BeamMark(pg.ROI): """A crosshair ROI whose position is at the center of the crosshairs. By default, it is scalable, rotatable and translatable.""" def __init__(self, pos=None, size=None, **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.drawEllipse(0, 0, 1, 1) p.drawRect(0, 0, 1, 1) p.setPen(pg.mkPen(width=3, color=[200, 100, 100])) p.drawLine(pg.Point(.5, 0), pg.Point(.5, 1)) p.drawLine(pg.Point( 0,.5), pg.Point( 1,.5)) #w, h = self.getState()["size"] #v1, v2 = -(h * 0.8) / 2, (h * 0.8) / 2 #h1, h2 = -(w * 0.8) / 2, (w * 0.8) / 2 #p.setRenderHint(QtGui.QPainter.Antialiasing) #p.setPen(pg.mkPen(width=3, color=[200, 100, 100])) #p.drawLine(pg.Point(0, v1), pg.Point(0, v2)) #p.drawLine(pg.Point(h1, 0), pg.Point(h2, 0)) #p.setPen(self.currentPen) ##p.setPen(pg.mkPen(width=3, color=[200, 200, 100])) #p.drawRect(-w / 2, -h / 2, w, h) ## p.drawText(-w, -h, '{:.0f}x{:.0f}'.format(*self._size)) class Grid(pg.ROI): '''a grid''' def __init__( self, pos=(0,0), size=(30,20), cnt=(6,4), **kargs): pg.ROI.__init__(self, pos, size, **kargs) self._cnt=cnt 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 )) #p.translate(r.left(), r.top()) #p.scale(r.width(), r.height()) #p.drawEllipse(0, 0, 1, 1) #p.drawRect(0, 0, 1, 1) class Path(pg.ROI): '''a grid''' def __init__( self, pos=(0,0), mark=np.array(((3,4),)), path=np.array(((6,4),(16,24),(-5,7),(3,12))), **kargs): all=np.vstack((mark,path)) mn=all.min(0) mx=all.max(0) size=mx-mn pg.ROI.__init__(self, pos, size, **kargs) self._mark=mark self._path=path 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'] mark=self._mark path=self._path p.setRenderHint(QtGui.QPainter.Antialiasing) p.setPen(self.currentPen) #p.translate(pos[0], pos[1]) p.drawEllipse(0, 0, int(sz[0]), int(sz[1])) p.drawRect(0, 0, int(sz[0]), int(sz[1])) qPlg=QPolygonF() for pt in path: qPlg.append(QPointF(*pt)) #pp =QPolygonF(path) p.drawPolyline(qPlg) ## Start Qt event loop unless running in interactive mode or using pyside. if __name__=='__main__': 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 obj_info(obj): pos=obj.pos() scnPos=obj.scenePos() t=obj.transform() obj_info print(f"obj_info:{obj}") print(f"Pos:({pos.x():.6g},{pos.y():.6g})") #in coordinate value on the scene (no change by zooming) print(f"scenePos:({scnPos.x():.6g},{scnPos.y():.6g})") #in pixel on the scene (changes by zooming) print(f"QTransform:{t.m11():8.5g} {t.m12():8.5g} {t.m13():8.5g}") print(f" {t.m21():8.5g} {t.m22():8.5g} {t.m23():8.5g}") print(f" {t.m31():8.5g} {t.m32():8.5g} {t.m33():8.5g}") 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 mouse_click_event(event): pos=event.pos() scn=event.scenePos() # there is a small black border, that makes the difference img=viImg.mapFromScene(scn) roi=viUsrRoi.mapFromScene(scn) print(f'mouse:{pt2str(pos)} {pt2str(scn)} img:{pt2str(img)} roi:{pt2str(roi)}') #childTree(vb) obj_info(viUsrRoi) ## 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)) # 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) 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], [100, 70]) vb.addItem(viRoi) viUsrRoi=BeamMark([10, 20], [30, 20]) vb.addItem(viUsrRoi) vi=Grid( (50,10), (200,150), (30,20)) #vi=Grid( (50,10), (200,150), (6,4)) vb.addItem(vi) #vi= visual item vi=Path() vb.addItem(vi) childTree(vb) w.scene().sigMouseClicked.connect(mouse_click_event) if (sys.flags.interactive!=1) or not hasattr(QtCore, 'PYQT_VERSION'): QtGui.QApplication.instance().exec_()