Accessing Appearance Thumbnails

Accessing Appearance Thumbnails

nnikbin
Collaborator Collaborator
780 Views
4 Replies
Message 1 of 5

Accessing Appearance Thumbnails

nnikbin
Collaborator
Collaborator

I would like to fill a DropDownCommandInput whose DropDownStyle is LabeledIconDropDownStyle with the names and thumbnails of all appearances available in a library.

 

Iterating through appearances and filling the names is simple but the difficult part is accessing the thumbnails of appearances.

 

I know that adsklib files are zip archives and appearance thumbnails are inside them as png resources. For example the following picture shows part of the contents of an adsklib file whose extension has changed to zip:

 

58.png

Is there any simple way to access these thumbnails and use them in a DropDownCommandInput?

0 Likes
781 Views
4 Replies
Replies (4)
Message 2 of 5

kandennti
Mentor
Mentor

Hi @nnikbin .

 

The icon is created by extracting the png file from the zip file.

 

# Description - Material Thumbnail Test
# Fusion360API Python script

import adsk.core
import adsk.fusion
import adsk.cam
import traceback

import re
import io
import zipfile
import pathlib
import shutil
from PIL import Image

_app = None
_ui = None
_handlers = []


def getAdsklibPaths() -> list:
    app: adsk.core.Application = adsk.core.Application.get()

    materialPath = pathlib.Path(
        app.executeTextCommand(f'Materials.MaterialLibraryPath'))

    adsklibPaths = [p for p in materialPath.iterdir()
                    if p.is_file() and p.suffix == '.adsklib']

    return adsklibPaths


def createIcon(zipPath: str):

    iconPaths = []
    basePath = getTempFolder()

    with zipfile.ZipFile(zipPath) as myzip:
        # get png file path
        paths = [f for f in myzip.namelist() if re.search('._png', f) != None]

        for path in paths:
            # open Byte stream
            with myzip.open(path) as img_file:
                img_bin = io.BytesIO(img_file.read())
                img = Image.open(img_bin)

                # get  material id
                path = pathlib.Path(path)
                id = re.sub(r'[\\|/|:|?|.|"|<|>|\|]', '-', str(path.parts[2]))
                iconPath = basePath / id

                # resize
                for width, height in [(32, 32), (16, 16)]:
                    # create icon folder
                    iconPath.mkdir(parents=True, exist_ok=True)

                    savePath = iconPath / f'{width}x{height}.png'
                    clone = img.copy()
                    img_resize_lanczos = clone.resize(
                        (width, height), Image.LANCZOS)

                    # save png
                    img_resize_lanczos.save(str(savePath))

            iconPaths.append(str(iconPath))

    return iconPaths


def getTempFolder():

    folder = pathlib.Path(__file__).parent/'temp'
    try:
        shutil.rmtree(folder)
    except:
        pass
    finally:
        folder.mkdir(exist_ok=True)

    return folder


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

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

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

            # get adsklib files
            adsklibPaths = getAdsklibPaths()

            # Change to the library of your choice.
            imagePaths = createIcon(adsklibPaths[-1])

            dropdownInput2 = inputs.addDropDownCommandInput(
                'dropdown1',
                'test',
                adsk.core.DropDownStyles.LabeledIconDropDownStyle
            )
            dropdown2Items = dropdownInput2.listItems
            for idx, path in enumerate(imagePaths):
                dropdown2Items.add(
                    f'image{idx}',
                    False,
                    path
                )

        except:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))


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

    def notify(self, args):
        adsk.terminate()


def run(context):
    try:
        global _app, _ui
        _app = adsk.core.Application.get()
        _ui = _app.userInterface

        cmdDef = _ui.commandDefinitions.itemById('Test2')
        if not cmdDef:
            cmdDef = _ui.commandDefinitions.addButtonDefinition(
                'Test2', 'Test', 'Test')

        global _handlers
        onCommandCreated = MyCommandCreatedHandler()
        cmdDef.commandCreated.add(onCommandCreated)
        _handlers.append(onCommandCreated)

        cmdDef.execute()

        adsk.autoTerminate(False)

    except:
        if _ui:
            _ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

 

 

Unfortunately, DropDownCommandInput is too small to tell the difference.

1.png

 

It was very difficult for me.

Message 3 of 5

nnikbin
Collaborator
Collaborator

Hi @kandennti 

Thank you so much for providing this nice code.

 

Unfortunately I have not been able to execute your code yet, because when I run it I receive the following error in Fusion 360 (I have python 3.9.1 and I have installed Pillow using pip)

 

ModuleNotFoundError: No module named 'PIL'

 

And VS Code shows:

 59.png

In addition I do not know how can I associate thumbnails with their appearance names.

I hoped there would be a solution at higher level.

0 Likes
Message 4 of 5

kandennti
Mentor
Mentor
0 Likes
Message 5 of 5

nnikbin
Collaborator
Collaborator

@kandennti , You are awesome!

It seems there isn't any direct method at API level for this task.

0 Likes