def onCreateLink(self): # parse the selected items selectedPart = [] for selected in self.partList.selectedIndexes(): # get the selected part selectedPart = self.allParts[selected.row()] # get the name of the link (as it should appear in the tree) linkName = self.linkNameInput.text() # repair broken link if self.brokenLink and selectedPart: self.origLink.LinkedObject = selectedPart self.origLink.recompute() self.UI.close() # ... and launch the placement of the inserted part Gui.Selection.clearSelection() Gui.Selection.addSelection(self.activeDoc.Name, self.rootAssembly.Name, self.origLink.Name + '.') # ... but only if we're in an Asm4 Model if self.rootAssembly == Asm4.getAssembly(): Gui.runCommand('Asm4_placeLink') # only create link if there is a Part object and a name elif self.rootAssembly and selectedPart and linkName: # check that the current document had been saved # or that it's the same document as that of the selected part if App.ActiveDocument.FileName != '' or App.ActiveDocument == selectedPart.Document: # create the App::Link with the user-provided name #createdLink = self.activeDoc.getObject('Model').newObject( 'App::Link', linkName ) createdLink = self.rootAssembly.newObject( 'App::Link', linkName) # assign the user-selected selectedPart to it createdLink.LinkedObject = selectedPart # If the name was already chosen, and a UID was generated: if createdLink.Name != linkName: # we try to set the label to the chosen name createdLink.Label = linkName # add the Asm4 properties Asm4.makeAsmProperties(createdLink) createdLink.AssemblyType = "Part::Link" # update the link createdLink.recompute() # close the dialog UI... self.UI.close() # ... and launch the placement of the inserted part Gui.Selection.clearSelection() Gui.Selection.addSelection(self.activeDoc.Name, self.rootAssembly.Name, createdLink.Name + '.') # ... but only if we're in an Asm4 Model if self.rootAssembly == Asm4.getAssembly(): Gui.runCommand('Asm4_placeLink') else: Asm4.warningBox( 'The current document must be saved before inserting an external part' ) return # if still open, close the dialog UI self.UI.close()
def Restore(self): selectedItems = self.configList.selectedItems() if len(selectedItems) != 1: Asm4.warningBox('Please select a configuration in the list') return confName = self.configList.currentItem().name restoreConfiguration(confName)
def onOK(self): confName = self.configName.text().strip() confDescr = self.configDescription.toPlainText().strip() if confName == '': Asm4.warningBox('Please specify configuration name!') self.configName.setFocus() return # if no description provided, give a single space to avoid errors if confDescr == '': confDescr = ' ' SaveConfiguration(confName, confDescr) self.UI.close()
def onExport(self): self.onStop() # check for OpenCV module installed (cv2) try: import cv2 if not self.exporter: # Only import the export-lib if requested. Helps to keep WB loading times in check. import AnimationExportLib self.exporter = AnimationExportLib.animationExporter(self) self.exporter.openUI() except: Asm4.warningBox('The Python module \"OpenCV\" is not installed')
def checkModel(self): if self.activeDoc.getObject('Model'): if self.activeDoc.Model.TypeId == 'App::Part' and self.activeDoc.Model.Type == 'Assembly4 Model': Asm4.warningBox( "This document already contains an Assembly4 Model.") else: Asm4.warningBox( "This document already contains another FreeCAD object called \"Model\". I cannot create an Assembly4 Model." ) return (True) else: return (False)
def Activated(self): # get the current active document to avoid errors if user changes tab self.activeDoc = App.activeDocument() # check whether there is already Model in the document if not self.checkModel(): # create a group 'Parts' to hold all parts in the assembly document (if any) # must be done before creating the Asm4 Model partsGroup = self.activeDoc.getObject('Parts') if partsGroup is None: partsGroup = self.activeDoc.addObject( 'App::DocumentObjectGroup', 'Parts') # create a new App::Part called 'Model' assembly = self.activeDoc.addObject('App::Part', 'Model') # set the type as a "proof" that it's an Assembly4 Model assembly.Type = 'Assembly' assembly.addProperty('App::PropertyString', 'AssemblyType', 'Assembly') assembly.AssemblyType = 'Part::Link' # add an LCS at the root of the Model, and attach it to the 'Origin' lcs0 = assembly.newObject('PartDesign::CoordinateSystem', 'LCS_Origin') lcs0.Support = [(assembly.Origin.OriginFeatures[0], '')] lcs0.MapMode = 'ObjectXY' lcs0.MapReversed = False # create a group Constraints to store future solver constraints there assembly.newObject('App::DocumentObjectGroup', 'Constraints') # create an object Variables to hold variables to be used in this document assembly.addObject(Asm4.createVariables()) # create a group Configurations to store future solver constraints there assembly.newObject('App::DocumentObjectGroup', 'Configurations') # move existing parts and bodies at the document root to the Parts group # not nested inside other parts, to keep hierarchy if partsGroup.TypeId == 'App::DocumentObjectGroup': for obj in self.activeDoc.Objects: if obj.TypeId in Asm4.containerTypes and obj.Name != 'Model' and obj.getParentGeoFeatureGroup( ) is None: partsGroup.addObject(obj) else: Asm4.warningBox( 'There seems to already be a Parts object, you might get unexpected behaviour' ) # recompute to get rid of the small overlays assembly.recompute() self.activeDoc.recompute()
def Activated(self): # check whether there is already Model in the document assy = App.ActiveDocument.getObject('Assembly') if assy: if assy.TypeId == 'App::Part': Asm4.warningBox( "This document already contains a valid Assembly, please use it" ) # set the Type to Assembly assy.Type = 'Assembly' else: message = "This document already contains another FreeCAD object called \"Assembly\", " message += "but it's of type \"" + assy.TypeId + "\", unsuitable for an assembly. I can\'t proceed." Asm4.warningBox(message) # abort return # there is no object called "Assembly" # create a group 'Parts' to hold all parts in the assembly document (if any) # must be done before creating the assembly partsGroup = App.ActiveDocument.getObject('Parts') if partsGroup is None: partsGroup = App.ActiveDocument.addObject( 'App::DocumentObjectGroup', 'Parts') # create a new App::Part called 'Assembly' assembly = App.ActiveDocument.addObject('App::Part', 'Assembly') # set the type as a "proof" that it's an Assembly assembly.Type = 'Assembly' assembly.addProperty('App::PropertyString', 'AssemblyType', 'Assembly') assembly.AssemblyType = 'Part::Link' # add an LCS at the root of the Model, and attach it to the 'Origin' lcs0 = assembly.newObject('PartDesign::CoordinateSystem', 'LCS_Origin') lcs0.Support = [(assembly.Origin.OriginFeatures[0], '')] lcs0.MapMode = 'ObjectXY' lcs0.MapReversed = False # create an object Variables to hold variables to be used in this document assembly.addObject(Asm4.createVariables()) # create a group Constraints to store future solver constraints there assembly.newObject('App::DocumentObjectGroup', 'Constraints') # create a group Configurations to store future solver constraints there assembly.newObject('App::DocumentObjectGroup', 'Configurations') # move existing parts and bodies at the document root to the Parts group # not nested inside other parts, to keep hierarchy if partsGroup.TypeId == 'App::DocumentObjectGroup': for obj in App.ActiveDocument.Objects: if obj.TypeId in Asm4.containerTypes and obj.Name != 'Assembly' and obj.getParentGeoFeatureGroup( ) is None: partsGroup.addObject(obj) else: Asm4.warningBox( 'There seems to already be a Parts object, you might get unexpected behaviour' ) # recompute to get rid of the small overlays assembly.recompute() App.ActiveDocument.recompute()
def Activated(self): (selDatum, selTree) = Asm4.getSelectionTree() # the selected datum is not deep enough if not selTree or len(selTree) < 3: message = selDatum.Name + ' is already at the top-level and cannot be imported' Asm4.warningBox(message) return # the root parent container is the first in the selection tree rootContainer = App.ActiveDocument.getObject(selTree[0]) selection = self.getSelectedDatums() # special case where 2 objects are selected in order to update the placement of the second one if len(selection) == 2 and selection[1].getParentGeoFeatureGroup( ) == rootContainer: confirm = False targetDatum = selection[1] # target datum is free if targetDatum.MapMode == 'Deactivated': message = 'This will superimpose ' + Asm4.labelName( targetDatum) + ' in ' + Asm4.labelName( rootContainer) + ' on:\n\n' for objName in selTree[1:-1]: message += '> ' + objName + '\n' message += '> ' + Asm4.labelName(selDatum) Asm4.warningBox(message) confirm = True # target datum is attached else: message = Asm4.labelName(targetDatum) + ' in ' + Asm4.labelName( rootContainer) + ' is already attached to some geometry. ' message += 'This will superimpose its Placement on:\n\n' for objName in selTree[1:-1]: message += '> ' + objName + '\n' message += '> ' + Asm4.labelName(selDatum) confirm = Asm4.confirmBox(message) if confirm: self.setupTargetDatum(targetDatum, self.getDatumExpression(selTree)) # hide initial datum selDatum.Visibility = False # select the newly created datum Gui.Selection.clearSelection() Gui.Selection.addSelection(App.ActiveDocument.Name, rootContainer.Name, targetDatum.Name + '.') # recompute assembly rootContainer.recompute(True) # Done with the special case, no need to continue with the normal process return # Single or Multiple selection(s) for regular case # Notify user that default names will be used and import all the objects proposedName = selTree[-2] + '_' + selDatum.Label if len(selection) == 1: message = 'Create a new ' + selDatum.TypeId + ' in ' + Asm4.labelName( rootContainer) + ' \nsuperimposed on:\n\n' for objName in selTree[1:]: message += '> ' + objName + '\n' message += '\nEnter name for this datum :' + ' ' * 40 userSpecifiedName, confirm = QtGui.QInputDialog.getText( None, 'Import Datum', message, text=proposedName) else: message = str( len(selection) ) + " selected datum objects will be imported into the root assembly\n" message += "with their default names such as:\n" message += proposedName confirm = Asm4.confirmBox(message) if confirm: for index in range(len(selection)): (selDatum, selTree) = Asm4.getSelectionTree(index) # create a new datum object if len(selection) == 1: proposedName = userSpecifiedName else: proposedName = selTree[-2] + '_' + selDatum.Label targetDatum = rootContainer.newObject(selDatum.TypeId, proposedName) targetDatum.Label = proposedName # apply existing view properties if applicable if hasattr(selDatum, 'ResizeMode') and selDatum.ResizeMode == 'Manual': targetDatum.ResizeMode = 'Manual' if hasattr(selDatum, 'Length'): targetDatum.Length = selDatum.Length if hasattr(selDatum, 'Width'): targetDatum.Width = selDatum.Width targetDatum.ViewObject.ShapeColor = selDatum.ViewObject.ShapeColor targetDatum.ViewObject.Transparency = selDatum.ViewObject.Transparency self.setupTargetDatum(targetDatum, self.getDatumExpression(selTree)) # hide initial datum selDatum.Visibility = False # select the last created datum Gui.Selection.clearSelection() Gui.Selection.addSelection(App.ActiveDocument.Name, rootContainer.Name, targetDatum.Name + '.') # recompute assembly rootContainer.recompute(True)
def __init__(self): self.form = QtGui.QWidget() iconFile = os.path.join(Asm4.iconPath, 'Place_Link.svg') self.form.setWindowIcon(QtGui.QIcon(iconFile)) self.form.setWindowTitle('Place a Part in the assembly') # get the current active document to avoid errors if user changes tab self.activeDoc = App.activeDocument() # the parent (top-level) assembly is the App::Part called Model (hard-coded) self.rootAssembly = Asm4.getAssembly() # has been checked before calling try: self.selectedObj = Gui.Selection.getSelection()[0] assert self.selectedObj.getTypeIdOfProperty( 'Placement') == 'App::PropertyPlacement' except: Asm4.warningBox( 'Please select ab object with a Placement property') # check where the fastener was attached to (self.old_Parent, separator, self.old_parentLCS) = self.selectedObj.AttachedTo.partition('#') # get and store the Placement's current ExpressionEngine: self.old_EE = Asm4.placementEE(self.selectedObj.ExpressionEngine) if hasattr(self.selectedObj, 'AttachmentOffset'): self.old_AO = self.selectedObj.AttachmentOffset else: self.old_AO = None # Now we can draw the UI self.drawUI() self.initUI() # now self.parentList and self.parentTable are available # find all the linked parts in the assembly for obj in self.activeDoc.findObjects("App::Link"): if self.rootAssembly.getObject(obj.Name) is not None and hasattr( obj.LinkedObject, 'isDerivedFrom'): linkedObj = obj.LinkedObject if linkedObj.isDerivedFrom( 'App::Part') or linkedObj.isDerivedFrom( 'PartDesign::Body'): # add to the object table holding the objects ... self.parentTable.append(obj) # ... and add to the drop-down combo box with the assembly tree's parts objIcon = linkedObj.ViewObject.Icon objText = Asm4.labelName(obj) self.parentList.addItem(objIcon, objText, obj) # decode the old ExpressionEngine # if the decode is unsuccessful, old_Expression is set to False # and old_attPart and old_attLCS are set to 'None' old_Parent = '' old_parentPart = '' old_parentLCS = '' if self.old_EE and self.old_Parent: (old_Parent, old_parentPart, old_parentLCS) = self.splitExpression(self.old_EE, self.old_Parent) # find the oldPart in the part list... parent_index = 1 if old_Parent == 'Parent Assembly': parent_found = True else: parent_found = False for item in self.parentTable[1:]: if item.Name == old_Parent: parent_found = True break else: parent_index += 1 if not parent_found: parent_index = 0 self.parentList.setCurrentIndex(parent_index) # this should have triggered self.getPartLCS() to fill the LCS list # find the oldLCS in the list of LCS of the linked part... lcs_found = [] lcs_found = self.attLCSlist.findItems(old_parentLCS, QtCore.Qt.MatchExactly) # may-be it was renamed, see if we can find it as (name) if not lcs_found: lcs_found = self.attLCSlist.findItems('(' + old_parentLCS + ')', QtCore.Qt.MatchContains) if lcs_found: # ... and select it self.attLCSlist.setCurrentItem(lcs_found[0]) Gui.Selection.addObserver(self, 0)
def Activated(self): # This function is executed when the command is activated self.UI = QtGui.QDialog() self.drawUI() # initialise stuff self.activeDoc = App.ActiveDocument self.rootAssembly = None self.origLink = None self.brokenLink = False self.allParts = [] self.partsDoc = [] self.filterPartList.clear() self.partList.clear() self.linkNameInput.clear() # if an Asm4 Assembly is present, that's where we put the variant link if Asm4.getAssembly(): self.rootAssembly = Asm4.getAssembly() # an App::Part at the root of the document is selected, we insert the link there elif Asm4.getSelectedRootPart(): self.rootAssembly = Asm4.getSelectedRootPart() # if a variant link is selected, we see if we can duplicate it if Asm4.getSelectedVarLink(): selObj = Asm4.getSelectedVarLink() parent = selObj.getParentGeoFeatureGroup() # if the selected link is in a root App::Part if parent.TypeId == 'App::Part' and parent.getParentGeoFeatureGroup() is None: self.rootAssembly = parent self.origLink = selObj self.origPart = selObj.SourceObject ''' # if a broken link is selected elif len(Gui.Selection.getSelection())==1 : selObj = Gui.Selection.getSelection()[0] if selObj.isDerivedFrom('App::Link') and selObj.LinkedObject is None: parent = selObj.getParentGeoFeatureGroup() # if the selected (broken) link is in a root App::Part if parent.TypeId == 'App::Part' and parent.getParentGeoFeatureGroup() is None: self.brokenLink = True self.rootAssembly = parent self.origLink = selObj self.UI.setWindowTitle('Re-link broken link') self.insertButton.setText('Replace') self.linkNameInput.setText(Asm4.labelName(selObj)) self.linkNameInput.setEnabled(False) ''' if self.rootAssembly is None: Asm4.warningBox( 'Please create an Assembly' ) return # Search for all App::Parts having a "Variables" property container in all open documents # Also store the document of the part for doc in App.listDocuments().values(): # don't consider temporary documents if not doc.Temporary: for obj in doc.findObjects("App::Part"): # we don't want to link to itself to the 'Model' object # other App::Part in the same document are OK # but only those at top level (not nested inside other containers) if obj != self.rootAssembly and obj.getParentGeoFeatureGroup() is None: # and only those that have a Variables property container variables = obj.getObject('Variables') if hasattr(variables,'Type') and variables.Type=='App::PropertyContainer': self.allParts.append( obj ) self.partsDoc.append( doc ) # build the list for part in self.allParts: newItem = QtGui.QListWidgetItem() newItem.setText( part.Document.Name +"#"+ Asm4.labelName(part) ) newItem.setIcon(part.ViewObject.Icon) self.partList.addItem(newItem) # if an existing valid App::Link was selected if self.origLink and not self.brokenLink: origPart = self.origLink.SourceObject # try to find the original part of the selected link origPartText = origPart.Document.Name +"#"+ Asm4.labelName(origPart) # MatchExactly, MatchContains, MatchEndsWith, MatchStartsWith ... partFound = self.partList.findItems( origPartText, QtCore.Qt.MatchExactly ) if partFound: self.partList.setCurrentItem(partFound[0]) # self.onItemClicked(partFound[0]) # if the last character is a number, we increment this number origName = self.origLink.Label lastChar = origName[-1] if lastChar.isnumeric(): (rootName,sep,num) = origName.rpartition('_') proposedLinkName = Asm4.nextInstance(rootName,startAtOne=True) # else we take the next instance else: proposedLinkName = Asm4.nextInstance(origName) # set the proposed name in the entry field if not self.brokenLink: self.linkNameInput.setText( proposedLinkName ) # show the UI self.UI.show()
def Activated(self): # try with a regular App::Link selection = Asm4.getSelectedLink() # may-be an Asm4::VariantLink ? if selection is None: selection = Asm4.getSelectedVarLink() # if we found a valid link if selection is not None: # check that it's in the root assembly parent = selection.getParentGeoFeatureGroup() if parent and parent == Asm4.getAssembly(): # if it's a valid assembly and part if Asm4.isAsm4EE(selection): # BUGFIX: if the part was corrupted by Assembly4 v0.11.5: if hasattr(selection, 'MapMode'): Asm4.warningBox( "This Part has the Attachment extension, it can only be placed manually" ) else: # launch the UI in the task panel ui = placeLinkUI() Gui.Control.showDialog(ui) # else try to convert it else: convert = Asm4.confirmBox( "This Part wasn't assembled with this Assembly4 WorkBench, but I can convert it." ) if convert: Asm4.makeAsmProperties(selection, reset=True) # launch the UI in the task panel ui = placeLinkUI() Gui.Control.showDialog(ui) else: Asm4.warningBox('Please select a link in the assembly Model.') else: # or any part that has a Placement ? if len(Gui.Selection.getSelection()) == 1: selection = Gui.Selection.getSelection()[0] # object has a Placement property if hasattr(selection, 'Placement') and selection.getTypeIdOfProperty( 'Placement') == 'App::PropertyPlacement': # we don't want to mess with obects that are attached with the Attacher (MapMode) if hasattr(selection, 'MapMode'): FCC.PrintMessage( 'Object has MapMode property, you should use that\n' ) else: # check that it's in the root assembly parent = selection.getParentGeoFeatureGroup() if parent and parent == Asm4.getAssembly(): # is it's a virgin object, give it Asm4 properties if not hasattr(selection, 'SolverId'): Asm4.makeAsmProperties(selection) # if it's a valid assembly and part if Asm4.isAsm4EE(selection): # launch the UI in the task panel ui = placePartUI() Gui.Control.showDialog(ui) # else try to convert it else: convert = Asm4.confirmBox( "This Part wasn't assembled with this Assembly4 WorkBench, but I can convert it." ) if convert: Asm4.makeAsmProperties(selection, reset=True) # launch the UI in the task panel ui = placePartUI() Gui.Control.showDialog(ui) # the selected object doesn't belong to the root assembly else: Asm4.warningBox( 'Please select an object in the assembly Model.' ) return
def Activated(self): # check that we have somewhere to put our stuff selectedObj = self.checkSelection() # default name increments the datum type's end numeral proposedName = Asm4.nextInstance(self.datumName, startAtOne=True) # check whether we have selected a container if selectedObj.TypeId in self.containers: parentContainer = selectedObj # if a datum object is selected elif selectedObj.TypeId in Asm4.datumTypes or selectedObj.TypeId == 'Sketcher::SketchObject': # see whether it's in a container parent = selectedObj.getParentGeoFeatureGroup() if parent.TypeId in self.containers: parentContainer = parent # if there is an assembly elif Asm4.getAssembly(): parentContainer = Asm4.getAssembly() # something went wrong else: Asm4.warningBox("I can't create a " + self.datumType + " with the current selections") # check whether there is already a similar datum, and increment the instance number # instanceNum = 1 #while App.ActiveDocument.getObject( self.datumName+'_'+str(instanceNum) ): # instanceNum += 1 #datumName = self.datumName+'_'+str(instanceNum) if parentContainer: # input dialog to ask the user the name of the Sketch: #proposedName = Asm4.nextInstance( self.datumName + '_' + selectedObj.Label, startAtOne=True ) text, ok = QtGui.QInputDialog.getText( None, 'Create new ' + self.datumName, 'Enter ' + self.datumName + ' name :' + ' ' * 40, text=proposedName) if ok and text: # App.activeDocument().getObject('Model').newObject( 'Sketcher::SketchObject', text ) createdDatum = App.ActiveDocument.addObject( self.datumType, text) parentContainer.addObject(createdDatum) createdDatum.Label = text # automatic resizing of datum Plane sucks, so we set it to manual if self.datumType == 'PartDesign::Plane': createdDatum.ResizeMode = 'Manual' createdDatum.Length = 100 createdDatum.Width = 100 elif self.datumType == 'PartDesign::Line': createdDatum.ResizeMode = 'Manual' createdDatum.Length = 200 # if color or transparency is specified for this datum type if self.datumColor: Gui.ActiveDocument.getObject( createdDatum.Name).ShapeColor = self.datumColor if self.datumAlpha: Gui.ActiveDocument.getObject( createdDatum.Name).Transparency = self.datumAlpha # highlight the created datum object Gui.Selection.clearSelection() Gui.Selection.addSelection(App.ActiveDocument.Name, parentContainer.Name, createdDatum.Name + '.') Gui.runCommand('Part_EditAttachment')