def _removeButtonClicked(self): """ Remove the active component. """ if self._compoCount < 2: return # Create new model if self._mode == "voigt": nm = fuf.MultiVoigt1d(self._compoCount - 1) else: nm = fuf.MultiGauss1d(self._compoCount - 1) # The offset has to be treated separately nm["off"] = self._model["off"] if "off" in self._model.freeParamNames(): nm.thaw("off") # t counts the "target" component t = 0 for i in smo.range(1, self._compoCount + 1): if i == self._activeComponent: # Disregard the currently active component continue n = str(i) t += 1 for p in self._compPars: nm[p + str(t)] = self._model[p + n] if (p + n) in self._model.freeParamNames(): nm.thaw(p + str(t)) self._model = nm self._compoCount -= 1 self._downAC() self._updateGUI()
def __init__(self, lineList, uniSig=None, modelBinsize=0.005, useFastRB=True, verbose=False, onlyAbs=True): if not ic.check["scipy"]: raise(PE.PyARequiredImport("Could not import scipy.", \ solution="Install scipy.")) # Check whether depths are given self._depthsGiven = (len(lineList[0,::]) == 3) if (not self._depthsGiven) and (uniSig is None): raise(PE.PyAValError("No width and no depth given.", \ solution="Specify line depth in `lineList` or use `uniSig`.")) # Check whether unified sigma shall be used # Note: Line depth will be ignored then self._uniSig = uniSig # Only absorption lines self._onlyAbs = onlyAbs # Verbosity self._verbose = verbose # Use fast rotational broadening? self._usefastRB = useFastRB # Save binsize for the model self._modelBinsize = modelBinsize # Copy line list self._lineList = lineList.copy() # Number of lines self._nl = len(lineList[::,0]) # A MultiGaussFit object self._mg = fuf.MultiGauss1d(self._nl) # Store all parameter names from GaussFit pars = list(self._mg.parameters().keys()) # Remove lin and off from multiGauss keys pars.remove("lin") pars.remove("off") pars.extend(["lineScale", "scale", "eps", "vrad", "vsini"]) fuf.OneDFit.__init__(self, pars) # Name the model self.setRootName("LineListGauss") # Assign start values for i in range(self._nl): p = str(i+1) # Assign position self._mg["mu"+p] = lineList[i,0] self["mu"+p] = lineList[i,0] # Assign amplitude/EW # Note: Positive EWs correspond to absorption lines self._mg["A"+p] = -lineList[i,1] self["A"+p] = lineList[i,1] # Assign width (sigma) if self._depthsGiven and (self._uniSig is None): # Depth IS given and no unified sigma specified self._mg["sig"+p] = abs(lineList[i,1])/((1.-lineList[i,2]) * np.sqrt(2.*np.pi)) self["sig"+p] = self._mg["sig"+p] elif not (self._uniSig is None): # uniSig IS given self._mg["sig"+p] = self._uniSig self["sig"+p] = self._mg["sig"+p] self["scale"] = 1.0 self["lineScale"] = 1.0 if self._onlyAbs: # Apply restrictions to prevent emission lines for i in range(self._nl): p = str(i+1) self.setRestriction({"A"+p:[0.0, None]})
def _newCompAddPoint(self, event): """ Add a point to point cache or create component if point set is complete. """ p = Point(event) # Plot point on canvas self.a.plot([p.xdata], [p.ydata], 'r+', markersize=10) self._pointCache.append([p, self.a.lines[-1]]) if len(self._pointCache) < 3: # More points have to be added self._updateView() return # Three points have been added fwhm = np.abs(self._pointCache[0][0].xdata - self._pointCache[2][0].xdata) depth = self._model["off"] - self._pointCache[1][0].ydata # Estimate std (Gaussian) sigma = fwhm / 2.35482 # Estimate area of a Gaussian with given depth agauss = sigma * np.sqrt(2. * np.pi) * depth # Add further component self._compoCount += 1 if self._mode == "voigt": nmodel = fuf.MultiVoigt1d(self._compoCount) elif self._mode == "gauss": nmodel = fuf.MultiGauss1d(self._compoCount) # Assign parameters from old model nmodel.assignValues(self._model.parameters()) # Transfer free parameters nmodel.thaw(self._model.freeParamNames()) self._model = nmodel if self._mode == "voigt": c0 = 2.0056 c1 = 1.0593 # fl/fg phi = self._config["default_alad"] fg = fwhm / (1.0 - c0 * c1 + np.sqrt(phi**2 + 2. * c1 * phi + c0**2 * c1**2)) fl = phi * fg n = str(self._compoCount) self._model["A" + n] = -agauss self._model["al" + n] = fl / 2.0 self._model["ad" + n] = fg / (2. * (2. * np.log(2.0))) self._model["mu" + n] = self._pointCache[1][0].xdata # Thaw default parameters self._model.thaw([x + n for x in self._config["defaultFreeVoigt"]]) elif self._mode == "gauss": n = str(self._compoCount) self._model["A" + n] = -agauss self._model["sig" + n] = sigma self._model["mu" + n] = self._pointCache[1][0].xdata # Thaw default parameters self._model.thaw([x + n for x in self._config["defaultFreeGauss"]]) if (self._commonSig.get() == 1) and (self._compoCount > 1): # Common widths for Gaussians requested self._model.thaw("sig" + n) self._model.relate("sig" + n, ["sig" + str(1)]) self._cleanPointCache() self._updateGUI()
def __init__(self, x, y, yerr=None, mode="gauss", config=None): self.windowTitle = "PyA interactive GV" self.f = Figure() self.a = self.f.add_subplot(111) self._config = {"modelLS":'r--', \ "dataLS":'bp', \ "dataLSerr":'b+', \ "activeCompLS":'k-', \ "inactiveCompLS":'y--', \ "plotIndComps":True, \ "rangeLS":'m--', \ "defaultFreeVoigt":["A", "ad", "al", "mu"], \ "default_alad":0.1, \ "defaultFreeGauss":["A", "sig", "mu"], \ "defaultWheelAdd":0.01, \ "defaultWheelMult": 2} # Modify configuration according to input if config is not None: for k in six.iterkeys(self._config): if k in config: # Value has been modified self._config[k] = config[k] # Tk root self.root = tk.Tk() # Create data plot self._x = x self._y = y self._yerr = yerr if self._yerr is None: self.a.plot(x, y, self._config["dataLS"]) else: self.a.errorbar(x, y, yerr=self._yerr, fmt=self._config["dataLSerr"]) # The input mode determines how clicks on the mouse wheel are # interpreted. self._inputMode = "newComp" # Component count/assignment starts at 1 self._activeComponent = 1 self._compoCount = 0 # Create model and parameter lists self._mode = mode if self._mode == "voigt": self._model = fuf.MultiVoigt1d(1) self._compPars = ["mu", "ad", "al", "A"] elif self._mode == "gauss": self._model = fuf.MultiGauss1d(1) self._compPars = ["mu", "sig", "A"] else: raise(PE.PyAValError("No such mode: " + str(self._mode), \ solution="Choose either 'voigt' or 'gauss'.")) self._model["off"] = 1.0 # List used to cache points used to add component # as long as component is not completely specified self._pointCache = [] # What to consider in the fit self._rangeIndices = np.arange(self._x.size, dtype=np.int) # A frame containing the mpl plot self.plotFrame = tk.Frame() self.plotFrame.pack(fill=tk.BOTH, side=tk.LEFT, expand=True) self.canvas = FigureCanvasTkAgg(self.f, master=self.plotFrame) # A frame containing the box with selected points # and control buttons self.pointFrame = tk.Frame(self.root) self.pointFrame.pack(side=tk.LEFT, fill=tk.BOTH) # Manage active component self.activeCompoFrame = tk.Frame(self.pointFrame) self.activeCompoFrame.pack(side=tk.TOP) self.currentComponentLabel = tk.Label(self.activeCompoFrame, text="Active component") self.currentComponentLabel.pack(side=tk.TOP) # De- and increase no. of active component self.downACB = tk.Button(self.activeCompoFrame, text="-", command=self._downAC) self._acstring = tk.StringVar() self._acstring.set(str(self._activeComponent)) self.ACLabel = tk.Label(self.activeCompoFrame, textvariable=self._acstring) self.upACB = tk.Button(self.activeCompoFrame, text="+", command=self._upAC) self.downACB.pack(side=tk.LEFT) self.ACLabel.pack(side=tk.LEFT) self.upACB.pack(side=tk.RIGHT) # Show and work with parameters self.parameterFrame = tk.Frame(self.pointFrame, relief=tk.RAISED, borderwidth=1) self.parameterFrame.pack(side=tk.TOP) # Headline tk.Label(self.parameterFrame, text="Parameters (active comp.)").pack(side=tk.TOP) # List of parameters to show pars = ["off"] pars.extend(self._compPars) self._parSelect = tk.IntVar() # Frames for individual parameters self._parFrames = [] self._parValues = [] self._parFreeVars = [] for i, p in enumerate(pars): self._parFrames.append(tk.Frame(self.parameterFrame)) self._parFrames[-1].pack(side=tk.TOP) b = tk.Radiobutton(self._parFrames[-1], text="", variable=self._parSelect, value=i) b.pack(side=tk.LEFT, anchor=tk.W) l = tk.Label(self._parFrames[-1], text=("%3s" % p), width=3) l.pack(side=tk.LEFT, anchor=tk.W) self._parValues.append(tk.StringVar()) self._parValues[-1].set("0.0") l = tk.Label(self._parFrames[-1], textvariable=self._parValues[-1], width=10) l.pack(side=tk.LEFT) self._parFreeVars.append(tk.IntVar()) c = tk.Checkbutton(self._parFrames[-1], text="free", variable=self._parFreeVars[-1], \ command=self._freeCheckboxClicked) c.pack(side=tk.LEFT) # Wheel modification self._wheelModFrame = tk.Frame(self.pointFrame, relief=tk.RAISED, borderwidth=1) self._wheelModFrame.pack(side=tk.TOP) # Headline tk.Label(self._wheelModFrame, text="Mouse wheel").pack(side=tk.TOP) self._multAddSelect = tk.IntVar() self._multAddSelect.set(1) multiFrame = tk.Frame(self._wheelModFrame) addFrame = tk.Frame(self._wheelModFrame) multiFrame.pack(side=tk.TOP) addFrame.pack(side=tk.TOP) tk.Radiobutton(multiFrame, text="multiply [%]", variable=self._multAddSelect, value=1, width=9, anchor=tk.W).pack(side=tk.LEFT) tk.Radiobutton(addFrame, text="add", variable=self._multAddSelect, value=2, width=9, anchor=tk.W).pack(side=tk.LEFT) self._multNumber = tk.StringVar() self._addNumber = tk.StringVar() self._multNumber.set(str(self._config["defaultWheelMult"])) self._addNumber.set(str(self._config["defaultWheelAdd"])) self._multNumberEntry = tk.Entry(multiFrame, textvariable=self._multNumber, width=10) self._multNumberEntry.pack(side=tk.RIGHT) self._addNumberEntry = tk.Entry(addFrame, textvariable=self._addNumber, width=10) self._addNumberEntry.pack(side=tk.RIGHT) # Common width if self._mode == "gauss": commonSigFrame = tk.Frame(self.pointFrame) commonSigFrame.pack(side=tk.TOP) self._commonSig = tk.IntVar() c = tk.Checkbutton(commonSigFrame, text="Common width (sig)", variable=self._commonSig, \ command=self._commonSigClicked) c.pack(side=tk.LEFT) # Fit button self._fitButton = tk.Button(self.pointFrame, text="Fit", command=self._fitModel) self._fitButton.pack(side=tk.TOP, fill=tk.X) # Range button self._rangeButton = tk.Button(self.pointFrame, text="Set fit range", command=self._rangeButtonClicked) self._rangeButton.pack(side=tk.TOP, fill=tk.X) # Remove button self.removeButton = tk.Button(master=self.pointFrame, text="Remove component", \ command=self._removeButtonClicked) self.removeButton.pack(side=tk.TOP, fill=tk.X) # a tk.DrawingArea self.canvas.get_tk_widget().pack() self.cid = self.f.canvas.mpl_connect('button_press_event', self._mouseButtonClicked) self.mwe = self.f.canvas.mpl_connect('scroll_event', self._mouseWheel) self.toolbar = NavigationToolbar2TkAgg(self.canvas, self.plotFrame) self.toolbar.update() self.canvas._tkcanvas.pack(side=tk.TOP, fill=tk.BOTH, expand=True) def _quit(): # stops main loop self.root.quit() # this is necessary on Windows to prevent # Fatal Python Error: PyEval_RestoreThread: NULL tstate self.root.destroy() self.quitButton = tk.Button(master=self.pointFrame, text='Quit', command=_quit) self.quitButton.pack(side=tk.BOTTOM, fill=tk.X)