仕様ツリーの構成をExcelファイルに出力するマクロ|CATIAマクロの作成方法

今回の記事は「マクロ案」よりいただいた内容です。
送って頂いた内容は以下のようなマクロです。

ワークベンチ: Part Design

マクロ案: 仕様ツリー上の各要素の名称をExcelに読み込み、
              Excel上で名称を変更したものを仕様ツリーに反映するマクロ
            (CATIAのマクロというよりExcelのマクロかも)

今回のマクロは下記の2つのマクロで構成していきます。
①「仕様ツリーのオブジェクト名をExcelファイルとして出力するマクロ」
②「編集したExcelファイルを読み込み仕様ツリーに反映するマクロ」
本ページでは①のマクロについて紹介していこうと思います。
(①のマクロだけの内容ですが結構長めなので頑張ってください)

CATIA VBAのオブジェクト構造を理解している方ならある程度予想ができると思いますが、仕様ツリーの階層を考慮して上から順にオブジェクトを取得することは実はかなり難しいです。そこで今回は「パートデザイン」「ジェネレーティブ・シェイプ・デザイン」の基本的なコマンドであれば実行可能なマクロを"簡単なコード"で作成しました。

簡単なコードの分、エラー処理や一部オブジェクトには対応していないという状態ですが、どのようにしてツリー構成を取得しているのかは理解しやすい内容になっていると思います。

もし本格的に取得したい場合は下記サイトのマクロを参考にしてみて下さい。
BodyのTreeを各種フォーマットでエクスポートする – C#ATIA
ハードルは少し上がりますがクラスモジュールなども使いしっかりとしたコードで作成されています。

 

マクロの機能

今回作成したのは仕様ツリーの構成をExcelファイルに出力するマクロです。
オブジェクト名を編集する際にわかりやすいよう、上画像のように仕様ツリーの階層をExcel上で再現して出力するようになっています。

具体的な機能は以下のとおりです。

  マクロの機能まとめ ・仕様ツリーの構成をExcelファイルに出力する
・[パートデザイン]だけでなく [GSD]ワークベンチでも実行可能
・一部オブジェクトは出力されない(座標系、スケッチ内要素、ナレッジ関連のオブジェクト等)
 → 非対応オブジェクトを対応させるにはコードにそれぞれの処理を書く必要あり

※すべてのオブジェクトで試したわけではないので、場合によってエラーが発生します。
 その場合はそのオブジェクトを除外するか専用の処理を追記する必要があります
 (詳しくはコード解説部分で説明しています)

今回のマクロでは仕様ツリーの階層を考慮してExcelファイルとして出力していますが、その部分を考慮せず上から順にオブジェクト名を羅列するだけあればもっと単純なコードで済みます。
 

サンプルコード

マクロのコードは以下のとおりです。

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

 'Excelに出力するオブジェクトの選別
    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")

    Dim wb As Workbook:    Set wb = appExcel.Workbooks.Add
    Dim ws As Worksheet:   Set ws = wb.Sheets(1)

    Dim hierarchy As Long
    Dim cnt As Long:       cnt = 1
    Dim max_r  As Long
    Dim max_c  As Long:    max_c = 1
    
 'オブジェクトの階層を取得しExcelにオブジェクト名を出力する
    appExcel.ScreenUpdating = False
    CATIA.StatusBar = "ツリー構成をExcelに出力中..."
    
    For Each obj In objs
        hierarchy = get_hierarchy(obj)
        Debug.Print obj.Name & " => " & hierarchy
        
        If cnt <> 1 Then
            ws.Cells(cnt, hierarchy - 1).Value = "∟"
        End If
        
        If max_c < hierarchy Then
            max_c = hierarchy
        End If
        
        ws.Cells(cnt, hierarchy).Value = obj.Name
        
        cnt = cnt + 1
    Next obj
    
 'Excelファイルの体裁を整える
    For i = 1 To max_c
        ws.Columns(i).ColumnWidth = 2
    Next i
    
    Dim r As Long
    Dim c As Long
    Dim last_r As Long
    Dim last_c As Long:     last_c = max_c
    Dim cell_val As String
    
    For c = 1 To last_c
        last_r = ws.Cells(Rows.Count, c).End(xlUp).Row
        
     '以下をコメントアウトでツリーの枝部分を再現しない(処理速度の向上)------
        For r = last_r To 1 Step -1
            cell_val = ws.Cells(r, c).Value
            If cell_val = "∟" Or cell_val = "|" Then
                If ws.Cells(r - 1, c).Value = "" Then
                    ws.Cells(r - 1, c).Value = "|"
                End If
            End If
        Next r
     '------------------------------------------------------------
        
        If max_r < last_r Then
            max_r = last_r
        End If
    Next c
    
    For c = 1 To max_c
        ws.Cells(max_r + 1, c).Value = "―"
    Next c
    
    appExcel.ScreenUpdating = True
    CATIA.StatusBar = "ツリー構成をExcelに出力しました"
    
    MsgBox "現行の仕様ツリーの構成をExcelファイルとして出力しました"
    appExcel.Visible = True
    
    
