AutoCAD 图框更新





5.00/5 (1投票)
介绍如何创建 AutoCAD 插件,并深入讨论如何更新 AutoCAD 图纸中的标题栏
引言
使用 Autodesk 的 ObjectARX for C# .NET 可以实现 AutoCAD 插件的创建。本文将首先介绍如何传递 AutoCAD 命令和接收消息。然后,我们将重点讲解如何更新当前打开的图纸中具有预定义属性的命名标题栏。最后,我们将加载指定目录中的所有图纸文件,并更新每一张图纸的标题栏。
要下载 ObjectARX SDK for .NET 并获取更多信息,请在此处 开始。请确保下载与您的 AutoCAD 版本匹配的 SDK。请查看 AutoCAD .NET 开发者指南的 PDF。该 PDF 来自较旧版本,要查找较新的在线文档,请在 AutoCAD 开发者中心 的文档部分查找。此外,在 Through the Interface 和 ADN Dev Blog 还有两个博客。
背景
作为一名控制工程师,我的职责之一是为各种制造设备创建电气图纸集。在过去的 14 多年里,我一直受益于使用 AutoCAD Electrical 来完成这项任务。当我最近换了一份工作后,AutoCAD Electrical 的效率、易用性、减少人为错误的能力和强大功能变得更加明显。过去只需几秒钟(例如,更新图纸上的标题栏)现在却需要几个小时,而且容易出错。我决定开始编写代码来恢复一些丢失的功能,首先是自动更新标题栏。由于除了 Autodesk 之外,我找不到多少帮助,因此我决定回馈社区。
项目
AutoCAD 插件利用 C# .NET 4.7 类库项目。我使用 4.7 是因为我针对的是 AutoCAD 2018 和 Windows 10。SDK 附带了几个可以引用并在您的类库中使用的 DLL 文件。其中三个 DLL 文件(即 AcCoreMgd.dll、AcDbMgd.dll 和 AcMgd.dll)是最常用的,并且在此类库项目中被引用。
下面的代码显示了 Update Title Block 类项目的开始部分。using
语句利用了我上面描述的已引用 DLL 文件。类开头的字段变量允许进行硬编码值。在我的生产代码中,我使用 SQL 数据库代码将这些信息拉取到我的命令方法中。
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.ApplicationServices.Core;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.IO;
namespace UpdateTitleBlock
{
public class Revision
{
private string BlockName = "Your Block Name";
private Dictionary<string, string> Attributes = new Dictionary<string, string>
{
{"AttributeName1", "AttributeValue1"},
{"AttributeName2", "AttributeValue2"},
{"AttributeName3", "AttributeValue3"}
};
string path = "C:\\Users\\";
使用命令
在项目属性的 Debug 选项卡中,将 AutoCAD 的 EXE 文件添加到 Start Action 部分。现在,当您按下 Start 按钮测试代码时,AutoCAD 程序将启动。AutoCAD 启动后,在其命令提示符处键入 NETLOAD。然后指向 bin/debug 文件夹中的 UpdateTitleBlock.dll 文件。现在您的插件已加载,您可以使用新命令了。请注意,您可以像平常一样进行调试,设置断点等来解决可能出现的任何问题。
下面的代码显示了一系列简单的命令。请注意,此命令标记为 [CommandMethod("CMD")]
。此声明既将方法标记为新的 AutoCAD 命令,又指定了运行命令的关键字。此 Command Method 使用 CMD
作为关键字。加载 DLL 后(如上所述),在 AutoCAD 命令提示符处键入 CMD
即可运行该命令。
此命令实际上是一个命令包含三个功能(即,再生、缩放范围和快速保存)。命令通过活动的图纸的 AutoCAD 编辑器进行控制。Editor
对象允许您的代码与 AutoCAD 的命令提示符进行交互。此对象可以直接发出命令(如本例所示),发送消息,并从用户那里获取输入。ed.command()
参数接受一个数组,其中每个索引都导致按下 Enter 键。例如,ZOOM 命令会在 AutoCAD 的命令提示符处输入 ZOOM
,然后按 Enter。第二个元素(E
)选择 ZOOM
的 extents(范围)选项。
[CommandMethod("CMD")]
public void Commands()
{
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
ed.Command("REGEN");
ed.Command("ZOOM", "E");
ed.Command("QSAVE");
}
更新标题栏
下面的代码允许基于硬编码的 Dictionary 更新活动图纸的标题栏属性。第一步是获取活动图纸的数据库。此数据库存储图纸的所有信息,包括块。标题栏只是图纸中可能包含的众多块之一。属性是块中的可变文本。我们正在更新的就是这些文本。请注意,此代码仅在您的标题栏位于模型空间而不是布局空间中时才有效。
然后使用数据库启动事务。像所有事务一样,在不使用 commit 的情况下,任何更改都不会生效。在事务中,我们首先从数据库中获取块表。块表用于获取模型空间的块表记录。块表记录存储块参照。这是对图纸中各种块的引用。每个参照都有一个属性集合。最后,属性集合包含我们试图更改的属性。
现在我们有了块表记录,我们可以对其进行迭代,查看每个 ID。每个 ID 都用于创建相应的块参照。一旦我们有了块参照,我们就可以将其名称与我们正在搜索的名称(即硬编码的 BlockName
)进行比较。一旦我们找到与标题栏匹配的块名称,就会迭代其属性集合。每次属性的标签名称与字典键匹配时,我们都会用相应的字典值更改其值。
[CommandMethod("REV")]
public void EditBlock()
{
var acDb = HostApplicationServices.WorkingDatabase;
using (var acTrans = acDb.TransactionManager.StartTransaction())
{
var acBlockTable =
acTrans.GetObject(acDb.BlockTableId, OpenMode.ForRead) as BlockTable;
if (acBlockTable == null) return;
var acBlockTableRecord = acTrans.GetObject(acBlockTable[BlockTableRecord.ModelSpace],
OpenMode.ForRead) as BlockTableRecord;
if (acBlockTableRecord == null) return;
foreach (var blkId in acBlockTableRecord)
{
var acBlock = acTrans.GetObject(blkId, OpenMode.ForWrite) as BlockReference;
if (acBlock == null) continue;
if (!acBlock.Name.Equals
(BlockName, StringComparison.CurrentCultureIgnoreCase)) continue;
foreach (ObjectId attId in acBlock.AttributeCollection)
{
var acAtt = acTrans.GetObject(attId, OpenMode.ForWrite) as AttributeReference;
if (acAtt == null) continue;
if (!Attributes.ContainsKey(acAtt.Tag)) continue;
acAtt.UpgradeOpen();
acAtt.TextString = Attributes[acAtt.Tag];
}
}
acTrans.Commit();
}
}
更新目录中的所有标题栏
此命令会加载目录中硬编码为字符串 'path
' 的所有图纸。当每张图纸被加载时,标题栏会被更新然后保存。加载意味着图纸被加载到内存中,并且永远不会显示在 AutoCAD 用户界面上。
如果图纸目录包含大量图纸,此命令可能会运行很长时间。因此,该命令利用一个任务将该进程发送到单独的线程。该命令首先执行的操作是存储当前图纸的数据库。此数据库将在命令结束时恢复。请注意,您不应该在 AutoCAD 中打开任何您试图加载的图纸。
此命令简单地使用上面的标题栏更新代码,并将其迭代到一个文件数组上。首先,使用路径收集 DirectoryInfo
。接下来,这被用来收集一个 FileInfo
数组,其中包含 path 目录中的所有图纸。在迭代每张文件时,会使用一个新的数据库将图纸读入内存中的数据库。然后,使用内存数据库像之前一样启动事务,并更新标题栏,就像上面所做的那样。
[CommandMethod("REVALL")]
public void RevAllDwgFiles()
{
Task t = Task.Run(() =>
{
// store the current database
Database currentDatabase =
Autodesk.AutoCAD.ApplicationServices.Core.Application.
DocumentManager.MdiActiveDocument.Database;
try
{
DirectoryInfo d = new DirectoryInfo(path);
FileInfo[] Files = d.GetFiles("*.dwg");
foreach (FileInfo file in Files)
{
var fileName = Path.GetFileName(file.FullName);
string dwgFlpath = path + fileName;
using (Database acDb = new Database(false, true))
{
acDb.ReadDwgFile
(dwgFlpath, FileOpenMode.OpenForReadAndAllShare, false, null);
HostApplicationServices.WorkingDatabase = acDb;
using (Transaction acTrans =
acDb.TransactionManager.StartTransaction())
{
var acBlockTable = acTrans.GetObject
(acDb.BlockTableId, OpenMode.ForRead) as BlockTable;
if (acBlockTable == null) return;
var acBlockTableRecord = acTrans.GetObject
(acBlockTable[BlockTableRecord.ModelSpace],
OpenMode.ForRead) as BlockTableRecord;
if (acBlockTableRecord == null) return;
foreach (var blkId in acBlockTableRecord)
{
var acBlock =
acTrans.GetObject(blkId, OpenMode.ForWrite)
as BlockReference;
if (acBlock == null) continue;
if (!acBlock.Name.Equals
(BlockName, StringComparison.CurrentCultureIgnoreCase))
continue;
foreach (ObjectId attId in acBlock.AttributeCollection)
{
var acAtt =
acTrans.GetObject(attId, OpenMode.ForWrite)
as AttributeReference;
if (acAtt == null) continue;
if (!Attributes.ContainsKey(acAtt.Tag)) continue;
acAtt.UpgradeOpen();
acAtt.TextString = Attributes[acAtt.Tag];
}
}
acTrans.Commit();
}
acDb.SaveAs(dwgFlpath, DwgVersion.AC1027);
// reset the current database as working database
HostApplicationServices.WorkingDatabase = currentDatabase;
}
}
Autodesk.AutoCAD.ApplicationServices.Application.ShowAlertDialog
("All files processed");
}
catch (System.Exception ex)
{
Autodesk.AutoCAD.ApplicationServices.Application.
DocumentManager.MdiActiveDocument.Editor.WriteMessage(ex.ToString());
}
});
t.Wait();
}
}
历史
- 2018年6月 - 创建插件
- 2020年1月 - 创建文章