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 <noreply@anthropic.com>
This commit is contained in:
9
local-deploy.sh
Executable file
9
local-deploy.sh
Executable file
@@ -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"
|
||||||
23
metadata.json
Normal file
23
metadata.json
Normal file
@@ -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"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
105
plugins/component_table/__init__.py
Normal file
105
plugins/component_table/__init__.py
Normal file
@@ -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()
|
||||||
Reference in New Issue
Block a user