def calc(*args): progress.visible = True if monochromatic_toggle.value is True: psf = instrument.calcPSF( monochromatic=monochromatic_wavelength.value * 1e-6, display=True, outfile=OUTPUT_FILENAME, overwrite=True) else: source = poppy.specFromSpectralType(source_selection.value) _log.debug("Got source type {}: {}".format(source_selection.value, source)) psf = instrument.calcPSF(source=source, display=True, outfile=OUTPUT_FILENAME, overwrite=True) fig, (ax_oversamp, ax_detsamp) = plt.subplots(1, 2, figsize=(12, 4)) title1 = "PSF sim for {}, {}\n".format(instrument.name, instrument.filter) poppy.display_PSF(psf, ax=ax_oversamp, title=title1 + "Oversampled PSF") poppy.display_PSF(psf, ax=ax_detsamp, ext='DET_SAMP', title=title1 + 'Detector pixel sampled PSF') progress.visible = None download_link.visible = True
def calc(*args): progress.visible = True if monochromatic_toggle.value is True: psf = instrument.calc_psf( monochromatic=monochromatic_wavelength.value * 1e-6, display=True, outfile=OUTPUT_FILENAME, overwrite=True ) else: source = poppy.specFromSpectralType(source_selection.value) _log.debug("Got source type {}: {}".format(source_selection.value, source)) psf = instrument.calc_psf( source=source, display=True, outfile=OUTPUT_FILENAME, overwrite=True ) fig, (ax_oversamp, ax_detsamp) = plt.subplots(1, 2,figsize=(12, 4)) title1 = "PSF sim for {}, {}\n".format(instrument.name, instrument.filter) poppy.display_PSF(psf, ax=ax_oversamp, title=title1+"Oversampled PSF") poppy.display_PSF(psf, ax=ax_detsamp, ext='DET_SAMP', title=title1+'Detector pixel sampled PSF') progress.visible = None download_link.visible = True
def ev_plotspectrum(self): "Event handler for Plot Spectrum " self._updateFromGUI() print("Spectral type is "+self.sptype) print("Selected instrument tab is "+self.iname) print("Selected instrument filter is "+self.filter) plt.clf() ax1 = plt.subplot(311) spectrum = poppy.specFromSpectralType(self.sptype) synplot(spectrum) ax1.set_ybound(1e-6, 1e8) # hard coded for now ax1.yaxis.set_major_locator(matplotlib.ticker.LogLocator(base=1000)) legend_font = matplotlib.font_manager.FontProperties(size=10) ax1.legend(loc='lower right', prop=legend_font) ax2 = plt.subplot(312, sharex=ax1) ax2.set_ybound(0,1.1) band = self.inst._getSynphotBandpass(self.inst.filter) #pysynphot.ObsBandpass(obsname) band.name = "%s %s" % (self.iname, self.inst.filter) synplot(band) #, **kwargs) legend_font = matplotlib.font_manager.FontProperties(size=10) plt.legend(loc='lower right', prop=legend_font) ax2.set_ybound(0,1.1) ax3 = plt.subplot(313, sharex=ax1) if self.nlambda is None: # Automatically determine number of appropriate wavelengths. # Make selection based on filter configuration file try: nlambda = self.inst._filters[self.filter].default_nlambda except KeyError: nlambda=10 else: nlambda = self.nlambda ax1.set_xbound(0.1, 100) plt.draw() waves, weights = self.inst._getWeights(spectrum, nlambda=nlambda) wave_step = waves[1]-waves[0] plot_waves = np.concatenate( ([waves[0]-wave_step], waves, [waves[-1]+wave_step])) * 1e6 plot_weights = np.concatenate(([0], weights,[0])) plt.ylabel("Weight") plt.xlabel("Wavelength [$\mu$m]") ax3.plot(plot_waves, plot_weights, drawstyle='steps-mid') ax1.set_xbound(0.1, 100) self._refresh_window()
def ev_plotspectrum(self): "Event handler for Plot Spectrum " self._updateFromGUI() print("Spectral type is "+self.sptype) print("Selected instrument tab is "+self.iname) print("Selected instrument filter is "+self.filter) plt.clf() ax1 = plt.subplot(311) spectrum = poppy.specFromSpectralType(self.sptype) synplot(spectrum) ax1.set_ybound(1e-6, 1e8) # hard coded for now ax1.yaxis.set_major_locator(matplotlib.ticker.LogLocator(base=1000)) legend_font = matplotlib.font_manager.FontProperties(size=10) ax1.legend(loc='lower right', prop=legend_font) ax2 = plt.subplot(312, sharex=ax1) ax2.set_ybound(0,1.1) band = self.inst._get_synphot_bandpass(self.inst.filter) #pysynphot.ObsBandpass(obsname) band.name = "%s %s" % (self.iname, self.inst.filter) synplot(band) #, **kwargs) legend_font = matplotlib.font_manager.FontProperties(size=10) plt.legend(loc='lower right', prop=legend_font) ax2.set_ybound(0,1.1) ax3 = plt.subplot(313, sharex=ax1) if self.nlambda is None: # Automatically determine number of appropriate wavelengths. # Make selection based on filter configuration file try: nlambda = self.inst._filters[self.filter].default_nlambda except KeyError: nlambda=10 else: nlambda = self.nlambda ax1.set_xbound(0.1, 100) plt.draw() waves, weights = self.inst._getWeights(spectrum, nlambda=nlambda) wave_step = waves[1]-waves[0] plot_waves = np.concatenate( ([waves[0]-wave_step], waves, [waves[-1]+wave_step])) * 1e6 plot_weights = np.concatenate(([0], weights,[0])) plt.ylabel("Weight") plt.xlabel("Wavelength [$\mu$m]") ax3.plot(plot_waves, plot_weights, drawstyle='steps-mid') ax1.set_xbound(0.1, 100) self._refresh_window()
def addPointSource(self, sptype_or_spectrum, name="unnamed source", separation=0.0, PA=0.0, normalization=None): """ Add a point source to the list for a given scene Parameters ----------- sptype_or_spectrum : string or pysynphot.Spectrum spectrum of the source name : str descriptive string separation : float arcsec PA : float deg from N normalization : scalar or tuple TBD Simple version: this is a float to multiply the PSF by. Complex version: Probably tuple of arguments to spectrum.renorm(). How normalization works: First the PSF for that source is calculated, using calcPSF(norm='first') i.e. the input intensity through the telescope pupil is set to 1. The resulting output PSF total counts will be proportional to the throughput through the OTE+SI (including filters, coronagraphs etc) Then we apply the normalization: 1) if it's just a number, we just multiply by it. 2) if it's something else: Then we use a separate bandpass object and parameters passed in here to figure out the overall normalization, and apply that as a multiplicative factor to the resulting PSF itself? """ if type(sptype_or_spectrum) is str: spectrum = poppy.specFromSpectralType(sptype_or_spectrum) else: spectrum = sptype_or_spectrum self.sources.append({ 'spectrum': sptype_or_spectrum, 'separation': separation, 'PA': PA, 'normalization': normalization, 'name': name })
def ev_calc_psf(self): "Event handler for PSF Calculations" self._updateFromGUI() if _HAS_PYSYNPHOT: source = poppy.specFromSpectralType(self.sptype) else: source=None # generic flat spectrum self.PSF_HDUlist = self.inst.calc_psf(source=source, detector_oversample= self.detector_oversampling, fft_oversample=self.fft_oversampling, fov_arcsec = self.FOV, nlambda = self.nlambda, display=True) #self.PSF_HDUlist.display() for w in ['Display PSF', 'Display profiles', 'Save PSF As...']: self.widgets[w].state(['!disabled']) self._refresh_window() _log.info("PSF calculation complete")
def ev_calcPSF(self): "Event handler for PSF Calculations" self._updateFromGUI() if _HAS_PYSYNPHOT: source = poppy.specFromSpectralType(self.sptype) else: source=None # generic flat spectrum self.PSF_HDUlist = self.inst.calcPSF(source=source, detector_oversample= self.detector_oversampling, fft_oversample=self.fft_oversampling, fov_arcsec = self.FOV, nlambda = self.nlambda, display=True) #self.PSF_HDUlist.display() for w in ['Display PSF', 'Display profiles', 'Save PSF As...']: self.widgets[w].state(['!disabled']) self._refresh_window() _log.info("PSF calculation complete")
def calc(*args): progress.visible = True if monochromatic_toggle.value is True: psf = instrument.calcPSF( monochromatic=monochromatic_wavelength.value * 1e-6, display=True, outfile=OUTPUT_FILENAME, overwrite=True) else: source = poppy.specFromSpectralType(source_selection.value) _log.debug("Got source type {}: {}".format(source_selection.value, source)) psf = instrument.calcPSF(source=source, display=True, outfile=OUTPUT_FILENAME, overwrite=True) fig, (ax_oversamp, ax_detsamp) = plt.subplots(1, 2) poppy.display_PSF(psf, ax=ax_oversamp) poppy.display_PSF(psf, ax=ax_detsamp, ext='DET_SAMP') progress.visible = None download_link.visible = True
def addPointSource(self, sptype_or_spectrum, name="unnamed source", separation=0.0, PA=0.0, normalization=None): """ Add a point source to the list for a given scene Parameters ----------- sptype_or_spectrum : string or pysynphot.Spectrum spectrum of the source name : str descriptive string separation : float arcsec PA : float deg from N normalization : scalar or tuple TBD Simple version: this is a float to multiply the PSF by. Complex version: Probably tuple of arguments to spectrum.renorm(). How normalization works: First the PSF for that source is calculated, using calcPSF(norm='first') i.e. the input intensity through the telescope pupil is set to 1. The resulting output PSF total counts will be proportional to the throughput through the OTE+SI (including filters, coronagraphs etc) Then we apply the normalization: 1) if it's just a number, we just multiply by it. 2) if it's something else: Then we use a separate bandpass object and parameters passed in here to figure out the overall normalization, and apply that as a multiplicative factor to the resulting PSF itself? """ if type(sptype_or_spectrum) is str: spectrum = poppy.specFromSpectralType(sptype_or_spectrum) else: spectrum = sptype_or_spectrum self.sources.append( {'spectrum': sptype_or_spectrum, 'separation': separation, 'PA': PA, 'normalization': normalization, 'name': name})
def calc(*args): progress.visible = True if monochromatic_toggle.value is True: psf = instrument.calc_psf( monochromatic=monochromatic_wavelength.value * 1e-6, display=True, outfile=OUTPUT_FILENAME, overwrite=True ) else: source = poppy.specFromSpectralType(source_selection.value) _log.debug("Got source type {}: {}".format(source_selection.value, source)) psf = instrument.calc_psf( source=source, display=True, outfile=OUTPUT_FILENAME, overwrite=True ) fig, (ax_oversamp, ax_detsamp) = plt.subplots(1, 2) poppy.display_PSF(psf, ax=ax_oversamp) poppy.display_PSF(psf, ax=ax_detsamp, ext='DET_SAMP') progress.visible = None download_link.visible = True
def combine_transmission(filt, SRC): """ Short Summary ------------- Combine the transmission of the filter and the spectral type Parameters ---------- filt: 1D array bandpass SRC: string spectral type string, e.g. A0V Returns ------- transmissionlist: list """ filt_wls = np.zeros(len(filt)) filt_wght = np.zeros(len(filt)) for ii in range(len(filt)): filt_wls[ii] = filt[ii][1] # in m filt_wght[ii] = filt[ii][0] src = specFromSpectralType(SRC) # converts t angstrom for pysynphot src = src.resample(np.array(filt_wls) * 1.0e10) specwavl, specwghts = src.getArrays() totalwght = specwghts * filt_wght transmissionlist = [] for ii in range(len(filt_wls)): transmissionlist.append((totalwght[ii], filt_wls[ii])) return transmissionlist
def show_notebook_interface_wfi(instrument): # Widget related imports. # (Currently not a hard dependency for the full webbpsf package, so we import # within the function.) import ipywidgets as widgets from IPython.display import display, clear_output from matplotlib import pyplot as plt try: import pysynphot except ImportError: raise ImportError("For now, PySynphot must be installed to use the notebook interface") # Clean up some warnings we know about so as not to scare the users import warnings from matplotlib.cbook import MatplotlibDeprecationWarning warnings.simplefilter('ignore', MatplotlibDeprecationWarning) warnings.simplefilter('ignore', fits.verify.VerifyWarning) def make_binding_for_attribute(attribute): def callback(trait_name, new_value): setattr(instrument, attribute, new_value) return callback filter_selection = widgets.ToggleButtons( options=instrument.filter_list, value=instrument.filter, description='Filter:' ) filter_selection.on_trait_change( make_binding_for_attribute('filter'), name='selected_label' ) display(filter_selection) monochromatic_wavelength = widgets.BoundedFloatText( value=0.76, min=0.6, max=2.0, ) monochromatic_wavelength.disabled = True monochromatic_toggle = widgets.Checkbox(description='Monochromatic calculation?') def update_monochromatic(trait_name, new_value): filter_selection.disabled = new_value monochromatic_wavelength.disabled = not new_value monochromatic_toggle.on_trait_change(update_monochromatic, name='value') display(widgets.HTML(value='''<p style="padding: 1em 0;"> <span style="font-style:italic; font-size:1.0em"> Monochromatic calculations can be performed for any wavelength in the 0.6 to 2.0 µm range. </span></p>''')) # kludge monochromatic_controls = widgets.HBox(children=( monochromatic_toggle, widgets.HTML(value='<span style="display: inline-block; width: 0.6em;"></span>'), monochromatic_wavelength, widgets.HTML(value='<span style="display: inline-block; width: 0.25em;"></span> µm '), )) display(monochromatic_controls) display(widgets.HTML(value="<hr>")) source_selection = widgets.Select( options=poppy.specFromSpectralType('', return_list=True), value='G0V', description="Source spectrum" ) display(source_selection) display(widgets.HTML(value="<hr>")) sca_selection = widgets.Dropdown( options=instrument.detector_list, value=instrument.detector, description='Detector:' ) sca_selection.on_trait_change( make_binding_for_attribute('detector'), name='selected_label' ) display(sca_selection) detector_field_points = [ ('Top left', (4.0, 4092.0)), ('Bottom left', (4.0, 4.0)), ('Center', (2048.0, 2048.0)), ('Top right', (4092.0, 4092.0)), ('Bottom right', (4092.0, 4.0)), ] # enforce ordering of buttons detector_field_point_labels = [a[0] for a in detector_field_points] detector_field_points = dict(detector_field_points) def set_field_position(trait_name, new_value): instrument.detector_position = detector_field_points[new_value] field_position = widgets.ToggleButtons(options=detector_field_point_labels, value='Center', description='Detector field point:') field_position.on_trait_change(set_field_position, name='selected_label') display(field_position) calculate_button = widgets.Button( description="Calculate PSF", width='10em', color='white', background_color='#00c403', border_color='#318732' ) display_osys_button = widgets.Button( description="Display Optical System", width='13em', color='white', background_color='#005fc4', border_color='#224A75' ) clear_button = widgets.Button( description="Clear Output", width='10em', color='white', background_color='#ed4747', border_color='#911C1C' ) progress = widgets.HTML(value='<progress>') OUTPUT_FILENAME = 'psf.fits' DOWNLOAD_BUTTON_HTML = """ <a class="btn btn-info" href="files/{}" target="_blank"> Download FITS image from last calculation </a> """ download_link = widgets.HTML(value=DOWNLOAD_BUTTON_HTML.format(OUTPUT_FILENAME)) def disp(*args): progress.visible = True plt.figure(figsize=(12, 8)) instrument.display() progress.visible = None def calc(*args): progress.visible = True if monochromatic_toggle.value is True: psf = instrument.calc_psf( monochromatic=monochromatic_wavelength.value * 1e-6, display=True, outfile=OUTPUT_FILENAME, overwrite=True ) else: source = poppy.specFromSpectralType(source_selection.value) _log.debug("Got source type {}: {}".format(source_selection.value, source)) psf = instrument.calc_psf( source=source, display=True, outfile=OUTPUT_FILENAME, overwrite=True ) fig, (ax_oversamp, ax_detsamp) = plt.subplots(1, 2) poppy.display_PSF(psf, ax=ax_oversamp) poppy.display_PSF(psf, ax=ax_detsamp, ext='DET_SAMP') progress.visible = None download_link.visible = True def clear(*args): clear_output() progress.visible = None download_link.visible = None calculate_button.on_click(calc) display_osys_button.on_click(disp) clear_button.on_click(clear) display(widgets.HTML(value="<br/>")) # kludge buttons = widgets.HBox(children=[calculate_button, display_osys_button, clear_button]) display(buttons) # Insert the progress bar, hidden by default display(progress) progress.visible = None # and the download link display(download_link) download_link.visible = None
def show_notebook_interface_jwst(instrument): """ Show Jupyter Notebook interface, for a JWST instrument Parameters ------------- instrument : string or object either a webbpsf instrument object, e.g. `NIRCam()` or the string name of an instrument. Example -------- nc = webbpsf.NIRCam() webbpsf.show_notebook_interface(nc) """ # Widget related imports. # (Currently not a hard dependency for the full webbpsf package, so we import # within the function.) import ipywidgets as widgets from IPython.display import display, clear_output from matplotlib import pyplot as plt if isinstance(instrument, str): instrument = Instrument(instrument) try: import pysynphot except ImportError: raise ImportError("For now, PySynphot must be installed to use the notebook interface") # Clean up some warnings we know about so as not to scare the users import warnings from matplotlib.cbook import MatplotlibDeprecationWarning warnings.simplefilter('ignore', MatplotlibDeprecationWarning) warnings.simplefilter('ignore', fits.verify.VerifyWarning) def make_binding_for_attribute(attribute): def callback(trait_name, new_value): if new_value == 'None': setattr(instrument, attribute, None) else: setattr(instrument, attribute, new_value) return callback display(widgets.HTML(value='''<p style="padding: 1em 0;"> <span style="font-weight:bold; font-size:1.0em"> Notebook Interface for {} PSF sims </span></p>'''.format(instrument.name))) filter_selection = widgets.Dropdown( options=instrument.filter_list, value=instrument.filter, description='Filter:') filter_selection.on_trait_change( make_binding_for_attribute('filter'), name='selected_label' ) display(filter_selection) wl_bounds = (5., 30., 10.0) if instrument.name=='MIRI' else (0.6, 5.3, 2.0) monochromatic_wavelength = widgets.BoundedFloatText( value=wl_bounds[2], min=wl_bounds[0], max=wl_bounds[1], ) monochromatic_wavelength.disabled = True monochromatic_toggle = widgets.Checkbox(description='Monochromatic calculation?') def update_monochromatic(trait_name, new_value): filter_selection.disabled = new_value monochromatic_wavelength.disabled = not new_value monochromatic_toggle.on_trait_change(update_monochromatic, name='value') display(widgets.HTML(value='''<p style="padding: 1em 0;"> <span style="font-style:italic; font-size:1.0em"> Monochromatic calculations can be performed for any wavelength in the {} to {} µm range. </span></p>'''.format(*wl_bounds))) # kludge monochromatic_controls = widgets.HBox(children=( monochromatic_toggle, widgets.HTML(value='<span style="display: inline-block; width: 0.6em;"></span>'), monochromatic_wavelength, widgets.HTML(value='<span style="display: inline-block; width: 0.25em;"></span> µm '), )) display(monochromatic_controls) display(widgets.HTML(value="<hr>")) if instrument.name != 'FGS': image_selection = widgets.Dropdown( options=['None'] + instrument.image_mask_list, value=str(instrument.image_mask), description='Image Mask:') image_selection.on_trait_change( make_binding_for_attribute('image_mask'), name='selected_label' ) display(image_selection) pupil_selection = widgets.Dropdown( options=['None'] + instrument.pupil_mask_list, value=str(instrument.pupil_mask), description='Pupil Mask: ') pupil_selection.on_trait_change( make_binding_for_attribute('pupil_mask'), name='selected_label' ) display(pupil_selection) display(widgets.HTML(value="<hr>")) source_selection = widgets.Dropdown( options=poppy.specFromSpectralType('', return_list=True), value='G0V', description="Source spectrum" ) display(source_selection) display(widgets.HTML(value="<hr>")) calculate_button = widgets.Button( description="Calculate PSF", width='10em', color='white', background_color='#00c403', border_color='#318732' ) display_osys_button = widgets.Button( description="Display Optical System", width='13em', color='white', background_color='#005fc4', border_color='#224A75' ) clear_button = widgets.Button( description="Clear Output", width='10em', color='white', background_color='#ed4747', border_color='#911C1C' ) progress = widgets.HTML(value='<progress>') OUTPUT_FILENAME = 'psf.fits' DOWNLOAD_BUTTON_HTML = """ <a class="btn btn-info" href="files/{}" target="_blank"> Download FITS image from last calculation </a> """ download_link = widgets.HTML(value=DOWNLOAD_BUTTON_HTML.format(OUTPUT_FILENAME)) def disp(*args): progress.visible = True plt.figure(figsize=(12, 8)) instrument.display() progress.visible = None def calc(*args): progress.visible = True if monochromatic_toggle.value is True: psf = instrument.calc_psf( monochromatic=monochromatic_wavelength.value * 1e-6, display=True, outfile=OUTPUT_FILENAME, overwrite=True ) else: source = poppy.specFromSpectralType(source_selection.value) _log.debug("Got source type {}: {}".format(source_selection.value, source)) psf = instrument.calc_psf( source=source, display=True, outfile=OUTPUT_FILENAME, overwrite=True ) fig, (ax_oversamp, ax_detsamp) = plt.subplots(1, 2,figsize=(12, 4)) title1 = "PSF sim for {}, {}\n".format(instrument.name, instrument.filter) poppy.display_PSF(psf, ax=ax_oversamp, title=title1+"Oversampled PSF") poppy.display_PSF(psf, ax=ax_detsamp, ext='DET_SAMP', title=title1+'Detector pixel sampled PSF') progress.visible = None download_link.visible = True def clear(*args): clear_output() progress.visible = None download_link.visible = None calculate_button.on_click(calc) display_osys_button.on_click(disp) clear_button.on_click(clear) display(widgets.HTML(value="<br/>")) # kludge buttons = widgets.HBox(children=[calculate_button, display_osys_button, clear_button]) display(buttons) # Insert the progress bar, hidden by default display(progress) progress.visible = None # and the download link display(download_link) download_link.visible = None
def _create_widgets(self): """Create a nice GUI using the enhanced widget set provided by the ttk extension to Tkinter, available in Python 2.7 or newer """ #---- create the GUIs insts = ['NIRCam', 'NIRSpec','NIRISS', 'MIRI', 'FGS'] self.root = tk.Tk() self.root.geometry('+50+50') self.root.title("James Webb Space Telescope PSF Calculator") frame = ttk.Frame(self.root) #frame = ttk.Frame(self.root, padx=10,pady=10) #ttk.Label(frame, text='James Webb PSF Calculator' ).grid(row=0) #-- star lf = ttk.LabelFrame(frame, text='Source Properties') if _HAS_PYSYNPHOT: self._add_labeled_dropdown("SpType", lf, label=' Spectral Type:', values=poppy.specFromSpectralType("",return_list=True), default='G0V', width=25, position=(0,0), sticky='W') ttk.Button(lf, text='Plot spectrum', command=self.ev_plotspectrum).grid(row=0,column=2,sticky='E',columnspan=4) r = 1 fr2 = ttk.Frame(lf) self._add_labeled_entry("source_off_r", fr2, label=' Source Position: r=', value='0.0', width=5, position=(r,0), sticky='W') self._add_labeled_entry("source_off_theta", fr2, label='arcsec, PA=', value='0', width=3, position=(r,2), sticky='W') self.vars["source_off_centerpos"] = tk.StringVar() self.vars["source_off_centerpos"].set('corner') ttk.Label(fr2, text='deg, centered on ' ).grid(row=r, column=4) pixel = ttk.Radiobutton(fr2, text='pixel', variable=self.vars["source_off_centerpos"], value='pixel') pixel.grid(row=r, column=5) corner = ttk.Radiobutton(fr2, text='corner', variable=self.vars["source_off_centerpos"], value='corner') corner.grid(row=r, column=6) fr2.grid(row=r, column=0, columnspan=5, sticky='W') lf.columnconfigure(2, weight=1) lf.grid(row=1, sticky='E,W', padx=10,pady=5) #-- instruments lf = ttk.LabelFrame(frame, text='Instrument Config') notebook = ttk.Notebook(lf) self.widgets['tabset'] = notebook notebook.pack(fill='both') for iname,i in zip(insts, range(len(insts))): page = ttk.Frame(notebook) notebook.add(page,text=iname) notebook.select(i) # make it active self.widgets[notebook.select()] = iname # save reverse lookup from meaningless widget "name" to string name if iname =='NIRCam': lframe = ttk.Frame(page) ttk.Label(lframe, text='Configuration Options for '+iname+', module: ').grid(row=0, column=0, sticky='W') mname='NIRCam module' self.vars[mname] = tk.StringVar() self.widgets[mname] = ttk.Combobox(lframe, textvariable=self.vars[mname], width=2, state='readonly') self.widgets[mname].grid(row=0,column=1, sticky='W') self.widgets[mname]['values'] = ['A','B'] self.widgets[mname].set('A') lframe.grid(row=0, columnspan=2, sticky='W') else: ttk.Label(page, text='Configuration Options for '+iname+" ").grid(row=0, columnspan=2, sticky='W') ttk.Button(page, text='Display Optics', command=self.ev_displayOptics ).grid(column=2, row=0, sticky='E', columnspan=3) #if iname != 'TFI': self._add_labeled_dropdown(iname+"_filter", page, label=' Filter:', values=self.instrument[iname].filter_list, default=self.instrument[iname].filter, width=12, position=(1,0), sticky='W') #else: #ttk.Label(page, text='Etalon wavelength: ' , state='disabled').grid(row=1, column=0, sticky='W') #self.widgets[iname+"_wavelen"] = ttk.Entry(page, width=7) #, disabledforeground="#A0A0A0") #self.widgets[iname+"_wavelen"].insert(0, str(self.instrument[iname].etalon_wavelength)) #self.widgets[iname+"_wavelen"].grid(row=1, column=1, sticky='W') #ttk.Label(page, text=' um' ).grid(row=1, column=2, sticky='W') #self.vars[iname+"_filter"] = tk.StringVar() #self.widgets[iname+"_filter"] = ttk.Combobox(page,textvariable =self.vars[iname+"_filter"], width=10, state='readonly') #self.widgets[iname+"_filter"]['values'] = self.instrument[iname].filter_list #self.widgets[iname+"_filter"].set(self.instrument[iname].filter) #self.widgets[iname+"_filter"]['readonly'] = True #ttk.Label(page, text=' Filter: ' ).grid(row=1, column=0) #self.widgets[iname+"_filter"].grid(row=1, column=1) #if hasattr(self.instrument[iname], 'ifu_wavelength'): if iname == 'NIRSpec' or iname =='MIRI': fr2 = ttk.Frame(page) #label = 'IFU' if iname !='TFI' else 'TF' ttk.Label(fr2, text=' IFU wavelen: ', state='disabled').grid(row=0, column=0) self.widgets[iname+"_ifu_wavelen"] = ttk.Entry(fr2, width=5) #, disabledforeground="#A0A0A0") self.widgets[iname+"_ifu_wavelen"].insert(0, str(self.instrument[iname].monochromatic)) self.widgets[iname+"_ifu_wavelen"].grid(row=0, column=1) self.widgets[iname+"_ifu_wavelen"].state(['disabled']) ttk.Label(fr2, text=' um' , state='disabled').grid(row=0, column=2) fr2.grid(row=1,column=2, columnspan=6, sticky='E') iname2 = iname+"" # need to make a copy so the following lambda function works right: self.widgets[iname+"_filter"].bind('<<ComboboxSelected>>', lambda e: self.ev_update_ifu_label(iname2)) if len(self.instrument[iname].image_mask_list) >0 : masks = self.instrument[iname].image_mask_list masks.insert(0, "") self._add_labeled_dropdown(iname+"_coron", page, label=' Coron:', values=masks, width=12, position=(2,0), sticky='W') #self.vars[iname+"_coron"] = tk.StringVar() #self.widgets[iname+"_coron"] = ttk.Combobox(page,textvariable =self.vars[iname+"_coron"], width=10, state='readonly') #self.widgets[iname+"_coron"]['values'] = masks #ttk.Label(page, text=' Coron: ' ).grid(row=2, column=0) #self.widgets[iname+"_coron"].set(self.widgets[iname+"_coron"]['values'][0]) #self.widgets[iname+"_coron"].grid(row=2, column=1) #fr2 = ttk.Frame(page) #self.vars[iname+"_cor_off_r"] = tk.StringVar() #self.vars[iname+"_cor_off_theta"] = tk.StringVar() #ttk.Label(fr2, text='target offset: r=' ).grid(row=2, column=4) #self.widgets[iname+"_cor_off_r"] = ttk.Entry(fr2,textvariable =self.vars[iname+"_cor_off_r"], width=5) #self.widgets[iname+"_cor_off_r"].insert(0,"0.0") #self.widgets[iname+"_cor_off_r"].grid(row=2, column=5) #ttk.Label(fr2, text='arcsec, PA=' ).grid(row=2, column=6) #self.widgets[iname+"_cor_off_theta"] = ttk.Entry(fr2,textvariable =self.vars[iname+"_cor_off_theta"], width=3) #self.widgets[iname+"_cor_off_theta"].insert(0,"0") #self.widgets[iname+"_cor_off_theta"].grid(row=2, column=7) #ttk.Label(fr2, text='deg' ).grid(row=2, column=8) #fr2.grid(row=2,column=3, sticky='W') if len(self.instrument[iname].image_mask_list) >0 : masks = self.instrument[iname].pupil_mask_list masks.insert(0, "") self._add_labeled_dropdown(iname+"_pupil", page, label=' Pupil:', values=masks, width=12, position=(3,0), sticky='W') fr2 = ttk.Frame(page) self._add_labeled_entry(iname+"_pupilshift_x", fr2, label=' pupil shift in X:', value='0', width=3, position=(3,4), sticky='W') self._add_labeled_entry(iname+"_pupilshift_y", fr2, label=' Y:', value='0', width=3, position=(3,6), sticky='W') ttk.Label(fr2, text='% of pupil' ).grid(row=3, column=8) fr2.grid(row=3,column=3, sticky='W') ttk.Label(page, text='Configuration Options for the OTE').grid(row=4, columnspan=2, sticky='W') fr2 = ttk.Frame(page) opd_list = self.instrument[iname].opd_list opd_list.insert(0,"Zero OPD (perfect)") #if os.getenv("WEBBPSF_ITM") or 1: if self._enable_opdserver: opd_list.append("OPD from ITM Server") default_opd = self.instrument[iname].pupilopd if self.instrument[iname].pupilopd is not None else "Zero OPD (perfect)" self._add_labeled_dropdown(iname+"_opd", fr2, label=' OPD File:', values=opd_list, default=default_opd, width=21, position=(0,0), sticky='W') self._add_labeled_dropdown(iname+"_opd_i", fr2, label=' # ', values= [str(i) for i in range(10)], width=3, position=(0,2), sticky='W') self.widgets[iname+"_opd_label"] = ttk.Label(fr2, text=' 0 nm RMS ', width=35) self.widgets[iname+"_opd_label"].grid( column=4,sticky='W', row=0) self.widgets[iname+"_opd"].bind('<<ComboboxSelected>>', lambda e: self.ev_update_OPD_labels() ) # The below code does not work, and I can't tell why. This only ever has iname = 'FGS' no matter which instrument. # So instead brute-force it with the above to just update all 5. #lambda e: self.ev_update_OPD_label(self.widgets[iname+"_opd"], self.widgets[iname+"_opd_label"], iname) ) ttk.Button(fr2, text='Display', command=self.ev_displayOPD).grid(column=5,sticky='E',row=0) fr2.grid(row=5, column=0, columnspan=4,sticky='S') # ITM interface here - build the widgets now but they will be hidden by default until the ITM option is selected fr2 = ttk.Frame(page) self._add_labeled_entry(iname+"_coords", fr2, label=' Source location:', value='0, 0', width=12, position=(1,0), sticky='W') units_list = ['V1,V2 coords', 'detector pixels'] self._add_labeled_dropdown(iname+"_coord_units", fr2, label='in:', values=units_list, default=units_list[0], width=11, position=(1,2), sticky='W') choose_list=['', 'SI center', 'SI upper left corner', 'SI upper right corner', 'SI lower left corner', 'SI lower right corner'] self._add_labeled_dropdown(iname+"_coord_choose", fr2, label='or select:', values=choose_list, default=choose_list[0], width=21, position=(1,4), sticky='W') ttk.Label(fr2, text=' ITM output:').grid(row=2, column=0, sticky='W') self.widgets[iname+"_itm_output"] = ttk.Label(fr2, text=' - no file available yet -') self.widgets[iname+"_itm_output"].grid(row=2, column=1, columnspan=4, sticky='W') ttk.Button(fr2, text='Access ITM...', command=self.ev_launch_ITM_dialog).grid(column=5,sticky='E',row=2) fr2.grid(row=6, column=0, columnspan=4,sticky='SW') self.widgets[iname+"_itm_coords"] = fr2 self.ev_update_OPD_labels() lf.grid(row=2, sticky='E,W', padx=10, pady=5) notebook.select(0) lf = ttk.LabelFrame(frame, text='Calculation Options') r =0 self._add_labeled_entry('FOV', lf, label='Field of View:', width=3, value='5', postlabel='arcsec/side', position=(r,0)) r+=1 self._add_labeled_entry('detector_oversampling', lf, label='Output Oversampling:', width=3, value='2', postlabel='x finer than instrument pixels ', position=(r,0)) #self.vars['downsamp'] = tk.BooleanVar() #self.vars['downsamp'].set(True) #self.widgets['downsamp'] = ttk.Checkbutton(lf, text='Save in instr. pixel scale, too?', onvalue=True, offvalue=False,variable=self.vars['downsamp']) #self.widgets['downsamp'].grid(row=r, column=4, sticky='E') output_options=['Oversampled PSF only', 'Oversampled + Detector Res. PSFs', 'Mock full image from JWST DMS'] self._add_labeled_dropdown("output_type", fr2, label='Output format:', values=output_options, default=output_options[1], width=31, position=(r,4), sticky='W') r+=1 self._add_labeled_entry('fft_oversampling', lf, label='Coronagraph FFT Oversampling:', width=3, value='2', postlabel='x finer than Nyquist', position=(r,0)) r+=1 self._add_labeled_entry('nlambda', lf, label='# of wavelengths:', width=3, value='', position=(r,0), postlabel='Leave blank for autoselect') r+=1 self._add_labeled_dropdown("jitter", lf, label='Jitter model:', values= ['Just use OPDs', 'Gaussian - 7 mas rms', 'Gaussian - 30 mas rms'], width=20, position=(r,0), sticky='W', columnspan=2) r+=1 self._add_labeled_dropdown("output_format", lf, label='Output Format:', values= ['Oversampled image','Detector sampled image','Both as FITS extensions', 'Mock JWST DMS Output' ], width=30, position=(r,0), sticky='W', columnspan=2) #self._add_labeled_dropdown("jitter", lf, label='Jitter model:', values= ['Just use OPDs', 'Gaussian blur', 'Accurate yet SLOW grid'], width=20, position=(r,0), sticky='W', columnspan=2) lf.grid(row=4, sticky='E,W', padx=10, pady=5) lf = ttk.Frame(frame) def addbutton(self,lf, text, command, pos, disabled=False): self.widgets[text] = ttk.Button(lf, text=text, command=command ) self.widgets[text].grid(column=pos, row=0, sticky='E') if disabled: self.widgets[text].state(['disabled']) addbutton(self,lf,'Compute PSF', self.ev_calc_psf, 0) addbutton(self,lf,'Display PSF', self.ev_displayPSF, 1, disabled=True) addbutton(self,lf,'Display profiles', self.ev_displayProfiles, 2, disabled=True) addbutton(self,lf,'Save PSF As...', self.ev_SaveAs, 3, disabled=True) addbutton(self,lf,'More options...', self.ev_options, 4, disabled=False) ttk.Button(lf, text='Quit', command=self.quit).grid(column=5, row=0) lf.columnconfigure(2, weight=1) lf.columnconfigure(4, weight=1) lf.grid(row=5, sticky='E,W', padx=10, pady=15) frame.grid(row=0, sticky='N,E,S,W') frame.columnconfigure(0, weight=1) frame.rowconfigure(0, weight=1) self.root.columnconfigure(0, weight=1) self.root.rowconfigure(0, weight=1)
def _create_widgets(self): """Create a nice GUI using the enhanced widget set provided by the ttk extension to Tkinter, available in Python 2.7 or newer """ #---- create the GUIs insts = ['NIRCam', 'NIRSpec', 'NIRISS', 'MIRI', 'FGS'] self.root = tk.Tk() self.root.geometry('+50+50') self.root.title("James Webb Space Telescope PSF Calculator") frame = ttk.Frame(self.root) #frame = ttk.Frame(self.root, padx=10,pady=10) #ttk.Label(frame, text='James Webb PSF Calculator' ).grid(row=0) #-- star lf = ttk.LabelFrame(frame, text='Source Properties') if _HAS_PYSYNPHOT: self._add_labeled_dropdown("SpType", lf, label=' Spectral Type:', values=poppy.specFromSpectralType( "", return_list=True), default='G0V', width=25, position=(0, 0), sticky='W') ttk.Button(lf, text='Plot spectrum', command=self.ev_plotspectrum).grid(row=0, column=2, sticky='E', columnspan=4) r = 1 fr2 = ttk.Frame(lf) self._add_labeled_entry("source_off_r", fr2, label=' Source Position: r=', value='0.0', width=5, position=(r, 0), sticky='W') self._add_labeled_entry("source_off_theta", fr2, label='arcsec, PA=', value='0', width=3, position=(r, 2), sticky='W') self.vars["source_off_centerpos"] = tk.StringVar() self.vars["source_off_centerpos"].set('corner') ttk.Label(fr2, text='deg, centered on ').grid(row=r, column=4) pixel = ttk.Radiobutton(fr2, text='pixel', variable=self.vars["source_off_centerpos"], value='pixel') pixel.grid(row=r, column=5) corner = ttk.Radiobutton(fr2, text='corner', variable=self.vars["source_off_centerpos"], value='corner') corner.grid(row=r, column=6) fr2.grid(row=r, column=0, columnspan=5, sticky='W') lf.columnconfigure(2, weight=1) lf.grid(row=1, sticky='E,W', padx=10, pady=5) #-- instruments lf = ttk.LabelFrame(frame, text='Instrument Config') notebook = ttk.Notebook(lf) self.widgets['tabset'] = notebook notebook.pack(fill='both') for iname, i in zip(insts, range(len(insts))): page = ttk.Frame(notebook) notebook.add(page, text=iname) notebook.select(i) # make it active self.widgets[notebook.select( )] = iname # save reverse lookup from meaningless widget "name" to string name if iname == 'NIRCam': lframe = ttk.Frame(page) ttk.Label(lframe, text='Configuration Options for ' + iname + ', module: ').grid(row=0, column=0, sticky='W') mname = 'NIRCam module' self.vars[mname] = tk.StringVar() self.widgets[mname] = ttk.Combobox( lframe, textvariable=self.vars[mname], width=2, state='readonly') self.widgets[mname].grid(row=0, column=1, sticky='W') self.widgets[mname]['values'] = ['A', 'B'] self.widgets[mname].set('A') lframe.grid(row=0, columnspan=2, sticky='W') else: ttk.Label(page, text='Configuration Options for ' + iname + " ").grid(row=0, columnspan=2, sticky='W') ttk.Button(page, text='Display Optics', command=self.ev_displayOptics).grid(column=2, row=0, sticky='E', columnspan=3) #if iname != 'TFI': self._add_labeled_dropdown( iname + "_filter", page, label=' Filter:', values=self.instrument[iname].filter_list, default=self.instrument[iname].filter, width=12, position=(1, 0), sticky='W') #else: #ttk.Label(page, text='Etalon wavelength: ' , state='disabled').grid(row=1, column=0, sticky='W') #self.widgets[iname+"_wavelen"] = ttk.Entry(page, width=7) #, disabledforeground="#A0A0A0") #self.widgets[iname+"_wavelen"].insert(0, str(self.instrument[iname].etalon_wavelength)) #self.widgets[iname+"_wavelen"].grid(row=1, column=1, sticky='W') #ttk.Label(page, text=' um' ).grid(row=1, column=2, sticky='W') #self.vars[iname+"_filter"] = tk.StringVar() #self.widgets[iname+"_filter"] = ttk.Combobox(page,textvariable =self.vars[iname+"_filter"], width=10, state='readonly') #self.widgets[iname+"_filter"]['values'] = self.instrument[iname].filter_list #self.widgets[iname+"_filter"].set(self.instrument[iname].filter) #self.widgets[iname+"_filter"]['readonly'] = True #ttk.Label(page, text=' Filter: ' ).grid(row=1, column=0) #self.widgets[iname+"_filter"].grid(row=1, column=1) #if hasattr(self.instrument[iname], 'ifu_wavelength'): if iname == 'NIRSpec' or iname == 'MIRI': fr2 = ttk.Frame(page) #label = 'IFU' if iname !='TFI' else 'TF' ttk.Label(fr2, text=' IFU wavelen: ', state='disabled').grid(row=0, column=0) self.widgets[iname + "_ifu_wavelen"] = ttk.Entry( fr2, width=5) #, disabledforeground="#A0A0A0") self.widgets[iname + "_ifu_wavelen"].insert( 0, str(self.instrument[iname].monochromatic)) self.widgets[iname + "_ifu_wavelen"].grid(row=0, column=1) self.widgets[iname + "_ifu_wavelen"].state(['disabled']) ttk.Label(fr2, text=' um', state='disabled').grid(row=0, column=2) fr2.grid(row=1, column=2, columnspan=6, sticky='E') iname2 = iname + "" # need to make a copy so the following lambda function works right: self.widgets[iname + "_filter"].bind( '<<ComboboxSelected>>', lambda e: self.ev_update_ifu_label(iname2)) if len(self.instrument[iname].image_mask_list) > 0: masks = self.instrument[iname].image_mask_list masks.insert(0, "") self._add_labeled_dropdown(iname + "_coron", page, label=' Coron:', values=masks, width=12, position=(2, 0), sticky='W') #self.vars[iname+"_coron"] = tk.StringVar() #self.widgets[iname+"_coron"] = ttk.Combobox(page,textvariable =self.vars[iname+"_coron"], width=10, state='readonly') #self.widgets[iname+"_coron"]['values'] = masks #ttk.Label(page, text=' Coron: ' ).grid(row=2, column=0) #self.widgets[iname+"_coron"].set(self.widgets[iname+"_coron"]['values'][0]) #self.widgets[iname+"_coron"].grid(row=2, column=1) #fr2 = ttk.Frame(page) #self.vars[iname+"_cor_off_r"] = tk.StringVar() #self.vars[iname+"_cor_off_theta"] = tk.StringVar() #ttk.Label(fr2, text='target offset: r=' ).grid(row=2, column=4) #self.widgets[iname+"_cor_off_r"] = ttk.Entry(fr2,textvariable =self.vars[iname+"_cor_off_r"], width=5) #self.widgets[iname+"_cor_off_r"].insert(0,"0.0") #self.widgets[iname+"_cor_off_r"].grid(row=2, column=5) #ttk.Label(fr2, text='arcsec, PA=' ).grid(row=2, column=6) #self.widgets[iname+"_cor_off_theta"] = ttk.Entry(fr2,textvariable =self.vars[iname+"_cor_off_theta"], width=3) #self.widgets[iname+"_cor_off_theta"].insert(0,"0") #self.widgets[iname+"_cor_off_theta"].grid(row=2, column=7) #ttk.Label(fr2, text='deg' ).grid(row=2, column=8) #fr2.grid(row=2,column=3, sticky='W') if len(self.instrument[iname].image_mask_list) > 0: masks = self.instrument[iname].pupil_mask_list masks.insert(0, "") self._add_labeled_dropdown(iname + "_pupil", page, label=' Pupil:', values=masks, width=12, position=(3, 0), sticky='W') fr2 = ttk.Frame(page) self._add_labeled_entry(iname + "_pupilshift_x", fr2, label=' pupil shift in X:', value='0', width=3, position=(3, 4), sticky='W') self._add_labeled_entry(iname + "_pupilshift_y", fr2, label=' Y:', value='0', width=3, position=(3, 6), sticky='W') ttk.Label(fr2, text='% of pupil').grid(row=3, column=8) fr2.grid(row=3, column=3, sticky='W') ttk.Label(page, text='Configuration Options for the OTE').grid( row=4, columnspan=2, sticky='W') fr2 = ttk.Frame(page) opd_list = self.instrument[iname].opd_list opd_list.insert(0, "Zero OPD (perfect)") #if os.getenv("WEBBPSF_ITM") or 1: if self._enable_opdserver: opd_list.append("OPD from ITM Server") default_opd = self.instrument[iname].pupilopd if self.instrument[ iname].pupilopd is not None else "Zero OPD (perfect)" self._add_labeled_dropdown(iname + "_opd", fr2, label=' OPD File:', values=opd_list, default=default_opd, width=21, position=(0, 0), sticky='W') self._add_labeled_dropdown(iname + "_opd_i", fr2, label=' # ', values=[str(i) for i in range(10)], width=3, position=(0, 2), sticky='W') self.widgets[iname + "_opd_label"] = ttk.Label( fr2, text=' 0 nm RMS ', width=35) self.widgets[iname + "_opd_label"].grid(column=4, sticky='W', row=0) self.widgets[iname + "_opd"].bind( '<<ComboboxSelected>>', lambda e: self.ev_update_OPD_labels()) # The below code does not work, and I can't tell why. This only ever has iname = 'FGS' no matter which instrument. # So instead brute-force it with the above to just update all 5. #lambda e: self.ev_update_OPD_label(self.widgets[iname+"_opd"], self.widgets[iname+"_opd_label"], iname) ) ttk.Button(fr2, text='Display', command=self.ev_displayOPD).grid(column=5, sticky='E', row=0) fr2.grid(row=5, column=0, columnspan=4, sticky='S') # ITM interface here - build the widgets now but they will be hidden by default until the ITM option is selected fr2 = ttk.Frame(page) self._add_labeled_entry(iname + "_coords", fr2, label=' Source location:', value='0, 0', width=12, position=(1, 0), sticky='W') units_list = ['V1,V2 coords', 'detector pixels'] self._add_labeled_dropdown(iname + "_coord_units", fr2, label='in:', values=units_list, default=units_list[0], width=11, position=(1, 2), sticky='W') choose_list = [ '', 'SI center', 'SI upper left corner', 'SI upper right corner', 'SI lower left corner', 'SI lower right corner' ] self._add_labeled_dropdown(iname + "_coord_choose", fr2, label='or select:', values=choose_list, default=choose_list[0], width=21, position=(1, 4), sticky='W') ttk.Label(fr2, text=' ITM output:').grid(row=2, column=0, sticky='W') self.widgets[iname + "_itm_output"] = ttk.Label( fr2, text=' - no file available yet -') self.widgets[iname + "_itm_output"].grid(row=2, column=1, columnspan=4, sticky='W') ttk.Button(fr2, text='Access ITM...', command=self.ev_launch_ITM_dialog).grid(column=5, sticky='E', row=2) fr2.grid(row=6, column=0, columnspan=4, sticky='SW') self.widgets[iname + "_itm_coords"] = fr2 self.ev_update_OPD_labels() lf.grid(row=2, sticky='E,W', padx=10, pady=5) notebook.select(0) lf = ttk.LabelFrame(frame, text='Calculation Options') r = 0 self._add_labeled_entry('FOV', lf, label='Field of View:', width=3, value='5', postlabel='arcsec/side', position=(r, 0)) r += 1 self._add_labeled_entry( 'detector_oversampling', lf, label='Output Oversampling:', width=3, value='2', postlabel='x finer than instrument pixels ', position=(r, 0)) #self.vars['downsamp'] = tk.BooleanVar() #self.vars['downsamp'].set(True) #self.widgets['downsamp'] = ttk.Checkbutton(lf, text='Save in instr. pixel scale, too?', onvalue=True, offvalue=False,variable=self.vars['downsamp']) #self.widgets['downsamp'].grid(row=r, column=4, sticky='E') output_options = [ 'Oversampled PSF only', 'Oversampled + Detector Res. PSFs', 'Mock full image from JWST DMS' ] self._add_labeled_dropdown("output_type", fr2, label='Output format:', values=output_options, default=output_options[1], width=31, position=(r, 4), sticky='W') r += 1 self._add_labeled_entry('fft_oversampling', lf, label='Coronagraph FFT Oversampling:', width=3, value='2', postlabel='x finer than Nyquist', position=(r, 0)) r += 1 self._add_labeled_entry('nlambda', lf, label='# of wavelengths:', width=3, value='', position=(r, 0), postlabel='Leave blank for autoselect') r += 1 self._add_labeled_dropdown("jitter", lf, label='Jitter model:', values=[ 'Just use OPDs', 'Gaussian - 7 mas rms', 'Gaussian - 30 mas rms' ], width=20, position=(r, 0), sticky='W', columnspan=2) r += 1 self._add_labeled_dropdown("output_format", lf, label='Output Format:', values=[ 'Oversampled image', 'Detector sampled image', 'Both as FITS extensions', 'Mock JWST DMS Output' ], width=30, position=(r, 0), sticky='W', columnspan=2) #self._add_labeled_dropdown("jitter", lf, label='Jitter model:', values= ['Just use OPDs', 'Gaussian blur', 'Accurate yet SLOW grid'], width=20, position=(r,0), sticky='W', columnspan=2) lf.grid(row=4, sticky='E,W', padx=10, pady=5) lf = ttk.Frame(frame) def addbutton(self, lf, text, command, pos, disabled=False): self.widgets[text] = ttk.Button(lf, text=text, command=command) self.widgets[text].grid(column=pos, row=0, sticky='E') if disabled: self.widgets[text].state(['disabled']) addbutton(self, lf, 'Compute PSF', self.ev_calc_psf, 0) addbutton(self, lf, 'Display PSF', self.ev_displayPSF, 1, disabled=True) addbutton(self, lf, 'Display profiles', self.ev_displayProfiles, 2, disabled=True) addbutton(self, lf, 'Save PSF As...', self.ev_SaveAs, 3, disabled=True) addbutton(self, lf, 'More options...', self.ev_options, 4, disabled=False) ttk.Button(lf, text='Quit', command=self.quit).grid(column=5, row=0) lf.columnconfigure(2, weight=1) lf.columnconfigure(4, weight=1) lf.grid(row=5, sticky='E,W', padx=10, pady=15) frame.grid(row=0, sticky='N,E,S,W') frame.columnconfigure(0, weight=1) frame.rowconfigure(0, weight=1) self.root.columnconfigure(0, weight=1) self.root.rowconfigure(0, weight=1)
def show_notebook_interface_wfi(instrument): # Widget related imports. # (Currently not a hard dependency for the full webbpsf package, so we import # within the function.) import ipywidgets as widgets from IPython.display import display, clear_output from matplotlib import pyplot as plt try: import pysynphot except ImportError: raise ImportError( "For now, PySynphot must be installed to use the notebook interface" ) # Clean up some warnings we know about so as not to scare the users import warnings from matplotlib.cbook import MatplotlibDeprecationWarning warnings.simplefilter('ignore', MatplotlibDeprecationWarning) warnings.simplefilter('ignore', fits.verify.VerifyWarning) def make_binding_for_attribute(attribute): def callback(trait_name, new_value): setattr(instrument, attribute, new_value) return callback filter_selection = widgets.ToggleButtons(options=instrument.filter_list, value=instrument.filter, description='Filter:') filter_selection.on_trait_change(make_binding_for_attribute('filter'), name='selected_label') display(filter_selection) monochromatic_wavelength = widgets.BoundedFloatText( value=0.76, min=0.6, max=2.0, ) monochromatic_wavelength.disabled = True monochromatic_toggle = widgets.Checkbox( description='Monochromatic calculation?') def update_monochromatic(trait_name, new_value): filter_selection.disabled = new_value monochromatic_wavelength.disabled = not new_value monochromatic_toggle.on_trait_change(update_monochromatic, name='value') display( widgets.HTML(value='''<p style="padding: 1em 0;"> <span style="font-style:italic; font-size:1.0em"> Monochromatic calculations can be performed for any wavelength in the 0.6 to 2.0 µm range. </span></p>''')) # kludge monochromatic_controls = widgets.HBox(children=( monochromatic_toggle, widgets.HTML( value='<span style="display: inline-block; width: 0.6em;"></span>' ), monochromatic_wavelength, widgets.HTML( value= '<span style="display: inline-block; width: 0.25em;"></span> µm ' ), )) display(monochromatic_controls) display(widgets.HTML(value="<hr>")) source_selection = widgets.Select(options=poppy.specFromSpectralType( '', return_list=True), value='G0V', description="Source spectrum") display(source_selection) display(widgets.HTML(value="<hr>")) sca_selection = widgets.Dropdown(options=instrument.detector_list, value=instrument.detector, description='Detector:') sca_selection.on_trait_change(make_binding_for_attribute('detector'), name='selected_label') display(sca_selection) detector_field_points = [ ('Top left', (4.0, 4092.0)), ('Bottom left', (4.0, 4.0)), ('Center', (2048.0, 2048.0)), ('Top right', (4092.0, 4092.0)), ('Bottom right', (4092.0, 4.0)), ] # enforce ordering of buttons detector_field_point_labels = [a[0] for a in detector_field_points] detector_field_points = dict(detector_field_points) def set_field_position(trait_name, new_value): instrument.detector_position = detector_field_points[new_value] field_position = widgets.ToggleButtons(options=detector_field_point_labels, value='Center', description='Detector field point:') field_position.on_trait_change(set_field_position, name='selected_label') display(field_position) calculate_button = widgets.Button(description="Calculate PSF", width='10em', color='white', background_color='#00c403', border_color='#318732') display_osys_button = widgets.Button(description="Display Optical System", width='13em', color='white', background_color='#005fc4', border_color='#224A75') clear_button = widgets.Button(description="Clear Output", width='10em', color='white', background_color='#ed4747', border_color='#911C1C') progress = widgets.HTML(value='<progress>') OUTPUT_FILENAME = 'psf.fits' DOWNLOAD_BUTTON_HTML = """ <a class="btn btn-info" href="files/{}" target="_blank"> Download FITS image from last calculation </a> """ download_link = widgets.HTML( value=DOWNLOAD_BUTTON_HTML.format(OUTPUT_FILENAME)) def disp(*args): progress.visible = True plt.figure(figsize=(12, 8)) instrument.display() progress.visible = None def calc(*args): progress.visible = True if monochromatic_toggle.value is True: psf = instrument.calcPSF( monochromatic=monochromatic_wavelength.value * 1e-6, display=True, outfile=OUTPUT_FILENAME, overwrite=True) else: source = poppy.specFromSpectralType(source_selection.value) _log.debug("Got source type {}: {}".format(source_selection.value, source)) psf = instrument.calcPSF(source=source, display=True, outfile=OUTPUT_FILENAME, overwrite=True) fig, (ax_oversamp, ax_detsamp) = plt.subplots(1, 2) poppy.display_PSF(psf, ax=ax_oversamp) poppy.display_PSF(psf, ax=ax_detsamp, ext='DET_SAMP') progress.visible = None download_link.visible = True def clear(*args): clear_output() progress.visible = None download_link.visible = None calculate_button.on_click(calc) display_osys_button.on_click(disp) clear_button.on_click(clear) display(widgets.HTML(value="<br/>")) # kludge buttons = widgets.HBox( children=[calculate_button, display_osys_button, clear_button]) display(buttons) # Insert the progress bar, hidden by default display(progress) progress.visible = None # and the download link display(download_link) download_link.visible = None
def show_notebook_interface_jwst(instrument): """ Show Jupyter Notebook interface, for a JWST instrument Parameters ------------- instrument : string or object either a webbpsf instrument object, e.g. `NIRCam()` or the string name of an instrument. Example -------- nc = webbpsf.NIRCam() webbpsf.show_notebook_interface(nc) """ # Widget related imports. # (Currently not a hard dependency for the full webbpsf package, so we import # within the function.) import ipywidgets as widgets from IPython.display import display, clear_output from matplotlib import pyplot as plt if isinstance(instrument, six.string_types): instrument = Instrument(instrument) try: import pysynphot except ImportError: raise ImportError( "For now, PySynphot must be installed to use the notebook interface" ) # Clean up some warnings we know about so as not to scare the users import warnings from matplotlib.cbook import MatplotlibDeprecationWarning warnings.simplefilter('ignore', MatplotlibDeprecationWarning) warnings.simplefilter('ignore', fits.verify.VerifyWarning) def make_binding_for_attribute(attribute): def callback(trait_name, new_value): if new_value == 'None': setattr(instrument, attribute, None) else: setattr(instrument, attribute, new_value) return callback display( widgets.HTML(value='''<p style="padding: 1em 0;"> <span style="font-weight:bold; font-size:1.0em"> Notebook Interface for {} PSF sims </span></p>'''.format(instrument.name))) filter_selection = widgets.Dropdown(options=instrument.filter_list, value=instrument.filter, description='Filter:') filter_selection.on_trait_change(make_binding_for_attribute('filter'), name='selected_label') display(filter_selection) wl_bounds = (5., 30., 10.0) if instrument.name == 'MIRI' else (0.6, 5.3, 2.0) monochromatic_wavelength = widgets.BoundedFloatText( value=wl_bounds[2], min=wl_bounds[0], max=wl_bounds[1], ) monochromatic_wavelength.disabled = True monochromatic_toggle = widgets.Checkbox( description='Monochromatic calculation?') def update_monochromatic(trait_name, new_value): filter_selection.disabled = new_value monochromatic_wavelength.disabled = not new_value monochromatic_toggle.on_trait_change(update_monochromatic, name='value') display( widgets.HTML(value='''<p style="padding: 1em 0;"> <span style="font-style:italic; font-size:1.0em"> Monochromatic calculations can be performed for any wavelength in the {} to {} µm range. </span></p>'''.format(*wl_bounds))) # kludge monochromatic_controls = widgets.HBox(children=( monochromatic_toggle, widgets.HTML( value='<span style="display: inline-block; width: 0.6em;"></span>' ), monochromatic_wavelength, widgets.HTML( value= '<span style="display: inline-block; width: 0.25em;"></span> µm ' ), )) display(monochromatic_controls) display(widgets.HTML(value="<hr>")) if instrument.name != 'FGS': image_selection = widgets.Dropdown(options=['None'] + instrument.image_mask_list, value=str(instrument.image_mask), description='Image Mask:') image_selection.on_trait_change( make_binding_for_attribute('image_mask'), name='selected_label') display(image_selection) pupil_selection = widgets.Dropdown(options=['None'] + instrument.pupil_mask_list, value=str(instrument.pupil_mask), description='Pupil Mask: ') pupil_selection.on_trait_change( make_binding_for_attribute('pupil_mask'), name='selected_label') display(pupil_selection) display(widgets.HTML(value="<hr>")) source_selection = widgets.Dropdown(options=poppy.specFromSpectralType( '', return_list=True), value='G0V', description="Source spectrum") display(source_selection) display(widgets.HTML(value="<hr>")) calculate_button = widgets.Button(description="Calculate PSF", width='10em', color='white', background_color='#00c403', border_color='#318732') display_osys_button = widgets.Button(description="Display Optical System", width='13em', color='white', background_color='#005fc4', border_color='#224A75') clear_button = widgets.Button(description="Clear Output", width='10em', color='white', background_color='#ed4747', border_color='#911C1C') progress = widgets.HTML(value='<progress>') OUTPUT_FILENAME = 'psf.fits' DOWNLOAD_BUTTON_HTML = """ <a class="btn btn-info" href="files/{}" target="_blank"> Download FITS image from last calculation </a> """ download_link = widgets.HTML( value=DOWNLOAD_BUTTON_HTML.format(OUTPUT_FILENAME)) def disp(*args): progress.visible = True plt.figure(figsize=(12, 8)) instrument.display() progress.visible = None def calc(*args): progress.visible = True if monochromatic_toggle.value is True: psf = instrument.calcPSF( monochromatic=monochromatic_wavelength.value * 1e-6, display=True, outfile=OUTPUT_FILENAME, overwrite=True) else: source = poppy.specFromSpectralType(source_selection.value) _log.debug("Got source type {}: {}".format(source_selection.value, source)) psf = instrument.calcPSF(source=source, display=True, outfile=OUTPUT_FILENAME, overwrite=True) fig, (ax_oversamp, ax_detsamp) = plt.subplots(1, 2, figsize=(12, 4)) title1 = "PSF sim for {}, {}\n".format(instrument.name, instrument.filter) poppy.display_PSF(psf, ax=ax_oversamp, title=title1 + "Oversampled PSF") poppy.display_PSF(psf, ax=ax_detsamp, ext='DET_SAMP', title=title1 + 'Detector pixel sampled PSF') progress.visible = None download_link.visible = True def clear(*args): clear_output() progress.visible = None download_link.visible = None calculate_button.on_click(calc) display_osys_button.on_click(disp) clear_button.on_click(clear) display(widgets.HTML(value="<br/>")) # kludge buttons = widgets.HBox( children=[calculate_button, display_osys_button, clear_button]) display(buttons) # Insert the progress bar, hidden by default display(progress) progress.visible = None # and the download link display(download_link) download_link.visible = None