Refactor FCAD command management

This commit is contained in:
Zheng, Lei 2017-10-19 14:05:29 +08:00
parent ccc6a89165
commit 990e5a2ef3
5 changed files with 106 additions and 71 deletions

View File

@ -1,4 +1,5 @@
import FreeCAD, FreeCADGui
from collections import OrderedDict
class Assembly3Workbench(FreeCADGui.Workbench):
import asm3
@ -10,38 +11,37 @@ class Assembly3Workbench(FreeCADGui.Workbench):
def Activated(self):
self.observer.attach()
from asm3.gui import AsmCmdManager
for cmd in AsmCmdManager.getInfo().Types:
cmd.workbenchActivated()
def Deactivated(self):
self.observer.detach()
from asm3.gui import AsmCmdManager
for cmd in AsmCmdManager.getInfo().Types:
cmd.workbenchDeactivated()
def Initialize(self):
import asm3
cmdInfo = asm3.gui.AsmCmdType.getInfo()
cmds = cmdInfo.TypeNames
asm3.utils.logger.debug(cmds)
self.appendToolbar('asm3',cmds)
self.appendMenu('&Assembly3', cmds)
self.appendToolbar('asm3 Constraint',
asm3.constraint.Constraint.CommandList)
self.observer = asm3.gui.SelectionObserver(
cmdInfo.Types + asm3.constraint.Constraint.Commands)
from asm3.gui import AsmCmdManager,SelectionObserver
cmdSet = set()
for name,cmds in AsmCmdManager.Toolbars.items():
cmdSet.update(cmds)
self.appendToolbar(name,[cmd.getName() for cmd in cmds])
for name,cmds in AsmCmdManager.Menus.items():
cmdSet.update(cmds)
self.appendMenu(name,[cmd.getName() for cmd in cmds])
self.observer = SelectionObserver(cmdSet)
# FreeCADGui.addPreferencePage(
# ':/assembly3/ui/assembly3_prefs.ui','Assembly3')
def ContextMenu(self, _recipient):
import asm3
cmds = []
for cmd in asm3.gui.AsmCmdType.getInfo().Types:
if cmd.IsActive:
cmds.append(cmd.getName())
if cmds:
self.appendContextMenu('Assembly',cmds)
cmds.clear()
for cmd in asm3.constraint.Constraint.Commands:
if cmd.IsActive:
cmds.append(cmd.getName())
if cmds:
self.appendContextMenu('Constraint',cmds)
from asm3.gui import AsmCmdManager
menus = OrderedDict()
for cmd in AsmCmdManager.getInfo().Types:
name = cmd.getContextMenuName()
if name:
menus.setdefault(name,[]).append(cmd.getName())
for name,cmds in menus.items():
self.appendContextMenu(name,cmds)
FreeCADGui.addWorkbench(Assembly3Workbench)

View File

@ -1,7 +1,6 @@
import FreeCAD, FreeCADGui, Part
from asm3 import proxy,utils,assembly,solver,constraint,system,gui
from asm3 import proxy,utils,gui,solver,constraint,system,assembly
from asm3.utils import logger
from asm3.assembly import Assembly,AsmConstraint
try:
from asm3 import sys_slvs
except ImportError as e:
@ -12,6 +11,7 @@ except ImportError as e:
logger.error('failed to import sympy: {}'.format(e))
def test():
from asm3.assembly import Assembly
doc = FreeCAD.newDocument()
cylinder1 = doc.addObject('Part::Cylinder','cylinder1')
cylinder1.Visibility = False

View File

@ -2,6 +2,7 @@ from future.utils import with_metaclass
from collections import namedtuple
import FreeCAD, FreeCADGui
import asm3.utils as utils
from asm3.gui import AsmCmdManager
from asm3.utils import logger, objName
from asm3.proxy import ProxyType, PropertyInfo, propGet, propGetValue
@ -146,8 +147,21 @@ def _a(solver,partInfo,subname,shape):
class ConstraintCommand:
_toolbarName = 'Assembly3 Constraints'
_menuGroupName = ''
def __init__(self,tp):
self.tp = tp
self._id = 100 + tp._id
def workbenchActivated(self):
pass
def workbenchDeactivated(self):
pass
def getContextMenuName(self):
pass
def getName(self):
return 'asm3Add'+self.tp.getName()
@ -173,7 +187,7 @@ class ConstraintCommand:
else:
self.tp._active = True
def deactive(self):
def onClearSelection(self):
self.tp._active = False
class Constraint(ProxyType):
@ -183,18 +197,11 @@ class Constraint(ProxyType):
_typeEnum = 'ConstraintType'
_disabled = 'Disabled'
CommandList = []
Commands = []
def register(cls):
super(Constraint,cls).register()
@classmethod
def register(mcs,cls):
super(Constraint,mcs).register(cls)
if cls._menuItem:
mcs = cls.__class__
cmd = ConstraintCommand(cls)
name = cmd.getName()
mcs.CommandList.append(name)
mcs.Commands.append(cmd)
FreeCADGui.addCommand(name,cmd)
AsmCmdManager.register(ConstraintCommand(cls))
@classmethod
def attach(mcs,obj,checkType=True):
@ -263,7 +270,7 @@ class Base(with_metaclass(Constraint,object)):
_props = []
_iconName = 'Assembly_ConstraintGeneral.svg'
_menuText = 'Add a "{}" constraint'
_menuText = 'Create "{}" constraint'
_active = False
_menuItem = False

74
gui.py
View File

