【Rhino】【Python】对包含特定关键词的文字的MTEXT对象添加指定内容

本文通过一个实际案例------批量为图纸中的文字添加 "(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)。这样整个操作会被记录为一次撤销步骤。


总结

文字对象处理的核心流程:

  1. 获取对象 → rs.AllObjects() 或 rs.ObjectsByType(512)
  2. 转换为 RhinoObject → sc.doc.Objects.Find(obj_id)
  3. 获取几何 → rhino_obj.Geometry
  4. 类型检查 → isinstance(geom, Rhino.Geometry.TextEntity)
  5. 读写文字 → geom.PlainText
  6. 提交修改 → rhino_obj.CommitChanges()
  7. 刷新视图 → sc.doc.Views.Redraw()

掌握这个流程,就能实现各种文字批量处理任务。


参考资源

相关推荐
默_笙4 天前
🍙 给每个请求过安检:FastAPI 是怎么把校验写进类型注解的
python
未济4 天前
linux 配置环境变量
linux
傲世仙尊4 天前
目录即文件-Ext文件系统收尾篇
linux·c语言
qq_426003964 天前
启动playwright录制codegen生成自动化测试脚本
python·自动化
虎头金猫4 天前
4K 视频总卡在公网带宽?用 N1 + OpenList 把网盘播放链路重新理顺
运维·服务器·网络·python·容器·beautifulsoup·pandas
长沙三为智能科技4 天前
家政小程序开发从0到上线:五阶段交付流程与验收清单
python
伞伞悦读4 天前
【第38期】Python 模块与包详解:import、from、模块搜索路径、包结构和 __init__
开发语言·python
只睡四小时4 天前
Canvas 弹道联机实战:700 行 + 固定时间步长
python·websocket·html5·游戏开发·canvas
_艾伦 耶格尔.4 天前
进程间通信
linux
Liuqy-054 天前
Linux IO编程——静态库、动态库
linux