Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
snhobbs committed Jun 2, 2024
1 parent 6c2ebf5 commit c885607
Show file tree
Hide file tree
Showing 6 changed files with 360 additions and 0 deletions.
Empty file added README.md
Empty file.
37 changes: 37 additions & 0 deletions metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
{
"$schema": "https://go.kicad.org/pcm/schemas/v1",
"name": "KiCad Testpoints",
"description": "Generate a test point report from a schematic",
"description_full": "Uses the fabrication property of pads to mark them as test points. The plugin reads those properties and generates a spreadsheet with the pad identifiers, nets, characteristics, and position on the board. The report is generated in the standard JigsApp format.",
"identifier": "com.github.TheJigsApp.kicadtestpoints",
"type": "plugin",
"author": {
"name": "The JigsApp",
"contact": {
"web": "https://www.thejigsapp.com",
"github": "https://github.com/TheJigsApp"
}
},
"maintainer": {
"name": "The JigsApp",
"contact": {
"web": "https://www.thejigsapp.com",
"github": "https://github.com/TheJigsApp"
}
},
"license": "LGPL-3.0",
"resources": {
"homepage": "https://github.com/TheJigsApp/kicad-testpoints"
},
"versions": [
{
"version": "1.0",
"status": "stable",
"kicad_version": "8.0",
"download_sha256": "fa88f458e0bba310ae27864c763789ff2c89d5e23b45df135f42550ed58f17db",
"download_size": 8727,
"download_url": "https://github.com/TheJigsApp/kicad-testpoints",
"install_size": 16862
}
]
}
Empty file added plugins/__init__.py
Empty file.
25 changes: 25 additions & 0 deletions plugins/common.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import wx
import threading

app = None

def initDialog(initFn):
global app
try:
return initFn()
except Exception:
try:
# Some Linux distributions ship incompatible wxPhoenix version.
# So we start a custom wxApp loop
app = wx.App()
app.InitLocale()
t = threading.Thread(target=app.MainLoop)
t.daemon = True
t.start()
return initFn()
except Exception as e:
raise e from None

def destroyDialog(dialog):
if dialog is not None:
dialog.Destroy()
298 changes: 298 additions & 0 deletions plugins/kicad_testpoints.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,298 @@
import pcbnew
import wx
import re
import os
from .common import initDialog, destroyDialog

class HideReferencesDialog(wx.Dialog):
def __init__(self, state, parent=None, board=None, action=None, updateState=None):
wx.Dialog.__init__(self,
parent,
title=f'Specify which components to hide (version {kikit.__version__})',
style=wx.RESIZE_BORDER | wx.DEFAULT_DIALOG_STYLE)
self.board = board
self.actionCallback = action
self.updateStatusCallback = updateState
self.text = ""
self.selectedItemOnly = False

self.Bind(wx.EVT_CLOSE, self.OnCancel, id=self.GetId())
self.Bind(wx.EVT_SIZE, self.OnSize)

self.SetMinSize(wx.Size(520, 620))

panel = self

vbox = wx.BoxSizer(wx.VERTICAL)
self.item_grid = wx.FlexGridSizer(0, 2, 3, 5)
self.item_grid.AddGrowableCol(1)

label = wx.StaticText(panel,
label="Apply to labels pattern:\n(regular expression)",
size=wx.Size(200, -1),
style=wx.ALIGN_RIGHT)
label.Wrap(200)
self.item_grid.Add(label, 1, wx.ALIGN_CENTRE_VERTICAL)
self.pattern = wx.TextCtrl(panel, style=wx.TE_LEFT, value=state.get("pattern", ".*"),
size=wx.Size(350, -1))
self.Bind(wx.EVT_TEXT, self.OnPatternChange, id=self.pattern.GetId())
self.item_grid.Add(self.pattern, 0, wx.EXPAND)

label = wx.StaticText(panel, label="Only Selected items:",
size=wx.Size(200, -1),
style=wx.ALIGN_RIGHT)
label.Wrap(200)
self.item_grid.Add(label, 1, wx.ALIGN_CENTRE_VERTICAL)
self.selected_only = wx.CheckBox(panel, style=wx.CHK_2STATE)
self.Bind(wx.EVT_CHECKBOX, self.OnselectedItemOnlyChange)
self.selected_only.SetValue(self.selectedItemOnly)
self.item_grid.Add(self.selected_only, 0, wx.EXPAND)

