save plugins Fusion 360

This commit is contained in:
2022-09-21 21:50:43 +02:00
commit 5a41c6454a
74 changed files with 1988 additions and 0 deletions

1
ControllByPs4/.env Normal file
View File

@@ -0,0 +1 @@
PYTHONPATH=C:/Users/bartool/AppData/Local/Autodesk/webdeploy/production/9209df45963e1599ff476303834125d21fd43de4/Api/Python/packages

20
ControllByPs4/.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,20 @@
{
"version": "0.2.0",
"configurations": [{
"name": "Python: Attach",
"type": "python",
"request": "attach",
"pathMappings": [{
"localRoot": "${workspaceRoot}",
"remoteRoot": "${workspaceRoot}"
}],
"osx": {
"filePath": "${file}"
},
"windows": {
"filePath": "${file}"
},
"port": 9000,
"host": "localhost"
}]
}

5
ControllByPs4/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,5 @@
{
"python.autoComplete.extraPaths": ["C:/Users/bartool/AppData/Roaming/Autodesk/Autodesk Fusion 360/API/Python/defs"],
"python.analysis.extraPaths": ["C:/Users/bartool/AppData/Roaming/Autodesk/Autodesk Fusion 360/API/Python/defs"],
"python.pythonPath": "C:/Users/bartool/AppData/Local/Autodesk/webdeploy/production/9209df45963e1599ff476303834125d21fd43de4/Python/python.exe"
}

View File

@@ -0,0 +1,145 @@
# Assuming you have not changed the general structure of the template no modification is needed in this file.
# from . import commands
from tkinter import E
from .lib import fusion360utils as futil
import threading
import adsk.core
import adsk.fusion
import adsk.cam
import traceback
import json
app = None
ui = adsk.core.UserInterface.cast(None)
handlers = []
stopFlag = None
myCustomEvent = 'MyCustomEventId'
customEvent = None
# The event handler that responds to the custom event being fired.
class ThreadEventHandler(adsk.core.CustomEventHandler):
def __init__(self):
super().__init__()
self.viewport = app.activeViewport
def notify(self, args: adsk.core.CustomEventArgs):
global app
try:
# Make sure a command isn't running before changes are made.
activeCmd = ui.activeCommand
if activeCmd != 'SelectCommand':
ui.commandDefinitions.itemById('SelectCommand').execute()
camera = self.viewport.camera
eye = camera.eye
target = camera.target
up = camera.upVector
zoom = camera.viewExtents
front = eye.vectorTo(target)
right = up.crossProduct(front)
rotate_matrix = adsk.core.Matrix3D.create()
rotate_matrix.setWithCoordinateSystem(target, right, front, up)
mat = rotate_matrix.asArray()
# Get the value from the JSON data passed through the event.
eventArgs = json.loads(args.additionalInfo)
X_radians = float(eventArgs['X_rotation'])
if X_radians:
rotate_matrix.setToRotation(X_radians, front, target)
eye.transformBy(rotate_matrix)
up.transformBy(rotate_matrix)
Y_radians = float(eventArgs['Y_rotation'])
if Y_radians:
rotate_matrix.setToRotation(Y_radians, right, target)
eye.transformBy(rotate_matrix)
up.transformBy(rotate_matrix)
Z_radians = float(eventArgs['Z_rotation'])
if Z_radians:
rotate_matrix.setToRotation(Z_radians, up, target)
eye.transformBy(rotate_matrix)
up.transformBy(rotate_matrix)
X_pan = float(eventArgs['X_pan'])
Y_pan = float(eventArgs['Y_pan'])
Z_pan = float(eventArgs['Z_pan'])
if any([X_pan, Z_pan]):
move_vector = adsk.core.Vector3D.create(X_pan, 0, Z_pan)
move_vector.transformBy(rotate_matrix)
eye.translateBy(move_vector)
target.translateBy(move_vector)
if Y_pan:
zoom = zoom + Y_pan * 10
camera.isSmoothTransition = True
camera.eye = eye
camera.upVector = up
camera.target = target
camera.viewExtents = zoom
app.activeViewport.camera = camera
app.activeViewport.refresh()
except:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def rotate(self, X_radians: float, Y_radians: float, Z_radians: float):
pass
# The class for the new thread.
class MyThread(threading.Thread):
def __init__(self, event):
threading.Thread.__init__(self)
self.stopped = event
def run(self):
# Every five seconds fire a custom event, passing a random number.
while not self.stopped.wait(2):
args = {'X_rotation': 0.0, 'Y_rotation': 0.0, 'Z_rotation': 0.0,
'X_pan': 0.0, 'Y_pan': 1.0, 'Z_pan': 0.0}
app.fireCustomEvent(myCustomEvent, json.dumps(args))
def run(context):
global ui
global app
try:
app = adsk.core.Application.get()
ui = app.userInterface
# Register the custom event and connect the handler.
global customEvent
customEvent = app.registerCustomEvent(myCustomEvent)
onThreadEvent = ThreadEventHandler()
customEvent.add(onThreadEvent)
handlers.append(onThreadEvent)
# Create a new thread for the other processing.
global stopFlag
stopFlag = threading.Event()
myThread = MyThread(stopFlag)
myThread.start()
except:
futil.handle_error('run')
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def stop(context):
try:
if handlers.count:
customEvent.remove(handlers[0])
stopFlag.set()
app.unregisterCustomEvent(myCustomEvent)
ui.messageBox('Stop addin')
except:
futil.handle_error('stop')
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

View File

@@ -0,0 +1,13 @@
{
"autodeskProduct": "Fusion360",
"type": "addin",
"id": "1a7fc1eb-71fd-42da-86f0-a7e640d647d0",
"author": "",
"description": {
"": ""
},
"version": "",
"runOnStartup": false,
"supportedOS": "windows|mac",
"editEnabled": true
}

View File

@@ -0,0 +1,24 @@
# Assuming you have not changed the general structure of the template no modification is needed in this file.
from . import commands
from .lib import fusion360utils as futil
def run(context):
try:
# This will run the start function in each of your commands as defined in commands/__init__.py
commands.start()
except:
futil.handle_error('run')
def stop(context):
try:
# Remove all of the event handlers your app has created
futil.clear_handlers()
# This will run the start function in each of your commands as defined in commands/__init__.py
commands.stop()
except:
futil.handle_error('stop')

Binary file not shown.

View File

@@ -0,0 +1,29 @@
# Here you define the commands that will be added to your add-in.
# TODO Import the modules corresponding to the commands you created.
# If you want to add an additional command, duplicate one of the existing directories and import it here.
# You need to use aliases (import "entry" as "my_module") assuming you have the default module named "entry".
import imp
from .commandDialog import entry as commandDialog
from .backend import backend
# TODO add your imported modules to this list.
# Fusion will automatically call the start() and stop() functions.
commands = [
commandDialog,
backend
]
# Assumes you defined a "start" function in each of your modules.
# The start function will be run when the add-in is started.
def start():
for command in commands:
command.start()
# Assumes you defined a "stop" function in each of your modules.
# The stop function will be run when the add-in is stopped.
def stop():
for command in commands:
command.stop()

View File

