_LabelHandler is, in Python terminology, an
abstract base class
(ABC).
It imports the usual MicroStation Python modules.
From the Python libraries, it additionally imports ABC and abstractmethod …
from abc import ABC, abstractmethod
Here is the class definition and initialisation …
class _LabelHandler(ABC):
'''
The base class for label handlers, such as DistanceLabelHandler.
_LabelHandler is an Abstract Base Class (see https://docs.python.org/3/library/abc.html),
meaning that you can't use it directly and that you should use its derived classes
(e.g. DistanceLabelHandler, AreaLabelHandler, ...)
ElementHandle is the DGN element we're measuring (such as a LineElement or ShapeElement).
'''
def __init__(self, host: ElementHandle):
self._host_element = host
assert self._host_element is not None, "_LabelHandler: host element is None"
self._dgn_model = self._host_element.GetDgnModel()
_text_style = DgnTextStyle.GetSettings(self._dgn_model.GetDgnFile())
assert _text_style is not None, "_LabelHandler: TextStyle is None"
self._text_block = TextBlock(_text_style, self._dgn_model)
assert self._text_block is not None, "_LabelHandler: TextBlock is None"
self._prefix: str = None
self._suffix: str = None
self._units: MstnPropertyFormatterOptions = MstnPropertyFormatterOptions.UseActiveMasterUnits
self._accuracy: int = 2
self._formatter: DgnECInstance = None
The DGN element we want to annotate is passed as the host argument, whose type is
ElementHandle.
Several class variables are initialised for later use …
self._dgn_model is extracted from the host element
self._dgn_model.GetDgnFile() gets the DGN file that contains all DGN objects
_text_style is the active text style extracted from the DGN file
self._text_block is manufactured for later use and assigned the active text style
Properties are available for later use …
self._prefix is an optional text prefix that, if present, will be added to the TextBlock
self._suffix is an optional text suffix that, if present, will be appended to the TextBlock
self._units are used by the EC formatter
self._accuracy is the decimal precision of displayed units
self._formatter is initialised in the constructor of a derived class.
The formatter chosen depends on the EC classification of the target element.
There is a number of methods that are used by derived classes.
What makes this a base class is method CreateLabel, which is
decorated with @abstractmethod …
@abstractmethod
def CreateLabel(self, origin: DPoint3d)->[bool, ElementHandle]:
'''
Classes that inherit this base class must implement method CreateLabel.
'''
return False, None
Classes that derive from _LabelHandler must provide an implementation of method CreateLabel.
This method is called by a derived class to initialise the appropriate EC formatter …
def GetFormatterInstance(self, class_name: str)->[bool, DgnECInstance]:
'''
Create a stand-alone formatter instance using this class's schema.
Returns: tuple [True, ECInstance] if we found a valid instance.
'''
status, format_schema = ECSchemaFactory.GetMstnPropertyFormatterSchema()
format_class: ECClass = format_schema.GetClass(class_name)
enabler: StandaloneECEnabler = format_class.GetDefaultStandaloneEnabler ()
formatter: StandaloneECInstance = enabler.CreateInstance()
return formatter is not None, formatter
This method is called by a
derived class
to create an appropriate TextField …
def CreateTextField (self, class_name: str, prop_name: str)->TextField:
'''
e.g. _LabelHandler.CreateTextField target ID 584 class 'MstnClosedBoundary' property 'EnclosedArea'
[Paul Connelly was, at the time of this comment around 2010, a MicroStation C++ developer at Bentley Systems]
Create a polymorphic ECQuery for the class BaseElementSchema:MstnClosedBoundary.
Execute the query using the ElementHandle as the scope.
If it returns an ECInstance, create a field pointing to a property (e.g. EnclosedArea) of that ECInstance
'''
status, format_schema = status, schema = ECSchemaFactory.GetBaseElementSchema ()
ec_class: ECClass = format_schema.GetClass (class_name)
assert ec_class is not None, f"_LabelHandler.CreateTextField: ec_class '{class_name}' is None"
manager: DgnECManager = DgnECManager.GetManager()
_POLYMORPHIC = True
instance: DgnElementECInstance = manager.FindInstanceOnElement (self._host_element, ec_class, _POLYMORPHIC)
if instance:
field: TextField = TextField.CreateForElement (instance, prop_name, self._formatter, ISessionMgr.GetActiveDgnModel ())
return field
else:
msg = f"_LabelHandler.CreateTextField unable to find EC instance of {class_name}.{prop_name}"
print(msg)
MessageCenter.ShowErrorMessage(msg, msg, False)
return None
_LabelHandler uses
EC schemas …
from EC.EC_Helpers.ec_schema_factory import (ECSchemaFactory, )
ECSchemaFactory provides class methods that manufacture an EC schema.
Its methods are all class methods, so you don't have to use a constructor before calling them.
For example …
ECSchemaFactory.GetNamedSchema('schema_name')
You don't need to do this …
factory = ECSchemaFactory()
factory.GetNamedSchema('schema_name')
_LabelHandler uses
formatter and area decorator options …
from EC.EC_Helpers.ec_formatter_options import (MstnPropertyFormatterOptions, AreaDecoratorOptions, is_member, )
Area decorators determine how a suffix should apply to an area value.
class AreaDecoratorOptions(IntEnum):
SquareUnit = 0
Unit2 = 1
# Our own formatting when we want m² not m2
Engineering = 2
Formatter options determine how a value is rendered and what units should be used. The formatter calculates an area depending on the chosen unit. The calculation is independent of the units used in a DGN model. Even if your DGN model master units are, say, feet you can nonetheless display a measurement in metres. In fact, you can create several labels, each presenting the same measurement using different units.
class MstnPropertyFormatterOptions(IntEnum):
'''
See MstPropertyFormatter schema XML to see the origin of these codes in ValueMap.
'''
NoUnits = 0
# Used for thousands of square feet: not used in MstPropertyFormatter schema.
KiloFeet = -23
# Furlong (one eighth of a mile): not used in MstPropertyFormatter schema.
Furlongs = -17
# US Gallons: not used in MstPropertyFormatter schema.
UsGallons = -13
# Imperial Gallons: not used in MstPropertyFormatter schema.
ImpGallons = -11
# Hectares: not used in MstPropertyFormatter schema.
Hectares = -7
# Acres.
Acres = -3
# Use Active Sub Units.
UseActiveSubUnits = -2
# Use Active Sub Units. Synonym for easier user key-in.
SubUnits = UseActiveSubUnits
# Use Active Master Units.
UseActiveMasterUnits = -1
# Use Active Master Units. Synonym for easier user key-in.
MasterUnits = UseActiveMasterUnits
# Miles.
Miles = 1050
# Yards.
Yards = 1075
# Feet.
Feet = 1100
# Inches.
Inches = 1125
# Mils. Thousandths of an inch.
Mils = 1150
# MicroInches.
MicroInches = 1175
# Kilometers.
Kilometers = 2050
# Kilometers abbreviation.
km = Kilometers
# Meters.
Meters = 2075
# Centimeters.
Centimeters = 2100
# Millimeters.
Millimeters = 2125
# Micrometers.
Micrometers = 2150
# Microns.
Microns = Micrometers
# US Survey Miles.
UsSurveyMiles = 1049
# US Survey Feet.
UsSurveyFeet = 1099
# US Survey Inches.
UsSurveyInches = 1124
@classmethod
def has_value(cls, value):
'''
Determine whether a value is a member of an enumeration.
'''
return value in cls._value2member_map_
Classes that inherit _LabelHandler include …
Post questions about MicroStation Python programming to the MicroStation Programming Forum.