SOLIDWORKS

SOLIDWORKS API を使って、表示方向を整える作業をマクロで自動化した話

この記事の背景

仕事で 3次元CAD設計ソフトウェア「SOLIDWORKS」 を使っています。

設計後の各アセンブリ・各モデルの表示方向(ビューの方向)は、それぞれ別になっていることが多いので、ファイルを開いた時に、おかしな向きを向いていることが多々あります。

おかしな向きのモデル例

このため、案件が終わったタイミングで、表示方向を等角投影 (Isometric)、または不等角投影 (Trimetric) に全て整えたいといつも思っています。しかし、現実には部品点数も多いし、一つひとつのファイルを「開いて、表示方向を設定して、上書き保存して閉じる」といった一連のながれが、面倒で仕方ありませんでした(「本来やらなければならない業務」でもないですし、これに時間をかけるのは筋が違う気がしています)。

不等角投影にしたモデル例

そのようなわけで、SOLIDWORKS のマクロ機能(記述は VBA と一緒)で解決しようと考えた次第です。

この記事で伝えたいこと

  • SOLIDWORKS API を使うと、API 関数により SOLIDWORKS の機能を直接操作できる
    • VB (Visual Basic)、VBA (Visual Basic for Applications)、VB.NET、C++、C#、または SOLIDWORKS マクロ ファイル[拡張子 swp]で扱える
    • 最も手軽に始められるのは、SOLIDWORKS マクロファイル
    • API について紹介された記事は、他にもたくさんあるのでどれもおすすめ
  • この記事で扱っているのは、開いたアセンブリ内に含まれる各アセンブリ・各モデルの表示方向を整える内容
    • SOLIDWORKS API Macro(マクロファイル)で記述した
    • 等角投影、不等角投影のいずれかに表示方向をそろえた(抑制しているもの・非表示のものは対象から外す)

SOLIDWORKS API について

API (Application Programming Interface) とは

API(アプリケーション プログラミング インターフェース)は、大まかにいうと「他のアプリケーションとの連携を簡単に行うための仕様を、間口として用意したもの」です。自分でアプリケーションを作る際に、他者が既に作成したアプリケーションの API が公開されていると、それを利用することで、より簡単に他者のアプリケーションの機能を扱えるため、大変便利です。

以下の記事が分かりやすかったので、ご紹介いたします。

APIとは何か? API連携ってどういうこと? 図解で仕組みをやさしく解説

SOLIDWORKS API とは

SOLIDWORKS の機能を、SOLIDWORK 以外の「開発言語で作成されたアプリケーション」から扱えるように用意されているものが、SOLIDWORKS API です。現在は、VBA (Visual Basic for Applications)VB.NETVisual C#Visual C++ 6.0、Visual C++/CLI または SOLIDWORKS マクロ ファイル[拡張子 swp]で、この API を扱うことができます。

今回は、この中でも最も簡単に始められる SOLIDWORKS マクロ (SW VBA Macro) を使うことにします。VBA で書くのは古い感じもしますが、そこはご容赦ください。2015 バージョンで書きましたので、上位互換性があります(2015 より前のバージョンでは動作しません)。

)Microsoft Visual Studio を使って本格的にアプリケーションを開発する場合は、SOLIDWORKS API SDK (Software Developement Kit) のインストールが必要です。インストーラーは、サブスクリプション・サービス契約と紐づいている SOLIDWORKS ID で、カスタマーポータル (customerportal.solidworks.com) にサインインして、ダウンロードします。詳しいインストール方法は、以下を参照してください。

SOLIDWORKS API Getting Started Overview

SOLIDWORKS API 関連サイトのご紹介

SOLIDWORKS API について、他の方もいくつか記事を書かれていますので、ご紹介いたします。

実現したいことを整理する

今回実現したいことを、前提条件から整理して考えました。

前提条件

  • 作成するマクロ機能は、アセンブリファイルを開いている際に実行する
  • アセンブリ内に、同じファイル名のサブアセンブリやモデルが複数ある(パターン配置以外で、複数置かれているものもある)
  • アセンブリ内のサブアセンブリ及びモデルの抑制状態は、次のようになっている
    • ライトウェイトに設定されている
    • 抑制されているもの、非表示のものも含まれている

実現したいこと

  • アセンブリ内の全てのサブアセンブリ・モデルについて、「ファイルを開いて、表示方向を設定して、上書き保存してファイルを閉じる」を自動で実行する
    • ユーザーフォームを作り、表示方向選択のラジオボタン、実行ボタン、ウィンドウを閉じるボタンを置く
    • 設定できる投影方法は、等角投影 (Isometric) または 不等角投影 (Trimetric) とする
    • 同じファイル名のサブアセンブリ・モデルを、何度も開かない(一度開くのみとする)
    • 抑制されているもの・非表示のものは、対象外とする
    • 実行状態を、目視で確認できるようにする

