Files
PBSwissMX/python/MAxyPlot.py
2017-01-19 13:40:04 +01:00

348 lines
12 KiB
Python

#!/usr/bin/env python
#*-----------------------------------------------------------------------*
#| |
#| Copyright (c) 2013 by Paul Scherrer Institute (http://www.psi.ch) |
#| |
#| Author Thierry Zamofing (thierry.zamofing@psi.ch) |
#*-----------------------------------------------------------------------*
'''
implements an image view to show a colored image of a hdf5 dataset.
'''
if __name__ == '__main__':
#Used to guarantee to use at least Wx2.8
import wxversion
wxversion.ensureMinimal('2.8')
import wx
import matplotlib as mpl
if __name__ == '__main__':
mpl.use('WXAgg')
#or mpl.use('WX')
#matplotlib.get_backend()
import wxutils as ut
import os
import numpy as np
from matplotlib.backends.backend_wxagg import FigureCanvasWxAgg as FigureCanvas
import pylab as plt #used for the colormaps
#or from matplotlib.backends.backend_wx import FigureCanvasWx as FigureCanvas
#The source of the DraggableColorbar is from:
#http://www.ster.kuleuven.be/~pieterd/python/html/plotting/interactive_colorbar.html
class MPLCanvasImg(FigureCanvas):
def __init__(self,parent,SetStatusCB=None):
if SetStatusCB:
self.SetStatusCB=SetStatusCB
fig = mpl.figure.Figure()
ax = fig.add_axes([0.075,0.075,0.85,0.85])
FigureCanvas.__init__(self,parent, -1, fig)
self.mpl_connect('motion_notify_event', self.OnMotion)
self.mpl_connect('button_press_event', self.OnBtnPress)
self.mpl_connect('button_release_event', self.OnBtnRelease)
self.mpl_connect('scroll_event', self.OnBtnScroll)
self.mpl_connect('key_press_event',self.OnKeyPress)
#self.mpl_connect('pick_event',self.OnPick) #works but locks the screed if debugging
self.fig=fig
self.ax=ax
def InitChild(self,pts,rec):
fig=self.fig
ax=self.ax
hl=[]
hl+=ax.plot(pts[:,0],pts[:,1],'r.',label='ptsDot') #,picker=5 default value
hl+=ax.plot(pts[:,0],pts[:,1],'y--',label='ptsLine')
ec = mpl.collections.EllipseCollection(1, 1, 0, units='xy', offsets=pts,transOffset=ax.transData,edgecolors='g',facecolors=(1,1,0,0.3))
ax.add_collection(ec)
hl+=ax.plot(rec[:, 5], rec[:, 4], 'b-',label='recDesPos')
hl+=ax.plot(rec[:,2],rec[:,1],'g-',label='recActPos')
ax.xaxis.set_label_text('x-pos um')
ax.yaxis.set_label_text('y-pos um')
fig.obj=self
self.ax=ax
self.hl=hl
def OnPick(self,event):
thisline = event.artist
xdata = thisline.get_xdata()
ydata = thisline.get_ydata()
ind = event.ind
points = tuple(zip(xdata[ind], ydata[ind]))
print('onpick points:', points)
pass
def OnMotion(self,event):
#print event,event.x,event.y,event.inaxes,event.xdata,event.ydata
if event.inaxes==self.ax:
x=int(round(event.xdata))
y=int(round(event.ydata))
s=''
for h in self.ax.get_lines(): # to get also the ellipses: self.ax.get_children()
c=h.contains(event)
if c[0]:
#s = str(h)+str(c[1])
#print "over %s" % s
if type(h)==mpl.lines.Line2D:
lbl=h.get_label()
arr=h.get_data()
idx=c[1]['ind'][0]
s += '%s[%d] = [%.2f, %.2f]'%(lbl,idx,arr[0][idx],arr[1][idx])
else:
s += str(h)+str(c[1])
self.SetStatusCB(self.Parent, 0, (x, y, s))
#try:
# #v=self.img.get_array()[y,x]
# v=0;
#except IndexError as e:
# pass
#else:
#print x,y,v
#if event.button==1:#move top,bottom,both
# pD = event.y - pS
# vD=(vmax-vmin)/(p1-p0)*(pS-event.y)
# colBar.norm.vmin = vmin+vD
# colBar.norm.vmax = vmax+vD
#elif event.button==3:#scale around point
# scale= np.exp((pS-event.y)/100)
# vS=vmin+(vmax-vmin)/(p1-p0)*(pS-p0)
# #print scale,vS
# colBar.norm.vmin = vS-scale*(vS-vmin)
# colBar.norm.vmax = vS-scale*(vS-vmax)
def OnBtnPress(self, event):
"""on button press we will see if the mouse is over us and store some data"""
print dir(event.guiEvent)
return
if event.inaxes == self.colBar.ax:
#if event.guiEvent.LeftDClick()==True:
# print dlg
pt=self.colBar.ax.bbox.get_points()[:,1]
nrm=self.colBar.norm
self.colBarPressed = (nrm.vmin,nrm.vmax,pt[0],pt[1],event.y)
#self.colBarPressed = event.x, event.y
#print self.colBarPressed
#self.OnMouse(event)
pass
def OnBtnRelease(self, event):
"""on release we reset the press data"""
#self.OnMouse(event)
return
try: del self.colBarPressed
except AttributeError: pass
def OnBtnScroll(self, event):
return
#self.OnMouse(event)
colBar=self.colBar
if event.inaxes==colBar.ax:
pt=colBar.ax.bbox.get_points()[:,1]
nrm=colBar.norm
vmin,vmax,p0,p1,pS = (nrm.vmin,nrm.vmax,pt[0],pt[1],event.y)
if isinstance(colBar.norm,mpl.colors.LogNorm):#type(colBar.norm)==mpl.colors.LogNorm does not work...
scale= np.exp((-event.step)/10)
colBar.norm.vmax=vmax*scale
else:#scale around point
scale= np.exp((-event.step)/10)
vS=vmin+(vmax-vmin)/(p1-p0)*(pS-p0)
#print scale,vS
colBar.norm.vmin = vS-scale*(vS-vmin)
colBar.norm.vmax = vS-scale*(vS-vmax)
self.img.set_norm(colBar.norm)#force image to redraw
colBar.patch.figure.canvas.draw()
def OnKeyPress(self, event):
return
colCycle=self.colCycle
colBar=self.colBar
if event.key=='down':
self.colIndex += 1
elif event.key=='up':
self.colIndex -= 1
self.colIndex%=len(colCycle)
cmap = colCycle[self.colIndex]
colBar.set_cmap(cmap)
colBar.draw_all()
self.img.set_cmap(cmap)
self.img.get_axes().set_title(cmap)
colBar.patch.figure.canvas.draw()
def OnMouse(self, event):
return
for k in dir(event):
if k[0]!='_':
print k,getattr(event,k)
class MAxyPlotFrame(wx.Frame):
def __del__(self):
self.doc.view.remove(self)
def __init__(self, parent,doc):
wx.Frame.__init__(self, parent, title='xy-Plot', size=wx.Size(850, 650))
self.doc=doc;doc.view.append(self)
imgDir=ut.Path.GetImage()
icon = wx.Icon(os.path.join(imgDir,'PBMA.ico'), wx.BITMAP_TYPE_ICO)
self.SetIcon(icon)
canvas = MPLCanvasImg(self,self.SetStatusCB)
sizer = wx.BoxSizer(wx.VERTICAL)
sizer.Add(canvas, 1, wx.LEFT | wx.TOP | wx.GROW)
self.SetSizer(sizer)
toolbar=ut.AddToolbar(canvas,sizer)
wxAxCtrlLst=[]
#l=len(data.shape)
#idxXY=(l-2,l-1)
#for idx,l in enumerate(data.shape):
# if idx in idxXY:
# continue
# wxAxCtrl=ut.SliderGroup(self, label='Axis:%d'%idx,range=(0,l-1))
# wxAxCtrl.idx=idx
# wxAxCtrlLst.append(wxAxCtrl)
# sizer.Add(wxAxCtrl.sizer, 0, wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, border=5)
# wxAxCtrl.SetCallback(HdfImageFrame.OnSetView,wxAxCtrl)
#sl=ut.GetSlice(idxXY,data.shape,wxAxCtrlLst)
#wxAxCtrl=ut.SliderGroup(self, label='Axis:%d'%1,range=(0,1000))
#wxAxCtrl.SetCallback(MAxyPlotFrame.OnSetView,wxAxCtrl)
#sizer.Add(wxAxCtrl.sizer, 0, wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, border=5)
canvas.InitChild(doc.fh['pts'],doc.fh['rec'])
#self.Fit()
self.Centre()
self.BuildMenu()
self.canvas=canvas
self.sizer=sizer
self.toolbar=toolbar
#self.data=data
#self.idxXY=idxXY
#self.wxAxCtrlLst=wxAxCtrlLst
def BuildMenu(self):
mnBar = wx.MenuBar()
#-------- Edit Menu --------
mn = wx.Menu()
#mnItem=mn.Append(wx.ID_ANY, 'Setup Colormap', 'Setup the color mapping ');self.Bind(wx.EVT_MENU, self.OnColmapSetup, mnItem)
#mnItem=mn.Append(wx.ID_ANY, 'Invert X-Axis', kind=wx.ITEM_CHECK);self.Bind(wx.EVT_MENU, self.OnInvertAxis, mnItem)
#self.mnIDxAxis=mnItem.GetId()
#mnItem=mn.Append(wx.ID_ANY, 'Invert Y-Axis', kind=wx.ITEM_CHECK);self.Bind(wx.EVT_MENU, self.OnInvertAxis, mnItem)
#mnItem=mn.Append(wx.ID_ANY, 'Show Moments', 'Show image moments ', kind=wx.ITEM_CHECK);self.Bind(wx.EVT_MENU, self.OnShowMoments, mnItem)
#self.mnItemShowMoment=mnItem
#mnItem=mn.Append(wx.ID_ANY, 'Tomo Normalize', 'Multiplies each pixel with a normalization factor. Assumes there exist an array exchange/data_white', kind=wx.ITEM_CHECK);self.Bind(wx.EVT_MENU, self.OnTomoNormalize, mnItem)
#self.mnItemTomoNormalize=mnItem
mnBar.Append(mn, '&Edit')
#mnItem=mn.Append(wx.ID_ANY, 'Animate', 'Animate the motion');self.Bind(wx.EVT_MENU, self.OnAnimate, mnItem)
mn = wx.Menu()
mnItem=mn.Append(wx.ID_ANY, 'Help', 'How to use the image viewer');self.Bind(wx.EVT_MENU, self.OnHelp, mnItem)
mnBar.Append(mn, '&Help')
self.SetMenuBar(mnBar)
self.CreateStatusBar()
def OnUpdate(self, msg, usrData):
# this is the model-view-control update function
#print 'OnUpdate',self, msg, usrData
if msg==0:
canvas=self.canvas
ax=canvas.ax
idx=usrData
rec=self.doc.fh['rec']
hl=canvas.hl
hl[2].set_data(rec[:idx+1, 5], rec[:idx+1, 4])
hl[3].set_data(rec[:idx+1, 2], rec[:idx+1, 1])
#ax.draw_artist(hl[2])
x=ax.get_xlim();x=(x[1]-x[0])/2;
y=ax.get_ylim();y=(y[1]-y[0])/2;
ax.set_xlim(rec[idx, 2]-x,rec[idx, 2]+x)
ax.set_ylim(rec[idx, 1]-y,rec[idx, 1]+y)
canvas.draw()
@staticmethod
def SetStatusCB(obj,mode,v):
if mode==0:
obj.SetStatusText( "x= %d y=%d val=%s"%v,0)
else:
raise KeyError('wrong mode')
def OnHelp(self,event):
msg='''to change the image selection:
use the toolbar at the bottom to pan and zoom the image
use the scrollbars at the bottom (if present) to select an other slice
to change the colorscale:
drag with left mouse button to move the colorbar up and down
drag with right mouse button to zoom in/out the colorbar at a given point
use mouse weel to zoom in/out the colorbar at a given point
double click left mouse button to set maximum and minimun colorbar values
use cursor up and down to use a different colormap'''
dlg = wx.MessageDialog(self, msg, 'Help', wx.OK|wx.ICON_INFORMATION)
dlg.ShowModal()
dlg.Destroy()
if __name__ == '__main__':
import os,sys,argparse #since python 2.7
def GetParser(required=True):
fnHDF='/scratch/detectorData/e14472_00033.hdf5'
#lbl='mcs'
lbl='pilatus_1'
#lbl='spec'
elem='/entry/dataScan00033/'+lbl
exampleCmd='--hdfFile='+fnHDF+' --elem='+elem
parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter,
description=__doc__,
epilog='Example:\n'+os.path.basename(sys.argv[0])+' '+exampleCmd+'\n ')
parser.add_argument('--hdfFile', required=required, default=fnHDF, help='the hdf5 to show')
parser.add_argument('--elem', required=required, default=elem, help='the path to the element in the hdf5 file')
return parser
args = parser.parse_args()
return args
class App(wx.App):
def OnInit(self):
parser=GetParser()
#parser=GetParser(False) # debug with exampleCmd
args = parser.parse_args()
try:
self.fid=fid=h5py.h5f.open(args.hdfFile)
except IOError as e:
sys.stderr.write('Unable to open File: '+args.hdfFile+'\n')
parser.print_usage(sys.stderr)
return True
try:
hid = h5py.h5o.open(fid,args.elem)
except KeyError as e:
sys.stderr.write('Unable to open Object: '+args.elem+'\n')
parser.print_usage(sys.stderr)
return True
frame = HdfImageFrame(None,args.elem,hid)
frame.Show()
self.SetTopWindow(frame)
return True
def OnExit(self):
self.fid.close()
ut.StopWatch.Start()
app = App()
app.MainLoop()