Beispiel #1
0
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()
Beispiel #2
0
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)
Beispiel #3
0
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_())