예제 #1
0
	def subPlot(self, s="Plot"):
		bar = {'size':(400,400), 'style':wx.DEFAULT_FRAME_STYLE|wx.NO_FULL_REPAINT_ON_RESIZE, 'title':s}
		f=wx.Frame(self, -1, **bar)
		g=Graph(f, -1)
		f.Show(True)
		g.killframe = f.Destroy
		self.subplot=g
		g.OnAltClick=lambda x:self.subAltClick(x)
		return g
예제 #2
0
	def subPlot(self, s="Plot"):
		bar = {'size':(400,400), 'style':wx.DEFAULT_FRAME_STYLE|wx.NO_FULL_REPAINT_ON_RESIZE, 'title':s}
		f=wx.Frame(self, -1, **bar)
		g=Graph(f, -1)
		f.Show(True)
		g.killframe = f.Destroy
		self.subplot=g
		g.contextMenuContents.append(("SpikeSort: Select Events",self.selectEvents))
		wx.EVT_MIDDLE_UP(g, self.subAltClick)
		g.OnAltClick=lambda x:self.subAltClick(x)
		return g
예제 #3
0
파일: fview.py 프로젝트: gic888/MIEN
	def __init__(self, dv, **kwargs):
		tty=kwargs.get('tty', None) 
		title=kwargs.get('title',"Locus Viewer")
		BaseGui.__init__(self, dv, title=title, menus=["File", "Display"], pycommand=True,height=4, TTY=tty, showframe=False)
		controls=[["File","Select Elements To Show", self.select],
					["Display", "Show All Functions", self.allFuncs],
					["Display", "Select Data", self.select],
					]	
		self.dv = dv	
		if dv:
			self.document = self.dv.document
		self.fillMenus(controls)
		self.mainSizer = wx.BoxSizer(wx.VERTICAL)
		self.main.SetSizer(self.mainSizer)
		self.main.SetAutoLayout(True)
		self.graph=Graph(self.main, -1)		
		self.mainSizer.Add(self.graph, 1, wx.GROW|wx.ALIGN_CENTRE|wx.ALL, 5)
		self.SetSize(wx.Size(800,600))
 		self.stdFileMenu()
		self.data = []
예제 #4
0
파일: fview.py 프로젝트: gic888/MIEN
class LocusViewer(BaseGui):
	def __init__(self, dv, **kwargs):
		tty=kwargs.get('tty', None) 
		title=kwargs.get('title',"Locus Viewer")
		BaseGui.__init__(self, dv, title=title, menus=["File", "Display"], pycommand=True,height=4, TTY=tty, showframe=False)
		controls=[["File","Select Elements To Show", self.select],
					["Display", "Show All Functions", self.allFuncs],
					["Display", "Select Data", self.select],
					]	
		self.dv = dv	
		if dv:
			self.document = self.dv.document
		self.fillMenus(controls)
		self.mainSizer = wx.BoxSizer(wx.VERTICAL)
		self.main.SetSizer(self.mainSizer)
		self.main.SetAutoLayout(True)
		self.graph=Graph(self.main, -1)		
		self.mainSizer.Add(self.graph, 1, wx.GROW|wx.ALIGN_CENTRE|wx.ALL, 5)
		self.SetSize(wx.Size(800,600))
 		self.stdFileMenu()
		self.data = []
		
	def dataImport(self, df, add=False, zoom=False):
		dpath = df.upath()
		if not df.xpath(1)[0] == self.document:
			if add:
				df=df.clone()
				self.document.newElement(df)
		if self.data and not add:
			self.data = []
		self.data.append(df.upath())
		print self.document, self.data
		self.display()
		self.report("Done Loading Data")
		
	
	def select(self, event=None):
		els=[e.upath() for e in self.document.getElements('Data')]
		l = self.askParam([{"Name":"Which Objects",
							"Type":'Select',
							"Value":els}])
		if not l:
			return
		self.data = l[0]
		self.display()
	
	def allFuncs(self, event=None):
		f = self.document.getElements("Data", {"SampleType":"function"})
		self.data = [i.upath() for i in f] 
		self.display()
		
	def display(self):
		#import time;st=time.time()
		self.graph.plots={}
		for p in self.data:
			dat = self.document.getInstance(p)
			dd = dat.getData()
			if dat.stype() in ['ensemble', 'histogram', 'timeseries']:
				for i in range(dd.shape[1]):
					self.graph.addPlot(dd[:,i], sampr=1.0/dat.fs(), style = 'line')
			elif 'events' in dat.stype():
				t = dd[:,0]/float(dat.fs())
				if 'labeled' in dat.stype():
					y = dd[:,1].astype(t.dtype())
				else:
					y = N.zeros_like(t)
				self.graph.addPlot(N.column_stack([t, y]), style = 'points', width = 3)
			else:
				style = dat.attrib("style") or 'points' 
				for i in range(1, dd.shape[1]):
					self.graph.addPlot(dd[:,[0,i]], style = style)
		self.graph.fullScale()
		self.graph.DrawAll()		
		