@@ -0,0 +1,178 @@
# Assuming you have not changed the general structure of the template no modification is needed in this file.
# from . import commands
from ast import arg
from glob import glob
from ...lib import fusion360utils as futil
import threading
import adsk.core
import adsk.fusion
import adsk.cam
import traceback
import json
CMD_NAME = 'View Controller backend'
app = adsk.core.Application.get()
ui = app.userInterface
stopFlag = None
cameraViewEvent = 'CameraViewEventId'
updateConstantsEvent = "UpdateConstantsEventId"
customEvents = []
local_handlers = []
X_ROTATION = 0
Y_ROTATION = 0
Z_ROTATION = 0
X_PAN = 0
Y_PAN = 0
Z_PAN = 0
class UpdateConstantsEventHandler(adsk.core.CustomEventHandler):
def __init__(self):
super().__init__()
def notify(self, args: adsk.core.CustomEventArgs) -> None:
futil.log(f'{CMD_NAME} UpdateConstants notity...')
eventArgs = json.loads(args.additionalInfo)
global X_ROTATION, Y_ROTATION, Z_ROTATION
global X_PAN, Y_PAN, Z_PAN
X_ROTATION = float(eventArgs["X_rotation"])
Y_ROTATION = float(eventArgs["Y_rotation"])
Z_ROTATION = float(eventArgs["Z_rotation"])
X_PAN = float(eventArgs["X_pan"])
Y_PAN = float(eventArgs["Y_pan"])
Z_PAN = float(eventArgs["Z_pan"])
# The event handler that responds to the custom event being fired.
class CameraViewEventHandler(adsk.core.CustomEventHandler):
def __init__(self):
super().__init__()
self.viewport = app.activeViewport
def notify(self, args: adsk.core.CustomEventArgs):
global app
try:
# Make sure a command isn't running before changes are made.
activeCmd = ui.activeCommand
if activeCmd != 'SelectCommand':
ui.commandDefinitions.itemById('SelectCommand').execute()
camera = self.viewport.camera
eye = camera.eye
target = camera.target
up = camera.upVector
zoom = camera.viewExtents
front = eye.vectorTo(target)
right = up.crossProduct(front)
rotate_matrix = adsk.core.Matrix3D.create()
rotate_matrix.setWithCoordinateSystem(target, right, front, up)
mat = rotate_matrix.asArray()
# Get the value from the JSON data passed through the event.
eventArgs: dict = json.loads(args.additionalInfo)
X_radians = float(eventArgs.get('X_rotation', 0)) * X_ROTATION
if X_radians:
rotate_matrix.setToRotation(X_radians, front, target)
eye.transformBy(rotate_matrix)
up.transformBy(rotate_matrix)
Y_radians = float(eventArgs.get('Y_rotation', 0)) * Y_ROTATION
if Y_radians:
rotate_matrix.setToRotation(Y_radians, right, target)
eye.transformBy(rotate_matrix)
up.transformBy(rotate_matrix)
Z_radians = float(eventArgs.get('Z_rotation', 0)) * Z_ROTATION
if Z_radians:
rotate_matrix.setToRotation(Z_radians, up, target)
eye.transformBy(rotate_matrix)
up.transformBy(rotate_matrix)
X_pan = float(eventArgs.get('X_pan', 0)) * X_pan
Y_pan = float(eventArgs.get('Y_pan', 0)) * Y_pan
Z_pan = float(eventArgs.get('Z_pan', 0)) * Z_pan
if any([X_pan, Z_pan]):
move_vector = adsk.core.Vector3D.create(X_pan, 0, Z_pan)
move_vector.transformBy(rotate_matrix)
eye.translateBy(move_vector)
target.translateBy(move_vector)
if Y_pan:
zoom = zoom + Y_pan * 10
camera.isSmoothTransition = True
camera.eye = eye
camera.upVector = up
camera.target = target
camera.viewExtents = zoom
app.activeViewport.camera = camera
app.activeViewport.refresh()
except:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def rotate(self, X_radians: float, Y_radians: float, Z_radians: float):
pass
# The class for the new thread.
class ControllerThread(threading.Thread):
def __init__(self, event):
threading.Thread.__init__(self)
self.stopped = event
def run(self):
# Every five seconds fire a custom event, passing a random number.
while not self.stopped.wait(2):
futil.log(f'{CMD_NAME} Thread start sending data')
args = {'X_rotation': 0.0, 'Y_rotation': 0.0, 'Z_rotation': 0.0,
'X_pan': 0.0, 'Y_pan': 0.0, 'Z_pan': 0.0}
app.fireCustomEvent(cameraViewEvent, json.dumps(args))
def start():
futil.log(f'{CMD_NAME} Start...')
# Register the custom event and connect the handler.
global customEvents
customEvents.append(app.registerCustomEvent(cameraViewEvent))
onCameraViewEvent = CameraViewEventHandler()
customEvents[0].add(onCameraViewEvent)
local_handlers.append(onCameraViewEvent)
futil.log(f'{CMD_NAME} CameraViewEvent register')
customEvents.append(app.registerCustomEvent(updateConstantsEvent))
onUpadateConstantEvent = UpdateConstantsEventHandler()
customEvents[1].add(onUpadateConstantEvent)
local_handlers.append(onUpadateConstantEvent)
futil.log(f'{CMD_NAME} UpdateConstantsEvent register')
# Create a new thread for the other processing.
global stopFlag
stopFlag = threading.Event()
myThread = ControllerThread(stopFlag)
# myThread.start()
futil.log(f'{CMD_NAME} ControllerThread started')
def stop():
try:
for event, handler in zip(customEvents, local_handlers):
event.remove(handler)
# if local_handlers.count:
# customEvent.remove(local_handlers[0])
stopFlag.set()
app.unregisterCustomEvent(cameraViewEvent)
app.unregisterCustomEvent(updateConstantsEvent)
ui.messageBox('Stop addin')
except:
futil.handle_error('stop')
# if ui:
# ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

View File

