Example #1
0
class ContTweaker:
	def __init__(self,iwvlngth,ispectrum,espectrum,oldxspline,oldyspline,outfile):
		#Initialize input variables for the class
		self.outfile=outfile
		self.iwvlngth=iwvlngth
		self.ispectrum=ispectrum
		self.espectrum=espectrum
		#intialize CT.XSPLINE/YSPLINE to the old values at first (will be replaced)
		self.xspline=oldxspline
		self.yspline=oldyspline
		self.oldxspline=oldxspline
		self.oldyspline=oldyspline
		#Interpolate cubic spline of the continuum for both "NEW" and old spline
		self.oldspline=scipy.interpolate.interp1d(oldxspline,oldyspline,kind='cubic')
		self.spline=scipy.interpolate.interp1d(oldxspline,oldyspline,kind='cubic')
		#Make the MPL figures appear
		self.MakePlot()
		#Add buttons to the bottom of the main TB_CONTFIT widget menu
		self.popup=Tkinter.Frame()
		self.popup.grid()
		But1=Tkinter.Button(self.popup,text='Refresh Plot',command=self.Refresh)
		But1.grid(column=0,row=0)
		But3=Tkinter.Button(self.popup,text='Remove Point',command=self.RemovePoint)
		But3.grid(column=0,row=1)
		But4=Tkinter.Button(self.popup,text='Add Point',command=self.AddPoint)
		But4.grid(column=0,row=2)
		But5=Tkinter.Button(self.popup,text='Reopen Window',command=self.MakePlot)
		But5.grid(column=0,row=4)		
		But6=Tkinter.Button(self.popup,text='Save Spline',command=self.Save)
		But6.grid(column=0,row=5)
		But7=Tkinter.Button(self.popup,text='Exit',command=self.Exit)
		But7.grid(column=0,row=6)


		
		#Toggle between a Continous-editing  and single click modes
		MODES=[("Single",False),("Continuous",True)]
		self.useContinuous=Tkinter.BooleanVar()
		self.useContinuous.set(False)
		ii=1
		labelMode=Tkinter.StringVar()
		labelmode=Tkinter.Label(self.popup,textvariable=labelMode,anchor="w",fg="black")
		labelmode.grid(column=1, row=0, columnspan=2, sticky='S')
		labelMode.set(u"Point-editing mode")
		for text,mode in MODES:
			b=Tkinter.Radiobutton(self.popup,text=text,variable=self.useContinuous,value=mode)
			b.grid(column=ii,row=1)
			ii+=1
			if ii==1: b.select()

		#Wait until the window is closed (i.e. CT.EXIT() is run by clicking EXIT button)
		self.popup.wait_window()




	def MakePlot(self):
		#This function generates the plot window that will be the vidual interface for how
		#the continuum tweaking is going

		#Make the Spline from the CURRENT spline values
		xcont=self.iwvlngth
		ycont=self.spline(xcont)
		#Make the spline from the OLD spline values
		ospectrum=self.oldspline(xcont)

		#Create a MPL figure
		self.tweakfig=plt.figure(figsize=(8,8))
		#First subplot is for displaying the original fit to the spectrum
		self.ax1=self.tweakfig.add_subplot(3,1,1)
		#Second subplot is for displaying the current fit to the spectrum
		#To help with looking at all regions of the same time, share the X and Y
		#axes to CT.AX1.
		self.ax2=self.tweakfig.add_subplot(3,1,2,sharex=self.ax1,sharey=self.ax1)
		#Third subplot for displaying the continuum-fitted spectrum based on the
		#current spline. Because this is now 0 and 1ish, only share the X axis
		#to CT.AX1
		self.ax3=self.tweakfig.add_subplot(3,1,3,sharex=self.ax1)
		#Create the TK window for embedding the figure, set it up, and draw
		self.TFroot=Tkinter.Tk()
		self.TFroot.wm_title("Continuum Tweaker")
		self.TweakPlot=FigureCanvasTkAgg(self.tweakfig,master=self.TFroot)
		#This toolbar is mainly for zooming. By sharing the x (and y) axes, when zooming in
		#on one axes, all three will zoom appropriately
		NavTweakPlot=NavigationToolbar2TkAgg(self.TweakPlot, self.TFroot)
		self.TweakPlot.get_tk_widget().pack(side=Tkinter.TOP, fill=Tkinter.BOTH, expand=1)

		#For CT.AX1, plot the original specturm, the error spectrum, and the original continuum fitted
		self.ax1.plot(self.iwvlngth,self.ispectrum,'k',drawstyle='steps',label='Spectrum')
		self.ax1.plot(self.iwvlngth,self.espectrum,'g',drawstyle='steps',label='Error')
		self.ax1.plot(xcont,ycont,'b',label='Continuum')
		self.ax1.plot(self.oldxspline,self.oldyspline,'or',label='Spline Pts.')
		#Make a legend
		self.ax1.legend(ncol=4,frameon=False, loc=9, bbox_to_anchor=(0.5, 1.3))
		self.ymin,self.ymax=self.ax1.get_ylim()
		self.ax1.set_ylabel('Flux\n(original)')

		#For CT.AX2, plot the spectrum, and the current spline points and the resulting continuum
		self.ax2.plot(self.iwvlngth,self.ispectrum,'k',drawstyle='steps')
		self.ax2.plot(xcont,ycont,'b')
		self.ax2.plot(self.xspline,self.yspline,'or',picker=5)#Picker needed to pick which point
		self.ax2.set_ylabel('Flux\n(tweaked fit)')



		#For CT.AX3, plot the spectrum upon dividing by the continuum. Plot the
		#error spectrum as 1-err and 1+err to reflect how much noise one MIGHT expect
		#As a rule of thumb, one should aim to have the continuum fluctuations within the error
		self.ax3.plot(xcont,self.ispectrum/ospectrum,'k',drawstyle='steps')
		self.ax3.plot(xcont,1.0-self.espectrum/ospectrum,'--g',drawstyle='steps')
		self.ax3.plot(xcont,1.0+self.espectrum/ospectrum,'--g',drawstyle='steps')
		self.xmin,self.xmax=self.ax3.get_xlim()
		self.ax3.plot([self.xmin,self.xmax],[1,1],'--r')
		self.ax3.plot([self.xmin,self.xmax],[0,0],'--r')
		#restrict CT.AX3 to only show the normalized range of values
		self.ax3.set_ylim(-1,2)
		self.ax3.set_ylabel('Relative flux')
		self.ax3.set_xlabel('Wavlength')

		#Draw all the new plotted stuff
		self.TweakPlot.draw()



	def Refresh(self,yrefresh=True):
		#This function takes any modifications that have taken place and updates
		#the plots accordingly. If YREFRESH=TRUE, this sets the y-axis to the
		#original scaling. Otherwise keep the current values.

		#Get the current x values (incase the user has zoomed in using toolbar)
		xmin,xmax=self.ax2.get_xlim()
		ymin,ymax=self.ax2.get_ylim()
		#if YREFRESH=TRUE, set to original values (SELF.YMIN/YMAX)
		if yrefresh:
			ymin=self.ymin
			ymax=self.ymax
		#Clear CT.AX2/AX3 of all previous information
		self.ax2.clear()
		self.ax3.clear()
		#Remake the spline based on the new XSPLINE/YSPLINE points, and save
		self.spline=scipy.interpolate.interp1d(self.xspline,self.yspline,kind='cubic')
		#Generate the continuum based on the new spline
		xcont=self.iwvlngth
		ycont=self.spline(xcont)

		#Update CT.AX1, perserve the x-axis bounds, and return to original y-axis
		self.ax1.set_ylim(ymin,ymax)
		self.ax1.set_xlim(xmin,xmax)

		#Update CT.AX2 by plotting new spline, also perserve the x-axis bounds
		self.ax2.plot(self.iwvlngth,self.ispectrum,'k',drawstyle='steps')
		self.ax2.plot(xcont,ycont,'b')
		self.ax2.plot(self.xspline,self.yspline,'or',picker=5)
		self.ax2.set_ylabel('Flux\n(tweaked fit)')
		self.ax2.set_ylim(ymin,ymax)
		self.ax2.set_xlim(xmin,xmax)

		#in CT.AX2, Divide out the spectrum&errorspectrum by the continuum, and plot
		self.ax3.plot(xcont,self.ispectrum/ycont,'k',drawstyle='steps')
		self.ax3.plot(xcont,1.0-self.espectrum/ycont,'--g',drawstyle='steps')
		self.ax3.plot(xcont,1.0+self.espectrum/ycont,'--g',drawstyle='steps')
		self.ax3.set_ylabel('Relative flux')
		self.ax3.set_xlabel('Wavlength')
		self.ax3.set_ylim(-1,2)
		self.ax3.plot([self.xmin,self.xmax],[1,1],'--r')
		self.ax3.plot([self.xmin,self.xmax],[0,0],'--r')
		self.ax3.set_xlim(xmin,xmax)

		#Update plotting window
		self.TweakPlot.draw()


	#Function for closing the current MPL event by it's ID, and stop the event loop
	#It might seem redundant that I have both things, but I intend to have a continous
	#editing method, which would need the looper.
	def QuitEdit(self,cid):
		#Close event ID
		self.TweakPlot.mpl_disconnect(cid)
		#Stop event loop
		self.TweakPlot.stop_event_loop()
	#Function when "Add Point" button is clicked.
	def AddPoint(self):
		#Show Tutorial message for what to do.
		if usetutorial: tkMessageBox.showinfo("Help Message", "Click where to add point.")
		#Start mouse click event, and run CT.ClickAdd
		self.cidbut=self.TweakPlot.mpl_connect('button_press_event',self.ClickAdd)
		self.TweakPlot.start_event_loop(0)
		#If use continuous button is on, repeat adding points
		while self.useContinuous.get():
			if usetutorial: tkMessageBox.showinfo("Help Message", "Continuous point addition on. Keep adding points.")
			try:
				self.cidbut=self.TweakPlot.mpl_connect('button_press_event',self.ClickAdd)
				self.TweakPlot.start_event_loop(0)
			except: self.useContinuous.set(False)
	#Given a mouse event for adding a point...	
	def ClickAdd(self,event):
		#Grab the x/y coordiantes of the click, and add to spline
		self.xspline.append(event.xdata)
		self.yspline.append(event.ydata)
		#Sort the spline data to be in order by wavelength
		self.xspline,self.yspline=SortList(self.xspline,self.yspline)
		#Refresh the plot with new data, but keep y-axis
		self.Refresh(yrefresh=False)
		#Close the MPL event stuff
		self.QuitEdit(self.cidbut)
	#Function ro remove a point when "Remove Point" button pressed
	def RemovePoint(self):
		#Show tutorial message on what to do
		if usetutorial: tkMessageBox.showinfo("Help Message", "Click point to remove.")
		#Start MPL event for picking an MPL artist, and start the loop. Run CT.ClickRemove
		self.cidpick=self.TweakPlot.mpl_connect('pick_event',self.ClickRemove)
		self.TweakPlot.start_event_loop(0)
		#If Use continuous button is on, repeat removing points
		while self.useContinuous.get():
			if usetutorial: tkMessageBox.showinfo("Help Message", "Continuous point removal on. Keep removing points.")
			try:
				self.cidpick=self.TweakPlot.mpl_connect('pick_event',self.ClickRemove)
				self.TweakPlot.start_event_loop(0)
			except:
				self.useContinuous.set(False)
	#Given a picker event for removing a point...
	def ClickRemove(self,event):
		#Get the spline point that you picked, it's x and y coordinates
		splinepoint = event.artist
		xsplineval=splinepoint.get_xdata()
		ysplineval=splinepoint.get_ydata()
		#Index of the artist
		ind = event.ind
		#Make sure the point is in the spline point lists
		if xsplineval[ind] in self.xspline:
			if ysplineval[ind] in self.yspline:
				#Remove that point from the spline, I think this is where sorting is important...
				self.xspline.pop(ind)
				self.yspline.pop(ind)
		#Refresh the plot with new spline, but keep y-axis
		self.Refresh(yrefresh=False)
		#Close the event and stop the event loop
		self.QuitEdit(self.cidpick)
	#Function saves the spline using SaveSpline function
	def Save(self):
		print "Saving Spline"
		SaveSpline(self.xspline,self.yspline,self.iwvlngth,self.outfile)
	#Destroy CT.POPUP and CT.TFROOT (the masters for the CT buttons and plots) and leave CT.
	def Exit(self):
		self.popup.destroy()
		self.TFroot.destroy()
		return