Skip to content

Commit b07e364

Browse files
committed
Implement local/global settings
Local means in same directory as pcb file and it overrides global settings.
1 parent 50c790c commit b07e364

File tree

9 files changed

+89
-51
lines changed

9 files changed

+89
-51
lines changed

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,5 @@
66
test
77
releases
88
demo
9-
config.ini
9+
*config.ini
1010
InteractiveHtmlBom/web/user*

InteractiveHtmlBom/core/config.py

Lines changed: 32 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ class Config:
2626
) # type: str
2727

2828
# Helper constants
29-
config_file = os.path.join(os.path.dirname(__file__), '..', 'config.ini')
3029
bom_view_choices = ['bom-only', 'left-right', 'top-bottom']
3130
layer_view_choices = ['F', 'FB', 'B']
3231
default_sort_order = [
@@ -88,14 +87,22 @@ def _split(s):
8887
def _join(lst):
8988
return ','.join([s.replace(',', '\\,') for s in lst])
9089

91-
def __init__(self, version):
90+
def __init__(self, version, local_dir):
9291
self.version = version
92+
self.local_config_file = os.path.join(local_dir, 'ibom.config.ini')
93+
self.global_config_file = os.path.join(
94+
os.path.dirname(__file__), '..', 'config.ini')
9395

9496
def load_from_ini(self):
9597
"""Init from config file if it exists."""
96-
if not os.path.isfile(self.config_file):
98+
if os.path.isfile(self.local_config_file):
99+
file = self.local_config_file
100+
elif os.path.isfile(self.global_config_file):
101+
file = self.global_config_file
102+
else:
97103
return
98-
f = FileConfig(localFilename=self.config_file)
104+
105+
f = FileConfig(localFilename=file)
99106

100107
f.SetPath('/html_defaults')
101108
self.dark_mode = f.ReadBool('dark_mode', self.dark_mode)
@@ -146,8 +153,10 @@ def load_from_ini(self):
146153
self._join(self.board_variant_blacklist)))
147154
self.dnp_field = f.Read('dnp_field', self.dnp_field)
148155

149-
def save(self):
150-
f = FileConfig(localFilename=self.config_file)
156+
def save(self, locally):
157+
file = self.local_config_file if locally else self.global_config_file
158+
print('Saving to', file)
159+
f = FileConfig(localFilename=file)
151160

152161
f.SetPath('/html_defaults')
153162
f.WriteBool('dark_mode', self.dark_mode)
@@ -294,9 +303,9 @@ def safe_set_checked_strings(clb, strings):
294303

295304
dlg.finish_init()
296305

