
This allow cache of element geometry inside assembly which can serve as reminder in case of missing element.
902 lines
27 KiB
Python
902 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 = ''
|
|
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
|