Synchronizing Python PCellDeclarationHelper between the two implementations (for standalone module + build-in for app)

This commit is contained in:
Matthias Koefferlein 2023-03-07 23:43:12 +01:00
parent 7d0964655d
commit 3e8f03ef5f
2 changed files with 288 additions and 277 deletions

View File

@ -459,7 +459,7 @@ class _PCellDeclarationHelper(pya.PCellDeclaration):
def produce(self, layout, layers, parameters, cell): def produce(self, layout, layers, parameters, cell):
""" """
coerce parameters (make consistent) produces the layout
""" """
self.init_values(parameters, layers) self.init_values(parameters, layers)
self.cell = cell self.cell = cell

View File

@ -2,298 +2,309 @@ from klayout.db import Trans, PCellDeclaration, PCellParameterDeclaration
class _PCellDeclarationHelperLayerDescriptor(object): class _PCellDeclarationHelperLayerDescriptor(object):
""" """
A descriptor object which translates the PCell parameters into class attributes A descriptor object which translates the PCell parameters into class attributes
""" """
def __init__(self, param_index): def __init__(self, param_index):
self.param_index = param_index self.param_index = param_index
def __get__(self, obj, type=None): def __get__(self, obj, type = None):
return obj._layers[self.param_index] return obj._layers[self.param_index]
def __set__(self, obj, value): def __set__(self, obj, value):
raise AttributeError("can't change layer attribute") raise AttributeError("can't change layer attribute")
class _PCellDeclarationHelperParameterDescriptor(object): class _PCellDeclarationHelperParameterDescriptor(object):
""" """
A descriptor object which translates the PCell parameters into class attributes A descriptor object which translates the PCell parameters into class attributes
In some cases (i.e. can_convert_from_shape), these placeholders are not
connected to real parameters (obj._param_values is None). In this case,
the descriptor acts as a value holder (self.value)
"""
def __init__(self, param_index):
self.param_index = param_index
self.value = None
def __get__(self, obj, type=None):
if obj._param_values:
return obj._param_values[self.param_index]
else:
return self.value
def __set__(self, obj, value):
if obj._param_values:
obj._param_values[self.param_index] = value
else:
self.value = value
In some cases (i.e. can_convert_from_shape), these placeholders are not
connected to real parameters (obj._param_values is None). In this case,
the descriptor acts as a value holder (self.value)
"""
def __init__(self, param_index, param_name):
self.param_index = param_index
self.param_name = param_name
self.value = None
def __get__(self, obj, type = None):
if obj._param_values:
return obj._param_values[self.param_index]
elif obj._param_states:
return obj._param_states.parameter(self.param_name)
else:
return self.value
def __set__(self, obj, value):
if obj._param_values:
obj._param_values[self.param_index] = value
else:
self.value = value
class _PCellDeclarationHelper(PCellDeclaration): class _PCellDeclarationHelper(PCellDeclaration):
"""
A helper class that somewhat simplifies the implementation
of a PCell
"""
def __init__(self):
""" """
A helper class that somewhat simplifies the implementation initialize this instance
of a PCell
""" """
# "private" attributes
self._param_decls = []
self._param_values = None
self._param_states = None
self._layer_param_index = []
self._layers = []
# public attributes
self.layout = None
self.shape = None
self.layer = None
self.cell = None
def __init__(self): def param(self, name, value_type, description, hidden = False, readonly = False, unit = None, default = None, choices = None):
""" """
initialize this instance Defines a parameter
""" name -> the short name of the parameter
# "private" attributes type -> the type of the parameter
self._param_decls = [] description -> the description text
self._param_values = None named parameters
self._layer_param_index = [] hidden -> (boolean) true, if the parameter is not shown in the dialog
self._layers = [] readonly -> (boolean) true, if the parameter cannot be edited
# public attributes unit -> the unit string
self.layout = None default -> the default value
self.shape = None choices -> ([ [ d, v ], ...) choice descriptions/value for choice type
self.layer = None this method defines accessor methods for the parameters
self.cell = None {name} -> read accessor
set_{name} -> write accessor ({name}= does not work because the
Ruby confuses that method with variables)
{name}_layer -> read accessor for the layer index for TypeLayer parameters
"""
# create accessor methods for the parameters
param_index = len(self._param_decls)
setattr(type(self), name, _PCellDeclarationHelperParameterDescriptor(param_index, name))
def param( if value_type == type(self).TypeLayer:
self, setattr(type(self), name + "_layer", _PCellDeclarationHelperLayerDescriptor(len(self._layer_param_index)))
name, self._layer_param_index.append(param_index)
value_type,
description, # store the parameter declarations
hidden=False, pdecl = PCellParameterDeclaration(name, value_type, description)
readonly=False, self._param_decls.append(pdecl)
unit=None,
default=None, # set additional attributes of the parameters
choices=None, pdecl.hidden = hidden
): pdecl.readonly = readonly
""" if not (default is None):
Defines a parameter pdecl.default = default
name -> the short name of the parameter if not (unit is None):
type -> the type of the parameter pdecl.unit = unit
description -> the description text if not (choices is None):
named parameters if not isinstance(choices, list) and not isinstance(choices, tuple):
hidden -> (boolean) true, if the parameter is not shown in the dialog raise "choices value must be an list/tuple of two-element arrays (description, value)"
readonly -> (boolean) true, if the parameter cannot be edited for c in choices:
unit -> the unit string if (not isinstance(choices, list) and not isinstance(choices, tuple)) or len(c) != 2:
default -> the default value raise "choices value must be an list/tuple of two-element arrays (description, value)"
choices -> ([ [ d, v ], ...) choice descriptions/value for choice type pdecl.add_choice(c[0],c[1])
this method defines accessor methods for the parameters
{name} -> read accessor # return the declaration object for further operations
set_{name} -> write accessor ({name}= does not work because the return pdecl
Ruby confuses that method with variables)
{name}_layer -> read accessor for the layer index for TypeLayer parameters def display_text(self, parameters):
""" """
implementation of display_text
"""
self._param_values = parameters
try:
text = self.display_text_impl()
finally:
self._param_values = None
return text
def get_parameters(self):
"""
gets the parameters
"""
return self._param_decls
# create accessor methods for the parameters def get_values(self):
param_index = len(self._param_decls) """
setattr( gets the temporary parameter values
type(self), name, _PCellDeclarationHelperParameterDescriptor(param_index) """
) v = self._param_values
self._param_values = None
return v
def init_values(self, values = None, layers = None, states = None):
"""
initializes the temporary parameter values
"values" are the original values. If "None" is given, the
default values will be used.
"layers" are the layer indexes corresponding to the layer
parameters.
"""
self._param_values = None
self._param_states = None
if states:
self._param_states = states
elif not values:
self._param_values = []
for pd in self._param_decls:
self._param_values.append(pd.default)
else:
self._param_values = values
self._layers = layers
if value_type == type(self).TypeLayer: def finish(self):
setattr( """
type(self), Needs to be called at the end of an implementation
name + "_layer", """
_PCellDeclarationHelperLayerDescriptor(len(self._layer_param_index)), self._param_values = None
) self._param_states = None
self._layer_param_index.append(param_index) self._layers = None
self._cell = None
self._layout = None
self._layer = None
self._shape = None
def get_layers(self, parameters):
"""
gets the layer definitions
"""
layers = []
for i in self._layer_param_index:
layers.append(parameters[i])
return layers
def callback(self, layout, name, states):
"""
callback (change state on parameter change)
"""
self.init_values(states = states)
self.layout = layout
try:
self.callback_impl(name)
finally:
self.finish()
# store the parameter declarations def coerce_parameters(self, layout, parameters):
pdecl = PCellParameterDeclaration(name, value_type, description) """
self._param_decls.append(pdecl) coerce parameters (make consistent)
"""
self.init_values(parameters)
self.layout = layout
try:
self.coerce_parameters_impl()
parameters = self.get_values()
finally:
self.finish()
return parameters
# set additional attributes of the parameters def produce(self, layout, layers, parameters, cell):
pdecl.hidden = hidden """
pdecl.readonly = readonly produces the layout
if not (default is None): """
pdecl.default = default self.init_values(parameters, layers)
if not (unit is None): self.cell = cell
pdecl.unit = unit self.layout = layout
if not (choices is None): try:
if not isinstance(choices, list) and not isinstance(choices, tuple): self.produce_impl()
raise Exception( finally:
"choices value must be an list/tuple of two-element arrays (description, value)" self.finish()
)
for c in choices:
if (
not isinstance(choices, list) and not isinstance(choices, tuple)
) or len(c) != 2:
raise Exception(
"choices value must be an list/tuple of two-element arrays (description, value)"
)
pdecl.add_choice(c[0], c[1])
# return the declaration object for further operations
return pdecl
def display_text(self, parameters):
"""
implementation of display_text
"""
self._param_values = parameters
text = self.display_text_impl()
self._param_values = None
return text
def get_parameters(self):
"""
gets the parameters
"""
return self._param_decls
def get_values(self):
"""
gets the temporary parameter values
"""
v = self._param_values
self._param_values = None
return v
def init_values(self, values=None, layers=None):
"""
initializes the temporary parameter values
"values" are the original values. If "None" is given, the
default values will be used.
"layers" are the layer indexes corresponding to the layer
parameters.
"""
if not values:
self._param_values = []
for pd in self._param_decls:
self._param_values.append(pd.default)
else:
self._param_values = values
self._layers = layers
def finish(self):
"""
Needs to be called at the end of produce() after init_values was used
"""
self._param_values = None
self._layers = None
def get_layers(self, parameters):
"""
get the layer definitions
"""
layers = []
for i in self._layer_param_index:
layers.append(parameters[i])
return layers
def coerce_parameters(self, layout, parameters):
"""
coerce parameters (make consistent)
"""
self.init_values(parameters)
self.layout = layout
self.coerce_parameters_impl()
self.layout = None
return self.get_values()
def produce(self, layout, layers, parameters, cell):
"""
coerce parameters (make consistent)
"""
self.init_values(parameters, layers)
self.cell = cell
self.layout = layout
self.produce_impl()
self.cell = None
self.layout = None
self.finish()
def can_create_from_shape(self, layout, shape, layer):
"""
produce a helper for can_create_from_shape
"""
self.layout = layout
self.shape = shape
self.layer = layer
ret = self.can_create_from_shape_impl()
self.layout = None
self.shape = None
self.layer = None
return ret
def transformation_from_shape(self, layout, shape, layer):
"""
produce a helper for parameters_from_shape
"""
self.layout = layout
self.shape = shape
self.layer = layer
t = self.transformation_from_shape_impl()
self.layout = None
self.shape = None
self.layer = None
return t
def parameters_from_shape(self, layout, shape, layer):
"""
produce a helper for parameters_from_shape
with this helper, the implementation can use the parameter setters
"""
self.init_values()
self.layout = layout
self.shape = shape
self.layer = layer
self.parameters_from_shape_impl()
param = self.get_values()
self.layout = None
self.shape = None
self.layer = None
return param
def display_text_impl(self):
"""
default implementation
"""
return ""
def coerce_parameters_impl(self):
"""
default implementation
"""
pass
def produce_impl(self):
"""
default implementation
"""
pass
def can_create_from_shape_impl(self):
"""
default implementation
"""
return False
def parameters_from_shape_impl(self):
"""
default implementation
"""
pass
def transformation_from_shape_impl(self):
"""
default implementation
"""
return Trans()
def can_create_from_shape(self, layout, shape, layer):
"""
produce a helper for can_create_from_shape
"""
self.layout = layout
self.shape = shape
self.layer = layer
try:
ret = self.can_create_from_shape_impl()
finally:
self.finish()
return ret
def transformation_from_shape(self, layout, shape, layer):
"""
produce a helper for parameters_from_shape
"""
self.layout = layout
self.shape = shape
self.layer = layer
try:
t = self.transformation_from_shape_impl()
finally:
self.finish()
return t
def parameters_from_shape(self, layout, shape, layer):
"""
produce a helper for parameters_from_shape
with this helper, the implementation can use the parameter setters
"""
self.init_values()
self.layout = layout
self.shape = shape
self.layer = layer
try:
self.parameters_from_shape_impl()
param = self.get_values()
finally:
self.finish()
return param
def display_text_impl(self):
"""
default implementation
"""
return ""
def coerce_parameters_impl(self):
"""
default implementation
"""
pass
def callback_impl(self, name):
"""
default implementation
"""
pass
def produce_impl(self):
"""
default implementation
"""
pass
def can_create_from_shape_impl(self):
"""
default implementation
"""
return False
def parameters_from_shape_impl(self):
"""
default implementation
"""
pass
def transformation_from_shape_impl(self):
"""
default implementation
"""
return Trans()
# import the Type... constants from PCellParameterDeclaration # import the Type... constants from PCellParameterDeclaration
for k in dir(PCellParameterDeclaration): for k in dir(PCellParameterDeclaration):
if k.startswith("Type"): if k.startswith("Type"):
setattr(_PCellDeclarationHelper, k, getattr(PCellParameterDeclaration, k)) setattr(_PCellDeclarationHelper, k, getattr(PCellParameterDeclaration, k))
# Inject the PCellDeclarationHelper into pya module for consistency: # Inject the PCellDeclarationHelper into module for consistency:
PCellDeclarationHelper = _PCellDeclarationHelper PCellDeclarationHelper = _PCellDeclarationHelper