import pcbnew import wx import wx.lib.mixins.listctrl as listmix COLUMNS = ['Designation', 'Value', 'Library', 'Package', 'Sheet'] class Sortable_Component_List(wx.ListCtrl, listmix.ColumnSorterMixin): def __init__(self, parent): wx.ListCtrl.__init__( self, parent, style=wx.LC_REPORT | wx.LC_HRULES | wx.LC_VRULES | wx.LC_SINGLE_SEL ) col_widths = [100, 120, 160, 200, 200] for i, (col, w) in enumerate(zip(COLUMNS, col_widths)): self.InsertColumn(i, col, width=w) self.item_data_map = {} listmix.ColumnSorterMixin.__init__(self, len(COLUMNS)) def GetListCtrl(self): return self def populate(self, rows): self.DeleteAllItems() self.item_data_map = {} for idx, row in enumerate(rows): item = self.InsertItem(idx, row[0]) for col, val in enumerate(row[1:], start=1): self.SetItem(idx, col, val) self.SetItemData(idx, idx) self.item_data_map[idx] = row class Component_Table_Dialog(wx.Dialog): def __init__(self, parent, components): super().__init__( parent, title='Component Table', size=(850, 550), style=wx.DEFAULT_DIALOG_STYLE | wx.RESIZE_BORDER ) sizer = wx.BoxSizer(wx.VERTICAL) label = wx.StaticText(self, label=f'{len(components)} component(s) — click column headers to sort') sizer.Add(label, 0, wx.LEFT | wx.TOP, 8) self.list_ctrl = Sortable_Component_List(self) self.list_ctrl.populate(components) sizer.Add(self.list_ctrl, 1, wx.EXPAND | wx.ALL, 6) btn_close = wx.Button(self, wx.ID_CLOSE, 'Close') btn_close.Bind(wx.EVT_BUTTON, lambda e: self.Close()) sizer.Add(btn_close, 0, wx.ALIGN_RIGHT | wx.RIGHT | wx.BOTTOM, 8) self.SetSizer(sizer) self.Centre() def _get_sheet(fp): try: props = fp.GetProperties() for key in ('Sheetname', 'Sheet', 'sheet'): val = props.get(key, '') if val: return val except Exception: pass try: path = str(fp.GetPath()) if path and path != '/': return path except Exception: pass return '-' class Component_Table_Action(pcbnew.ActionPlugin): def defaults(self): self.name = 'Component Table' self.category = 'Inspect' self.description = 'Show a sortable table of all board components' self.show_toolbar_button = True self.icon_file_name = '' def Run(self): board = pcbnew.GetBoard() components = [] for fp in sorted(board.GetFootprints(), key=lambda f: f.GetReference()): ref = fp.GetReference() value = fp.GetValue() fpid = fp.GetFPID() library = str(fpid.GetLibNickname()) package = str(fpid.GetLibItemName()) sheet = _get_sheet(fp) components.append((ref, value, library, package, sheet)) dlg = Component_Table_Dialog(None, components) dlg.ShowModal() dlg.Destroy() Component_Table_Action().register()