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:
Is there any simple way to access these thumbnails and use them in a DropDownCommandInput?
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.
It was very difficult for me.
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:
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.
@kandennti , You are awesome!
It seems there isn't any direct method at API level for this task.