いきなり書いて実現できれば良いのですが、私にはそれが出来なかったので練習をしました。「SW VBA Macro にまず慣れる」がそれに相当します。とにかく結論を知りたいかたは「出来上がったもの」へ飛んでください。

SW VBA Macro にまず慣れる

以下の順でやれば、多分できるんだろうなあと思って、その見込みを立てることにしました。

  1. 使い方を確認する
  2. Office VBA と同じ要領のことができるか、確認する
  3. 表示方向を変えられるか、試す
  4. 「ファイルを開く・上書き保存する・閉じる」を、試す
  5. アセンブリ内のサブアセンブリとモデルを参照できるか、試す

使い方を確認する

ツール(T)マクロ(M)新規…(N) を選択します (メニューに「マクロ」を表示させることもできます)。

マクロはここから始めます

SW VBA Macro を新規で作成するにあたり、名前を付けるように表示されます。ここでは名前を TestMacro.swp としました。

すると、下の画像のようなエディタが開きます。見たところ、Microsoft Office 製品で VBA を使ったことのあるかたにはおなじみの Visual Basic Editor と同じです。閉じる時には、ファイル(F)終了して SOLIDWORKS へ戻る(C) を押すと、閉じます。

開いたエディタ

SW VBA Macro ファイルを作成したので、次に開くときには「編集…(E)」から開きます。ツール(T)マクロ(M)編集…(E) から、作成した TestMacro.swp を選択して開けば、エディタ画面に戻ってきます。

Office VBA と同じ要領のことができるか、確認する

エディタが同じなのだから、同じことができるだろうとは思いつつ、念のため確認しました。実行したのは、For…Next ステートメントを使った繰り返しで、数値を 1 から 10 までイミディエイトウィンドウに表示させる内容です。

Dim swApp As Object

Sub main()
    '初めから書かれている
    Set swApp = Application.SldWorks

    '変数宣言
    Dim i As Integer
    'For - Next で数値をイミディエイトウィンドウに出力
    For i = 1 To 10
        Debug.Print i
    Next i

End Sub

実行する時は緑色の三角を押します。実行結果は、以下の通りです。Office VBA と同じことができました。ここまでは問題なさそうです。

Excel VBA と同じことができます

エディタを介さずに直接マクロを実行する場合は、ツール(T)マクロ(M)実行…(U) を押して、SW VBA Macro ファイルを開くと直ぐに実行されます。

表示方向を変えられるか、試す

次に、SOLIDWORKS API で用意されている関数を使って、表示方向を変更します。SOLIDWORKS では、表示のことを ビュー (View) と表現するので、View を Change するのだろうなあ、と考えて SOLIDWORKS API Help に view change と入れて検索してみました。

すると、まさに Change to Isometric and Zoom to Fit View Mode Example (VBA) という正解が検索で出てきてしまったわけです。

Change to Isometric and Zoom to Fit View Mode Example (VBA) – 2015 – SOLIDWORKS API Help

ヘルプで正解が引き当てられた例

サンプルプログラムをそのままコピー&ペーストしても、表示方向は切り替わりません。SOLIDWORKS 日本語版には *Isometric という名前のビューが無いからです。そこで、サンプルプログラム上で使われているメソッドを手掛かりに、以下のドキュメントも合わせて確認します。

サンプルプログラム上の、swModel.ShowNamedView2 “*Isometric”, -1 と書かれている箇所の、-1 は ViewId のことを示すと説明されています。swStandardViews_e のメンバー定義を見ると、ViewID を 7 とすると swIsometricView(等角投影)、8 とすると swTrimetricView(不等角投影)になることがわかります(列挙 enum [イニューム]なので、メンバー名で指定するのが自然かもしれないです)。

swStandardViews_e Enumeration – Members

ViewId の指定を 7 にすれば等角投影の表示方向になるはずです。サンプルプログラムの以下の部分を書き換えたところ、等角投影表示になりました。

    ' Not quite the same as when done through the user interface;
    ' model is zoomed out a bit further
    swModel.ShowNamedView2 "*Isometric", 7

「表示方向を変えられるか」試した結果が、良好だったので、これ以降も API Help をメインに確認を進めました。全て説明すると先が長くなりそうなので、ポイントを絞って説明します。

「ファイルを開く・上書き保存する・閉じる」を、試す

以下のヘルプの記述を参考にして、無事に試せました。

