AutoCAD VBAの極意:全文字オブジェクトを一括制御!Text/MTextのフォントと高さを完璧に操る
皆さん、こんにちは! AutoCADの自動化に情熱を注ぐチーフアーキテクトです。
日々設計業務に勤しむ中で、「この図面の文字、フォントがバラバラで統一感がないな…」「全体的に文字が小さすぎて見にくいから、まとめて大きくしたい!」と感じたことはありませんか? 手作業で一つ一つ修正するのは、まさに気の遠くなる作業ですよね。
そんな時、「マクロの記録」でなんとなく動くコードは作れるけれど、もっとスマートに、もっと確実に、そして何よりも「パフォーマンスを意識して」解決したい。そう願うあなたのために、今回はAutoCAD VBAを使って、図面内の全てのテキスト(Text)およびMTextオブジェクトのフォントと高さを一括で変更する方法を、私の持つ「極限の知見」とともにお伝えします。
この記事を読み終えれば、あなたは単にコードを動かすだけでなく、その裏側にあるAutoCADオブジェクトモデルの本質を理解し、次のステップへと確実に進めるでしょう。さあ、一緒にAutoCAD VBAの扉を深く開いていきましょう。
—
目次
1. AutoCAD VBAの心臓部:`AcadApplication`と`AcadDocument`を理解する
2. 図形要素への旅:`AcadBlocks`コレクションと`For Each`ループ
3. 目的の文字を捕獲せよ:`AcadText`と`AcadMText`の識別
4. 実践!全文字オブジェクトのフォントと高さを一括変更するVBAコード
5. 知見を深める:パフォーマンスとライフサイクルを意識したプログラミング
6. さらなる高みへ:応用と拡張のヒント
—
1. AutoCAD VBAの心臓部:`AcadApplication`と`AcadDocument`を理解する
AutoCAD VBAを語る上で、この二つのオブジェクトは避けて通れません。これらは、VBAからAutoCADそのもの、そして開いている図面を操作するための「入口」であり、「心臓部」と言っても過言ではありません。
`AcadApplication`:AutoCADアプリケーションそのもの
これは、現在実行中のAutoCADアプリケーションインスタンス全体を表します。VBAからAutoCADの環境設定、開いているすべての図面、さらにはアプリケーションレベルのイベント(たとえば、新しい図面が開かれた時など)にアクセスするために使います。
例えるなら、`AcadApplication`はAutoCADという巨大なオフィスビル全体です。このビルには多くの部屋(図面)があり、様々な設備(設定)が備わっています。
VBAでは、`ThisDrawing.Application`で現在操作している図面が属するAutoCADアプリケーションにアクセスできます。
`AcadDocument`:開いている個々の図面
これは、AutoCADで現在開いている個々の図面ファイルを表します。図面内のオブジェクト(線、円、文字など)へのアクセス、画層の管理、ブロック定義の操作、レイアウト空間の制御など、ほとんどの図面固有の操作は`AcadDocument`オブジェクトを通じて行われます。
先ほどの例えで言えば、`AcadDocument`はオフィスビルの中の特定の「部屋」です。この部屋には、机や椅子(図形)、書類棚(画層)、設計図(ブロック)など、様々な要素が詰まっています。
VBAで現在アクティブな図面(つまり、あなたが今見ている図面)にアクセスするには、主に以下の方法を使います。
- `ThisDrawing`: VBAプロジェクトが埋め込まれている図面ファイルそのものを指します。
- `Application.ActiveDocument`: 現在AutoCADでアクティブになっている(画面に表示されている)図面を指します。VBAプロジェクトが埋め込まれていない、別の図面を操作したい場合に非常に有用です。
今回のテーマである「図面内の全文字オブジェクトを変更する」という目的においては、`ThisDrawing`または`Application.ActiveDocument`から、その図面内の要素にアクセスしていくことになります。
【極限の知見】オブジェクトのライフサイクルと参照
VBAでオブジェクトを扱う際、`Set`キーワードを使ってオブジェクト変数に参照を割り当てます。例えば `Set objDoc = ThisDrawing` のように。この時、`objDoc`はオブジェクトそのものではなく、そのオブジェクトへの「参照」を保持しているに過ぎません。処理が終わったら `Set objDoc = Nothing` とすることで、不要になった参照を解放し、メモリリークを防ぐことが重要です。特に大規模な図面や長時間の処理では、この意識がパフォーマンスと安定性に直結します。
2. 図形要素への旅:`AcadBlocks`コレクションと`For Each`ループ
AutoCADの図面には、様々な図形要素が存在します。線、円、文字、ブロック…これらは全て`AcadEntity`という最も基本的な図形オブジェクトの派生形です。では、これらの図形にVBAからどのようにアクセスするのでしょうか?
`AcadBlocks`コレクションとは?
AutoCADの図面では、モデル空間(ModelSpace)とレイアウト空間(PaperSpace)という概念があります。これらは、実は`AcadBlock`オブジェクトの一種として扱われ、`AcadBlocks`というコレクションの中に格納されています。
- `ThisDrawing.ModelSpace`: モデル空間(図面作成の主空間)に含まれる全ての図形オブジェクトのコレクションです。
- `ThisDrawing.PaperSpace`: レイアウト空間(印刷レイアウト)に含まれる全ての図形オブジェクトのコレクションです。
通常、図面本体に描かれた文字は`ModelSpace`に含まれています。ブロックとして挿入された文字や、外部参照内の文字は少し複雑ですが、今回のシンプル化された目的では`ModelSpace`のみを対象とすれば十分でしょう。
`For Each`ループで図形要素を巡る
`For Each`ループは、コレクション内の全ての要素を一つずつ処理する際に非常に便利です。
‘ 変数宣言
Dim objEntity As AcadEntity
‘ モデル空間内の全ての図形をループで処理
For Each objEntity In ThisDrawing.ModelSpace
‘ ここでobjEntityに対して処理を行う
Next objEntity
しかし、ここで一つ重要なポイントがあります。`ModelSpace`コレクションには、`AcadText`や`AcadMText`だけでなく、`AcadLine`、`AcadCircle`など、様々な種類の図形が混在しています。目的の文字オブジェクトだけを抽出するには、次のステップで「型の識別」が必要になります。
【極限の知見】Variant型とパフォーマンス
`For Each`ループでコレクションの要素を受け取る際に、`Dim objEntity As Object` や `Dim objEntity As Variant` と宣言することがよくあります。`Variant`型はどんな型のデータでも受け取れるため非常に柔軟ですが、型変換のオーバーヘッドが発生するため、パフォーマンスが若干低下する可能性があります。しかし、AutoCADオブジェクトモデルでは、コレクション内の要素が厳密な型を持つ保証がない場合や、複数の型をまとめて扱いたい場合に`Object`または`Variant`を使用するのは一般的なプラクティスです。今回は後続の型判定で具体的な型にキャストするため、`AcadEntity`として宣言しつつ、必要に応じて`Object`や`Variant`も視野に入れると良いでしょう。
3. 目的の文字を捕獲せよ:`AcadText`と`AcadMText`の識別
`For Each`ループで取得した`objEntity`は、現時点では「一般的な図形」でしかありません。これを「文字オブジェクト」として認識させ、フォントや高さを変更するためのプロパティにアクセスするには、そのオブジェクトが`AcadText`または`AcadMText`のどちらであるかを識別する必要があります。
`TypeOf`演算子と`TypeName`関数
オブジェクトの型を識別する方法はいくつかありますが、VBAで最もよく使われるのは`TypeOf … Is`演算子と`TypeName`関数です。
- `TypeOf obj Is AcadText`: `obj`が`AcadText`型またはその派生型であるかを真偽値で返します。非常に効率的で、オブジェクトの実際の型に基づいて処理を分岐させるのに適しています。
- `TypeName(obj)`: `obj`の型名を文字列で返します(例: “AcadText”, “AcadMText”)。デバッグ目的や、型名そのものに基づいて柔軟な処理をしたい場合に便利です。
今回は、`TypeOf … Is`演算子を使って、確実に`AcadText`または`AcadMText`であるかを判定し、そのオブジェクトを適切な型の変数に「キャスト」してプロパティにアクセスします。
‘ … For Each objEntity In ThisDrawing.ModelSpace …
If TypeOf objEntity Is AcadText Then
‘ objEntityはAcadTextオブジェクトである
Dim objText As AcadText
Set objText = objEntity ‘ AcadEntityからAcadTextへキャスト
‘ ここでobjTextのプロパティを操作
ElseIf TypeOf objEntity Is AcadMText Then
‘ objEntityはAcadMTextオブジェクトである
Dim objMText As AcadMText
Set objMText = objEntity ‘ AcadEntityからAcadMTextへキャスト
‘ ここでobjMTextのプロパティを操作
End If
‘ … Next objEntity …
`AcadText`と`AcadMText`のプロパティ
両者とも文字オブジェクトですが、その構造には違いがあります。しかし、フォント名と高さに関しては共通のプロパティを持っています。
- `FontFile`プロパティ: 文字のフォントファイルを指定します。例えば、`”MS ゴシック.ttf”` のように、フォントのファイル名(拡張子含む)を文字列で設定します。システムフォント、SHXフォント、TrueTypeフォントなどに対応しますが、AutoCADが認識できるフォントである必要があります。
- `Height`プロパティ: 文字の高さを図面単位で指定します。例えば、`5.0` のように数値で設定します。
【極限の知見】フォントファイルの指定の罠
`FontFile`プロパティは通常フォントのファイル名を指定しますが、システムによっては絶対パスを要求する場合もあります。また、AutoCADが使用するSHXフォントとWindowsのTrueTypeフォントでは、内部的な管理方法が異なります。特定のフォントが見つからない場合は、AutoCADのサポートパス設定や、Windowsのフォントフォルダを確認する必要があります。
4. 実践!全文字オブジェクトのフォントと高さを一括変更するVBAコード
それでは、これまでの知識を統合し、実際に動作するVBAコードを作成しましょう。今回は、AutoCADのVBAエディタ(VBE)で標準モジュールに記述することを想定しています。
Option Explicit ‘ 変数宣言を強制する(推奨)
Sub ChangeAllTextFontAndHeight()
‘ — 設定値 —
Const NEW_FONT_NAME As String = “メイリオ.ttf” ‘ 変更したいフォントファイル名(例: “MS ゴシック.ttf”, “Arial.ttf”)
Const NEW_TEXT_HEIGHT As Double = 3.5 ‘ 変更したい文字の高さ(図面単位)
‘ —————
‘ — オブジェクト変数宣言 —
Dim acadApp As AcadApplication ‘ AutoCADアプリケーション
Dim acadDoc As AcadDocument ‘ 現在の図面ドキュメント
Dim objEntity As AcadEntity ‘ 図面内の全ての図形オブジェクトを一時的に格納
Dim objText As AcadText ‘ Textオブジェクトを格納
Dim objMText As AcadMText ‘ MTextオブジェクトを格納
Dim lngCount As Long ‘ 処理した文字オブジェクトの数をカウント
‘ —————————-
‘ エラー発生時の処理を指定
On Error GoTo ErrorHandler
‘ AutoCADアプリケーションと現在アクティブなドキュメントを取得
Set acadApp = Application
Set acadDoc = ThisDrawing ‘ 現在のVBAプロジェクトが埋め込まれた図面を操作
‘ ユーザーへの情報提供
acadApp.ActiveDocument.Utility.Prompt vbCrLf & “全ての文字オブジェクトのフォントと高さを変更します…” & vbCrLf
‘ — パフォーマンス向上のための設定 —
‘ 画面更新を一時停止し、処理を高速化
acadApp.ActiveDocument.ActiveViewport.Regen False ‘ 現在のビューポートの再描画を停止
acadApp.ScreenUpdating = False ‘ AutoCADの画面更新を停止
acadDoc.StartUndoGroup ‘ 処理全体を一つのアンドゥグループにまとめる
‘ ————————————–
lngCount = 0 ‘ カウンターを初期化
‘ モデル空間内の全ての図形をループで処理
For Each objEntity In acadDoc.ModelSpace
‘ オブジェクトがTextオブジェクトであるか判定
If TypeOf objEntity Is AcadText Then
Set objText = objEntity
On Error Resume Next ‘ 特定のプロパティ設定でエラーが発生しても処理を続行
objText.FontFile = NEW_FONT_NAME
objText.Height = NEW_TEXT_HEIGHT
On Error GoTo ErrorHandler ‘ エラーハンドラに戻す
lngCount = lngCount + 1
‘ DoEventsは大規模図面でVBAがフリーズしたように見えないようにする(任意)
‘ ただし、頻繁に呼び出すとパフォーマンスに影響が出る可能性もある
‘ If lngCount Mod 100 = 0 Then DoEvents
‘ オブジェクトがMTextオブジェクトであるか判定
ElseIf TypeOf objEntity Is AcadMText Then
Set objMText = objEntity
On Error Resume Next ‘ 特定のプロパティ設定でエラーが発生しても処理を続行
objMText.FontFile = NEW_FONT_NAME
objMText.Height = NEW_TEXT_HEIGHT
On Error GoTo ErrorHandler ‘ エラーハンドラに戻す
lngCount = lngCount + 1
‘ If lngCount Mod 100 = 0 Then DoEvents
End If
‘ オブジェクト参照を解放(メモリ使用量削減のため、極めて重要)
Set objEntity = Nothing
‘ ステータスバーに進捗を表示(ユーザーへのフィードバック)
acadApp.StatusBar = “処理中: ” & lngCount & “個の文字オブジェクトを処理しました…”
Next objEntity
‘ — 処理後の設定 —
acadDoc.EndUndoGroup ‘ アンドゥグループを終了
acadApp.ScreenUpdating = True ‘ AutoCADの画面更新を再開
acadApp.ZoomExtents ‘ 図面全体を表示
acadDoc.Regen acActiveViewport ‘ 現在のビューポートを再描画して変更を反映
‘ ——————–
‘ 完了メッセージ
acadApp.ActiveDocument.Utility.Prompt vbCrLf & “処理が完了しました。合計 ” & lngCount & “個の文字オブジェクトを変更しました。” & vbCrLf
acadApp.StatusBar = “処理完了。”
GoTo CleanUp ‘ 正常終了
ErrorHandler:
‘ エラーが発生した場合の処理
MsgBox “エラーが発生しました: ” & Err.Description, vbCritical, “VBAエラー”
‘ エラー発生時でもアンドゥグループは終了させる
If acadDoc.GetUndoGroupOpen Then acadDoc.EndUndoGroup
‘ 画面更新も再開する
acadApp.ScreenUpdating = True
acadApp.ActiveDocument.Regen acActiveViewport
CleanUp:
‘ 使用したオブジェクトの参照を解放する(メモリリーク防止のため重要)
Set objText = Nothing
Set objMText = Nothing
Set objEntity = Nothing
Set acadDoc = Nothing
Set acadApp = Nothing
End Sub
コードの解説とポイント
1. `Option Explicit`: 常にコードの先頭に記述することを強く推奨します。変数宣言を強制し、スペルミスによるバグを防ぎます。
2. 設定値の定数化: `NEW_FONT_NAME`と`NEW_TEXT_HEIGHT`を定数として定義することで、変更が容易になります。
3. `On Error GoTo ErrorHandler`: 堅牢なマクロにはエラー処理が不可欠です。予期せぬエラーが発生しても、VBAがクラッシュするのを防ぎ、適切なメッセージを表示して終了できます。
4. `acadApp.ActiveDocument.Utility.Prompt` / `acadApp.StatusBar`: ユーザーへのフィードバックは、VBA処理中にAutoCADがフリーズしたと誤解されるのを防ぎ、ユーザー体験を向上させます。
5. パフォーマンスチューニング:
- `acadApp.ScreenUpdating = False`: VBA実行中に画面がチカチカするのを防ぎ、処理速度を大幅に向上させます。
- `acadDoc.StartUndoGroup` / `acadDoc.EndUndoGroup`: 複数の操作を一つの「元に戻す(Undo)」操作としてグループ化します。これにより、ユーザーは一回のUndoで全ての変更を取り消せ、非常に便利です。
- `acadDoc.Regen acActiveViewport` / `acadApp.ZoomExtents`: 全ての変更が完了した後に一度だけ画面を再描画し、変更を反映させます。ループ内で頻繁に描画を行うと、パフォーマンスが著しく低下します。
6. `On Error Resume Next` と個別のエラー処理: `FontFile`や`Height`プロパティの設定は、特定の条件下でエラーとなる可能性があります(例:存在しないフォント名を指定した場合)。`On Error Resume Next`で一時的にエラーを無視し、問題のあるオブジェクトをスキップして処理を続行させることができます。ただし、その後すぐに`On Error GoTo ErrorHandler`で元に戻すことを忘れないでください。
7. `Set objEntity = Nothing`: ループ内で取得したオブジェクト参照は、そのオブジェクトに対する処理が完了したらすぐに`Nothing`に設定して解放します。これは、特に大規模な図面を扱う際に、メモリ使用量を最適化し、安定した動作を保つための「極限の知見」です。これができていないと、VBAが突然落ちたり、AutoCADが異常終了したりする原因になります。
8. `CleanUp`ラベル: 正常終了時も、エラー終了時も、全てのオブジェクト参照を解放する処理をまとめて記述します。
5. 知見を深める:パフォーマンスとライフサイクルを意識したプログラミング
先ほどのコードには、単に機能を実装するだけでなく、AutoCAD VBAを実務で使いこなす上で不可欠な「パフォーマンス」と「オブジェクトのライフサイクル」に関する深い配慮が盛り込まれています。
- 画面更新の停止と再開 (`ScreenUpdating`, `Regen`):
AutoCADは図形が変更されるたびに、画面の再描画を試みます。何百、何千という文字オブジェクトをループで処理する際に、その都度再描画が行われると、処理時間は劇的に長くなります。これを一時停止し、処理完了後に一度だけ再描画することで、体感速度も実際の処理速度も大幅に改善されます。これは、AutoCAD VBAにおける「聖杯」とも言える重要なテクニックです。
- アンドゥグループの活用 (`StartUndoGroup`, `EndUndoGroup`):
ユーザーがVBAで一括変更を行った後、もし「やっぱり元に戻したい」と思った時に、一回のUndo操作で全てを元に戻せるのは非常に親切です。これがなければ、変更されたオブジェクトの数だけUndoを繰り返すことになり、ユーザー体験は最悪です。この機能は、ユーザーフレンドリーなVBAマクロを作成する上で必須と言えるでしょう。
- オブジェクト参照の解放 (`Set obj = Nothing`):
これは最も重要なポイントの一つです。VBAでは、オブジェクト変数に`Set`でオブジェクトを割り当てると、そのオブジェクトへの参照が保持されます。この参照が残ったままだと、VBAがそのオブジェクトをメモリから解放できず、メモリリークの原因となります。特にループ内で大量のオブジェクトを扱う場合、参照を適切に解放しないと、メモリが逼迫し、AutoCADやVBAが不安定になったり、最悪クラッシュしたりします。処理が終わり次第、`Set obj = Nothing` を明示的に行う習慣をつけましょう。これは、AutoCAD VBAを「安定して動かす」ための「極限の知見」です。
6. さらなる高みへ:応用と拡張のヒント
今回のコードは基礎的な一括変更ですが、ここからさらに発展させることで、あなたの業務自動化スキルは飛躍的に向上します。
- 特定の画層のみ変更:
`objEntity.Layer`プロパティを使って、特定の画層に属する文字のみを対象とすることができます。
If TypeOf objEntity Is AcadText Then
If objEntity.Layer = “TEXT_LAYER” Then
‘ 処理
End If
End If
- 選択セット内の文字のみ変更:
`acadDoc.PickfirstSelectionSet`や`acadDoc.Utility.GetSelectionSet`を使って、ユーザーが事前に選択したオブジェクト、またはVBAで選択セットを作成したオブジェクトのみを処理対象とすることも可能です。
- 文字スタイルごとの変更:
今回は直接文字オブジェクトの`FontFile`と`Height`を変更しましたが、AutoCADには`AcadTextStyle`という概念があります。本来は、文字スタイルを定義し、そのスタイルを文字オブジェクトに適用することで、より効率的かつ統一的な管理が可能です。`AcadTextStyle`オブジェクトのプロパティを変更すれば、そのスタイルを使用している全ての文字が自動的に更新されます。これが、AutoCADの設計思想に沿った、より洗練されたアプローチと言えるでしょう。
- ユーザーからの入力による設定:
`acadDoc.Utility.GetString`や`InputBox`関数を使って、変更したいフォント名や高さを実行時にユーザーに入力させることで、より汎用性の高いマクロになります。
—
まとめ
今回の記事では、AutoCAD VBAを使って図面内の全てのテキスト(Text)およびMTextオブジェクトのフォントと高さを一括で変更する方法を、その基礎から「極限の知見」まで踏み込んで解説しました。
- `AcadApplication`と`AcadDocument`がAutoCAD VBAの入り口であることを理解し、
- `ModelSpace`コレクションと`For Each`ループで図形要素にアクセスし、
- `TypeOf`演算子で`AcadText`と`AcadMText`を識別し、
- パフォーマンスとオブジェクトのライフサイクルを意識した堅牢なコードを構築する
ここをクリアすれば、AutoCAD VBAの基本はバッチリですよ!
この知識を基盤に、あなたはもう「マクロの記録」に頼るだけの初学者ではありません。オブジェクトモデルを理解し、パフォーマンスを意識した、より高度な自動化へとステップアップできる準備ができました。
さあ、あなたのアイデアとこの知識を組み合わせて、AutoCADの業務自動化の世界をさらに切り開いていってください。もし疑問や、もっと深いテーマへの探求心があれば、いつでも私に声をかけてください。次なる挑戦でお会いしましょう!