@@ -0,0 +1,190 @@
import adsk.core
import os
from ...lib import fusion360utils as futil
from ... import config
import json
app = adsk.core.Application.get()
ui = app.userInterface
# TODO *** Specify the command identity information. ***
CMD_ID = f'{config.COMPANY_NAME}_{config.ADDIN_NAME}_cmdDialog'
CMD_NAME = 'View Controller'
CMD_Description = 'A Fusion 360 Add-in Command with a dialog'
# Specify that the command will be promoted to the panel.
IS_PROMOTED = True
# TODO *** Define the location where the command button will be created. ***
# This is done by specifying the workspace, the tab, and the panel, and the
# command it will be inserted beside. Not providing the command to position it
# will insert it at the end.
WORKSPACE_ID = 'FusionSolidEnvironment'
PANEL_ID = 'SolidScriptsAddinsPanel'
COMMAND_BESIDE_ID = 'ScriptsManagerCommand'
# Resource location for command icons, here we assume a sub folder in this directory named "resources".
ICON_FOLDER = os.path.join(os.path.dirname(
os.path.abspath(__file__)), 'resources', '')
# Local list of event handlers used to maintain a reference so
# they are not released and garbage collected.
local_handlers = []
# Executed when add-in is run.
def start():
# Create a command Definition.
cmd_def = ui.commandDefinitions.addButtonDefinition(
CMD_ID, CMD_NAME, CMD_Description, ICON_FOLDER)
# Define an event handler for the command created event. It will be called when the button is clicked.
futil.add_handler(cmd_def.commandCreated, command_created)
# ******** Add a button into the UI so the user can run the command. ********
# Get the target workspace the button will be created in.
workspace = ui.workspaces.itemById(WORKSPACE_ID)
# Get the panel the button will be created in.
panel = workspace.toolbarPanels.itemById(PANEL_ID)
# Create the button command control in the UI after the specified existing command.
control = panel.controls.addCommand(cmd_def, COMMAND_BESIDE_ID, False)
# Specify if the command is promoted to the main toolbar.
control.isPromoted = IS_PROMOTED
# Executed when add-in is stopped.
def stop():
# Get the various UI elements for this command
workspace = ui.workspaces.itemById(WORKSPACE_ID)
panel = workspace.toolbarPanels.itemById(PANEL_ID)
command_control = panel.controls.itemById(CMD_ID)
command_definition = ui.commandDefinitions.itemById(CMD_ID)
# Delete the button command control
if command_control:
command_control.deleteMe()
# Delete the command definition
if command_definition:
command_definition.deleteMe()
# Function that is called when a user clicks the corresponding button in the UI.
# This defines the contents of the command dialog and connects to the command related events.
def command_created(args: adsk.core.CommandCreatedEventArgs):
# General logging for debug.
futil.log(f'{CMD_NAME} Command Created Event')
# https://help.autodesk.com/view/fusion360/ENU/?contextId=CommandInputs
inputs = args.command.commandInputs
# TODO Define the dialog for your command by adding different inputs to the command.
# # Create a simple text box input.
# inputs.addTextBoxCommandInput(
# 'text_box', 'Some Text', 'Enter some text.', 1, False)
default_angle = adsk.core.ValueInput.createByString('5')
inputs.addAngleValueCommandInput("x_rotation", "X Rotation", default_angle)
inputs.addAngleValueCommandInput("y_rotation", "Y Rotation", default_angle)
inputs.addAngleValueCommandInput("z_rotation", "Z Rotation", default_angle)
# # Create a value input field and set the default using 1 unit of the default length unit.
defaultLengthUnits = app.activeProduct.unitsManager.defaultLengthUnits
default_value_x = adsk.core.ValueInput.createByString("1")
default_value_y = adsk.core.ValueInput.createByString("2")
default_value_z = adsk.core.ValueInput.createByString("3")
inputs.addValueInput('x_pan', 'X Disytance',
defaultLengthUnits, default_value_x)
inputs.addValueInput('y_pan', 'Y Disytance',
defaultLengthUnits, default_value_y)
inputs.addValueInput('z_pan', 'Z Disytance',
defaultLengthUnits, default_value_z)
# TODO Connect to the events that are needed by this command.
futil.add_handler(args.command.execute, command_execute,
local_handlers=local_handlers)
# futil.add_handler(args.command.inputChanged, command_input_changed, local_handlers=local_handlers)
# futil.add_handler(args.command.executePreview, command_preview, local_handlers=local_handlers)
# futil.add_handler(args.command.validateInputs, command_validate_input, local_handlers=local_handlers)
futil.add_handler(args.command.destroy, command_destroy,
local_handlers=local_handlers)
# This event handler is called when the user clicks the OK button in the command dialog or
# is immediately called after the created event not command inputs were created for the dialog.
def command_execute(args: adsk.core.CommandEventArgs):
# General logging for debug.
futil.log(f'{CMD_NAME} Command Execute Event')
# TODO ******************************** Your code here ********************************
# Get a reference to your command's inputs.
inputs = args.command.commandInputs
# text_box: adsk.core.TextBoxCommandInput = inputs.itemById('text_box')
# value_input: adsk.core.ValueCommandInput = inputs.itemById('value_input')
triad_input: adsk.core.TriadCommandInput = inputs.itemById('matrix_input')
args = {}
# value_input: adsk.core.ValueCommandInput = inputs.itemById["x_rotation"]
args["X_rotation"] = inputs.itemById("x_rotation").value
args["Y_rotation"] = inputs.itemById("y_rotation").value
args["Z_rotation"] = inputs.itemById("z_rotation").value
# angle_input: adsk.core.AngleValueCommandInput = inputs.itemById["x_pan"]
args["X_pan"] = inputs.itemById("x_pan").value
args["Y_pan"] = inputs.itemById("y_pan").value
args["Z_pan"] = inputs.itemById("z_pan").value
# Do something interesting
# text = text_box.text
# expression = value_input.expression
# msg = f'Your text: {text}<br>Your value: {expression}'
# ui.messageBox(msg)
app.fireCustomEvent('UpdateConstantsEventId', json.dumps(args))
# This event handler is called when the command needs to compute a new preview in the graphics window.
def command_preview(args: adsk.core.CommandEventArgs):
# General logging for debug.
futil.log(f'{CMD_NAME} Command Preview Event')
inputs = args.command.commandInputs
# This event handler is called when the user changes anything in the command dialog
# allowing you to modify values of other inputs based on that change.
def command_input_changed(args: adsk.core.InputChangedEventArgs):
changed_input = args.input
inputs = args.inputs
# General logging for debug.
futil.log(
f'{CMD_NAME} Input Changed Event fired from a change to {changed_input.id}')
# This event handler is called when the user interacts with any of the inputs in the dialog
# which allows you to verify that all of the inputs are valid and enables the OK button.
def command_validate_input(args: adsk.core.ValidateInputsEventArgs):
# General logging for debug.
futil.log(f'{CMD_NAME} Validate Input Event')
inputs = args.inputs
# Verify the validity of the input values. This controls if the OK button is enabled or not.
# valueInput = inputs.itemById('value_input')
# if valueInput.value >= 0:
# args.areInputsValid = True
# else:
# args.areInputsValid = False
# This event handler is called when the command terminates.
def command_destroy(args: adsk.core.CommandEventArgs):
# General logging for debug.
futil.log(f'{CMD_NAME} Command Destroy Event')
global local_handlers
local_handlers = []

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

21
ControllByPs4/config.py Normal file
View File

@@ -0,0 +1,21 @@
# Application Global Variables
# This module serves as a way to share variables across different
# modules (global variables).
import os
# Flag that indicates to run in Debug mode or not. When running in Debug mode
# more information is written to the Text Command window. Generally, it's useful
# to set this to True while developing an add-in and set it to False when you
# are ready to distribute it.
DEBUG = True
# Gets the name of the add-in from the name of the folder the py file is in.
# This is used when defining unique internal names for various UI elements
# that need a unique name. It's also recommended to use a company name as
# part of the ID to better ensure the ID is unique.
ADDIN_NAME = os.path.basename(os.path.dirname(__file__))
COMPANY_NAME = 'BTL'
# Palettes
sample_palette_id = f'{COMPANY_NAME}_{ADDIN_NAME}_palette_id'

View File

@@ -0,0 +1,2 @@
from .general_utils import *
from .event_utils import *

View File

@@ -0,0 +1,88 @@
# Copyright 2022 by Autodesk, Inc.
# Permission to use, copy, modify, and distribute this software in object code form
# for any purpose and without fee is hereby granted, provided that the above copyright
# notice appears in all copies and that both that copyright notice and the limited
# warranty and restricted rights notice below appear in all supporting documentation.
#
# AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. AUTODESK SPECIFICALLY
# DISCLAIMS ANY IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE.
# AUTODESK, INC. DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
# UNINTERRUPTED OR ERROR FREE.
import sys
from typing import Callable
import adsk.core
from .general_utils import handle_error
# Global Variable to hold Event Handlers
_handlers = []
def add_handler(
event: adsk.core.Event,
callback: Callable,
*,
name: str = None,
local_handlers: list = None
):
"""Adds an event handler to the specified event.
Arguments:
event -- The event object you want to connect a handler to.
callback -- The function that will handle the event.
name -- A name to use in logging errors associated with this event.
Otherwise the name of the event object is used. This argument
must be specified by its keyword.
local_handlers -- A list of handlers you manage that is used to maintain
a reference to the handlers so they aren't released.
This argument must be specified by its keyword. If not
specified the handler is added to a global list and can
be cleared using the clear_handlers function. You may want
to maintain your own handler list so it can be managed
independently for each command.
:returns:
The event handler that was created. You don't often need this reference, but it can be useful in some cases.
"""
module = sys.modules[event.__module__]
handler_type = module.__dict__[event.add.__annotations__['handler']]
handler = _create_handler(handler_type, callback, event, name, local_handlers)
event.add(handler)
return handler
def clear_handlers():
"""Clears the global list of handlers.
"""
global _handlers
_handlers = []
def _create_handler(
handler_type,
callback: Callable,
event: adsk.core.Event,
name: str = None,
local_handlers: list = None
):
handler = _define_handler(handler_type, callback, name)()
(local_handlers if local_handlers is not None else _handlers).append(handler)
return handler
def _define_handler(handler_type, callback, name: str = None):
name = name or handler_type.__name__
class Handler(handler_type):
def __init__(self):
super().__init__()
def notify(self, args):
try:
callback(args)
except:
handle_error(name)
return Handler

View File

@@ -0,0 +1,64 @@
# Copyright 2022 by Autodesk, Inc.
# Permission to use, copy, modify, and distribute this software in object code form
# for any purpose and without fee is hereby granted, provided that the above copyright
# notice appears in all copies and that both that copyright notice and the limited
# warranty and restricted rights notice below appear in all supporting documentation.
#
# AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. AUTODESK SPECIFICALLY
# DISCLAIMS ANY IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE.
# AUTODESK, INC. DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
# UNINTERRUPTED OR ERROR FREE.
import os
import traceback
import adsk.core
app = adsk.core.Application.get()
ui = app.userInterface
# Attempt to read DEBUG flag from parent config.
try:
from ... import config
DEBUG = config.DEBUG
except:
DEBUG = False
def log(message: str, level: adsk.core.LogLevels = adsk.core.LogLevels.InfoLogLevel, force_console: bool = False):
"""Utility function to easily handle logging in your app.
Arguments:
message -- The message to log.
level -- The logging severity level.
force_console -- Forces the message to be written to the Text Command window.
"""
# Always print to console, only seen through IDE.
print(message)
# Log all errors to Fusion log file.
if level == adsk.core.LogLevels.ErrorLogLevel:
log_type = adsk.core.LogTypes.FileLogType
app.log(message, level, log_type)
# If config.DEBUG is True write all log messages to the console.
if DEBUG or force_console:
log_type = adsk.core.LogTypes.ConsoleLogType
app.log(message, level, log_type)
def handle_error(name: str, show_message_box: bool = False):
"""Utility function to simplify error handling.
Arguments:
name -- A name used to label the error.
show_message_box -- Indicates if the error should be shown in the message box.
If False, it will only be shown in the Text Command window
and logged to the log file.
"""
log('===== Error =====', adsk.core.LogLevels.ErrorLogLevel)
log(f'{name}\n{traceback.format_exc()}', adsk.core.LogLevels.ErrorLogLevel)
# If desired you could show an error as a message box.
if show_message_box:
ui.messageBox(f'{name}\n{traceback.format_exc()}')