OpenDoc7 メソッドを実行する前に、IDocumentSpecification インターフェイスを使って「モデルドキュメントの開き方」を指定する必要があります。また OpenDoc7 を実行するには、ファイルのパス指定が必要です。

CloseDoc メソッドでドキュメントを閉じる際には、どのモデルデータがアクティブなのか意識する必要があります。

「予め開けていたモデルデータ(Aデータとする)」と「マクロで OpenDoc7 メソッドを使って開いたモデルデータ(Bデータとする)」があって Bデータを閉じる場合、Bデータのモデル名称を GetTitle メソッドで取得しておき、それを使って明示的に CloseDoc で閉じます。

アセンブリ内のサブアセンブリとモデルを参照できるか、試す

以下のヘルプの記述を参考にして、無事に試せました。

現在開いているアセンブリ内でアクティブになっているコンポーネントをまとめて取り込むのが、GetComponents メソッドです。サンプルプログラムでは、ルートアセンブリ内にある最上位のコンポーネントを取り込んでいます(プロパティに True と書いているから。False と書くと、サブアセンブリを含め全てのコンポーネントを取り込みます)。

その上で、取り込んだ結果を基に、 For-Next ステートメントで配列の最後の要素まで見ることで、一つひとつのアセンブリデータ・モデルデータを参照する仕組みになっています。

Components = swAssembly.GetComponents(True)
For i = 0 To UBound(Components)
    Set swComponent = Components(i)
    If (swComponent.IsLoaded) Then
        Debug.Print "Component: " & swComponent.Name & " is loaded."
    Else
        Debug.Print "Component: " & swComponent.Name & " is not loaded. "
    End If

    Debug.Print " Suppressed: " & swConfig.GetComponentSuppressionState(swComponent.Name)
    Debug.Print ""
Next

この仕組みを応用して、For-Next ステートメントの間で、「ファイルを開いて、表示方向を変更して、上書き保存して、閉じる」仕組みを入れれば、完成できそうです。

目処が立ったので、作ることにしました。

出来上がったもの

標準モジュール

標準モジュールの記述は、このようにしました。

Option Explicit

Sub main()
    MainForm.Show vbModeless
End Sub

モードレスな状態にしてユーザーフォーム(フォーム名は MainForm にしましたが、名前は何でも構いません)を表示させると、フォームを表示したまま SOLIDWORKS を動かせます。この記述は Office VBA を使ったことのある人には、おなじみだと思います。

フォーム

フォームはこのように作りました。

フォームはこのような感じにしました

フォームのコードには、以下のように記述をしました。

Private Sub IsometricButton_Click()
    IsometricButton.GroupName = "ViewGroup"
    TrimetricButton.GroupName = "ViewGroup"
End Sub

Private Sub TrimetricButton_Click()
    IsometricButton.GroupName = "ViewGroup"
    TrimetricButton.GroupName = "ViewGroup"
End Sub


Private Sub CloseWindowButton_Click()
    Unload Me
End Sub