297-
# noinspection PyTypeChecker
298-
def add_options(self, parser, file_name_format_hint):
299-
# type: (argparse.ArgumentParser, str) -> None
306+
@classmethod
307+
def add_options(cls, parser):
308+
# type: (argparse.ArgumentParser) -> None
300309
parser.add_argument('--show-dialog', action='store_true',
301310
help='Shows config dialog. All other flags '
302311
'will be ignored.')
@@ -319,17 +328,17 @@ def add_options(self, parser, file_name_format_hint):
319328
help='Do not redraw pcb on drag by default.',
320329
action='store_true')
321330
parser.add_argument('--board-rotation', type=int,
322-
default=self.board_rotation * 5,
331+
default=cls.board_rotation * 5,
323332
help='Board rotation in degrees (-180 to 180). '
324333
'Will be rounded to multiple of 5.')
325334
parser.add_argument('--checkboxes',
326-
default=self.checkboxes,
335+
default=cls.checkboxes,
327336
help='Comma separated list of checkbox columns.')
328-
parser.add_argument('--bom-view', default=self.bom_view,
329-
choices=self.bom_view_choices,
337+
parser.add_argument('--bom-view', default=cls.bom_view,
338+
choices=cls.bom_view_choices,
330339
help='Default BOM view.')
331-
parser.add_argument('--layer-view', default=self.layer_view,
332-
choices=self.layer_view_choices,
340+
parser.add_argument('--layer-view', default=cls.layer_view,
341+
choices=cls.layer_view_choices,
333342
help='Default layer view.')
334343
parser.add_argument('--no-compression',
335344
help='Disable compression of pcb data.',
@@ -338,11 +347,11 @@ def add_options(self, parser, file_name_format_hint):
338347
action='store_true')
339348

340349
# General
341-
parser.add_argument('--dest-dir', default=self.bom_dest_dir,
350+
parser.add_argument('--dest-dir', default=cls.bom_dest_dir,
342351
help='Destination directory for bom file '
343352
'relative to pcb file directory.')
344-
parser.add_argument('--name-format', default=self.bom_name_format,
345-
help=file_name_format_hint.replace('%', '%%'))
353+
parser.add_argument('--name-format', default=cls.bom_name_format,
354+
help=cls.FILE_NAME_FORMAT_HINT.replace('%', '%%'))
346355
parser.add_argument('--include-tracks', action='store_true',
347356
help='Include track/zone information in output. '
348357
'F.Cu and B.Cu layers only.')
@@ -351,9 +360,9 @@ def add_options(self, parser, file_name_format_hint):
351360
parser.add_argument('--sort-order',
352361
help='Default sort order for components. '
353362
'Must contain "~" once.',
354-
default=','.join(self.component_sort_order))
363+
default=','.join(cls.component_sort_order))
355364
parser.add_argument('--blacklist',
356-
default=','.join(self.component_blacklist),
365+
default=','.join(cls.component_blacklist),
357366
help='List of comma separated blacklisted '
358367
'components or prefixes with *. '
359368
'E.g. "X1,MH*"')
@@ -372,10 +381,10 @@ def add_options(self, parser, file_name_format_hint):
372381
'for --show-fields and --group-fields '
373382
'with values "Value,Footprint,X,Y"')
374383
parser.add_argument('--show-fields',
375-
default=self._join(self.show_fields),
384+
default=cls._join(cls.show_fields),
376385
help='List of fields to show in the BOM.')
377386
parser.add_argument('--group-fields',
378-
default=self._join(self.group_fields),
387+
default=cls._join(cls.group_fields),
379388
help='Fields that components will be grouped by.')
380389
parser.add_argument('--normalize-field-case',
381390
help='Normalize extra field name case. E.g. "MPN" '
@@ -390,7 +399,7 @@ def add_options(self, parser, file_name_format_hint):
390399
parser.add_argument('--variants-blacklist', default='',
391400
help='List of board variants to '
392401
'exclude from the BOM.')
393-
parser.add_argument('--dnp-field', default=self.dnp_field,
402+
parser.add_argument('--dnp-field', default=cls.dnp_field,
394403
help='Name of the extra field that indicates '
395404
'do not populate status. Components with '
396405
'this field not empty will be excluded.')

InteractiveHtmlBom/core/ibom.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -318,9 +318,9 @@ def main(parser, config, logger):
318318

319319
def run_with_dialog(parser, config, logger):
320320
# type: (EcadParser, Config, Logger) -> None
321-
def save_config(dialog_panel):
321+
def save_config(dialog_panel, locally=False):
322322
config.set_from_dialog(dialog_panel)
323-
config.save()
323+
config.save(locally)
324324

325325
config.load_from_ini()
326326
dlg = SettingsDialog(extra_data_func=parser.parse_extra_data,

InteractiveHtmlBom/dialog/dialog_base.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,19 +46,19 @@ def __init__( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.
4646

4747
bSizer39 = wx.BoxSizer( wx.HORIZONTAL )
4848

49-
self.m_button41 = wx.Button( self, wx.ID_ANY, u"Save current settings", wx.DefaultPosition, wx.DefaultSize, 0|wx.BORDER_DEFAULT )
50-
bSizer39.Add( self.m_button41, 0, wx.ALL, 5 )
49+
self.saveSettingsBtn = wx.Button( self, wx.ID_ANY, u"Save current settings...", wx.DefaultPosition, wx.DefaultSize, 0|wx.BORDER_DEFAULT )
50+
bSizer39.Add( self.saveSettingsBtn, 0, wx.ALL, 5 )
5151

5252

5353
bSizer39.Add( ( 50, 0), 1, wx.EXPAND, 5 )
5454

55-
self.m_button42 = wx.Button( self, wx.ID_ANY, u"Generate BOM", wx.DefaultPosition, wx.DefaultSize, 0|wx.BORDER_DEFAULT )
55+
self.generateBomBtn = wx.Button( self, wx.ID_ANY, u"Generate BOM", wx.DefaultPosition, wx.DefaultSize, 0|wx.BORDER_DEFAULT )
5656

57-
self.m_button42.SetDefault()
58-
bSizer39.Add( self.m_button42, 0, wx.ALL, 5 )
57+
self.generateBomBtn.SetDefault()
58+
bSizer39.Add( self.generateBomBtn, 0, wx.ALL, 5 )
5959

60-
self.m_button43 = wx.Button( self, wx.ID_CANCEL, u"Cancel", wx.DefaultPosition, wx.DefaultSize, 0|wx.BORDER_DEFAULT )
61-
bSizer39.Add( self.m_button43, 0, wx.ALL, 5 )
60+
self.cancelBtn = wx.Button( self, wx.ID_CANCEL, u"Cancel", wx.DefaultPosition, wx.DefaultSize, 0|wx.BORDER_DEFAULT )
61+
bSizer39.Add( self.cancelBtn, 0, wx.ALL, 5 )
6262

6363

6464
bSizer20.Add( bSizer39, 0, wx.EXPAND, 5 )
@@ -68,16 +68,16 @@ def __init__( self, parent, id = wx.ID_ANY, pos = wx.DefaultPosition, size = wx.
6868
self.Layout()
6969

7070
# Connect Events
71-
self.m_button41.Bind( wx.EVT_BUTTON, self.OnSaveSettings )
72-
self.m_button42.Bind( wx.EVT_BUTTON, self.OnGenerateBom )
73-
self.m_button43.Bind( wx.EVT_BUTTON, self.OnExit )
71+
self.saveSettingsBtn.Bind( wx.EVT_BUTTON, self.OnSave )
72+
self.generateBomBtn.Bind( wx.EVT_BUTTON, self.OnGenerateBom )
73+
self.cancelBtn.Bind( wx.EVT_BUTTON, self.OnExit )
7474

7575
def __del__( self ):
7676
pass
7777

7878

7979
# Virtual event handlers, override them in your derived class
80-
def OnSaveSettings( self, event ):
80+
def OnSave( self, event ):
8181
event.Skip()
8282

8383
def OnGenerateBom( self, event ):

InteractiveHtmlBom/dialog/settings_dialog.py

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,18 +54,37 @@ def __init__(self, parent, extra_data_func, extra_data_wildcard,
5454
self.notebook.AddPage(self.html, "Html defaults")
5555
self.notebook.AddPage(self.fields, "Fields")
5656

57+
self.save_menu = wx.Menu()
58+
self.save_locally = self.save_menu.Append(
59+
wx.ID_ANY, u"Locally", wx.EmptyString, wx.ITEM_NORMAL)
60+
self.save_globally = self.save_menu.Append(
61+
wx.ID_ANY, u"Globally", wx.EmptyString, wx.ITEM_NORMAL)
62+
63+
self.Bind(
64+
wx.EVT_MENU, self.OnSaveLocally, id=self.save_locally.GetId())
65+
self.Bind(
66+
wx.EVT_MENU, self.OnSaveGlobally, id=self.save_globally.GetId())
67+
5768
def OnExit(self, event):
5869
self.GetParent().EndModal(wx.ID_CANCEL)
5970

60-
def OnSaveSettings(self, event):
61-
self.config_save_func(self)
62-
6371
def OnGenerateBom(self, event):
6472
self.GetParent().EndModal(wx.ID_OK)
6573

6674
def finish_init(self):
6775
self.html.OnBoardRotationSlider(None)
6876

77+
def OnSave(self, event):
78+
# type: (wx.CommandEvent) -> None
79+
pos = wx.Point(0, event.GetEventObject().GetSize().y)
80+
self.saveSettingsBtn.PopupMenu(self.save_menu, pos)
81+
82+
def OnSaveGlobally(self, event):
83+
self.config_save_func(self)
84+
85+
def OnSaveLocally(self, event):
86+
self.config_save_func(self, locally=True)
87+
6988

7089
# Implementing HtmlSettingsPanelBase
7190
class HtmlSettingsPanel(dialog_base.HtmlSettingsPanelBase):

InteractiveHtmlBom/dialog_test.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
from dialog.settings_dialog import *
1+
import wx
2+
from dialog.settings_dialog import SettingsDialog
23

34

45
class MyApp(wx.App):

InteractiveHtmlBom/ecad/kicad.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -778,16 +778,18 @@ def defaults(self):
778778
def Run(self):
779779
from ..version import version
780780
from ..errors import ParsingException
781-
config = Config(version)
781+
782+
logger = ibom.Logger()
782783
board = pcbnew.GetBoard()
783784
pcb_file_name = board.GetFileName()
784785

785-
logger = ibom.Logger()
786786
if not pcb_file_name:
787787
logger.error('Please save the board file before generating BOM.')
788788
return
789789

790+
config = Config(version, os.path.dirname(pcb_file_name))
790791
parser = PcbnewParser(pcb_file_name, config, logger, board)
792+
791793
try:
792794
ibom.run_with_dialog(parser, config, logger)
793795
except ParsingException as e:

InteractiveHtmlBom/generate_interactive_bom.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,15 +40,22 @@ def to_utf(s):
4040
parser.add_argument('file',
4141
type=lambda s: to_utf(s),
4242
help="KiCad PCB file")
43-
config = Config(version)
44-
config.add_options(parser, config.FILE_NAME_FORMAT_HINT)
43+
44+
Config.add_options(parser)
4545
args = parser.parse_args()
4646
logger = ibom.Logger(cli=True)
47+
4748
if not os.path.isfile(args.file):
4849
exit_error(logger, ExitCodes.ERROR_FILE_NOT_FOUND,
4950
"File %s does not exist." % args.file)
51+
5052
print("Loading %s" % args.file)
51-
parser = get_parser_by_extension(os.path.abspath(args.file), config, logger)
53+
54+
config = Config(version, os.path.dirname(os.path.abspath(args.file)))
55+
56+
parser = get_parser_by_extension(
57+
os.path.abspath(args.file), config, logger)
58+
5259
if args.show_dialog:
5360
if not create_wx_app:
5461
exit_error(logger, ExitCodes.ERROR_NO_DISPLAY,

settings_dialog.fbp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@
191191
<property name="gripper">0</property>
192192
<property name="hidden">0</property>
193193
<property name="id">wxID_ANY</property>
194-
<property name="label">Save current settings</property>
194+
<property name="label">Save current settings...</property>
195195
<property name="margins"></property>
196196
<property name="markup">0</property>
197197
<property name="max_size"></property>
@@ -201,7 +201,7 @@
201201
<property name="minimize_button">0</property>
202202
<property name="minimum_size"></property>
203203
<property name="moveable">1</property>
204-
<property name="name">m_button41</property>
204+
<property name="name">saveSettingsBtn</property>
205205
<property name="pane_border">1</property>
206206
<property name="pane_position"></property>
207207
<property name="pane_size"></property>
@@ -224,7 +224,7 @@
224224
<property name="window_extra_style"></property>
225225
<property name="window_name"></property>
226226
<property name="window_style">wxBORDER_DEFAULT</property>
227-
<event name="OnButtonClick">OnSaveSettings</event>
227+
<event name="OnButtonClick">OnSave</event>
228228
</object>
229229
</object>
230230
<object class="sizeritem" expanded="1">
@@ -285,7 +285,7 @@
285285
<property name="minimize_button">0</property>
286286
<property name="minimum_size"></property>
287287
<property name="moveable">1</property>
288-
<property name="name">m_button42</property>
288+
<property name="name">generateBomBtn</property>
289289
<property name="pane_border">1</property>
290290
<property name="pane_position"></property>
291291
<property name="pane_size"></property>
@@ -359,7 +359,7 @@
359359
<property name="minimize_button">0</property>
360360
<property name="minimum_size"></property>
361361
<property name="moveable">1</property>
362-
<property name="name">m_button43</property>
362+
<property name="name">cancelBtn</property>
363363
<property name="pane_border">1</property>
364364
<property name="pane_position"></property>
365365
<property name="pane_size"></property>

0 commit comments

Comments
 (0)