End Sub

'--------------------------------------------------------------------------------------
' 関数名 get_hierarchy
'
' 機能  引数objの階層を求めLong型で返す
'        Partの場合「1」XY平面,パーツボディーの場合「2」が返される
'
'        ※引数objのタイプによっては正常に求められないものあり
'          -> そのオブジェクトは除外して考える or そのオブジェクト専用の処理を追加する必要あり
'--------------------------------------------------------------------------------------
Function get_hierarchy(ByVal obj As AnyObject) As Long

    Dim tmp_obj As AnyObject
    Dim cnt As Long
    
    If InStr(TypeName(obj), "Explicit") = 0 Then
        Set tmp_obj = obj
    Else
        Set tmp_obj = obj.Thickness
    End If
    
    Do Until TypeName(tmp_obj) = "PartDocument"
        If TypeName(tmp_obj) <> "AnyObject" Then
            cnt = cnt + 1
        End If
        Set tmp_obj = tmp_obj.Parent
        If cnt > 50 Then Exit Do '無限ループ強制終了用
    Loop
    
    get_hierarchy = cnt

End Function

処理の流れとしては下記のような流れになっています。ツリーの階層は考慮せずに1行でExcelに出力する場合は、"階層の取得"が不要になるため②の処理がもっとシンプルになります。

① ドキュメント内の全オブジェクトからExcelに出力するオブジェクトを選別しすべて取得 
② 取得したオブジェクトをループ処理
 ②-1 オブジェクトの階層を取得
 ②-2 オブジェクトの階層に応じてExcelの行を変化させながらオブジェクト名を出力
③ Excelを表示
 

 

コード解説

アクティブドキュメント等の定義

 'アクティブドキュメント等の定義
    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ループ用カウント変数

 
Excelに出力するオブジェクトの選別

 'Excelに出力するオブジェクトの選別
    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 'objsにExcelに出力するオブジェクトのみを追加していく
            End If
        End If
    Next i
    sel.Clear

つぎにExcelに出力するオブジェクトをすべて取得します。
このとき、必要のないオブジェクトは除外します。この部分は自身の出力したい内容に合わせて書き換えて下さい。

オブジェクトの選別する方法は簡単で一度オブジェクトをすべて選択状態にし、条件を絞りながら選択しているオブジェクトを順に「objsコレクション」に格納していくだけです。この考え方は「選択しているオブジェクトを一時保管する方法」を参照下さい。

上記コードでいう「TypeName(obj)="XXXX"」のXXXXのオブジェクトは除外するようになっています。除外する理由は2つあります。

1つ目はそこまで細かく必要ないためです。
あくまで個人の主観ですが、「長さや角度といったパラメータ」「スケッチ内の形状や拘束」などはあえて出力する必要はないと感じたため、ここでは除外しています。コードでいうと一番初めの条件分岐「If InStr(obj.Name, “\") = 0 And InStr(TypeName(obj), “2D") = 0 Then」と次の条件分岐内の「TypeName(obj) = “Constraint"」の部分です。

2つ目は除外しないとコードがうまく実行できないためです。
今回のコードはのちに出てくる関数「get_hierarchy」を使ってオブジェクトの階層を調べます。
この関数では単純に「obj.Parent.Parent…」のように親オブジェクトをさかのぼっていくものなのですが、一部オブジェクトではこの方法が通用しません。例えば「式」や「座標系」などのオブジェクトはVBAオブジェクトの関係上、通常のオブジェクトと同じ方法では階層を調べることが出来ません。