1
demo/.env Normal file
View File

@@ -0,0 +1 @@
PYTHONPATH=C:/Users/bartool/AppData/Local/Autodesk/webdeploy/production/9209df45963e1599ff476303834125d21fd43de4/Api/Python/packages

20
demo/.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,20 @@
{
"version": "0.2.0",
"configurations": [{
"name": "Python: Attach",
"type": "python",
"request": "attach",
"pathMappings": [{
"localRoot": "${workspaceRoot}",
"remoteRoot": "${workspaceRoot}"
}],
"osx": {
"filePath": "${file}"
},
"windows": {
"filePath": "${file}"
},
"port": 9000,
"host": "localhost"
}]
}

6
demo/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"python.autoComplete.extraPaths": ["C:/Users/bartool/AppData/Roaming/Autodesk/Autodesk Fusion 360/API/Python/defs"],
"python.analysis.extraPaths": ["C:/Users/bartool/AppData/Roaming/Autodesk/Autodesk Fusion 360/API/Python/defs"],
"python.defaultInterpreterPath": "C:/Users/bartool/AppData/Local/Autodesk/webdeploy/production/9209df45963e1599ff476303834125d21fd43de4/Python/python.exe",
"python.pythonPath": "C:/Users/bartool/AppData/Local/Autodesk/webdeploy/production/9209df45963e1599ff476303834125d21fd43de4/Python/python.exe"
}

10
demo/demo.manifest Normal file
View File

@@ -0,0 +1,10 @@
{
"autodeskProduct": "Fusion360",
"type": "script",
"author": "",
"description": {
"": ""
},
"supportedOS": "windows|mac",
"editEnabled": true
}

58
demo/demo.py Normal file
View File

@@ -0,0 +1,58 @@
# Fusion360API Python script
import adsk.core, adsk.fusion, adsk.cam, traceback
import math, random
# Number of divisions during one revolution
COUNT = 60
def run(context):
ui = None
try:
app: adsk.core.Application = adsk.core.Application.get()
ui = app.userInterface
# get state
vi :adsk.core.Viewport = app.activeViewport
camera :adsk.core.Camera = vi.camera #This is a deep copy.
eye :adsk.core.Point3D = camera.eye
tgt :adsk.core.Point3D = camera.target
up :adsk.core.Vector3D = camera.upVector
baseArea = camera.viewExtents
# get matrix
front :adsk.core.Vector3D = eye.vectorTo(tgt)
right :adsk.core.Vector3D = up.copy()
right = right.crossProduct(front)
mat :adsk.core.Matrix3D = adsk.core.Matrix3D.create()
mat.setWithCoordinateSystem(tgt, right, front, up)
# get position & ratio
unit = math.radians(360 // COUNT)
rads = [unit] * COUNT
ratios = [random.uniform(0.1, 5.0) for _ in range(COUNT)]
if 360 % COUNT != 0:
rads.append(math.radians(360 % COUNT))
ratios.append(1)
# show
camera.isSmoothTransition = False
for rad, ratio in zip(rads, ratios):
mat.setToRotation(rad, up, tgt)
eye.transformBy(mat)
up.transformBy(mat)
mat.setToRotation(rad, right, tgt)
eye.transformBy(mat)
up.transformBy(mat)
# mat.setToRotation(rad, front, tgt)
camera.eye = eye
camera.upVector = up
# zoom = baseArea * ratio
# camera.viewExtents = zoom
vi.camera = camera
vi.refresh()
adsk.doEvents()
except:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

1
eyeCameraTest/.env Normal file
View File

@@ -0,0 +1 @@
PYTHONPATH=C:/Users/bartool/AppData/Local/Autodesk/webdeploy/production/9209df45963e1599ff476303834125d21fd43de4/Api/Python/packages

20
eyeCameraTest/.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,20 @@
{
"version": "0.2.0",
"configurations": [{
"name": "Python: Attach",
"type": "python",
"request": "attach",
"pathMappings": [{
"localRoot": "${workspaceRoot}",
"remoteRoot": "${workspaceRoot}"
}],
"osx": {
"filePath": "${file}"
},
"windows": {
"filePath": "${file}"
},
"port": 9000,
"host": "localhost"
}]
}

5
eyeCameraTest/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,5 @@
{
"python.autoComplete.extraPaths": ["C:/Users/bartool/AppData/Roaming/Autodesk/Autodesk Fusion 360/API/Python/defs"],
"python.analysis.extraPaths": ["C:/Users/bartool/AppData/Roaming/Autodesk/Autodesk Fusion 360/API/Python/defs"],
"python.pythonPath": "C:/Users/bartool/AppData/Local/Autodesk/webdeploy/production/9209df45963e1599ff476303834125d21fd43de4/Python/python.exe"
}

View File

@@ -0,0 +1,10 @@
{
"autodeskProduct": "Fusion360",
"type": "script",
"author": "",
"description": {
"": ""
},
"supportedOS": "windows|mac",
"editEnabled": true
}

View File

@@ -0,0 +1,27 @@
# Author-
# Description-
import adsk.core
import adsk.fusion
import adsk.cam
import traceback
def run(context):
try:
app = adsk.core.Application.get()
ui = app.userInterface
camera = app.activeViewport.camera
eye = camera.eye
target = camera.target
upvector = camera.upVector
baseExtent = camera.viewExtents
ui.messageBox('Eye: {0}, {1}, {2} \ntarget: {3}, {4}, {5} \nupvector: {6}, {7}, {8}, \nviewExtants: {9}'.format(
eye.x, eye.y, eye.z, target.x, target.y, target.z, upvector.x, upvector.y, upvector.z, baseExtent))
except:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

1
rotate_Test/.env Normal file
View File

@@ -0,0 +1 @@
PYTHONPATH=C:/Users/bartool/AppData/Local/Autodesk/webdeploy/production/9209df45963e1599ff476303834125d21fd43de4/Api/Python/packages

20
rotate_Test/.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,20 @@
{
"version": "0.2.0",
"configurations": [{
"name": "Python: Attach",
"type": "python",
"request": "attach",
"pathMappings": [{
"localRoot": "${workspaceRoot}",
"remoteRoot": "${workspaceRoot}"
}],
"osx": {
"filePath": "${file}"
},
"windows": {
"filePath": "${file}"
},
"port": 9000,
"host": "localhost"
}]
}

5
rotate_Test/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,5 @@
{
"python.autoComplete.extraPaths": ["C:/Users/bartool/AppData/Roaming/Autodesk/Autodesk Fusion 360/API/Python/defs"],
"python.analysis.extraPaths": ["C:/Users/bartool/AppData/Roaming/Autodesk/Autodesk Fusion 360/API/Python/defs"],
"python.pythonPath": "C:/Users/bartool/AppData/Local/Autodesk/webdeploy/production/9209df45963e1599ff476303834125d21fd43de4/Python/python.exe"
}

View File

@@ -0,0 +1,10 @@
{
"autodeskProduct": "Fusion360",
"type": "script",
"author": "",
"description": {
"": ""
},
"supportedOS": "windows|mac",
"editEnabled": true
}

View File

@@ -0,0 +1,58 @@
# Author-
# Description-
import math
import adsk.core
import adsk.fusion
import adsk.cam
import traceback
def run(context):
ui = None
try:
print('hello, world')
app = adsk.core.Application.get()
move_camera(app, app.activeViewport)
ui = app.userInterface
# ui.messageBox('Hello script')
except:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def move_camera(app, view):
try:
camera = view.camera
# target = adsk.core.Point3D.create(0, 0, 0)
up = adsk.core.Vector3D.create(0, 0, 1)
steps = 1000
eye = camera.eye
dist = camera.target.distanceTo(eye)
x_eye = eye.x
y_eye = eye.y
for i in range(0, steps):
x = dist * math.cos((math.pi*2) * (i/steps)) + x_eye
y = dist * math.sin((math.pi*2) * (i/steps)) + y_eye
z = eye.z
eye = adsk.core.Point3D.create(x, y, z)
camera.eye = eye
# camera.target = target
# camera.upVector = up
# camera.isSmoothTransition = False
view.camera = camera
adsk.doEvents()
view.refresh()
except:
ui = app.userInterface
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))

