Fusion ToolbarPanel potential persistence Bug

Fusion ToolbarPanel potential persistence Bug

zxynine
Enthusiast Enthusiast
531 Views
2 Replies
Message 1 of 3

Fusion ToolbarPanel potential persistence Bug

zxynine
Enthusiast
Enthusiast

I made a post a while ago about an odd feature of toolbar panels I had noticed (link). The thread got pretty full and I found that it may be a bug in fusion, so I am creating this post and will be closing the other. The problem comes from creating and removing toolbarPanels. If I add a panel to one toolbarTab, then remove it, then add one with the same id to a separate toolbar tab, the panel will show up in both locations. I have not found a way for me to remove it completely without a full fusion restart. My script below will take you, step by step, through the problem and should demonstrate it clearly. If anyone has any idea if this is an error on my end please let me know.

import adsk.core, adsk.fusion, adsk.cam, traceback
#List for every object
createdObjects= []

#Context manager
class errorPrint:
	def __init__(self,errorReport): self.errorrep = errorReport
	def __enter__(self,*args): return self
	def __exit__(self,excType,excVal,excTB):
		if excType is not None: 
			self.errorrep.append(excType.__name__ + ": \t"+ str(excVal))
		return True

#||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
#Generic function that both gets ant creates new objects depending on if they already exist  and whether the command calling it wants to create it
#If an object is ever made, it gets appended to the global list of objects for proper deletion at the end of the commands life
def addGeneric(baseObj, cmdID, cmdFunc, *addArgs, TryAdd=True):
	global createdObjects
	generic = baseObj.itemById(cmdID)
	if generic == None and TryAdd: 
		generic = cmdFunc(*addArgs)
		createdObjects.append(generic)
	return generic	

#These funcs are focused on toolbar panels and the command buttons in them
#These will either create the obj if it does not exist, or it will return the object if already defined
def AddPanel(panels:adsk.core.ToolbarPanels, id='SolidScriptsAddinsPanel', name = 'Add-Ins', positionID='', isBefore=False):
	return addGeneric(panels, id, panels.add, id, name,positionID,isBefore)
def NewCommand(cmdDefs:adsk.core.CommandDefinitions, cmdID, cmdName, cmdToolTip='', cmdIcon=''):
	return addGeneric(cmdDefs,cmdID, cmdDefs.addButtonDefinition, cmdID,cmdName, cmdToolTip, cmdIcon)
def AddCommand(toolbarControls:adsk.core.ToolbarControls, commandDef:adsk.core.CommandDefinition, positionID='', isBefore=False):
	return addGeneric(toolbarControls, commandDef.id, toolbarControls.addCommand, commandDef, positionID, isBefore)

#These are just util funcs that get me the controls i want
def GetWorkspace(workspaces:adsk.core.Workspaces, id='FusionSolidEnvironment', name='Design', icon=''):
	return addGeneric(workspaces, id, workspaces.add, id, name,icon, TryAdd=False)
def GetTab(toolbarTabs:adsk.core.ToolbarTabs,id='ToolsTab', name = 'Tools'):
	return addGeneric(toolbarTabs, id, toolbarTabs.add, id, name, TryAdd=False)
#||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||