そのため、必要であれば関数「get_hierarchy」に各オブジェクト別の処理を追加すればここで除外する必要は無くなります。ただ、それをすべてのオブジェクトで対応するには膨大なコードになるため、ここでは個人的にやっていてうまく階層が取得できなかったオブジェクトのみを全て除外しています。(おそらく他にもうまくできないオブジェクトあるので、適宜ここで除外するか関数内に専用の処理を追加してください)
 

Excel定義 + 変数定義

 'Excel定義
    Dim appExcel As Excel.Application
    Set appExcel = CreateObject("Excel.Application")

    Dim wb As Workbook:    Set wb = appExcel.Workbooks.Add
    Dim ws As Worksheet:   Set ws = wb.Sheets(1)

    Dim hierarchy As Long
    Dim cnt As Long:       cnt = 1
    Dim max_r  As Long
    Dim max_c  As Long:    max_c = 1

つぎに出力先となるExcelのブックを新規作成し、シートを定義します。
Excelの定義方法については「CATIAマクロでExcelを操作する方法」を参照下さい。
上記ページにも書かれている通り、参照設定をしていないと実行できないので注意して下さい。

Excelが定義できたら、以降で使うための変数をまとめて定義しておきます。ここでは下記の用途で各変数を定義しています。

hierarchy (As Long):オブジェクトのの階層格納用
cnt           (As Long):カウント変数
max_r     (As Long):Excel出力時に値を持つセルの最終行格納用
max_c     (As Long):
Excel出力時に値を持つセルの最終列格納用

 
オブジェクトの階層を取得しExcelにオブジェクト名を出力する

 'オブジェクトの階層を取得しExcelにオブジェクト名を出力する
    appExcel.ScreenUpdating = False
    CATIA.StatusBar = "ツリー構成をExcelに出力中..."
    
    For Each obj In objs
        hierarchy = get_hierarchy(obj)
        Debug.Print obj.Name & " => " & hierarchy
        
        If cnt <> 1 Then
            ws.Cells(cnt, hierarchy - 1).Value = "∟"
        End If
        
        If max_c < hierarchy Then
            max_c = hierarchy    '値の入ったセルの中で最も右側にあるセルの列を取得
        End If
        
        ws.Cells(cnt, hierarchy).Value = obj.Name
        
        cnt = cnt + 1
    Next obj

つぎに本マクロの核となるExcel出力処理です。
objsコレクション内に入っているオブジェクトの名称を順にExcelに出力していきます。

その際に関数「get_hierarchy」を使ってオブジェクトの階層を取得します。
この取得した階層を使って「Cells(cnt, hierarchy).Value = obj.Name」と書くことで、1行目から順にオブジェクトの名前を出力するだけでなく、階層別に列もそろえることが可能になります。
あわせて「Cells(cnt, hierarchy – 1).Value = “∟"」と書いておけば、オブジェクト名を出力したセルの左側のセルにツリーの枝部分を書き出すことができます。

これでオブジェクトの書き出しの処理自体は完了です。
以降ではより見やすくなるようセルの幅を揃えたり、ツリーの枝部分を作成したりします。

また、初めに書いている2行はそれぞれ下記のような役割です。
書かなくても処理自体に大きな影響は与えません。

appExcel.ScreenUpdating = False        Excelの動作を不可視化し処理速度を上げる
CATIA.StatusBar = “ツリー構成をExcelに出力中…"  CATIAステータズバーにメッセージ表示

 

Excelファイルの体裁を整える

'Excelファイルの体裁を整える
'①ツリー枝部分の列幅を調整
    For i = 1 To max_c
        ws.Columns(i).ColumnWidth = 2
    Next i
    
    Dim r As Long
    Dim c As Long
    Dim last_r As Long
    Dim last_c As Long:     last_c = max_c
    Dim cell_val As String
    
 '②ツリー枝部分の作成
    For c = 1 To last_c
        last_r = ws.Cells(Rows.Count, c).End(xlUp).Row
        
     '以下をコメントアウトでツリーの枝部分を再現しない(処理速度の向上)------
        For r = last_r To 1 Step -1
            cell_val = ws.Cells(r, c).Value
            If cell_val = "∟" Or cell_val = "|" Then
                If ws.Cells(r - 1, c).Value = "" Then
                    ws.Cells(r - 1, c).Value = "|"
                End If
            End If
        Next r
     '------------------------------------------------------------
        
        If max_r < last_r Then
            max_r = last_r
        End If
    Next c
 
 '③ツリー最終位置に"――"を入力    
    For c = 1 To max_c
        ws.Cells(max_r + 1, c).Value = "―"
    Next c

