GUI: Handle packaged Workbenches and their icons

This commit is contained in:
Chris Hennes
2021-10-26 23:05:52 -05:00
parent a225695dd8
commit 3aebf0aead
3 changed files with 96 additions and 46 deletions

View File

@@ -100,10 +100,7 @@ def InitApplications():
Lib64Dir = os.path.realpath(Lib64Dir)
if os.path.exists(Lib64Dir):
libpaths.append(Lib64Dir)
if sys.version_info[0] == 3:
LibPyDir = FreeCAD.getHomePath()+'lib-py3'
else:
LibPyDir = FreeCAD.getHomePath()+'lib-py2'
LibPyDir = FreeCAD.getHomePath()+'lib-py3'
LibPyDir = os.path.realpath(LibPyDir)
if (os.path.exists(LibPyDir)):
libpaths.append(LibPyDir)
@@ -168,31 +165,45 @@ def InitApplications():
# proper python modules this can eventuelly be removed.
sys.path = [ModDir] + libpaths + [ExtDir] + sys.path
def RunInitPy(Dir):
InstallFile = os.path.join(Dir,"Init.py")
if (os.path.exists(InstallFile)):
try:
with open(file=InstallFile, encoding="utf-8") as f:
exec(f.read())
except Exception as inst:
Log('Init: Initializing ' + Dir + '... failed\n')
Log('-'*100+'\n')
Log(traceback.format_exc())
Log('-'*100+'\n')
Err('During initialization the error "' + str(inst) + '" occurred in ' + InstallFile + '\n')
Err('Please look into the log file for further information\n')
else:
Log('Init: Initializing ' + Dir + '... done\n')
else:
Log('Init: Initializing ' + Dir + '(Init.py not found)... ignore\n')
for Dir in ModDict.values():
if ((Dir != '') & (Dir != 'CVS') & (Dir != '__init__.py')):
sys.path.insert(0,Dir)
PathExtension.append(Dir)
InstallFile = os.path.join(Dir,"Init.py")
if (os.path.exists(InstallFile)):
try:
# XXX: This looks scary securitywise...
if sys.version_info.major < 3:
with open(InstallFile) as f:
exec(f.read())
else:
with open(file=InstallFile, encoding="utf-8") as f:
exec(f.read())
except Exception as inst:
Log('Init: Initializing ' + Dir + '... failed\n')
Log('-'*100+'\n')
Log(traceback.format_exc())
Log('-'*100+'\n')
Err('During initialization the error "' + str(inst) + '" occurred in ' + InstallFile + '\n')
Err('Please look into the log file for further information\n')
MetadataFile = os.path.join(Dir, "package.xml")
if os.path.exists(MetadataFile):
meta = FreeCAD.Metadata(MetadataFile)
content = meta.Content
if "workbench" in content:
workbenches = content["workbench"]
for workbench in workbenches:
subdirectory = workbench.Name if not workbench.Subdirectory else workbench.Subdirectory
subdirectory = os.path.join(Dir, subdirectory)
classname = workbench.Classname
sys.path.insert(0,subdirectory)
PathExtension.append(subdirectory)
RunInitPy(subdirectory)
else:
Log('Init: Initializing ' + Dir + '... done\n')
continue # The package content says there are no workbenches here, so just skip
else:
Log('Init: Initializing ' + Dir + '(Init.py not found)... ignore\n')
RunInitPy(Dir)
extension_modules = []

View File

