def compoundFromAssembly(root, flatten, exclude, recursive = True, visit_set = None): if visit_set is None: visit_set = set() #recursion guard if root in visit_set: raise ValueError("Circular dependency") visit_set.add(root) if hasattr(root, 'Shape'): return root.Shape else: children = Containers.getDirectChildren(root) shapes = [] for child in children: if child in exclude: continue if child.isDerivedFrom('App::Origin'): continue #otherwise, origins create empty compounds - undesirable. if hasattr(child, 'Shape'): shapes.append(child.Shape) elif Containers.isContainer(child) and recursive: cmp = compoundFromAssembly(child, flatten, exclude, recursive, visit_set) if flatten: shapes.extend(cmp.childShapes()) else: shapes.append(cmp) transform = root.Placement if hasattr(root, 'Placement') else None ret = Part.makeCompound(shapes) if transform is not None: ret.Placement = transform return ret
def RunOrTest(self, b_run): sel = Gui.Selection.getSelection() if len(sel)==0 : ac = Containers.activeContainer() if not hasattr(ac, "Tip"): raise CommandError(self,u"{cnt} can't have Tip object (it is not a module or a body).".format(cnt= ac.Label)) if type(ac.Tip) is list: if Gui.ActiveDocument.getInEdit() is not None: raise CommandError(self,"Please leave editing mode.") if b_run: Gui.ActiveDocument.setEdit(ac, 0) return raise CommandError(self, "Set as Tip command. Please select an object to become Tip, first. The object must be geometry. ") elif len(sel)==1: sel = screen(sel[0]) ac = Containers.getContainer(sel) if not hasattr(ac, "Tip"): raise CommandError(self,u"{cnt} can't have Tip object (it is not a module or a body).".format(cnt= ac.Label)) if type(ac.Tip) is list: if Gui.ActiveDocument.getInEdit() is not None: raise CommandError(self,"Please leave editing mode.") if b_run: Gui.ActiveDocument.setEdit(ac, 0) else: if not sel in Containers.getDirectChildren(ac): raise CommandError(self, u"{feat} is not from active container ({cnt}). Please select an object belonging to active container.".format(feat= sel.Label, cnt= ac.Label)) if screen(ac.Tip) is sel: raise CommandError(self, u"{feat} is already a Tip of ({cnt}).".format(feat= sel.Label, cnt= ac.Label)) if b_run: ac.Tip = sel else: raise CommandError(self, u"Set as Tip command. You need to select exactly one object (you selected {num}).".format(num= len(sel)))
def RunOrTest(self, b_run): sel = Gui.Selection.getSelection() if len(sel)==0 : raise CommandError(self,"No object selected. Please select objects to duplicate, first.") else: if b_run: (full_list, top_list, implicit_list) = Containers.expandList(sel) duplicateObjects(full_list, top_list, Containers.activeContainer())
def slotDeletedObject(self, feature): print (u"deleted {feat}. container chain: {chain}" .format(feat= feature.Name, chain= ".".join([cnt.Name for cnt in GT.getContainerChain(feature)]))) ac = activeContainer() if feature in GT.getContainerChain(ac)+[ac]: # active container was deleted. Need to leave it ASAP, as long as we can access the chain setActiveContainer(GT.getContainer(feature)) self.poll()
def RunOrTest(self, b_run): if b_run: sel = set(Gui.Selection.getSelection()) children = Containers.getDirectChildren(Containers.activeContainer()) for child in children: if child in sel: Gui.Selection.removeSelection(child) else: Gui.Selection.addSelection(child)
def RunOrTest(self, b_run): sel = Gui.Selection.getSelection() if len(sel)==0 : raise CommandError(self,"No object selected. Please select an object from another container, first.") elif len(sel)==1: sel = sel[0] cnt = Containers.getContainer(sel) if cnt is Containers.activeContainer(): raise CommandError(self, "This object is already in active container. Please select an object that is not in active container, or activate another container.") if b_run: TransferObject([sel], Containers.activeContainer()) else: # multiple selection. Checking is involved. Let's just assume it's correct. if b_run: TransferObject(sel, Containers.activeContainer())
def RunOrTest(self, b_run): sel = Gui.Selection.getSelection() if len(sel)==0 : raise CommandError(self, "MUX Assembly command. Please select a Part container, then invoke the tool. A mux object will be created, a compound of all features of Part.") elif len(sel)==1: sel = sel[0] ac = Containers.activeContainer() if b_run: if sel in (Containers.getAllDependent(ac)+ [ac]): raise CommandError(self, "Can't create MUX here, because a circular dependency will result.") Create(sel) else: raise CommandError(self, u"MUX Assembly command. You need to select exactly one object (you selected {num}).".format(num= len(sel)))
def RunOrTest(self, b_run): sel = Gui.Selection.getSelection() if len(sel)==0 : raise CommandError(self,"No object selected. Please select something, first.") else: if b_run: new_sel = [] for obj in sel: if Containers.isContainer(obj): new_sel.extend(Containers.getDirectChildren(obj)) else: new_sel.extend(obj.ViewObject.claimChildren()) select(new_sel)
def RunOrTest(self, b_run): sel = Gui.Selection.getSelection() if len(sel)==0 : raise CommandError(self, "Shapebinder command. Please select an object to import to active container, first. The object must be geometry.") elif len(sel)==1: sel = screen(sel[0]) ac = Containers.activeContainer() if sel in Containers.getDirectChildren(ac): raise CommandError(self, u"{feat} is from active container ({cnt}). Please select an object belonging to another container.".format(feat= sel.Label, cnt= ac.Label)) if sel in (Containers.getAllDependent(ac)+ [ac]): raise CommandError(self, "Can't create a shapebinder, because a circular dependency will result.") if b_run: CreateShapeBinder(sel) else: raise CommandError(self, u"Shapebinder command. You need to select exactly one object (you selected {num}).".format(num= len(sel)))
def duplicateObjects(objects, top_objects, target_cnt): with Transaction("PoM Duplicate"): keeper = Observer.suspend() objset = set(objects) doc = objects[0].Document namelist = [obj.Name for obj in doc.TopologicalSortedObjects if obj in objset][::-1] tmp_prj = FCProject.fromFC(doc, namelist) map = tmp_prj.mergeToFC(doc) for obj in top_objects: new_top_obj = doc.getObject(map[obj.Name]) Containers.addObjectTo(target_cnt, new_top_obj, b_advance_tip= True) keeper.release()
def enterContainer(self, cnt): '''enterContainer(self, cnt): when cnt either directly is being activated, or one of its child containers is being activated. Assumes container of cnt is already entered.''' print ("entering "+cnt.Name) if cnt.isDerivedFrom("App::Document"): # may happen when creating new document. Ignoring. return key = cnt.Document.Name+"."+cnt.Name if key in self.TVs: # just in case old tempovis associated with the container wasn't removed for some reason. self.TVs[key].forget() tv = TempoVis(cnt.Document) self.TVs[key] = tv list_hiding = [o for o in GT.getDirectChildren(GT.getContainer(cnt)) if not o is cnt] list_hiding = [o for o in list_hiding if not o.isDerivedFrom('App::DocumentObjectGroup')] # don't touch visibility of groups just yet... tv.hide(list_hiding) tv.show(cnt)
def activeContainerChanged(self, oldContainer, newContainer): n1 = "None" n2 = "None" if oldContainer: n1 = oldContainer.Name if newContainer: n2 = newContainer.Name print(u"container changed from {c1} to {c2}".format(c1= n1, c2= n2)) if oldContainer is None: #happens when creating new document return chain_from, chain_to = GT.getContainerRelativePath(oldContainer, newContainer) for cnt in chain_from[::-1]: try: gc = GenericContainer(cnt) gc.ViewObject.call(gc.ViewObject.activationChanged, oldContainer, newContainer, event= -1) except Exception as err: App.Console.PrintError(u"Error deactivating container '{cnt}': {err}".format(cnt= cnt.Label, err= err.message)) self.leaveContainer(cnt) for cnt in chain_to: try: gc = GenericContainer(cnt) gc.ViewObject.call(gc.ViewObject.activationChanged, oldContainer, newContainer, event= +1) except Exception as err: App.Console.PrintError(u"Error activating container '{cnt}': {err}".format(cnt= cnt.Label, err= err.message)) self.enterContainer(cnt) self.updateVPs()
def fillList(self): lw = self.form.treeR lw.clear() lw.setColumnCount(len(self.columns)) lw.setHeaderLabels(self.column_titles) for repl in self.replacements: item = QtGui.QTreeWidgetItem(lw) item.setText(self.column['Kind'], repl.relation.kind) item.setText(self.column['Object'], repl.relation.linking_object.Label) item.setText(self.column['Property'], repl.relation.linking_property) try: chain = Containers.getContainerChain(repl.relation.linking_object)[1:] + [repl.relation.linking_object] #[1:] strips off project name, to unclutter the column. path = '.'.join([cnt.Name for cnt in chain]) except Exception as err: import traceback tb = traceback.format_exc() App.Console.PrintError(tb+'\n\n') path = "!" + str(err) item.setText(self.column['Path'], path) item.setText(self.column['Value'], str(repl.relation.value_repr)) repl.gui_item = item item.setData(0,256,repl) flags = Qt.ItemIsUserCheckable | Qt.ItemIsSelectable if not repl.disabled: flags = flags | Qt.ItemIsEnabled item.setFlags(flags) item.setCheckState(0, Qt.Checked if repl.checked else Qt.Unchecked) if repl.disabled: item.setText(self.column['State'], repl.disabled_reason)
def updateVPs(self): '''updates many viewprovider properties (except visibility, which is handled by TempoVis objets)''' ac = activeContainer() if ac.isDerivedFrom("App::Document"): objects_in = set(App.ActiveDocument.Objects) else: objects_in = set( GT.getDirectChildren(ac) ) objects_out = set(App.ActiveDocument.Objects) - objects_in # make all object in context selectable. This is mainly to undo consequences of old behavior of making everything out-of-context non-selectable for o in objects_in: if hasattr(o.ViewObject, "Selectable"): o.ViewObject.Selectable = True for o in App.ActiveDocument.findObjects("App::Origin"): o.ViewObject.Visibility = GT.getContainer(o) is ac
def RunOrTest(self, b_run): sel = Gui.Selection.getSelection() if len(sel)==0 : raise CommandError(self,"No object selected. Please select something, first.") else: if b_run: (full_list, top_list, implicit_list) = Containers.expandList(sel) select(implicit_list)
def RunOrTest(self, b_run): # outline. # 0. user click calls Activated(). # 1. set up self.waiter, which waits for new object to be created # 2. waiter calls Activated() of this command, and knows what object was created # 3. command removes waiter, and calls morphContainer if self.waiter is not None: if self.waiter.is_done: waiter = self.waiter self.waiter = None assert(b_run) #enforce PoM observer to put feature into appropriate container from PartOMagic.Gui import Observer as pomObserver if pomObserver.isRunning(): pomObserver.observerInstance.poll() #check if not Containers.isContainer(waiter.target_container): raise CommandError(self, u"You created {obj}, which isn't a container. Can't morph {src} into {objt}. Morphing canceled." .format(obj= waiter.target_container, objt= waiter.target_container, src= waiter.source_container)) #make sure active container is not being messed with... ac = Containers.activeContainer() if waiter.source_container in Containers.getContainerChain(ac)+[ac]: pomObserver.activateContainer(Containers.getContainer(waiter.source_container)) if waiter.target_container in Containers.getContainerChain(ac)+[ac]: pomObserver.activateContainer(Containers.getContainer(waiter.target_container)) # do it! with Transaction("Morph container"): morphContainer(waiter.source_container, waiter.target_container) return else: raise CommandError(self, "(waiting for new container to be created...)") sel = Gui.Selection.getSelection() if len(sel)==0 : raise CommandError(self, self.GetResources()['ToolTip']) elif len(sel)==1: sel = sel[0] if not Containers.isContainer(sel): raise CommandError(self, "Selected object is not a container") ac = Containers.activeContainer() if sel in Containers.getContainerChain(ac)+[ac]: raise CommandError(self, "Deactivate the container to be morphed first, please.") if b_run: self.waiter = WaitForNewContainer(self, sel) if b_run: Gui.Selection.clearSelection() else: raise CommandError(self, u"You need to select exactly one object (you selected {num}), ad it must be a container.".format(num= len(sel)))
def RunOrTest(self, b_run): if Gui.ActiveDocument.getInEdit() is not None: if b_run: Gui.ActiveDocument.resetEdit() App.ActiveDocument.recompute() App.ActiveDocument.commitTransaction() elif Gui.Control.activeDialog(): if b_run: Gui.Control.closeDialog() App.ActiveDocument.recompute() App.ActiveDocument.commitTransaction() else: ac = Containers.activeContainer() if ac.isDerivedFrom("App::Document"): raise CommandError(self, "Nothing to leave.") if b_run: Containers.setActiveContainer(Containers.getContainer(ac)) if b_run: Gui.Selection.clearSelection() if b_run: Gui.Selection.addSelection(ac) if b_run: App.ActiveDocument.recompute() #fixme: scoped recompute, maybe?
def check_dag(self): """check_dag(): returns True if DAG-compatible, and False otherwise. Note that it may be wrong for mass-replacements""" if replaced: raise AlreadyReplacedError("Replacement already done, can't check.") if self.new_object is None: return True import Containers deps = Containers.getAllDependent(self.relation.linking_object) if self.new_object in deps: return False return True
def containerAdded(self): App.removeDocumentObserver(self) for obj in self.new_objects: if Containers.canBeActive(obj): if self.target_container is not None: raise CommandError(self.command, "More than one container was created. I have no idea what to do! --Part-o-Magic") self.target_container = obj #if multiple objects had been created, pick one that is a container if self.target_container is None: self.target_container = self.new_objects[0] #return something... so that an error message is displayed. self.is_done = True self.command.Activated()
def trackExpands(self): docname = App.ActiveDocument.Name for obj in App.ActiveDocument.Objects: objname = obj.Name key = (docname, objname) curstate = 'Expanded' in obj.State oldstate = self.expandedness.get(key, False) if curstate != oldstate: self.expandedness[key] = curstate if GT.isContainer(obj): gc = GenericContainer(obj) gc.ViewObject.call(gc.ViewObject.expandednessChanged, oldstate, curstate)
def addObjectTo(container, feature): if hasattr(App, 'ActiveContainer'): # New FreeCAD. Feature is added to the container automatically. All that's left to do is call advanceTip(). return container if container.isDerivedFrom("App::Document"): return container # close editing before addition. # Adding to container while editing causes editing to close anyway. But we want do do # that ourself, so that we know that we need to re-open it. bool_editingclosed = False if hasattr(Gui.ActiveDocument,"getInEdit"): if Gui.ActiveDocument.getInEdit(): if Gui.ActiveDocument.getInEdit().Object is feature: Gui.ActiveDocument.resetEdit() bool_editingclosed = True #actual addition added = False actual_container = container while not added: try: GT.addObjectTo(actual_container, feature, b_advance_tip = False) added = True except App.Base.FreeCADError as err: #assuming it's a "not allowed" error #try adding to upper level container. Until Document is reached, which should never fail with FreeCADError actual_container = GT.getContainer(actual_container) if actual_container is not container: msgbox("Part-o-magic",u"Cannot add the new object of type {typ} to '{container}'. Feature added to '{actual_container}' instead." .format(container= container.Label, actual_container= actual_container.Label, typ= feature.TypeId)) # re-open editing that we had closed... if bool_editingclosed: Gui.ActiveDocument.setEdit(feature) return actual_container
def enterContainer(self, cnt): '''enterContainer(self, cnt): when cnt either directly is being activated, or one of its child containers is being activated. Assumes container of cnt is already entered.''' print("entering " + cnt.Name) if cnt.isDerivedFrom( "App::Document" ): # may happen when creating new document. Ignoring. return key = cnt.Document.Name + "." + cnt.Name if key in self.TVs: # just in case old tempovis associated with the container wasn't removed for some reason. self.TVs[key].forget() tv = TempoVis(cnt.Document) self.TVs[key] = tv list_hiding = [ o for o in GT.getDirectChildren(GT.getContainer(cnt)) if not o is cnt ] list_hiding = [ o for o in list_hiding if not o.isDerivedFrom('App::DocumentObjectGroup') ] # don't touch visibility of groups just yet... tv.hide(list_hiding) tv.show(cnt)
def setEdit(self, selfvp, mode): print("ShapeGroup enter edit mode {num}".format(num= mode)) if mode == 0: try: selfobj = selfvp.Object #ensure right container is active from PartOMagic.Base import Containers container = Containers.getContainer(selfobj) if Containers.activeContainer() is not Containers.getContainer(selfobj): from PartOMagic.Gui import Observer Observer.activateContainer(container) selfobj.Document.openTransaction(u'Edit Tip of {sg}'.format(sg= selfobj.Label)) #prepare scene from PartOMagic.Gui.TempoVis import TempoVis self.tv = TempoVis(selfobj.Document) tv = self.tv #update visibilities of children to match what's in tip children = selfobj.Group tip = selfobj.Tip for child in children: tv.modifyVPProperty(child, 'Visibility', child in tip) #ensure children are visible, and nothing else... tv.modifyVPProperty(selfobj, 'DisplayMode','Group') tv.show(selfobj) for child in Containers.getDirectChildren(container): if child is not selfobj: tv.hide(child) #start editing self.observer = VisibilityObserver(selfobj.Group, self.editCallback) except Exception as err: App.Console.PrintError(u"Error in ShapeGroup setEdit: {err}\n".format(err= err.message)) return False return True raise NotImplementedError()
def compoundFromAssembly(root, flatten, exclude, recursive=True, visit_set=None): if visit_set is None: visit_set = set() #recursion guard if root in visit_set: raise ValueError("Circular dependency") visit_set.add(root) if hasattr(root, 'Shape'): return root.Shape else: children = Containers.getDirectChildren(root) shapes = [] for child in children: if child in exclude: continue if child.isDerivedFrom('App::Origin'): continue #otherwise, origins create empty compounds - undesirable. if hasattr(child, 'Shape'): shapes.append(child.Shape) elif Containers.isContainer(child) and recursive: cmp = compoundFromAssembly(child, flatten, exclude, recursive, visit_set) if flatten: shapes.extend(cmp.childShapes()) else: shapes.append(cmp) transform = root.Placement if hasattr(root, 'Placement') else None ret = Part.makeCompound(shapes) if transform is not None: ret.Placement = transform return ret
def slotStartEditing(self, feature): print(u"Start Editing {f}".format(f=feature.Name)) cnt = GT.getContainer(feature) # if GT.activeContainer() is not cnt: # print(u"Feature being edited is not in active container. Activating {cnt}...".format(cnt= cnt.Name)) # Gui.ActiveDocument.resetEdit() # GT.setActiveContainer(cnt) # return if feature.isDerivedFrom("PartDesign::Boolean"): # show all bodies nearby... part = GT.getContainer(cnt) children = GT.getDirectChildren(part) children = [ child for child in children if child.isDerivedFrom("Part::BodyBase") ] children = set(children) children.remove(cnt) for obj in GT.getAllDependent(cnt): if obj in children: children.remove(obj) tv = TempoVis(feature.Document) tv.show(children) self.edit_TVs[feature.Document.Name] = tv
def onDelete(self, viewprovider, subelements): # subelements is a tuple of strings try: selfobj = self.Object import PartOMagic.Base.Containers as Containers body = Containers.getContainer(selfobj) if not body.isDerivedFrom('PartDesign::Body'): return if self.ViewObject.Visibility and selfobj.BaseFeature: selfobj.BaseFeature.ViewObject.show() body.removeObject(selfobj) except Exception as err: App.Console.PrintError("Error in onDelete: " + err.message) return True
def containerAdded(self): App.removeDocumentObserver(self) for obj in self.new_objects: if Containers.canBeActive(obj): if self.target_container is not None: raise CommandError( self.command, "More than one container was created. I have no idea what to do! --Part-o-Magic" ) self.target_container = obj #if multiple objects had been created, pick one that is a container if self.target_container is None: self.target_container = self.new_objects[ 0] #return something... so that an error message is displayed. self.is_done = True self.command.Activated()
def activeContainerChanged(self, oldContainer, newContainer): n1 = "None" n2 = "None" if oldContainer: n1 = oldContainer.Name if newContainer: n2 = newContainer.Name print(u"container changed from {c1} to {c2}".format(c1=n1, c2=n2)) if oldContainer is None: #happens when creating new document return chain_from, chain_to = GT.getContainerRelativePath( oldContainer, newContainer) for cnt in chain_from[::-1]: try: gc = GenericContainer(cnt) gc.ViewObject.call(gc.ViewObject.activationChanged, oldContainer, newContainer, event=-1) except Exception as err: App.Console.PrintError( u"Error deactivating container '{cnt}': {err}".format( cnt=cnt.Label, err=err.message)) self.leaveContainer(cnt) for cnt in chain_to: try: gc = GenericContainer(cnt) gc.ViewObject.call(gc.ViewObject.activationChanged, oldContainer, newContainer, event=+1) except Exception as err: App.Console.PrintError( u"Error activating container '{cnt}': {err}".format( cnt=cnt.Label, err=err.message)) self.enterContainer(cnt) self.updateVPs()
def RunOrTest(self, b_run): if Gui.ActiveDocument: in_edit = Gui.ActiveDocument.getInEdit() if in_edit is not None: raise CommandError(self, u"{object} is currently being edited. Can't enter anything.".format(object= in_edit.Object.Label)) sel = Gui.Selection.getSelection() if len(sel)==0 : raise CommandError(self, "Enter Object command. Please select an object to enter, first. It can be a container, or a sketch.") elif len(sel)==1: sel = screen(sel[0]) ac = Containers.activeContainer() if Containers.isContainer(sel): if sel in Containers.getContainerChain(ac) + [ac]: raise CommandError(self, "Already inside this object") if b_run: Containers.setActiveContainer(sel) if b_run: Gui.Selection.clearSelection() else: cnt = Containers.getContainer(sel) if ac is cnt: if b_run: Gui.ActiveDocument.setEdit(sel) else: if b_run: Containers.setActiveContainer(cnt) else: raise CommandError(self, u"Enter Object command. You need to select exactly one object (you selected {num}).".format(num= len(sel)))
def slotStartEditing(self, feature): print(u"Start Editing {f}".format(f= feature.Name)) cnt = GT.getContainer(feature) if GT.activeContainer() is not cnt: print(u"Feature being edited is not in active container. Activating {cnt}...".format(cnt= cnt.Name)) Gui.ActiveDocument.resetEdit() GT.setActiveContainer(cnt) return if feature.isDerivedFrom("PartDesign::Boolean"): # show all bodies nearby... part = GT.getContainer(cnt) children = GT.getDirectChildren(part) children = [child for child in children if child.isDerivedFrom("Part::BodyBase")] children = set(children) children.remove(cnt) for obj in GT.getAllDependent(cnt): if obj in children: children.remove(obj) tv = TempoVis(feature.Document) tv.show(children) self.edit_TVs[feature.Document.Name] = tv
def fillList(self): lw = self.form.treeR lw.clear() lw.setColumnCount(len(self.columns)) lw.setHeaderLabels(self.column_titles) for repl in self.replacements: item = QtGui.QTreeWidgetItem(lw) item.setText(self.column['Kind'], repl.relation.kind) item.setText(self.column['Object'], repl.relation.linking_object.Label) item.setText(self.column['Property'], repl.relation.linking_property) try: chain = Containers.getContainerChain( repl.relation.linking_object)[1:] + [ repl.relation.linking_object ] #[1:] strips off project name, to unclutter the column. path = '.'.join([cnt.Name for cnt in chain]) except Exception as err: import traceback tb = traceback.format_exc() App.Console.PrintError(tb + '\n\n') path = "!" + str(err) item.setText(self.column['Path'], path) item.setText(self.column['Value'], str(repl.relation.value_repr)) repl.gui_item = item item.setData(0, 256, repl) flags = Qt.ItemIsUserCheckable | Qt.ItemIsSelectable if not repl.disabled: flags = flags | Qt.ItemIsEnabled item.setFlags(flags) item.setCheckState(0, Qt.Checked if repl.checked else Qt.Unchecked) if repl.disabled: item.setText(self.column['State'], repl.disabled_reason)
def expandednessChanged(self, old_state, new_state): ac = Containers.activeContainer() activeChain = Containers.getContainerChain(ac) + [ac] self.call( self.doDisplayModeAutomation, ac, ac, +1 if new_state == True else (0 if self.selfobj in activeChain else -1))
def getPartOf(feature): chain = GT.getContainerChain(feature) for cnt in chain[::-1]: if not cnt.isDerivedFrom("PartDesign::Body"): return cnt assert(False)
def slotDeletedObject(self, feature): ac = activeContainer() if feature in GT.getContainerChain(ac)+[ac]: # active container was deleted. Need to leave it ASAP, as long as we can access the chain setActiveContainer(GT.getContainer(feature)) self.poll()
def doubleClicked(self, vobj): Containers.setActiveContainer(Containers.getContainer( vobj.Object.Base)) return True
def addObjectVars(obj): vardict['object_name' ] = obj.Name vardict['object_label' ] = obj.Label vardict['object_container_label' ] = Containers.getContainer(obj).Label vardict['object_container_name' ] = Containers.getContainer(obj).Name
def morphContainer(src_container, dst_container): if not Containers.isContainer(src_container): raise TypeError(u"{obj} is not a container".format(obj= src_container.Label)) if not Containers.isContainer(dst_container): raise TypeError(u"{obj} is not a container".format(obj= dst_container.Label)) if src_container in Containers.getContainerChain(dst_container): raise Containers.ContainerTreeError(u"Cannot morph {src} into {dst}, because {src} contains {dst}" .format(src= src_container.Label, dst= dst_container.Label)) doc = dst_container.Document #origin... if hasattr(src_container, "Origin") and hasattr(dst_container, "Origin"): if dst_container.Origin is not None: doc.removeObject(dst_container.Origin.Name) dst_container.Origin = src_container.Origin; src_container.Origin = None #content assert(len(dst_container.Group) == 0) g = src_container.Group src_container.Group = [] #withdraw first, add last - otherwise, error is thrown dst_container.Group = g if hasattr(dst_container, 'Origin'): if hasattr(dst_container, 'Proxy') and dst_container.Origin is not None: #workaround for origin not being claimed as child on Py-powered containers dst_container.Group = [dst_container.Origin] + dst_container.Group elif dst_container.Origin is not None and dst_container.Origin in dst_container.Group: #if converting Py cotainer into c++-one, undo the workaround content = dst_container.Group content.remove(dst_container.Origin) #Tip if hasattr(src_container, "Tip") and hasattr(dst_container, "Tip"): tip_list = src_container.Tip if type(tip_list) is not list: tip_list = [tip_list] if tip_list is not None else [] if type(dst_container.Tip) is list: dst_container.Tip = tip_list else: if len(tip_list) == 0: dst_container.Tip = None elif len(tip_list) == 1: dst_container.Tip = tip_list[0] else: App.Console.PrintWarning(u"Target Tip can only point to one object. Source Tip points to {num}. Last object from source tip was taken.\n" .format(num= len(tip_list))) dst_container.Tip = tip_list[-1] #other properties... properties_to_avoid = set([ # these are avoided because they need manual treatment... 'Group', 'Origin', 'ExpressionEngine', 'Tip', 'Label', #.. and these should never be copied at all 'ExtensionProxy', 'Proxy', ]) properties_to_copy = [] for prop in src_container.PropertiesList: if len(src_container.getEditorMode(prop)) == 0: # screen out read-only and hidden properties if not prop in properties_to_avoid: properties_to_copy.append(prop) print("copying properties") for prop in properties_to_copy: print(" "+prop) copyProperty(src_container, dst_container, prop) #Copy expressions for expr in src_container.ExpressionEngine: dst_container.setExpression(*expr) #redirect links substituteObjectInProperties(src_container, dst_container, doc.Objects) substituteObjectInExpressions(src_container, dst_container, doc.Objects) substituteObjectInSpreadsheets(src_container, dst_container, doc.Objects) label = src_container.Label label = label.replace(src_container.Name, dst_container.Name) Containers.withdrawObject(src_container) doc.removeObject(src_container.Name) #copy Label last, because otherwise it gets "001" at the end... dst_container.Label = label
def RunOrTest(self, b_run): if b_run: children = Containers.getDirectChildren( Containers.activeContainer()) select(children)
def TransferObject(objects, target_cnt): with Transaction("Transfer object"): for obj in objects: Containers.moveObjectTo(obj, target_cnt)
def _replace(self, check_dag): # does the business of replacing. But does not set self.replaced flag, so that I can use return statement without caution. prop = self.relation.linking_property new = self.new_object obj = self.relation.linking_object kind = self.relation.kind printLog(u" replacing: {self}\n".format(self=self)) if check_dag: if self.check_dag() == False: raise DAGError( u"Replacement will cause a cycle: {repl}".format( repl=repr(self))) try: self.relation.self_check() except ExpressionGoneError: if new is None: return #during mass-replacements with no new object, expression may have been gone already as a result of previous replacement. Skip this. raise if kind == 'CellExpression': if self.relation.linked_object is self.new_object: printWarn( u"Replacement invoked, but nothing to do. {self}".format( self=repr(self))) return #nothing to do range = self.relation.expression_charrange oldexpr = obj.getContents(prop) #raises ValueError if not a cell if len(oldexpr) == 0 and new is None: return #during mass-replacements with no new object, expression may have been gone already as a result of previous replacement. Skip this. if not oldexpr.startswith('='): raise ReplacementError("No expression found for replacement") if new is not None: newexpr = oldexpr[0:range[0]] + new.Name + oldexpr[range[1]:] else: newexpr = '' if newexpr is not None: printLog(u" '{oldexpr}' -> '{newexpr}'\n".format( oldexpr=oldexpr, newexpr=newexpr)) obj.set(prop, newexpr) elif kind == 'Expression': if self.relation.linked_object is self.new_object: printWarn( u"Replacement invoked, but nothing to do. {self}".format( self=repr(self))) return #nothing to do ee = dict(obj.ExpressionEngine) if not prop in ee and new is None: return #during mass-replacements with no new object, expression may have been gone already as a result of previous replacement. Skip this. if not prop in ee: raise ReplacementError("No expression found for replacement") oldexpr = ee[prop] range = self.relation.expression_charrange if new is not None: newexpr = oldexpr[0:range[0]] + new.Name + oldexpr[range[1]:] else: newexpr = '' if newexpr is not None: printLog(u" '{oldexpr}' -> '{newexpr}'\n".format( oldexpr=oldexpr, newexpr=newexpr)) obj.setExpression(prop, newexpr) elif kind == 'Child' or kind == 'Link': if self.relation.linked_object is self.new_object: printWarn( u"Replacement invoked, but nothing to do. {self}".format( self=repr(self))) return #nothing to do if kind == 'Child': #when replacing child links, make sure the new object is not in a container. Otherwise FreeCAD throws an error. from PartOMagic.Base import Containers Containers.withdrawObject(new) typ = obj.getTypeIdOfProperty(prop) val = getattr(obj, prop) if typ.startswith('App::PropertyLinkList'): val = val[0:self.relation.list_index] + ( [] if new is None else [new]) + val[self.relation.list_index + 1:] elif typ.startswith('App::PropertyLink'): val = new else: raise TypeError( u"Unexpected type of property: {typ}".format(typ=typ)) setattr(obj, prop, val) elif kind == 'Sublink': typ = obj.getTypeIdOfProperty(prop) val = getattr(obj, prop) if self.relation.linked_object is self.new_object and ( self.new_sublist is None or self.relation.sublist == self.new_sublist): printWarn( u'Replacement invoked, but nothing to do. {self}'.format( self=repr(self))) return #nothing to do sublist = self.new_sublist if self.new_sublist is not None else self.relation.sublist if typ.startswith('App::PropertyLinkSubList'): val = val[0:self.relation.list_index] + [ (new, sublist) ] + val[self.relation.list_index + 1:] elif typ.startswith('App::PropertyLinkSub'): val = (new, sublist) else: raise TypeError( u"Unexpected type of property: {typ}".format(typ=typ)) setattr(obj, prop, val) else: raise TypeError( u"Unexpected kind of dependency: {kind}".format(kind=kind))
def export(self, selfobj): #check the model from PartOMagic.Base import Containers for obj in Containers.getAllDependencies(selfobj): if 'Invalid' in obj.State: raise RuntimeError(u"File not exported, because {feat} is in error state.".format(feat= obj.Label)) #form absolute path filepath = selfobj.FilePath from os import path extension = path.splitext(filepath)[1][1:] if len(extension)<1: raise ValueError("File has no extension, can't determine export type.") self.fetchModule(selfobj) import importlib mod = importlib.import_module(selfobj.UsingModule) if not path.isabs(filepath): if len(selfobj.Document.FileName)==0: raise ValueError("Can't save to a relative path, because the project is not saved to a file.") context = path.dirname(selfobj.Document.FileName) filepath = path.join(context, filepath) #collect what to export objects_to_export = [] containermode = selfobj.ContainerMode if hasattr(selfobj.ObjectToExport, "Group"): if containermode == '(Auto)': if hasattr(selfobj.ObjectToExport, 'Shape'): containermode = 'Feed straight to exporter' elif hasattr(selfobj.ObjectToExport, 'Tip'): containermode = 'Feed tip features to exporter' else: containermode = 'Feed all children' selfobj.ContainerMode = containermode if containermode == 'Feed straight to exporter': objects_to_export.append(selfobj.ObjectToExport) elif containermode == 'Feed tip features to exporter': objects_to_export += selfobj.ObjectToExport.Tip if type(selfobj.ObjectToExport.Tip) is list else [selfobj.ObjectToExport.Tip] elif containermode == 'Feed all children': objects_to_export += selfobj.ObjectToExport.Group else: raise NotImplementedError(u"Unexpected contaner mode {mode}".format(mode= repr(containermode))) else: objects_to_export.append(selfobj.ObjectToExport) if selfobj.MeshAccuracy > 1e-7: for obj in objects_to_export: if hasattr(obj, 'Shape'): obj.Shape.tessellate(selfobj.MeshAccuracy) else: App.Console.PrintWarning(u"Exporter {exporter}: object to export ({object}) has no b-rep shape. Can't re-tessellate." .format(exporter= selfobj.Name, object= obj.Label)) vardict = { 'project_name' : selfobj.Document.Name, 'project_filetitle' : path.splitext(path.basename(selfobj.Document.FileName))[0], 'project_filename' : path.basename(selfobj.Document.FileName), 'project_folder' : path.split(path.split(selfobj.Document.FileName)[0])[1], 'project_label' : selfobj.Document.Label, 'exporter_label' : selfobj.Label, 'exporter_name' : selfobj.Name, 'exporter_container_label' : Containers.getContainer(selfobj).Label, 'exporter_container_name' : Containers.getContainer(selfobj).Name, } def addObjectVars(obj): vardict['object_name' ] = obj.Name vardict['object_label' ] = obj.Label vardict['object_container_label' ] = Containers.getContainer(obj).Label vardict['object_container_name' ] = Containers.getContainer(obj).Name try: if selfobj.MultiMode == 'Write one file': addObjectVars(selfobj.ObjectToExport) filepath = filepath.replace('%Label%', selfobj.ObjectToExport.Label).format(**vardict) mod.export(objects_to_export, filepath) print(u"Exported {file}".format(file= filepath)) if selfobj.FullActualPath != filepath: #check, to avoid touching all exporters upon every file save selfobj.FullActualPath = filepath elif selfobj.MultiMode == 'Write many files': files_written = set() for obj in objects_to_export: addObjectVars(obj) thisfilepath = filepath.replace('%Label%', obj.Label).format(**vardict) if thisfilepath in files_written: raise ValueError(u'Exporter {exporter} is supposed to write multiple files, but the filenames repeat: {fn}. Please make sure a variable is used in the file name, such as {{object_name}}, or {{object_label}}.' .format(exporter= selfobj.Label, fn= thisfilepath)) mod.export([obj], thisfilepath) print(u"Exported {file}".format(file= thisfilepath)) if selfobj.FullActualPath != thisfilepath: #check, to avoid touching all exporters upon every file save selfobj.FullActualPath = thisfilepath else: raise NotImplementedError(u"Unexpected MultiMode: {mode}".format(mode= repr(selfobj.MultiMode))) except KeyError as ke: key = ke.args[0] message = (u'Variable name not recognized: {key}.\n\nVariables available:\n{varlist}' .format( key= key, varlist = '\n'.join(['{' + var + '}' + ': ' + vardict[var] for var in vardict]) ) ) raise KeyError(message)
def morphContainer(src_container, dst_container): if not Containers.isContainer(src_container): raise TypeError( u"{obj} is not a container".format(obj=src_container.Label)) if not Containers.isContainer(dst_container): raise TypeError( u"{obj} is not a container".format(obj=dst_container.Label)) if src_container in Containers.getContainerChain(dst_container): raise Containers.ContainerTreeError( u"Cannot morph {src} into {dst}, because {src} contains {dst}". format(src=src_container.Label, dst=dst_container.Label)) doc = dst_container.Document #origin... if hasattr(src_container, "Origin") and hasattr(dst_container, "Origin"): if dst_container.Origin is not None: doc.removeObject(dst_container.Origin.Name) dst_container.Origin = src_container.Origin src_container.Origin = None #content assert (len(dst_container.Group) == 0) g = src_container.Group src_container.Group = [ ] #withdraw first, add last - otherwise, error is thrown dst_container.Group = g if hasattr(dst_container, 'Origin'): if hasattr(dst_container, 'Proxy') and dst_container.Origin is not None: #workaround for origin not being claimed as child on Py-powered containers dst_container.Group = [dst_container.Origin] + dst_container.Group elif dst_container.Origin is not None and dst_container.Origin in dst_container.Group: #if converting Py cotainer into c++-one, undo the workaround content = dst_container.Group content.remove(dst_container.Origin) #Tip if hasattr(src_container, "Tip") and hasattr(dst_container, "Tip"): tip_list = src_container.Tip if type(tip_list) is not list: tip_list = [tip_list] if tip_list is not None else [] if type(dst_container.Tip) is list: dst_container.Tip = tip_list else: if len(tip_list) == 0: dst_container.Tip = None elif len(tip_list) == 1: dst_container.Tip = tip_list[0] else: App.Console.PrintWarning( u"Target Tip can only point to one object. Source Tip points to {num}. Last object from source tip was taken.\n" .format(num=len(tip_list))) dst_container.Tip = tip_list[-1] #other properties... properties_to_avoid = set([ # these are avoided because they need manual treatment... 'Group', 'Origin', 'ExpressionEngine', 'Tip', 'Label', #.. and these should never be copied at all 'ExtensionProxy', 'Proxy', ]) properties_to_copy = [] for prop in src_container.PropertiesList: if len(src_container.getEditorMode( prop)) == 0: # screen out read-only and hidden properties if not prop in properties_to_avoid: properties_to_copy.append(prop) print("copying properties") for prop in properties_to_copy: print(" " + prop) copyProperty(src_container, dst_container, prop) #Copy expressions for expr in src_container.ExpressionEngine: dst_container.setExpression(*expr) #redirect links substituteObjectInProperties(src_container, dst_container, doc.Objects) substituteObjectInExpressions(src_container, dst_container, doc.Objects) substituteObjectInSpreadsheets(src_container, dst_container, doc.Objects) label = src_container.Label label = label.replace(src_container.Name, dst_container.Name) Containers.withdrawObject(src_container) doc.removeObject(src_container.Name) #copy Label last, because otherwise it gets "001" at the end... dst_container.Label = label
def RunOrTest(self, b_run): # outline. # 0. user click calls Activated(). # 1. set up self.waiter, which waits for new object to be created # 2. waiter calls Activated() of this command, and knows what object was created # 3. command removes waiter, and calls morphContainer if self.waiter is not None: if self.waiter.is_done: waiter = self.waiter self.waiter = None assert (b_run) #enforce PoM observer to put feature into appropriate container from PartOMagic.Gui import Observer as pomObserver if pomObserver.isRunning(): pomObserver.observerInstance.poll() #check if not Containers.isContainer(waiter.target_container): raise CommandError( self, u"You created {obj}, which isn't a container. Can't morph {src} into {objt}. Morphing canceled." .format(obj=waiter.target_container, objt=waiter.target_container, src=waiter.source_container)) #make sure active container is not being messed with... ac = Containers.activeContainer() if waiter.source_container in Containers.getContainerChain( ac) + [ac]: pomObserver.activateContainer( Containers.getContainer(waiter.source_container)) if waiter.target_container in Containers.getContainerChain( ac) + [ac]: pomObserver.activateContainer( Containers.getContainer(waiter.target_container)) # do it! with Transaction("Morph container"): morphContainer(waiter.source_container, waiter.target_container) return else: raise CommandError( self, "(waiting for new container to be created...)") sel = Gui.Selection.getSelection() if len(sel) == 0: raise CommandError(self, self.GetResources()['ToolTip']) elif len(sel) == 1: sel = sel[0] if not Containers.isContainer(sel): raise CommandError(self, "Selected object is not a container") ac = Containers.activeContainer() if sel in Containers.getContainerChain(ac) + [ac]: raise CommandError( self, "Deactivate the container to be morphed first, please.") if b_run: self.waiter = WaitForNewContainer(self, sel) if b_run: Gui.Selection.clearSelection() else: raise CommandError( self, u"You need to select exactly one object (you selected {num}), ad it must be a container." .format(num=len(sel)))