1
tamplate/.env Normal file
View File

@@ -0,0 +1 @@
PYTHONPATH=C:/Users/bartool/AppData/Local/Autodesk/webdeploy/production/9209df45963e1599ff476303834125d21fd43de4/Api/Python/packages

20
tamplate/.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,20 @@
{
"version": "0.2.0",
"configurations": [{
"name": "Python: Attach",
"type": "python",
"request": "attach",
"pathMappings": [{
"localRoot": "${workspaceRoot}",
"remoteRoot": "${workspaceRoot}"
}],
"osx": {
"filePath": "${file}"
},
"windows": {
"filePath": "${file}"
},
"port": 9000,
"host": "localhost"
}]
}

5
tamplate/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,5 @@
{
"python.autoComplete.extraPaths": ["C:/Users/bartool/AppData/Roaming/Autodesk/Autodesk Fusion 360/API/Python/defs"],
"python.analysis.extraPaths": ["C:/Users/bartool/AppData/Roaming/Autodesk/Autodesk Fusion 360/API/Python/defs"],
"python.pythonPath": "C:/Users/bartool/AppData/Local/Autodesk/webdeploy/production/9209df45963e1599ff476303834125d21fd43de4/Python/python.exe"
}

View File

@@ -0,0 +1,30 @@
# Here you define the commands that will be added to your add-in.
# TODO Import the modules corresponding to the commands you created.
# If you want to add an additional command, duplicate one of the existing directories and import it here.
# You need to use aliases (import "entry" as "my_module") assuming you have the default module named "entry".
from .commandDialog import entry as commandDialog
from .paletteShow import entry as paletteShow
from .paletteSend import entry as paletteSend
# TODO add your imported modules to this list.
# Fusion will automatically call the start() and stop() functions.
commands = [
commandDialog,
paletteShow,
paletteSend
]
# Assumes you defined a "start" function in each of your modules.
# The start function will be run when the add-in is started.
def start():
for command in commands:
command.start()
# Assumes you defined a "stop" function in each of your modules.
# The stop function will be run when the add-in is stopped.
def stop():
for command in commands:
command.stop()

View File

@@ -0,0 +1,158 @@
import adsk.core
import os
from ...lib import fusion360utils as futil
from ... import config
app = adsk.core.Application.get()
ui = app.userInterface
# TODO *** Specify the command identity information. ***
CMD_ID = f'{config.COMPANY_NAME}_{config.ADDIN_NAME}_cmdDialog'
CMD_NAME = 'Command Dialog Sample'
CMD_Description = 'A Fusion 360 Add-in Command with a dialog'
# Specify that the command will be promoted to the panel.
IS_PROMOTED = True
# TODO *** Define the location where the command button will be created. ***
# This is done by specifying the workspace, the tab, and the panel, and the
# command it will be inserted beside. Not providing the command to position it
# will insert it at the end.
WORKSPACE_ID = 'FusionSolidEnvironment'
PANEL_ID = 'SolidScriptsAddinsPanel'
COMMAND_BESIDE_ID = 'ScriptsManagerCommand'
# Resource location for command icons, here we assume a sub folder in this directory named "resources".
ICON_FOLDER = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'resources', '')
# Local list of event handlers used to maintain a reference so
# they are not released and garbage collected.
local_handlers = []
# Executed when add-in is run.
def start():
# Create a command Definition.
cmd_def = ui.commandDefinitions.addButtonDefinition(CMD_ID, CMD_NAME, CMD_Description, ICON_FOLDER)
# Define an event handler for the command created event. It will be called when the button is clicked.
futil.add_handler(cmd_def.commandCreated, command_created)
# ******** Add a button into the UI so the user can run the command. ********
# Get the target workspace the button will be created in.
workspace = ui.workspaces.itemById(WORKSPACE_ID)
# Get the panel the button will be created in.
panel = workspace.toolbarPanels.itemById(PANEL_ID)
# Create the button command control in the UI after the specified existing command.
control = panel.controls.addCommand(cmd_def, COMMAND_BESIDE_ID, False)
# Specify if the command is promoted to the main toolbar.
control.isPromoted = IS_PROMOTED
# Executed when add-in is stopped.
def stop():
# Get the various UI elements for this command
workspace = ui.workspaces.itemById(WORKSPACE_ID)
panel = workspace.toolbarPanels.itemById(PANEL_ID)
command_control = panel.controls.itemById(CMD_ID)
command_definition = ui.commandDefinitions.itemById(CMD_ID)
# Delete the button command control
if command_control:
command_control.deleteMe()
# Delete the command definition
if command_definition:
command_definition.deleteMe()
# Function that is called when a user clicks the corresponding button in the UI.
# This defines the contents of the command dialog and connects to the command related events.
def command_created(args: adsk.core.CommandCreatedEventArgs):
# General logging for debug.
futil.log(f'{CMD_NAME} Command Created Event')
# https://help.autodesk.com/view/fusion360/ENU/?contextId=CommandInputs
inputs = args.command.commandInputs
# TODO Define the dialog for your command by adding different inputs to the command.
# Create a simple text box input.
inputs.addTextBoxCommandInput('text_box', 'Some Text', 'Enter some text.', 1, False)
# Create a value input field and set the default using 1 unit of the default length unit.
defaultLengthUnits = app.activeProduct.unitsManager.defaultLengthUnits
default_value = adsk.core.ValueInput.createByString('1')
inputs.addValueInput('value_input', 'Some Value', defaultLengthUnits, default_value)
# TODO Connect to the events that are needed by this command.
futil.add_handler(args.command.execute, command_execute, local_handlers=local_handlers)
futil.add_handler(args.command.inputChanged, command_input_changed, local_handlers=local_handlers)
futil.add_handler(args.command.executePreview, command_preview, local_handlers=local_handlers)
futil.add_handler(args.command.validateInputs, command_validate_input, local_handlers=local_handlers)
futil.add_handler(args.command.destroy, command_destroy, local_handlers=local_handlers)
# This event handler is called when the user clicks the OK button in the command dialog or
# is immediately called after the created event not command inputs were created for the dialog.
def command_execute(args: adsk.core.CommandEventArgs):
# General logging for debug.
futil.log(f'{CMD_NAME} Command Execute Event')
# TODO ******************************** Your code here ********************************
# Get a reference to your command's inputs.
inputs = args.command.commandInputs
text_box: adsk.core.TextBoxCommandInput = inputs.itemById('text_box')
value_input: adsk.core.ValueCommandInput = inputs.itemById('value_input')
# Do something interesting
text = text_box.text
expression = value_input.expression
msg = f'Your text: {text}<br>Your value: {expression}'
ui.messageBox(msg)
# This event handler is called when the command needs to compute a new preview in the graphics window.
def command_preview(args: adsk.core.CommandEventArgs):
# General logging for debug.
futil.log(f'{CMD_NAME} Command Preview Event')
inputs = args.command.commandInputs
# This event handler is called when the user changes anything in the command dialog
# allowing you to modify values of other inputs based on that change.
def command_input_changed(args: adsk.core.InputChangedEventArgs):
changed_input = args.input
inputs = args.inputs
# General logging for debug.
futil.log(f'{CMD_NAME} Input Changed Event fired from a change to {changed_input.id}')
# This event handler is called when the user interacts with any of the inputs in the dialog
# which allows you to verify that all of the inputs are valid and enables the OK button.
def command_validate_input(args: adsk.core.ValidateInputsEventArgs):
# General logging for debug.
futil.log(f'{CMD_NAME} Validate Input Event')
inputs = args.inputs
# Verify the validity of the input values. This controls if the OK button is enabled or not.
valueInput = inputs.itemById('value_input')
if valueInput.value >= 0:
args.areInputsValid = True
else:
args.areInputsValid = False
# This event handler is called when the command terminates.
def command_destroy(args: adsk.core.CommandEventArgs):
# General logging for debug.
futil.log(f'{CMD_NAME} Command Destroy Event')
global local_handlers
local_handlers = []