つぎに出力先したExcelファイルを見やすくするために軽く編集していきます。

やっていることとしては下記の3点です。
①「ツリー枝部分の列幅を調整」
②「ツリー枝部分の作成」
「ツリー最終位置に"――"を入力」

このうち②「ツリー枝部分の作成」はオブジェクトの数によっては(オブジェクト数が5000を超えたあたりから)処理が重くなるため、コメントアウトして処理速度を優先にすることもできます。場合によって使い分けるか、もっと効率の良い方法があればそちらに書き換えてみて下さい。

③「ツリー最終位置に"――"を入力」は、Cells(max_r+1 , 1)からCells(max_r+1 , max_c)の間に"―"を入力しています。つまりは、その"―"部分を読み取れば、ツリーのサイズ(最終行と最終列)を取得することができます。
これは「編集したExcelファイルを読み込み仕様ツリーに反映するマクロ」を使う際に使用するためのものです。そちらのマクロも合わせて使う場合はこの処理は残しておいて下さい

最後に出力したExcelブックや完了メッセージを表示させれば完了です。
 

関数「get_hierarchy」

Function get_hierarchy(ByVal obj As AnyObject) As Long

    Dim tmp_obj As AnyObject
    Dim cnt As Long
    
    If InStr(TypeName(obj), "Explicit") = 0 Then
        Set tmp_obj = obj
    Else
        Set tmp_obj = obj.Thickness
    End If
    
    Do Until TypeName(tmp_obj) = "PartDocument"
        If TypeName(tmp_obj) <> "AnyObject" Then
            cnt = cnt + 1
        End If
        Set tmp_obj = tmp_obj.Parent
        If cnt > 50 Then Exit Do '無限ループ強制終了用
    Loop
    
    get_hierarchy = cnt

End Function

最後にこれまでにも何度か出てきた関数「get_hierarchy」についてです。
この関数は入力されたオブジェクトの階層を取得し、Long型の数値として返す機能を持っています。

ここで注意してほしいのは、「ここで出力される階層≠CATIAのツリーの階層」という点です。
この関数はExcel出力用のためPartが「1」、パーツボディーやXY平面などが「2」として出力されます。
つまりは「ここで出力される階層 – 1=CATIAのツリーの階層」と思ってもらえれば大丈夫です。

階層の取得方法は、入力されたオブジェクトの親オブジェクトをたどっていき「PartDocument」にたどり着くまでの回数を数えているだけです。

ここで注意しないといけないのが一部オブジェクトではこの方法で対応できないという点です。
たとえばデータム化した点や曲線の場合、ツリーの深くの階層にいたとしても下記のような流れで「PartDocument」にたどり着くため、階層がおかしなことになってしまいます。

 HybridShapeCurveExplicit < Parameters < Part <PartDocument 

 
この場合は「Thickness」を挟めば問題が解決するとわかったため、データム化されたオブジェクト(つまりは「Explicit」と含まれるオブジェクト)には「Thickness」を挟む専用の処理を追加しています。

これと同じで親をさかのぼっていくことがうまくできないオブジェクトがいくつか存在しています。
そもそも「PartDocument」にたどり着かないもの、たどり着くまでの回数がおかしいものなどの要因があります。こういった場合はデータム化されたオブジェクトのように専用の処理を追加するか、コード冒頭のオブジェクトの選別の時点で除外するかのどちらかが必要になってきます。

本マクロを使う場合はエラーが出るたびにそのオブジェクトをどうするか(除外or専用コード追加)を決め、徐々に最適化していくことになると思います。ただ、これは一部のオブジェクトに対していえることなだけであって、基本的なオブジェクトには対応しているはずです。

 

まとめ

今回は仕様ツリーの構成をExcelファイルに出力するマクロについての内容でした。

やっていることは自体は簡単なコードにしましたが、どうしてもその分対応しきれないオブジェクトが出てきてしまっています。ある程度は対応しているので、これで良しとするのであれば問題ないですが、もっとしかっりとしたコードにしたいという場合は冒頭でも紹介した下記サイトを参考にしてみて下さい。
BodyのTreeを各種フォーマットでエクスポートする – C#ATIA
 

サンプルマクロ集に戻る
目次へ戻る

 

 CATIAマクロを本気で勉強するなら

2024年8月26日CATIA,CATIAマクロ