Revit 2015-2018中Python放置详图构件脚本报错求助
解决Revit详图构件按钮的commandData问题及宏测试失败方案
嘿,我来帮你搞定这个Revit按钮的问题!你遇到的核心问题其实是宏与外部命令的上下文差异,加上族过滤逻辑的小疏漏,咱们一步步来解决:
一、宏环境没有commandData的问题
宏的运行逻辑和自定义Ribbon按钮的外部命令完全不一样:
- 外部命令的入口自带
commandData参数,但宏是直接在Revit内部跑的,根本没有这个参数!你得直接从this.Application拿当前文档。
给你改好的宏测试代码,直接就能用:
// 宏专用代码,去掉对commandData的依赖 UIDocument uidoc = this.Application.ActiveUIDocument; Document doc = uidoc.Document; // 匹配你目标族的名称和类型名 FamilySymbol symbol = GetElements<FamilySymbol>(doc) .Where(item => item.FamilyName == "Break Line" && item.Name == "Break Line") .FirstOrDefault(); if (symbol != null) { // 别忘了激活族类型!不激活的话根本没法放置 if (!symbol.IsActive) { symbol.Activate(); doc.Regenerate(); } uidoc.PromptForFamilyInstancePlacement(symbol); } else { TaskDialog.Show("提示", "没找到你要的Break Line详图构件哦"); } /// <summary> /// 获取指定类型的元素集合 /// <para>指定类型必须继承自Element,用Element会拿到所有元素</para> /// </summary> /// <typeparam name="T">要获取的元素类型</typeparam> /// <param name="document">Revit文档</param> /// <returns>指定类型的元素列表</returns> public IEnumerable<T> GetElements<T>(Document document) where T : Element { FilteredElementCollector collector = new FilteredElementCollector(document); collector.OfClass(typeof(T)); return collector.Cast<T>(); }
二、族过滤的关键细节
你原来的代码只过滤了item.Name,但对于Detail Items的族,必须同时匹配族名称(FamilyName)和族类型名称(Name)——毕竟不同族可能有重名的类型。你的目标族信息是Family = Break Line,Type = Break Line,所以过滤条件必须同时卡这两个值。
另外一定要检查族类型是否激活:如果目标类型不是当前激活的,调用放置命令直接会失败,所以必须先激活再执行放置。
三、适配自定义Ribbon按钮的外部命令代码(兼容2015-2018)
当你把代码改成外部命令时,要回到commandData的上下文,同时保证版本兼容性:
using Autodesk.Revit.Attributes; using Autodesk.Revit.DB; using Autodesk.Revit.UI; using System.Collections.Generic; using System.Linq; [Transaction(TransactionMode.Manual)] public class PlaceBreakLine : IExternalCommand { public Result Execute(ExternalCommandData commandData, ref string message, ElementSet elements) { UIApplication uiapp = commandData.Application; UIDocument uidoc = uiapp.ActiveUIDocument; Document doc = uidoc.Document; FamilySymbol symbol = GetElements<FamilySymbol>(doc) .Where(item => item.FamilyName == "Break Line" && item.Name == "Break Line") .FirstOrDefault(); if (symbol == null) { message = "未找到名为Break Line的详图构件族类型"; return Result.Failed; } // 激活族类型需要事务,这是Revit API的硬性要求 if (!symbol.IsActive) { using (Transaction trans = new Transaction(doc, "激活Break Line族类型")) { trans.Start(); symbol.Activate(); doc.Regenerate(); trans.Commit(); } } uidoc.PromptForFamilyInstancePlacement(symbol); return Result.Succeeded; } public IEnumerable<T> GetElements<T>(Document document) where T : Element { FilteredElementCollector collector = new FilteredElementCollector(document); collector.OfClass(typeof(T)); return collector.Cast<T>(); } }
四、版本兼容的注意点
- Revit 2015-2018的API里,
FilteredElementCollector.OfClass(typeof(T))完全能用,不用改; PromptForFamilyInstancePlacement方法在这些版本里参数一致,直接传FamilySymbol就行;- 激活族类型必须开事务,这在所有Revit版本里都是强制要求的,原代码没加事务肯定会报错。
五、测试步骤
- 先在宏编辑器里跑修改后的宏代码,确认能找到族并弹出放置界面;
- 把外部命令代码编译成DLL,加载到Revit里绑定到你的自定义Ribbon按钮;
- 在2015-2018各版本Revit里测试,确保功能正常。
内容的提问来源于stack exchange,提问作者J. Kirkpatrick
相关产品推荐
相关产品推荐