@@ -42,6 +42,7 @@ class Workbench:
"""The workbench base class."""
MenuText = ""
ToolTip = ""
Icon = None
def Initialize(self):
"""Initializes this workbench."""
@@ -117,30 +118,56 @@ def InitApplications():
ModDirs = FreeCAD.__ModDirs__
#print ModDirs
Log('Init: Searching modules...\n')
def RunInitGuiPy(Dir):
InstallFile = os.path.join(Dir,"InitGui.py")
if (os.path.exists(InstallFile)):
try:
with open(file=InstallFile, encoding="utf-8") as f:
exec(f.read())
except Exception as inst:
Log('Init: Initializing ' + Dir + '... failed\n')
Log('-'*100+'\n')
Log(traceback.format_exc())
Log('-'*100+'\n')
Err('During initialization the error "' + str(inst) + '" occurred in ' + InstallFile + '\n')
Err('Please look into the log file for further information\n')
else:
Log('Init: Initializing ' + Dir + '... done\n')
else:
Log('Init: Initializing ' + Dir + '(InitGui.py not found)... ignore\n')
for Dir in ModDirs:
if ((Dir != '') & (Dir != 'CVS') & (Dir != '__init__.py')):
InstallFile = os.path.join(Dir,"InitGui.py")
if (os.path.exists(InstallFile)):
try:
# XXX: This looks scary securitywise...
if sys.version_info.major < 3:
with open(InstallFile) as f:
exec(f.read())
else:
with open(file=InstallFile, encoding="utf-8") as f:
exec(f.read())
except Exception as inst:
Log('Init: Initializing ' + Dir + '... failed\n')
Log('-'*100+'\n')
Log(traceback.format_exc())
Log('-'*100+'\n')
Err('During initialization the error "' + str(inst) + '" occurred in ' + InstallFile + '\n')
Err('Please look into the log file for further information\n')
else:
Log('Init: Initializing ' + Dir + '... done\n')
else:
Log('Init: Initializing ' + Dir + '(InitGui.py not found)... ignore\n')
MetadataFile = os.path.join(Dir, "package.xml")
if os.path.exists(MetadataFile):
meta = FreeCAD.Metadata(MetadataFile)
content = meta.Content
if "workbench" in content:
FreeCAD.Gui.addIconPath(Dir)
workbenches = content["workbench"]
for workbench_metadata in workbenches:
subdirectory = workbench_metadata.Name if not workbench_metadata.Subdirectory else workbench_metadata.Subdirectory
subdirectory = os.path.join(Dir, subdirectory)
RunInitGuiPy(subdirectory)
# Brute force this for now:
classname = workbench_metadata.Classname
wb_handle = FreeCAD.Gui.getWorkbench(classname)
if wb_handle:
GeneratePackageIcon(dir, subdirectory, workbench_metadata, wb_handle)
else:
Log("Failed to get handle to {classname} -- no icon can be generated, check classname in package.xml\n")
else:
continue # The package content says there are no workbenches here, so just skip
else:
RunInitGuiPy(Dir)
extension_modules = []
try:
import pkgutil
@@ -169,6 +196,18 @@ def InitApplications():
except ImportError as inst:
Err('During initialization the error "' + str(inst) + '" occurred\n')
def GeneratePackageIcon(dir:str, subdirectory:str, workbench_metadata:FreeCAD.Metadata, wb_handle:Workbench) -> None:
relative_filename = workbench_metadata.Icon
if not relative_filename:
# Although a required element, this content item does not have an icon. Just bail out
return
absolute_filename = os.path.join(subdirectory, relative_filename)
if wb_handle.Icon:
Wrn(f"Packaged workbench {workbench_metadata.Name} specified icon in class {workbench_metadata.Classname}:\n")
Wrn(f" ... Overwriting that specification with package.xml data")
wb_handle.Icon = absolute_filename
Log ('Init: Running FreeCADGuiInit.py start script...\n')

View File

@@ -176,8 +176,8 @@ using namespace Gui;
* \section moredetails More details and limitations
* One of the key concepts of the workbench framework is to load a module at runtime when the user needs some function that it
* provides. So, if the user doesn't need a module it never gets loaded into RAM. This speeds up the startup procedure of
* FreeCAD and saves memory.
*
* At startup FreeCAD scans all module directories and invokes InitGui.py. So an item for a workbench gets created. If the user
* clicks on such an item the matching module gets loaded, the C++ workbench gets registered and activated.
*
@@ -192,7 +192,7 @@ using namespace Gui;
* w=Workbench() # creates a standard workbench (the same as StdWorkbench in C++)
* w.MenuText = "My Workbench" # the text that will appear in the combo box
* dir(w) # lists all available function of the object
* FreeCADGui.addWorkbench(w) # Creates an item for our workbenmch now
* FreeCADGui.addWorkbench(w) # Creates an item for our workbench now
* # Note: We must first add the workbench to run some initialization code
* # Then we are ready to customize the workbench
* list = ["Std_Test1", "Std_Test2", "Std_Test3"] # creates a list of new functions