예제 #5
0
파일: viewer.py 프로젝트: gic888/MIEN
	def __init__(self, master=None,returnData=None ):
		BaseGui.__init__(self, master, title="Image Viewer", menus=["File", "Controls", "Tools"], pycommand=True,height=4)
		
		self._current_display=[-1, 0]
		self._framecache=None
		self.images=[]
		
		controls=[["File", "New Viewer", self.newView],
					 ["File", "profile", self.prof],
					 ["File", "Save Measurements", self.saveMeasure],
				  ["Controls","Marker Locations",self.markInfo],
					["Controls", "Toggle pseudocolor", self.pcolor],
					["Controls", "Set Contrast Range", self.setRange],
					["Controls", "Show Contrast Range", self.getRange],
					["Controls", "Remove Contrast Range", self.remRange],
					["Controls", "Maximize Contrast", self.maxRange],
					["Controls", "Calibrate Measurements", self.calibM],
					["Controls", "Goto Frame", self.showFrame]]	
		
		self.mainSizer = wx.BoxSizer(wx.VERTICAL)
		self.main.SetSizer(self.mainSizer)
		self.main.SetAutoLayout(True)
		
		self.graph=Graph(self.main, -1)
		self.graph.axis["ticks"] = 0
		self.graph.legend = False
		#self.graph.fixAR=1.0
	
		psize = wx.BoxSizer(wx.HORIZONTAL)
		psize.Add(self.graph, 3, wx.GROW|wx.ALIGN_CENTRE|wx.ALL, 5)
		self.imlist = AWList(self.main, -1, style=wx.LC_REPORT
							 | wx.SUNKEN_BORDER | wx.WANTS_CHARS)
		self.imlist.InsertColumn(0, "Images")
		wx.EVT_LEFT_DCLICK(self.imlist, self.OnItemClick)
		psize.Add(self.imlist, 1, wx.GROW|wx.ALIGN_CENTRE|wx.ALL, 5)
		self.mainSizer.Add(psize, 10, wx.GROW|wx.ALIGN_CENTRE|wx.ALL, 5)
		
		if master:
			self.xm = master
			self.document = self.xm.document
			self.images=self.document.getElements('Data', {'SampleType':'image'})
			for d in self.images:
				self.addItem(d)
			controls.append(["File", "Close", lambda x: self.Destroy()])
			self.fillMenus(controls)
		else:
			controls.append(['Controls', 'Launch Data Editor', self.launchDE])
			self.fillMenus(controls)
			self.stdFileMenu()
		
		# id=wx.NewId()
		# self.menus["Controls"].AppendCheckItem(id, "Measure")
		# wx.EVT_MENU(self, id, self.measureMode)
		self._inMeasureMode = False
		self.measurements=[]
		self.measureCalib = None
		
		cbox = wx.BoxSizer(wx.HORIZONTAL)
		self.tmeasure = wx.ToggleButton(self.main, -1, " Measure ")
		cbox.Add(self.tmeasure, 0, wx.ALIGN_BOTTOM|wx.ALL, 5)
		wx.EVT_TOGGLEBUTTON(self.main, self.tmeasure.GetId(), self.measureMode)
		btn = wx.Button(self.main, -1, " Previous ")
		cbox.Add(btn, 0, wx.ALIGN_BOTTOM|wx.ALL, 5)
		wx.EVT_BUTTON(self.main, btn.GetId (), self.previous)
		btn = wx.Button(self.main, -1, " Next ")
		cbox.Add(btn, 0, wx.ALIGN_BOTTOM|wx.ALL, 5)
		wx.EVT_BUTTON(self.main, btn.GetId(), self.next)
		self.imageInfo=wx.StaticText(self.main, -1, "No Image")
		cbox.Add(self.imageInfo, 1, wx.ALIGN_BOTTOM|wx.ALL, 5)
		
		self.mainSizer.Add(cbox, 0, wx.GROW|wx.ALIGN_BOTTOM|wx.ALL, 5)		
			
		self.graph.keybindings['space']=self.next
		self.graph.keybindings['backspace']=self.previous	
			
		gfdt=FileDropLoad(self)
		self.imlist.SetDropTarget(gfdt)
		self.graph.twoDzoom=True
		self.graph.Show(True)
		self.extmod=ime.IVExtMod(self)
		self.extmod.makeMenus()
		self.mainSizer.Fit(self)
		self.SetSize(wx.Size(1000,700))
