#!/usr/bin/env python # *-----------------------------------------------------------------------* # | | # | Copyright (c) 2016 by Paul Scherrer Institute (http://www.psi.ch) | # | | # | Author Thierry Zamofing (thierry.zamofing@psi.ch) | # *-----------------------------------------------------------------------* from __future__ import print_function import os,sys import wx import wx.py import numpy as np from MAxyPlot import * from MAError import * from MAVelocity import * #from hdfTree import * #from hdfGrid import * #from hdfAttrib import * #from hdfImage import * import wxutils as ut class MADoc(): ''' lenRec=fh['rec'] lenPts=fh['pts'] pts= X,Y array idx 0 1 2 3 4 5 6 OLD Motor[3].ActPos Motor[2].ActPos Motor[1].ActPos Motor[3].DesPos Motor[2].DesPos Motor[1].DesPos Gate3[1].Chan[1].UserFlag NEW Motor[1].ActPos Motor[2].ActPos Motor[1].DesPos Motor[2].DesPos Gate3[1].Chan[1].UserFlag NEW y.ActPos x.ActPos y.DesPos x.DesPos Gate3[1].Chan[1].UserFlag OLD->NEW 0->none 1->1 2->0 3->none 4->3 5->2 Implemented messages 0: The time slider has changed. usrData=index 1: The position slider has changed. usrData=index ''' def __init__(self): self.view=[] #list of object that have view on this document pass def Update(self,skipView,msg,usrData): for v in self.view: if v==skipView: continue v.OnUpdate(msg,usrData) class AboutFrame(wx.Frame): def __init__(self,parent): wx.Frame.__init__(self,parent,-1,'About MotionAnalyzer',size=(300,330)) imgDir=ut.Path.GetImage() icon = wx.Icon(os.path.join(imgDir,'PBMA.ico'), wx.BITMAP_TYPE_ICO) self.SetIcon(icon) self.Centre() panel=wx.Panel(self,-1) #import pkg_resources #v=pkg_resources.get_distribution("h5pyViewer") v='my version info' s='Version:'+str(v)+'\n(c) www.psi.ch\n Author: Thierry Zamofing\n thierry.zamofing@psi.ch' st0=wx.StaticText(panel,-1,s,(30,10)) bmp = wx.StaticBitmap(panel,-1,wx.Bitmap(os.path.join(imgDir,'PBMA.png'), wx.BITMAP_TYPE_ANY ), (30,st0.Position[1]+st0.Size[1]+10)) for k,v in os.environ.iteritems(): print(k,'=',v) class MAMainFrame(wx.Frame): def __init__(self, parent, title,doc): wx.Frame.__init__(self, parent, title=title, size=wx.Size(650, 230),style = wx.DEFAULT_FRAME_STYLE | wx.STAY_ON_TOP) 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) #wxSplt = wx.SplitterWindow(self, -1) #wxTree = HdfTreeCtrl(wxSplt, 1, wx.DefaultPosition, (-1,-1), wx.TR_HAS_BUTTONS) #wxTree.Bind(wx.EVT_TREE_SEL_CHANGED, self.OnSelChanged, id=1) #wxTree.Bind(wx.EVT_TREE_ITEM_MENU, self.OnMenu, id=1) #wx.EVT_TREE_ITEM_MENU(id, func) #wxTxt = wx.StaticText(wxSplt, -1, '',(10,10) )#, style=wx.ALIGN_CENTRE) sizer = wx.BoxSizer(wx.VERTICAL) self.SetSizer(sizer) wxTxt = wx.StaticText(self, wx.ID_ANY, "MyLabel", wx.DefaultPosition, wx.DefaultSize, 0 ) sizer.Add(wxTxt, 1, wx.LEFT | wx.TOP | wx.GROW) wxTimeCtrl=ut.SliderGroup(self, label='DataPoint', range=(0, 1000)) wxTimeCtrl.SetCallback(MAMainFrame.OnSetTime, wxTimeCtrl) sizer.Add(wxTimeCtrl.sizer, 0, wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, border=5) #wxSplt.SplitVertically(wxTree, wxTxt) wxPosCtrl=ut.SliderGroup(self, label='IdxInPos', range=(0, 1000)) wxPosCtrl.SetCallback(MAMainFrame.OnSetIdxInPos, wxPosCtrl) sizer.Add(wxPosCtrl.sizer, 0, wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, border=5) #wxSplt.SplitVertically(wxTree, wxTxt) wxTrigCtrl=ut.SliderGroup(self, label='IdxTrigger', range=(0, 1000)) wxTrigCtrl.SetCallback(MAMainFrame.OnSetIdxTrigger, wxTrigCtrl) sizer.Add(wxTrigCtrl.sizer, 0, wx.EXPAND | wx.ALIGN_CENTER | wx.ALL, border=5) #wxSplt.SplitVertically(wxTree, wxTxt) #wxSplt.SetMinimumPaneSize(320) #wxLstCtrl=HdfAttrListCtrl(wxSplt) #wxSplt.SplitVertically(wxTree, wxLstCtrl) self.BuildMenu() self.Centre() self.wxTimeCtrl=wxTimeCtrl self.wxPosCtrl=wxPosCtrl self.wxTrigCtrl=wxTrigCtrl self.wxTxt=wxTxt #self.wxTree=wxTree #self.display=wxTxt def __del__(self): self.CloseFile() def OpenFile(self,fn_npz): doc=self.doc try: doc.fh=fh=np.load(fn_npz) except IOError as e: sys.stderr.write('Unable to open File: '+fn_npz+'\n') else: pass s='content of numpy file: '+fn_npz+'\n' for k,v in fh.iteritems(): s+=' '+k+': '+str(v.dtype)+' '+str(v.shape)+'\n' self.wxTxt.SetLabel(s) rec=fh['rec'] pts=fh['pts'] lenRec=rec.shape[0] lenPts=pts.shape[0] #idxTrigger=np.hstack(([0],rec[:,4])) #doc.idxTrigger=idxTrigger=np.where(np.diff(idxTrigger)==1)[0] idxTrigger=rec[:,4] doc.idxTrigger=idxTrigger=np.where(np.diff(idxTrigger)==1)[0]+1 idxInPos=[] #first point at idx 0 try:# find approximate distance of 2 points rng =idxTrigger[2]-idxTrigger[1] except IndexError: rng = int(lenRec / lenPts) idx=rng/2 for i in range(lenPts): l=rec[idx:idx+rng,(3,2)]-pts[i,:] l2=l[:,0]**2+l[:,1]**2 try: ofs=l2.argmin() except ValueError: break#print(l2[ofs]) idx+=ofs idxInPos.append(idx) idx+=rng/2 doc.idxInPos=idxInPos=np.array(idxInPos) idx=min(idxInPos.shape[0],idxTrigger.shape[0])-1 self.SetStatusText('scaling of DesTimeBase: %f'%(float(idxInPos[idx])/idxTrigger[idx])) #self.PlotJitter() self.wxTimeCtrl.slider.SetRange(0,lenRec-1) self.wxTimeCtrl.slider.SetPageSize(idxInPos[1]-idxInPos[0]) self.wxPosCtrl.slider.SetRange(0,lenPts-1) self.wxPosCtrl.slider.SetPageSize(10) self.wxTrigCtrl.slider.SetRange(0,idxTrigger.shape[0]-1) self.wxTrigCtrl.slider.SetPageSize(10) def CloseFile(self): #http://docs.wxwidgets.org/2.8/wx_windowdeletionoverview.html#windowdeletionoverview #print 'CloseFile' try: self.fh.close() del self.fh except AttributeError as e: pass def PlotJitter(self): doc=self.doc fh=doc.fh idxInPos=doc.idxInPos idxTrigger=doc.idxTrigger #display the jitter of inPos->trigger ts=fh['meta'].item()['timebase'] jitter=idxTrigger[:idxInPos.shape[0]]-idxInPos import matplotlib.pyplot as plt # used for the colormaps plt.figure('jitter inPos -> trigger: scaling of DesTimeBase: %f'%(float(idxInPos[-1])/idxTrigger[idxInPos.shape[0]-1])) plt.plot(jitter*ts) plt.ylabel('jitter in ms') plt.ylabel('point idx') plt.show() def DispJitter(self,idxInPos,idxTrigger): doc=self.doc ts=doc.fh['meta'].item()['timebase'] self.SetStatusText("Jitter: %.2gms scaling: %.6g"%((idxTrigger-idxInPos)*ts,float(idxInPos)/idxTrigger)) def OnOpen(self, event): dlg = wx.FileDialog(self, "Choose a file", os.getcwd(), '','numpy files (*.npz;*.npy)|*.npz;*.npy|all (*.*)|*.*', wx.OPEN|wx.FD_CHANGE_DIR) if dlg.ShowModal() == wx.ID_OK: path = dlg.GetPath() #mypath = os.path.basename(path) #self.SetStatusText("You selected: %s" % mypath) self.CloseFile() self.OpenFile(path) #print 'OnOpen',path dlg.Destroy() def OnCloseWindow(self, event): #print 'OnCloseWindow' self.Destroy() def OnAbout(self,event): frame=AboutFrame(self) frame.Show() def BuildMenu(self): #http://wiki.wxpython.org/AnotherTutorial#wx.MenuBar mnBar = wx.MenuBar() #-------- File Menu -------- mn = wx.Menu() mnItem=mn.Append(wx.ID_OPEN, '&Open', 'Open a new document');self.Bind(wx.EVT_MENU, self.OnOpen, mnItem) #mnSub = wx.Menu() #mnItem=mnSub.Append(wx.ID_ANY, 'SubMenuEntry', 'My SubMenuEntry') #mn.AppendMenu(wx.ID_ANY, 'SubMenu', mnSub) mn.AppendSeparator() mnItem=mn.Append(wx.ID_EXIT, '&Quit', 'Quit the Application');self.Bind(wx.EVT_MENU, self.OnCloseWindow, mnItem) mnBar.Append(mn, '&File') self.Bind(wx.EVT_CLOSE, self.OnCloseWindow) #-------- Edit Menu -------- mn = wx.Menu() mnItem = mn.Append(wx.ID_ANY, 'Show &XY-Path', 'Show XY-path motion path');self.Bind(wx.EVT_MENU, self.OnShowXYPath, mnItem) mnItem = mn.Append(wx.ID_ANY, 'Show &Error', 'Show error of motion path');self.Bind(wx.EVT_MENU, self.OnShowError, mnItem) mnItem = mn.Append(wx.ID_ANY, 'Show &Velocity', 'Show velocity of motion path');self.Bind(wx.EVT_MENU, self.OnShowVelocity, mnItem) mnItem = mn.Append(wx.ID_ANY, '&Python Shell', 'Opens an interactive python shell"');self.Bind(wx.EVT_MENU, self.OnShell, mnItem) mnBar.Append(mn, '&Window') #-------- Help Menu -------- mn = wx.Menu() #mnItem=mn.Append(wx.ID_HELP,'Help','Application Help') mnItem=mn.Append(wx.ID_ABOUT,'About','Application About');self.Bind(wx.EVT_MENU, self.OnAbout, mnItem) mnBar.Append(mn, '&Help') #mn.AppendSeparator() #mnItem = wx.MenuItem(mn, 105, '&Quit\tCtrl+Q', 'Quit the Application') #mnItem.SetBitmap(wx.Image('stock_exit-16.png', wx.BITMAP_TYPE_PNG).ConvertToBitmap()) #mn.AppendItem(mnItem) self.SetMenuBar(mnBar) self.CreateStatusBar() self.SetStatusText("This is the status message") def OnShowXYPath(self, event): frame=MAxyPlotFrame(self,self.doc) frame.Show(True) def OnShowError(self, event): frame=MAErrorFrame(self,self.doc) frame.Show(True) def OnShowVelocity(self, event): frame=MAVelocityFrame(self,self.doc) frame.Show(True) def OnShell(self, event): frame = wx.Frame(self, -1, "wxPyShell",size=wx.Size(800, 500)) imgDir=Path.GetImage() icon = wx.Icon(os.path.join(imgDir,'PBMA.ico'), wx.BITMAP_TYPE_ICO) frame.SetIcon(icon) frame.Centre() fh=app.GetTopWindow().fh wnd=app.GetTopWindow() loc={'app' :app, 'fh' :fh, 'pts' :fh['pts'], 'rec' :fh['rec'] } introText='''Shell to the HDF5 objects app: application object fh: numpy file pts: desired motion points rec: recorded data #Examples: pts rec #using user defined modules #import userSample as us;reload(us);us.test1(hid) ''' shell=wx.py.shell.Shell(frame, introText=introText,locals=loc) frame.Show(True) #if loc is None, all variables are visible. the context is global #shell.push('wnd=app.GetTopWindow()') #for cmd in [ # 'wnd=app.GetTopWindow();wxTree=wnd.wxTree', # 'wxNode=wnd.wxTree.GetSelection()', # 'print wnd.fid', # 'lbl=wxTree.GetItemText(wxNode)', # 'hid=wxTree.GetPyData(wxNode)']: # shell.run(cmd, prompt=False) def OnUpdate(self,msg,usrData): #this is the model-view-control update function #print self,msg,usrData pass @staticmethod def OnSetTime(usrData, idxRec, msg): 'called when the time slider has been changed' #print('OnSetTime', usrData, value, msg) view=usrData.slider.Parent doc=view.doc idxInPos=np.argmin(abs(doc.idxInPos-idxRec)) view.wxPosCtrl.SetValue(idxInPos) idxTrigger=np.argmin(abs(doc.idxTrigger-idxRec)) view.wxTrigCtrl.SetValue(idxTrigger) view.DispJitter(doc.idxInPos[idxInPos],doc.idxTrigger[idxTrigger]) doc.Update(view,0,idxRec) @staticmethod def OnSetIdxInPos(usrData, idxInPos, msg): 'called when the idxInPos slider has been changed' #print('OnSetPosIdx', usrData, value, msg) view = usrData.slider.Parent doc = view.doc idxRec=doc.idxInPos[idxInPos] view.wxTimeCtrl.SetValue(idxRec) idxTrigger=np.argmin(abs(doc.idxTrigger-idxRec)) view.wxTrigCtrl.SetValue(idxTrigger) view.DispJitter(doc.idxInPos[idxInPos],doc.idxTrigger[idxTrigger]) doc.Update(view, 0, idxRec) @staticmethod def OnSetIdxTrigger(usrData, idxTrigger, msg): 'called when the idxTrigger slider has been changed' #print('OnSetIdxTrigger', usrData, value, msg) view = usrData.slider.Parent doc = view.doc idxRec=doc.idxTrigger[idxTrigger] view.wxTimeCtrl.SetValue(idxRec) idxInPos=np.argmin(abs(doc.idxInPos-idxRec)) view.wxPosCtrl.SetValue(idxInPos) view.DispJitter(doc.idxInPos[idxInPos],doc.idxTrigger[idxTrigger]) doc.Update(view, 0, idxRec) if __name__ == '__main__': def GetArgs(): import sys,argparse #since python 2.7 exampleCmd='/tmp/shapepath.npz' parser = argparse.ArgumentParser(formatter_class=argparse.RawDescriptionHelpFormatter, description=__doc__, epilog='Example:\n'+os.path.basename(sys.argv[0])+' '+exampleCmd+'\n ') parser.add_argument('npzFile', nargs='?', help='the npz file with motion path data to show',default='/tmp/shapepath.npz') args = parser.parse_args() return args class MyApp(wx.App): def OnInit(self): args=GetArgs() doc=MADoc() frame = MAMainFrame(None,'PBMotionAnalyzer',doc) if args.npzFile: frame.OpenFile(args.npzFile) frame.Show(True) f1 = MAxyPlotFrame(frame, doc) f1.Show(True) #f2= MAErrorFrame(frame, doc) #f2.Show(True) #f3= MAVelocityFrame(frame, doc) #f3.Show(True) self.SetTopWindow(frame) return True #------------------ Main Code ---------------------------------- #redirect stdout/stderr: #http://www.blog.pythonlibrary.org/2009/01/01/wxpython-redirecting-stdout-stderr/ #https://groups.google.com/forum/#!topic/wxpython-users/S9uSKIYdYoo #https://17677433047266577941.googlegroups.com/attach/e4d343dc6a751906/REDIRECT.PY?part=2&view=1&vt=ANaJVrFeyCjCMydKnkyfFbYJM7ip07mE-ozUIBxJ5A1QuK1GhycJYJsPTxpAaNk5L2LpXvGhzRPInxDt8_WUcUyK2Ois28Dq8LNebfYoWG9Yxr-tujf5Jk4 #http://www.wxpython.org/docs/api/wx.PyOnDemandOutputWindow-class.html rd=not sys.stdout.isatty()#have a redirect window, if there is no console #rd=True #force to open a redirect window rd=False #avoid a redirect window app = MyApp(redirect=rd) app.MainLoop()