C#ATIA

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

全てのスケッチテキストのフォントを変更する

以前、フォーラムのこちらにアクティブなドキュメントの
全てのスケッチテキストのフォントを変更するスクリプト
作った事があります。
Solved: Re: Script to edit font across multiple sketches - Autodesk Community

実は心残りな部分がありまして、変更するフォントを
ドロップダウンリスト(コンボボックス)で表示させているのですが
f:id:kandennti:20210922150733p:plain
この使用出来るフォントのリストの取得が難しいんです。

この時は事前にフォントリストを調べてcsvファイルにしておき、
それを読み込んで対処していました。
これだと、PCによって使用出来るフォントが異なるはずなのですが
それに対応することが出来ませんでした。

今回は、実際に使用出来るフォント名を動的に取得して
ドロップダウンリストに表示させています。

# Change Sketch Text Font ver0.0.2
# Author-kantoku
# Description-Changes the font for all sketch text in the document.
# Fusion360API Python script

import adsk.core
import adsk.fusion
import traceback

# Command ID
_cmdId = 'ChangeSketchTextFont'

# TextBoxCommandInput
_txtIptId = 'txtIpt'

# DropDownCommandInput
_ddIptId = 'ddIpt'

# handlers
_handlers = []

def run(context):
    ui = None
    try:
        app :adsk.core.Application = adsk.core.Application.get()
        ui :adsk.core.UserInterface = app.userInterface

        # Command Definition
        global _cmdId, _cmdName, _cmdTooltip
        cmdDefs :adsk.core.CommandDefinitions = ui.commandDefinitions
        cmdDef :adsk.core.CommandDefinition = cmdDefs.itemById(_cmdId)
        if cmdDef:
            cmdDef.deleteMe()
        cmdDef = cmdDefs.addButtonDefinition(
            _cmdId,
            'Change Sketch Text Font',
            'Changes the font for all sketch text in the document.')

        # Command Created Handler
        global _handlers
        onCommandCreated = CommandCreatedHandler()
        cmdDef.commandCreated.add(onCommandCreated)
        _handlers.append(onCommandCreated)

        cmdDef.execute()

        adsk.autoTerminate(False)
    except:
        if ui:
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

def getAllFontList() -> list:
    app: adsk.core.Application = adsk.core.Application.get()
    ui = app.userInterface
    des: adsk.fusion.Design = app.activeProduct
    root: adsk.fusion.Component = des.rootComponent

    app.executeTextCommand(u'PTransaction.Start getFontName')

    skt: adsk.fusion.Sketch = root.sketches.add(root.xYConstructionPlane)
    sktTxtIpt: adsk.fusion.SketchTextInput = skt.sketchTexts.createInput2(
        'Arial',
        2.0
    )

    sktTxtIpt.setAsMultiLine(
        adsk.core.Point3D.create(0, 0, 0),
        adsk.core.Point3D.create(10, 5, 0),
        adsk.core.HorizontalAlignments.LeftHorizontalAlignment,
        adsk.core.VerticalAlignments.TopVerticalAlignment,
        0
    )
    sktTxt: adsk.fusion.SketchText = skt.sketchTexts.add(sktTxtIpt)

    sels: adsk.core.Selections = ui.activeSelections
    sels.clear()
    sels.add(sktTxt)

    app.executeTextCommand(u'Commands.Start EditMTextCmd')
    dialogInfo = app.executeTextCommand(u'Toolkit.cmdDialog')
    app.executeTextCommand(u'NuCommands.CancelCmd')
    skt.deleteMe()

    app.executeTextCommand(u'PTransaction.Abort')

    # app.log(dialogInfo)
    fontInfos = dialogInfo.split('TextFont,')[-1].split('TextExStyle,')[0].split('MenuItems:')[-1].split('\n')
    fonts = [str.strip(t.split(',')[0]) for t in fontInfos]
    # [print(k) for k in fonts]
    return fonts

# I think it would be more correct to create a list based on the sketches displayed, 
# sketches with specific names, etc.
def getAllSketchTextList(
    des :adsk.fusion.Design
    ) -> list:

    sktsLst = [c.sketches for c in des.allComponents]
    txts = []
    for skts in sktsLst:
        for skt in skts:
            for txt in skt.sketchTexts:
                txts.append(txt)
    
    return txts


class CommandCreatedHandler(adsk.core.CommandCreatedEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args):
        try:
            global _ui, _handlers
            cmd = adsk.core.Command.cast(args.command)

            # -- event --
            onExecute = ExecuteHandler()
            cmd.execute.add(onExecute)
            _handlers.append(onExecute)

            onDestroy = DestroyHandler()
            cmd.destroy.add(onDestroy)
            _handlers.append(onDestroy)

            # -- dialog --
            # message
            app :adsk.core.Application = adsk.core.Application.get()
            des :adsk.fusion.Design = app.activeProduct
            txts = getAllSketchTextList(des)

            if len(txts) < 1:
                cmd.isOKButtonVisible = False

            global _txtIptId
            inputs = cmd.commandInputs
            inputs.addTextBoxCommandInput(
                _txtIptId,
                'info',
                f'There are {len(txts)} sketch texts',
                1,
                True)

            if len(txts) < 1:
                return

            # font
            global _ddIptId
            fonts = getAllFontList()

            ddIpt :adsk.core.DropDownCommandInput = inputs.addDropDownCommandInput(
                _ddIptId,
                'select font',
                adsk.core.DropDownStyles.TextListDropDownStyle)
            items :adsk.core.ListItems = ddIpt.listItems

            for font in fonts:
                if txts[0].fontName == font.split('.')[0]:
                    items.add(font, True)
                else:
                    items.add(font, False)

        except:
            app :adsk.core.Application = adsk.core.Application.get()
            ui :adsk.core.UserInterface = app.userInterface
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))


class DestroyHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()
    def notify(self, args):
        try:
            adsk.terminate()
        except:
            app :adsk.core.Application = adsk.core.Application.get()
            ui :adsk.core.UserInterface = app.userInterface
            ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))


class ExecuteHandler(adsk.core.CommandEventHandler):
    def __init__(self):
        super().__init__()

    def notify(self, args):
        eventArgs = adsk.core.CommandEventArgs.cast(args)

        ipts :adsk.core.CommandInputs = eventArgs.command.commandInputs
        global _ddIptId
        ddIpt :adsk.core.DropDownCommandInput = ipts.itemById(_ddIptId)
        fontName = ddIpt.selectedItem.name

        app :adsk.core.Application = adsk.core.Application.get()
        des :adsk.fusion.Design = app.activeProduct
        txts = getAllSketchTextList(des)

        for txt in txts:
            try:
                txt.fontName = fontName
            except:
                pass

        des.computeAll()

今回は "getAllFontList" 関数を書き換えただけなんですけどね。
PCに登録されているフォントのリストの取得方法があるのは
知っているのですが、その場合だとFusion360で使用出来るとは
限らない為、実際にスケッチテキストのダイアログから取得しています。

本当は正規表現でリストを取得したかったのですが、上手く行かず
断念。残念。