From 27b79d38a7157dccf463247fc8d2836971e81a59 Mon Sep 17 00:00:00 2001 From: mikael-lovqvists-claude-agent Date: Fri, 13 Mar 2026 16:31:05 +0000 Subject: [PATCH] Add component table plugin for KiCad PCB editor Shows a sortable table of all board components with columns for designation, value, library, package, and sheet instance. Co-Authored-By: Claude Sonnet 4.6 --- local-deploy.sh | 9 +++ metadata.json | 23 ++++++ plugins/component_table/__init__.py | 105 ++++++++++++++++++++++++++++ 3 files changed, 137 insertions(+) create mode 100755 local-deploy.sh create mode 100644 metadata.json create mode 100644 plugins/component_table/__init__.py diff --git a/local-deploy.sh b/local-deploy.sh new file mode 100755 index 0000000..46497ab --- /dev/null +++ b/local-deploy.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -e + +PLUGIN_NAME='component_table' +PLUGIN_SRC="$(realpath plugins/$PLUGIN_NAME)" +KICAD_PLUGINS=~/.local/share/kicad/9.0/scripting/plugins + +ln -sf "$PLUGIN_SRC" "$KICAD_PLUGINS/$PLUGIN_NAME" +echo "Linked $PLUGIN_SRC -> $KICAD_PLUGINS/$PLUGIN_NAME" diff --git a/metadata.json b/metadata.json new file mode 100644 index 0000000..03edaf8 --- /dev/null +++ b/metadata.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://go.kicad.org/pcm/schemas/v1", + "name": "Component Table", + "description": "Shows a sortable table of all board components", + "description_full": "Displays a dialog with a sortable table listing all components on the board, including their designation, value, library, package, and sheet instance.", + "identifier": "com.example.component-table", + "type": "plugin", + "author": { + "name": "KiCad User" + }, + "maintainer": { + "name": "KiCad User" + }, + "license": "MIT", + "resources": {}, + "versions": [ + { + "version": "1.0.0", + "status": "stable", + "kicad_version": "6.0" + } + ] +} diff --git a/plugins/component_table/__init__.py b/plugins/component_table/__init__.py new file mode 100644 index 0000000..b5da952 --- /dev/null +++ b/plugins/component_table/__init__.py @@ -0,0 +1,105 @@ +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()