label = wx.StaticText(panel, label="What to do:",
size=wx.Size(200, -1),
style=wx.ALIGN_RIGHT)
label.Wrap(200)
self.item_grid.Add(label, 1, wx.ALIGN_CENTRE_VERTICAL)
self.action = wx.Choice(panel, style=wx.CB_DROPDOWN,
choices=["Show", "Hide"])
self.action.SetSelection(state.get("action", 1))
self.item_grid.Add(self.action, 0, wx.EXPAND)

label = wx.StaticText(panel, label="Apply to:",
size=wx.Size(200, -1),
style=wx.ALIGN_RIGHT)
label.Wrap(200)
self.item_grid.Add(label, 1, wx.ALIGN_CENTRE_VERTICAL)
self.scope = wx.Choice(panel, style=wx.CB_DROPDOWN,
choices=["References only", "Values only", "References and values"])
self.scope.SetSelection(state.get("scope", 2))
self.item_grid.Add(self.scope, 0, wx.EXPAND)

label = wx.StaticText(panel, label="Layers to include:",
size=wx.Size(200, -1),
style=wx.ALIGN_RIGHT | wx.ALIGN_TOP)
label.Wrap(200)
self.item_grid.Add(label, 0, wx.ALIGN_CENTRE_VERTICAL)

self.layers = wx.CheckListBox(panel, choices=[str(Layer(l).name) for l in Layer.all()])
layerCheckState = state.get("layers", [True for _ in Layer.all()])
for l, checked in zip(Layer.all(), layerCheckState):
self.layers.Check(l, checked)
self.item_grid.Add(self.layers, 0, wx.EXPAND)

label = wx.StaticText(panel, label="Select layers:",
size=wx.Size(200, -1),
style=wx.ALIGN_RIGHT | wx.ALIGN_TOP)
label.Wrap(200)
self.item_grid.Add(label, 0, wx.ALIGN_CENTRE_VERTICAL)

buttonGrid = wx.FlexGridSizer(0, 2, 3, 5)
buttonGrid.AddGrowableCol(0)
buttonGrid.AddGrowableCol(1)
self.item_grid.Add(buttonGrid, 0, wx.EXPAND)

allLayersBtn = wx.Button(panel, label='All layers')
self.Bind(wx.EVT_BUTTON, self.OnAllLayers, id=allLayersBtn.GetId())
buttonGrid.Add(allLayersBtn, 1, wx.EXPAND)

noLayersBtn = wx.Button(panel, label='No layers')
self.Bind(wx.EVT_BUTTON, self.OnNoLayers, id=noLayersBtn.GetId())
buttonGrid.Add(noLayersBtn, 1, wx.EXPAND)

techLayersBtn = wx.Button(panel, label='Toggle technical layers')
self.Bind(wx.EVT_BUTTON, self.OnTechnicalLayers, id=techLayersBtn.GetId())
buttonGrid.Add(techLayersBtn, 1, wx.EXPAND)

silkLayersBtn = wx.Button(panel, label='Toggle silkscreen layers')
self.Bind(wx.EVT_BUTTON, self.OnSilkscreenLayers, id=silkLayersBtn.GetId())
buttonGrid.Add(silkLayersBtn, 1, wx.EXPAND)


label = wx.StaticText(panel,
label="Matching references:",
size=wx.Size(200, -1),
style=wx.ALIGN_RIGHT)
label.Wrap(200)
self.item_grid.Add(label, 1, wx.ALIGN_TOP)
self.matchingText = wx.StaticText(panel,
label="Matching references:",
size=wx.Size(350, 80),
style=wx.ALIGN_LEFT | wx.ST_ELLIPSIZE_END)
self.item_grid.Add(self.matchingText, 1, wx.EXPAND)
self.item_grid.AddGrowableRow(5)

button_box = wx.BoxSizer(wx.HORIZONTAL)

cancelButton = wx.Button(panel, label='Close')
self.Bind(wx.EVT_BUTTON, self.OnCancel, id=cancelButton.GetId())
button_box.Add(cancelButton, 1, wx.RIGHT, 10)

self.applyButton = wx.Button(panel, label='Apply')
self.Bind(wx.EVT_BUTTON, self.OnApply, id=self.applyButton.GetId())
button_box.Add(self.applyButton, 1)

vbox.Add(self.item_grid, 1, wx.EXPAND | wx.ALL, 10)
vbox.Add(button_box, 0, wx.ALIGN_RIGHT | wx.LEFT | wx.RIGHT | wx.BOTTOM, 20)

panel.SetSizer(vbox)
vbox.Fit(self)
self.Centre()
self.OnPatternChange(None)

