215 lines
6.7 KiB
Python
215 lines
6.7 KiB
Python
#!/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_()
|
|
|