├── .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 |
135 |
--------------------------------------------------------------------------------
/icons/a2p_CheckAssembly.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
138 |
--------------------------------------------------------------------------------
/icons/a2p_DeleteConnections.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
155 |
--------------------------------------------------------------------------------
/icons/a2p_EditUndo.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
236 |
--------------------------------------------------------------------------------
/icons/a2p_Obj.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
240 |
--------------------------------------------------------------------------------
/icons/a2p_ObjReference.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
264 |
--------------------------------------------------------------------------------
/icons/a2p_PartLabel.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
142 |
--------------------------------------------------------------------------------
/icons/a2p_PartLabelRemove.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
213 |
--------------------------------------------------------------------------------
/icons/a2p_PartialProcessing.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
278 |
--------------------------------------------------------------------------------
/icons/a2p_PartsList_CutListOptimizer.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
151 |
--------------------------------------------------------------------------------
/icons/a2p_SketchReference.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
221 |
--------------------------------------------------------------------------------
/icons/a2p_ToggleAutoSolve.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
215 |
--------------------------------------------------------------------------------
/icons/a2p_TogglePartial.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
235 |
--------------------------------------------------------------------------------
/icons/a2p_ToggleTransparency.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
182 |
--------------------------------------------------------------------------------
/icons/a2p_Workbench.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
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 |
--------------------------------------------------------------------------------