def main(args=None): """ This is the main function called by the `velplot` script. """ from astropy.utils.compat import argparse from astropy.extern.configobj import configobj, validate from pkg_resources import resource_stream parser = argparse.ArgumentParser( description='Creates a stacked velocity plot.\nTo dump a default ' 'configuration file: velplot -d\nTo dump an extended ' 'default configuration file: velplot -dd', formatter_class=argparse.RawTextHelpFormatter) parser.add_argument('config', help='path to the configuration file') config = resource_stream(__name__, '/config/velplot.cfg') config_extended = resource_stream(__name__, '/config/velplot_extended.cfg') spec = resource_stream(__name__, '/config/velplot_specification.cfg') if len(sys.argv) > 1: if sys.argv[1] == '-d': cfg = ConfigObj(config) cfg.filename = '{0}/velplot.cfg'.format(os.getcwd()) cfg.write() return elif sys.argv[1] == '-dd': cfg = ConfigObj(config_extended) cfg.filename = '{0}/velplot.cfg'.format(os.getcwd()) cfg.write() return args = parser.parse_args(args) try: cfg = configobj.ConfigObj(args.config, configspec=spec) validator = validate.Validator() cfg.validate(validator) except: raise IOError('Configuration file could not be read') figname = cfg['FIGURE'].pop('filename') # Create list of transitions: fname = cfg['FIGURE'].pop('transitions') print('Reading transitions from ', fname) fh = open(fname) transitions = list(fh) fh.close() # Don't include transitions that are commented out: transitions = [transition for transition in transitions if not transition.startswith('#')] # Initialise figure: velplot = VelocityPlot(transitions, **cfg['FIGURE']) fname = cfg['DATA'].pop('filename') if not fname: raise IOError('no data to plot!') # Get spectrum information and plot: spectrum = (Table.read(fname) if fname.endswith('fits') else ascii.read(fname)) wavelength = spectrum[cfg['DATA'].pop('wavelength_column')] flux = spectrum[cfg['DATA'].pop('flux_column')] error = spectrum[cfg['DATA'].pop('error_column')] continuum = spectrum[cfg['DATA'].pop('continuum_column')] velplot.plot_data(wavelength, flux, error, continuum, **cfg['DATA']) # Get model information and plot if specified: fname = cfg['MODEL'].pop('filename') if fname: ion = cfg['MODEL'].pop('ion_column') redshift = cfg['MODEL'].pop('redshift_column') logn = cfg['MODEL'].pop('logn_column') b = cfg['MODEL'].pop('b_column') if fname.endswith('f26'): model = read_f26(fname) absorbers = model.absorbers else: table = (Table.read(fname) if fname.endswith('fits') else ascii.read(fname)) absorbers = [Absorber(row[ion], row[redshift], row[logn], row[b]) for row in table] velplot.plot_models(absorbers, **cfg['MODEL']) # Save: if figname: print('Saving to {0}'.format(figname)) velplot.savefig(figname) # Display: velplot.display()
class ConfigItem(metaclass=InheritDocstrings): """ A setting and associated value stored in a configuration file. These objects should be created as members of `ConfigNamespace` subclasses, for example:: class _Conf(config.ConfigNamespace): unicode_output = config.ConfigItem( False, 'Use Unicode characters when outputting values, and writing widgets ' 'to the console.') conf = _Conf() Parameters ---------- defaultvalue : object, optional The default value for this item. If this is a list of strings, this item will be interpreted as an 'options' value - this item must be one of those values, and the first in the list will be taken as the default value. description : str or None, optional A description of this item (will be shown as a comment in the configuration file) cfgtype : str or None, optional A type specifier like those used as the *values* of a particular key in a ``configspec`` file of ``configobj``. If None, the type will be inferred from the default value. module : str or None, optional The full module name that this item is associated with. The first element (e.g. 'astropy' if this is 'astropy.config.configuration') will be used to determine the name of the configuration file, while the remaining items determine the section. If None, the package will be inferred from the package within which this object's initializer is called. aliases : str, or list of str, optional The deprecated location(s) of this configuration item. If the config item is not found at the new location, it will be searched for at all of the old locations. Raises ------ RuntimeError If ``module`` is `None`, but the module this item is created from cannot be determined. """ # this is used to make validation faster so a Validator object doesn't # have to be created every time _validator = validate.Validator() cfgtype = None """ A type specifier like those used as the *values* of a particular key in a ``configspec`` file of ``configobj``. """ def __init__(self, defaultvalue='', description=None, cfgtype=None, module=None, aliases=None): from astropy.utils import isiterable if module is None: module = find_current_module(2) if module is None: msg1 = 'Cannot automatically determine get_config module, ' msg2 = 'because it is not called from inside a valid module' raise RuntimeError(msg1 + msg2) else: module = module.__name__ self.module = module self.description = description self.__doc__ = description # now determine cfgtype if it is not given if cfgtype is None: if (isiterable(defaultvalue) and not isinstance(defaultvalue, str)): # it is an options list dvstr = [str(v) for v in defaultvalue] cfgtype = 'option(' + ', '.join(dvstr) + ')' defaultvalue = dvstr[0] elif isinstance(defaultvalue, bool): cfgtype = 'boolean' elif isinstance(defaultvalue, int): cfgtype = 'integer' elif isinstance(defaultvalue, float): cfgtype = 'float' elif isinstance(defaultvalue, str): cfgtype = 'string' defaultvalue = str(defaultvalue) self.cfgtype = cfgtype self._validate_val(defaultvalue) self.defaultvalue = defaultvalue if aliases is None: self.aliases = [] elif isinstance(aliases, str): self.aliases = [aliases] else: self.aliases = aliases def __set__(self, obj, value): return self.set(value) def __get__(self, obj, objtype=None): if obj is None: return self return self() def set(self, value): """ Sets the current value of this ``ConfigItem``. This also updates the comments that give the description and type information. Parameters ---------- value The value this item should be set to. Raises ------ TypeError If the provided ``value`` is not valid for this ``ConfigItem``. """ try: value = self._validate_val(value) except validate.ValidateError as e: msg = 'Provided value for configuration item {0} not valid: {1}' raise TypeError(msg.format(self.name, e.args[0])) sec = get_config(self.module) sec[self.name] = value @contextmanager def set_temp(self, value): """ Sets this item to a specified value only inside a with block. Use as:: ITEM = ConfigItem('ITEM', 'default', 'description') with ITEM.set_temp('newval'): #... do something that wants ITEM's value to be 'newval' ... print(ITEM) # ITEM is now 'default' after the with block Parameters ---------- value The value to set this item to inside the with block. """ initval = self() self.set(value) try: yield finally: self.set(initval) def reload(self): """ Reloads the value of this ``ConfigItem`` from the relevant configuration file. Returns ------- val The new value loaded from the configuration file. """ self.set(self.defaultvalue) baseobj = get_config(self.module, True) secname = baseobj.name cobj = baseobj # a ConfigObj's parent is itself, so we look for the parent with that while cobj.parent is not cobj: cobj = cobj.parent newobj = configobj.ConfigObj(cobj.filename, interpolation=False) if secname is not None: if secname not in newobj: return baseobj.get(self.name) newobj = newobj[secname] if self.name in newobj: baseobj[self.name] = newobj[self.name] return baseobj.get(self.name) def __repr__(self): out = '<{}: name={!r} value={!r} at 0x{:x}>'.format( self.__class__.__name__, self.name, self(), id(self)) return out def __str__(self): out = '\n'.join( ('{0}: {1}', ' cfgtype={2!r}', ' defaultvalue={3!r}', ' description={4!r}', ' module={5}', ' value={6!r}')) out = out.format(self.__class__.__name__, self.name, self.cfgtype, self.defaultvalue, self.description, self.module, self()) return out def __call__(self): """ Returns the value of this ``ConfigItem`` Returns ------- val This item's value, with a type determined by the ``cfgtype`` attribute. Raises ------ TypeError If the configuration value as stored is not this item's type. """ def section_name(section): if section == '': return 'at the top-level' else: return f'in section [{section}]' options = [] sec = get_config(self.module) if self.name in sec: options.append((sec[self.name], self.module, self.name)) for alias in self.aliases: module, name = alias.rsplit('.', 1) sec = get_config(module) if '.' in module: filename, module = module.split('.', 1) else: filename = module module = '' if name in sec: if '.' in self.module: new_module = self.module.split('.', 1)[1] else: new_module = '' warn( "Config parameter '{}' {} of the file '{}' " "is deprecated. Use '{}' {} instead.".format( name, section_name(module), get_config_filename(filename), self.name, section_name(new_module)), AstropyDeprecationWarning) options.append((sec[name], module, name)) if len(options) == 0: self.set(self.defaultvalue) options.append((self.defaultvalue, None, None)) if len(options) > 1: filename, sec = self.module.split('.', 1) warn( "Config parameter '{}' {} of the file '{}' is " "given by more than one alias ({}). Using the first.".format( self.name, section_name(sec), get_config_filename(filename), ', '.join([ '.'.join(x[1:3]) for x in options if x[1] is not None ])), AstropyDeprecationWarning) val = options[0][0] try: return self._validate_val(val) except validate.ValidateError as e: raise TypeError('Configuration value not valid:' + e.args[0]) def _validate_val(self, val): """ Validates the provided value based on cfgtype and returns the type-cast value throws the underlying configobj exception if it fails """ # note that this will normally use the *class* attribute `_validator`, # but if some arcane reason is needed for making a special one for an # instance or sub-class, it will be used return self._validator.check(self.cfgtype, val)
def main(args=None): """ This is the main function called by the `ivelplot` script. """ from astropy.utils.compat import argparse from astropy.extern.configobj import configobj, validate from pkg_resources import resource_stream parser = argparse.ArgumentParser( description='An interactive environment for absorption line ' 'identification and Voigt profile \nfitting with VPFIT.\n' '\nTo dump a default configuration file: ivelplot -d' '\nTo dump an extended default configuration file: ' 'ivelplot -dd', formatter_class=argparse.RawTextHelpFormatter) parser.add_argument('config', help='path to the configuration file') parser.add_argument('-z', '--redshift', help='redshift') parser.add_argument('--search', action='store_true', help='display a general search list of ions') parser.add_argument('--lyman', action='store_true', help='display the Lyman series transitions') parser.add_argument('--galactic', action='store_true', help='display the common Galactic transitions') parser.add_argument('--agn', action='store_true', help='display the common AGN associated transitions') config = resource_stream(__name__, '/config/ivelplot.cfg') config_extended = resource_stream( __name__, '/config/ivelplot_extended.cfg') spec = resource_stream(__name__, '/config/ivelplot_specification.cfg') if len(sys.argv) > 1: if sys.argv[1] == '-d': cfg = configobj.ConfigObj(config) cfg.filename = '{0}/ivelplot.cfg'.format(os.getcwd()) cfg.write() return elif sys.argv[1] == '-dd': cfg = configobj.ConfigObj(config_extended) cfg.filename = '{0}/ivelplot.cfg'.format(os.getcwd()) cfg.write() return args = parser.parse_args(args) try: cfg = configobj.ConfigObj(args.config, configspec=spec) validator = validate.Validator() cfg.validate(validator) except: raise IOError('Configuration file could not be read') fname = cfg['WINDOW'].pop('transitions') if args.search: fh = resource_stream(__name__, '/data/search.dat') transitions = list(fh) fh.close() elif args.lyman: fh = resource_stream(__name__, '/data/lyman.dat') transitions = list(fh) fh.close() elif args.galactic: fh = resource_stream(__name__, '/data/galactic.dat') transitions = list(fh) fh.close() elif args.agn: fh = resource_stream(__name__, '/data/agn.dat') transitions = list(fh) fh.close() else: print('Reading transitions from ', fname) fh = open(fname) transitions = list(fh) fh.close() transitions = [transition for transition in transitions if not transition.startswith('#')] fname = cfg['DATA'].pop('filename') if not fname: raise IOError('no data to plot!') spectrum = Table.read(fname) if fname[-4:] == 'fits' else ascii.read(fname) wavelength = spectrum[cfg['DATA'].pop('wavelength_column')] flux = spectrum[cfg['DATA'].pop('flux_column')] error = spectrum[cfg['DATA'].pop('error_column')] continuum = spectrum[cfg['DATA'].pop('continuum_column')] redshift = float(args.redshift) if args.redshift is not None else 0 cfg['MODEL']['system_width'] = (cfg['WINDOW']['vmax'] - cfg['WINDOW']['vmin']) cfg['MODEL']['absorbers'] = None print(info) app = QApplication(sys.argv) app.aboutToQuit.connect(app.deleteLater) desktop = app.desktop() screen = desktop.screenGeometry() width = screen.width() / desktop.physicalDpiX() * 0.88 fontsize = 0.7 * width label_fontsize = 0.6 * width cfg['WINDOW']['width'] = width cfg['WINDOW']['fontsize'] = fontsize cfg['WINDOW']['label_fontsize'] = label_fontsize velocity_plot = InteractiveVelocityPlot( fname, transitions, wavelength, flux, error, continuum, redshift, **cfg) velocity_plot.window.show() output_stream = OutputStream() output_stream.text_written.connect(velocity_plot.on_output) sys.stdout = output_stream sys.exit(app.exec_())