C#ATIA

↑タイトル詐欺 主にFusion360API 偶にCATIA V5 VBA(絶賛ネタ切れ中)

VS2017

社内で利用する為のツールを作らなきゃならないと思いつつ
数年経ってしまっているのですが、気が重いです。

目的のものはGUIが必要なのですが、個人的に非常に苦手です。

本来であれば、勉強ついでにPythonで と思っていたのですが
GUIが必要となるとC#の方が楽な気がして、仕方無しに
VS2017 Communityエディションをインストールしました。

何もかも忘れてます。 困ったな・・・。

ボディをかき集める

Fusion360と他のCADを併用されている方いらっしゃいますか?

Fusion360は色々なフォーマットのデータを読み込んでくれるので
助かっているのですが、Fusion360上ではこんな感じで表示しているのに
f:id:kandennti:20171027193132p:plain
中間フォーマットでエクスポートしたものを、他のCADで読み込んだ際
こんな感じになってガッカリした経験は無いでしょうか?
f:id:kandennti:20171027193141p:plain
何で、色が抜けるのか? 僕、理由を知ってます。
実はデータの状態で色が抜けてしまうようです。
f:id:kandennti:20171027193149p:plain
ちょっと大げさにしましたが赤印位置にボディがあり
全てルートコンポーネント(一番トップのコンポーネント)ではない位置に
入っています。 原因はこれなんです。
(Upしたデータは、子コンポーネントに入っているような気がしてます)

色を反映させてエクスポートしたいのであれば、全てのボディを
ルートコンポーネントに移動させればOKです。
f:id:kandennti:20171027193200p:plain
これって、Tree上でD&Dをやれば済む話ではあるのですが、
対象のボディが案外Treeの深い位置にあり、数が多いと面倒なんです。

・・・そう、面倒。 なので全てのボディをかき集めるスプリクトを
作ってみました。

#FusionAPI_python
#Author-kantoku
#Description-Collect_All_Body
#アクティブなプロダクトの全てのボディをルートにかき集める

import adsk.core, adsk.fusion, traceback

def run(context):
    ui = None
    try:
        app = adsk.core.Application.get()
        ui  = app.userInterface
        
        #確認
        msg = '全てのボディをルートコンポーネントにかき集めます。\nよろしいですか?'
        title = 'ボディかき集め'
        btn = adsk.core.MessageBoxButtonTypes.OKCancelButtonType
        icon = adsk.core.MessageBoxIconTypes.QuestionIconType
        Res = ui.messageBox(msg, title, btn, icon) 
        
        if Res != adsk.core.DialogResults.DialogOK: return       
        
        #モロモロ
        product = app.activeProduct
        des = adsk.fusion.Design.cast(product)       
        
        #パラメトリックじゃないとNG
        des.designType = adsk.fusion.DesignTypes.ParametricDesignType
        
        #ルート
        root = des.rootComponent
        
        #ルート以下のコンポーネント
        cmplst = []
        cmplst = Get_Comps(root, cmplst)
        
        #全ボディコンテナ
        bdyslst = [cmp.bRepBodies for cmp in cmplst if cmp.bRepBodies.count > 0]
        
        #全ボディ
        bdylst = []
        for bdys in bdyslst:
            for bdy in bdys:
                bdylst.append(bdy)
        if bdylst.count == 0 :
            return
        
        #カットペースト
        tgtBodies = root.features.cutPasteBodies
        [tgtBodies.add(bdy) for bdy in bdylst]
        
        #おしまい
        ui.messageBox('Done')

    except:
        if ui:
            ui.messageBox('エラー\n{}'.format(traceback.format_exc()))

#全コンポーネント
def Get_Comps(comp, complst):
    occs = comp.occurrences
    
    lst = [occ.component for occ in occs]
    
    if lst.count == 0:
        return complst
    
    for cmp in lst:
        complst.append(cmp)
        Get_Comps(cmp, complst)
    return complst

Pythonまだまだ駄目だなぁ。見事に二重ループがあるし…。
こんな感じです。

本当は、CATIAのProductからPartを作るコマンドをイメージしていたのですが
異なるDocument間でどうやったらコピペ出来るのか、方法わかりませんでした。
僕はこれで事足りるんですけどね。

Drawのテーブルに最後の列を追加2

こちらの続きです。
Drawのテーブルに最後の列を追加 - C#ATIA

