forked from snhobbs/kicad-testpoints-pcm
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
6 changed files
with
360 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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() |
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.