예제 #6
0
파일: viewer.py 프로젝트: gic888/MIEN
class ImageViewer(BaseGui):

	def __init__(self, master=None,returnData=None ):
		BaseGui.__init__(self, master, title="Image Viewer", menus=["File", "Controls", "Tools"], pycommand=True,height=4)
		
		self._current_display=[-1, 0]
		self._framecache=None
		self.images=[]
		
		controls=[["File", "New Viewer", self.newView],
					 ["File", "profile", self.prof],
					 ["File", "Save Measurements", self.saveMeasure],
				  ["Controls","Marker Locations",self.markInfo],
					["Controls", "Toggle pseudocolor", self.pcolor],
					["Controls", "Set Contrast Range", self.setRange],
					["Controls", "Show Contrast Range", self.getRange],
					["Controls", "Remove Contrast Range", self.remRange],
					["Controls", "Maximize Contrast", self.maxRange],
					["Controls", "Calibrate Measurements", self.calibM],
					["Controls", "Goto Frame", self.showFrame]]	
		
		self.mainSizer = wx.BoxSizer(wx.VERTICAL)
		self.main.SetSizer(self.mainSizer)
		self.main.SetAutoLayout(True)
		
		self.graph=Graph(self.main, -1)
		self.graph.axis["ticks"] = 0
		self.graph.legend = False
		#self.graph.fixAR=1.0
	
		psize = wx.BoxSizer(wx.HORIZONTAL)
		psize.Add(self.graph, 3, wx.GROW|wx.ALIGN_CENTRE|wx.ALL, 5)
		self.imlist = AWList(self.main, -1, style=wx.LC_REPORT
							 | wx.SUNKEN_BORDER | wx.WANTS_CHARS)
		self.imlist.InsertColumn(0, "Images")
		wx.EVT_LEFT_DCLICK(self.imlist, self.OnItemClick)
		psize.Add(self.imlist, 1, wx.GROW|wx.ALIGN_CENTRE|wx.ALL, 5)
		self.mainSizer.Add(psize, 10, wx.GROW|wx.ALIGN_CENTRE|wx.ALL, 5)
		
		if master:
			self.xm = master
			self.document = self.xm.document
			self.images=self.document.getElements('Data', {'SampleType':'image'})
			for d in self.images:
				self.addItem(d)
			controls.append(["File", "Close", lambda x: self.Destroy()])
			self.fillMenus(controls)
		else:
			controls.append(['Controls', 'Launch Data Editor', self.launchDE])
			self.fillMenus(controls)
			self.stdFileMenu()
		
		# id=wx.NewId()
		# self.menus["Controls"].AppendCheckItem(id, "Measure")
		# wx.EVT_MENU(self, id, self.measureMode)
		self._inMeasureMode = False
		self.measurements=[]
		self.measureCalib = None
		
		cbox = wx.BoxSizer(wx.HORIZONTAL)
		self.tmeasure = wx.ToggleButton(self.main, -1, " Measure ")
		cbox.Add(self.tmeasure, 0, wx.ALIGN_BOTTOM|wx.ALL, 5)
		wx.EVT_TOGGLEBUTTON(self.main, self.tmeasure.GetId(), self.measureMode)
		btn = wx.Button(self.main, -1, " Previous ")
		cbox.Add(btn, 0, wx.ALIGN_BOTTOM|wx.ALL, 5)
		wx.EVT_BUTTON(self.main, btn.GetId (), self.previous)
		btn = wx.Button(self.main, -1, " Next ")
		cbox.Add(btn, 0, wx.ALIGN_BOTTOM|wx.ALL, 5)
		wx.EVT_BUTTON(self.main, btn.GetId(), self.next)
		self.imageInfo=wx.StaticText(self.main, -1, "No Image")
		cbox.Add(self.imageInfo, 1, wx.ALIGN_BOTTOM|wx.ALL, 5)
		
		self.mainSizer.Add(cbox, 0, wx.GROW|wx.ALIGN_BOTTOM|wx.ALL, 5)		
			
		self.graph.keybindings['space']=self.next
		self.graph.keybindings['backspace']=self.previous	
			
		gfdt=FileDropLoad(self)
		self.imlist.SetDropTarget(gfdt)
		self.graph.twoDzoom=True
		self.graph.Show(True)
		self.extmod=ime.IVExtMod(self)
		self.extmod.makeMenus()
		self.mainSizer.Fit(self)
		self.SetSize(wx.Size(1000,700))

	def prof(self, event):
		import profile
		profile.runctx("self.display(0,0)", globals(), locals())
		
	def launchDE(self, event=None):
		'''Launch a MIEN XML Editor window '''
		from mien.interface.main import MienGui
		d=MienGui(self)
		d.newDoc(self.document)	
		
	def newView(self, event=None):
		'''Make another image viewer as a child of the current one (viewing the same document) '''
		i=ImageViewer(self)
		i.Show(True)
		i.onNewDoc()
		
	def select(self, data):
		'''Display (and select in the image list) the image contained in element data. Data is an instance, but it is used to select from the list self.images, so it must be an instance in the current document.'''
		self.display(self.images.index(data))
		
	def getSelected(self, mode='instances'):
		'''Returns a list of the currently selected images. Mode may be "indexes" (returns integer indexes into the image list), "instances" (returns the objects themselves), or "paths" (returns the upaths of the objects).'''
		sel=[]
		for i in range(self.imlist.GetItemCount()):
			s=self.imlist.GetItemState(i, wx.LIST_STATE_SELECTED)
			if s:
				sel.append(i)
		if mode=='indexes':
			return sel
		images=[self.images[i] for i in sel]
		if mode=='instances':
			return images
		return [i.upath() for i in images]
		
	def onNewDoc(self):
		'''Internal method. Update the image list when the document changes'''
		cdi=None
		self._framecache=None
		if self._current_display[0]>=0:
			cdi=self.images[self._current_display[0]].upath()
		self.images=self.document.getElements('Data', {'SampleType':'image'})
		self.imlist.DeleteAllItems()
		for d in self.images:
			self.addItem(d)
		if cdi:	
			try:
				self.document.getInstance(cdi)
				self.display(-1)
			except StandardError:
				self.display(None)
		elif self.images:
			self.display(0)	
			
			
	def getCurrentImage(self):	
		'''Returns the instance of the currently displayed image if there is one, or the first image in the selection list, or None if there is no display and no selection '''	
		if self._current_display[0] > -1:
			return self.images[self._current_display[0]]
		else:
			try:
				i=self.getSelected('instances')[0]
				return i
			except IndexError:
				return None		
		
		
		
	def imageCoords(self, a, horiz=True, crop=True):
		'''a is a 1D array. Returns an array of the same shape converted to image coordinates for the indicated axis (horizontal if horiz is True, vertical otherwise), using the current image. Image coordinates are integer (of type int32), (0,0) is the upper right, and larger Y values are closer to the bottom of the screen. This is the reverse of Y coordinates returned by most MIEN graphs, including the labels on the Y markers. If there is no currently selected image, Y value conversion will assume an image size of 600x400px. If crop is True (default), coordinates are are constrained to never be smaller than 4 pixels of selected area, and never be larger than the size of the image. '''	
		a=array(a).astype(int32)
		im = self.getCurrentImage()
		if im:
			x=im.getData().shape[0]
			y=im.getData().shape[1]
		else:
			x=600
			y=400
		if horiz:
			s=x
		else:
			a=y-a
			s=y
		if crop:
			a=maximum(a, 0)
			a=minimum(a, s)
		return a
			
			
	def getBoundaries(self, crop=True):
		'''Return a 2x2 array, containing the bounding box of the current view ((xmin, ymin), (xmax, ymax)). Note that the values are in image coordinates, as described in self.imageCoords. '''
		l=self.graph.limits.astype(int32)
		l[2:]=self.imageCoords(l[2:], False, crop)
		l[:2]=self.imageCoords(l[:2], True, crop)
		b=array([[l[0], l[3]],[l[1], l[2]]])
		return b
		
	def getMarkers(self, crop=True):
		'''Returns a 2tuple of 1D arrays (x, y), where x contains the coordinates of all vertical markers, and y contains the coordinates of all horizontal markers. Coordinates and the behavior of the "crop" flag are as described in self.imageCoords.'''
		x=self.imageCoords([m['loc'] for m in self.graph.xmarkers], True, crop)
		y=self.imageCoords([m['loc'] for m in self.graph.xmarkers], False, crop)
		return (x, y)

	def update_self(self, **kwargs):
		'''Internal state maintenance method. This implementation calls self.onNewDoc'''
		self.onNewDoc()

	def OnItemClick(self, event=None):
		pt = event.GetPosition();
		item, flags = self.imlist.HitTest(pt)
		try:
			self.display(item)
		except:
			self.report("can't display %s" % (str(item),))
			return
	
	def addItem(self, dat):
		ind=self.imlist.GetItemCount()
		if not dat.attrib('Url'):
			dat.setAttrib('Url', "Unknown")
		name="%s (%s)" % (dat.upath(), dat.attrib('Url') )
		if len(name)>30:
			name=dat.upath()
			
		ind=self.imlist.InsertStringItem(ind, name)
		return ind

	def display(self, ind, frame=0, contrange=0):
		if ind is None:
			self._current_display=[-1, 0]
			self.graph.killAll()
			self.graph.DrawAll()
			self.imageInfo.SetLabel(' No Image ')
			return
		elif ind==-1:
			ind=self._current_display[0]
			frame=self._current_display[1]
		else:
			self._current_display=[ind, frame]
		self.imlist.SetItemState(ind, wx.LIST_STATE_SELECTED, wx.LIST_STATE_SELECTED)	
		image=self.images[ind]
		dat=image.getData()
		dshape=dat.shape
		if len(dat.shape)<3:
			ncolors=1
		else:
			ncolors=dat.shape[2]
		if len(dat.shape)<4:
			stack=0
		else:	
			stack=dat.shape[3]
			dat=dat[:,:,:,frame]
		self.graph.killAll()
		lim=array([[0.0,0.0],[dat.shape[0],dat.shape[1]]])
		self.graph.addPlot(dat, style="ScalingImage", limits=lim, colorrange=image.attrib('ColorRange'), pcolor=image.attrib('pseudocolor'))
		pw=image.attrib("PixelWidth") or 1.0
		ph=image.attrib("PixelHeight") or pw
		ar=float((pw*dat.shape[0]))/(ph*dat.shape[1])
		if self.graph.fixAR!=ar:
			self.graph.fixAR=ar
			self.graph.fullScale()
		self.graph.DrawAll()
		info = self.imlist.GetItemText(ind)
		info += " %Gx%G" % (dshape[0], dshape[1])
		if ncolors == 1:
			info+=", Grey"
		elif ncolors ==3:
			info+= ", RGB"
		else:
			info+=", %i color channels" % ncolors
		if contrange:
			info+=", levels %G to %G" % (dat.min(), dat.max())
		if stack>1:
			info+=", stack of %i frames. Showing frame %i" % (stack, frame)	
		self.imageInfo.SetLabel(info)
		if self._inMeasureMode and self.measurements:
			self.showMeasure(False)
			
	def getRange(self, event):
		ind, frame=self._current_display
		dat=self.images[ind].getData()
		info = self.imlist.GetItemText(ind)
		info += " %Gx%G" % (dat.shape[0], dat.shape[1])
		if len(dat.shape)<3 or dat.shape[2]==1:
			info+=", Grey"
		elif dat.shape[2] ==3:
			info+= ", RGB"
		else:
			info+=", %i color channels" % dat.shape[2]
		info+=", levels %G to %G" % (dat.min(), dat.max())
		if len(dat.shape)>3 and dat.shape[3]>1:
			info+=", stack of %i frames. Showing frame %i" % (dat.shape[3], frame)	
		self.imageInfo.SetLabel(info)		
		
	def showFrame(self, event):
		if not self._current_display:
			return
		try:
			dat=self.images[self._current_display[0]].getData()
			nf=dat.shape[3]
			if nf==1:
				raise
		except:
			self.report("Current image isn't a stack")
			return
		if type(event)==int:
			i=event
		else:	
			d=self.askParam([{"Name":"Frame Index (0 - %i)" % (nf-1,),
				"Type":int, "Value":self._current_display[1]}])
			if not d:
				return
			i=d[0]
		if i<0:
			i=nf+i
		if i<0 or i>nf-1:
			self.report("Current stack  doesn't have a frame index %i" % i)
			return
		self.display(self._current_display[0], i)
			

	def build_framecache(self):
		self._framecache=[]
		for i, im in enumerate(self.images):
			s=im.getData().shape
			if len(s)<4:
				nf=1
			else:
				nf=s[3]
			for j in range(nf):
				self._framecache.append((i,j))
	
	def increment(self, inc):
		if not self.images:
			return
		if not self._current_display:
			self._current_display=[0, 0]
		if not self._framecache:
			self.build_framecache()
		i=self._framecache.index(tuple(self._current_display))
		i+=inc
		if i<0:
			i=len(self._framecache)+i
		i=i % len(self._framecache)
		d=self._framecache[i]
		self.display(d[0], d[1])
		
	def previous(self, event=None):
		self.increment(-1)	
	
	def next(self, event=None):
		self.increment(+1)	
			
	def pcolor(self, event):
		try:
			im=self.images[self._current_display[0]]
		except:
			self.report('no image displayed')
			return
		if im.attrib('pseudocolor'):
			im.setAttrib('pseudocolor', None)
		else:
			im.setAttrib('pseudocolor', 'hot')
		self.display(-1)
		
	def setRange(self, event):
		d=self.askParam([{'Name':'Minimum', 'Value':0.0}, {'Name':'Maximum', 'Value':255.0}])
		if not d:
			return
		for i in self.getSelected():
			i.setAttrib('ColorRange', (d[0], d[1]))
		self.display(-1)
			
	def remRange(self, event):	
		for i in self.getSelected():
			i.setAttrib('ColorRange', None)
		self.display(-1)

	def maxRange(self, event):	
		for i in self.getSelected():
			d=i.getData()
			i.setAttrib('ColorRange', (d.min(), d.max()))
		self.display(-1)		
			
	def calibM(self, event):
		im = self.getCurrentImage()
		xmin = 0
		ymin = 0
		xpix = 100
		ypix = 100
		if len(self.measurements)>1:
			m = array(self.measurements)
			xpix = m[:,0].max() - m[:,0].min()
			ypix = m[:,1].max() - m[:,1].min()
			xmin = m[:,0].min()
			ymin = m[:,1].max()
		elif im:
			d = im.getData()
			xpix = d.shape[0]
			ypix = d.shape[1]
			
		d = self.askParam([{"Name":"X Pixels", 
			"Value":xpix},{"Name":"X Distance", 
			"Value":1.0},{"Name":"X Origin", 
			"Value":xmin},{"Name":"Y Pixels", 
			"Value":ypix},{"Name":"Y Distance", 
			"Value":1.0},{"Name":"Y Origin", 
			"Value":ymin}
			])
		if not d:
			return
		xdpp = float(d[1])/d[0]
		ydpp = float(d[4])/d[3]
		xor = d[2]
		yor = d[5]
		self.measureCalib = (xor, xdpp, yor, ydpp)
		self.showMeasure()
		
	def saveMeasure(self, event):
		im = self.getCurrentImage()
		if not self.measurements:
			self.report("No measurements")
			return
		d = self.askParam([{"Name":"file name", "Value":"image_measurements.txt"}])
		if not d:
			return
		f = open(d[0], 'w')
		for m in self.measurements:
			if self.measureCalib:
				xmm = (m[0]-self.measureCalib[0])*self.measureCalib[1]
				ymm = (-m[1]+self.measureCalib[2])*self.measureCalib[3]
				s = "%.5g %.5g" % (xmm, ymm)
			else:
				s= "%i %i" % (m[0], m[1])
			try:
				d=im.getData()[m[0], m[1], :, self._current_display[1]]
				ts = " %G"* len(d)
				d = tuple(d)
				s+= ts % d
			except:
				pass
			s+="\n"
			f.write(s)
		f.close()
		self.report("wrote measurement file")
	
		
	def doMeasure(self, event):
		im = self.getCurrentImage()
		if not im:
			self.report("You must have a displayed image to measure it!")
			return
		x=event.GetX()
		y=event.GetY()
		rx, ry = self.graph.numericalCoordinates((float(x),float(y)))[0,:]
		yh=im.getData().shape[1]
		ry=yh-ry
		self.measurements.append((int(round(rx)), int(round(ry))))
		self.showMeasure()
	
	def showMeasure(self, report=True):
		pl=0
		im = self.getCurrentImage()
		yh=im.getData().shape[1]
		rsl=['Measurements:']
		for i, m in enumerate(self.measurements):
			l="%i" % i	
			y=yh-m[1]
			self.graph.drawTaggedLocation(m[0], yh-m[1], '%i' % i, wx.Colour(128,0,128))
			s = "%i : (%i, %i)" % (i, m[0], m[1])
			if self.measureCalib:
				xmm = (m[0]-self.measureCalib[0])*self.measureCalib[1]
				ymm = (-m[1]+self.measureCalib[2])*self.measureCalib[3]
				s = "%i : (%i:%.4g, %i:%.4g)" % (i, m[0], xmm, m[1], ymm)
			else:
				s = "%i : (%i, %i)" % (i, m[0], m[1])
			if im:
				try:
					d=im.getData()[m[0], m[1], :, self._current_display[1]]
					if len(d)==1:
						s+=" = %G" % (float(d[0]),)
					else:
						s+=" = %s" % (str(list(d)))
				except:
					pass
			if i>0:
				lm=self.measurements[i-1]
				d=sqrt( (m[0] - lm[0])**2+(m[1]-lm[1])**2 )
				s+=" dist: %G" % d
				if pl:
					s+=" path: %G" % (d+pl,)
				pl+=d
			rsl.append(s)
		if report:
			self.report("\n".join(rsl))
		else:
			return rsl
			
	def measureMode(self, event):
		self._inMeasureMode = not self._inMeasureMode
		print self._inMeasureMode
		if self._inMeasureMode:
			self.measurements=[]
			wx.EVT_LEFT_UP(self.graph, lambda x:0)
			wx.EVT_LEFT_DOWN(self.graph, self.doMeasure)
			self.graph.DrawAll()
		else:
			wx.EVT_LEFT_UP(self.graph, self.graph.OnLeftRelease)
			wx.EVT_LEFT_DOWN(self.graph, self.graph.OnLeftClick)
	


	def markInfo(self, event=None):
		rep = "Markers\n"
		last=None
		xlocs=[m['loc'] for m in self.graph.xmarkers]
		xlocs.sort()
		for t in xlocs:
			rep+= "X Mark at %G" % t
			if last:
				rep+=" dx = %G" % (t-last,)
			last=t
		last=None
		ylocs=[m['loc'] for m in self.graph.ymarkers]
		ylocs.sort()
		for t in ylocs:
			rep+= "Y Mark at %G" % t
			if last:
				rep+=" dx = %G" % (t-last,)
			last=t
		self.report(rep)