Source code for ahds.header

# -*- coding: utf-8 -*-
# amira_header.py
"""
Module to convert parsed data from an Amira (R) header into a set of nested objects. The key class is :py:class:`ahds.header.AmiraHeader`.

Usage:

::

    >>> from ahds.header import AmiraHeader
    >>> ah = AmiraHeader.from_file('file.am')
    >>> print ah

Each nested object is constructed from the :py:class:`ahds.header.Block` class defined.

There are four top-level attributes that every :py:class:`ahds.header.AmiraHeader` will have:

*    designation

*    definitions

*    parameters

*    data_pointers

Each attribute can be queried using the ``attrs`` attribute.

:: 

    >>> print ah.data_pointers.attrs
    ['data_pointer_1', 'data_pointer_2', 'data_pointer_3', 'data_pointer_4', 'data_pointer_5', 'data_pointer_6']
    >>> print ah.data_pointers.data_pointer_1
    data_pointer_1
    pointer_name: VERTEX
    data_format: None
    data_dimension: 3
    data_type: float
    data_name: VertexCoordinates
    data_index: 1
    data_length: None

Data pointers are identified by the name ``data_pointer_<n>``.

"""
from __future__ import print_function

import sys

from .grammar import get_parsed_data


[docs]class Block(object): """Generic block to be loaded with attributes""" def __init__(self, name): self.name = name self.attrs = list()
[docs] def add_attr(self, name, value): """Add an attribute to an :py:class:`ahds.header.Block` object""" setattr(self, name, value) self.attrs.append(name)
def __str__(self): string = "{}\n".format(self.name) for attr in self.attrs: if isinstance(getattr(self, attr), Block): string += "{}\n".format(getattr(self, attr)) else: string += "{}: {}\n".format(attr, getattr(self, attr)) return string @property def ids(self): """Convenience method to get the IDs for Materials present""" assert self.name == "Materials" ids = list() for attr in self.attrs: attr_obj = getattr(self, attr) if hasattr(attr_obj, 'Id'): ids.append(getattr(attr_obj, 'Id')) return ids def __getitem__(self, index): """Convenience method to get an attribute with 'Id' for a Material""" assert self.name == "Materials" assert isinstance(index, int) for attr in self.attrs: attr_obj = getattr(self, attr) if hasattr(attr_obj, 'Id'): if getattr(attr_obj, 'Id') == index: return attr_obj else: continue # next attr else: return None
[docs]class AmiraHeader(object): """Class to encapsulate Amira (R) metadata""" def __init__(self, parsed_data): self._parsed_data = parsed_data self._load()
[docs] @classmethod def from_file(cls, fn, *args, **kwargs): """Constructor to build an :py:class:`ahds.header.AmiraHeader` object from a file :param str fn: Amira (R) file :return ah: object of class :py:class:`ahds.header.AmiraHeader` containing header metadata :rtype: ah: :py:class:`ahds.header.AmiraHeader` """ return cls(get_parsed_data(fn, *args, **kwargs))
@property def raw_header(self): """Show the raw header data""" return self._parsed_data def __len__(self): return len(self._parsed_data) @staticmethod def flatten_dict(in_dict): block_data = dict() for block in in_dict: block_data[block.keys()[0]] = block[block.keys()[0]] return block_data def _load(self): # first flatten the dict block_data = self.flatten_dict(self._parsed_data) # pprint(block_data, width=147) self._load_designation(block_data['designation']) self._load_definitions(block_data['definitions']) self._load_data_pointers(block_data['data_pointers']) self._load_parameters(block_data['parameters']) # self._load_date() @property def designation(self): """Designation of the Amira (R) file defined in the first row Designations consist of some or all of the following data: * filetype e.g. ``AmiraMesh`` or ``HyperSurface`` * dimensions e.g. ``3D`` * format e.g. ``BINARY-LITTLE-ENDIAN`` * version e.g. ``2.1`` * extra format e.g. ``<hxsurface>`` """ return self._designation @property def definitions(self): """Definitions consist of a key-value pair specified just after the designation preceded by the key-word 'define' """ return self._definitions @property def parameters(self): """The set of parameters for each of the segments specified e.g. colour, data pointer etc. """ return self._parameters @property def data_pointers(self): """The list of data pointers together with a name, data type, dimension, index, format and length """ return self._data_pointers def _load_designation(self, block_data): self._designation = Block('designation') if 'filetype' in block_data: self._designation.add_attr('filetype', block_data['filetype']) else: self._designation.add_attr('filetype', None) if 'dimension' in block_data: self._designation.add_attr('dimension', block_data['dimension']) else: self._designation.add_attr('dimension', None) if 'format' in block_data: self._designation.add_attr('format', block_data['format']) else: self._designation.add_attr('format', None) if 'version' in block_data: self._designation.add_attr('version', block_data['version']) else: self._designation.add_attr('version', None) if 'extra_format' in block_data: self._designation.add_attr('extra_format', block_data['extra_format']) else: self._designation.add_attr('extra_format', None) def _load_definitions(self, block_data): self._definitions = Block('definitions') for definition in block_data: self._definitions.add_attr(definition['definition_name'], definition['definition_value']) def _load_parameters(self, block_data): self._parameters = Block('parameters') for parameter in block_data: if 'nested_parameter' in parameter: nested_parameter = parameter['nested_parameter'] self._parameters.add_attr(nested_parameter['nested_parameter_name'], Block(nested_parameter['nested_parameter_name'])) nested_parameter_obj = getattr(self._parameters, nested_parameter['nested_parameter_name']) for nested_parameter_value in nested_parameter['nested_parameter_values']: if 'attributes' in nested_parameter_value: if nested_parameter_value['attributes']: nested_parameter_obj.add_attr(nested_parameter_value['name'], Block(nested_parameter_value['name'])) nested_parameter_value_obj = getattr(nested_parameter_obj, nested_parameter_value['name']) for attribute in nested_parameter_value['attributes']: nested_parameter_value_obj.add_attr(attribute['attribute_name'], attribute['attribute_value']) else: nested_parameter_obj.add_attr(nested_parameter_value['name'], None) elif 'nested_attributes' in nested_parameter_value: nested_parameter_obj.add_attr(nested_parameter_value['name'], Block(nested_parameter_value['name'])) for nested_attribute in nested_parameter_value['nested_attributes']: nested_attribute_obj = getattr(nested_parameter_obj, nested_parameter_value['name']) nested_attribute_obj.add_attr(nested_attribute['nested_attribute_name'], Block(nested_attribute['nested_attribute_name'])) nested_attribute_value_obj = getattr(nested_attribute_obj, nested_attribute['nested_attribute_name']) for nested_attribute_value in nested_attribute['nested_attribute_values']: nested_attribute_value_obj.add_attr( nested_attribute_value['nested_attribute_value_name'], nested_attribute_value['nested_attribute_value_value']) else: nested_parameter_obj.add_attr(nested_parameter_value['name'], nested_parameter_value['inline_parameter_value']) if 'inline_parameter' in parameter: inline_parameter = parameter['inline_parameter'] self._parameters.add_attr(inline_parameter['inline_parameter_name'], inline_parameter['inline_parameter_value']) def _load_data_pointers(self, block_data): self._data_pointers = Block('data_pointers') for data_pointer in block_data: data_pointer_name = "data_pointer_{}".format(data_pointer['data_index']) self._data_pointers.add_attr(data_pointer_name, Block(data_pointer_name)) pointer_obj = getattr(self._data_pointers, data_pointer_name) if 'pointer_name' in data_pointer: pointer_obj.add_attr('pointer_name', data_pointer['pointer_name']) else: pointer_obj.add_attr('pointer_name', None) if 'data_format' in data_pointer: pointer_obj.add_attr('data_format', data_pointer['data_format']) else: pointer_obj.add_attr('data_format', None) if 'data_dimension' in data_pointer: pointer_obj.add_attr('data_dimension', data_pointer['data_dimension']) else: pointer_obj.add_attr('data_dimension', None) if 'data_type' in data_pointer: pointer_obj.add_attr('data_type', data_pointer['data_type']) else: pointer_obj.add_attr('data_type', None) if 'data_name' in data_pointer: pointer_obj.add_attr('data_name', data_pointer['data_name']) else: pointer_obj.add_attr('data_name', None) if 'data_index' in data_pointer: pointer_obj.add_attr('data_index', data_pointer['data_index']) else: pointer_obj.add_attr('data_index', None) if 'data_length' in data_pointer: pointer_obj.add_attr('data_length', data_pointer['data_length']) else: pointer_obj.add_attr('data_length', None) def __repr__(self): return "<AmiraHeader with {:,} bytes>".format(len(self)) def __str__(self): string = "*" * 50 + "\n" string += "AMIRA HEADER\n" string += "-" * 50 + "\n" string += "{}\n".format(self.designation) string += "-" * 50 + "\n" string += "{}\n".format(self.definitions) string += "-" * 50 + "\n" string += "{}\n".format(self.parameters) string += "-" * 50 + "\n" string += "{}\n".format(self.data_pointers) string += "*" * 50 return string
def main(): try: fn = sys.argv[1] except IndexError: print("usage: ./{} <amira-fn>".format(__file__), file=sys.stderr) return 1 h = AmiraHeader.from_file(fn, verbose=False) # print h.parameters # print h.parameters.attrs # print h.parameters.Materials, type(h.parameters.Materials) # print h.parameters.Materials.attrs # print h.parameters.Materials.Exterior # print h.parameters.Materials.ids # print h.parameters.Materials[1].attrs for id_ in h.parameters.Materials.ids: print h.parameters.Materials[id_] print return 0 if __name__ == "__main__": sys.exit(main())