@@ -38,8 +38,9 @@ class Config:
3838 html_config_fields = [
3939 'dark_mode' , 'show_pads' , 'show_fabrication' , 'show_silkscreen' ,
4040 'highlight_pin1' , 'redraw_on_drag' , 'board_rotation' , 'checkboxes' ,
41- 'bom_view' , 'layer_view' , 'extra_fields'
41+ 'bom_view' , 'layer_view'
4242 ]
43+ default_show_group_fields = ["Value" , "Footprint" ]
4344
4445 # Defaults
4546
@@ -70,7 +71,8 @@ class Config:
7071 # Extra fields section
7172 extra_data_file = None
7273 netlist_initial_directory = '' # This is relative to pcb file directory
73- extra_fields = []
74+ show_fields = default_show_group_fields
75+ group_fields = default_show_group_fields
7476 normalize_field_case = False
7577 board_variant_field = ''
7678 board_variant_whitelist = []
@@ -79,7 +81,7 @@ class Config:
7981
8082 @staticmethod
8183 def _split (s ):
82- """Splits string by ',' and drops empty strings from resulting array. """
84+ """Splits string by ',' and drops empty strings from resulting array"""
8385 return [a .replace ('\\ ,' , ',' ) for a in re .split (r'(?<!\\),' , s ) if a ]
8486
8587 @staticmethod
@@ -99,9 +101,9 @@ def load_from_ini(self):
99101 self .dark_mode = f .ReadBool ('dark_mode' , self .dark_mode )
100102 self .show_pads = f .ReadBool ('show_pads' , self .show_pads )
101103 self .show_fabrication = f .ReadBool (
102- 'show_fabrication' , self .show_fabrication )
104+ 'show_fabrication' , self .show_fabrication )
103105 self .show_silkscreen = f .ReadBool (
104- 'show_silkscreen' , self .show_silkscreen )
106+ 'show_silkscreen' , self .show_silkscreen )
105107 self .highlight_pin1 = f .ReadBool ('highlight_pin1' , self .highlight_pin1 )
106108 self .redraw_on_drag = f .ReadBool ('redraw_on_drag' , self .redraw_on_drag )
107109 self .board_rotation = f .ReadInt ('board_rotation' , self .board_rotation )
@@ -115,32 +117,33 @@ def load_from_ini(self):
115117 self .bom_dest_dir = f .Read ('bom_dest_dir' , self .bom_dest_dir )
116118 self .bom_name_format = f .Read ('bom_name_format' , self .bom_name_format )
117119 self .component_sort_order = self ._split (f .Read (
118- 'component_sort_order' ,
119- ',' .join (self .component_sort_order )))
120+ 'component_sort_order' ,
121+ ',' .join (self .component_sort_order )))
120122 self .component_blacklist = self ._split (f .Read (
121- 'component_blacklist' ,
122- ',' .join (self .component_blacklist )))
123+ 'component_blacklist' ,
124+ ',' .join (self .component_blacklist )))
123125 self .blacklist_virtual = f .ReadBool (
124- 'blacklist_virtual' , self .blacklist_virtual )
126+ 'blacklist_virtual' , self .blacklist_virtual )
125127 self .blacklist_empty_val = f .ReadBool (
126- 'blacklist_empty_val' , self .blacklist_empty_val )
128+ 'blacklist_empty_val' , self .blacklist_empty_val )
127129 self .include_tracks = f .ReadBool ('include_tracks' , self .include_tracks )
128130 self .include_nets = f .ReadBool ('include_nets' , self .include_nets )
129131
130- f .SetPath ('/extra_fields' )
131- self .extra_fields = self ._split (f .Read (
132- 'extra_fields' ,
133- self ._join (self .extra_fields )))
132+ f .SetPath ('/fields' )
133+ self .show_fields = self ._split (f .Read (
134+ 'show_fields' , self ._join (self .show_fields )))
135+ self .group_fields = self ._split (f .Read (
136+ 'group_fields' , self ._join (self .group_fields )))
134137 self .normalize_field_case = f .ReadBool (
135- 'normalize_field_case' , self .normalize_field_case )
138+ 'normalize_field_case' , self .normalize_field_case )
136139 self .board_variant_field = f .Read (
137- 'board_variant_field' , self .board_variant_field )
140+ 'board_variant_field' , self .board_variant_field )
138141 self .board_variant_whitelist = self ._split (f .Read (
139- 'board_variant_whitelist' ,
140- ',' . join (self .board_variant_whitelist )))
142+ 'board_variant_whitelist' ,
143+ self . _join (self .board_variant_whitelist )))
141144 self .board_variant_blacklist = self ._split (f .Read (
142- 'board_variant_blacklist' ,
143- ',' . join (self .board_variant_blacklist )))
145+ 'board_variant_blacklist' ,
146+ self . _join (self .board_variant_blacklist )))
144147 self .dnp_field = f .Read ('dnp_field' , self .dnp_field )
145148
146149 def save (self ):
@@ -164,7 +167,7 @@ def save(self):
164167 bom_dest_dir = self .bom_dest_dir
165168 if bom_dest_dir .startswith (self .netlist_initial_directory ):
166169 bom_dest_dir = os .path .relpath (
167- bom_dest_dir , self .netlist_initial_directory )
170+ bom_dest_dir , self .netlist_initial_directory )
168171 f .Write ('bom_dest_dir' , bom_dest_dir )
169172 f .Write ('bom_name_format' , self .bom_name_format )
170173 f .Write ('component_sort_order' ,
@@ -176,14 +179,15 @@ def save(self):
176179 f .WriteBool ('include_tracks' , self .include_tracks )
177180 f .WriteBool ('include_nets' , self .include_nets )
178181
179- f .SetPath ('/extra_fields' )
180- f .Write ('extra_fields' , self ._join (self .extra_fields ))
182+ f .SetPath ('/fields' )
183+ f .Write ('show_fields' , self ._join (self .show_fields ))
184+ f .Write ('group_fields' , self ._join (self .group_fields ))
181185 f .WriteBool ('normalize_field_case' , self .normalize_field_case )
182186 f .Write ('board_variant_field' , self .board_variant_field )
183187 f .Write ('board_variant_whitelist' ,
184- ',' . join (self .board_variant_whitelist ))
188+ self . _join (self .board_variant_whitelist ))
185189 f .Write ('board_variant_blacklist' ,
186- ',' . join (self .board_variant_blacklist ))
190+ self . _join (self .board_variant_blacklist ))
187191 f .Write ('dnp_field' , self .dnp_field )
188192 f .Flush ()
189193
@@ -216,19 +220,20 @@ def set_from_dialog(self, dlg):
216220 self .include_tracks = dlg .general .includeTracksCheckbox .IsChecked ()
217221 self .include_nets = dlg .general .includeNetsCheckbox .IsChecked ()
218222
219- # Extra fields
220- self .extra_data_file = dlg .extra .extraDataFilePicker .Path
221- self .extra_fields = list (dlg .extra .extraFieldsList .GetCheckedStrings ())
222- self .normalize_field_case = dlg .extra .normalizeCaseCheckbox .Value
223- self .board_variant_field = dlg .extra .boardVariantFieldBox .Value
224- if self .board_variant_field == dlg .extra .NONE_STRING :
223+ # Fields
224+ self .extra_data_file = dlg .fields .extraDataFilePicker .Path
225+ self .show_fields = dlg .fields .GetShowFields ()
226+ self .group_fields = dlg .fields .GetGroupFields ()
227+ self .normalize_field_case = dlg .fields .normalizeCaseCheckbox .Value
228+ self .board_variant_field = dlg .fields .boardVariantFieldBox .Value
229+ if self .board_variant_field == dlg .fields .NONE_STRING :
225230 self .board_variant_field = ''
226231 self .board_variant_whitelist = list (
227- dlg .extra .boardVariantWhitelist .GetCheckedStrings ())
232+ dlg .fields .boardVariantWhitelist .GetCheckedStrings ())
228233 self .board_variant_blacklist = list (
229- dlg .extra .boardVariantBlacklist .GetCheckedStrings ())
230- self .dnp_field = dlg .extra .dnpFieldBox .Value
231- if self .dnp_field == dlg .extra .NONE_STRING :
234+ dlg .fields .boardVariantBlacklist .GetCheckedStrings ())
235+ self .dnp_field = dlg .fields .dnpFieldBox .Value
236+ if self .dnp_field == dlg .fields .NONE_STRING :
232237 self .dnp_field = ''
233238
234239 def transfer_to_dialog (self , dlg ):
@@ -243,9 +248,9 @@ def transfer_to_dialog(self, dlg):
243248 dlg .html .boardRotationSlider .Value = self .board_rotation
244249 dlg .html .bomCheckboxesCtrl .Value = self .checkboxes
245250 dlg .html .bomDefaultView .Selection = self .bom_view_choices .index (
246- self .bom_view )
251+ self .bom_view )
247252 dlg .html .layerDefaultView .Selection = self .layer_view_choices .index (
248- self .layer_view )
253+ self .layer_view )
249254 dlg .html .compressionCheckbox .Value = self .compression
250255 dlg .html .openBrowserCheckbox .Value = self .open_browser
251256
@@ -255,7 +260,7 @@ def transfer_to_dialog(self, dlg):
255260 dlg .general .bomDirPicker .Path = self .bom_dest_dir
256261 else :
257262 dlg .general .bomDirPicker .Path = os .path .join (
258- self .netlist_initial_directory , self .bom_dest_dir )
263+ self .netlist_initial_directory , self .bom_dest_dir )
259264 dlg .general .fileNameFormatTextControl .Value = self .bom_name_format
260265 dlg .general .componentSortOrderBox .SetItems (self .component_sort_order )
261266 dlg .general .blacklistBox .SetItems (self .component_blacklist )
@@ -264,28 +269,28 @@ def transfer_to_dialog(self, dlg):
264269 dlg .general .includeTracksCheckbox .Value = self .include_tracks
265270 dlg .general .includeNetsCheckbox .Value = self .include_nets
266271
267- # Extra fields
268- dlg .extra .extraDataFilePicker .SetInitialDirectory (
269- self .netlist_initial_directory )
272+ # Fields
273+ dlg .fields .extraDataFilePicker .SetInitialDirectory (
274+ self .netlist_initial_directory )
270275
271276 def safe_set_checked_strings (clb , strings ):
272- safe_strings = list (clb .GetStrings ())
273- if safe_strings :
274- present_strings = [s for s in strings if s in safe_strings ]
275- not_present_strings = [s for s in safe_strings if s not in strings ]
277+ current = list (clb .GetStrings ())
278+ if current :
279+ present_strings = [s for s in strings if s in current ]
280+ not_present_strings = [s for s in current if s not in strings ]
276281 clb .Clear ()
277282 clb .InsertItems (present_strings + not_present_strings , 0 )
278283 clb .SetCheckedStrings (present_strings )
279284
280- safe_set_checked_strings ( dlg .extra . extraFieldsList , self .extra_fields )
281- dlg .extra .normalizeCaseCheckbox .Value = self .normalize_field_case
282- dlg .extra .boardVariantFieldBox .Value = self .board_variant_field
283- dlg .extra .OnBoardVariantFieldChange (None )
284- safe_set_checked_strings (dlg .extra .boardVariantWhitelist ,
285+ dlg .fields . SetCheckedFields ( self . show_fields , self .group_fields )
286+ dlg .fields .normalizeCaseCheckbox .Value = self .normalize_field_case
287+ dlg .fields .boardVariantFieldBox .Value = self .board_variant_field
288+ dlg .fields .OnBoardVariantFieldChange (None )
289+ safe_set_checked_strings (dlg .fields .boardVariantWhitelist ,
285290 self .board_variant_whitelist )
286- safe_set_checked_strings (dlg .extra .boardVariantBlacklist ,
291+ safe_set_checked_strings (dlg .fields .boardVariantBlacklist ,
287292 self .board_variant_blacklist )
288- dlg .extra .dnpFieldBox .Value = self .dnp_field
293+ dlg .fields .dnpFieldBox .Value = self .dnp_field
289294
290295 dlg .finish_init ()
291296
@@ -350,24 +355,31 @@ def add_options(self, parser, file_name_format_hint):
350355 parser .add_argument ('--blacklist' ,
351356 default = ',' .join (self .component_blacklist ),
352357 help = 'List of comma separated blacklisted '
353- 'components or prefixes with *. E.g. "X1,MH*"' )
358+ 'components or prefixes with *. '
359+ 'E.g. "X1,MH*"' )
354360 parser .add_argument ('--no-blacklist-virtual' , action = 'store_true' ,
355361 help = 'Do not blacklist virtual components.' )
356362 parser .add_argument ('--blacklist-empty-val' , action = 'store_true' ,
357363 help = 'Blacklist components with empty value.' )
358364
359- # Extra fields section
365+ # Fields section
360366 parser .add_argument ('--netlist-file' ,
361367 help = '(Deprecated) Path to netlist or xml file.' )
362368 parser .add_argument ('--extra-data-file' ,
363369 help = 'Path to netlist or xml file.' )
364370 parser .add_argument ('--extra-fields' ,
365- default = self ._join (self .extra_fields ),
366- help = 'Comma separated list of extra fields to '
367- 'pull from netlist or xml file.' )
371+ help = 'Passing --extra-fields "X,Y" is a shortcut '
372+ 'for --show-fields and --group-fields '
373+ 'with values "Value,Footprint,X,Y"' )
374+ parser .add_argument ('--show-fields' ,
375+ default = self ._join (self .show_fields ),
376+ help = 'List of fields to show in the BOM.' )
377+ parser .add_argument ('--group-fields' ,
378+ default = self ._join (self .group_fields ),
379+ help = 'Fields that components will be grouped by.' )
368380 parser .add_argument ('--normalize-field-case' ,
369381 help = 'Normalize extra field name case. E.g. "MPN" '
370- 'and "mpn" will be considered the same field.' ,
382+ ', "mpn" will be considered the same field.' ,
371383 action = 'store_true' )
372384 parser .add_argument ('--variant-field' ,
373385 help = 'Name of the extra field that stores board '
@@ -380,8 +392,8 @@ def add_options(self, parser, file_name_format_hint):
380392 'exclude from the BOM.' )
381393 parser .add_argument ('--dnp-field' , default = self .dnp_field ,
382394 help = 'Name of the extra field that indicates '
383- 'do not populate status. Components with this '
384- 'field not empty will be blacklisted .' )
395+ 'do not populate status. Components with '
396+ 'this field not empty will be excluded .' )
385397
386398 def set_from_args (self , args ):
387399 # type: (argparse.Namespace) -> None
@@ -411,9 +423,15 @@ def set_from_args(self, args):
411423 self .include_tracks = args .include_tracks
412424 self .include_nets = args .include_nets
413425
414- # Extra
426+ # Fields
415427 self .extra_data_file = args .extra_data_file or args .netlist_file
416- self .extra_fields = self ._split (args .extra_fields )
428+ if args .extra_fields is not None :
429+ self .show_fields = self .default_show_group_fields + \
430+ self ._split (args .extra_fields )
431+ self .group_fields = self .show_fields
432+ else :
433+ self .show_fields = self ._split (args .show_fields )
434+ self .group_fields = self ._split (args .group_fields )
417435 self .normalize_field_case = args .normalize_field_case
418436 self .board_variant_field = args .variant_field
419437 self .board_variant_whitelist = self ._split (args .variants_whitelist )
@@ -423,7 +441,5 @@ def set_from_args(self, args):
423441 def get_html_config (self ):
424442 import json
425443 d = {f : getattr (self , f ) for f in self .html_config_fields }
426- # Temporary until currently hardcoded columns are made configurable
427- d ["fields" ] = ["References" ] + self .extra_fields + \
428- ["Value" , "Footprint" , "Quantity" ]
444+ d ["fields" ] = self .show_fields
429445 return json .dumps (d )
0 commit comments