編集したExcelファイルを読み込み仕様ツリーに反映するマクロ|CATIAマクロの作成方法
今回の記事は「マクロ案」よりいただいた内容です。
送って頂いた内容は以下のようなマクロです。
ワークベンチ: Part Design
マクロ案: 仕様ツリー上の各要素の名称をExcelに読み込み、
Excel上で名称を変更したものを仕様ツリーに反映するマクロ
(CATIAのマクロというよりExcelのマクロかも)
今回のマクロは下記の2つのマクロで構成していきます。
①「仕様ツリーのオブジェクト名をExcelファイルとして出力するマクロ」
②「編集したExcelファイルを読み込み仕様ツリーに反映するマクロ」
本ページでは②のマクロについて紹介していこうと思います。
※仕様ツリーの構成をExcelファイルに出力するマクロの使用が大前提です。
まずは上記リンクページの内容を行ってから本ページを進めて下さい。
CATIA VBAのオブジェクト構造を理解している方ならある程度予想ができると思いますが、仕様ツリーの階層を考慮して上から順にオブジェクトを取得することは実はかなり難しいです。そこで今回は「パートデザイン」「ジェネレーティブ・シェイプ・デザイン」の基本的なコマンドであれば実行可能なマクロを”簡単なコード”で作成しました。
簡単なコードの分、エラー処理や一部オブジェクトには対応していないという状態ですが、どのようにしてツリー構成を取得しているのかは理解しやすい内容になっていると思います。
もし本格的に取得したい場合は下記サイトのマクロを参考にしてみて下さい。
BodyのTreeを各種フォーマットでエクスポートする – C#ATIA
ハードルは少し上がりますがクラスモジュールなども使いしっかりとしたコードで作成されています。
マクロの機能
今回作成したのは編集したExcelファイルを読み込み仕様ツリーに反映するマクロです。
本マクロは仕様ツリーの構成をExcelファイルに出力するマクロで出力したExcelファイルの使用が前提となっています。出力されたExcelファイルのオブジェクト名部分を任意の名称に変更し、本マクロを使って再度Excelファイルを読み込ませることでツリー上のオブジェクト名を一括で変更することができます。
具体的な機能は以下のとおりです。
① 仕様ツリーの構成をExcelファイルに出力するマクロでExcelファイルとして出力
② 出力されたExcelファイルのオブジェクト名部分を任意の名称に書き換える
③ 名称変更後のExcelファイルをデスクトップ等に保存
④ 本マクロを実行し③のExcelファイルを選択し読み込ませる
⑤ ツリーの名称が変更される(イミディエイトウィンドウに変更内容の詳細を表示)
・[パートデザイン]だけでなく [GSD]ワークベンチでも実行可能
・名称変更後、[元に戻す]コマンド([Ctrl]+[Z])で変更前に一括で戻すことが可能
サンプルコード
マクロのコードは以下のとおりです。
仕様ツリーの構成をExcelファイルに出力するマクロでExcelファイルとして出力し、その出力したファイル内のオブジェクト名を変更したい名称に書き換え再度読み込ませます。
変更されたオブジェクトの個数がメッセージボックスで、何が何に変更されたのかがVBEのイミディエイトウィンドウに表示されるため、変更した箇所以外のオブジェクト名が変わっていないかを確認することができます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 |
Option Explicit Sub CATMain() 'アクティブドキュメント等の定義 If TypeName(CATIA.ActiveDocument) <> "PartDocument" Then MsgBox "このマクロはPartDocument専用です。" & vbLf & _ "CATPartに切り替えて実行してください。" Exit Sub End If Dim doc As PartDocument: Set doc = CATIA.ActiveDocument Dim pt As Part: Set pt = doc.Part Dim sel As Selection: Set sel = doc.Selection Dim objs As Collection: Set objs = New Collection Dim obj As AnyObject Dim i As Long 'オブジェクトの選定(※出力時と全く同じになるようにする) sel.Clear sel.Search ("タイプ=*,all") 'English ver -> sel.Search ("type=*,all") For i = 1 To sel.Count Set obj = sel.Item(i).Value If InStr(obj.Name, "\") = 0 And InStr(TypeName(obj), "2D") = 0 Then '"\"が入っている=パラメータ, "2D"が入っている=Sketch要素 If Not (TypeName(obj) = "Item" Or _ TypeName(obj) = "Formula" Or _ TypeName(obj) = "AxisSystem" Or _ TypeName(obj) = "Rule" Or _ TypeName(obj) = "KnowledgeObject" Or _ TypeName(obj) = "Constraint" Or _ TypeName(obj) = "AnyObject") Then objs.Add obj End If End If Next i sel.Clear 'Excel定義 Dim appExcel As Excel.Application Set appExcel = CreateObject("Excel.Application") 'ユーザーが選択したBook取得 Dim fn As String fn = Application.GetOpenFilename(FileFilter:="Excelファイル,*.xlsx") If fn <> "False" Then Dim wb As Workbook Set wb = appExcel.Workbooks.Open(fn) Else MsgBox "キャンセルしました。" Exit Sub End If Dim ws As Worksheet: Set ws = wb.Sheets(1) 'Excelファイルが現行の仕様ツリーのものか確認 Dim max_r As Long: max_r = ws.Cells(Rows.Count, 1).End(xlUp).Row Dim max_c As Long If ws.Cells(1, 1).Value <> pt.Name Then wb.Close MsgBox "選択したExcelファイルと現行の仕様ツリーが違います。" & vbLf & _ "ExcelのA1セルの値とパーツ番号を確認して下さい。" Exit Sub End If 'オブジェクト名称の変更 Dim r As Long Dim c As Long Dim cell_val As String Dim cnt As Long: cnt = 0 For r = 2 To (max_r - 1) max_c = ws.Cells(r, Columns.Count).End(xlToLeft).Column cell_val = ws.Cells(r, max_c).Value Set obj = objs.Item(r) If obj.Name <> cell_val Then Debug.Print obj.Name & " => " & cell_val obj.Name = cell_val cnt = cnt + 1 End If Next r wb.Close If cnt = 0 Then MsgBox "名称の変更されたオブジェクトありませんでした。" Else MsgBox cnt & "個のオブジェクト名を変更しました。" End If End Sub |
処理の流れとしては下記のような流れになっています。ツリーの階層は考慮せずに1行でExcelに出力する場合は、”階層の取得”が不要になるため②の処理がもっとシンプルになります。
② Excelの行ループ処理 (オブジェクトはツリーの上から順にループ)
②-1 オブジェクト名とセルの値が違った場合にオブジェクト名をセルの値に変更する
②-2 変更した場合、「変更前=>変更前」と変更内容をイミディエイトウィンドウに出力
③ オブジェクト名の変更数をメッセージボックスで出力
コード解説
アクティブドキュメント等の定義
1 2 3 4 5 6 7 8 9 10 11 12 13 |
'アクティブドキュメント等の定義 If TypeName(CATIA.ActiveDocument) <> "PartDocument" Then MsgBox "このマクロはPartDocument専用です。" & vbLf & _ "CATPartに切り替えて実行してください。" Exit Sub End If Dim doc As PartDocument: Set doc = CATIA.ActiveDocument Dim pt As Part: Set pt = doc.Part Dim sel As Selection: Set sel = doc.Selection Dim objs As Collection: Set objs = New Collection Dim obj As AnyObject Dim i As Long |
まずはじめにアクティブドキュメントを定義をします。
今回のマクロはCATPartのみ有効なものなので、アクティブドキュメントがCATPart以外の場合はTypeName関数を使った条件分岐でマクロを終了するようにしています。つまり、アクティブドキュメントがCATPartの場合のみ変数「doc」にアクティブドキュメントを代入し、マクロの処理を続けます。
アクティブドキュメントが定義できたら、以降で使うためのオブジェクト/変数をまとめて定義しておきます。ここでは下記の用途で各オブジェクト/変数を定義しています。
Partオブジェクト :ツリー第1階層指定用
Selectionオブジェクト :ドキュメント内のオブジェクトを”上から順”に取得する用
objs (As Collection) :Excelに出力するオブジェクトを全て格納する用
obj (As AnyObject) :objs内ループ(For Each ~ In ~)用変数
i (As Long) :Forループ用カウント変数
オブジェクトの選定
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
'オブジェクトの選定(※出力時と全く同じになるようにする) sel.Clear sel.Search ("タイプ=*,all") 'English ver -> sel.Search ("type=*,all") For i = 1 To sel.Count Set obj = sel.Item(i).Value If InStr(obj.Name, "\") = 0 And InStr(TypeName(obj), "2D") = 0 Then '"\"が入っている=パラメータ, "2D"が入っている=Sketch要素 If Not (TypeName(obj) = "Item" Or _ TypeName(obj) = "Formula" Or _ TypeName(obj) = "AxisSystem" Or _ TypeName(obj) = "Rule" Or _ TypeName(obj) = "KnowledgeObject" Or _ TypeName(obj) = "Constraint" Or _ TypeName(obj) = "AnyObject") Then objs.Add obj End If End If Next i sel.Clear |
つぎにExcelに出力するオブジェクトをすべて取得します。
このマクロは「仕様ツリーの構成をExcelファイルに出力するマクロ」実行後に使用する前提のマクロなので、選別するオブジェクトも「仕様ツリーの構成をExcelファイルに出力するマクロ」で選別した内容と全く同じものにします。
コード内容も「仕様ツリーの構成をExcelファイルに出力するマクロ」と全く同じです。
Excel出力したオブジェクトとここで取得するオブジェクトを一致させれば、Excelでは行を1行ずつループさせるのに対し、オブジェクトではobjsコレクションの中身を1つずつループしていけば同じオブジェクトを表すことができます。
つまりExcelのセルの値とobjsコレクション内のオブジェクト名が異なった場合にオブジェクト名をセルの値にすれば、ユーザーがExcel上で変更したオブジェクト名に変更することができるという訳です。
Excel定義
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
'Excel定義 Dim appExcel As Excel.Application Set appExcel = CreateObject("Excel.Application") 'ユーザーが選択したBook取得 Dim fn As String fn = Application.GetOpenFilename(FileFilter:="Excelファイル,*.xlsx") If fn <> "False" Then Dim wb As Workbook Set wb = appExcel.Workbooks.Open(fn) Else MsgBox "キャンセルしました。" Exit Sub End If Dim ws As Worksheet: Set ws = wb.Sheets(1) |
つぎにCATIAに読み込ませるExcelシートの定義をしていきます。
まずはCATIA内でExcel VBAが使えるように設定する必要があります。
Excelの定義方法については「CATIAマクロでExcelを操作する方法」を参照下さい。
上記ページにも書かれている通り、参照設定をしていないと実行できないので注意して下さい。
Excel VBAの使用を可能にしたら「GetOpenFilename」を使って、「仕様ツリーの構成をExcelファイルに出力するマクロ」で出力し編集したExcelファイルを読み込ませます。
この時、読み込ませたExcelブックを「wb」、シートを「ws」として定義します。
Excelファイルが現行の仕様ツリーのものか確認
1 2 3 4 5 6 7 8 9 10 |
'Excelファイルが現行の仕様ツリーのものか確認 Dim max_r As Long: max_r = ws.Cells(Rows.Count, 1).End(xlUp).Row Dim max_c As Long If ws.Cells(1, 1).Value <> pt.Name Then wb.Close MsgBox "選択したExcelファイルと現行の仕様ツリーが違います。" & vbLf & _ "ExcelのA1セルの値とパーツ番号を確認して下さい。" Exit Sub End If |
つぎに読み込まれたExcelが現在のアクティブドキュメントのツリーと同じものかを確認します。
ここでの確認は非常に単純で、出力したExcelファイルのA1セル(つまりはパーツ番号の部分)がアクティブドキュメントのパーツ番号と一致しているかを確認しているだけです。
これだとあまりにも確認がザルなので、本格的に使おうという場合は個々の確認をもっとしっかりしたものにしましょう。
オブジェクト名称の変更
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
'オブジェクト名称の変更 Dim r As Long Dim c As Long Dim cell_val As String Dim cnt As Long: cnt = 0 For r = 2 To (max_r - 1) max_c = ws.Cells(r, Columns.Count).End(xlToLeft).Column cell_val = ws.Cells(r, max_c).Value Set obj = objs.Item(r) If obj.Name <> cell_val Then Debug.Print obj.Name & " => " & cell_val obj.Name = cell_val cnt = cnt + 1 End If Next r wb.Close If cnt = 0 Then MsgBox "名称の変更されたオブジェクトありませんでした。" Else MsgBox cnt & "個のオブジェクト名を変更しました。" End If |
つぎに本マクロの核部分であるオブジェクト名の変更処理を行います。
処理自体はExcel出力時のマクロよりも簡単です。
Excelの[r]行目の値とobjsコレクション内の[r]番目のオブジェクト名を比較し、違った場合にのみオブジェクト名をセルの値に変換するだけです。
一応マクロの実行確認としてオブジェクト名を変更した際には「変更前=>変更前」とイミディエイトウィンドウに表示するようにしています。これにより意図しないオブジェクト名の変更を抑止することができます。
あとは読み込ませたブックを閉じたり、オブジェクト名の変更数を表示させたりすれば完了です。
まとめ
今回は編集したExcelファイルを読み込み仕様ツリーに反映するマクロについての内容でした。
「仕様ツリーの構成をExcelファイルに出力するマクロ」でも行っていた通りマクロの処理自体は単純ですが、単純がゆえに対応しきれない部分も出てきてしまっています。
“ツリーの上から順”というのはVBAで表そうとするとかなり面倒で、全てのオブジェクトに対応させるにはそれだけの知識とコード量が必要になってくるためどこかで妥協する部分が必要になってきます。
ある程度使用するオブジェクトは限られてくるはずなので、自身の環境に合わせてコード内容を書き直せばある程度は使えるマクロにはなっています。
もっとしかっりとしたコードにしたいという場合は冒頭でも紹介した下記サイトを参考にしてみて下さい。
BodyのTreeを各種フォーマットでエクスポートする – C#ATIA