forked from Zolko-123/FreeCAD_Assembly4
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmakeBomCmd.py
337 lines (285 loc) · 12.1 KB
/
makeBomCmd.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
#!/usr/bin/env python3
# coding: utf-8
#
# makeBomCmd.py
#
# parses the Asm4 Model tree and creates a list of parts
import os
from PySide import QtGui, QtCore
import FreeCADGui as Gui
import FreeCAD as App
from FreeCAD import Console as FCC
import libAsm4 as Asm4
"""
+-----------------------------------------------+
| Helper functions |
+-----------------------------------------------+
"""
"""
+-----------------------------------------------+
| prints a parts list |
+-----------------------------------------------+
def Partlist(object,level=0):
indent = ' '*level
# list the Variables
if object.Name=='Variables':
print(indent+'Variables:')
vars = object
for prop in vars.PropertiesList:
if vars.getGroupOfProperty(prop)=='Variables' :
propValue = vars.getPropertyByName(prop)
print(indent+' '+prop+' = '+str(propValue))
# if its a link, look for the linked object
elif object.TypeId=='App::Link':
print(indent+object.Label+' -> '+object.LinkedObject.Document.Name+'#'+object.LinkedObject.Label)
Partlist(object.LinkedObject,level+1)
# everything else
else:
print(indent+object.Label+' ('+object.TypeId+')')
# if it's a part, look for sub-objects
if object.TypeId=='App::Part':
for objname in object.getSubObjects():
subobj = object.Document.getObject( objname[0:-1] )
Partlist(subobj,level+1)
return
import FreeCAD,Draft
import csv
forbbox = ('PartDesign::Body', 'Part::Feature', 'Part::FeaturePython')
def Partlist(object,level=0,tab=None):
indent = ' '*level
if tab is None:
tab = {}
if object.TypeId=='App::Link':
print(indent+object.Label+' -> '+object.LinkedObject.Document.Name+'#'+object.LinkedObject.Label+' => '+object.LinkedObject.FullName)
Partlist(object.LinkedObject,level+1,tab)
else:
print(indent+object.Label+' ('+object.TypeId+')')
if hasattr(object, 'Shape') and object.TypeId in forbbox:
if object.FullName in tab:
tab[object.FullName]["count"] = tab[object.FullName]["count"] + 1
else:
tab[object.FullName] = {}
tab[object.FullName]["var"] = {}
tab[object.FullName]["count"] = 1
tab[object.FullName]['label'] = object.Label
tab[object.FullName]['fullname'] = object.FullName
tab[object.FullName]['Id'] = object.Label
bb=object.Shape.BoundBox
tab[object.FullName]['xlen'] = bb.XLength
tab[object.FullName]['ylen'] = bb.YLength
tab[object.FullName]['zlen'] = bb.ZLength
tab[object.FullName]['volume'] = str(object.Shape.Volume)
if hasattr(object, 'AttachedTo'):
tab[object.FullName]['attachedto'] = object.AttachedTo
print (indent+" => BBox: "+str(object.Shape.BoundBox))
print (indent+" => Volume: "+str(object.Shape.Volume))
if object.TypeId=='App::Part':
# look for Variables
if object.Document.getObject( 'Variables' ):
print(indent+' Variables:')
vars = object.Document.getObject( 'Variables' )
for prop in vars.PropertiesList:
if vars.getGroupOfProperty(prop)=='Variables' :
propValue = vars.getPropertyByName(prop)
print(indent+' '+prop+' = '+str(propValue))
if not object.FullName in tab:
tab[object.FullName] = {}
tab[object.FullName]['fullname'] = object.FullName
tab[object.FullName]["var"] = {}
tab[object.FullName]["var"][prop] = str(propValue)
# look for sub-objects
for objname in object.getSubObjects():
subobj = object.Document.getObject( objname[0:-1] )
Partlist(subobj,level+1, tab)
return tab
def dictoarr(tab):
keys = {}
for obj in tab.keys():
if isinstance(tab[obj], dict):
for key in tab[obj].keys():
if isinstance(tab[obj][key], dict):
for inner_key in tab[obj][key].keys():
keys[key+"."+inner_key] = {};
keys[key+"."+inner_key][0] = key;
keys[key+"."+inner_key][1] = inner_key;
else:
keys[key] = 1;
headings = sorted(keys.keys())
arr = [ headings ]
for obj in sorted(tab.keys()):
line = []
for head in headings:
value = ''
lookup = keys[head]
if isinstance(lookup, dict):
if lookup[0] in tab[obj] and lookup[1] in tab[obj][lookup[0]]:
value = tab[obj][lookup[0]][lookup[1]]
else:
if head in tab[obj]:
value = tab[obj][head]
line.append(value)
arr.append(line)
return arr
a = Partlist(FreeCAD.ActiveDocument.getObject("Model"), 0)
print("\n\n")
print(a)
t = dictoarr(a)
print("\n\n")
print(t)
with open('/tmp/table.csv', 'w') as csvfile:
writer = csv.writer(csvfile, delimiter="\t")
writer.writerows(t)
"""
class makeBOM:
def __init__(self):
super(makeBOM,self).__init__()
def GetResources(self):
return {"MenuText": "Create Part List",
"ToolTip": "Create the Bom (Bill of Materials) of an Assembly4 Model",
"Pixmap" : os.path.join( Asm4.iconPath , 'Asm4_PartsList.svg')
}
def IsActive(self):
return self.checkModel()
def Activated(self):
self.UI = QtGui.QDialog()
# get the current active document to avoid errors if user changes tab
self.modelDoc = App.ActiveDocument
self.model = self.modelDoc.Model
self.drawUI()
self.UI.show()
self.BOM.clear()
self.PartsList = ''
self.listParts(self.model)
self.BOM.setPlainText(self.PartsList)
def listParts( self, obj, level=0 ):
indent = '\n'+'\t'*level
if obj.Document == self.modelDoc:
docName = ''
else:
docName = obj.Document.Name+'#'
#partBB = App.BoundBox()
# list the Variables
if obj.Name=='Variables':
#print(indent+'Variables:')
self.PartsList += indent+'Variables:'
for prop in obj.PropertiesList:
if obj.getGroupOfProperty(prop)=='Variables' :
propValue = obj.getPropertyByName(prop)
self.PartsList += indent+'\t'+prop+' = '+str(propValue)
# if it's part we look for sub-objects
elif obj.TypeId=='App::Part':
self.PartsList += indent +docName +obj.Label
for objname in obj.getSubObjects():
subobj = obj.Document.getObject( objname[0:-1] )
self.listParts( subobj, level+1 )
# if its a link, look for the linked object
elif obj.TypeId=='App::Link':
self.PartsList += indent+obj.Label+' -> '
self.listParts( obj.LinkedObject, level )
# if its a Body container we also add the document name and the size
elif obj.TypeId=='PartDesign::Body':
self.PartsList += indent +docName +obj.Label
if obj.Label2:
self.PartsList += ' ('+obj.Label2+')'
bb = obj.Shape.BoundBox
if abs(max(bb.XLength,bb.YLength,bb.ZLength)) < 1e+10:
Xsize = str(int((bb.XLength * 10)+0.099)/10)
Ysize = str(int((bb.YLength * 10)+0.099)/10)
Zsize = str(int((bb.ZLength * 10)+0.099)/10)
self.PartsList += ', Size: '+Xsize+' x '+Ysize+' x '+Zsize
# everything else except datum objects
elif obj.TypeId not in Asm4.datumTypes:
self.PartsList += indent+obj.Label
if obj.Label2:
self.PartsList += ' ('+obj.Label2+')'
else:
self.PartsList += ' ('+obj.TypeId+')'
# if the object has a shape, add it at the end of the line
if hasattr(obj,'Shape') and obj.Shape.BoundBox.isValid():
bb = obj.Shape.BoundBox
if max(bb.XLength,bb.YLength,bb.ZLength) < 1e+10:
Xsize = str(int((bb.XLength * 10)+0.099)/10)
Ysize = str(int((bb.YLength * 10)+0.099)/10)
Zsize = str(int((bb.ZLength * 10)+0.099)/10)
self.PartsList += ', Size: '+Xsize+' x '+Ysize+' x '+Zsize
return
def onSave(self):
#pass
"""Saves ASCII tree to user system file"""
_path = QtGui.QFileDialog.getSaveFileName()
if _path[0]:
save_file = QtCore.QFile(_path[0])
if save_file.open(QtCore.QFile.ReadWrite):
save_fileContent = QtCore.QTextStream(save_file)
save_fileContent << self.BOM
save_file.flush()
save_file.close()
self.BOM.setPlainText("Saved to file : " + _path[0])
else:
#FCC.PrintError("ERROR : Can't open file : "+ _path[0]+'\n')
self.BOM.setPlainText("ERROR : Can't open file : " + _path[0])
else:
self.BOM.setPlainText("ERROR : Can't open file : " + _path[0])
QtCore.QTimer.singleShot(3000, lambda:self.BOM.setPlainText(self.PartsList))
#self.UI.close()
def isReal( bb ):
# check if the BoundingBox is a real one
if bb.isValid() and abs(max(bb.XLength,bb.YLength,bb.ZLength)) < 1e+10:
return True
else:
return False
def checkModel(self):
# check whether there is already a Model in the document
# Returns True if there is an object called 'Model'
if App.ActiveDocument and App.ActiveDocument.getObject('Model') and App.ActiveDocument.Model.TypeId=='App::Part':
return(True)
else:
return(False)
def onCopy(self):
"""Copies Parts List to clipboard"""
self.BOM.selectAll()
self.BOM.copy()
self.BOM.setPlainText("Copied BoM to clipboard")
QtCore.QTimer.singleShot(3000, lambda:self.BOM.setPlainText(self.PartsList))
def onOK(self):
self.UI.close()
"""
+-----------------------------------------------+
| defines the UI, only static elements |
+-----------------------------------------------+
"""
def drawUI(self):
# Our main window will be a QDialog
self.UI.setWindowTitle('Parts List / BOM')
self.UI.setWindowIcon( QtGui.QIcon( os.path.join( Asm4.iconPath , 'FreeCad.svg' ) ) )
self.UI.setWindowFlags( QtCore.Qt.WindowStaysOnTopHint )
self.UI.setModal(False)
# set main window widgets layout
self.mainLayout = QtGui.QVBoxLayout(self.UI)
# The list, is a plain text field
self.BOM = QtGui.QPlainTextEdit()
self.BOM.setMinimumSize(600,500)
self.BOM.setLineWrapMode(QtGui.QPlainTextEdit.NoWrap)
self.mainLayout.addWidget(self.BOM)
# the button row definition
self.buttonLayout = QtGui.QHBoxLayout()
self.buttonLayout.addStretch()
# Save button
self.CopyButton = QtGui.QPushButton('Copy')
self.buttonLayout.addWidget(self.CopyButton)
# Save button
#self.SaveButton = QtGui.QPushButton('Save')
#self.buttonLayout.addWidget(self.SaveButton)
# OK button
self.OkButton = QtGui.QPushButton('Close')
self.OkButton.setDefault(True)
self.buttonLayout.addWidget(self.OkButton)
self.mainLayout.addLayout(self.buttonLayout)
# finally, apply the layout to the main window
self.UI.setLayout(self.mainLayout)
# Actions
self.CopyButton.clicked.connect(self.onCopy)
#self.SaveButton.clicked.connect(self.onSave)
self.OkButton.clicked.connect(self.onOK)
# add the command to the workbench
Gui.addCommand( 'Asm4_makeBOM', makeBOM() )