Binary file not shown.

After

Width:  |  Height:  |  Size: 415 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 701 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,149 @@
import json
import adsk.core
import os
from ...lib import fusion360utils as futil
from ... import config
app = adsk.core.Application.get()
ui = app.userInterface
# TODO ********************* Change these names *********************
CMD_ID = f'{config.COMPANY_NAME}_{config.ADDIN_NAME}_palette_send'
CMD_NAME = 'Send to Palette'
CMD_Description = 'Send some information to the palette'
IS_PROMOTED = False
# Using "global" variables by referencing values from /config.py
PALETTE_ID = config.sample_palette_id
# TODO *** Define the location where the command button will be created. ***
# This is done by specifying the workspace, the tab, and the panel, and the
# command it will be inserted beside. Not providing the command to position it
# will insert it at the end.
WORKSPACE_ID = 'FusionSolidEnvironment'
PANEL_ID = 'SolidScriptsAddinsPanel'
COMMAND_BESIDE_ID = 'ScriptsManagerCommand'
# Resource location for command icons, here we assume a sub folder in this directory named "resources".
ICON_FOLDER = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'resources', '')
# Local list of event handlers used to maintain a reference so
# they are not released and garbage collected.
local_handlers = []
# Executed when add-in is run.
def start():
# Create a command Definition.
cmd_def = ui.commandDefinitions.addButtonDefinition(CMD_ID, CMD_NAME, CMD_Description, ICON_FOLDER)
# Add command created handler. The function passed here will be executed when the command is executed.
futil.add_handler(cmd_def.commandCreated, command_created)
# ******** Add a button into the UI so the user can run the command. ********
# Get the target workspace the button will be created in.
workspace = ui.workspaces.itemById(WORKSPACE_ID)
# Get the panel the button will be created in.
panel = workspace.toolbarPanels.itemById(PANEL_ID)
# Create the button command control in the UI after the specified existing command.
control = panel.controls.addCommand(cmd_def, COMMAND_BESIDE_ID, False)
# Specify if the command is promoted to the main toolbar.
control.isPromoted = IS_PROMOTED
# Executed when add-in is stopped.
def stop():
# Get the various UI elements for this command
workspace = ui.workspaces.itemById(WORKSPACE_ID)
panel = workspace.toolbarPanels.itemById(PANEL_ID)
command_control = panel.controls.itemById(CMD_ID)
command_definition = ui.commandDefinitions.itemById(CMD_ID)
# Delete the button command control
if command_control:
command_control.deleteMe()
# Delete the command definition
if command_definition:
command_definition.deleteMe()
# Event handler that is called when the user clicks the command button in the UI.
# To have a dialog, you create the desired command inputs here. If you don't need
# a dialog, don't create any inputs and the execute event will be immediately fired.
# You also need to connect to any command related events here.
def command_created(args: adsk.core.CommandCreatedEventArgs):
# General logging for debug.
futil.log(f'{CMD_NAME} Command Created Event')
# TODO Create the event handlers you will need for this instance of the command
futil.add_handler(args.command.execute, command_execute, local_handlers=local_handlers)
futil.add_handler(args.command.inputChanged, command_input_changed, local_handlers=local_handlers)
futil.add_handler(args.command.executePreview, command_preview, local_handlers=local_handlers)
futil.add_handler(args.command.destroy, command_destroy, local_handlers=local_handlers)
# Create the user interface for your command by adding different inputs to the CommandInputs object
# https://help.autodesk.com/view/fusion360/ENU/?contextId=CommandInputs
inputs = args.command.commandInputs
# TODO ******************************** Define your UI Here ********************************
# Simple text input box
inputs.addTextBoxCommandInput('text_input', 'Text Message', 'Enter some text', 1, False)
# To create a numerical input with units, we need to get the current units and create a "ValueInput"
# https://help.autodesk.com/view/fusion360/ENU/?contextId=ValueInput
users_current_units = app.activeProduct.unitsManager.defaultLengthUnits
default_value = adsk.core.ValueInput.createByString(f'1 {users_current_units}')
inputs.addValueInput('value_input', 'Value Message', users_current_units, default_value)
# This function will be called when the user hits the OK button in the command dialog
def command_execute(args: adsk.core.CommandEventArgs):
# General logging for debug
futil.log(f'{CMD_NAME} Command Execute Event')
inputs = args.command.commandInputs
# TODO ******************************** Your code here ********************************
# Get a reference to your command's inputs
text_input: adsk.core.TextBoxCommandInput = inputs.itemById('text_input')
value_input: adsk.core.ValueCommandInput = inputs.itemById('value_input')
# Construct a message
message_action = 'updateMessage'
message_data = {
'myValue': f'{value_input.value} cm',
'myExpression': value_input.expression,
'myText': text_input.formattedText
}
# JSON strings are a useful way to translate between javascript objects and python dictionaries
message_json = json.dumps(message_data)
# Get a reference to the palette and send the message to the palette javascript
palette = ui.palettes.itemById(PALETTE_ID)
palette.sendInfoToHTML(message_action, message_json)
# This function will be called when the command needs to compute a new preview in the graphics window
def command_preview(args: adsk.core.CommandEventArgs):
inputs = args.command.commandInputs
futil.log(f'{CMD_NAME} Command Preview Event')
# This function will be called when the user changes anything in the command dialog
def command_input_changed(args: adsk.core.InputChangedEventArgs):
changed_input = args.input
inputs = args.inputs
futil.log(f'{CMD_NAME} Input Changed Event fired from a change to {changed_input.id}')
# This event handler is called when the command terminates.
def command_destroy(args: adsk.core.CommandEventArgs):
global local_handlers
local_handlers = []
futil.log(f'{CMD_NAME} Command Destroy Event')

Binary file not shown.

After