def SetTextPreview(self, text = None):
if text is not None:
self.text = text
self.matchingText.SetLabel(self.text)
self.matchingText.Wrap(self.item_grid.GetColWidths()[1])

def Show(self):
self.OnPatternChange(None)
super().Show()

def OnSize(self, event):
self.SetTextPreview()
if event is not None:
event.Skip()

def OnCancel(self, event):
if self.updateStatusCallback is not None:
selectedLayers = self.GetActiveLayers()
self.updateStatusCallback({
"pattern": self.GetPattern(),
"scope": self.scope.GetSelection(),
"action": self.action.GetSelection(),
"layers": [l in selectedLayers for l in Layer.all()]
})
destroyDialog(self)

def OnApply(self, event):
if self.actionCallback is not None:
self.actionCallback(self)

def OnAllLayers(self, event):
for l in Layer.all():
self.layers.Check(l)

def OnNoLayers(self, event):
for l in Layer.all():
self.layers.Check(l, False)

def OnTechnicalLayers(self, event):
checked = set(self.layers.GetCheckedItems())
enable = not all(x in checked for x in Layer.allTech())
for l in Layer.allTech():
self.layers.Check(l, enable)

def OnSilkscreenLayers(self, event):
checked = set(self.layers.GetCheckedItems())
enable = not all(x in checked for x in Layer.allSilk())
for l in Layer.allSilk():
self.layers.Check(l, enable)

def GetShowLabels(self):
return self.action.GetSelection() == 0

def GetPattern(self):
return self.pattern.GetValue()

def GetActiveLayers(self):
return set(self.layers.GetCheckedItems())

def GetSelectedItemOnly(self):
return self.selectedItemOnly

def ModifyReferences(self):
return self.scope.GetSelection() in [0, 2]

def ModifyValues(self):
return self.scope.GetSelection() in [1, 2]

def OnselectedItemOnlyChange(self, event):
self.selectedItemOnly = not self.selectedItemOnly
self.OnPatternChange(None)

def OnPatternChange(self, event):
try:
regex = re.compile(self.pattern.GetValue())
self.applyButton.Enable()
if not self.board:
self.SetTextPreview("")
else:
refs = []
for footprint in self.board.GetFootprints():
if self.selectedItemOnly and not footprint.IsSelected():
continue

if regex.match(footprint.GetReference()):
refs.append(footprint.GetReference())
if len(refs) > 0:
self.SetTextPreview(", ".join(refs))
else:
self.SetTextPreview("None")
except re.error as e:
self.applyButton.Disable()
self.SetTextPreview(f"Invalid regular expression: {e}")
finally:
self.SendSizeEvent()


class HideReferencesPlugin(pcbnew.ActionPlugin):
def defaults(self):
self.name = "KiKit: Show/hide references"
self.category = "KiKit"
self.description = "Show/hide references in the board based on regular expression"
self.icon_file_name = os.path.join(PKG_BASE, "resources", "graphics", "removeRefIcon_24x24.png")
self.show_toolbar_button = True

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._dialogState = {}

def error(self, msg):
wx.MessageBox(msg, "Error", wx.OK | wx.ICON_ERROR)

def action(self, dialog):
try:
if dialog.ModifyReferences():
modify.references(dialog.board, dialog.GetShowLabels(),
dialog.GetPattern(), dialog.GetActiveLayers(),
dialog.GetSelectedItemOnly())
if dialog.ModifyValues():
modify.values(dialog.board, dialog.GetShowLabels(),
dialog.GetPattern(), dialog.GetActiveLayers(),
dialog.GetSelectedItemOnly())
pcbnew.Refresh()
except Exception as e:
self.error(f"Cannot perform: {e}")

def updateState(self, newState):
self._dialogState = newState

def Run(self):
# Find the pcbnew main window
pcbnew_window = wx.FindWindowByName("PcbFrame")
if pcbnew_window is None:
# Something failed, abort
self.error("Failed to find pcbnew main window")
return
try:
dialog = initDialog(lambda: HideReferencesDialog(
state=self._dialogState,
board=pcbnew.GetBoard(),
action=lambda d: self.action(d),
updateState=lambda s: self.updateState(s),
parent=pcbnew_window))
dialog.Show()
except Exception as e:
self.error(f"Cannot perform: {e}")

plugin = HideReferencesPlugin

if __name__ == "__main__":
import sys

dialog = initDialog(lambda: HideReferencesDialog(
{},
board=pcbnew.LoadBoard(sys.argv[1]),
updateState=print))
dialog.ShowModal()
Binary file added resources/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit c885607

Please sign in to comment.