import pcbnew import wx import wx.lib.mixins.listctrl as listmix import traceback 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.itemDataMap = {} listmix.ColumnSorterMixin.__init__(self, len(COLUMNS)) def GetListCtrl(self): return self def populate(self, rows): self.DeleteAllItems() self.itemDataMap = {} 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.itemDataMap[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: name = fp.GetSheetname() if name: return name 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): try: self._run() except Exception: msg = traceback.format_exc() dlg = wx.Dialog(None, title='Component Table — Error', size=(600, 350)) sizer = wx.BoxSizer(wx.VERTICAL) txt = wx.TextCtrl(dlg, value=msg, style=wx.TE_MULTILINE | wx.TE_READONLY | wx.HSCROLL) txt.SetFont(wx.Font(9, wx.FONTFAMILY_TELETYPE, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)) sizer.Add(txt, 1, wx.EXPAND | wx.ALL, 6) btn = wx.Button(dlg, wx.ID_CLOSE, 'Close') btn.Bind(wx.EVT_BUTTON, lambda e: dlg.Close()) sizer.Add(btn, 0, wx.ALIGN_RIGHT | wx.RIGHT | wx.BOTTOM, 8) dlg.SetSizer(sizer) dlg.ShowModal() dlg.Destroy() 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()