Width:  |  Height:  |  Size: 474 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,193 @@
import json
import adsk.core
import os
from ...lib import fusion360utils as futil
from ... import config
from datetime import datetime
app = adsk.core.Application.get()
ui = app.userInterface
# TODO ********************* Change these names *********************
CMD_ID = f'{config.COMPANY_NAME}_{config.ADDIN_NAME}_PalleteShow'
CMD_NAME = 'Show My Palette'
CMD_Description = 'A Fusion 360 Add-in Palette'
PALETTE_NAME = 'My Palette Sample'
IS_PROMOTED = False
# Using "global" variables by referencing values from /config.py
PALETTE_ID = config.sample_palette_id
# Specify the full path to the local html. You can also use a web URL
# such as 'https://www.autodesk.com/'
PALETTE_URL = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'resources', 'html', 'index.html')
# The path function builds a valid OS path. This fixes it to be a valid local URL.
PALETTE_URL = PALETTE_URL.replace('\\', '/')
# Set a default docking behavior for the palette
PALETTE_DOCKING = adsk.core.PaletteDockingStates.PaletteDockStateRight
# TODO *** Define the location where the command button will be created. ***
# This is done by specifying the workspace, the tab, and the panel, and the
# command it will be inserted beside. Not providing the command to position it
# will insert it at the end.
WORKSPACE_ID = 'FusionSolidEnvironment'
PANEL_ID = 'SolidScriptsAddinsPanel'
COMMAND_BESIDE_ID = 'ScriptsManagerCommand'
# Resource location for command icons, here we assume a sub folder in this directory named "resources".
ICON_FOLDER = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'resources', '')
# Local list of event handlers used to maintain a reference so
# they are not released and garbage collected.
local_handlers = []
# Executed when add-in is run.
def start():
# Create a command Definition.
cmd_def = ui.commandDefinitions.addButtonDefinition(CMD_ID, CMD_NAME, CMD_Description, ICON_FOLDER)
# Add command created handler. The function passed here will be executed when the command is executed.
futil.add_handler(cmd_def.commandCreated, command_created)
# ******** Add a button into the UI so the user can run the command. ********
# Get the target workspace the button will be created in.
workspace = ui.workspaces.itemById(WORKSPACE_ID)
# Get the panel the button will be created in.
panel = workspace.toolbarPanels.itemById(PANEL_ID)
# Create the button command control in the UI after the specified existing command.
control = panel.controls.addCommand(cmd_def, COMMAND_BESIDE_ID, False)
# Specify if the command is promoted to the main toolbar.
control.isPromoted = IS_PROMOTED
# Executed when add-in is stopped.
def stop():
# Get the various UI elements for this command
workspace = ui.workspaces.itemById(WORKSPACE_ID)
panel = workspace.toolbarPanels.itemById(PANEL_ID)
command_control = panel.controls.itemById(CMD_ID)
command_definition = ui.commandDefinitions.itemById(CMD_ID)
palette = ui.palettes.itemById(PALETTE_ID)
# Delete the button command control
if command_control:
command_control.deleteMe()
# Delete the command definition
if command_definition:
command_definition.deleteMe()
# Delete the Palette
if palette:
palette.deleteMe()
# Event handler that is called when the user clicks the command button in the UI.
# To have a dialog, you create the desired command inputs here. If you don't need
# a dialog, don't create any inputs and the execute event will be immediately fired.
# You also need to connect to any command related events here.
def command_created(args: adsk.core.CommandCreatedEventArgs):
# General logging for debug.
futil.log(f'{CMD_NAME}: Command created event.')
# Create the event handlers you will need for this instance of the command
futil.add_handler(args.command.execute, command_execute, local_handlers=local_handlers)
futil.add_handler(args.command.destroy, command_destroy, local_handlers=local_handlers)
# Because no command inputs are being added in the command created event, the execute
# event is immediately fired.
def command_execute(args: adsk.core.CommandEventArgs):
# General logging for debug.
futil.log(f'{CMD_NAME}: Command execute event.')
palettes = ui.palettes
palette = palettes.itemById(PALETTE_ID)
if palette is None:
palette = palettes.add(
id=PALETTE_ID,
name=PALETTE_NAME,
htmlFileURL=PALETTE_URL,
isVisible=True,
showCloseButton=True,
isResizable=True,
width=650,
height=600,
useNewWebBrowser=True
)
futil.add_handler(palette.closed, palette_closed)
futil.add_handler(palette.navigatingURL, palette_navigating)
futil.add_handler(palette.incomingFromHTML, palette_incoming)
futil.log(f'{CMD_NAME}: Created a new palette: ID = {palette.id}, Name = {palette.name}')
if palette.dockingState == adsk.core.PaletteDockingStates.PaletteDockStateFloating:
palette.dockingState = PALETTE_DOCKING
palette.isVisible = True
# Use this to handle a user closing your palette.
def palette_closed(args: adsk.core.UserInterfaceGeneralEventArgs):
# General logging for debug.
futil.log(f'{CMD_NAME}: Palette was closed.')
# Use this to handle a user navigating to a new page in your palette.
def palette_navigating(args: adsk.core.NavigationEventArgs):
# General logging for debug.
futil.log(f'{CMD_NAME}: Palette navigating event.')
# Get the URL the user is navigating to:
url = args.navigationURL
log_msg = f"User is attempting to navigate to {url}\n"
futil.log(log_msg, adsk.core.LogLevels.InfoLogLevel)
# Check if url is an external site and open in user's default browser.
if url.startswith("http"):
args.launchExternally = True
# Use this to handle events sent from javascript in your palette.
def palette_incoming(html_args: adsk.core.HTMLEventArgs):
# General logging for debug.
futil.log(f'{CMD_NAME}: Palette incoming event.')
message_data: dict = json.loads(html_args.data)
message_action = html_args.action
log_msg = f"Event received from {html_args.firingEvent.sender.name}\n"
log_msg += f"Action: {message_action}\n"
log_msg += f"Data: {message_data}"
futil.log(log_msg, adsk.core.LogLevels.InfoLogLevel)
# TODO ******** Your palette reaction code here ********
# Read message sent from palette javascript and react appropriately.
if message_action == 'messageFromPalette':
arg1 = message_data.get('arg1', 'arg1 not sent')
arg2 = message_data.get('arg2', 'arg2 not sent')
msg = 'An event has been fired from the html to Fusion with the following data:<br/>'
msg += f'<b>Action</b>: {message_action}<br/><b>arg1</b>: {arg1}<br/><b>arg2</b>: {arg2}'
ui.messageBox(msg)
# Return value.
now = datetime.now()
currentTime = now.strftime('%H:%M:%S')
html_args.returnData = f'OK - {currentTime}'
# This event handler is called when the command terminates.
def command_destroy(args: adsk.core.CommandEventArgs):
# General logging for debug.
futil.log(f'{CMD_NAME}: Command destroy event.')
global local_handlers
local_handlers = []

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 587 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="static/palette.js"></script>
</head>
<body>
<div>
<h1>Fusion 360 Palette Sample</h1>
<div>
<a href="https://help.autodesk.com/view/fusion360/ENU/?guid=GUID-6C0C8148-98D0-4DBC-A4EC-D8E03A8A3B5B">
Learn more about working with Palettes in Fusion 360
</a>
</div>
<br><hr>
<h3>Send Data to HTML Event Handler</h3>
<div style='margin-left: 30px;'>
<label for="sampleData"><b>Data to send:</b></label>
<input type="text" id="sampleData" value="Enter Some Text"><br/><br/>
<button type='button' onclick='sendInfoToFusion()' style='background-color: #cccccc; padding: 5px'>
<b>Send HTML Event</b>
</button>
</div>
<h3>HTML Event Response Value:</h3>
<div id='returnValue' style='margin-left: 30px;'>Response</div>
<h3>Message from "Send to Palette" Command</h3>
<div style='margin-left: 30px;'>
<p id='fusionMessage'>Message from Fusion</p>
<br/><br/>
</div>
</div>
</body>
</html>

View File

@@ -0,0 +1,48 @@
function getDateString() {
const today = new Date();
const date = `${today.getDate()}/${today.getMonth() + 1}/${today.getFullYear()}`;
const time = `${today.getHours()}:${today.getMinutes()}:${today.getSeconds()}`;
return `Date: ${date}, Time: ${time}`;
}
function sendInfoToFusion() {
const args = {
arg1: document.getElementById("sampleData").value,
arg2: getDateString()
};
// Send the data to Fusion as a JSON string. The return value is a Promise.
adsk.fusionSendData("messageFromPalette", JSON.stringify(args)).then((result) =>
document.getElementById("returnValue").innerHTML = `${result}`
);
}
function updateMessage(messageString) {
// Message is sent from the add-in as a JSON string.
const messageData = JSON.parse(messageString);
// Update a paragraph with the data passed in.
document.getElementById("fusionMessage").innerHTML =
`<b>Your text</b>: ${messageData.myText} <br/>` +
`<b>Your expression</b>: ${messageData.myExpression} <br/>` +
`<b>Your value</b>: ${messageData.myValue}`;
}
window.fusionJavaScriptHandler = {
handle: function (action, data) {
try {
if (action === "updateMessage") {
updateMessage(data);
} else if (action === "debugger") {
debugger;
} else {
return `Unexpected command type: ${action}`;
}
} catch (e) {
console.log(e);
console.log(`Exception caught with command: ${action}, data: ${data}`);
}
return "OK";
},
};

21
tamplate/config.py Normal file
View File

@@ -0,0 +1,21 @@
# Application Global Variables
# This module serves as a way to share variables across different
# modules (global variables).
import os
# Flag that indicates to run in Debug mode or not. When running in Debug mode
# more information is written to the Text Command window. Generally, it's useful
# to set this to True while developing an add-in and set it to False when you
# are ready to distribute it.
DEBUG = True
# Gets the name of the add-in from the name of the folder the py file is in.
# This is used when defining unique internal names for various UI elements
# that need a unique name. It's also recommended to use a company name as
# part of the ID to better ensure the ID is unique.
ADDIN_NAME = os.path.basename(os.path.dirname(__file__))
COMPANY_NAME = 'ACME'
# Palettes
sample_palette_id = f'{COMPANY_NAME}_{ADDIN_NAME}_palette_id'

View File

@@ -0,0 +1,2 @@
from .general_utils import *
from .event_utils import *

View File

