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 …
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 …
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.
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.
| Source | Link |
|---|---|
| MicroStation Help | Fields - Derived Content in Text |
| MicroStation Python API Help | TextField class |
The class connects to a DGN element, model or file …
TextField.CreateForElement()
TextField.CreateForModel()
TextField.CreateForFile()
In this example we're working with a DGN line element.
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.
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.
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.
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
Class CreateLengthLabel_ec manages the interaction with MicroStation's EC schemas.
ECClass and ECProperty of that class
TextPropertyManager.CreateDistanceTextField
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()
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)
CreateDistanceTextField comes in two flavours (i.e. it's overridden) …
CreateDistanceTextField (target: ElementHandle, class_name: str, prop_name: str, formatter: IECInstance) requires the
EC class name and EC property name to retrieve those EC data from the GetBaseElementSchema
CreateDistanceTextField(eh: ElementHandle, formatter: IECInstance) which computes the
EC class name and EC property name depending on the type of DGN element that was picked.
It forwards those to the version of the method that requires those class and property names
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)
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
Post questions about MicroStation Python programming to the MicroStation Programming Forum.