import ctypes
import copy
import enum
import logging
from collections.abc import Iterable
from ctypes import (c_char, c_char_p, c_double, c_size_t, c_int64, pointer,
POINTER)
from ..tecutil import _tecutil, _tecutil_connector
from ..constant import *
from ..exception import *
from .. import tecutil, version
from ..tecutil import sv
log = logging.getLogger(__name__)
@tecutil.lock(with_recording=False)
def set_style(value, *args, **kwargs):
if __debug__:
assert len(args) < 7, ', '.join(args)
if isinstance(value, int):
if value < tecutil.minint64 or tecutil.maxuint64 < value:
raise TecplotOverflowError('Integer outside of valid range.')
if log.getEffectiveLevel() < logging.INFO:
msg = 'SetStyle\n'
msg += ' value: {}\n'.format(value)
for a in args:
msg += ' {} {}\n'.format(type(a), a)
for k, v in kwargs.items():
msg += ' {} : {} {}\n'.format(k, type(v), v)
log.debug(msg[:-1])
_tecutil_connector._style_call_count['SET'][' '.join(args)] += 1
with tecutil.ArgList() as arglist:
allocd = []
try:
for i, p in enumerate(args):
arglist[getattr(sv, 'P' + str(i + 1))] = p
if isinstance(value, enum.Enum):
arglist[sv.IVALUE] = c_size_t(value.value)
elif isinstance(value, tecutil.Index):
arglist[sv.IVALUE] = c_size_t(value + 1)
elif isinstance(value, (int, bool)):
arglist[sv.IVALUE] = c_size_t(value)
elif isinstance(value, float):
arglist[sv.DVALUE] = value
elif isinstance(value, str):
arglist[sv.STRVALUE] = value
elif hasattr(value, '__iter__'):
value_list = list(value)
if not len(value_list) or isinstance(value_list[0], int):
value_obj = tecutil.IndexSet(*value_list)
allocd.append(value_obj)
arglist[sv.IVALUE] = c_size_t(value_obj.value)
else:
value_obj = tecutil.StringList(*value_list)
allocd.append(value_obj)
arglist[sv.IVALUE] = c_size_t(ctypes.addressof(
ctypes.cast(value_obj, POINTER(c_size_t)).contents))
elif value is None:
arglist[sv.IVALUE] = c_size_t(0)
else:
raise TecplotTypeError
for k, v in kwargs.items():
k = k.upper()
if k == sv.UNIQUEID:
v = c_size_t(v)
elif k in [sv.OBJECTSET]:
v = tecutil.IndexSet(*v)
allocd.append(v)
elif k in [sv.OFFSET1, sv.OFFSET2]:
v = v + 1
arglist[k] = v
if __debug__:
if log.getEffectiveLevel() < logging.INFO:
msg = 'SetStyle\n'
for k, v in arglist.items():
msg += ' {}: {}\n'.format(k, v)
log.debug(msg[:-1])
try:
res = _tecutil.StyleSetLowLevelX(arglist)
if res not in [SetValueReturnCode.Ok,
SetValueReturnCode.DuplicateValue]:
if __debug__:
msg = 'SetStyle\n'
for k, v in arglist.items():
msg += ' {}: {}\n'.format(k, v)
raise TecplotSystemError(str(res) + '\n' + msg)
raise TecplotSystemError(res)
except TecplotLogicError as e:
if __debug__:
msg = 'SetStyle\n'
for k, v in arglist.items():
msg += ' {}: {}\n'.format(k, v)
raise TecplotLogicError(str(e) + '\n' + msg)
else:
raise
finally:
for a in allocd:
a.dealloc()
def get_style(return_type, *args, **kwargs):
if __debug__:
assert len(args) < 7
if log.getEffectiveLevel() < logging.INFO:
msg = 'GetStyle\n'
for a in args:
msg += ' {} {}\n'.format(type(a), a)
for k, v in kwargs.items():
msg += ' {} : {} {}\n'.format(k, type(v), v)
log.debug(msg[:-1])
_tecutil_connector._style_call_count['GET'][' '.join(args)] += 1
with tecutil.ArgList() as arglist:
allocd = []
try:
for i, p in enumerate(args):
arglist[getattr(sv, 'P' + str(i + 1))] = p
for k, v in kwargs.items():
k = k.upper()
if k == sv.UNIQUEID:
v = c_size_t(v)
elif k in [sv.OBJECTSET]:
v = tecutil.IndexSet(*v)
allocd.append(v)
elif k in [sv.OFFSET1, sv.OFFSET2]:
v = v + 1
arglist[k] = v
if (return_type in [int, bool, tecutil.Index, list, set] or
issubclass(return_type, enum.Enum)):
arglist[sv.IVALUE] = pointer(c_size_t())
elif return_type in [str]:
arglist[sv.IVALUE] = pointer(pointer(c_char()))
elif return_type in [float]:
arglist[sv.DVALUE] = pointer(c_double())
else:
raise TecplotTypeError('unknown return_type: {}'.format(return_type))
if __debug__:
if log.getEffectiveLevel() < logging.INFO:
msg = 'GetStyle\n'
for k, v in arglist.items():
msg += ' {}: {}\n'.format(k, v)
log.debug(msg[:-1])
try:
res = _tecutil.StyleGetLowLevelX(arglist)
if res is not GetValueReturnCode.Ok:
raise TecplotSystemError(res)
except TecplotLogicError as e:
if __debug__:
msg = 'GetStyle\n'
for k, v in arglist.items():
msg += ' {}: {}\n'.format(k, v)
raise TecplotLogicError(str(e) + '\n' + msg)
else:
raise
if return_type in [str]:
ivalue = ctypes.cast(arglist[sv.IVALUE], POINTER(c_char_p))
result = ivalue.contents.value
if result not in [None, '']:
result = result.decode('utf-8')
elif issubclass(return_type, enum.Enum):
ival = arglist[sv.IVALUE]
val = ctypes.cast(ival, POINTER(c_int64)).contents.value
result = return_type(int(val))
elif return_type in [tecutil.Index]:
ival = arglist[sv.IVALUE]
val = ctypes.cast(ival, POINTER(c_int64)).contents.value
result = return_type(int(val) - 1)
elif return_type in [list, set]:
ptr = ctypes.cast(arglist[sv.IVALUE], POINTER(tecutil.IndexSet))
iset = ptr.contents
result = return_type(iset)
iset.dealloc()
elif return_type in [int, bool]:
result = return_type(arglist[sv.IVALUE].contents.value)
else: # if return_type in [float]:
result = return_type(arglist[sv.DVALUE].contents.value)
if __debug__:
if log.getEffectiveLevel() < logging.INFO:
log.debug('GetStyle result: {}'.format(result))
return result
finally:
for a in allocd:
a.dealloc()
@tecutil.lock_attributes
class Style(object):
def __init__(self, *svargs, **kwargs):
self._sv = list(tecutil.flatten_args(*svargs))
uniqueid = kwargs.pop('uniqueid', None)
offset1 = kwargs.pop('offset1', None)
offset2 = kwargs.pop('offset2', None)
objectset = kwargs.pop('objectset', None)
assert not (offset1 and objectset), \
'offset1 and objectset are mutually exclusive'
assert not (offset2 and not (offset1 or objectset)), \
'offset2 requires offset1 or objectset to also be specified'
self._style_attrs = {}
if uniqueid is not None:
self._style_attrs[sv.UNIQUEID] = int(uniqueid)
if offset1 is not None:
self._style_attrs[sv.OFFSET1] = tecutil.Index(offset1)
if offset2 is not None:
self._style_attrs[sv.OFFSET2] = tecutil.Index(offset2)
if objectset is not None:
self._style_attrs[sv.OBJECTSET] = set(objectset)
@property
def _kw(self):
return {k.lower(): v for k, v in self._style_attrs.items()}
def _get_style(self, rettype, *svargs, **kwargs):
svargs = self._sv + list(svargs)
kw = self._style_attrs.copy()
kw.update(**kwargs)
if sv.OBJECTSET in kw:
objectset = kw.pop(sv.OBJECTSET)
result = []
for offset1 in sorted(objectset):
kw[sv.OFFSET1] = tecutil.Index(offset1)
result.append(get_style(rettype, *svargs, **kw))
return tuple(result)
else:
return get_style(rettype, *svargs, **kw)
def _set_style(self, value, *svargs, **kwargs):
svargs = self._sv + list(svargs)
kw = self._style_attrs.copy()
kw.update(**kwargs)
set_style(value, *svargs, **kw)
@tecutil.lock_attributes
class SubStyle(object):
def __init__(self, parent, *svargs):
self.parent = parent
self._svargs = list(tecutil.flatten_args(*svargs))
def __eq__(self, that):
return (isinstance(that, type(self))
and self.parent == that.parent
and self._svargs == that._svargs)
def __ne__(self, that):
return not (self == that)
def _get_style(self, rettype, *svargs, **kwargs):
svargs = self._svargs + list(tecutil.flatten_args(*svargs))
return self.parent._get_style(rettype, *svargs, **kwargs)
def _set_style(self, value, *svargs, **kwargs):
svargs = self._svargs + list(tecutil.flatten_args(*svargs))
self.parent._set_style(value, *svargs, **kwargs)
class StyleConfig(Style):
"""Runtime Configuration Control Base Class."""
def __init__(self, name=None, *svargs):
if svargs:
Style.__init__(self, *svargs)
self._ns = name.split('.') if name else []
def __setattr__(self, name, value):
if not name.startswith('_'):
if not hasattr(self, '_keys'):
self._keys = list(filter(lambda x: not x.startswith('_'), dir(self)))
if name not in self._keys:
msg = 'No configuration found: '
raise TecplotAttributeError(msg + '.'.join(self._ns + [name]))
super().__setattr__(name, value)
@property
def _opts(self):
if not hasattr(self, '_attrs'):
configs, props = [], []
for key in filter(lambda x: not x.startswith('_'), dir(self)):
value = getattr(self, key)
if isinstance(value, StyleConfig):
configs.append((key, value))
else:
props.append(key)
self._attrs = (sorted(configs), sorted(props))
return self._attrs
def __str__(self):
configs, props = self._opts
lines = []
for keyname in props:
key = '.'.join(self._ns + [keyname])
lines.append('{} = {}'.format(key, getattr(self, keyname)))
for _, value in configs:
lines.append(str(value))
return '\n'.join(lines)
def style_property(value_type, svarg, value_cast=None):
def getter(self):
return self._get_style(value_type, svarg)
if value_cast:
def setter(self, val):
val = value_cast(val)
if val is not None:
self._set_style(val, svarg)
else:
def setter(self, val):
if val is not None:
self._set_style(value_type(val), svarg)
return property(getter, setter)
class NamedTupleStyle(SubStyle, Iterable):
def __len__(self):
return len(self._keys)
def __getitem__(self, index):
if isinstance(index, slice):
rng = range(index.start or 0,
index.stop or len(self),
index.step or 1)
return tuple([self[x] for x in rng])
else:
return getattr(self, self._keys[index])
def __setitem__(self, index, value):
if isinstance(index, slice):
for i in range(index.start or 0,
index.stop or len(self),
index.step or 1):
self[i] = value[i] if i < len(value) else None
else:
setattr(self, self._keys[index], value)
def __iter__(self):
self._current_index = -1
return self
def __next__(self):
self._current_index += 1
if self._current_index < len(self):
return self.__getitem__(self._current_index)
else:
del self._current_index
raise StopIteration()
def __str__(self):
return '({})'.format(', '.join(str(x) for x in self))
def __eq__(self, other):
return (len(self) == len(other)
and all(a == b for a, b in zip(self, other)))
class NamedVectorStyle(NamedTupleStyle):
def __neg__(self):
return tuple([-v for v in self])
def __add__(self, other):
return tuple([a - b for a, b in zip(self, other)])
def __sub__(self, other):
return tuple([a - b for a, b in zip(self, other)])
def __iadd__(self, other):
for k, v in zip(self._keys, other):
setattr(self, k, getattr(self, k) + v)
return self
def __isub__(self, other):
for k, v in zip(self._keys, other):
setattr(self, k, getattr(self, k) - v)
return self
class IndependentVariableLimits(NamedTupleStyle):
_keys = ('min', 'max')
min = style_property(float, sv.INDVARMIN)
max = style_property(float, sv.INDVARMAX)
class IJK(NamedVectorStyle):
_keys = ('i', 'j', 'k')
i = style_property(int, sv.I)
j = style_property(int, sv.J)
k = style_property(int, sv.K)
class IJKMaxFract(NamedTupleStyle):
_keys = ('i', 'j', 'k')
i = style_property(float, sv.IMAXFRACT)
j = style_property(float, sv.JMAXFRACT)
k = style_property(float, sv.KMAXFRACT)
class IJKMinFract(NamedTupleStyle):
_keys = ('i', 'j', 'k')
i = style_property(float, sv.IMINFRACT)
j = style_property(float, sv.JMINFRACT)
k = style_property(float, sv.KMINFRACT)
class Limits(NamedTupleStyle):
_keys = ('min', 'max')
min = style_property(float, sv.MIN)
max = style_property(float, sv.MAX)
class IndexIJK(NamedVectorStyle):
_keys = ('i', 'j', 'k')
i = style_property(tecutil.Index, sv.I)
j = style_property(tecutil.Index, sv.J)
k = style_property(tecutil.Index, sv.K)
[docs]
class IndexRange(NamedTupleStyle):
"""Index range specification along some axis.
This is similar to Python's :class:`slice` object except that ``max`` is
included in the evaluated indexes. Here are some things to note:
* All indices start with 0 and go to some maximum index ``m``.
* Negative values represent the indexes starting with the maximum at -1
and continuing back to the beginning of the range.
* A step of `None`, 0 and 1 are all equivalent and mean that no elements
are skipped.
* A negative step indicates a skip less than the maximum.
.. versionchanged:: 1.1
**(Bug fix)** ``IndexRange`` max value of zero is now interpreted as
the first index in the range instead of the last index. Prior to
version 1.1, the ``max`` parameter interpreted zero to be the end of
the range instead of the first element. This meant that an
``IndexRange`` of ``(0, 0, 1)`` would represent the whole range instead
of just the first item. The standard way to represent the entire index
"""
_keys = ('min', 'max', 'step')
min = style_property(tecutil.Index, sv.MIN,
lambda x: tecutil.Index(x or 0))
max = style_property(tecutil.Index, sv.MAX,
lambda x: tecutil.Index(-1 if x is None else x))
step = style_property(int, sv.SKIP, lambda x: int(x or 1))
class RectPosition(NamedTupleStyle):
_keys = ('x1', 'y1', 'x2', 'y2')
x1 = style_property(float, sv.X1)
y1 = style_property(float, sv.Y1)
x2 = style_property(float, sv.X2)
y2 = style_property(float, sv.Y2)
class RectSize(NamedTupleStyle):
_keys = ('width', 'height')
width = style_property(float, sv.WIDTH)
height = style_property(float, sv.HEIGHT)
class SplineDerivativeAtEnds(NamedTupleStyle):
_keys = ('start', 'end')
start = style_property(float, sv.SPLINEDERIVATIVEATSTART)
end = style_property(float, sv.SPLINEDERIVATIVEATEND)
class XY(NamedVectorStyle):
_keys = ('x', 'y')
x = style_property(float, sv.X)
y = style_property(float, sv.Y)
class XYZ(NamedVectorStyle):
_keys = ('x', 'y', 'z')
x = style_property(float, sv.X)
y = style_property(float, sv.Y)
z = style_property(float, sv.Z)