@ -1,7 +1,8 @@
from future.utils import with_metaclass
from collections import OrderedDict
import FreeCAD, FreeCADGui
import asm3
from asm3.utils import logger,objName,addIconToFCAD
from asm3.assembly import isTypeOf,Assembly,AsmConstraint,AsmElementLink
from asm3.proxy import ProxyType
class SelectionObserver:
@ -24,7 +25,7 @@ class SelectionObserver:
def clearSelection(self,*_args):
for cmd in self.cmds:
cmd.deactive()
cmd.onClearSelection()
def attach(self):
if not self._attached:
@ -39,21 +40,38 @@ class SelectionObserver:
self.clearSelection('')
class AsmCmdType(ProxyType):
def register(cls):
super(AsmCmdType,cls).register()
if cls._id >= 0:
FreeCADGui.addCommand(cls.getName(),cls())
class AsmCmdBase(with_metaclass(AsmCmdType,object)):
_id = -1
_active = True
class AsmCmdManager(ProxyType):
Toolbars = OrderedDict()
Menus = OrderedDict()
_defaultMenuGroupName = '&Assembly3'
@classmethod
def register(mcs,cls):
if cls._id < 0:
return
super(AsmCmdManager,mcs).register(cls)
FreeCADGui.addCommand(cls.getName(),cls)
if cls._toolbarName:
mcs.Toolbars.setdefault(cls._toolbarName,[]).append(cls)
if cls._menuGroupName is not None:
name = cls._menuGroupName
if not name:
name = mcs._defaultMenuGroupName
mcs.Menus.setdefault(name,[]).append(cls)
def workbenchActivated(cls):
pass
def workbenchDeactivated(cls):
pass
def getContextMenuName(cls):
if cls.IsActive() and cls._contextMenuName:
return cls._contextMenuName
def getName(cls):
return 'asm3'+cls.__name__[3:]
@classmethod
def GetResources(cls):
return {
'Pixmap':addIconToFCAD(cls._iconName),
@ -61,41 +79,45 @@ class AsmCmdBase(with_metaclass(AsmCmdType,object)):
'ToolTip':cls.getToolTip()
}
@classmethod
def getMenuText(cls):
return cls._menuText
@classmethod
def getToolTip(cls):
return getattr(cls,'_tooltip',cls.getMenuText())
@classmethod
def IsActive(cls):
if cls._active and cls._id>=0 and FreeCAD.ActiveDocument:
return True
@classmethod
def checkActive(cls):
pass
@classmethod
def deactive(cls):
def onClearSelection(cls):
pass
class AsmCmdBase(with_metaclass(AsmCmdManager,object)):
_id = -1
_active = True
_toolbarName = 'Assembly3'
_menuGroupName = ''
_contextMenuName = 'Assembly'
class AsmCmdNew(AsmCmdBase):
_id = 0
_menuText = 'Create assembly'
_iconName = 'Assembly_New_Assembly.svg'
def Activated(self):
Assembly.make()
@classmethod
def Activated(cls):
asm3.assembly.Assembly.make()
class AsmCmdSolve(AsmCmdBase):
_id = 1
_menuText = 'Solve constraints'
_iconName = 'AssemblyWorkbench.svg'
def Activated(self):
@classmethod
def Activated(cls):
import asm3.solver as solver
solver.solve()
@ -107,17 +129,19 @@ class AsmCmdMove(AsmCmdBase):
@classmethod
def getSelection(cls):
from asm3.assembly import isTypeOf,AsmElementLink
sels = FreeCADGui.Selection.getSelection()
if len(sels)==1 and isTypeOf(sels[0],AsmElementLink):
return sels[0].ViewObject
def Activated(self):
vobj = self.getSelection()
@classmethod
def Activated(cls):
vobj = cls.getSelection()
if vobj:
doc = FreeCADGui.editDocument()
if doc:
doc.resetEdit()
vobj.UseCenterballDragger = self._useCenterballDragger
vobj.UseCenterballDragger = cls._useCenterballDragger
vobj.doubleClicked()
@classmethod
@ -125,7 +149,7 @@ class AsmCmdMove(AsmCmdBase):
cls._active = True if cls.getSelection() else False
@classmethod
def deactive(cls):
def onClearSelection(cls):
cls._active = False
class AsmCmdAxialMove(AsmCmdMove):

View File

@ -56,7 +56,7 @@ class ProxyType(type):
for tp in info.Types:
tp._idx = -1
mcs.getInfo().Types.append(tp)
tp.register()
mcs.register(tp)
@classmethod
def getType(mcs,tp):
@ -181,23 +181,27 @@ class ProxyType(type):
def __init__(cls, name, bases, attrs):
super(ProxyType,cls).__init__(name,bases,attrs)
cls._idx = -1
mcs = cls.__class__
mcs.getInfo().Types.append(cls)
cls.register()
mcs.register(cls)
def register(cls):
@classmethod
def register(mcs,cls):
'''
Register a class to this meta class
To make the registration automatic at the class definition time, simply
set __metaclass__ of that class to ProxyType of its derived type.
You can also call this methode directly to register an unrelated class
It is defined as a meta class method in order for you to call this
method directly to register an unrelated class
'''
cls._idx = -1
mcs.getInfo().Types.append(cls)
callback = getattr(cls,'onRegister',None)
if callback:
callback()
if cls._id < 0:
return
mcs = cls.__class__
info = mcs.getInfo()
if cls._id in info.TypeMap:
raise RuntimeError('Duplicate {} type id {}'.format(