前回のマクロ、ちょっとダメなんです。
セルの文字だけのコピーだったので、フォントのボールド等が正しくなかったのです。
f:id:kandennti:20171027123422p:plain

寸法テーブルのサイズ変更する方法をmineさんに教えて
頂いたのですが、実際に使う際、一定しない列数を追加し
テーブルの見出し? も入れたいのです。
f:id:kandennti:20171027123434p:plain
結構めんどくさいんです。

そこで修正してみました。

'vba DrawTable_Add_LastColumn ver0.0.2  using-'KCL0.0.12'  by Kantoku

Option Explicit

Private Const Def = "hoge,piyo,huga" 'デフォルト文字

Sub CATMain()

    'ドキュメントのチェック
    If Not CanExecute("DrawingDocument") Then Exit Sub

    'テーブル選択
    Dim Msg As String
    Msg = "列を追加するテーブルを選択 // [Esc]=Cancel"
    
    Dim Tbl As DrawingTable
    Set Tbl = KCL.SelectItem(Msg, "DrawingTable")
    If Tbl Is Nothing Then Exit Sub
    
    'ユーザー
    Msg = "追加するタイトルを入力 例)hoge,piyo""
    Dim Inp As String
    Inp = InputBox(Msg, "", Def)
    If Inp = vbNullString Then Exit Sub
    
    '追加数列
    Dim Txts As Variant: Txts = Split(Inp, ",")
    Dim AddCnt As Long: AddCnt = UBound(Txts) + 1
    
    '元列数
    Dim CntCol As Long: CntCol = Tbl.NumberOfColumns
    Dim AllColCnt As Long: AllColCnt = CntCol + AddCnt
    
    '列追加
    Tbl.ComputeMode = CatTableComputeOFF
    
    Dim i As Long
    For i = 1 To AddCnt
        Call Tbl.AddColumn(CntCol)
    Next
    
    '最後を追加の最初にコピー
    Call Copy_Column(AllColCnt, CntCol, Tbl)
    
    '空欄設置
    Call Reset_Column(CntCol + 1, AllColCnt, Tbl)
    
    'タイトル設置
    Call Set_Title(Txts, 25#, Tbl)
    
    Tbl.ComputeMode = CatTableComputeON
End Sub

Private Sub Set_Title(ByVal Ary As Variant, _
                      ByVal Width As Double, _
                      ByVal Tbl As DrawingTable)
    Dim Ed As Long: Ed = Tbl.NumberOfColumns
    Dim St As Long: St = Ed - UBound(Ary)
    
    Dim i As Long, Cell As DrawingText
    Dim Cnt As Long: Cnt = 0
    
    For i = St To Ed
        Call Tbl.SetColumnSize(i, Width)
        Set Cell = Tbl.GetCellObject(1, i)
        Cell.Text = Ary(Cnt)
        Cnt = Cnt + 1
        Cell.TextProperties.Justification = catCenter
    Next
End Sub

Private Sub Reset_Column(ByVal A As Long, _
                         ByVal B As Long, _
                         ByVal Tbl As DrawingTable)

    Dim Vw As DrawingView: Set Vw = Tbl.Parent.Parent
    Dim Dmy As DrawingText: Set Dmy = Vw.Texts.Add("", 0#, 0#)
    Dmy.TextProperties.Justification = catCenter
    
    Dim CntRow As Long: CntRow = Tbl.NumberOfRows
    Dim Cell As DrawingText
    Dim i As Long
    
    For i = 1 To CntRow
        Set Cell = Tbl.GetCellObject(i, B)
        Call Copy_DrwTxtProperties(Cell.TextProperties, _
                                   Dmy.TextProperties)
        Cell.Text = ""
    Next
    
    For i = B To A + 1 Step -1
        Call Copy_Column(i, i - 1, Tbl)
    Next
    
    Dim Sel As Selection
    Set Sel = KCL.GetParent_Of_T(Vw, "DrawingDocument").Selection
    With Sel
        .Clear
        .Add Dmy
        .Delete
    End With
End Sub

Private Sub Copy_Column(ByVal A As Long, _
                        ByVal B As Long, _
                        ByVal Tbl As DrawingTable)
    Dim CntRow As Long: CntRow = Tbl.NumberOfRows
    Dim Cell(1) As DrawingText
    Dim TxtPpt(1) As DrawingTextProperties
    Dim i As Long
    
    For i = 1 To CntRow
        Set Cell(0) = Tbl.GetCellObject(i, A)
        Set Cell(1) = Tbl.GetCellObject(i, B)
        Call Copy_DrwTxtProperties(Cell(0).TextProperties, _
                                   Cell(1).TextProperties)
        Cell(1).Text = Cell(0).Text
    Next
End Sub

Private Sub Copy_DrwTxtProperties(ByVal Tpp1 As DrawingTextProperties, _
                                  ByVal Tpp2 As DrawingTextProperties)
    With Tpp2
        .AnchorPoint = Tpp1.AnchorPoint
        .Blanking = Tpp1.Blanking
        .Bold = Tpp1.Bold
        .Color = Tpp1.Color
        .FONTNAME = Tpp1.FONTNAME
        .FONTSIZE = Tpp1.FONTSIZE
        .FrameName = Tpp1.FrameName
        .FrameType = Tpp1.FrameType
        .Italic = Tpp1.Italic
        .Justification = Tpp1.Justification
        .Kerning = Tpp1.Kerning
        .Mirror = Tpp1.Mirror
        .Overline = Tpp1.Overline
        .StrikeThru = Tpp1.StrikeThru
        .Subscript = Tpp1.Subscript
        .Underline = Tpp1.Underline
    End With
End Sub

見事な肥大化っぷり・・・。
最初の時点ではかなり遅かったのですが、DrawingTable.ComputeModeを
利用するとストレスを感じるほどでは無くなりました。

マクロ実行後、ダイアログが出現しカンマ区切りで文字を入力すると
区切った分の列を追加し、見出しとして入力されます。

作ったけど、使うかなぁ・・・。

複数のフォルダを削除する

仕事が薄い時に、日頃の不満を解消したい。

PowerMill内のフォルダを削除したい時が多々あるのですが、
まとめて削除出来ず、一個一個チマチマやるのが辛いです。

一度に複数のフォルダを削除する為のマクロを作りました。

//PMill_Macro2018
//フォルダー削除 ver0.0.1

function main(string args) {
	//フォルダリスト
	string list folderLst = {}
	call GetFolderNameLIST($args, $folderLst)
	if is_empty($folderLst) {
		message error  'フォルダがありません!!'
		return
	}
	
	string msg = '削除するフォルダーを選択してください'
	call Exec_DownMenu($folderLst, $msg, 1, $folderLst)
	if size($folderLst) < 1 {
		return
	}
	
	//削除
	call Msgoff()
	string  path = ""
	foreach fdr in $folderLst {
		$path = $args + '\' + replace($fdr, '@', '\')
		//message info  $path 
		string cmd = 'DELETE ' + $args + ' FOLDER "' + $path + '"'
		call Exec_Cmd(cmd) 
	}
	call Msgon()
	
	//終了
	message info  'Done'	
}

//指定した組み込みルートフォルダ内のフォルダ名を取得
function GetFolderNameLIST(string FolderType, output string list Outlst ) {
	string list Folders = get_folders($FolderType)
	if (size($Folders) == 0) {
		return
   }
   
	string list names={}
	foreach path in $Folders {
		string name = path
		$name = replace($name, $FolderType + '\', '')
		$name = replace($name, '\', '@')
		int dmy = add_last($names,$name)
   }
	$Outlst= $names
	return
}
//コマンド
function Exec_Cmd(string cmd) {
	docommand $cmd
}

function Msgoff() {
	graphics lock
	dialogs message off
	dialogs error off
}

function Msgon() {
	graphics unlock
	dialogs message on
	dialogs error on
}
include downmenu.inc

mainのargsは、"Toolpath"等のフォルダ名で、コンテキストメニューから起動させる
タイプです。

過去に、こちらでフォルダ名を取得するものを作りましたが、
イマイチだった為、Forumにあった方法をパクリ参考に
させてもらいました。
フォルダ名リストを取得する - C#ATIA

又、以前作成したプルダウンリストが必要です。
"group" フォルダは犠牲にした、プルダウンリストライブラリ - C#ATIA

Drawのテーブルに最後の列を追加

Drawの穴寸法/座標寸法テーブルで、最後の列を増やしたい事が
結構あるのですが、恐らく手動では出来ないような気がしてます。
手動で出来ます(コメント部で、mineさんに教えて頂きました)
f:id:kandennti:20171023192119p:plain
(方法知っている方いらっしゃいましたら、教えて頂けると助かります)

仕方ないので、マクロを作成しました。

'vba DrawTable_Add_LastColumn ver0.0.1  using-'KCL0.0.12'  by Kantoku

Option Explicit

Sub CATMain()

    'ドキュメントのチェック
    If Not CanExecute("DrawingDocument") Then Exit Sub

    'テーブル選択
    Dim Msg As String
    Msg = "最後に列を追加するテーブルを選択 // [Esc]=Cancel"
    
    Dim Tbl As DrawingTable
    Set Tbl = KCL.SelectItem(Msg, "DrawingTable")
    If Tbl Is Nothing Then Exit Sub
    
    '一列追加
    Dim CntCol As Long: CntCol = Tbl.NumberOfColumns
    Call Tbl.AddColumn(CntCol)
    
    'コピー
    Dim CntRow As Long: CntRow = Tbl.NumberOfRows
    Dim Txt As String, i As Long
    
    For i = 1 To CntRow
        Txt = Tbl.GetCellString(i, CntCol + 1)
        Call Tbl.SetCellString(i, CntCol, Txt)
        Call Tbl.SetCellString(i, CntCol + 1, "")
    Next
End Sub

最後から2番目の位置に列を追加し、最後の列をコピーしつつ
最後の列を空欄にしています。(vbNullStringではNG)

コード的にはくだらないのですが、これを手動でやると本当に
手間がかかるんですよね。Excelの様に複数のセルをまとめて
コピー出来ないですし。

・・・最後に追加出来ない! ってMS製品でも多いですよね?

参考寸法

手抜きメニューに登録しながらマクロを修正していたら
地味なヤツを見つけました。

こんな感じの、丸括弧付きの寸法って何と呼ぶのが正式なんでしょうか?
(きっとJISとかで決まっていると思うのですが)

f:id:kandennti:20171020102205p:plain

僕は参考寸法と呼んでいます。 こちらでもそんな記載になってます。
寸法の記入の基本ルール
「2重寸法の禁止」の所に記載されています。
(個人的に重複した寸法は、かなり重罪です)

こちらでは括弧寸法と記載されていました。
括弧寸法の使い方をここまで細かく語るエンジニアはいない | メカ設計のツボ|マザーマシンの元エンジニアが語る


これ、図面を描いているAdvanceCADには、寸法をクリックしただけで
行ってくれる機能があるのですが、CATIAには無いですよね?

これを行うだけのマクロです。

'vba Sample_Draw_Ref_Dimension ver0.0.2  using-'KCL0.0.12'

Option Explicit

Private Enum Dimension_Value
    Main_Value = 1
    Dual_Value = 2
End Enum

Sub CATMain()

    'ドキュメントのチェック
    If Not CanExecute("DrawingDocument") Then Exit Sub
    
    Dim Msg$: Msg = "参考/非参考化する寸法を選択 // [Esc]=Cancel"
    Dim DrwDim As DrawingDimension
    Dim DimVal As Variant 'DrawingDimValue
    Dim Bfr$, Afr$, Upr$, Lwr$
    
    Do
        Set DrwDim = KCL.SelectItem(Msg, "DrawingDimension")
        If DrwDim Is Nothing Then Exit Sub
        
        Set DimVal = DrwDim.GetValue
        Call DimVal.GetBaultText(Dimension_Value.Main_Value, _
                                 Bfr, Afr, Upr, Lwr)
        If IsRefDimension(Bfr, Afr) Then
            '既に参考寸法
            Call DimVal.SetBaultText(Dimension_Value.Main_Value, _
                                    Mid(Bfr, 2), _
                                    Left(Afr, Len(Afr) - 1), _
                                    Upr, Lwr)
        Else
            'ただの寸法
            Call DimVal.SetBaultText(Dimension_Value.Main_Value, _
                                    "(" & Bfr, _
                                    Afr & ")", _
                                    Upr, Lwr)
        End If
    Loop
End Sub

Private Function IsRefDimension(ByVal Bfr As String, _
                                ByVal Afr As String) As Boolean
    IsRefDimension = False
    
    If Bfr = vbNullString Or Afr = vbNullString Then Exit Function
    
    Dim Tp$, Ed$
    Tp = Left(Bfr, 1)
    Ed = Right(Afr, 1)
    
    If Tp = "(" And Ed = ")" Then IsRefDimension = True
End Function

寸法のプロパティの [寸法テキスト]タブ [関連テキスト] の
・前側の最初の文字が「(」
・後側の最後の文字が「)」
の二つの条件がそろった時に、丸括弧を外し
そうでないときは丸括弧を追加します。
f:id:kandennti:20171020102233p:plain

こんな感じです。

・・・地味

手抜きマクロ起動用メニュー3

こちらの続きです。
手抜きマクロ起動用メニュー2 - C#ATIA

「Cat_Macro_Menu_Model.bas」の宣言セクション部に幾つか定数を
設けています。お好みで変更してください。

'Cat_Macro_Menu_Model.bas
'using-'KCL0.0.12'
'タグを追記したモジュールをプロジェクトに追加するだけで
'自動的にボタンを追加します

Const FormTitle = "Macro"

'----- メニューの仕様 お好みで ---------------------------------------

'メニューの表示方法
'True-モーダレス表示  False-モーダル表示
Private Const MENU_SHOW_TYPE = True

'メニューを閉じるタイミング
'True-フォームのXボタンを押して閉じます False-ボタンクリック後閉じます
Private Const MENU_HIDE_TYPE = True

'メニューのマルチページの設定
'変更する際は
'{ タグのグループ番号 : ページのタイトル文字 }
'の状態にして下さい
Private Const GROUP_NUMBER_CAPTION = _
            "{ 1 : Part }" & _
            "{11 : Assy }" & _
            "{21 : Draw }" & _
            "{51 : Other }"
'-----------------------------------------------------------------
・・・

・「MENU_SHOW_TYPE」
  メニューの表示で、モーダレスかモーダルかをBoolean型で切り替えられます。

・「MENU_HIDE_TYPE」
  メニュー終了タイミングですが、Upしたデータのコメント間違えていました・・。
  正しくは上記の状態で、Boolean型で切り替えられます。
  ボタンクリック後に終了させるか、表示しっぱなしかです。

  実質、「モーダレス + 表示しっぱなし」 か 「モーダル + クリック後終了」
  に、なるのだろうと思います。

・「GROUP_NUMBER_CAPTION」
  マルチページの分類と表示させる文字になります。
  挿入する標準モジュールの {Gp}タグは、こちらの番号にない場合は
  ボタンが表示されません。


続いて、フォームコードの「Cat_Macro_Menu_View.frm」の宣言セクション部です。
フォームのレイアウト等はこちらの定数で、若干調整が可能です。
(環境によってはボタンの文字が途切れる等あるかもしれないため・・・))

'Cat_Macro_Menu_View.frm
'メニューのUIです。

'フォーム
Private FrmMargin As Variant '上,下,左,右のボタン配置のマージン
Private Const ADJUST_F_W = 13 'フォームの左右の調整幅
Private Const ADJUST_F_H = 30 'フォームの上下の調整幅

'マルチページ
Private Const ADJUST_M_W = 5 'マルチページの左右の調整幅
Private Const ADJUST_M_H = 18 'マルチページの上下の調整幅

'ボタン
Private Const BTN_W = 70 'ボタンの幅-フォームの最低幅以下にすると余白が増える
Private Const BTN_H = 20 'ボタン1個の高さ

・・・

各マルチページに該当するボタンがない場合は、ページ自体が作成されません。
又、各ページに配置されるボタンの順番は、モジュール名でソートして表示させています。
好みの順番で表示させたい場合は・・・・モジュール名を工夫してください。
(良いアイデアが思いつきませんでした)

あと何か有ったかなぁ? マクロ用のメニューなんて今更だし。


念の為、このままではOffice製品では機能しないです。

プロジェクト名・パスの取得方法は、imihitoさんが
こちらのコメント欄に記載して頂いてます。
実行中のプロジェクト名とパス取得する - C#ATIA

ボタン押した際の各マクロの実行方法は、こちらの
「Application.Run」の方法で可能だろうと思います。
外部のマクロを実行する2 - C#ATIA


正規表現もまともに使ったの初めてでしたし、まさか自分が
CallByNameを使うコードを書く日が来るとは思っていませんでしたよ。