forked from Zolko-123/FreeCAD_Assembly4
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmakeBomCmd.py
248 lines (206 loc) · 8.75 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
#!/usr/bin/env python3
# coding: utf-8
#
# makeBomCmd.py
#
# parses the Asm4 Model tree and creates a list of parts
import os
import json
from PySide import QtGui, QtCore
import FreeCADGui as Gui
import FreeCAD as App
import Asm4_libs as Asm4
import infoPartCmd
import InfoKeys
# protection against update of user configuration
### to have the dir of external configuration file
ConfUserDir = os.path.join(App.getUserAppDataDir(),'Templates')
ConfUserFilename = "Asm4_infoPartConf.json"
ConfUserFilejson = os.path.join(ConfUserDir, ConfUserFilename)
### try to open existing external configuration file of user
try :
file = open(ConfUserFilejson, 'r')
file.close()
### else make the default external configuration file
except :
partInfoDef = dict()
for prop in InfoKeys.partInfo:
partInfoDef.setdefault(prop,{'userData':prop + 'User','active':True})
os.mkdir(ConfUserDir)
file = open(ConfUserFilejson, 'x')
json.dump(partInfoDef,file)
file.close()
### now user configuration is :
file = open(ConfUserFilejson, 'r')
infoKeysUser = json.load(file).copy()
file.close()
crea = infoPartCmd.infoPartUI.makePartInfo
fill = infoPartCmd.infoPartUI.infoDefault
"""
+-----------------------------------------------+
| Helper functions |
+-----------------------------------------------+
"""
"""
+-----------------------------------------------+
| prints a parts list |
+-----------------------------------------------+
"""
class makeBOM:
def __init__(self):
super(makeBOM,self).__init__()
file = open(ConfUserFilejson, 'r')
self.infoKeysUser = json.load(file).copy()
file.close()
def GetResources(self):
tooltip = "Bill of Materials"
tooltip += "Create the Bill of Materials of an Assembly"
tooltip += "With the Info and Config of Edit Part Information"
iconFile = os.path.join( Asm4.iconPath, 'Asm4_PartsList.svg' )
return {"MenuText": "Create Part List", "ToolTip": tooltip, "Pixmap": iconFile }
def IsActive(self):
# return self.checkModel()
if Asm4.getAssembly() is None:
return False
else:
return True
def Activated(self):
self.UI = QtGui.QDialog()
# get the current active document to avoid errors if user changes tab
self.modelDoc = App.ActiveDocument
# for the compatibility with the old Model
try :
self.model = self.modelDoc.Assembly
except:
try:
self.model = self.modelDoc.Model
print("legacy Assembly4 Model")
except:
print("Hum, this might not work")
self.drawUI()
self.UI.show()
self.BOM.clear()
self.Verbose=str()
self.PartsList = {}
self.listParts(self.model)
self.inSpreadsheet()
self.BOM.setPlainText(self.Verbose)
### def listParts use of Part info Edit
def listParts(self,object,level=0):
file = open(ConfUserFilejson, 'r')
self.infoKeysUser = json.load(file).copy()
file.close()
if object == None:
return
if self.PartsList == None:
self.PartsList = {}
# research App::Part because the partInfo attribute is on
if object.TypeId=='App::Link':
self.listParts(object.LinkedObject,level+1)
else:
if object.TypeId=='App::Part':
if level > 0:
# write PartsList
# test if the part already exist on PartsList
if object.Label in self.PartsList:
# if already exist =+ 1 in qty of this part count
self.PartsList[object.Label]['Qty.'] = self.PartsList[object.Label]['Qty.'] + 1
else:
# if not exist , create a dict() for this part
self.PartsList[object.Label] = dict()
for prop in self.infoKeysUser:
if self.infoKeysUser.get(prop).get('active'):
try:
# try to get partInfo in part
getattr(object,self.infoKeysUser.get(prop).get('userData'))
except AttributeError:
self.Verbose+='you don\'t have fill the info of this Part :'+ object.Label +'\n'
crea(self,object)
self.Verbose+='info create for :'+ object.Label +'\n'
fill(object)
self.Verbose+='info auto filled for :'+ object.Label+'\n'
self.PartsList[object.Label][self.infoKeysUser.get(prop).get('userData')] = getattr(object,self.infoKeysUser.get(prop).get('userData'))
self.PartsList[object.Label]['Qty.'] = 1
# look for sub-objects
for objname in object.getSubObjects():
subobj = object.Document.getObject( objname[0:-1] )
self.listParts(subobj,level+1)
return
self.Verbose+='Your Bill of Materials is Done\n'
### def Copy - Copy on Spreadsheet
def inSpreadsheet(self):
# Copies Parts List to Spreadsheet
document = App.ActiveDocument
# init plist with dict() PartsList
plist = self.PartsList
if len(plist) == 0:
return
# BOM on Spreadsheet
if not hasattr(document, 'BOM'):
spreadsheet = document.addObject('Spreadsheet::Sheet','BOM')
else:
spreadsheet = document.BOM
spreadsheet.Label = "BOM"
# clean the BOM
spreadsheet.clearAll()
# to write line in spreadsheet
def wrow(drow: [str], row: int):
for i, d in enumerate(drow):
if row==0:
spreadsheet.set(str(chr(ord('a') + i)).upper()+str(row+1),infoPartCmd.decodeXml(str(d)))
else :
spreadsheet.set(str(chr(ord('a') + i)).upper()+str(row+1),str(d))
# to make list of values of dict() plist
data = list(plist.values())
# to write first line with keys
wrow(data[0].keys(),0)
# to write line by line BoM in Spreadsheet
for i,_ in enumerate(data):
wrow(data[i].values(),i+1)
document.recompute()
self.Verbose+='Your Bill of Materials is Write on BOM Spreadsheet\n'
def onOK(self):
document = App.ActiveDocument
Gui.Selection.addSelection(document.Name,'BOM')
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)
# Help and Log :
self.LabelBOML1 = QtGui.QLabel()
self.LabelBOML1.setText('BoM:\n\nThis tool makes a BoM with the Info and Config of Edit Part Information. \n\nIf you have auto-infoField in your Config you can use BoM directly.\nBoM complete automatically your auto-infoField.\n\n')
self.LabelBOML2 = QtGui.QLabel()
self.LabelBOML2.setText("<a href='https://github.com/Zolko-123/FreeCAD_Assembly4/tree/master/Examples/ConfigBOM/README.md'>-=Tuto=-</a>")
self.LabelBOML2.setOpenExternalLinks(True)
self.LabelBOML3 = QtGui.QLabel()
self.LabelBOML3.setText('\n\nLog :')
self.mainLayout.addWidget(self.LabelBOML1)
self.mainLayout.addWidget(self.LabelBOML2)
self.mainLayout.addWidget(self.LabelBOML3)
# The Log (Verbose) is a plain text field
self.BOM = QtGui.QPlainTextEdit()
self.BOM.setLineWrapMode(QtGui.QPlainTextEdit.NoWrap)
self.mainLayout.addWidget(self.BOM)
# the button row definition
self.buttonLayout = QtGui.QHBoxLayout()
# OK button
self.OkButton = QtGui.QPushButton('OK')
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.OkButton.clicked.connect(self.onOK)
# add the command to the workbench
Gui.addCommand( 'Asm4_makeBOM', makeBOM() )