From 821d7194fc85595682a573bda40d38d6b426b8a2 Mon Sep 17 00:00:00 2001 From: "Zheng, Lei" Date: Fri, 24 Aug 2018 17:27:29 +0800 Subject: [PATCH] gui: add New Element command --- assembly.py | 193 ++++++++++++++++++++++++++++++++-------------------- gui.py | 29 +++++++- 2 files changed, 145 insertions(+), 77 deletions(-) diff --git a/assembly.py b/assembly.py index 25539e7..4f6dd85 100644 --- a/assembly.py +++ b/assembly.py @@ -460,73 +460,77 @@ class AsmElement(AsmBase): # Group: the immediate child object of an assembly (i.e. ConstraintGroup, # ElementGroup, or PartGroup) # Subname: the subname reference realtive to 'Group' - Selection = namedtuple('AsmElementSelection',('Element','Group','Subname')) + Selection = namedtuple('AsmElementSelection',('Element','Group','Subname', + 'SelObj', 'SelSubname')) @staticmethod - def getSelection(): - ''' - Parse Gui.Selection for making an element + def getSelections(): + 'Parse Gui.Selection for making one or more elements' - If there is only one selection, then the selection must refer to a sub - element of some part object of an assembly. We shall create a new - element belonging to the top-level assembly - - If there are two selections, then first one shall be either the - element group or an individual element. The second selection shall - be a sub-element belong to a child assembly of the parent assembly of - the first selected element/element group - ''' sels = FreeCADGui.Selection.getSelectionEx('',False) if not sels: - return - if len(sels)>1: - raise RuntimeError( - 'The selections must have a common (grand)parent assembly') - - sel = sels[0] - subs = list(sel.SubElementNames) - if not subs: + raise RuntimeError('no selection') + if not sels[0].SubElementNames: raise RuntimeError('no sub-object in selection') - if len(subs)>2: - raise RuntimeError('At most two selection is allowed.\n' - 'The first selection must be a sub-element belonging to some ' - 'assembly. The optional second selection must be an element ' - 'belonging to the same assembly of the first selection') - if len(subs)==2: - if len(subs[0])1: + raise RuntimeError('too many selection') + hierarchies = [] + assembly = None element = None - if len(subs)>1: - ret = Assembly.findElementGroup(sel.Object,subs[1]) - if not ret: - raise RuntimeError('The second selection must be an element') + selObj = sels[0].Object + selSubname = None + for sub in sels[0].SubElementNames: + path = Assembly.findChildren(selObj,sub) + if not path: + raise RuntimeError('no assembly in selection {}.{}'.format( + objName(selObj),sub)) + if not path[-1].Object or \ + path[-1].Subname.index('.')+1==len(path[-1].Subname): + if assembly: + raise RuntimeError('invalid selection') + assembly = path[-1].Assembly + selSubname = sub[:-len(path[-1].Subname)] + continue - if ret.Assembly != link.Assembly: - raise RuntimeError( - 'The two selections must belong to the same assembly') + elif isTypeOf(path[-1].Object,AsmElementGroup) and \ + (not element or len(element)>len(path)): + if element: + hierarchies.append(element) + element = path + continue - element = ret.Object.getSubObject(ret.Subname,1) + hierarchies.append(path) + + if not hierarchies: + if not element: + raise RuntimeError('no element selection') + hierarchies.append(element) + element = None + + if element: + if len(hierarchies)>1: + raise RuntimeError('too many selections') + element = element[-1].Assembly.getSubObject( + element[-1].Subname,retType=1) if not isTypeOf(element,AsmElement): - raise RuntimeError('The second selection must be an element') + element = None - return AsmElement.Selection(Element=element, Group=link.Object, - Subname=link.Subname+subElement) + if not assembly: + path = hierarchies[0] + assembly = path[0].Assembly + selSubname = sels[0].SubElementNames[0][:-len(path[0].Subname)] + for i,hierarchy in enumerate(hierarchies): + for path in hierarchy: + if path.Assembly == assembly: + hierarchies[i] = AsmElement.Selection( + Element=element,Group=path.Object, + Subname=path.Subname[path.Subname.index('.')+1:], + SelObj=selObj, SelSubname=selSubname) + break + else: + raise RuntimeError('parent assembly mismatch') + return hierarchies @classmethod def create(cls,name,elements): @@ -538,10 +542,42 @@ class AsmElement(AsmBase): return element @staticmethod - def make(selection=None,name='Element',undo=False,radius=None): + def make(selection=None,name='Element',undo=False, + radius=None,allowDuplicate=False): '''Add/get/modify an element with the given selected object''' if not selection: - selection = AsmElement.getSelection() + sels = AsmElement.getSelections() + if len(sels)==1: + ret = [AsmElement.make(sels[0],name,undo,radius,allowDuplicate)] + else: + if undo: + FreeCAD.setActiveTransaction('Assembly create element') + try: + ret = [] + for sel in sels: + ret.append(AsmElement.make( + sel,name,False,radius,allowDuplicate)) + if undo: + FreeCAD.closeActiveTransaction() + if not ret: + return + except Exception: + if undo: + FreeCAD.closeActiveTransaction(True) + raise + + FreeCADGui.Selection.pushSelStack() + FreeCADGui.Selection.clearSelection() + for obj in ret: + if sels[0].SelSubname: + subname = sels[0].SelSubname + else: + subname = '' + subname += '1.{}.'.format(obj.Name) + FreeCADGui.Selection.addSelection(sels[0].SelObj,subname) + FreeCADGui.Selection.pushSelStack() + FreeCADGui.runCommand('Std_TreeSelection') + return ret group = selection.Group subname = selection.Subname @@ -553,7 +589,9 @@ class AsmElement(AsmBase): if not isTypeOf(element,AsmElement): raise RuntimeError('Invalid element reference {}.{}'.format( group.Name,subname)) - return element + if not allowDuplicate: + return element + group,subname = element.LinkedObject if isTypeOf(group,AsmConstraintGroup): # if the selected object is an element link of a constraint of the @@ -592,8 +630,8 @@ class AsmElement(AsmBase): # In case there are intermediate assembly inside subname, we'll # recursively export the element in child assemblies first, and # then import that element to the current assembly. - sel = AsmElement.Selection(Element=None, - Group=ret.Object, Subname=ret.Subname) + sel = AsmElement.Selection(SelObj=None,SelSubname=None, + Element=None, Group=ret.Object, Subname=ret.Subname) element = AsmElement.make(sel,radius=radius) radius=None @@ -629,16 +667,17 @@ class AsmElement(AsmBase): elements = group.Proxy.getAssembly().getElementGroup() idx = -1 if not element: - # try to search the element group for an existing element - for e in elements.Group: - if not e.Offset.isIdentity(): - continue - sub = logger.catch('',e.Proxy.getSubName) - if sub!=subname: - continue - r = getattr(e,'Radius',None) - if (not radius and not r) or radius==r: - return e + if not allowDuplicate: + # try to search the element group for an existing element + for e in elements.Group: + if not e.Offset.isIdentity(): + continue + sub = logger.catch('',e.Proxy.getSubName) + if sub!=subname: + continue + r = getattr(e,'Radius',None) + if (not radius and not r) or radius==r: + return e element = AsmElement.create(name,elements) if radius: element.addProperty('App::PropertyFloat','Radius','','') @@ -689,7 +728,8 @@ class ViewProviderAsmElement(ViewProviderAsmOnTop): if not elements: elements = [''] for element in elements: - AsmElement.make(AsmElement.Selection(Element=vobj.Object, + AsmElement.make(AsmElement.Selection( + SelObj=None, SelSubname=None, Element=vobj.Object, Group=owner, Subname=subname+element),undo=True) def doubleClicked(self,_vobj): @@ -1087,15 +1127,15 @@ class AsmElementLink(AsmBase): ret = Assembly.find(owner,subname) if not ret: # if not, add/get an element in our own element group - sel = AsmElement.Selection(Element=None, Group=owner, - Subname=subname) + sel = AsmElement.Selection(SelObj=None, SelSubname=None, + Element=None, Group=owner, Subname=subname) element = AsmElement.make(sel,radius=radius) owner = element.Proxy.parent.Object subname = '${}.'.format(element.Label) else: # if so, add/get an element from the sub-assembly - sel = AsmElement.Selection(Element=None, Group=ret.Object, - Subname=ret.Subname) + sel = AsmElement.Selection(SelObj=None, SelSubname=None, + Element=None, Group=ret.Object, Subname=ret.Subname) element = AsmElement.make(sel,radius=radius) owner = owner.Proxy.getAssembly().getPartGroup() @@ -1617,6 +1657,7 @@ class AsmConstraint(AsmGroup): undo = False if sel.SelObject: + FreeCADGui.Selection.pushSelStack() FreeCADGui.Selection.clearSelection() if sel.SelSubname: subname = sel.SelSubname @@ -1625,6 +1666,7 @@ class AsmConstraint(AsmGroup): subname += sel.Assembly.Proxy.getConstraintGroup().Name + \ '.' + cstr.Name + '.' FreeCADGui.Selection.addSelection(sel.SelObject,subname) + FreeCADGui.Selection.pushSelStack() FreeCADGui.runCommand('Std_TreeSelection') return cstr @@ -1893,6 +1935,7 @@ class ViewProviderAsmElementGroup(ViewProviderAsmGroup): elements = [''] for element in elements: obj = AsmElement.make(AsmElement.Selection( + SelObj=None, SelSubname=None, Element=None, Group=owner, Subname=subname+element)) if obj and sel: FreeCADGui.Selection.addSelection(sel.Object, diff --git a/gui.py b/gui.py index deee02c..59c4648 100644 --- a/gui.py +++ b/gui.py @@ -1,5 +1,6 @@ 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 @@ -143,7 +144,6 @@ class AsmCmdManager(ProxyType): if not hgrp.GetBool(toolbar,True): show = True break - from PySide import QtGui mw = FreeCADGui.getMainWindow() for toolbar in mcs._HiddenToolbars: if show != hgrp.GetBool(toolbar,True): @@ -247,6 +247,31 @@ class AsmCmdNew(AsmCmdBase): from . import assembly assembly.Assembly.make() + +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 AsmCmdSolve(AsmCmdBase): _id = 1 _menuText = 'Solve constraints' @@ -660,7 +685,7 @@ class AsmCmdDown(AsmCmdUp): cls.move(1) -class ASmCmdMultiply(AsmCmdBase): +class AsmCmdMultiply(AsmCmdBase): _id = 18 _menuText = 'Multiply constraint' _tooltip = 'Mutiply the part owner of the first element to constrain\n'\