Private Sub ExecuteButton_Click()

    Dim swApp As SldWorks.SldWorks
    Dim swDoc As SldWorks.ModelDoc2
    Dim swADoc As SldWorks.AssemblyDoc
    Dim varComp As Variant
    
    Dim viewText As String
    Dim viewNum As Integer

    Dim lErrors As Long
    Dim lWarnings As Long

    Set swApp = Application.SldWorks
    Set swDoc = swApp.ActiveDoc
    Set swADoc = swDoc

    ' ルートアセンブリから確認したサブアセンブリ内を含めて全てのコンポーネントを取得
    varComp = swADoc.GetComponents(False)
    
    ' ルートアセンブリ内のコンポーネントを順に確認するインデックス
    Dim SearchIndex As Long

    ' ファイルパスを格納する配列
    ' (ファイルパスを利用して一意のファイルを特定し、最終要素に追加する、動的配列)
    Dim filePath() As String
    Dim isInit As Boolean
    isInit = False
    
    'ビューの指定
    If IsometricButton.Value = True Then
        viewText = "*Isometric"
        viewNum = 7
    ElseIf TrimetricButton.Value = True Then
        viewText = "*Trimetric"
        viewNum = 8
    End If
    
    
    ' ルートアセンブリから取得した全てのコンポーネントを順に確認する
    For SearchIndex = LBound(varComp) To UBound(varComp)

        Dim swComp As SldWorks.Component2
        Set swComp = varComp(SearchIndex)
        
        ' そのコンポーネントがパターンインスタンスではない場合
        If swComp.IsPatternInstance = False Then
             
            ' 配列の動的配置
            If Not isInit Then
                isInit = True
                ReDim filePath(0)
            End If
            
            ' 同じファイル名があるか確認し、無ければ配列に追加する
            ' (同じファイルを何度も開く処理を阻止するため)
            Dim i As Integer
            Dim isSameFile As Boolean
            isSameFile = False

            For i = 0 To UBound(filePath)
                If filePath(i) = swComp.GetPathName Then
                    isSameFile = True
                End If
            Next i

            ' 既存の配列内に同じファイル名が存在しなければ、
            ' 最後に入れた要素(ファイルパス)を使用して、以下の処理を実施する。
            ' ファイルを開き、等角投影または不等角投影にして保存して、ファイルを閉じる。
            If isSameFile = False Then
                filePath(UBound(filePath)) = swComp.GetPathName
                
                ' コンポーネントの抑制状態 及び コンポーネントの表示状態を確認する
                Dim getSuppressedFlag As Integer
                Dim visibleFlag As Integer
                
                '.GetSuppression() で抑制状態を確認する。return 0 が完全抑制状態を示す。
                getSuppressedFlag = swComp.GetSuppression()

                '.Visible は表示状態を示す。1 は Visible, 0 が Hidden。
                visibleFlag = swComp.Visible
                
                If getSuppressedFlag <> 0 And visibleFlag = 1 Then
                    ' 引数:ファイル絶対パス, {Isometric|Trimetric}, {7|8}
                    Call SaveAsIsometricProjection(swComp.GetPathName, viewText, viewNum)
                End If
                
                ' 配列の要素数を動的に増やす処理
                ReDim Preserve filePath(UBound(filePath) + 1)
                
            End If
        End If
    Next SearchIndex
    
    ' ファイルを等角投影で保存して、画面にズームフィットさせる
    swDoc.ShowNamedView2 viewText, viewNum
    swDoc.ViewZoomtofit2
    
    ' ドキュメントの強制再構築
    swDoc.ForceRebuild3 (True)
    
    ' 取得したコンポーネントを空白にする
    varComp = Empty

    ' ファイルを保存する
    boolstatus = swDoc.Save3(swSaveAsOptions_Silent, lErrors, lWarnings)
    
    MsgBox ("終了しました")
End Sub

'アセンブリ上のコンポーネントを取得する
Private Sub SaveAsIsometricProjection(filePath As String, viewText As String, viewNum As Integer)

    Dim swFuncApp As SldWorks.SldWorks
    Set swFuncApp = Application.SldWorks

    '確認のためにイミディエイトにファイルの絶対パスを表示させる
    Debug.Print filePath

    ' ファイルを開く・保存する・閉じる為の前処理
    Dim swModel As SldWorks.ModelDoc2
    Dim swDocSpecification As SldWorks.DocumentSpecification
    Dim swModelTitle As String
    Dim boolstatus As Boolean
    Dim lErrors As Long
    Dim lWarnings As Long

    ' ファイルの開き方を指定する
    Set swDocSpecification = swFuncApp.GetOpenDocSpec(filePath)

    swDocSpecification.UseLightWeightDefault = False    ' システムのデフォルトの軽量設定を使用しない
    swDocSpecification.LightWeight = True               ' 軽量パーツを含むアセンブリ ドキュメントを開く
    swDocSpecification.Silent = True                    ' モデルをサイレントモードで開く
    swDocSpecification.IgnoreHiddenComponents = True    ' 非表示のコンポーネントをロードしない

    ' ファイルを開く
    Set swModel = swFuncApp.OpenDoc7(swDocSpecification)

    ' 開いたファイルのタイトルを取得する
    swModelTitle = swModel.GetTitle

    ' ファイルを等角投影または不等角投影で保存して、画面にズームフィットさせる
    swModel.ShowNamedView2 viewText, viewNum
    swModel.ViewZoomtofit2

    ' ファイルを保存する
    boolstatus = swModel.Save3(swSaveAsOptions_Silent, lErrors, lWarnings)

    ' ファイルを閉じる
    swFuncApp.CloseDoc swModelTitle

End Sub

コンポーネントの抑制状態を調べるための GetSuppression メソッド や、コンポーネントの表示状態を示す Visible プロパティ 、ルートアセンブリの再構築を行う ForceRebuild3 メソッド を新たに使っていますが、実現したいことはこれで実現できました。

別のことも試してみましょう

ここでは、SW VBA Macro を使って作業の自動化をしました。他にも応用して、出来るだけ手間を減らしてその分、楽になれるといいです。公式のヘルプドキュメントを中心に、いろんなことを調べて、手を動かしていけるといいですね。