Python

_LabelHandler class

_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

_LabelHandler Class Constructor

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 …

Properties are available for later use …

self._formatter is initialised in the constructor of a derived class. The formatter chosen depends on the EC classification of the target element.

_LabelHandler Class Methods

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 …

CreateLabel abstract method

  @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.

GetFormatterInstance method

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

CreateTextField method

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

Schema Factory

_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')

Formatter and Area Decorator Options

_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_

Derived Classes

Classes that inherit _LabelHandler include …

Questions

Post questions about MicroStation Python programming to the MicroStation Programming Forum.