本文通过一个实际案例------批量为图纸中的文字添加 "(U.S.S.)" 后缀,来介绍 Rhino Python 中文字对象的读取与修改方法。
1. 案例需求
在结构图纸中,需要为钢构件标注添加 "(U.S.S.)"(Unless Otherwise Specified)后缀,但要排除以下情况:
- 包含特定关键词的文字(如 BASE PLATE、GROUT 等)
- 已经有后缀的文字
2. 代码结构总览
python
#coding=utf-8
import Rhino
import rhinoscriptsyntax as rs
import scriptcontext as sc
# 1. 定义配置
skip_keywords = [...]
# 2. 获取所有对象
all_objects = rs.AllObjects()
# 3. 遍历并筛选文字对象
for obj_id in all_objects:
if rs.ObjectType(obj_id) == 512:
# 4. 读取文字内容
# 5. 判断是否需要修改
# 6. 修改文字
# 7. 刷新视图
sc.doc.Views.Redraw()
3. 关键概念
3.1 rs vs Rhino vs sc
| 模块 | 层级 | 适用场景 |
|---|---|---|
rhinoscriptsyntax (rs) |
高级封装 | 日常操作,语法简洁 |
Rhino |
RhinoCommon API | 需要精细控制时使用 |
scriptcontext (sc) |
文档上下文 | 访问当前文档、修改对象属性 |
为什么这个案例需要用 Rhino 和 sc?
rs 模块没有提供直接修改 MText 内容的函数,必须通过 RhinoCommon 来操作。
4. 文字对象操作详解
4.1 识别文字对象
python
obj_type = rs.ObjectType(obj_id)
if obj_type == 512: # Annotation 类型
# 这是标注类对象(包括文字、尺寸标注等)
4.2 获取底层几何对象
python
rhino_obj = sc.doc.Objects.Find(obj_id)
geom = rhino_obj.Geometry
| 对象 | 说明 |
|---|---|
obj_id |
GUID,对象的唯一标识符 |
rhino_obj |
RhinoObject,包含几何和属性 |
geom |
GeometryBase,纯几何信息 |
4.3 判断是否为文字实体
python
if isinstance(geom, Rhino.Geometry.TextEntity):
# 确认是文字对象
TextEntity 是 Rhino 中表示文字的几何类型,包括:
- 单行文字 (Text)
- 多行文字 (MText)
4.4 读取文字内容
python
old_text = geom.PlainText
| 属性 | 说明 |
|---|---|
PlainText |
纯文本内容,不含格式 |
RichText |
富文本,包含格式代码 |
4.5 修改文字内容
python
geom.PlainText = new_text
rhino_obj.CommitChanges()
⚠️ 重要 : 修改几何属性后必须调用
CommitChanges()才能生效!
5. 字符串处理技巧
5.1 大小写转换
python
text_upper = old_text.upper() # 转大写
text_lower = old_text.lower() # 转小写
用于不区分大小写的比较。
5.2 关键词检查
python
skip_keywords = ["BASE PLATE", "CAST-IN", "GROUT"]
should_skip = False
for keyword in skip_keywords:
if keyword in text_upper:
should_skip = True
break
更简洁的写法:
python
should_skip = any(keyword in text_upper for keyword in skip_keywords)
5.3 检查子字符串
python
if "(U.S.S.)" in old_text:
# 已包含后缀
5.4 字符串拼接
python
new_text = old_text + "(U.S.S.)"
6. 对象修改的完整流程
┌─────────────────┐
│ rs.AllObjects() │ 获取对象 ID 列表
└────────┬────────┘
▼
┌─────────────────┐
│ sc.doc.Objects.Find() │ 获取 RhinoObject
└────────┬────────┘
▼
┌─────────────────┐
│ rhino_obj.Geometry │ 获取几何对象
└────────┬────────┘
▼
┌─────────────────┐
│ 修改属性值 │ geom.PlainText = ...
└────────┬────────┘
▼
┌─────────────────┐
│ rhino_obj.CommitChanges() │ 提交修改
└────────┬────────┘
▼
┌─────────────────┐
│ sc.doc.Views.Redraw() │ 刷新视图
└─────────────────┘
7. 文字对象的其他属性
7.1 读取属性
python
# 文字高度
height = geom.TextHeight
# 对齐方式
justification = geom.Justification
# 字体
font = geom.Font.FamilyName
# 插入点
plane = geom.Plane
insertion_point = plane.Origin
7.2 修改属性
python
# 修改文字高度
geom.TextHeight = 2.5
# 修改后必须提交
rhino_obj.CommitChanges()
8. 对齐方式详解
python
justification = geom.Justification
| 值 | 说明 |
|---|---|
TextJustification.Left |
左对齐 |
TextJustification.Center |
居中 |
TextJustification.Right |
右对齐 |
TextJustification.TopLeft |
左上 |
TextJustification.BottomCenter |
底部居中 |
| ... | 共 9 种组合 |
9. 使用 rs 模块的替代方案
对于简单的文字操作,也可以用 rs 模块:
9.1 创建文字
python
text_id = rs.AddText("Hello", [0, 0, 0], height=2.5)
9.2 读取文字内容
python
text = rs.TextObjectText(text_id)
9.3 修改文字内容
python
rs.TextObjectText(text_id, "New Text")
注意 :
rs.TextObjectText()对某些复杂的 MText 可能不生效,此时需要用 RhinoCommon 方法。
10. 错误处理
10.1 检查 None
python
rhino_obj = sc.doc.Objects.Find(obj_id)
if rhino_obj: # 确保对象存在
geom = rhino_obj.Geometry
10.2 检查文字内容
python
old_text = geom.PlainText
if old_text: # 确保文字不为空
# 处理文字
10.3 Try-Except 包裹
python
try:
geom.PlainText = new_text
rhino_obj.CommitChanges()
count += 1
except Exception as e:
print("修改失败: {}".format(e))
11. 性能优化
11.1 批量操作时关闭重绘
python
rs.EnableRedraw(False)
# 批量操作...
for obj_id in all_objects:
# 修改对象
rs.EnableRedraw(True)
sc.doc.Views.Redraw()
11.2 使用对象过滤器
python
# 只获取 Annotation 类型的对象
annotations = rs.ObjectsByType(512)
比获取所有对象后再筛选更高效。
12. 完整代码解析
python
#coding=utf-8
import Rhino
import rhinoscriptsyntax as rs
import scriptcontext as sc
# 配置:不需要添加后缀的关键词
skip_keywords = ["BASE PLATE", "CAST-IN", "IN-SITU",
"EMBEDDED", "GROUT", "SHIM"]
# 获取所有对象
all_objects = rs.AllObjects()
count = 0
if all_objects:
for obj_id in all_objects:
obj_type = rs.ObjectType(obj_id)
# 筛选 Annotation 类型
if obj_type == 512:
rhino_obj = sc.doc.Objects.Find(obj_id)
if rhino_obj:
geom = rhino_obj.Geometry
# 确认是文字对象
if isinstance(geom, Rhino.Geometry.TextEntity):
old_text = geom.PlainText
if old_text:
text_upper = old_text.upper()
# 检查是否应该跳过
should_skip = any(kw in text_upper
for kw in skip_keywords)
# 检查是否已有后缀
if "(U.S.S.)" in old_text:
should_skip = True
# 执行修改
if not should_skip:
geom.PlainText = old_text + "(U.S.S.)"
rhino_obj.CommitChanges()
count += 1
sc.doc.Views.Redraw()
print("完成!修改了 {} 个 MText".format(count))
13. 扩展应用
13.1 批量替换文字
python
geom.PlainText = old_text.replace("OLD", "NEW")
13.2 添加前缀
python
geom.PlainText = "PREFIX-" + old_text
13.3 根据图层筛选
python
layer = rs.ObjectLayer(obj_id)
if layer == "TEXT":
# 只处理 TEXT 图层的对象
13.4 统计文字内容
python
text_dict = {}
for obj_id in all_objects:
if rs.ObjectType(obj_id) == 512:
rhino_obj = sc.doc.Objects.Find(obj_id)
if rhino_obj:
geom = rhino_obj.Geometry
if isinstance(geom, Rhino.Geometry.TextEntity):
text = geom.PlainText
text_dict[text] = text_dict.get(text, 0) + 1
for text, count in text_dict.items():
print("{}: {}".format(text, count))
14. 常见问题
Q: 为什么 rs.TextObjectText() 不起作用?
A: 某些复杂的多行文字(MText)需要用 RhinoCommon 的 TextEntity.PlainText 来操作。
Q: 修改后看不到变化?
A: 确保调用了 rhino_obj.CommitChanges() 和 sc.doc.Views.Redraw()。
Q: 如何撤销脚本的修改?
A: 在脚本开头添加 rs.EnableRedraw(False),结束时 rs.EnableRedraw(True)。这样整个操作会被记录为一次撤销步骤。
总结
文字对象处理的核心流程:
- 获取对象 →
rs.AllObjects()或rs.ObjectsByType(512) - 转换为 RhinoObject →
sc.doc.Objects.Find(obj_id) - 获取几何 →
rhino_obj.Geometry - 类型检查 →
isinstance(geom, Rhino.Geometry.TextEntity) - 读写文字 →
geom.PlainText - 提交修改 →
rhino_obj.CommitChanges() - 刷新视图 →
sc.doc.Views.Redraw()
掌握这个流程,就能实现各种文字批量处理任务。