def run(context):
	ui = None
	try:
		#Setting up some objs
		app:adsk.core.Application= adsk.core.Application.get()
		ui  = app.userInterface
		ui.messageBox('Hello addin')
		FusionSolid = GetWorkspace(ui.workspaces)
		SolidTab = GetTab(FusionSolid.toolbarTabs, 'SolidTab')
		ToolsTab = GetTab(FusionSolid.toolbarTabs, 'ToolsTab')
		#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		#Creating the toolbar panel within the solids tab
		ExampleCommand:adsk.core.CommandDefinition = NewCommand(ui.commandDefinitions, '__A_Completly_Unique_ID__', 'PanelBugCommand', '', '/resources')
		PanelInSolidTab:adsk.core.ToolbarPanel = AddPanel(SolidTab.toolbarPanels, 'SolidPanelTest', 'Im a solid panel')
		SolidPanelCommand = AddCommand(PanelInSolidTab.controls, ExampleCommand)
		SolidTab.activate()
		ui.messageBox('The Toolbar panel should be visible')
		#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

		#Asserting that fusion says the obects have been deleted
		assert ui.commandDefinitions.itemById('__A_Completly_Unique_ID__').deleteMe()
		assert ui.allToolbarPanels.itemById('SolidPanelTest').deleteMe()

		DelConfirm = []	#Needed a mutable to recieve info back easily
		#Trying to get fusion to complain about the objs being deleted
		with errorPrint(DelConfirm): SolidPanelCommand.id
		with errorPrint(DelConfirm): PanelInSolidTab.id
		DelConfirm.append('Deleting the variables then accessing errors:')
		#This i dont think matters since the objects are stored globally anyhow but whatever
		del PanelInSolidTab
		del SolidPanelCommand
		with errorPrint(DelConfirm): SolidPanelCommand.id
		with errorPrint(DelConfirm): PanelInSolidTab.id
		ui.messageBox('The Toolbar panel and command should be be gone \n\n Errors when trying to access those variables as proof:\n'+ str('\n'.join(DelConfirm)))


		#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		ExampleCommand:adsk.core.CommandDefinition = NewCommand(ui.commandDefinitions, '__A_Completly_Unique_ID__', 'PanelBugCommand', '', '/resources')
		PanelInToolsTab:adsk.core.ToolbarPanel = AddPanel(ToolsTab.toolbarPanels, 'SolidPanelTest', 'Im a solid panel')
		ToolsPanelCommand = AddCommand(PanelInToolsTab.controls, ExampleCommand)
		ToolsTab.activate()
		ui.messageBox('The Tools tab ToolbarPanel should be visible')
		#~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
		SolidTab.activate()
		ui.messageBox('The Toolbar panel should not be in the Solids Tab')
		assert ui.commandDefinitions.itemById('__A_Completly_Unique_ID__').deleteMe()
		assert ui.allToolbarPanels.itemById('SolidPanelTest').deleteMe()
		ui.messageBox('Panel and command removed')
	except:TraceError(ui)



def stop(context):
	app:adsk.core.Application= adsk.core.Application.get()
	ui  = app.userInterface
	try: clearCreated(ui)
	except:TraceError(ui)


#||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

def TraceError(ui): ui.messageBox('Failed:\n{}'.format(traceback.format_exc()))
def clearCreated(ui):
	global createdObjects
	try:
		# ui.messageBox(str(len(createdObjects)))
		for obj in createdObjects:
			try:
				obj.deleteMe()
				ui.messageBox('Item')
			except:pass
		# one double check to make sure, more than likley fails
		assert ui.allToolbarPanels.itemById('SolidPanelTest').deleteMe()
		assert ui.commandDefinitions.itemById('__A_Completly_Unique_ID__').deleteMe()
	except: pass
	#Wipes all references to them.
	createdObjects.clear()
532 Views
2 Replies
Replies (2)
Message 2 of 3

zxynine
Enthusiast
Enthusiast

Is anyone able to confirm that the problem shows up for you as well? Im still not sure if its only for me.

0 Likes
Message 3 of 3

zxynine
Enthusiast
Enthusiast

@BrianEkins On the old post I made, you requested a test sample for the code, this post I made to report the issue since I found an easy way to reproduce the bug. Would you be able to test the code and confirm it is indeed a bug and not me failing to delete things properly? I'm only directly asking you since we have talked about the topic before, and it seems this post has gone a month with no replies. Im not keen on repeatedly bumping the topic to get more people to see it but its a problem I've run into a few times recently.

 

I can rework the code, if you need, to make it more similar to how most people create these objects, but I hope the comments, variable names, and step by step message boxes help enough that it is not needed.

0 Likes