Python

Smart Line Labeller

The line labeller described here demonstrates how to pick a line element, then compose a TextBlock. We append a TextField to the TextBlock to display the line's length …

Smart Label with text style background

By default, MicroStation doesn't apply the text style background of the text element to the text field. You can see that in my screenshot above: text Length: is plain text with a brown background; text 3.15m is the TextField without that background.

You can change a text field's rendering in MicroStation preferences. Navigate to Preferences Dialog, Text Category and toggle option Use Test Style Background Color for Field Background. MicroStation help tells us: If on, the TextFields will display the Field background color defined in the text style. You can switch from default grey background color to the Field background from the TextStyle, if defined.

Here's the same line and label with that option enabled …

Smart Label with text style background

Association

The label is a DGN text node element. It is stand-alone text and is not associated with the picked line. If you move the line, the text position does not change. If you edit the line and change its length, the text label updates to reflect the new length.

If the label were associated with the line, then moving the line would cause the label also to move. It's possible to associate the label with its host. But, to keep things simple, we don't do that in this example.

Dependency

The TextField has a dependency on the host line element. That is, the TextField is dependent on that line. That's how the label knows to update if the host element is changed. Use the ANALYZE ELEMENT tool to see those dependency data on the label text node.

Introduction to the TextField Class

TextField Documentation

Source Link
MicroStation HelpFields - Derived Content in Text
MicroStation Python API HelpTextField class

The class connects to a DGN element, model or file …

In this example we're working with a DGN line element.

EC Classes

EC data are linked to a DGN object by that object's Element ID. An Element ID is a 64-bit integer. It is unique within the context of a DGN file. MicroStation provides formatter classes that link to an element by its Element ID. A formatter understands the context of a DGN element: whether it's 2D or 3D; whether it's open or closed (i.e. a line vs. a shape); how it should be measured.

Using the TextField Class

We need code that makes an EC formatter from the EC data belonging to an element. We'll assign that formatter to the TextField. The TextField that we create needs to know about its target DGN element.

Making a TextField

Much of the smart label example is implemented in cmdPickLineElement, a line picker class that inherits from DgnElementSetTool. There are several examples that show how to use DgnElementSetTool and I won't discuss it further here.

What I will discuss is how to connect the picked element with its corresponding TextField.

_OnElementModify

The magic starts in method cmdPickLineElement._OnElementModify, where we instantiate a CreateLengthLabel_ec class …

def _OnElementModify(self, eh: ElementHandle)->int:

     … several lines omitted

    mid_point = self.get_line_mid_point(primitive)
    # Here's where we start working with EC schemas
    labeller = CreateLengthLabel_ec(mid_point, eh)
    labeller.place_label()

    # Return ERROR to signify no change.
    return BentleyStatus.eERROR

CreateLengthLabel_ec

Class CreateLengthLabel_ec manages the interaction with MicroStation's EC schemas.

place_label

Method place_label accepts a TextBlock that we will modify. First, we append a prefix Length:, followed by a TextField …

def place_label(self):
    self._text_block.AppendText("Length: ")
    self._text_block.SetUserOrigin(self._origin)
    # Create a text field that displays EC property length
    self.append_distance_text_field(self._eh, self._text_block)
    label = EditElementHandle()
    # Convert the TextBlock to a DGN element
    status = TextHandlerBase.CreateElement(label, None, self._text_block)
    result = label.AddToModel()

append_distance_text_field

Method append_distance_text_field uses the helper class TextPropertyManager which provides methods that work with EC schemas …

def append_distance_text_field(self, target: ElementHandle, text_block: TextBlock, n_accuracy: int = 2):
    manager = TextPropertyManager()
    status, formatter = manager.CreateDistanceFormatter (MstnPropertyFormatterOptions.UseActiveMasterUnits, n_accuracy)
    text_field = manager.CreateDistanceTextField (target, formatter)
    text_block.AppendField (text_field)

TextPropertyManager

CreateDistanceTextField

CreateDistanceTextField comes in two flavours (i.e. it's overridden) …

That TextPropertyManager supplies the CreateDistanceFormatter and CreateDistanceTextField methods. The first thing we do is to get the EC class and property names. This is complicated by the class and property names of different types of element. Depending on whether measuring a line or line-string element, or something more complex such as a B-spline curve, those names mutate …

def CreateDistanceTextField(eh: ElementHandle, formatter: IECInstance)->TextField:
    match (eh.GetElementType ()):
        case LINE_ELM:
        case LINE_STRING_ELM:
            ecClass = "MstnLineSegments"
            ecProp  = "TotalLength"
            break
        case CMPLX_STRING_ELM:
            ecClass = "MstnComplex"
            ecProp  = "TotalLength"
            break
        case BSPLINE_CURVE_ELM:
            ecClass = "MstnBSplineCurve"
            ecProp  = "Length"
            break
        case ARC_ELM:
            ecClass = "MstnArc"
            ecProp  = "Length"
            break
        default:
            return False
            break
    #  Forward the class and property names 
    return CreateDistanceTextField (eh, ecClass, ecProp, formatter)

CreateDistanceTextField

Get theBase Element Schema. and create a TextField using that schema with the class names for its classes and properties …

def CreateDistanceTextField (target: ElementHandle, className: str, propName: str, formatter: IECInstance)->TextField:
    schema = ECSchema()
    if SchemaFactory::GetBaseElementSchema (schema):
        return CreateTextField (target, schema, className, propName, formatter)
    return None

Questions

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