├── .gitignore ├── A2plus.qrc ├── CD_A2plusupdater.py ├── CD_CheckConstraints.py ├── CD_ConstraintViewer.py ├── CD_FeatureLabels.py ├── CD_Help for Diagnostic tools.pdf ├── CD_OneButton.py ├── GuiA2p └── Resources │ ├── compile_resources_pack.py │ ├── resources-a2p.rcc │ ├── resources.rcc │ ├── ui │ └── a2p_prefs.ui │ └── wiki │ ├── Assembly2_AngleConstraint.png │ ├── Assembly2_AxialConstraint.png │ ├── Assembly2_BoltMultipleCircularEdges.png │ ├── Assembly2_CheckAssembly.png │ ├── Assembly2_CircularEdgeConstraint.png │ ├── Assembly2_DegreesOfFreedomAnimation.png │ ├── Assembly2_DraftMove.png │ ├── Assembly2_FlipConstraint.png │ ├── Assembly2_Help.png │ ├── Assembly2_ImportPart.png │ ├── Assembly2_ImportPartUpdate.png │ ├── Assembly2_LockRotation.png │ ├── Assembly2_MuxAssembly.png │ ├── Assembly2_PartsList.png │ ├── Assembly2_Pause.png │ ├── Assembly2_PlaneConstraint.png │ ├── Assembly2_Play.png │ ├── Assembly2_PreferencesAssembly2.png │ ├── Assembly2_SolveConstraints.png │ ├── Assembly2_SphericalSurfaceConstraint.png │ ├── Assembly2_Stop.png │ ├── Assembly2_WorkBenchIcon.png │ └── Thumbs.db ├── Init.py ├── InitGui.py ├── LICENSE ├── README.md ├── __init__.py ├── a2p_BoM.py ├── a2p_ConstraintCommands.py ├── a2p_ConstraintDialog.py ├── a2p_MuxAssembly.py ├── a2p_Resources3.py ├── a2p_Resources3_Qt6.py ├── a2p_constraintServices.py ├── a2p_constraints.py ├── a2p_convertPart.py ├── a2p_dependencies.py ├── a2p_fcdocumentreader.py ├── a2p_importedPart_class.py ├── a2p_importpart.py ├── a2p_lcs_support.py ├── a2p_libDOF.py ├── a2p_observers.py ├── a2p_partinformation.py ├── a2p_partlistglobals.py ├── a2p_recursiveUpdatePlanner.py ├── a2p_rigid.py ├── a2p_searchConstraintConflicts.py ├── a2p_simpleXMLreader.py ├── a2p_solversystem.py ├── a2p_topomapper.py ├── a2p_translateUtils.py ├── a2p_viewProviderProxies.py ├── a2plib.py ├── compileA2pResources.py ├── crowdin.yml ├── icons ├── CD_ConstraintChecker.svg ├── CD_ConstraintViewer.svg ├── CD_OneButton.svg ├── a2p_AngleConstraint.svg ├── a2p_Asm.svg ├── a2p_AutoSolve.svg ├── a2p_AxialConstraint.svg ├── a2p_AxisParallelConstraint.svg ├── a2p_AxisPlaneAngleConstraint.svg ├── a2p_AxisPlaneNormalConstraint.svg ├── a2p_AxisPlaneParallelConstraint.svg ├── a2p_AxisPlaneVerticalConstraint.svg ├── a2p_CenterOfMassConstraint.svg ├── a2p_CheckAssembly.svg ├── a2p_CircularEdgeConstraint.svg ├── a2p_ConvertPart.svg ├── a2p_DOFs.svg ├── a2p_DefineConstraints.svg ├── a2p_DegreesOfFreedomAnimation.svg ├── a2p_DeleteConnections.svg ├── a2p_DuplicatePart.svg ├── a2p_EditConstraint.svg ├── a2p_EditPart.svg ├── a2p_EditUndo.svg ├── a2p_FlipConstraint.svg ├── a2p_Help.svg ├── a2p_ImportPart.svg ├── a2p_ImportPart_Update.svg ├── a2p_Isolate_Element.svg ├── a2p_LCS_group.svg ├── a2p_LockRotation.svg ├── a2p_MovePart.svg ├── a2p_MovePartUnderConstraints.svg ├── a2p_Obj.svg ├── a2p_ObjReference.svg ├── a2p_PartLabel.svg ├── a2p_PartLabelRemove.svg ├── a2p_PartialProcessing.svg ├── a2p_PartsInfo.svg ├── a2p_PartsList.svg ├── a2p_PartsList_CutListOptimizer.svg ├── a2p_Pause.svg ├── a2p_PlaneCoincidentConstraint.svg ├── a2p_PlanesParallelConstraint.svg ├── a2p_Play.svg ├── a2p_PointIdentity.svg ├── a2p_PointOnLineConstraint.svg ├── a2p_PointOnPlaneConstraint.svg ├── a2p_ReAdjustConstraints.svg ├── a2p_RecursiveUpdate.svg ├── a2p_RemoveDebug3D.svg ├── a2p_RepairTree.svg ├── a2p_Restore_Transparency.svg ├── a2p_Save_and_exit.svg ├── a2p_SearchConstraintConflicts.svg ├── a2p_SetRelativePathes.svg ├── a2p_ShapeReference.svg ├── a2p_SimpleAssemblyShape.svg ├── a2p_SketchReference.svg ├── a2p_Solver.svg ├── a2p_SphericalSurfaceConstraint.svg ├── a2p_Stop.svg ├── a2p_ToggleAutoSolve.svg ├── a2p_TogglePartial.svg ├── a2p_ToggleTransparency.svg ├── a2p_Treeview.svg ├── a2p_Unlabel_DOFs.svg ├── a2p_Update.svg ├── a2p_Upgrade.svg ├── a2p_ViewConnection.svg ├── a2p_Workbench.svg └── preferences-a2plus.svg ├── package.xml └── translations ├── A2plus.ts ├── A2plus_de.qm ├── A2plus_de.ts ├── A2plus_es-AR.qm ├── A2plus_es-AR.ts ├── A2plus_es-ES.qm ├── A2plus_es-ES.ts ├── A2plus_fr.qm ├── A2plus_fr.ts ├── A2plus_it.qm ├── A2plus_it.ts ├── A2plus_pt-br.qm ├── A2plus_pt-br.ts ├── A2plus_pt-pt.qm ├── A2plus_pt-pt.ts ├── A2plus_ru.qm ├── A2plus_ru.ts ├── A2plus_zh-CN.qm ├── A2plus_zh-CN.ts ├── README.md └── update_ts.py /.gitignore: -------------------------------------------------------------------------------- 1 | # file types to ignore 2 | 3 | *.pyc 4 | *.obj 5 | *.lib 6 | *.pch 7 | *.vcproj 8 | *.exp 9 | *.dep 10 | *.bak 11 | *.manifest 12 | qrc_*.cpp 13 | BuildLog.htm 14 | cmake_install.cmake 15 | *~ 16 | CMakeFiles/ 17 | *qrc.depends 18 | ui_*.h 19 | moc_*.cpp 20 | Makefile 21 | CMakeCache.txt 22 | config.h 23 | install_manifest.txt 24 | /bin/ 25 | /ALL_BUILD.dir/ 26 | /doc/ 27 | /lib/ 28 | /Mod/ 29 | /ZERO_CHECK.dir/ 30 | /build/ 31 | /src/Tools/offlinedoc/localwiki/ 32 | /src/Tools/offlinedoc/*.txt 33 | OpenSCAD_rc.py 34 | .subuser-dev 35 | /\.idea/ 36 | .tags 37 | tags 38 | -------------------------------------------------------------------------------- /A2plus.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | icons/a2p_SetRelativePathes.svg 4 | icons/a2p_CenterOfMassConstraint.svg 5 | icons/a2p_RecursiveUpdate.svg 6 | icons/a2p_Asm.svg 7 | icons/a2p_Obj.svg 8 | icons/a2p_EditConstraint.svg 9 | icons/a2p_DefineConstraints.svg 10 | icons/a2p_AngleConstraint.svg 11 | icons/a2p_AutoSolve.svg 12 | icons/a2p_AxialConstraint.svg 13 | icons/a2p_AxisParallelConstraint.svg 14 | icons/a2p_AxisPlaneParallelConstraint.svg 15 | icons/a2p_CheckAssembly.svg 16 | icons/a2p_CircularEdgeConstraint.svg 17 | icons/a2p_DegreesOfFreedomAnimation.svg 18 | icons/a2p_DeleteConnections.svg 19 | icons/a2p_DuplicatePart.svg 20 | icons/a2p_EditPart.svg 21 | icons/a2p_EditUndo.svg 22 | icons/a2p_FlipConstraint.svg 23 | icons/a2p_Help.svg 24 | icons/a2p_ImportPart.svg 25 | icons/a2p_ConvertPart.svg 26 | icons/a2p_ImportPart_Update.svg 27 | icons/a2p_Isolate_Element.svg 28 | icons/a2p_LockRotation.svg 29 | icons/a2p_MovePart.svg 30 | icons/a2p_PartialProcessing.svg 31 | icons/a2p_PartsInfo.svg 32 | icons/a2p_PartsList.svg 33 | icons/a2p_PartsList_CutListOptimizer.svg 34 | icons/a2p_Pause.svg 35 | icons/a2p_PlaneCoincidentConstraint.svg 36 | icons/a2p_PlanesParallelConstraint.svg 37 | icons/a2p_Play.svg 38 | icons/a2p_PointIdentity.svg 39 | icons/a2p_PointOnLineConstraint.svg 40 | icons/a2p_PointOnPlaneConstraint.svg 41 | icons/a2p_SimpleAssemblyShape.svg 42 | icons/a2p_Solver.svg 43 | icons/a2p_SphericalSurfaceConstraint.svg 44 | icons/a2p_Stop.svg 45 | icons/a2p_ToggleTransparency.svg 46 | icons/a2p_ToggleAutoSolve.svg 47 | icons/a2p_TogglePartial.svg 48 | icons/a2p_Treeview.svg 49 | icons/a2p_RepairTree.svg 50 | icons/a2p_ViewConnection.svg 51 | icons/a2p_Workbench.svg 52 | icons/preferences-a2plus.svg 53 | 54 | 55 | -------------------------------------------------------------------------------- /CD_FeatureLabels.py: -------------------------------------------------------------------------------- 1 | # *************************************************************************** 2 | # * * 3 | # * Copyright (c) 2020 Dan Miel * 4 | # * * 5 | # * This program is free software; you can redistribute it and/or modify * 6 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 7 | # * the License, or (at your option) any later version. * 8 | # * for detail see the LICENCE text file. * 9 | # * * 10 | # * This program is distributed in the hope that it will be useful, * 11 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 12 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 13 | # * GNU Library General Public License for more details. * 14 | # * * 15 | # * You should have received a copy of the GNU Library General Public * 16 | # * License along with this program; if not, write to the Free Software * 17 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 18 | # * USA * 19 | # * * 20 | # *************************************************************************** 21 | # This is to be used with A2plus Assembly WorkBench 22 | 23 | # from PySide.QtGui import * 24 | from PySide import QtGui, QtCore 25 | 26 | import FreeCAD 27 | import FreeCADGui 28 | 29 | translate = FreeCAD.Qt.translate 30 | 31 | 32 | class formMain(QtGui.QMainWindow): 33 | def __init__(self, name): 34 | self.name = name 35 | super(formMain, self).__init__() 36 | self.setWindowTitle(translate("A2plus", "Create Labels")) 37 | self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) 38 | self.setGeometry(300, 200, 260, 200) 39 | self.setStyleSheet("font: 11pt arial MS") 40 | 41 | self.btnLabels = [[translate("A2plus", "Add Face Labels"), translate("A2plus", "Add labels to all of the faces on a selected part")], 42 | [translate("A2plus", "Add Edge Labels"), translate("A2plus", "Add labels to all of the edges on a selected part")], 43 | [translate("A2plus", "Add Vertex Labels"), translate("A2plus", "Add labels to all of the vertices on a selected part")], 44 | [translate("A2plus", "Delete Labels"), translate("A2plus", "Delete all labels")], 45 | [translate("A2plus", "Close"), translate("A2plus", "Close this window")] 46 | ] 47 | self.btns = [] 48 | BtnNum = 0 49 | for row in range(0, len(self.btnLabels)): 50 | btny = 20 + (28*row) 51 | self.btn = QtGui.QPushButton(str(self.btnLabels[row][0]), self) 52 | self.btn.move(5, btny) 53 | self.btn.setFixedWidth(250) 54 | self.btn.setFixedHeight(25) 55 | self.btn.setToolTip(self.btnLabels[row][1]) 56 | self.btn.released.connect(self.button_pushed) # pressed 57 | self.btns.append(self.btn) 58 | BtnNum = BtnNum + 1 59 | 60 | def button_pushed(self): 61 | index = self.btns.index(self.sender()) 62 | btext = self.btns[index].text() 63 | if btext == translate("A2plus", "Add Face Labels"): 64 | labels.addlabels(translate("A2plus", "Face")) 65 | if btext == translate("A2plus", "Add Edge Labels"): 66 | labels.addlabels(translate("A2plus", "Edge")) 67 | if btext == translate("A2plus", "Add Vertex Labels"): 68 | labels.addlabels(translate("A2plus", "Vertex")) 69 | 70 | if btext == translate("A2plus", "Delete Labels"): 71 | labels.deletelabels() 72 | if btext == translate("A2plus", "Close"): 73 | self.Closeme() 74 | 75 | # Not present? 76 | if btext == 'Attach to': 77 | labels.attachto() 78 | if btext == 'Selected Labels': 79 | labels.selectedlabels() 80 | 81 | def hideMe(self): 82 | QtGui.Selection.clearSelection() 83 | self.close() 84 | 85 | def showme(self): 86 | self.show() 87 | 88 | def Closeme(self): 89 | self.close() 90 | 91 | def closeEvent(self, event): 92 | form1.Closeme() 93 | self.close() 94 | 95 | 96 | form1 = formMain('form1') 97 | 98 | 99 | class classLabels(): 100 | def __init__(self): 101 | self.labelGroup = None 102 | 103 | def checkselection(self): 104 | """Checks to see if labels already exist.""" 105 | doc = FreeCAD.activeDocument() 106 | self.labelGroup = doc.getObject("partLabels") 107 | if self.labelGroup is None: 108 | self.labelGroup = doc.addObject("App::DocumentObjectGroup", "partLabels") 109 | if len(FreeCADGui.Selection.getSelection()) == 0: 110 | mApp(translate("A2plus", "One part must be selected.") + "\n" + translate("A2plus", "Please select One part and try again")) 111 | return(False) 112 | return(True) 113 | 114 | def addlabels(self, feat): 115 | sel = self.checkselection() 116 | if not sel: 117 | return 118 | sel = FreeCADGui.Selection.getSelection() # Select an object 119 | if feat == translate("A2plus", "Face"): 120 | features = sel[0].Shape.Faces 121 | if feat == translate("A2plus", "Edge"): 122 | features = sel[0].Shape.Edges 123 | if feat == translate("A2plus", "Vertex"): 124 | features = sel[0].Shape.Vertexes 125 | 126 | for num in range(0, len(features)): 127 | ent = features[num] 128 | if feat == translate("A2plus", "Vertex"): 129 | loc = ent.Point 130 | else: 131 | loc = ent.CenterOfMass 132 | partLabel = self.makelabel(ent, feat+str(num+1), loc) 133 | self.labelGroup.addObject(partLabel) 134 | 135 | def makelabel(self, ent, name, loc): 136 | partLabel = FreeCAD.ActiveDocument.addObject("App::AnnotationLabel", "partLabel") 137 | partLabel.LabelText = name 138 | partLabel.BasePosition.x = loc[0] 139 | partLabel.BasePosition.y = loc[1] 140 | partLabel.BasePosition.z = loc[2] 141 | partLabel.ViewObject.BackgroundColor = (1.0, 1.0, 0.0) 142 | partLabel.ViewObject.TextColor = (0.0, 0.0, 0.0) 143 | return(partLabel) 144 | 145 | def deletelabels(self): 146 | for obj in FreeCAD.ActiveDocument.Objects: 147 | if "partLabel" in obj.Label: 148 | FreeCAD.ActiveDocument.removeObject(obj.Name) 149 | 150 | def attachto(self, sel=None, featname=''): 151 | sel = self.checkselection() 152 | if not sel: 153 | return 154 | if featname == '': 155 | featname = form1.txtboxaddlabel.text() 156 | if sel is None: 157 | sel = FreeCADGui.Selection.getSelection()[0] 158 | FreeCADGui.Selection.clearSelection() 159 | FreeCADGui.Selection.addSelection(sel, featname) 160 | s = FreeCADGui.Selection.getSelectionEx()[0] 161 | ent = s.SubObjects[0] 162 | self.makelabel(ent, name, loc) 163 | 164 | def getEntLoc(self, ent, featname): 165 | if 'V' in featname: 166 | loc = ent.Point 167 | else: 168 | loc = ent.CenterOfMass 169 | partLabel = self.makelabel(ent, featname, loc) 170 | self.labelGroup.addObject(partLabel) 171 | self.makelabel(ent, featname, loc) 172 | 173 | def labelForTable(self, ent, featname): 174 | """Create a label to find a part.""" 175 | sel = self.checkselection() 176 | self.getEntLoc(ent, featname) 177 | 178 | def selectedlabels(self): 179 | sel = self.checkselection() 180 | if not sel: 181 | return 182 | sels = FreeCADGui.Selection.getSelectionEx() # Select an object 183 | for sel in sels: 184 | featname = sel.SubElementNames[0] 185 | ent = sel.SubObjects[0] 186 | self.getentloc(ent, featname) 187 | 188 | 189 | labels = classLabels() 190 | 191 | 192 | class mApp(QtGui.QWidget): 193 | ''' This message box was added to make this file a standalone file''' 194 | # for error messages 195 | def __init__(self, msg): 196 | super().__init__() 197 | self.initUI(msg) 198 | 199 | def initUI(self, msg): 200 | self.setGeometry(100, 100, 400, 300) 201 | self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) 202 | QtGui.QMessageBox.question(self, translate("A2plus", "Info"), msg, QtGui.QMessageBox.Ok | QtGui.QMessageBox.Ok) 203 | self.show() 204 | -------------------------------------------------------------------------------- /CD_Help for Diagnostic tools.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/CD_Help for Diagnostic tools.pdf -------------------------------------------------------------------------------- /CD_OneButton.py: -------------------------------------------------------------------------------- 1 | # *************************************************************************** 2 | # * * 3 | # * Copyright (c) 2020 Dan Miel * 4 | # * * 5 | # * This program is free software; you can redistribute it and/or modify * 6 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 7 | # * as published by the Free Software Foundation; either version 2 of * 8 | # * the License, or (at your option) any later version. * 9 | # * for detail see the LICENCE text file. * 10 | # * * 11 | # * This program is distributed in the hope that it will be useful, * 12 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 13 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 14 | # * GNU Library General Public License for more details. * 15 | # * * 16 | # * You should have received a copy of the GNU Library General Public * 17 | # * License along with this program; if not, write to the Free Software * 18 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 19 | # * USA * 20 | # * * 21 | # *************************************************************************** 22 | """ 23 | This is to be used in conjunction with A2plus Assembly Workbench. 24 | 25 | Enables two features to be selected without using the control key. 26 | """ 27 | 28 | import os 29 | from PySide import QtGui, QtCore 30 | 31 | import FreeCAD 32 | import FreeCADGui 33 | 34 | translate = FreeCAD.Qt.translate 35 | 36 | 37 | class globaluseclass: 38 | def __init__(self, name): 39 | self.sONOFF = 'off' 40 | self.feat1 = '' 41 | self.buttonenabled = False 42 | self.obj1 = '' 43 | self.partselected = False 44 | 45 | 46 | g = globaluseclass("g") 47 | 48 | 49 | class onebutton: 50 | def readselect(self, doc, obj, sub): 51 | if g.partselected: 52 | g.partselected = False 53 | return 54 | sels = len(FreeCADGui.Selection.getSelectionEx()) 55 | if sub == "": 56 | pass 57 | elif sels == 1: 58 | if g.obj1 == '' and g.feat1 == '' or obj == g.obj1: 59 | g.obj1 = obj 60 | g.feat1 = sub 61 | obj = '' 62 | sub = '' 63 | 64 | elif g.obj1 != '' and g.feat1 != '': 65 | if obj != '' and sub != '': 66 | try: 67 | obj1 = FreeCAD.ActiveDocument.getObject(g.obj1) 68 | obj2 = FreeCAD.ActiveDocument.getObject(obj) 69 | FreeCADGui.Selection.addSelection(obj1, g.feat1) 70 | FreeCADGui.Selection.addSelection(obj2, sub) 71 | g.partselected = True 72 | except: 73 | pass 74 | g.feat1 = '' 75 | g.obj1 = '' 76 | obj = '' 77 | sub = '' 78 | 79 | 80 | class SelObserver: 81 | def __init__(self): 82 | pass 83 | 84 | def SelObserverON(self): 85 | if g.sONOFF != 'on': 86 | FreeCADGui.Selection.addObserver(selObv) 87 | g.sONOFF = 'on' 88 | # print('SelObserverON') 89 | 90 | def SelObserverOFF(self): 91 | try: 92 | FreeCADGui.Selection.removeObserver(selObv) 93 | g.sONOFF = 'off' 94 | # print('SelObserverOFF') 95 | except: 96 | FreeCAD.Console.PrintMessage(translate("A2plus", "SelObserverOFF by except") + "\n") 97 | 98 | def addSelection(self, doc, obj, sub, pnt): # Selection object 99 | onebutton.readselect(onebutton, doc, obj, sub) 100 | 101 | def removeSelection(self, doc, obj, sub): # Delete the selected object 102 | pass 103 | 104 | def setSelection(self, doc): 105 | pass 106 | 107 | 108 | selObv = SelObserver() 109 | 110 | 111 | # This class looks for mouse clicks in space to unselect parts. 112 | class ViewObserver: 113 | def __init__(self): 114 | self.view = None 115 | self.o = None 116 | self.c = None 117 | 118 | def vostart(self): 119 | self.view = FreeCADGui.activeDocument().activeView() 120 | self.o = ViewObserver() 121 | self.c = self.view.addEventCallback("SoMouseButtonEvent", self.o.logPosition) 122 | 123 | def vooff(self): 124 | try: 125 | self.view.removeEventCallback("SoMouseButtonEvent", self.c) 126 | except Exception as e: 127 | print(str(e)) 128 | 129 | def logPosition(self, myinfo): 130 | down = (myinfo["State"] == "DOWN") 131 | up = (myinfo["State"] == "UP") 132 | pos = myinfo["Position"] 133 | if up: 134 | pass 135 | if (down): 136 | if myinfo['Button'] == 'BUTTON1': 137 | pos = FreeCADGui.ActiveDocument.ActiveView.getCursorPos() 138 | partinfo = FreeCADGui.activeDocument().activeView().getObjectInfo(pos) 139 | if partinfo is None: 140 | g.feat1 = '' 141 | g.obj1 = '' 142 | FreeCADGui.Selection.clearSelection() 143 | else: 144 | pass 145 | 146 | 147 | viewob = ViewObserver() 148 | 149 | 150 | class rnp_OneButton: 151 | def GetResources(self): 152 | mypath = os.path.dirname(__file__) 153 | return { 154 | 'Pixmap': mypath + "/icons/CD_OneButton.svg", 155 | 'MenuText': translate("A2plus", "Use one mouse button to select features"), 156 | 'ToolTip': translate("A2plus", "Use left mouse button to select two features.\nDo not use the control key."), 157 | 'Checkable': self.IsChecked() 158 | } 159 | 160 | def Activated(self, placeholder=None): 161 | if FreeCAD.activeDocument() is None: 162 | mApp(translate("A2plus", "No file is opened.\nYou must open an assembly file first.")) 163 | return 164 | FreeCADGui.Selection.clearSelection() 165 | if g.buttonenabled is False: 166 | selObv.SelObserverON() # Checks for part and entity click 167 | viewob.vostart() # Checks for click in background 168 | g.buttonenabled = True 169 | FreeCAD.Console.PrintMessage(translate("A2plus", "OneButton is ON") + "\n") 170 | else: 171 | selObv.SelObserverOFF() 172 | viewob.vooff() 173 | g.buttonenabled = False 174 | FreeCAD.Console.PrintMessage(translate("A2plus", "OneButton is OFF") + "\n") 175 | 176 | def Deactivated(self): 177 | """This function is executed when the workbench is deactivated.""" 178 | selObv.SelObserverOFF() 179 | viewob.vooff() 180 | 181 | def IsChecked(self): 182 | return(g.buttonenabled) 183 | 184 | def IsActive(self): 185 | return(True) 186 | 187 | 188 | FreeCADGui.addCommand('rnp_OneButton', rnp_OneButton()) 189 | 190 | 191 | class mApp(QtGui.QWidget): 192 | """This message box was added to make this file a standalone file""" 193 | # for error messages 194 | def __init__(self, msg, msgtype='ok'): 195 | super().__init__() 196 | self.title = translate("A2plus", "Warning") 197 | self.left = 100 198 | self.top = 100 199 | self.width = 400 200 | self.height = 300 201 | self.initUI(msg) 202 | 203 | def initUI(self, msg): 204 | # self.setWindowTitle(self.title) 205 | self.setGeometry(self.left, self.top, self.width, self.height) 206 | self.setWindowFlags(QtCore.Qt.WindowStaysOnTopHint) 207 | QtGui.QMessageBox.question(self, translate("A2plus", "Warning"), msg, QtGui.QMessageBox.Ok | QtGui.QMessageBox.Ok) 208 | self.show() 209 | -------------------------------------------------------------------------------- /GuiA2p/Resources/compile_resources_pack.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python 2 | import os, glob 3 | 4 | qrc_filename = 'resources.qrc' 5 | assert not os.path.exists(qrc_filename) 6 | 7 | qrc = ''' 8 | ''' 9 | for fn in glob.glob('./icons/*.svg') + glob.glob('./ui/*.ui'): 10 | qrc = qrc + '\n\t\t%s' % fn 11 | qrc = qrc + '''\n\t 12 | ''' 13 | 14 | print(qrc) 15 | 16 | f = open(qrc_filename,'w') 17 | f.write(qrc) 18 | f.close() 19 | 20 | os.system('rcc -binary %s -o resources.rcc' % qrc_filename) 21 | os.remove(qrc_filename) 22 | -------------------------------------------------------------------------------- /GuiA2p/Resources/resources-a2p.rcc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/GuiA2p/Resources/resources-a2p.rcc -------------------------------------------------------------------------------- /GuiA2p/Resources/resources.rcc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/GuiA2p/Resources/resources.rcc -------------------------------------------------------------------------------- /GuiA2p/Resources/wiki/Assembly2_AngleConstraint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/GuiA2p/Resources/wiki/Assembly2_AngleConstraint.png -------------------------------------------------------------------------------- /GuiA2p/Resources/wiki/Assembly2_AxialConstraint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/GuiA2p/Resources/wiki/Assembly2_AxialConstraint.png -------------------------------------------------------------------------------- /GuiA2p/Resources/wiki/Assembly2_BoltMultipleCircularEdges.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/GuiA2p/Resources/wiki/Assembly2_BoltMultipleCircularEdges.png -------------------------------------------------------------------------------- /GuiA2p/Resources/wiki/Assembly2_CheckAssembly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/GuiA2p/Resources/wiki/Assembly2_CheckAssembly.png -------------------------------------------------------------------------------- /GuiA2p/Resources/wiki/Assembly2_CircularEdgeConstraint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/GuiA2p/Resources/wiki/Assembly2_CircularEdgeConstraint.png -------------------------------------------------------------------------------- /GuiA2p/Resources/wiki/Assembly2_DegreesOfFreedomAnimation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/GuiA2p/Resources/wiki/Assembly2_DegreesOfFreedomAnimation.png -------------------------------------------------------------------------------- /GuiA2p/Resources/wiki/Assembly2_DraftMove.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/GuiA2p/Resources/wiki/Assembly2_DraftMove.png -------------------------------------------------------------------------------- /GuiA2p/Resources/wiki/Assembly2_FlipConstraint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/GuiA2p/Resources/wiki/Assembly2_FlipConstraint.png -------------------------------------------------------------------------------- /GuiA2p/Resources/wiki/Assembly2_Help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/GuiA2p/Resources/wiki/Assembly2_Help.png -------------------------------------------------------------------------------- /GuiA2p/Resources/wiki/Assembly2_ImportPart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/GuiA2p/Resources/wiki/Assembly2_ImportPart.png -------------------------------------------------------------------------------- /GuiA2p/Resources/wiki/Assembly2_ImportPartUpdate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/GuiA2p/Resources/wiki/Assembly2_ImportPartUpdate.png -------------------------------------------------------------------------------- /GuiA2p/Resources/wiki/Assembly2_LockRotation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/GuiA2p/Resources/wiki/Assembly2_LockRotation.png -------------------------------------------------------------------------------- /GuiA2p/Resources/wiki/Assembly2_MuxAssembly.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/GuiA2p/Resources/wiki/Assembly2_MuxAssembly.png -------------------------------------------------------------------------------- /GuiA2p/Resources/wiki/Assembly2_PartsList.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/GuiA2p/Resources/wiki/Assembly2_PartsList.png -------------------------------------------------------------------------------- /GuiA2p/Resources/wiki/Assembly2_Pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/GuiA2p/Resources/wiki/Assembly2_Pause.png -------------------------------------------------------------------------------- /GuiA2p/Resources/wiki/Assembly2_PlaneConstraint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/GuiA2p/Resources/wiki/Assembly2_PlaneConstraint.png -------------------------------------------------------------------------------- /GuiA2p/Resources/wiki/Assembly2_Play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/GuiA2p/Resources/wiki/Assembly2_Play.png -------------------------------------------------------------------------------- /GuiA2p/Resources/wiki/Assembly2_PreferencesAssembly2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/GuiA2p/Resources/wiki/Assembly2_PreferencesAssembly2.png -------------------------------------------------------------------------------- /GuiA2p/Resources/wiki/Assembly2_SolveConstraints.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/GuiA2p/Resources/wiki/Assembly2_SolveConstraints.png -------------------------------------------------------------------------------- /GuiA2p/Resources/wiki/Assembly2_SphericalSurfaceConstraint.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/GuiA2p/Resources/wiki/Assembly2_SphericalSurfaceConstraint.png -------------------------------------------------------------------------------- /GuiA2p/Resources/wiki/Assembly2_Stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/GuiA2p/Resources/wiki/Assembly2_Stop.png -------------------------------------------------------------------------------- /GuiA2p/Resources/wiki/Assembly2_WorkBenchIcon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/GuiA2p/Resources/wiki/Assembly2_WorkBenchIcon.png -------------------------------------------------------------------------------- /GuiA2p/Resources/wiki/Thumbs.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/GuiA2p/Resources/wiki/Thumbs.db -------------------------------------------------------------------------------- /Init.py: -------------------------------------------------------------------------------- 1 | #*************************************************************************** 2 | #* * 3 | #* Copyright (c) 2018 kbwbe * 4 | #* * 5 | #* Portions of code based on hamish's assembly 2 * 6 | #* * 7 | #* This program is free software; you can redistribute it and/or modify * 8 | #* it under the terms of the GNU Lesser General Public License (LGPL) * 9 | #* as published by the Free Software Foundation; either version 2 of * 10 | #* the License, or (at your option) any later version. * 11 | #* for detail see the LICENCE text file. * 12 | #* * 13 | #* This program is distributed in the hope that it will be useful, * 14 | #* but WITHOUT ANY WARRANTY; without even the implied warranty of * 15 | #* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 16 | #* GNU Library General Public License for more details. * 17 | #* * 18 | #* You should have received a copy of the GNU Library General Public * 19 | #* License along with this program; if not, write to the Free Software * 20 | #* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 21 | #* USA * 22 | #* * 23 | #*************************************************************************** 24 | 25 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/__init__.py -------------------------------------------------------------------------------- /a2p_constraintServices.py: -------------------------------------------------------------------------------- 1 | # *************************************************************************** 2 | # * * 3 | # * Copyright (c) 2020 kbwbe * 4 | # * * 5 | # * Portions of code based on hamish's assembly 2 * 6 | # * * 7 | # * This program is free software; you can redistribute it and/or modify * 8 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 9 | # * as published by the Free Software Foundation; either version 2 of * 10 | # * the License, or (at your option) any later version. * 11 | # * for detail see the LICENCE text file. * 12 | # * * 13 | # * This program is distributed in the hope that it will be useful, * 14 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 15 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 16 | # * GNU Library General Public License for more details. * 17 | # * * 18 | # * You should have received a copy of the GNU Library General Public * 19 | # * License along with this program; if not, write to the Free Software * 20 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 21 | # * USA * 22 | # * * 23 | # *************************************************************************** 24 | 25 | from PySide import QtGui 26 | 27 | import FreeCAD 28 | import FreeCADGui 29 | import a2plib 30 | import a2p_constraints 31 | 32 | translate = FreeCAD.Qt.translate 33 | 34 | 35 | def reAdjustConstraintDirections(doc): 36 | """ 37 | Recalculate value of property 'direction' and the sign of property 'offset' 38 | of all a2p-constraints of a document, in order to reach a solvable state if 39 | possible, especially used after updating of imported parts. 40 | """ 41 | result = [] # Added for Constraint Diagnostic function 42 | unknown_constraints = [] 43 | constraints = [obj for obj in doc.Objects if 'ConstraintInfo' in obj.Content] 44 | for c in constraints: 45 | try: # process as much constraints as possible 46 | if c.Type == 'pointIdentity': 47 | a2p_constraints.PointIdentityConstraint.recalculateMatingDirection(c) 48 | elif c.Type == 'pointOnLine': 49 | a2p_constraints.PointOnLineConstraint.recalculateMatingDirection(c) 50 | elif c.Type == 'pointOnPlane': 51 | a2p_constraints.PointOnPlaneConstraint.recalculateMatingDirection(c) 52 | elif c.Type == 'circularEdge': 53 | a2p_constraints.CircularEdgeConstraint.recalculateMatingDirection(c) 54 | elif c.Type == 'axial': 55 | a2p_constraints.AxialConstraint.recalculateMatingDirection(c) 56 | elif c.Type == 'axisParallel': 57 | a2p_constraints.AxisParallelConstraint.recalculateMatingDirection(c) 58 | elif c.Type == 'axisPlaneParallel': 59 | a2p_constraints.AxisPlaneParallelConstraint.recalculateMatingDirection(c) 60 | elif c.Type == 'axisPlaneAngle': 61 | a2p_constraints.AxisPlaneAngleConstraint.recalculateMatingDirection(c) 62 | elif c.Type == 'axisPlaneNormal': 63 | a2p_constraints.AxisPlaneNormalConstraint.recalculateMatingDirection(c) 64 | elif c.Type == 'planesParallel': 65 | a2p_constraints.PlanesParallelConstraint.recalculateMatingDirection(c) 66 | elif c.Type == 'plane': 67 | a2p_constraints.PlaneConstraint.recalculateMatingDirection(c) 68 | elif c.Type == 'angledPlanes': 69 | a2p_constraints.AngledPlanesConstraint.recalculateMatingDirection(c) 70 | elif c.Type == 'sphereCenterIdent': 71 | a2p_constraints.SphericalConstraint.recalculateMatingDirection(c) 72 | elif c.Type == 'CenterOfMass': 73 | a2p_constraints.CenterOfMassConstraint.recalculateMatingDirection(c) 74 | else: 75 | unknown_constraints.append(c.Type) 76 | except: 77 | # Print - All not Ok, we have errors 78 | print(translate( 79 | "A2plus", 80 | "Errors occurred during processing of {}").format( 81 | c.Label 82 | ) 83 | ) 84 | result.append(c.Name) # Added for Constraint Diagnostic function 85 | 86 | if len(unknown_constraints) > 0: 87 | # Print - All not Ok, we have problem 88 | print(translate( 89 | "A2plus_Constraints", 90 | "reAdjustConstraintDirections(): Found unknown constraints: {}" 91 | ).format( 92 | set(unknown_constraints) 93 | ) 94 | ) 95 | else: 96 | # Print - All Ok 97 | print(translate( 98 | "A2plus_Constraints", 99 | "reAdjustConstraintDirections(): All constraints are recalculated." 100 | )) 101 | 102 | return(result) # Added for Constraint Diagnostic function 103 | 104 | 105 | class a2p_reAdjustConstraintDirectionsCommand: 106 | def Activated(self): 107 | flags = QtGui.QMessageBox.StandardButton.Yes | QtGui.QMessageBox.StandardButton.No 108 | response = QtGui.QMessageBox.information( 109 | QtGui.QApplication.activeWindow(), 110 | translate("A2plus_Constraints", "Recalculate direction of constraints"), 111 | translate("A2plus_Constraints", "Do you really want to recalculate the directions of all constraints?"), 112 | flags 113 | ) 114 | if response == QtGui.QMessageBox.Yes: 115 | doc = FreeCAD.activeDocument() 116 | doc.openTransaction("Readjust constraint's directions") 117 | reAdjustConstraintDirections(doc) 118 | doc.commitTransaction() 119 | 120 | def IsActive(self): 121 | if FreeCAD.activeDocument() is None: 122 | return False 123 | return True 124 | 125 | def GetResources(self): 126 | return { 127 | 'Pixmap': a2plib.get_module_path()+'/icons/a2p_ReAdjustConstraints.svg', 128 | 'MenuText': translate("A2plus_Constraints", "Re-adjust directions of all constraints"), 129 | 'ToolTip': translate("A2plus_Constraints", "Re-adjust directions of all constraints to best fit") 130 | } 131 | 132 | 133 | FreeCADGui.addCommand('a2p_reAdjustConstraintDirectionsCommand', a2p_reAdjustConstraintDirectionsCommand()) 134 | -------------------------------------------------------------------------------- /a2p_convertPart.py: -------------------------------------------------------------------------------- 1 | #*************************************************************************** 2 | #* * 3 | #* Copyright (c) 2018 WandererFan * 4 | #* * 5 | #* Portions of code based on hamish's assembly 2 * 6 | #* * 7 | #* This program is free software; you can redistribute it and/or modify * 8 | #* it under the terms of the GNU Lesser General Public License (LGPL) * 9 | #* as published by the Free Software Foundation; either version 2 of * 10 | #* the License, or (at your option) any later version. * 11 | #* for detail see the LICENCE text file. * 12 | #* * 13 | #* This program is distributed in the hope that it will be useful, * 14 | #* but WITHOUT ANY WARRANTY; without even the implied warranty of * 15 | #* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 16 | #* GNU Library General Public License for more details. * 17 | #* * 18 | #* You should have received a copy of the GNU Library General Public * 19 | #* License along with this program; if not, write to the Free Software * 20 | #* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 21 | #* USA * 22 | #* * 23 | #*************************************************************************** 24 | 25 | import FreeCADGui 26 | import FreeCAD 27 | from PySide import QtGui 28 | import copy 29 | import time 30 | from a2p_translateUtils import * 31 | import a2plib 32 | from a2p_MuxAssembly import createTopoInfo 33 | from a2p_importedPart_class import Proxy_importPart 34 | from a2p_importedPart_class import Proxy_convertPart # for compat. 35 | from a2p_importedPart_class import ImportedPartViewProviderProxy # for compat. 36 | 37 | from a2p_topomapper import TopoMapper 38 | 39 | 40 | 41 | def updateConvertedPart(doc, obj): 42 | 43 | obj.timeLastImport = time.time() 44 | 45 | baseObject = doc.getObject(obj.localSourceObject) 46 | 47 | savedPlacement = obj.Placement 48 | obj.ViewObject.ShapeColor = baseObject.ViewObject.ShapeColor 49 | topoMapper = TopoMapper(doc) # imports the objects and creates toponames if wanted 50 | baseObject.ViewObject.Visibility = True #the topomapper ignores invisible shapes 51 | obj.muxInfo, obj.Shape, obj.ViewObject.DiffuseColor, obj.ViewObject.Transparency = \ 52 | topoMapper.createTopoNames(desiredShapeLabel = baseObject.Label) 53 | baseObject.ViewObject.Visibility = False #set baseObject invisible again. 54 | obj.Placement = savedPlacement 55 | 56 | for p in baseObject.ViewObject.PropertiesList: 57 | if hasattr(baseObject.ViewObject, p) and p not in [ 58 | 'DiffuseColor', 59 | 'Proxy', 60 | 'MappedColors', 61 | 'DisplayModeBody' 62 | ]: 63 | try: 64 | setattr(obj.ViewObject, p, getattr( baseObject.ViewObject, p)) 65 | except: 66 | pass # a lot of attributes related e.g. to sketcher 67 | 68 | if not a2plib.getPerFaceTransparency(): 69 | # switch of perFaceTransparency 70 | obj.ViewObject.Transparency = 1 71 | obj.ViewObject.Transparency = 0 # default = nontransparent 72 | 73 | obj.recompute() 74 | obj.ViewObject.Visibility = True 75 | 76 | def convertToImportedPart(doc, obj): 77 | """ 78 | convertToImportedPart(document, documentObject) - changes a regular FreeCAD object into an A2plus 79 | importedPart, adds the importedPart to the document and hides the original object from the 80 | document. Updating the assembly will also update the converted part 81 | """ 82 | partName = a2plib.findUnusedObjectName( obj.Label, document=doc ) 83 | partLabel = a2plib.findUnusedObjectLabel( obj.Label, document=doc ) 84 | filename = "converted" #or none? if obj is already in this doc, we don't know it's original filename 85 | 86 | newObj = doc.addObject("Part::FeaturePython",partName) 87 | newObj.Label = partLabel 88 | 89 | Proxy_importPart(newObj) 90 | ImportedPartViewProviderProxy(newObj.ViewObject) 91 | 92 | newObj.a2p_Version = a2plib.getA2pVersion() 93 | newObj.sourceFile = filename 94 | newObj.localSourceObject = obj.Name 95 | #newObj.sourcePart = "" 96 | newObj.setEditorMode("timeLastImport",1) 97 | newObj.timeLastImport = time.time() 98 | newObj.fixedPosition = False 99 | newObj.subassemblyImport = False 100 | newObj.setEditorMode("subassemblyImport",1) 101 | newObj.updateColors = True 102 | 103 | newObj.ViewObject.ShapeColor = obj.ViewObject.ShapeColor 104 | 105 | #------------------------------------------- 106 | # Initialize the new TopoMapper 107 | #------------------------------------------- 108 | topoMapper = TopoMapper(doc) 109 | newObj.muxInfo, newObj.Shape, newObj.ViewObject.DiffuseColor, newObj.ViewObject.Transparency = \ 110 | topoMapper.createTopoNames(desiredShapeLabel = obj.Label) 111 | 112 | for p in obj.ViewObject.PropertiesList: 113 | if hasattr(obj.ViewObject, p) and p not in [ 114 | 'DiffuseColor', 115 | 'Proxy', 116 | 'MappedColors', 117 | 'DisplayModeBody' 118 | ]: 119 | try: 120 | setattr(newObj.ViewObject, p, getattr( obj.ViewObject, p)) 121 | except: #some sketcher attributes e.g. 122 | pass 123 | 124 | if not a2plib.getPerFaceTransparency(): 125 | # switch of perFaceTransparency 126 | newObj.ViewObject.Transparency = 1 127 | newObj.ViewObject.Transparency = 0 # default = nontransparent 128 | 129 | 130 | newObj.Placement.Base = obj.Placement.Base 131 | newObj.Placement.Rotation = obj.Placement.Rotation 132 | 133 | obj.ViewObject.Visibility = False 134 | newObj.recompute() 135 | 136 | 137 | toolTip = \ 138 | translate("A2plus_convertPart", 139 | ''' 140 | Convert a part, created with 141 | another WB, to a full functional 142 | A2plus part. 143 | 144 | After converting, constraints 145 | can be applied. Also you can 146 | duplicate the converted part. 147 | 148 | For editing a converted part, 149 | hit the edit button and follow 150 | the instructions shown on screen. 151 | 152 | This function is useful, if 153 | you want to use e.g. fasteners 154 | within this workbench. 155 | ''' 156 | ) 157 | 158 | class a2p_ConvertPartCommand(): 159 | 160 | def GetResources(self): 161 | return {'Pixmap' : a2plib.get_module_path()+'/icons/a2p_ConvertPart.svg', 162 | # 'Accel' : "Shift+C", # a default shortcut (optional) 163 | 'MenuText': translate("A2plus_convertPart", "Convert a part to A2plus"), 164 | 'ToolTip' : toolTip 165 | } 166 | 167 | def Activated(self): 168 | doc = FreeCAD.activeDocument() 169 | selection = FreeCADGui.Selection.getSelection() 170 | for s in selection: 171 | if s.ViewObject.Visibility == False: 172 | msg = translate("A2plus_convertPart","Please select only visible parts!") 173 | QtGui.QMessageBox.information( 174 | QtGui.QApplication.activeWindow(), 175 | translate("A2plus_convertPart","Conversion Aborted"), 176 | msg 177 | ) 178 | return 179 | for s in selection: 180 | doc.openTransaction(u"part converted to A2plus") 181 | convertToImportedPart(doc, s) 182 | doc.commitTransaction() 183 | 184 | def IsActive(self): 185 | if FreeCAD.activeDocument() is None: 186 | return False 187 | 188 | selection = FreeCADGui.Selection.getSelection() 189 | if not selection: return False 190 | for s in selection: 191 | if a2plib.isA2pPart(s): return False 192 | if ( 193 | not s.isDerivedFrom("Part::Feature") and 194 | not s.Name.startswith('Sketch') 195 | ): 196 | return False 197 | return True 198 | 199 | FreeCADGui.addCommand('a2p_ConvertPart',a2p_ConvertPartCommand()) 200 | -------------------------------------------------------------------------------- /a2p_importedPart_class.py: -------------------------------------------------------------------------------- 1 | #*************************************************************************** 2 | #* * 3 | #* Copyright (c) 2019 kbwbe * 4 | #* * 5 | #* Portions of code based on hamish's assembly 2 * 6 | #* * 7 | #* This program is free software; you can redistribute it and/or modify * 8 | #* it under the terms of the GNU Lesser General Public License (LGPL) * 9 | #* as published by the Free Software Foundation; either version 2 of * 10 | #* the License, or (at your option) any later version. * 11 | #* for detail see the LICENCE text file. * 12 | #* * 13 | #* This program is distributed in the hope that it will be useful, * 14 | #* but WITHOUT ANY WARRANTY; without even the implied warranty of * 15 | #* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 16 | #* GNU Library General Public License for more details. * 17 | #* * 18 | #* You should have received a copy of the GNU Library General Public * 19 | #* License along with this program; if not, write to the Free Software * 20 | #* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 21 | #* USA * 22 | #* * 23 | #*************************************************************************** 24 | 25 | import FreeCAD 26 | import FreeCADGui 27 | import Part 28 | import os 29 | import copy 30 | import numpy 31 | from FreeCAD import Base 32 | from PySide import QtGui 33 | from a2p_translateUtils import * 34 | import a2plib 35 | #============================================================================== 36 | class Proxy_importPart: 37 | """The a2p importPart object.""" 38 | 39 | def __init__(self,obj): 40 | obj.Proxy = self 41 | Proxy_importPart.setProperties(self,obj) 42 | self.type = "a2p_importPart" 43 | 44 | def setProperties(self,obj): 45 | propList = obj.PropertiesList 46 | 47 | if not "objectType" in propList: 48 | obj.addProperty("App::PropertyString", "objectType", "importPart") 49 | obj.objectType = 'a2pPart' 50 | if not "a2p_Version" in propList: 51 | obj.addProperty("App::PropertyString", "a2p_Version", "importPart") 52 | obj.a2p_Version = a2plib.getA2pVersion() 53 | if not "sourceFile" in propList: 54 | obj.addProperty("App::PropertyFile", "sourceFile", "importPart") 55 | if not "sourcePart" in propList: 56 | obj.addProperty("App::PropertyString", "sourcePart", "importPart") 57 | if not "localSourceObject" in propList: 58 | obj.addProperty("App::PropertyString", "localSourceObject", "importPart") 59 | if not "muxInfo" in propList: 60 | obj.addProperty("App::PropertyStringList","muxInfo","importPart") 61 | if not "timeLastImport" in propList: 62 | obj.addProperty("App::PropertyFloat", "timeLastImport","importPart") 63 | if not "fixedPosition" in propList: 64 | obj.addProperty("App::PropertyBool","fixedPosition","importPart") 65 | if not "subassemblyImport" in propList: 66 | obj.addProperty("App::PropertyBool","subassemblyImport","importPart") 67 | obj.subassemblyImport = False 68 | if not "updateColors" in propList: 69 | obj.addProperty("App::PropertyBool","updateColors","importPart") 70 | obj.updateColors = True 71 | if a2plib.GRAPHICALDEBUG == True and not "debugmode" in propList: 72 | obj.addProperty("App::PropertyBool","debugmode","importPart") 73 | obj.debugmode = False 74 | if a2plib.GRAPHICALDEBUG == False and "debugmode" in propList: 75 | obj.removeProperty("debugmode") 76 | 77 | self.type = "a2p_importPart" 78 | 79 | def onDocumentRestored(self,obj): 80 | Proxy_importPart.setProperties(self,obj) 81 | 82 | def __getstate__(self): 83 | return None 84 | 85 | def __setstate__(self, state): 86 | return None 87 | 88 | def dumps(self): 89 | return None 90 | 91 | def loads(self, state): 92 | return None 93 | 94 | def execute(self, obj): 95 | # if a group containing LCS's exists, then move it 96 | # according to the imported part 97 | if hasattr(obj,"lcsLink"): 98 | if len(obj.lcsLink) > 0: 99 | lcsGroup = obj.lcsLink[0] 100 | lcsGroup.Placement = obj.Placement 101 | lcsGroup.purgeTouched() #untouch the lcsGroup, otherwise it stays touched. 102 | 103 | 104 | #============================================================================== 105 | class ImportedPartViewProviderProxy: 106 | """A ViewProvider for the a2p importPart object.""" 107 | 108 | def __init__(self,vobj): 109 | vobj.Proxy = self 110 | 111 | def claimChildren(self): 112 | if hasattr(self,'Object'): 113 | try: 114 | children = list() 115 | for obj in self.Object.InList: 116 | if a2plib.isA2pObject(obj): 117 | children.append(obj) 118 | if hasattr(self.Object,'lcsLink'): 119 | for obj in self.Object.lcsLink: 120 | children.append(obj) 121 | return children 122 | except: 123 | # FreeCAD has already deleted self.Object !! 124 | return[] 125 | else: 126 | return [] 127 | 128 | def onDelete(self, viewObject, subelements): # subelements is a tuple of strings 129 | if FreeCAD.activeDocument() != viewObject.Object.Document: 130 | return False # only delete objects in the active Document anytime !! 131 | obj = viewObject.Object 132 | doc = obj.Document 133 | 134 | deleteList = [] 135 | for c in doc.Objects: 136 | if 'ConstraintInfo' in c.Content: # a related Constraint 137 | if obj.Name in [ c.Object1, c.Object2 ]: 138 | deleteList.append(c) 139 | if len(deleteList) > 0: 140 | for c in deleteList: 141 | a2plib.removeConstraint(c) # also deletes the mirrors... 142 | 143 | if hasattr(obj,"lcsLink"): 144 | if len(obj.lcsLink)>0: 145 | lscGroup = doc.getObject(obj.lcsLink[0].Name) 146 | lscGroup.deleteContent(doc) 147 | doc.removeObject(lscGroup.Name) 148 | 149 | return True # If False is returned the object won't be deleted 150 | 151 | def getIcon(self): 152 | if hasattr(self,"Object"): 153 | if a2plib.isA2pSketch(self.Object): 154 | return a2plib.get_module_path()+'/icons/a2p_SketchReference.svg' 155 | if hasattr(self.Object,"sourceFile") and hasattr(self.Object,"sourcePart"): 156 | if self.Object.sourcePart is not None and self.Object.sourcePart !='': 157 | return a2plib.get_module_path()+'/icons/a2p_ObjReference.svg' 158 | if hasattr(self.Object,"subassemblyImport"): 159 | if self.Object.subassemblyImport: 160 | return ":/icons/a2p_Asm.svg" 161 | if hasattr(self.Object,"sourceFile"): 162 | if self.Object.sourceFile == 'converted': 163 | return ":/icons/a2p_ConvertPart.svg" 164 | return ":/icons/a2p_Obj.svg" 165 | 166 | def __getstate__(self): 167 | return None 168 | 169 | def __setstate__(self, state): 170 | return None 171 | 172 | def dumps(self): 173 | return None 174 | 175 | def loads(self, state): 176 | return None 177 | 178 | def attach(self, vobj): 179 | self.object_Name = vobj.Object.Name 180 | self.Object = vobj.Object 181 | 182 | def setupContextMenu(self, ViewObject, popup_menu): 183 | pass 184 | 185 | #============================================================================== 186 | class Proxy_muxAssemblyObj(Proxy_importPart): 187 | """ 188 | A wrapper for compatibility reasons... 189 | """ 190 | pass 191 | #============================================================================== 192 | class Proxy_convertPart(Proxy_importPart): 193 | """ 194 | A wrapper for compatibility reasons... 195 | """ 196 | pass 197 | #============================================================================== 198 | -------------------------------------------------------------------------------- /a2p_lcs_support.py: -------------------------------------------------------------------------------- 1 | #*************************************************************************** 2 | #* * 3 | #* Copyright (c) 2019 kbwbe * 4 | #* * 5 | #* This program is free software; you can redistribute it and/or modify * 6 | #* it under the terms of the GNU Lesser General Public License (LGPL) * 7 | #* as published by the Free Software Foundation; either version 2 of * 8 | #* the License, or (at your option) any later version. * 9 | #* for detail see the LICENCE text file. * 10 | #* * 11 | #* This program is distributed in the hope that it will be useful, * 12 | #* but WITHOUT ANY WARRANTY; without even the implied warranty of * 13 | #* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 14 | #* GNU Library General Public License for more details. * 15 | #* * 16 | #* You should have received a copy of the GNU Library General Public * 17 | #* License along with this program; if not, write to the Free Software * 18 | #* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 19 | #* USA * 20 | #* * 21 | #*************************************************************************** 22 | 23 | import FreeCAD 24 | import FreeCADGui 25 | from a2p_translateUtils import * 26 | 27 | 28 | #============================================================================== 29 | def LCS_Group_deleteContent(_self,doc): 30 | """ 31 | LCS_Group featurepython extending function deleteContent 32 | """ 33 | if len(_self.Group) > 0: 34 | deleteList = [] 35 | deleteList.extend(_self.Group) 36 | _self.Group = [] 37 | for ob in deleteList: 38 | doc.removeObject(ob.Name) # delete the imported LCS' 39 | 40 | #============================================================================== 41 | 42 | class LCS_Group(object): 43 | def __init__(self, obInstance): 44 | obInstance.addExtension('App::GeoFeatureGroupExtensionPython', self) 45 | obInstance.addProperty("App::PropertyString", "Owner").Owner = '' 46 | obInstance.setEditorMode('Owner', 1) 47 | obInstance.setEditorMode('Placement', 1) # read-only # KBWBE: does not work... 48 | obInstance.deleteContent = LCS_Group_deleteContent # add a function to this featurepython class 49 | 50 | def execute(self, obj): 51 | pass 52 | 53 | def onChanged(self, obj, prop): 54 | pass 55 | 56 | #============================================================================== 57 | 58 | class VP_LCS_Group(object): 59 | def __init__(self,vobj): 60 | vobj.addExtension('Gui::ViewProviderGeoFeatureGroupExtensionPython', self) 61 | vobj.Proxy = self 62 | 63 | def attach(self, vobj): 64 | self.ViewObject = vobj 65 | self.Object = vobj.Object 66 | 67 | # restore lost functions to featurePython object during reload 68 | if not hasattr(self.Object,'deleteContent'): 69 | self.Object.deleteContent = LCS_Group_deleteContent 70 | 71 | def onDelete(self, viewObject, subelements): # subelements is a tuple of strings 72 | if FreeCAD.activeDocument() != viewObject.Object.Document: 73 | return False # only delete objects in the active Document anytime !! 74 | obj = viewObject.Object 75 | doc = obj.Document 76 | try: 77 | if obj.Owner != '': 78 | owner = doc.getObject(obj.Owner) 79 | owner.lcsLink = [] # delete link entry within owning A2p part. 80 | except: 81 | pass 82 | obj.deleteContent(doc) # Clean up this group complete with all content 83 | return True 84 | 85 | def getIcon(self): 86 | return ":/icons/a2p_LCS_group.svg" 87 | 88 | def __getstate__(self): 89 | return None 90 | 91 | def __setstate__(self, state): 92 | return None 93 | 94 | def dumps(self): 95 | return None 96 | 97 | def loads(self, state): 98 | return None 99 | 100 | #============================================================================== 101 | 102 | def getListOfLCS(targetDoc,sourceDoc): 103 | lcsOut = [] 104 | for sourceOb in sourceDoc.Objects: 105 | if ( 106 | sourceOb.Name.startswith("Local_CS") or 107 | sourceOb.Name.startswith("App__Placement") or 108 | sourceOb.Name.startswith("a2pLCS") or 109 | sourceOb.Name.startswith("PartDesign__CoordinateSystem") 110 | ): 111 | newLCS = targetDoc.addObject("PartDesign::CoordinateSystem","a2pLCS") 112 | pl = sourceOb.getGlobalPlacement() 113 | newLCS.Placement = pl 114 | newLCS.setEditorMode('Placement', 1) #read-only # KBWBE: does not work... 115 | lcsOut.append(newLCS) 116 | return lcsOut 117 | -------------------------------------------------------------------------------- /a2p_observers.py: -------------------------------------------------------------------------------- 1 | #*************************************************************************** 2 | #* * 3 | #* Copyright (c) 2018 kbwbe * 4 | #* * 5 | #* This program is free software; you can redistribute it and/or modify * 6 | #* it under the terms of the GNU Lesser General Public License (LGPL) * 7 | #* as published by the Free Software Foundation; either version 2 of * 8 | #* the License, or (at your option) any later version. * 9 | #* for detail see the LICENCE text file. * 10 | #* * 11 | #* This program is distributed in the hope that it will be useful, * 12 | #* but WITHOUT ANY WARRANTY; without even the implied warranty of * 13 | #* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 14 | #* GNU Library General Public License for more details. * 15 | #* * 16 | #* You should have received a copy of the GNU Library General Public * 17 | #* License along with this program; if not, write to the Free Software * 18 | #* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 19 | #* USA * 20 | #* * 21 | #*************************************************************************** 22 | 23 | 24 | import FreeCADGui,FreeCAD 25 | from PySide import QtGui, QtCore 26 | import os, copy, time 27 | from a2p_translateUtils import * 28 | import a2plib 29 | 30 | 31 | class RedoUndoObserver(object): 32 | def slotRedoDocument(self,doc): 33 | a2plib.a2p_repairTreeView() 34 | def slotUndoDocument(self,doc): 35 | a2plib.a2p_repairTreeView() 36 | 37 | redoUndoObserver = RedoUndoObserver() 38 | -------------------------------------------------------------------------------- /a2p_partinformation.py: -------------------------------------------------------------------------------- 1 | #*************************************************************************** 2 | #* * 3 | #* Copyright (c) 2018 kbwbe * 4 | #* * 5 | #* This program is free software; you can redistribute it and/or modify * 6 | #* it under the terms of the GNU Lesser General Public License (LGPL) * 7 | #* as published by the Free Software Foundation; either version 2 of * 8 | #* the License, or (at your option) any later version. * 9 | #* for detail see the LICENCE text file. * 10 | #* * 11 | #* This program is distributed in the hope that it will be useful, * 12 | #* but WITHOUT ANY WARRANTY; without even the implied warranty of * 13 | #* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 14 | #* GNU Library General Public License for more details. * 15 | #* * 16 | #* You should have received a copy of the GNU Library General Public * 17 | #* License along with this program; if not, write to the Free Software * 18 | #* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 19 | #* USA * 20 | #* * 21 | #*************************************************************************** 22 | 23 | import FreeCAD, FreeCADGui 24 | import Spreadsheet 25 | from PySide import QtGui, QtCore 26 | import os, copy, time, sys, platform 27 | from a2p_translateUtils import * 28 | import a2plib 29 | from a2p_partlistglobals import PARTLIST_COLUMN_NAMES 30 | 31 | from a2p_partlistglobals import ( 32 | PARTINFORMATION_SHEET_NAME, 33 | PARTINFORMATION_SHEET_LABEL 34 | ) 35 | 36 | 37 | 38 | #------------------------------------------------------------------------------ 39 | toolTip = \ 40 | translate("A2plus", 41 | """ 42 | Create a spreadsheet for ordering or 43 | logistics information. 44 | 45 | The created spreadsheet can be found 46 | within the tree view. 47 | 48 | Please fill in your information. 49 | This spreadsheet will be read out 50 | by the parts list function of A2plus. 51 | """ 52 | ) 53 | 54 | class a2p_CreatePartInformationSheet_Command: 55 | 56 | def Activated(self): 57 | doc = FreeCAD.activeDocument() 58 | if doc is None: 59 | QtGui.QMessageBox.information( QtGui.QApplication.activeWindow(), 60 | translate("A2plus","No active document found!"), 61 | translate("A2plus","You have to open a FCStd file first.") 62 | ) 63 | return 64 | 65 | try: 66 | found = doc.getObject(PARTINFORMATION_SHEET_NAME) 67 | if found is not None: return # object already exists 68 | except: 69 | pass # proceed and create the shett 70 | 71 | # create a spreadsheet with a special reserved name... 72 | ss = doc.addObject('Spreadsheet::Sheet',PARTINFORMATION_SHEET_NAME) 73 | ss.Label = PARTINFORMATION_SHEET_LABEL 74 | 75 | for idx,name in enumerate(PARTLIST_COLUMN_NAMES): 76 | ss.set('A'+str(idx+1), name) 77 | ss.set('B'+str(idx+1), '') 78 | 79 | ss.setColumnWidth('A', 220) 80 | ss.setColumnWidth('B', 300) 81 | ss.setBackground('A1:A'+str(len(PARTLIST_COLUMN_NAMES)), (0.000000,1.000000,0.000000,1.000000)) 82 | ss.setBackground('B1:B'+str(len(PARTLIST_COLUMN_NAMES)), (0.85,0.85,0.85,1.000000)) 83 | doc.recompute() 84 | FreeCAD.Console.PrintMessage(translate("A2p_BoM", "#PARTINFO# spreadsheet has been created") + "\n") 85 | 86 | def GetResources(self): 87 | return { 88 | 'Pixmap' : ':/icons/a2p_PartsInfo.svg', 89 | 'MenuText': translate("A2plus", "Create a spreadsheet for ordering or logistics information"), 90 | 'ToolTip' : toolTip 91 | } 92 | 93 | FreeCADGui.addCommand('a2p_CreatePartInformationSheet_Command', a2p_CreatePartInformationSheet_Command()) 94 | #------------------------------------------------------------------------------ 95 | -------------------------------------------------------------------------------- /a2p_partlistglobals.py: -------------------------------------------------------------------------------- 1 | #*************************************************************************** 2 | #* * 3 | #* Copyright (c) 2018 kbwbe * 4 | #* * 5 | #* This program is free software; you can redistribute it and/or modify * 6 | #* it under the terms of the GNU Lesser General Public License (LGPL) * 7 | #* as published by the Free Software Foundation; either version 2 of * 8 | #* the License, or (at your option) any later version. * 9 | #* for detail see the LICENCE text file. * 10 | #* * 11 | #* This program is distributed in the hope that it will be useful, * 12 | #* but WITHOUT ANY WARRANTY; without even the implied warranty of * 13 | #* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 14 | #* GNU Library General Public License for more details. * 15 | #* * 16 | #* You should have received a copy of the GNU Library General Public * 17 | #* License along with this program; if not, write to the Free Software * 18 | #* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 19 | #* USA * 20 | #* * 21 | #*************************************************************************** 22 | 23 | from a2p_translateUtils import * 24 | 25 | PARTINFORMATION_SHEET_NAME = "_PARTINFO_" 26 | PARTINFORMATION_SHEET_LABEL = "#" + translate("A2p_BoM", "PARTINFO") + "#" 27 | 28 | # BOM = BillOfMaterials... 29 | BOM_SHEET_NAME = "_PARTSLIST_" 30 | BOM_SHEET_LABEL = "#" + translate("A2p_BoM", "PARTSLIST") + "#" 31 | 32 | # CutListOptimizer BillOfMaterials... 33 | CLO_BOM_SHEET_NAME = "_PARTSLIST_CUT-LIST-OPTIMIZER_" 34 | CLO_BOM_SHEET_LABEL = "#" + translate("A2p_BoM", "CLO_PARTSLIST") + "#" 35 | 36 | BOM_MAX_COLS = 10 37 | BOM_MAX_LENGTH = 150 38 | 39 | CLO_BOM_MAX_COLS = 10 40 | CLO_BOM_MAX_LENGTH = BOM_MAX_LENGTH 41 | 42 | PARTLIST_COLUMN_NAMES = [ 43 | translate("A2p_BoM", "IDENTNO"), 44 | translate("A2p_BoM", "DESCRIPTION"), 45 | translate("A2p_BoM", "SUPPLIER"), 46 | translate("A2p_BoM", "SUPP.IDENTNO"), 47 | translate("A2p_BoM", "SUPP.DESCRIPTION"), 48 | translate("A2p_BoM", "(FILENAME)") 49 | ] 50 | 51 | CLO_PARTLIST_COLUMN_NAMES = [ 52 | translate("A2p_BoM", "Length"), 53 | translate("A2p_BoM", "Width"), 54 | translate("A2p_BoM", "Qty"), 55 | translate("A2p_BoM", "Material"), 56 | translate("A2p_BoM", "Label"), 57 | translate("A2p_BoM", "Enabled") 58 | ] 59 | -------------------------------------------------------------------------------- /a2p_recursiveUpdatePlanner.py: -------------------------------------------------------------------------------- 1 | # *************************************************************************** 2 | # * * 3 | # * Copyright (c) 2018 kbwbe * 4 | # * * 5 | # * This program is free software; you can redistribute it and/or modify * 6 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 7 | # * as published by the Free Software Foundation; either version 2 of * 8 | # * the License, or (at your option) any later version. * 9 | # * for detail see the LICENCE text file. * 10 | # * * 11 | # * This program is distributed in the hope that it will be useful, * 12 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 13 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 14 | # * GNU Library General Public License for more details. * 15 | # * * 16 | # * You should have received a copy of the GNU Library General Public * 17 | # * License along with this program; if not, write to the Free Software * 18 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 19 | # * USA * 20 | # * * 21 | # *************************************************************************** 22 | 23 | import os 24 | from PySide import QtGui 25 | 26 | import FreeCAD 27 | import FreeCADGui 28 | import a2plib 29 | from a2p_importpart import updateImportedParts 30 | from a2p_simpleXMLreader import FCdocumentReader 31 | 32 | translate = FreeCAD.Qt.translate 33 | 34 | 35 | def createUpdateFileList( 36 | importPath, 37 | parentAssemblyDir, 38 | filesToUpdate, 39 | recursive=False, 40 | selectedFiles=[] # only update parts with these sourceFiles 41 | ): 42 | 43 | # do not update converted parts 44 | print( 45 | translate( 46 | "A2plus", 47 | "createUpdateFileList(): ImportPath = {}").format( 48 | importPath 49 | ) 50 | ) 51 | 52 | if a2plib.to_bytes(importPath) == b'converted': 53 | return False, filesToUpdate 54 | 55 | fileNameInProject = a2plib.findSourceFileInProject( 56 | importPath, 57 | parentAssemblyDir 58 | ) 59 | workingDir, basicFileName = os.path.split(fileNameInProject) 60 | docReader1 = FCdocumentReader() 61 | 62 | docReader1.openDocument(fileNameInProject) 63 | needToUpdate = False 64 | subAsmNeedsUpdate = False 65 | for ob in docReader1.getA2pObjects(): 66 | 67 | if a2plib.to_bytes(ob.getA2pSource()) == b'converted': 68 | print( 69 | translate( 70 | "A2plus", "Did not update converted part '{}'").format( 71 | ob.name 72 | ) 73 | ) 74 | continue 75 | 76 | # Only update parts which are selected by the user... 77 | fDir, fName = os.path.split(ob.getA2pSource()) 78 | if len(selectedFiles) > 0 and fName not in selectedFiles: 79 | continue 80 | 81 | if ob.isSubassembly() and recursive: 82 | subAsmNeedsUpdate, filesToUpdate = createUpdateFileList( 83 | ob.getA2pSource(), 84 | workingDir, 85 | filesToUpdate, 86 | recursive 87 | ) 88 | if subAsmNeedsUpdate: 89 | needToUpdate = True 90 | 91 | objFileNameInProject = a2plib.findSourceFileInProject( 92 | ob.getA2pSource(), 93 | workingDir 94 | ) 95 | mtime = os.path.getmtime(objFileNameInProject) 96 | if ob.getTimeLastImport() < mtime: 97 | needToUpdate = True 98 | 99 | if needToUpdate: 100 | if fileNameInProject not in filesToUpdate: 101 | filesToUpdate.append(fileNameInProject) 102 | 103 | return needToUpdate, filesToUpdate 104 | 105 | 106 | class a2p_recursiveUpdateImportedPartsCommand: 107 | 108 | def Activated(self): 109 | a2plib.setAutoSolve(True) # makes no sense without autosolve = ON 110 | doc = FreeCAD.activeDocument() 111 | 112 | fileName = doc.FileName 113 | workingDir, basicFileName = os.path.split(fileName) 114 | 115 | selectedFiles = [] 116 | partial = False 117 | selection = [s for s in FreeCADGui.Selection.getSelection() 118 | if s.Document == FreeCAD.ActiveDocument and 119 | (a2plib.isA2pPart(s) or a2plib.isA2pSketch(s)) 120 | ] 121 | if selection and len(selection) > 0: 122 | flags = QtGui.QMessageBox.StandardButton.Yes | QtGui.QMessageBox.StandardButton.No 123 | msg = translate("A2plus", "Do you want to update the selected parts only?") 124 | response = QtGui.QMessageBox.information( 125 | QtGui.QApplication.activeWindow(), 126 | translate("A2plus", "RECURSIVE UPDATE"), 127 | msg, 128 | flags 129 | ) 130 | if response == QtGui.QMessageBox.Yes: 131 | for s in selection: 132 | fDir, fName = os.path.split(s.sourceFile) 133 | selectedFiles.append(fName) 134 | partial = True 135 | 136 | filesToUpdate = [] 137 | subAsmNeedsUpdate, filesToUpdate = createUpdateFileList( 138 | fileName, 139 | workingDir, 140 | filesToUpdate, 141 | True, 142 | selectedFiles 143 | ) 144 | 145 | for f in filesToUpdate: 146 | # update necessary documents 147 | 148 | # look only for filenames, not paths, as there are problems on WIN10 (Address-translation??) 149 | importDoc = None 150 | importDocIsOpen = False 151 | requestedFile = os.path.split(f)[1] 152 | for d in FreeCAD.listDocuments().values(): 153 | recentFile = os.path.split(d.FileName)[1] 154 | if requestedFile == recentFile: 155 | importDoc = d # file is already open... 156 | importDocIsOpen = True 157 | break 158 | 159 | if not importDocIsOpen: 160 | if f.lower().endswith('.fcstd'): 161 | importDoc = FreeCAD.openDocument(f) 162 | elif f.lower().endswith('.stp') or f.lower().endswith('.step'): 163 | import ImportGui 164 | fname = os.path.splitext(os.path.basename(f))[0] 165 | FreeCAD.newDocument(fname) 166 | newname = FreeCAD.ActiveDocument.Name 167 | FreeCAD.setActiveDocument(newname) 168 | ImportGui.insert(filename, newname) 169 | importDoc = FreeCAD.ActiveDocument 170 | else: 171 | QtGui.QMessageBox.information( 172 | QtGui.QApplication.activeWindow(), 173 | translate("A2plus", "Value Error"), 174 | translate("A2plus", "A part can only be imported from a FreeCAD '*.FCStd' file") 175 | ) 176 | return 177 | 178 | if importDoc == doc and partial is True: 179 | updateImportedParts(importDoc, True) 180 | else: 181 | updateImportedParts(importDoc) 182 | 183 | FreeCADGui.updateGui() 184 | importDoc.save() 185 | FreeCAD.Console.PrintMessage( 186 | "===== " + translate( 187 | "A2plus", 188 | "Assembly '{}' has been updated!").format( 189 | importDoc.FileName 190 | ) + " =====\n" 191 | ) 192 | if importDoc != doc: 193 | FreeCAD.closeDocument(importDoc.Name) 194 | 195 | def GetResources(self): 196 | return { 197 | 'Pixmap': ':/icons/a2p_RecursiveUpdate.svg', 198 | 'MenuText': translate("A2plus", "Update imports recursively"), 199 | 'ToolTip': translate( 200 | "A2plus", 201 | "Update parts, which have been" + "\n" + 202 | "imported to the assembly." + "\n\n" + 203 | "(If you modify a part in an" + "\n" + 204 | "external file, the new shape" + "\n" + 205 | "is taken to the assembly by" + "\n" + 206 | "this function)." + "\n\n" + 207 | "This command does this recursively" + "\n" + 208 | "over all involved subassemblies." + "\n\n" + 209 | "Subassemblies are updated," + "\n" + 210 | "if necessary, too." 211 | ) 212 | } 213 | 214 | def IsActive(self): 215 | doc = FreeCAD.activeDocument() 216 | if doc is None: return False 217 | return True 218 | 219 | 220 | FreeCADGui.addCommand('a2p_recursiveUpdateImportedPartsCommand', a2p_recursiveUpdateImportedPartsCommand()) 221 | -------------------------------------------------------------------------------- /a2p_searchConstraintConflicts.py: -------------------------------------------------------------------------------- 1 | #*************************************************************************** 2 | #* * 3 | #* Copyright (c) 2020 kbwbe * 4 | #* * 5 | #* Based on Work of Dan Miel (Thank you) * 6 | #* * 7 | #* This program is free software; you can redistribute it and/or modify * 8 | #* it under the terms of the GNU Lesser General Public License (LGPL) * 9 | #* as published by the Free Software Foundation; either version 2 of * 10 | #* the License, or (at your option) any later version. * 11 | #* for detail see the LICENCE text file. * 12 | #* * 13 | #* This program is distributed in the hope that it will be useful, * 14 | #* but WITHOUT ANY WARRANTY; without even the implied warranty of * 15 | #* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 16 | #* GNU Library General Public License for more details. * 17 | #* * 18 | #* You should have received a copy of the GNU Library General Public * 19 | #* License along with this program; if not, write to the Free Software * 20 | #* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 21 | #* USA * 22 | #* * 23 | #*************************************************************************** 24 | 25 | import FreeCAD 26 | import FreeCADGui 27 | from PySide import QtGui 28 | 29 | from a2p_translateUtils import * 30 | import a2plib 31 | import a2p_solversystem 32 | 33 | #============================================================================== 34 | 35 | toolTipMessage = \ 36 | translate("A2plus_searchConstraintConflicts", 37 | ''' 38 | Conflict finder tool: 39 | 40 | Resolves conflicting constraints by 41 | trying to solve them one after another 42 | ''' 43 | ) 44 | 45 | class a2p_SearchConstraintConflictsCommand: 46 | ''' 47 | Search conflicting constraints by solving them one after each other. 48 | ''' 49 | def Activated(self): 50 | doc = FreeCAD.activeDocument() 51 | 52 | workList = [] 53 | constraints = [ obj for obj in doc.Objects if 'ConstraintInfo' in obj.Content] 54 | 55 | if len(constraints) == 0: 56 | flags = QtGui.QMessageBox.StandardButton.Yes 57 | QtGui.QMessageBox.information( 58 | QtGui.QApplication.activeWindow(), 59 | translate("A2plus_searchConstraintConflicts",'Searching for conflicting constraints'), 60 | translate("A2plus_searchConstraintConflicts",'There are no a2p constraints within this document.'), 61 | flags 62 | ) 63 | return 64 | 65 | a2plib.SHOW_WARNING_FLOATING_PARTS = False 66 | for c in constraints: 67 | workList.append(c) 68 | solved = a2p_solversystem.solveConstraints(doc,matelist = workList, showFailMessage=False) 69 | if solved == False: 70 | cMirrorName = c.ViewObject.Proxy.mirror_name 71 | cmirror = doc.getObject(cMirrorName) 72 | ob1 = doc.getObject(c.Object1) 73 | ob2 = doc.getObject(c.Object2) 74 | message = \ 75 | translate("A2plus_searchConstraintConflicts", 76 | u''' 77 | The following constraint-pair is conflicting 78 | with previously defined constraints: 79 | 80 | constraint : {} 81 | with mirror: {} 82 | 83 | The constraint-pair belongs to the objects: 84 | 85 | object1: {} 86 | object2: {} 87 | 88 | Do you want to delete this constraint-pair? 89 | ''').format( 90 | c.Label, 91 | cmirror.Label, 92 | ob1.Label, 93 | ob2.Label 94 | ) 95 | flags = QtGui.QMessageBox.StandardButton.Yes | QtGui.QMessageBox.StandardButton.No 96 | response = QtGui.QMessageBox.information( 97 | QtGui.QApplication.activeWindow(), 98 | translate("A2plus_searchConstraintConflicts",'Searching for conflicting constraints'), 99 | message, 100 | flags 101 | ) 102 | if response == QtGui.QMessageBox.Yes: 103 | a2plib.removeConstraint(c) 104 | a2plib.SHOW_WARNING_FLOATING_PARTS = True 105 | 106 | def IsActive(self): 107 | if FreeCAD.activeDocument() is None: return False 108 | return True 109 | 110 | def GetResources(self): 111 | return { 112 | 'Pixmap' : a2plib.get_module_path()+'/icons/a2p_SearchConstraintConflicts.svg', 113 | 'MenuText': translate("A2plus_searchConstraintConflicts", "Identify conflicting constraints"), 114 | 'ToolTip' : toolTipMessage 115 | } 116 | FreeCADGui.addCommand('a2p_SearchConstraintConflictsCommand', a2p_SearchConstraintConflictsCommand()) 117 | #============================================================================== 118 | 119 | 120 | -------------------------------------------------------------------------------- /a2p_translateUtils.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf8 -*- 2 | 3 | #*************************************************************************** 4 | #* * 5 | #* Copyright (c) 2020 kbwbe * 6 | #* * 7 | #* * 8 | #* This program is free software; you can redistribute it and/or modify * 9 | #* it under the terms of the GNU Lesser General Public License (LGPL) * 10 | #* as published by the Free Software Foundation; either version 2 of * 11 | #* the License, or (at your option) any later version. * 12 | #* for detail see the LICENCE text file. * 13 | #* * 14 | #* This program is distributed in the hope that it will be useful, * 15 | #* but WITHOUT ANY WARRANTY; without even the implied warranty of * 16 | #* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 17 | #* GNU Library General Public License for more details. * 18 | #* * 19 | #* You should have received a copy of the GNU Library General Public * 20 | #* License along with this program; if not, write to the Free Software * 21 | #* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 22 | #* USA * 23 | #* * 24 | #*************************************************************************** 25 | 26 | import FreeCAD 27 | 28 | 29 | if FreeCAD.GuiUp: 30 | from PySide.QtCore import QT_TRANSLATE_NOOP 31 | from DraftGui import translate 32 | else: 33 | def QT_TRANSLATE_NOOP(context, text): 34 | return text 35 | 36 | def translate(context, text): 37 | return text 38 | 39 | 40 | def tr_(text): 41 | return translate("A2plus", text) 42 | -------------------------------------------------------------------------------- /compileA2pResources.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # -*- coding: utf-8 -*- 3 | #*************************************************************************** 4 | #* * 5 | #* Copyright (c) 2019 kbwbe * 6 | #* * 7 | #* Portions of code based on hamish's assembly 2 * 8 | #* * 9 | #* This program is free software; you can redistribute it and/or modify * 10 | #* it under the terms of the GNU Lesser General Public License (LGPL) * 11 | #* as published by the Free Software Foundation; either version 2 of * 12 | #* the License, or (at your option) any later version. * 13 | #* for detail see the LICENCE text file. * 14 | #* * 15 | #* This program is distributed in the hope that it will be useful, * 16 | #* but WITHOUT ANY WARRANTY; without even the implied warranty of * 17 | #* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 18 | #* GNU Library General Public License for more details. * 19 | #* * 20 | #* You should have received a copy of the GNU Library General Public * 21 | #* License along with this program; if not, write to the Free Software * 22 | #* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 23 | #* USA * 24 | #* * 25 | #*************************************************************************** 26 | 27 | # This script compiles the A2plus icons for py2 and py3 28 | # For Linux only 29 | # Start this file in A2plus main directory 30 | # Make sure pyside-rcc is installed 31 | 32 | import os, glob 33 | 34 | qrc_filename = 'temp.qrc' 35 | if os.path.exists(qrc_filename): 36 | os.remove(qrc_filename) 37 | 38 | 39 | qrc = ''' 40 | \t''' 41 | for fn in glob.glob('./icons/*.svg'): 42 | qrc = qrc + '\n\t\t%s' % fn 43 | qrc = qrc + '''\n\t 44 | ''' 45 | 46 | print(qrc) 47 | 48 | f = open(qrc_filename,'w') 49 | f.write(qrc) 50 | f.close() 51 | 52 | #os.system( 53 | # 'pyside-rcc -o a2p_Resources2.py {}'.format( 54 | # qrc_filename 55 | # ) 56 | # ) 57 | os.system( 58 | 'rcc -o a2p_Resources3.py -g python {}'.format( 59 | qrc_filename 60 | ) 61 | ) 62 | 63 | # Need to determine if we are on a system using PySide2 or PySide6 64 | try: 65 | import PySide6 # try Qt6 first 66 | os.system( 67 | 'pyside6-lupdate *.py -ts translations/A2plus.ts -verbose' 68 | ) 69 | except ImportError: 70 | os.system( 71 | 'pyside2-lupdate *.py -ts translations/A2plus.ts -verbose' 72 | ) 73 | ''' 74 | os.system( 75 | 'lrelease "translations/A2plus.ts"' 76 | ) 77 | ''' 78 | 79 | os.remove(qrc_filename) 80 | 81 | """NOTES: (adding Translations...) 82 | # gather the strings from the .py files of the WB 83 | pyside-lupdate *.py -ts translations/pyfiles.ts -verbose 84 | 85 | # merge ts files if there is more then one... 86 | # lconvert is not found on my system without path ??? 87 | /usr/lib/x86_64-linux-gnu/qt5/bin/lconvert -i translations/pyfiles.ts another.ts -o translations/A2plus 88 | 89 | # convert .ts files to .qm files (compiled translations) 90 | lrelease "translations/A2plus.ts" 91 | 92 | """ 93 | -------------------------------------------------------------------------------- /crowdin.yml: -------------------------------------------------------------------------------- 1 | files: 2 | - source: /translations/A2plus.ts 3 | translation: /translations/A2plus_%two_letters_code%.ts 4 | -------------------------------------------------------------------------------- /icons/CD_ConstraintChecker.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 35 | 37 | 39 | 42 | 47 | 48 | 50 | 53 | 58 | 59 | 61 | 65 | 70 | 71 | 79 | 86 | 94 | 95 | 98 | 101 | 107 | 115 | 125 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /icons/a2p_CheckAssembly.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 24 | 28 | 32 | 33 | 43 | 45 | 49 | 53 | 54 | 63 | 72 | 73 | 93 | 100 | 101 | 103 | 104 | 106 | image/svg+xml 107 | 109 | 110 | 111 | 112 | 117 | 120 | 122 | 128 | 134 | 135 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /icons/a2p_DeleteConnections.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 25 | 29 | 33 | 34 | 37 | 41 | 45 | 46 | 56 | 66 | 67 | 89 | 97 | 98 | 100 | 101 | 103 | image/svg+xml 104 | 106 | 107 | 108 | 109 | 114 | 116 | 122 | 128 | 134 | 140 | 146 | 152 | 153 | 154 | 155 | -------------------------------------------------------------------------------- /icons/a2p_EditUndo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 25 | 27 | 30 | 34 | 38 | 39 | 42 | 46 | 50 | 51 | 54 | 58 | 62 | 63 | 66 | 70 | 74 | 75 | 86 | 89 | 93 | 97 | 98 | 108 | 117 | 126 | 137 | 138 | 161 | 163 | 164 | 166 | image/svg+xml 167 | 169 | 170 | 171 | Jakub Steiner 172 | 173 | 174 | http://jimmac.musichall.cz 175 | 177 | Edit Undo 178 | 179 | 180 | edit 181 | undo 182 | revert 183 | 184 | 185 | 186 | 188 | 190 | 192 | 194 | 196 | 198 | 200 | 201 | 202 | 203 | 207 | 210 | 218 | 223 | 228 | 233 | 234 | 235 | 236 | -------------------------------------------------------------------------------- /icons/a2p_Obj.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 25 | 29 | 33 | 34 | 44 | 54 | 64 | 67 | 71 | 75 | 76 | 86 | 96 | 99 | 103 | 107 | 108 | 111 | 115 | 119 | 120 | 121 | 142 | 151 | 152 | 154 | 155 | 157 | image/svg+xml 158 | 160 | Path-Stock 161 | 2015-07-04 162 | http://www.freecadweb.org/wiki/index.php?title=Artwork 163 | 164 | 165 | FreeCAD 166 | 167 | 168 | FreeCAD/src/Mod/Path/Gui/Resources/icons/Path-Stock.svg 169 | 170 | 171 | FreeCAD LGPL2+ 172 | 173 | 174 | https://www.gnu.org/copyleft/lesser.html 175 | 176 | 177 | [agryson] Alexander Gryson 178 | 179 | 180 | 181 | 182 | 183 | 187 | 189 | 195 | 201 | 207 | 213 | 219 | 225 | 231 | 237 | 238 | 239 | 240 | -------------------------------------------------------------------------------- /icons/a2p_ObjReference.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 25 | 29 | 33 | 34 | 44 | 54 | 64 | 67 | 71 | 75 | 76 | 86 | 96 | 99 | 103 | 107 | 108 | 111 | 115 | 119 | 120 | 130 | 133 | 137 | 141 | 142 | 143 | 164 | 173 | 174 | 176 | 177 | 179 | image/svg+xml 180 | 182 | Path-Stock 183 | 2015-07-04 184 | http://www.freecadweb.org/wiki/index.php?title=Artwork 185 | 186 | 187 | FreeCAD 188 | 189 | 190 | FreeCAD/src/Mod/Path/Gui/Resources/icons/Path-Stock.svg 191 | 192 | 193 | FreeCAD LGPL2+ 194 | 195 | 196 | https://www.gnu.org/copyleft/lesser.html 197 | 198 | 199 | [agryson] Alexander Gryson 200 | 201 | 202 | 203 | 204 | 205 | 209 | 215 | 221 | 227 | 233 | 239 | 245 | 251 | 257 | 262 | 263 | 264 | -------------------------------------------------------------------------------- /icons/a2p_PartLabel.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 26 | 30 | 34 | 35 | 42 | 51 | 60 | 61 | 83 | 90 | 91 | 93 | 94 | 96 | image/svg+xml 97 | 99 | 100 | 101 | 102 | 106 | 109 | 112 | 118 | 124 | 131 | 138 | 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /icons/a2p_PartLabelRemove.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 23 | 26 | 30 | 34 | 35 | 42 | 51 | 60 | 70 | 73 | 77 | 81 | 82 | 92 | 93 | 115 | 122 | 123 | 125 | 126 | 128 | image/svg+xml 129 | 131 | 132 | 133 | 134 | 138 | 141 | 144 | 150 | 156 | 166 | 173 | 174 | 180 | 186 | 192 | 198 | 204 | 210 | 211 | 212 | 213 | -------------------------------------------------------------------------------- /icons/a2p_PartialProcessing.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 21 | 24 | 28 | 32 | 33 | 35 | 39 | 43 | 44 | 54 | 56 | 60 | 64 | 65 | 75 | 85 | 87 | 91 | 95 | 96 | 106 | 108 | 112 | 116 | 117 | 126 | 136 | 139 | 143 | 147 | 148 | 158 | 161 | 165 | 169 | 170 | 171 | 191 | 198 | 213 | 214 | 216 | 217 | 219 | image/svg+xml 220 | 222 | 223 | 224 | 225 | 230 | 232 | 240 | 247 | 254 | 261 | 268 | 275 | 276 | 277 | 278 | -------------------------------------------------------------------------------- /icons/a2p_PartsList_CutListOptimizer.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 19 | 37 | 39 | 41 | 42 | 44 | image/svg+xml 45 | 47 | 48 | 49 | 50 | 52 | 56 | List 66 | 67 | 75 | 77 | 79 | 83 | 84 | CutList Optimizer 94 | 95 | 97 | 101 | Parts 111 | 112 | 119 | 126 | 134 | 142 | 150 | 151 | -------------------------------------------------------------------------------- /icons/a2p_SketchReference.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 24 | 26 | 29 | 33 | 37 | 38 | 40 | 44 | 48 | 49 | 59 | 66 | 77 | 87 | 90 | 94 | 98 | 99 | 100 | 122 | 129 | 130 | 132 | 133 | 135 | image/svg+xml 136 | 138 | 139 | 140 | [triplus] 141 | 142 | 143 | SketcherWorkbench 144 | 2016-02-26 145 | http://www.freecadweb.org/wiki/index.php?title=Artwork 146 | 147 | 148 | FreeCAD 149 | 150 | 151 | FreeCAD/src/Mod/Sketcher/Gui/Resources/icons/SketcherWorkbench.svg 152 | 153 | 154 | FreeCAD LGPL2+ 155 | 156 | 157 | https://www.gnu.org/copyleft/lesser.html 158 | 159 | 160 | [agryson] Alexander Gryson 161 | 162 | 163 | 164 | 165 | 166 | 170 | 176 | 178 | 185 | 195 | 202 | 212 | 217 | 218 | 219 | 220 | 221 | -------------------------------------------------------------------------------- /icons/a2p_ToggleAutoSolve.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 25 | 29 | 33 | 34 | 44 | 54 | 57 | 61 | 65 | 66 | 68 | 72 | 76 | 77 | 78 | 99 | 108 | 109 | 111 | 112 | 114 | image/svg+xml 115 | 117 | Path-Stock 118 | 2015-07-04 119 | http://www.freecadweb.org/wiki/index.php?title=Artwork 120 | 121 | 122 | FreeCAD 123 | 124 | 125 | FreeCAD/src/Mod/Path/Gui/Resources/icons/Path-Stock.svg 126 | 127 | 128 | FreeCAD LGPL2+ 129 | 130 | 131 | https://www.gnu.org/copyleft/lesser.html 132 | 133 | 134 | [agryson] Alexander Gryson 135 | 136 | 137 | 138 | 139 | 140 | 144 | 147 | 150 | 153 | 155 | 157 | 164 | 171 | 172 | 175 | 182 | 189 | 190 | 191 | 198 | 199 | Auto 211 | 212 | 213 | 214 | 215 | -------------------------------------------------------------------------------- /icons/a2p_TogglePartial.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 25 | 29 | 33 | 34 | 44 | 54 | 57 | 61 | 65 | 66 | 68 | 72 | 76 | 77 | 78 | 99 | 108 | 109 | 111 | 112 | 114 | image/svg+xml 115 | 117 | Path-Stock 118 | 2015-07-04 119 | http://www.freecadweb.org/wiki/index.php?title=Artwork 120 | 121 | 122 | FreeCAD 123 | 124 | 125 | FreeCAD/src/Mod/Path/Gui/Resources/icons/Path-Stock.svg 126 | 127 | 128 | FreeCAD LGPL2+ 129 | 130 | 131 | https://www.gnu.org/copyleft/lesser.html 132 | 133 | 134 | [agryson] Alexander Gryson 135 | 136 | 137 | 138 | 139 | 140 | 144 | 147 | 150 | 152 | 154 | 157 | 159 | 161 | 168 | 175 | 176 | 179 | 186 | 193 | 194 | 195 | 202 | 203 | 204 | 209 | 210 | 217 | 224 | 231 | 232 | 233 | 234 | 235 | -------------------------------------------------------------------------------- /icons/a2p_ToggleTransparency.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 25 | 29 | 33 | 34 | 44 | 54 | 57 | 61 | 65 | 66 | 68 | 72 | 76 | 80 | 81 | 83 | 87 | 91 | 95 | 96 | 97 | 120 | 129 | 130 | 132 | 133 | 135 | image/svg+xml 136 | 138 | Path-Stock 139 | 2015-07-04 140 | http://www.freecadweb.org/wiki/index.php?title=Artwork 141 | 142 | 143 | FreeCAD 144 | 145 | 146 | FreeCAD/src/Mod/Path/Gui/Resources/icons/Path-Stock.svg 147 | 148 | 149 | FreeCAD LGPL2+ 150 | 151 | 152 | https://www.gnu.org/copyleft/lesser.html 153 | 154 | 155 | [agryson] Alexander Gryson 156 | 157 | 158 | 159 | 160 | 161 | 165 | 170 | 175 | 180 | 181 | 182 | -------------------------------------------------------------------------------- /icons/a2p_Workbench.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 20 | 22 | 25 | 29 | 33 | 34 | 44 | 54 | 57 | 61 | 65 | 66 | 67 | 88 | 97 | 98 | 100 | 101 | 103 | image/svg+xml 104 | 106 | Path-Stock 107 | 2015-07-04 108 | http://www.freecadweb.org/wiki/index.php?title=Artwork 109 | 110 | 111 | FreeCAD 112 | 113 | 114 | FreeCAD/src/Mod/Path/Gui/Resources/icons/Path-Stock.svg 115 | 116 | 117 | FreeCAD LGPL2+ 118 | 119 | 120 | https://www.gnu.org/copyleft/lesser.html 121 | 122 | 123 | [agryson] Alexander Gryson 124 | 125 | 126 | 127 | 128 | 129 | 133 | 136 | 139 | 146 | 155 | 156 | 158 | 166 | 173 | 174 | 177 | 184 | 191 | 192 | 193 | 2 205 | 206 | 207 | -------------------------------------------------------------------------------- /package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | A2plus 4 | Another assembly workbench for FreeCAD, following and extending Hamish's Assembly 2 workbench hence Assembly2plus. The main goal of A2plus is to create a very simple, easy to use, and not over-featured workbench for FreeCAD assemblies. Using the KISS principle: KEEP IT SIMPLE, STUPID 5 | 0.4.68 6 | 2024-10-01 7 | kbwbe 8 | LGPL-2.1-or-later 9 | https://github.com/kbwbe/A2plus 10 | https://github.com/kbwbe/A2plus/issues 11 | icons/a2p_Workbench.svg 12 | 13 | 14 | 15 | A2plusWorkbench 16 | ./ 17 | assembly 18 | 0.19 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /translations/A2plus_de.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/translations/A2plus_de.qm -------------------------------------------------------------------------------- /translations/A2plus_es-AR.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/translations/A2plus_es-AR.qm -------------------------------------------------------------------------------- /translations/A2plus_es-ES.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/translations/A2plus_es-ES.qm -------------------------------------------------------------------------------- /translations/A2plus_fr.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/translations/A2plus_fr.qm -------------------------------------------------------------------------------- /translations/A2plus_it.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/translations/A2plus_it.qm -------------------------------------------------------------------------------- /translations/A2plus_pt-br.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/translations/A2plus_pt-br.qm -------------------------------------------------------------------------------- /translations/A2plus_pt-pt.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/translations/A2plus_pt-pt.qm -------------------------------------------------------------------------------- /translations/A2plus_ru.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/translations/A2plus_ru.qm -------------------------------------------------------------------------------- /translations/A2plus_zh-CN.qm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/kbwbe/A2plus/228c64b6d2a12f13b1d4cd4c295b3783eb100440/translations/A2plus_zh-CN.qm -------------------------------------------------------------------------------- /translations/README.md: -------------------------------------------------------------------------------- 1 | # About translating A2plus Workbench 2 | 3 | **Note**: all commands **must** be run in `A2plus/translations/` directory. 4 | 5 | ## 1. Creating file for missing locale 6 | 7 | To create a file for a new language with all **A2plus** translatable strings execute: 8 | 9 | ```shell 10 | python3 update_ts.py -c 11 | ``` 12 | 13 | ## 2. Renaming file 14 | 15 | Now you can rename new `A2plus.ts` file by: 16 | 17 | - appending the locale code, example: `A2plus_it.ts` for Italy or 18 | - using command: 19 | 20 | ```shell 21 | pylupdate5 -verbose ../*.py -ts A2plus_it.ts 22 | ``` 23 | 24 | As of 31/08/2024 the supported locales on FreeCAD 25 | (according to `FreeCADGui.supportedLocales()`) are 43: 26 | 27 | ```python 28 | {'English': 'en', 'Afrikaans': 'af', 'Arabic': 'ar', 'Basque': 'eu', 29 | 'Belarusian': 'be', 'Bulgarian': 'bg', 'Catalan': 'ca', 30 | 'Chinese Simplified': 'zh-CN', 'Chinese Traditional': 'zh-TW', 'Croatian': 'hr', 31 | 'Czech': 'cs', 'Dutch': 'nl', 'Filipino': 'fil', 'Finnish': 'fi', 'French': 'fr', 32 | 'Galician': 'gl', 'Georgian': 'ka', 'German': 'de', 'Greek': 'el', 'Hungarian': 'hu', 33 | 'Indonesian': 'id', 'Italian': 'it', 'Japanese': 'ja', 'Kabyle': 'kab', 34 | 'Korean': 'ko', 'Lithuanian': 'lt', 'Norwegian': 'no', 'Polish': 'pl', 35 | 'Portuguese': 'pt-PT', 'Portuguese, Brazilian': 'pt-BR', 'Romanian': 'ro', 36 | 'Russian': 'ru', 'Serbian': 'sr', 'Serbian, Latin': 'sr-CS', 'Slovak': 'sk', 37 | 'Slovenian': 'sl', 'Spanish': 'es-ES', 'Spanish, Argentina': 'es-AR', 38 | 'Swedish': 'sv-SE', 'Turkish': 'tr', 'Ukrainian': 'uk', 'Valencian': 'val-ES', 39 | 'Vietnamese': 'vi'} 40 | ``` 41 | 42 | ## 3. Translating 43 | 44 | To edit your language file open your file in `Qt Linguist` from `qt5-tools` 45 | or in a text editor like `xed`, `mousepad`, `gedit`, `nano`, `vim`/`nvim`, 46 | `geany` etc. and translate it. 47 | 48 | ## 4. Updating translations 49 | 50 | To update all language files from source files (for developers only) 51 | you should use this command: 52 | 53 | ```shell 54 | python3 update_ts.py -u 55 | ``` 56 | 57 | ## 5. Compiling translations 58 | 59 | To convert all `.ts` files to `.qm` files (merge) you can use this command: 60 | 61 | ```shell 62 | python3 update_ts.py -m 63 | ``` 64 | 65 | If you are a translator that wants to update only their language file 66 | to test it on **FreeCAD** before doing a PR you can use this command: 67 | 68 | ```shell 69 | lrelease A2plus_it.ts 70 | ``` 71 | 72 | This will update the `.qm` file for your language (Italian in this case). 73 | 74 | ## 6. Sending translations 75 | 76 | Now you can contribute your translated `.ts` file to **A2plus** repository 77 | 78 | 79 | 80 | ## More information 81 | 82 | You can read more about translating external workbenches here: 83 | 84 | 85 | -------------------------------------------------------------------------------- /translations/update_ts.py: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env python3 2 | # 3 | # *************************************************************************** 4 | # * * 5 | # * Copyright (c) 2019 kbwbe * 6 | # * * 7 | # * Portions of code based on hamish's assembly 2 * 8 | # * * 9 | # * This program is free software; you can redistribute it and/or modify * 10 | # * it under the terms of the GNU Lesser General Public License (LGPL) * 11 | # * as published by the Free Software Foundation; either version 2 of * 12 | # * the License, or (at your option) any later version. * 13 | # * for detail see the LICENCE text file. * 14 | # * * 15 | # * This program is distributed in the hope that it will be useful, * 16 | # * but WITHOUT ANY WARRANTY; without even the implied warranty of * 17 | # * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 18 | # * GNU Library General Public License for more details. * 19 | # * * 20 | # * You should have received a copy of the GNU Library General Public * 21 | # * License along with this program; if not, write to the Free Software * 22 | # * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * 23 | # * USA * 24 | # * * 25 | # *************************************************************************** 26 | 27 | import os 28 | import sys 29 | 30 | 31 | # ============================================================================== 32 | # Script for create translations file for A2plus Workbench 33 | # 34 | # The script has to be started within the A2plus/translations Folder 35 | # ============================================================================== 36 | def create_ts(): 37 | 38 | print("1. Scan UI file for strings") 39 | os.system("lupdate ../GuiA2p/Resources/ui/*.ui -ts uifiles.ts") 40 | 41 | print("2. Scan .py files for strings") 42 | os.system("pylupdate5 -verbose ../*.py -ts pyfiles.ts") 43 | 44 | print("3. Combine both scans above") 45 | os.system( 46 | "lconvert -i uifiles.ts pyfiles.ts -o A2plus.ts -sort-contexts -no-obsolete -verbose" 47 | ) 48 | 49 | print("4. Remove temporary files") 50 | os.system("rm uifiles.ts") 51 | print(" uifiles.ts") 52 | os.system("rm pyfiles.ts") 53 | print(" pyfiles.ts") 54 | print("5. You have fresh A2plus.ts file now") 55 | 56 | 57 | # ============================================================================== 58 | # Script for merging different translations of A2plus Workbench 59 | # 60 | # The script has to be started within the A2plus/translations Folder 61 | # ============================================================================== 62 | def merge_ts(): 63 | 64 | os.system("lrelease *_*.ts") 65 | print("You have fresh all A2plus_*.qm files now") 66 | 67 | 68 | # ============================================================================== 69 | # Script for update all translations of A2plus Workbench 70 | # 71 | # The script has to be started within the A2plus/translations folder 72 | # ============================================================================== 73 | def update_ts(): 74 | 75 | os.system("pylupdate5 -verbose ../GuiA2p/Resources/ui/*.ui ../*.py -ts *.ts") 76 | print("You have fresh all A2plus_*.ts files now") 77 | 78 | 79 | par = '' 80 | if __name__ == '__main__': 81 | if len(sys.argv) > 1: 82 | par = sys.argv[1] 83 | 84 | if par == '-c': 85 | print("Script for create translation file for A2plus Workbench:") 86 | create_ts() 87 | elif par == '-h': 88 | print("Script for preparing translation files for A2plus Workbench") 89 | print("Copyright (c) 2019 kbwbe") 90 | print("") 91 | print("Commands:") 92 | print(" -c - create A2plus.ts file from *.ui file and from all *.py files") 93 | print(" -h - this help") 94 | print(" -m - merge all A2plus_*.ts files to A2plus_*.qm files") 95 | print(" -u - update all A2plus_*.ts files") 96 | elif par == '-m': 97 | print("Script for merging all translations of A2plus Workbench:") 98 | merge_ts() 99 | elif par == '-u': 100 | print("Script for update all translations of A2plus Workbench:") 101 | update_ts() 102 | else: 103 | print(f"Parameter '{par}' not present.") 104 | print("Use 'python3 update_ts.py -h' for information") 105 | --------------------------------------------------------------------------------