903 lines
27 KiB
Python
903 lines
27 KiB
Python
from collections import OrderedDict
|
|
import FreeCAD, FreeCADGui
|
|
from PySide import QtCore, QtGui
|
|
from .deps import with_metaclass
|
|
from .utils import getElementPos,objName,addIconToFCAD,guilogger as logger
|
|
from .proxy import ProxyType
|
|
from .FCADLogger import FCADLogger
|
|
|
|
class SelectionObserver:
|
|
def __init__(self):
|
|
self._attached = False
|
|
self.timer = QtCore.QTimer()
|
|
self.timer.setSingleShot(True)
|
|
self.timer.timeout.connect(self.onTimer)
|
|
self.cmds = []
|
|
self.elements = dict()
|
|
self.attach()
|
|
|
|
def setCommands(self,cmds):
|
|
self.cmds = cmds
|
|
|
|
def _setElementVisible(self,obj,subname,vis):
|
|
sobj = obj.getSubObject(subname,1)
|
|
from .assembly import isTypeOf,AsmConstraint,\
|
|
AsmElement,AsmElementLink
|
|
if isTypeOf(sobj,(AsmElement,AsmElementLink)):
|
|
res = sobj.Proxy.parent.Object.isElementVisible(sobj.Name)
|
|
if res and vis:
|
|
return False
|
|
sobj.Proxy.parent.Object.setElementVisible(sobj.Name,vis)
|
|
elif isTypeOf(sobj,AsmConstraint):
|
|
vis = [vis] * len(sobj.Group)
|
|
sobj.setPropertyStatus('VisibilityList','-Immutable')
|
|
sobj.VisibilityList = vis
|
|
sobj.setPropertyStatus('VisibilityList','Immutable')
|
|
else:
|
|
return
|
|
if vis:
|
|
FreeCADGui.Selection.updateSelection(vis,obj,subname)
|
|
|
|
def setElementVisible(self,docname,objname,subname,vis,presel=False):
|
|
if FreeCAD.isRestoring():
|
|
self.resetElementVisible()
|
|
return
|
|
if not AsmCmdManager.AutoElementVis:
|
|
self.elements.clear()
|
|
return
|
|
doc = FreeCAD.getDocument(docname)
|
|
if not doc:
|
|
return
|
|
obj = doc.getObject(objname)
|
|
if not obj:
|
|
return
|
|
key = (docname,objname,subname)
|
|
val = None
|
|
if not vis:
|
|
val = self.elements.get(key,None)
|
|
if val is None or (presel and val):
|
|
return
|
|
if logger.catchWarn('',self._setElementVisible,
|
|
obj,subname,vis) is False and presel:
|
|
return
|
|
if not vis:
|
|
self.elements.pop(key,None)
|
|
elif not presel:
|
|
self.elements[key] = True
|
|
else:
|
|
self.elements.setdefault(key,False)
|
|
|
|
def resetElementVisible(self):
|
|
elements = list(self.elements)
|
|
self.elements.clear()
|
|
for docname,objname,subname in elements:
|
|
doc = FreeCAD.getDocument(docname)
|
|
if not doc:
|
|
continue
|
|
obj = doc.getObject(objname)
|
|
if not obj:
|
|
continue
|
|
logger.catchWarn('',self._setElementVisible,obj,subname,False)
|
|
|
|
def onChange(self,hasSelection=True):
|
|
if not hasSelection:
|
|
self.timer.stop()
|
|
for cmd in self.cmds:
|
|
cmd.onSelectionChange(False)
|
|
return
|
|
self.timer.start(50)
|
|
|
|
def onTimer(self):
|
|
hasSelection = FreeCADGui.Selection.hasSelection()
|
|
for cmd in self.cmds:
|
|
cmd.onSelectionChange(hasSelection)
|
|
|
|
def addSelection(self,docname,objname,subname,_pos):
|
|
self.onChange()
|
|
self.setElementVisible(docname,objname,subname,True)
|
|
|
|
def removeSelection(self,docname,objname,subname):
|
|
self.onChange(FreeCADGui.Selection.hasSelection())
|
|
self.setElementVisible(docname,objname,subname,False)
|
|
|
|
def setPreselection(self,docname,objname,subname):
|
|
self.setElementVisible(docname,objname,subname,True,True)
|
|
|
|
def removePreselection(self,docname,objname,subname):
|
|
self.setElementVisible(docname,objname,subname,False,True)
|
|
|
|
def setSelection(self,*_args):
|
|
self.onChange()
|
|
if AsmCmdManager.AutoElementVis:
|
|
self.resetElementVisible()
|
|
for sel in FreeCADGui.Selection.getSelectionEx('*',False):
|
|
for sub in sel.SubElementNames:
|
|
self.setElementVisible(sel.Object.Document.Name,
|
|
sel.Object.Name,sub,True)
|
|
|
|
def clearSelection(self,*_args):
|
|
self.onChange(False)
|
|
self.resetElementVisible()
|
|
|
|
def attach(self):
|
|
logger.trace('attach selection aboserver {}',self._attached)
|
|
if not self._attached:
|
|
FreeCADGui.Selection.addObserver(self,False)
|
|
self._attached = True
|
|
|
|
def detach(self):
|
|
logger.trace('detach selection aboserver {}',self._attached)
|
|
if self._attached:
|
|
FreeCADGui.Selection.removeObserver(self)
|
|
self._attached = False
|
|
self.clearSelection('')
|
|
|
|
|
|
class AsmCmdManager(ProxyType):
|
|
_HiddenToolbars = set()
|
|
Toolbars = OrderedDict()
|
|
Menus = OrderedDict()
|
|
_defaultMenuGroupName = '&Assembly3'
|
|
WorkbenchActivated = False
|
|
|
|
@staticmethod
|
|
def getToolbarParams():
|
|
return FreeCAD.ParamGet('User parameter:BaseApp/MainWindow/Toolbars')
|
|
|
|
@classmethod
|
|
def init(mcs):
|
|
if not mcs.getParam('Bool','GuiInited',False):
|
|
hgrp = mcs.getToolbarParams()
|
|
for toolbar in mcs._HiddenToolbars:
|
|
hgrp.SetBool(toolbar,False)
|
|
mcs.setParam('Bool','GuiInited',True)
|
|
|
|
@classmethod
|
|
def toggleToolbars(mcs):
|
|
hgrp = mcs.getToolbarParams()
|
|
show = False
|
|
for toolbar in mcs._HiddenToolbars:
|
|
if not hgrp.GetBool(toolbar,True):
|
|
show = True
|
|
break
|
|
mw = FreeCADGui.getMainWindow()
|
|
for toolbar in mcs._HiddenToolbars:
|
|
if show != hgrp.GetBool(toolbar,True):
|
|
hgrp.SetBool(toolbar,show)
|
|
tb = mw.findChild(QtGui.QToolBar,toolbar)
|
|
if not tb:
|
|
logger.error('cannot find toolbar "{}"',toolbar)
|
|
tb.setVisible(show)
|
|
|
|
@classmethod
|
|
def register(mcs,cls):
|
|
if cls._id < 0:
|
|
return
|
|
super(AsmCmdManager,mcs).register(cls)
|
|
FreeCADGui.addCommand(cls.getName(),cls)
|
|
if cls._toolbarName:
|
|
tb = mcs.Toolbars.setdefault(cls._toolbarName,[])
|
|
if not tb and not getattr(cls,'_toolbarVisible',True):
|
|
mcs._HiddenToolbars.add(cls._toolbarName)
|
|
tb.append(cls)
|
|
|
|
if cls._menuGroupName is not None:
|
|
name = cls._menuGroupName
|
|
if not name:
|
|
name = mcs._defaultMenuGroupName
|
|
mcs.Menus.setdefault(name,[]).append(cls)
|
|
|
|
@classmethod
|
|
def getParamGroup(mcs):
|
|
return FreeCAD.ParamGet(
|
|
'User parameter:BaseApp/Preferences/Mod/Assembly3')
|
|
|
|
@classmethod
|
|
def getParam(mcs,tp,name,default=None):
|
|
return getattr(mcs.getParamGroup(),'Get'+tp)(name,default)
|
|
|
|
@classmethod
|
|
def setParam(mcs,tp,name,v):
|
|
getattr(mcs.getParamGroup(),'Set'+tp)(name,v)
|
|
|
|
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:]
|
|
|
|
def getMenuText(cls):
|
|
return cls._menuText
|
|
|
|
def getToolTip(cls):
|
|
return getattr(cls,'_tooltip',cls.getMenuText())
|
|
|
|
def IsActive(cls):
|
|
if cls._id<0 or not FreeCAD.ActiveDocument:
|
|
return False
|
|
if cls._active is None:
|
|
cls.checkActive()
|
|
return cls._active
|
|
|
|
def onSelectionChange(cls, hasSelection):
|
|
_ = hasSelection
|
|
|
|
class AsmCmdBase(with_metaclass(AsmCmdManager, object)):
|
|
_id = -1
|
|
_active = None
|
|
_toolbarName = 'Assembly3'
|
|
_menuGroupName = ''
|
|
_contextMenuName = 'Assembly'
|
|
_accel = None
|
|
|
|
@classmethod
|
|
def checkActive(cls):
|
|
cls._active = True
|
|
|
|
@classmethod
|
|
def getIconName(cls):
|
|
return addIconToFCAD(cls._iconName)
|
|
|
|
@classmethod
|
|
def GetResources(cls):
|
|
ret = {
|
|
'Pixmap':cls.getIconName(),
|
|
'MenuText':cls.getMenuText(),
|
|
'ToolTip':cls.getToolTip()
|
|
}
|
|
if cls._accel:
|
|
ret['Accel'] = cls._accel
|
|
return ret
|
|
|
|
class AsmCmdNew(AsmCmdBase):
|
|
_id = 0
|
|
_menuText = 'Create assembly'
|
|
_iconName = 'Assembly_New_Assembly.svg'
|
|
_accel = 'A, N'
|
|
|
|
@classmethod
|
|
def Activated(cls):
|
|
from . import assembly
|
|
assembly.Assembly.make()
|
|
|
|
|
|
class AsmCmdSolve(AsmCmdBase):
|
|
_id = 1
|
|
_menuText = 'Solve constraints'
|
|
_iconName = 'AssemblyWorkbench.svg'
|
|
_accel = 'A, S'
|
|
|
|
@classmethod
|
|
def Activated(cls):
|
|
from . import solver
|
|
FreeCAD.setActiveTransaction('Assembly solve')
|
|
logger.report('command "{}" exception'.format(cls.getName()),
|
|
solver.solve,reportFailed=True)
|
|
FreeCAD.closeActiveTransaction()
|
|
|
|
|
|
class AsmCmdQuickSolve(AsmCmdBase):
|
|
_id = 21
|
|
_menuText = 'Quick solve'
|
|
_iconName = 'Assembly_QuickSolve.svg'
|
|
_accel = 'A, F'
|
|
|
|
@classmethod
|
|
def Activated(cls):
|
|
from . import solver
|
|
FreeCAD.setActiveTransaction('Assembly quick solve')
|
|
logger.report('command "{}" exception'.format(cls.getName()),
|
|
solver.solve)
|
|
FreeCAD.closeActiveTransaction()
|
|
|
|
|
|
class AsmCmdNewElement(AsmCmdBase):
|
|
_id = 19
|
|
_menuText = 'Create element'
|
|
_iconName = 'Assembly_New_Element.svg'
|
|
_accel = 'A, E'
|
|
|
|
@classmethod
|
|
def Activated(cls):
|
|
from . import assembly
|
|
logger.report('Failed to add element',
|
|
assembly.AsmElement.make, undo=True, allowDuplicate=
|
|
QtGui.QApplication.keyboardModifiers()==QtCore.Qt.ControlModifier)
|
|
|
|
@classmethod
|
|
def checkActive(cls):
|
|
from . import assembly
|
|
cls._active = logger.catchTrace(
|
|
'',assembly.AsmElement.getSelections) is not None
|
|
|
|
@classmethod
|
|
def onSelectionChange(cls,hasSelection):
|
|
cls._active = None if hasSelection else False
|
|
|
|
|
|
class AsmCmdMove(AsmCmdBase):
|
|
_id = 2
|
|
_menuText = 'Move part'
|
|
_iconName = 'Assembly_Move.svg'
|
|
_accel = 'A, M'
|
|
_moveInfo = None
|
|
|
|
@classmethod
|
|
def Activated(cls):
|
|
from . import mover
|
|
mover.movePart(True,cls._moveInfo)
|
|
|
|
@classmethod
|
|
def canMove(cls):
|
|
from . import mover
|
|
cls._moveInfo = None
|
|
cls._moveInfo = mover.getMovingElementInfo()
|
|
mover.checkFixedPart(cls._moveInfo.ElementInfo)
|
|
return True
|
|
|
|
@classmethod
|
|
def checkActive(cls):
|
|
cls._active = True if logger.catchTrace('',cls.canMove) else False
|
|
|
|
@classmethod
|
|
def onSelectionChange(cls,hasSelection):
|
|
if not hasSelection:
|
|
cls._active = False
|
|
else:
|
|
cls._active = None
|
|
cls._moveInfo = None
|
|
|
|
class AsmCmdAxialMove(AsmCmdBase):
|
|
_id = 3
|
|
_menuText = 'Axial move part'
|
|
_iconName = 'Assembly_AxialMove.svg'
|
|
_useCenterballDragger = False
|
|
_accel = 'A, A'
|
|
|
|
@classmethod
|
|
def IsActive(cls):
|
|
return AsmCmdMove.IsActive()
|
|
|
|
@classmethod
|
|
def Activated(cls):
|
|
from . import mover
|
|
mover.movePart(False,AsmCmdMove._moveInfo)
|
|
|
|
class AsmCmdQuickMove(AsmCmdAxialMove):
|
|
_id = 13
|
|
_menuText = 'Quick move'
|
|
_tooltip = 'Bring an object contained in an assembly to where the mouse\n'\
|
|
'is located. This is designed to help bringing an object far\n'\
|
|
'away quickly into view.'
|
|
_iconName = 'Assembly_QuickMove.svg'
|
|
_accel = 'A, Q'
|
|
|
|
@classmethod
|
|
def Activated(cls):
|
|
from . import mover
|
|
mover.quickMove()
|
|
|
|
class AsmCmdCheckable(AsmCmdBase):
|
|
_id = -2
|
|
_saveParam = False
|
|
_defaultValue = False
|
|
|
|
@classmethod
|
|
def getAttributeName(cls):
|
|
return cls.__name__[6:]
|
|
|
|
@classmethod
|
|
def getChecked(cls):
|
|
return getattr(cls.__class__,cls.getAttributeName())
|
|
|
|
@classmethod
|
|
def setChecked(cls,v):
|
|
setattr(cls.__class__,cls.getAttributeName(),v)
|
|
if cls._saveParam:
|
|
cls.setParam('Bool',cls.getAttributeName(),v)
|
|
|
|
@classmethod
|
|
def onRegister(cls):
|
|
if cls._saveParam:
|
|
v = cls.getParam('Bool',cls.getAttributeName(),cls._defaultValue)
|
|
else:
|
|
v = False
|
|
cls.setChecked(v)
|
|
|
|
@classmethod
|
|
def GetResources(cls):
|
|
ret = super(AsmCmdCheckable,cls).GetResources()
|
|
ret['Checkable'] = cls.getChecked()
|
|
return ret
|
|
|
|
@classmethod
|
|
def Activated(cls,checked):
|
|
cls.setChecked(True if checked else False)
|
|
|
|
class AsmCmdLockMover(AsmCmdCheckable):
|
|
_id = 15
|
|
_menuText = 'Lock mover'
|
|
_tooltip = 'Lock mover for fixed part'
|
|
_iconName = 'Assembly_LockMover.svg'
|
|
_saveParam = True
|
|
|
|
@classmethod
|
|
def Activated(cls,checked):
|
|
super(AsmCmdLockMover,cls).Activated(checked)
|
|
AsmCmdMove._active = None
|
|
AsmCmdAxialMove._active = None
|
|
AsmCmdQuickMove._active = None
|
|
|
|
|
|
class AsmCmdToggleVisibility(AsmCmdBase):
|
|
_id = 17
|
|
_menuText = 'Toggle part visibility'
|
|
_tooltip = 'Toggle the visibility of the selected part'
|
|
_iconName = 'Assembly_TogglePartVisibility.svg'
|
|
_accel = 'A, Space'
|
|
|
|
@classmethod
|
|
def Activated(cls):
|
|
moveInfo = AsmCmdMove._moveInfo
|
|
if not moveInfo:
|
|
return
|
|
info = moveInfo.ElementInfo
|
|
if info.Subname:
|
|
subs = moveInfo.SelSubname[:-len(info.Subname)]
|
|
else:
|
|
subs = moveInfo.SelSubname
|
|
subs = subs.split('.')
|
|
if isinstance(info.Part,tuple):
|
|
part = info.Part[0]
|
|
vis = part.isElementVisible(str(info.Part[1]))
|
|
part.setElementVisible(str(info.Part[1]),not vis)
|
|
else:
|
|
from .assembly import resolveAssembly
|
|
partGroup = resolveAssembly(info.Parent).getPartGroup()
|
|
vis = partGroup.isElementVisible(info.Part.Name)
|
|
partGroup.setElementVisible(info.Part.Name,not vis)
|
|
|
|
FreeCADGui.Selection.clearSelection()
|
|
FreeCADGui.Selection.addSelection(moveInfo.SelObj,'.'.join(subs))
|
|
FreeCADGui.runCommand('Std_TreeSelection')
|
|
if vis:
|
|
FreeCADGui.runCommand('Std_TreeCollapse')
|
|
|
|
@classmethod
|
|
def IsActive(cls):
|
|
return AsmCmdMove._moveInfo is not None
|
|
|
|
|
|
class AsmCmdTrace(AsmCmdCheckable):
|
|
_id = 4
|
|
_menuText = 'Trace part move'
|
|
_iconName = 'Assembly_Trace.svg'
|
|
|
|
_object = None
|
|
_subname = None
|
|
|
|
@classmethod
|
|
def Activated(cls,checked):
|
|
super(AsmCmdTrace,cls).Activated(checked)
|
|
if not checked:
|
|
cls._object = None
|
|
return
|
|
sel = FreeCADGui.Selection.getSelectionEx('',False)
|
|
if len(sel)==1:
|
|
subs = sel[0].SubElementNames
|
|
if len(subs)==1:
|
|
cls._object = sel[0].Object
|
|
cls._subname = subs[0]
|
|
logger.info('trace {}.{}',cls._object.Name,cls._subname)
|
|
return
|
|
logger.info('trace moving element')
|
|
|
|
@classmethod
|
|
def getPosition(cls):
|
|
if not cls._object:
|
|
return
|
|
try:
|
|
if cls._object.Document != FreeCAD.ActiveDocument:
|
|
cls._object = None
|
|
return getElementPos((cls._object,cls._subname))
|
|
except Exception:
|
|
cls._object = None
|
|
|
|
class AsmCmdAutoRecompute(AsmCmdCheckable):
|
|
_id = 5
|
|
_menuText = 'Auto recompute'
|
|
_iconName = 'Assembly_AutoRecompute.svg'
|
|
_saveParam = True
|
|
|
|
@classmethod
|
|
def IsActive(cls):
|
|
return True
|
|
|
|
class AsmCmdSmartRecompute(AsmCmdCheckable):
|
|
_id = 22
|
|
_menuText = 'Smart recompute'
|
|
_tooltip = 'Toggle smart recompute to reduce recompution time'
|
|
_iconName = 'Assembly_SmartRecompute.svg'
|
|
_saveParam = True
|
|
|
|
@classmethod
|
|
def IsActive(cls):
|
|
return True
|
|
|
|
class AsmCmdAutoElementVis(AsmCmdCheckable):
|
|
_id = 9
|
|
_menuText = 'Auto element visibility'
|
|
_iconName = 'Assembly_AutoElementVis.svg'
|
|
_saveParam = True
|
|
_defaultValue = True
|
|
|
|
@classmethod
|
|
def IsActive(cls):
|
|
return True
|
|
|
|
@classmethod
|
|
def Activated(cls,checked):
|
|
super(AsmCmdAutoElementVis,cls).Activated(checked)
|
|
from .assembly import isTypeOf,AsmConstraint,\
|
|
AsmElement,AsmElementLink,AsmElementGroup
|
|
for doc in FreeCAD.listDocuments().values():
|
|
for obj in doc.Objects:
|
|
if isTypeOf(obj,(AsmConstraint,AsmElementGroup)):
|
|
obj.Visibility = False
|
|
if isTypeOf(obj,AsmConstraint):
|
|
obj.ViewObject.OnTopWhenSelected = 2
|
|
obj.setPropertyStatus('VisibilityList',
|
|
'NoModify' if checked else '-NoModify')
|
|
elif isTypeOf(obj,(AsmElementLink,AsmElement)):
|
|
if checked:
|
|
obj.Proxy.parent.Object.setElementVisible(
|
|
obj.Name,False)
|
|
obj.Visibility = False
|
|
obj.ViewObject.OnTopWhenSelected = 2
|
|
|
|
|
|
class AsmCmdAddWorkplane(AsmCmdBase):
|
|
_id = 8
|
|
_menuText = 'Add workplane'
|
|
_iconName = 'Assembly_Add_Workplane.svg'
|
|
_toolbarName = None
|
|
_menuGroupName = None
|
|
_accel = 'A, P'
|
|
_makeType = 0
|
|
|
|
@classmethod
|
|
def checkActive(cls):
|
|
from . import assembly
|
|
if logger.catchTrace('Add workplane selection',
|
|
assembly.AsmWorkPlane.getSelection):
|
|
cls._active = True
|
|
else:
|
|
cls._active = False
|
|
|
|
@classmethod
|
|
def onSelectionChange(cls,hasSelection):
|
|
cls._active = None if hasSelection else False
|
|
|
|
@classmethod
|
|
def Activated(cls,idx=0):
|
|
_ = idx
|
|
from . import assembly
|
|
assembly.AsmWorkPlane.make(tp=cls._makeType)
|
|
|
|
class AsmCmdAddWorkplaneXZ(AsmCmdAddWorkplane):
|
|
_id = 10
|
|
_menuText = 'Add XZ workplane'
|
|
_iconName = 'Assembly_Add_WorkplaneXZ.svg'
|
|
_makeType = 1
|
|
|
|
|
|
class AsmCmdAddWorkplaneZY(AsmCmdAddWorkplane):
|
|
_id = 11
|
|
_menuText = 'Add ZY workplane'
|
|
_iconName = 'Assembly_Add_WorkplaneZY.svg'
|
|
_makeType = 2
|
|
|
|
class AsmCmdAddOrigin(AsmCmdCheckable):
|
|
_id = 14
|
|
_menuText = 'Add Origin'
|
|
_iconName = 'Assembly_Add_Origin.svg'
|
|
_makeType = 3
|
|
_saveParam = False
|
|
_toolbarName = None
|
|
_menuGroupName = None
|
|
|
|
@classmethod
|
|
def IsActive(cls):
|
|
return True
|
|
|
|
@classmethod
|
|
def Activated(cls,checked):
|
|
logger.info('checked {}'.format(checked))
|
|
cls.setChecked(checked)
|
|
if checked:
|
|
from . import assembly
|
|
sels = logger.catchTrace('Add origin selection',
|
|
assembly.AsmWorkPlane.getSelection)
|
|
if sels:
|
|
assembly.AsmWorkPlane.make(sels,tp=cls._makeType)
|
|
|
|
class AsmCmdAddWorkplaneGroup(AsmCmdBase):
|
|
_id = 12
|
|
_iconName = AsmCmdAddWorkplane._iconName
|
|
_menuText = AsmCmdAddWorkplane._menuText
|
|
_menuGroupName = ''
|
|
_toolbarName = AsmCmdBase._toolbarName
|
|
_cmds = (AsmCmdAddWorkplane.getName(),
|
|
AsmCmdAddWorkplaneXZ.getName(),
|
|
AsmCmdAddWorkplaneZY.getName(),
|
|
AsmCmdAddOrigin.getName())
|
|
|
|
@classmethod
|
|
def IsActive(cls):
|
|
return True
|
|
|
|
@classmethod
|
|
def GetCommands(cls):
|
|
return cls._cmds
|
|
|
|
class AsmCmdGotoRelation(AsmCmdBase):
|
|
_id = 16
|
|
_menuText = 'Go to relation'
|
|
_tooltip = 'Select the corresponding part object in the relation group'
|
|
_iconName = 'Assembly_GotoRelation.svg'
|
|
_accel = 'A, R'
|
|
_toolbarName = ''
|
|
|
|
@classmethod
|
|
def Activated(cls):
|
|
from .assembly import AsmRelationGroup
|
|
if AsmCmdMove._moveInfo:
|
|
AsmRelationGroup.gotoRelation(AsmCmdMove._moveInfo)
|
|
return
|
|
sels = FreeCADGui.Selection.getSelectionEx('',0,True)
|
|
if sels and len(sels[0].SubElementNames)==1:
|
|
AsmRelationGroup.gotoRelationOfConstraint(
|
|
sels[0].Object,sels[0].SubElementNames[0])
|
|
|
|
@classmethod
|
|
def IsActive(cls):
|
|
if AsmCmdMove._moveInfo:
|
|
return True
|
|
if cls._active is None:
|
|
cls.checkActive()
|
|
return cls._active
|
|
|
|
@classmethod
|
|
def checkActive(cls):
|
|
from .assembly import isTypeOf, AsmConstraint, AsmElementLink
|
|
sels = FreeCADGui.Selection.getSelection('',1,True)
|
|
if sels and isTypeOf(sels[0],(AsmConstraint,AsmElementLink)):
|
|
cls._active = True
|
|
else:
|
|
cls._active = False
|
|
|
|
@classmethod
|
|
def onSelectionChange(cls,hasSelection):
|
|
cls._active = None if hasSelection else False
|
|
|
|
class AsmCmdGotoLinked(AsmCmdBase):
|
|
_id = 20
|
|
_menuText = 'Select linked object'
|
|
_tooltip = 'Select the linked object'
|
|
_accel = 'A, G'
|
|
_toolbarName = ''
|
|
|
|
@classmethod
|
|
def getIconName(cls):
|
|
return 'LinkSelect'
|
|
|
|
@classmethod
|
|
def Activated(cls):
|
|
from .assembly import isTypeOf, \
|
|
AsmElement, AsmElementLink, AsmElementGroup
|
|
sels = FreeCADGui.Selection.getSelectionEx('',0,True)
|
|
if not sels:
|
|
return
|
|
if not sels[0].SubElementNames:
|
|
FreeCADGui.runCommand('Std_LinkSelectLinked')
|
|
return
|
|
subname = sels[0].SubElementNames[0]
|
|
obj = sels[0].Object.getSubObject(subname,retType=1)
|
|
if not isTypeOf(obj,AsmElementLink) and not isTypeOf(obj,AsmElement):
|
|
FreeCADGui.runCommand('Std_LinkSelectLinked')
|
|
return
|
|
import Part
|
|
subname = Part.splitSubname(subname)[0].split('.')
|
|
link = obj.LinkedObject
|
|
if isinstance(link,tuple):
|
|
linkSub = link[1]
|
|
link = link[0]
|
|
else:
|
|
linkSub = link.Name + '.'
|
|
link = obj.Proxy.getAssembly().getElementGroup()
|
|
if isTypeOf(obj,AsmElementLink):
|
|
subname = subname[:-4]
|
|
if not isTypeOf(link,AsmElementGroup):
|
|
subname.append('3')
|
|
else:
|
|
subname = subname[:-2]
|
|
subname[-1] = '2'
|
|
subname.append(link.Name)
|
|
subname = '.'.join(subname+linkSub.split('.'))
|
|
sobj = sels[0].Object.getSubObject(subname,retType=1)
|
|
if not sobj:
|
|
logger.error('Cannot find sub object {}.{}',
|
|
objName(sels[0].Object),subname)
|
|
return
|
|
FreeCADGui.Selection.pushSelStack()
|
|
FreeCADGui.Selection.clearSelection()
|
|
FreeCADGui.Selection.addSelection(sels[0].Object,subname)
|
|
FreeCADGui.Selection.pushSelStack()
|
|
FreeCADGui.runCommand('Std_TreeSelection')
|
|
|
|
@classmethod
|
|
def IsActive(cls):
|
|
return FreeCADGui.isCommandActive('Std_LinkSelectLinked')
|
|
|
|
class AsmCmdGotoLinkedFinal(AsmCmdBase):
|
|
_id = 23
|
|
_menuText = 'Select linked final'
|
|
_tooltip = 'Select the deepest linked object'
|
|
_accel = 'A, F'
|
|
_toolbarName = ''
|
|
|
|
@classmethod
|
|
def getIconName(cls):
|
|
return 'LinkSelectFinal'
|
|
|
|
@classmethod
|
|
def Activated(cls):
|
|
from .assembly import isTypeOf, AsmElement, AsmElementLink
|
|
sels = FreeCADGui.Selection.getSelectionEx('',0,True)
|
|
if not sels:
|
|
return
|
|
if not sels[0].SubElementNames:
|
|
FreeCADGui.runCommand('Std_LinkSelectLinkedFinal')
|
|
return
|
|
subname = sels[0].SubElementNames[0]
|
|
obj = sels[0].Object.getSubObject(subname,retType=1)
|
|
if not isTypeOf(obj,(AsmElementLink,AsmElement)):
|
|
FreeCADGui.runCommand('Std_LinkSelectLinkedFinal')
|
|
return
|
|
|
|
while True:
|
|
linked = obj.LinkedObject
|
|
if isinstance(linked,tuple):
|
|
subname = linked[1]
|
|
linked = linked[0]
|
|
else:
|
|
subname = ''
|
|
obj = linked.getSubObject(subname,retType=1)
|
|
if not isTypeOf(obj,AsmElement):
|
|
break
|
|
|
|
obj = obj.getLinkedObject(True)
|
|
|
|
import Part
|
|
subname = Part.splitSubname(subname)[-1]
|
|
|
|
FreeCADGui.Selection.pushSelStack()
|
|
FreeCADGui.Selection.clearSelection()
|
|
FreeCADGui.Selection.addSelection(obj,subname)
|
|
FreeCADGui.Selection.pushSelStack()
|
|
FreeCADGui.runCommand('Std_TreeSelection')
|
|
|
|
@classmethod
|
|
def IsActive(cls):
|
|
from .assembly import isTypeOf, AsmElement, AsmElementLink
|
|
sels = FreeCADGui.Selection.getSelectionEx('',0,True)
|
|
if len(sels)==1 and sels[0].SubElementNames:
|
|
obj = sels[0].Object.getSubObject(sels[0].SubElementNames[0],1)
|
|
if isTypeOf(obj, (AsmElementLink,AsmElement)):
|
|
return True
|
|
return FreeCADGui.isCommandActive('Std_LinkSelectLinkedFinal')
|
|
|
|
class AsmCmdUp(AsmCmdBase):
|
|
_id = 6
|
|
_menuText = 'Move item up'
|
|
_iconName = 'Assembly_TreeItemUp.svg'
|
|
|
|
@classmethod
|
|
def getSelection(cls):
|
|
from .assembly import isTypeOf, Assembly, AsmGroup
|
|
sels = FreeCADGui.Selection.getSelectionEx('',False)
|
|
if len(sels)!=1 or len(sels[0].SubElementNames)!=1:
|
|
return
|
|
ret= sels[0].Object.resolve(sels[0].SubElementNames[0])
|
|
obj,parent = ret[0],ret[1]
|
|
if isTypeOf(parent,Assembly) or not isTypeOf(parent,AsmGroup) or \
|
|
len(parent.Group) <= 1:
|
|
return
|
|
return (obj,parent,sels[0].Object,sels[0].SubElementNames[0])
|
|
|
|
@classmethod
|
|
def checkActive(cls):
|
|
cls._active = True if cls.getSelection() else False
|
|
|
|
@classmethod
|
|
def move(cls,step):
|
|
ret = cls.getSelection()
|
|
if not ret:
|
|
return
|
|
obj,parent,topParent,subname = ret
|
|
children = parent.Group
|
|
i = children.index(obj)
|
|
j = i+step
|
|
if j<0:
|
|
j = len(children)-1
|
|
elif j>=len(children):
|
|
j = 0
|
|
logger.debug('move {}:{} -> {}:{}',
|
|
i,objName(obj),j,objName(children[j]))
|
|
FreeCAD.setActiveTransaction(cls._menuText)
|
|
readonly = 'Immutable' in parent.getPropertyStatus('Group')
|
|
if readonly:
|
|
parent.setPropertyStatus('Group','-Immutable')
|
|
parent.Group = {i:children[j],j:obj}
|
|
if readonly:
|
|
parent.setPropertyStatus('Group','Immutable')
|
|
FreeCAD.closeActiveTransaction();
|
|
# The tree view may deselect the item because of claimChildren changes,
|
|
# so we restore the selection here
|
|
FreeCADGui.Selection.addSelection(topParent,subname)
|
|
|
|
@classmethod
|
|
def onSelectionChange(cls,hasSelection):
|
|
cls._active = None if hasSelection else False
|
|
|
|
@classmethod
|
|
def Activated(cls):
|
|
cls.move(-1)
|
|
|
|
|
|
class AsmCmdDown(AsmCmdUp):
|
|
_id = 7
|
|
_menuText = 'Move item down'
|
|
_iconName = 'Assembly_TreeItemDown.svg'
|
|
|
|
@classmethod
|
|
def Activated(cls):
|
|
cls.move(1)
|
|
|
|
|
|
class AsmCmdMultiply(AsmCmdBase):
|
|
_id = 18
|
|
_menuText = 'Multiply constraint'
|
|
_tooltip = 'Mutiply the part owner of the first element to constrain\n'\
|
|
'against the rest of the elements.\n\n'\
|
|
'To activate this function, the FIRST part must be of the\n'\
|
|
'FIRST element of a link array. In will optionally expand\n'\
|
|
'colplanar circular edges with the same radius in the second\n'\
|
|
'element on wards. To disable auto expansion, use NoExpand\n'\
|
|
'property in the element link.'
|
|
_iconName = 'Assembly_ConstraintMultiply.svg'
|
|
|
|
@classmethod
|
|
def checkActive(cls):
|
|
from .assembly import AsmConstraint
|
|
if logger.catchTrace('',AsmConstraint.makeMultiply,True):
|
|
cls._active = True
|
|
else:
|
|
cls._active = False
|
|
|
|
@classmethod
|
|
def Activated(cls):
|
|
from .assembly import AsmConstraint
|
|
logger.report('',AsmConstraint.makeMultiply)
|
|
|
|
@classmethod
|
|
def onSelectionChange(cls,hasSelection):
|
|
cls._active = None if hasSelection else False
|