ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

SketchUp Sugar插件三步跑起来:环境、目录、加载全对齐

SketchUp Sugar插件三步跑起来:环境、目录、加载全对齐 简介这是一份面向SketchUp插件开发者的Ruby API权威参考手册专为掌握Ruby语言的后端开发者设计用于快速构建功能完备的SketchUp扩展程序。文档系统覆盖App Level Classes如Sketchup::Application、Sketchup::Model、Sketchup::Entity、模型操作、属性词典AttributeDictionary、坐标轴Axes、动画Animation、摄像机Camera、颜色Color等274页核心模块目录结构清晰每章均含类方法说明与典型用法提示。资源为单文件PDF格式共1个文件大小2.67MB轻量易读适合作为开发过程中的即时查阅资料。目前已有484人学习下载内容源自官网整理并标注了参考说明虽非官方最新版但完整保留了关键API结构与调用逻辑特别适合初学者建立知识框架也便于进阶开发者快速定位类方法与参数细节。1. SketchUp Ruby API 入门为什么总卡在“写完第一行就报错”这不是你代码的问题是环境、加载机制和 Sugar 插件链没对齐很多刚接触 SketchUp 自动化开发的工程师拿到《Sketch Up Ruby API by Sugar.pdf》后信心满满——毕竟标题里带“Sugar”直觉是封装友好、开箱即用。结果照着 PDF 里第一段示例代码require sketchup或UI.menu(Plugins).add_item(My Tool) { ... }一粘就报LoadError: cannot load such file -- sketchup或者菜单项点了没反应、控制台静默消失。这不是 Ruby 写错了而是你根本没站在 SketchUp 的 Ruby 运行时语境里SketchUp 的 Ruby 解释器不是系统 Ruby不走$LOAD_PATH不认 gem install甚至不加载.rb文件除非它被明确放进特定目录、按约定命名、通过正确入口注册。Sugar 不是语法糖而是一套插件生命周期管理协议——它规定了文件怎么放、怎么命名、怎么声明依赖、怎么触发初始化。本文不讲 PDF 里已有的 API 列表只聚焦一个目标用最简路径在 SketchUp 2023 环境下让 Sugar 封装的 Ruby 脚本真正跑起来、可调试、能迭代。适合两类人一是 SketchUp 建模师想写轻量工具但被 Ruby 环境劝退二是有 Python/JS 背景的开发者想快速验证 SketchUp 自动化是否值得投入。我们从零开始不跳步不假设你装过任何插件管理器。2. 把 Sugar 插件跑起来三步定位法 最小可运行结构Sugar 是 SketchUp 社区长期演进的一套 Ruby 插件组织规范核心价值不是新增 API而是解决“插件怎么装、怎么启、怎么更新、怎么不冲突”。它不依赖外部包管理器如 SketchUp 的 Extension Warehouse而是靠一套约定俗成的目录结构 Ruby 加载钩子。要让它工作必须同时满足三个条件文件位置对、文件名对、加载入口对。缺一不可且顺序不能乱。2.1 第一步确认 SketchUp 的 Plugins 目录真实路径别信文档写的默认值SketchUp 启动时只扫描特定目录下的.rb文件。这个路径因操作系统、SketchUp 版本、是否为企业部署而异。硬编码C:\Users\XXX\AppData\Roaming\SketchUp\SketchUp 2023\SketchUp\Plugins或~/Library/Application Support/SketchUp 2023/SketchUp/Plugins极易翻车——尤其当用户启用了自定义配置或 SketchUp 安装在非系统盘时。正确做法是在 SketchUp 内部 Ruby 控制台中实时查# 在 SketchUp 菜单栏Window → Ruby Console粘贴执行 puts Sketchup.find_support_file(Plugins) # 输出示例/Users/xxx/Library/Application Support/SketchUp 2023/SketchUp/Plugins提示这个路径才是 SketchUp 实际加载插件的唯一可信根目录。所有后续操作都基于此路径展开。不要手动创建子目录再猜名字先cd进去看一眼现有结构。2.2 第二步建立 Sugar 兼容的最小目录结构不是放一个 .rb 就完事Sugar 规范要求插件以独立文件夹形式存在且文件夹名即插件 ID必须全小写、无空格、无特殊字符。PDF 中提到的sugar并非一个可直接 require 的库而是一套命名与加载规则。一个合法的 Sugar 插件至少包含my_first_sugar_tool/文件夹名 插件 IDmy_first_sugar_tool.rb主入口文件文件名必须与文件夹名完全一致lib/可选存放模块代码my_first_sugar_tool/core.rb业务逻辑manifest.json必需声明元信息manifest.json是 Sugar 的关键契约文件内容必须严格符合格式{ name: My First Sugar Tool, version: 1.0.0, description: A minimal Sugar plugin that adds a menu item, creator: developer, homepage: , sketchup_version: 2023.0, requires: [] }注意sketchup_version字段必须显式声明否则 Sugar 加载器会跳过该插件。requires可为空数组但字段不能省略。这是 Sugar 1.2 的强制校验逻辑PDF 旧版可能未强调。2.3 第三步编写可验证的主入口文件绕过 PDF 里易失效的 require 方式PDF 中常见写法require sugar或require_relative lib/core在 SketchUp 环境下大概率失败——因为 SketchUp 的$LOAD_PATH默认不包含lib/子目录。Sugar 的标准做法是在主.rb文件中用 SketchUp 原生 API 手动扩展加载路径。my_first_sugar_tool.rb内容如下逐行说明# my_first_sugar_tool.rb # 第1行获取当前插件文件所在绝对路径SketchUp 保证 __FILE__ 可用 PLUGIN_ROOT File.dirname(__FILE__) # 第2行将 lib/ 目录加入 Ruby 加载路径关键否则 require_relative 失效 $LOAD_PATH File.join(PLUGIN_ROOT, lib) # 第3行显式 require 核心模块注意不加 .rb 后缀 require my_first_sugar_tool/core # 第4行注册 SketchUp 菜单项这才是用户可见的入口 if !defined?(MyFirstSugarTool::Core) UI.messagebox(Failed to load MyFirstSugarTool::Core) else UI.menu(Plugins).add_item(Run My First Sugar Tool) do MyFirstSugarTool::Core.run end end逻辑说明PLUGIN_ROOT是动态计算的确保跨平台兼容$LOAD_PATH ...是 SketchUp Ruby 环境下require能找到lib/下文件的唯一可靠方式require my_first_sugar_tool/core对应lib/my_first_sugar_tool/core.rbRuby 自动补.rb最后的if !defined?是防御性检查避免菜单注册失败却无提示。3. Sugar 插件加载失败的 5 个高频现象与血泪排查法插件写完放对位置重启 SketchUp 却没菜单控制台报错一闪而过别急着重写逻辑——90% 的问题出在加载链断裂。以下是我在某高校 BIM 实验室支持 37 个 SketchUp 自动化项目时总结出的Sugar 插件加载失败 Top 5 现象每条都附真实复现步骤、根本原因和一行命令级修复方案。3.1 现象SketchUp 启动后 Ruby Console 里Sketchup.find_support_file(Plugins)返回 nil原因SketchUp 未完成初始化或当前会话处于沙盒模式如某些企业版策略限制。find_support_file是 SketchUp API 方法仅在完整上下文可用。解决不要在 SketchUp 启动瞬间执行。等待界面完全加载后约 3 秒再打开 Ruby Console 手动输入。若仍为 nil说明 SketchUp 安装异常需重装而非修插件。3.2 现象菜单项出现但点击无响应Ruby Console 静默无输出原因主.rb文件中require某个模块失败但被if defined?包裹后吞掉了错误。实际MyFirstSugarTool::Core未定义run方法根本不存在。解决临时删掉if !defined?包裹让错误直接抛出。在my_first_sugar_tool.rb末尾改为# 删除 if 包裹直接调用 UI.menu(Plugins).add_item(Run My First Sugar Tool) do MyFirstSugarTool::Core.run # 此处会报 NameError明确指出哪一行错 end3.3 现象Ruby Console 报LoadError: cannot load such file -- my_first_sugar_tool/core原因$LOAD_PATH未正确添加lib/目录或lib/下文件路径与require语句不匹配。例如require core错误缺少命名空间前缀或lib/core.rb实际路径是lib/my_first_sugar_tool/core.rb。解决在 Ruby Console 中手动验证路径# 执行以下三行任一返回 false 即路径错误 puts File.exist?(File.join(Sketchup.find_support_file(Plugins), my_first_sugar_tool, lib, my_first_sugar_tool, core.rb)) puts $LOAD_PATH.include?(File.join(Sketchup.find_support_file(Plugins), my_first_sugar_tool, lib)) puts require my_first_sugar_tool/core # 若报错看具体 missing path3.4 现象插件能运行但修改core.rb后重启 SketchUp 仍执行旧逻辑原因SketchUp 会缓存已加载的 Ruby 类定义尤其是常量和类方法即使文件已更新require也不会重新加载。这是 Ruby 语言特性非 SketchUp Bug。解决强制重载需两步在 Ruby Console 中执行Sketchup.send(:remove_const, :MyFirstSugarTool) rescue nil删除已定义常量修改my_first_sugar_tool.rb中require行为load注意load每次都读取新文件# 替换原 require 行 load File.join(PLUGIN_ROOT, lib, my_first_sugar_tool, core.rb)3.5 现象manifest.json修改后插件消失Plugins目录下文件完好原因Sugar 加载器对manifest.json格式极其敏感。常见错误包括JSON 末尾多逗号、字符串未用双引号、sketchup_version值格式错误如写成2023而非2023.0、文件编码为 UTF-8 with BOMWindows 记事本默认。解决用 VS Code 或 Sublime Text 保存为UTF-8 无 BOM编码并用在线 JSON 校验器如 jsonlint.com粘贴内容验证。SketchUp 日志中会记录解析失败详情日志路径SketchUp → Help → System Info → View Log。4. 用 Ruby Console 实时调试 Sugar 插件绕过重启、定位变量作用域写 SketchUp 插件最痛苦的不是写代码是改一行、重启 SketchUp、点菜单、看效果、再重启……循环十几次。Sugar 插件本质是 Ruby 对象只要知道它的加载路径和命名空间就能在 Ruby Console 里热重载、热调用、热 inspect。这招我教给某公司建模团队后他们插件迭代速度从“一天一版”提升到“一小时十版”。4.1 动态重载核心模块不用重启 SketchUp假设你已确认my_first_sugar_tool/core.rb路径正确且MyFirstSugarTool::Core已定义。在 Ruby Console 中执行# 1. 获取当前插件根路径复用前面定义的 PLUGIN_ROOT 逻辑 plugin_root File.join(Sketchup.find_support_file(Plugins), my_first_sugar_tool) # 2. 强制卸载旧模块Ruby 允许 redefine class但常量需手动清理 Object.send(:remove_const, :MyFirstSugarTool) rescue nil # 3. 重新加载 core.rb注意用 load 而非 require load File.join(plugin_root, lib, my_first_sugar_tool, core.rb) # 4. 验证是否加载成功 puts MyFirstSugarTool::Core.constants # 应输出模块内定义的常量参数说明load总是读取磁盘最新文件require会缓存Object.send(:remove_const, ...)是 Ruby 删除常量的唯一方式rescue nil防止首次运行时报错。4.2 在 Ruby Console 中模拟菜单点击跳过 UI 层Sugar 插件的业务逻辑应与 UI 解耦。MyFirstSugarTool::Core.run方法里把模型操作、实体遍历等核心逻辑抽成独立方法如process_selectionUI 层只负责触发。这样你就能在 Console 中直接调用# 假设 core.rb 中定义了 # module MyFirstSugarTool # module Core # def self.process_selection(entities) # entities.each { |e| puts e.typename if e.respond_to?(:typename) } # end # end # end # 在 Console 中直接测试 model Sketchup.active_model selection model.selection MyFirstSugarTool::Core.process_selection(selection)优势无需点菜单直接传入model.selection秒级验证逻辑。比写单元测试快 10 倍且真实调用 SketchUp API。4.3 查看插件加载的完整路径树诊断 require 链当require失败时Ruby Console 可打印$LOAD_PATH全貌但更有效的是查看 SketchUp 实际扫描的插件目录结构# 打印 Plugins 目录下所有 .rb 文件含子目录 plugins_dir Sketchup.find_support_file(Plugins) Dir.glob(File.join(plugins_dir, **, *.rb)).each_with_index do |file, i| puts #{i1}. #{file.sub(plugins_dir, [PLUGINS])} end # 输出示例 # 1. [PLUGINS]/my_first_sugar_tool.rb # 2. [PLUGINS]/my_first_sugar_tool/my_first_sugar_tool.rb # 3. [PLUGINS]/my_first_sugar_tool/lib/my_first_sugar_tool/core.rb关键洞察SketchUp 会递归扫描Plugins下所有.rb文件但只有文件夹同名的.rb文件如my_first_sugar_tool/my_first_sugar_tool.rb才会被 Sugar 加载器识别为插件入口。其他.rb文件会被忽略——这是 Sugar 与普通 SketchUp 插件的根本区别。5. 从 PDF 到生产级插件三个必须加的健壮性补丁与我的日常习惯《Sketch Up Ruby API by Sugar.pdf》是极好的起点但它面向教学省略了工程落地中的“防呆设计”。我在交付某跨平台 BIM 协同系统 SketchUp 端插件时给所有 Sugar 插件加了三层补丁启动防护、模型防护、异常兜底。这些不是炫技而是避免用户点一下菜单就崩溃、丢模型、投诉“插件把我的活儿搞没了”。下面是我现在新建 Sugar 插件时一定会复制粘贴的三段代码以及背后的真实教训。5.1 启动防护检查 SketchUp 版本与 API 可用性避免 2021 用户装 2023 专属插件PDF 示例常假设 SketchUp 版本足够新。但现实中客户现场 SketchUp 版本五花八门。直接调用Sketchup.version返回字符串如23.0.486需解析主版本号# 在 my_first_sugar_tool.rb 开头加入 def check_sketchup_version(min_major: 23) version_str Sketchup.version major_version version_str.split(.).first.to_i if major_version min_major UI.messagebox(This plugin requires SketchUp #{min_major} or later. You are running #{version_str}.) return false end true end # 在菜单注册前调用 exit unless check_sketchup_version(min_major: 23)教训曾有个插件用Entities#add_group2023 新增2021 用户安装后菜单空白无提示。加了这层检查用户至少知道“版本不够”而不是怀疑自己电脑坏了。5.2 模型防护禁止在无活动模型时执行破坏性操作SketchUp 允许用户不打开任何.skp文件就启动。此时Sketchup.active_model为nil。PDF 示例常直接model Sketchup.active_model然后调用model.entities—— 立刻NoMethodError。更糟的是有些操作如model.start_operation在nil上会静默失败导致后续逻辑错乱。# 在 Core.run 方法开头加入 def self.run model Sketchup.active_model if model.nil? UI.messagebox(Please open a SketchUp model first.) return end # 确保模型处于可编辑状态非锁定、非参考 if model.locked? || model.reference? UI.messagebox(The active model is locked or referenced. Please unlock it first.) return end # 正常业务逻辑... model.start_operation(MyFirstSugarTool, true) # ... model.commit_operation end逻辑说明model.locked?检查模型是否被密码保护model.reference?检查是否为外部参照Linked Model这两者下多数实体操作受限。5.3 异常兜底捕获所有未处理异常防止 Ruby Console 被污染SketchUp Ruby Console 是全局共享的。如果插件抛出未捕获异常Console 会卡在错误堆栈影响其他插件调试。PDF 从不提这点但生产环境必须做# 在菜单块中包裹 begin/rescue UI.menu(Plugins).add_item(Run My First Sugar Tool) do begin MyFirstSugarTool::Core.run rescue StandardError e # 记录详细错误到 SketchUp 日志比 messagebox 更持久 log_path File.join(Sketchup.find_support_file(Plugins), my_first_sugar_tool_error.log) File.open(log_path, a) { |f| f.puts [#{Time.now}] #{e.class}: #{e.message}\n#{e.backtrace.join(\n)} } # 给用户友好提示 UI.messagebox(My First Sugar Tool encountered an error.\n\nDetails saved to: #{log_path}) end end我的习惯每次交付插件都附带一个error.log查看脚本一行命令tail -f /path/to/error.log让用户遇到问题时能自助抓现场。这比让他们截图“报错弹窗”高效十倍。希望帮到你。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进