@@ -0,0 +1,88 @@
# Copyright 2022 by Autodesk, Inc.
# Permission to use, copy, modify, and distribute this software in object code form
# for any purpose and without fee is hereby granted, provided that the above copyright
# notice appears in all copies and that both that copyright notice and the limited
# warranty and restricted rights notice below appear in all supporting documentation.
#
# AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. AUTODESK SPECIFICALLY
# DISCLAIMS ANY IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE.
# AUTODESK, INC. DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
# UNINTERRUPTED OR ERROR FREE.
import sys
from typing import Callable
import adsk.core
from .general_utils import handle_error
# Global Variable to hold Event Handlers
_handlers = []
def add_handler(
event: adsk.core.Event,
callback: Callable,
*,
name: str = None,
local_handlers: list = None
):
"""Adds an event handler to the specified event.
Arguments:
event -- The event object you want to connect a handler to.
callback -- The function that will handle the event.
name -- A name to use in logging errors associated with this event.
Otherwise the name of the event object is used. This argument
must be specified by its keyword.
local_handlers -- A list of handlers you manage that is used to maintain
a reference to the handlers so they aren't released.
This argument must be specified by its keyword. If not
specified the handler is added to a global list and can
be cleared using the clear_handlers function. You may want
to maintain your own handler list so it can be managed
independently for each command.
:returns:
The event handler that was created. You don't often need this reference, but it can be useful in some cases.
"""
module = sys.modules[event.__module__]
handler_type = module.__dict__[event.add.__annotations__['handler']]
handler = _create_handler(handler_type, callback, event, name, local_handlers)
event.add(handler)
return handler
def clear_handlers():
"""Clears the global list of handlers.
"""
global _handlers
_handlers = []
def _create_handler(
handler_type,
callback: Callable,
event: adsk.core.Event,
name: str = None,
local_handlers: list = None
):
handler = _define_handler(handler_type, callback, name)()
(local_handlers if local_handlers is not None else _handlers).append(handler)
return handler
def _define_handler(handler_type, callback, name: str = None):
name = name or handler_type.__name__
class Handler(handler_type):
def __init__(self):
super().__init__()
def notify(self, args):
try:
callback(args)
except:
handle_error(name)
return Handler

View File

@@ -0,0 +1,64 @@
# Copyright 2022 by Autodesk, Inc.
# Permission to use, copy, modify, and distribute this software in object code form
# for any purpose and without fee is hereby granted, provided that the above copyright
# notice appears in all copies and that both that copyright notice and the limited
# warranty and restricted rights notice below appear in all supporting documentation.
#
# AUTODESK PROVIDES THIS PROGRAM "AS IS" AND WITH ALL FAULTS. AUTODESK SPECIFICALLY
# DISCLAIMS ANY IMPLIED WARRANTY OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR USE.
# AUTODESK, INC. DOES NOT WARRANT THAT THE OPERATION OF THE PROGRAM WILL BE
# UNINTERRUPTED OR ERROR FREE.
import os
import traceback
import adsk.core
app = adsk.core.Application.get()
ui = app.userInterface
# Attempt to read DEBUG flag from parent config.
try:
from ... import config
DEBUG = config.DEBUG
except:
DEBUG = False
def log(message: str, level: adsk.core.LogLevels = adsk.core.LogLevels.InfoLogLevel, force_console: bool = False):
"""Utility function to easily handle logging in your app.
Arguments:
message -- The message to log.
level -- The logging severity level.
force_console -- Forces the message to be written to the Text Command window.
"""
# Always print to console, only seen through IDE.
print(message)
# Log all errors to Fusion log file.
if level == adsk.core.LogLevels.ErrorLogLevel:
log_type = adsk.core.LogTypes.FileLogType
app.log(message, level, log_type)
# If config.DEBUG is True write all log messages to the console.
if DEBUG or force_console:
log_type = adsk.core.LogTypes.ConsoleLogType
app.log(message, level, log_type)
def handle_error(name: str, show_message_box: bool = False):
"""Utility function to simplify error handling.
Arguments:
name -- A name used to label the error.
show_message_box -- Indicates if the error should be shown in the message box.
If False, it will only be shown in the Text Command window
and logged to the log file.
"""
log('===== Error =====', adsk.core.LogLevels.ErrorLogLevel)
log(f'{name}\n{traceback.format_exc()}', adsk.core.LogLevels.ErrorLogLevel)
# If desired you could show an error as a message box.
if show_message_box:
ui.messageBox(f'{name}\n{traceback.format_exc()}')

View File

@@ -0,0 +1,13 @@
{
"autodeskProduct": "Fusion360",
"type": "addin",
"id": "f1a3be42-3a4c-4ea3-9b2d-86ad07c09291",
"author": "",
"description": {
"": ""
},
"version": "",
"runOnStartup": false,
"supportedOS": "windows|mac",
"editEnabled": true
}

24
tamplate/tamplate.py Normal file
View File

@@ -0,0 +1,24 @@
# Assuming you have not changed the general structure of the template no modification is needed in this file.
from . import commands
from .lib import fusion360utils as futil
def run(context):
try:
# This will run the start function in each of your commands as defined in commands/__init__.py
commands.start()
except:
futil.handle_error('run')
def stop(context):
try:
# Remove all of the event handlers your app has created
futil.clear_handlers()
# This will run the start function in each of your commands as defined in commands/__init__.py
commands.stop()
except:
futil.handle_error('stop')

1
zoom_test/.env Normal file
View File

@@ -0,0 +1 @@
PYTHONPATH=C:/Users/bartool/AppData/Local/Autodesk/webdeploy/production/9209df45963e1599ff476303834125d21fd43de4/Api/Python/packages

20
zoom_test/.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,20 @@
{
"version": "0.2.0",
"configurations": [{
"name": "Python: Attach",
"type": "python",
"request": "attach",
"pathMappings": [{
"localRoot": "${workspaceRoot}",
"remoteRoot": "${workspaceRoot}"
}],
"osx": {
"filePath": "${file}"
},
"windows": {
"filePath": "${file}"
},
"port": 9000,
"host": "localhost"
}]
}

5
zoom_test/.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,5 @@
{
"python.autoComplete.extraPaths": ["C:/Users/bartool/AppData/Roaming/Autodesk/Autodesk Fusion 360/API/Python/defs"],
"python.analysis.extraPaths": ["C:/Users/bartool/AppData/Roaming/Autodesk/Autodesk Fusion 360/API/Python/defs"],
"python.pythonPath": "C:/Users/bartool/AppData/Local/Autodesk/webdeploy/production/9209df45963e1599ff476303834125d21fd43de4/Python/python.exe"
}

View File

@@ -0,0 +1,10 @@
{
"autodeskProduct": "Fusion360",
"type": "script",
"author": "",
"description": {
"": ""
},
"supportedOS": "windows|mac",
"editEnabled": true
}

65
zoom_test/zoom_test.py Normal file
View File

@@ -0,0 +1,65 @@
# Author-
# Description-
import adsk.core
import adsk.fusion
import adsk.cam
import traceback
def run(context):
ui = None
try:
app = adsk.core.Application.get()
ui = app.userInterface
zoom_camera(app, app.activeViewport)
# ui.messageBox('Hello script')
except:
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def zoom_camera(app: adsk.core.Application, view: adsk.core.Viewport):
try:
camera = view.camera
eye = camera.eye
target = camera.target
eye_x = eye.x
eye_y = eye.y
eye_z = eye.z
tgt_x = target.x
tgt_y = target.y
tgt_z = target.z
eye_tgt_vector = eye.vectorTo(target)
eye_tgt_vector.scaleBy(0.5)
new_x = eye_tgt_vector.asPoint().x
new_y = eye_tgt_vector.asPoint().y
new_z = eye_tgt_vector.asPoint().z
# eye.set(eye_x - new_x, eye_y - new_y, eye_z - new_z)
eye = adsk.core.Point3D.create(
eye_x - new_x, eye_y - new_y, eye_z - new_z)
# eye.translateBy(eye_tgt_vector)
eye = adsk.core.Point3D.create(100, 100, 100)
camera.eye = eye
fit = camera.isFitView
base = camera.viewExtents
base = base*2
camera.viewExtents = base
view.camera = camera
view.refresh()
ui = app.userInterface
ui.messageBox('Eye: {0}, {1}, {2} \ntarget: {3}, {4}, {5}'.format(
eye.x, eye.y, eye.z, target.x, target.y, target.z))
except:
ui = app.userInterface
if ui:
ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))