From 37e0a2a07fd4d134e8fd09984518ff484b528acd Mon Sep 17 00:00:00 2001 From: easyw Date: Sun, 3 Nov 2019 15:29:21 +0100 Subject: [PATCH] first step to renew the repo --- AnnularChecker/AR-params - Copy.fbp | 914 ++++++++++ AnnularChecker/AR-params.fbp | 1016 +++++++++++ AnnularChecker/__init__ (copy).py.txt | 2 + AnnularChecker/__init__.py | 2 + AnnularChecker/__init__.pyc | Bin 0 -> 265 bytes AnnularChecker/__init__HTML.py.txt | 2 + AnnularChecker/__init__OLD.py.txt | 56 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 209 bytes .../annular_checker.cpython-36.pyc | Bin 0 -> 11995 bytes .../annular_checker_html.cpython-36.pyc | Bin 0 -> 10294 bytes AnnularChecker/annular.png | Bin 0 -> 1092 bytes .../annular_checker-OLD.py.txt | 1213 ++++++------ AnnularChecker/annular_checker-html.py.txt | 455 +++++ AnnularChecker/annular_checker.py | 692 +++++++ AnnularChecker/annular_checker.pyc | Bin 0 -> 14910 bytes AnnularChecker/annular_checker_html.py.txt | 455 +++++ AnnularChecker/annular_ico.svg | 214 +++ AnnularChecker/dlg.py.txt | 85 + AnnularChecker/outDialog.fbp | 540 ++++++ AnnularChecker/tkinter-test.txt | 2 + FabricationPositions/__init__.py | 2 + .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 238 bytes .../fabrication_positions.cpython-36.pyc | Bin 0 -> 12389 bytes .../fabricationPositions-old.png | Bin 0 -> 1326 bytes .../fabricationPositions-old.svg | 308 ++++ FabricationPositions/fabricationPositions.png | Bin 0 -> 1332 bytes FabricationPositions/fabricationPositions.svg | 302 +++ .../fabrication_positions.py | 13 +- FabricationPositions/g888.png | Bin 0 -> 831 bytes MoveToLayer/Move2LayerDlg.py | 75 + MoveToLayer/__init__.py | 2 + .../__pycache__/Move2LayerDlg.cpython-36.pyc | Bin 0 -> 1928 bytes .../__pycache__/__init__.cpython-36.pyc | Bin 0 -> 228 bytes .../__pycache__/move_to_layer.cpython-36.pyc | Bin 0 -> 3209 bytes MoveToLayer/move2layer.png | Bin 0 -> 1501 bytes MoveToLayer/move2layer.svg | 200 ++ .../move_to_layer (copy).py | 79 +- MoveToLayer/move_to_layer.py | 288 +++ MoveToLayer/move_to_layer_dlg-t.fbp | 1623 +++++++++++++++++ MoveToLayer/move_to_layer_dlg.fbp | 536 ++++++ .../action_menu_pcb2dxf.py | 0 action_menu_get3D_models.py | 15 - 42 files changed, 8432 insertions(+), 659 deletions(-) create mode 100644 AnnularChecker/AR-params - Copy.fbp create mode 100644 AnnularChecker/AR-params.fbp create mode 100644 AnnularChecker/__init__ (copy).py.txt create mode 100644 AnnularChecker/__init__.py create mode 100644 AnnularChecker/__init__.pyc create mode 100644 AnnularChecker/__init__HTML.py.txt create mode 100644 AnnularChecker/__init__OLD.py.txt create mode 100644 AnnularChecker/__pycache__/__init__.cpython-36.pyc create mode 100644 AnnularChecker/__pycache__/annular_checker.cpython-36.pyc create mode 100644 AnnularChecker/__pycache__/annular_checker_html.cpython-36.pyc create mode 100644 AnnularChecker/annular.png rename action_menu_annular_check.py => AnnularChecker/annular_checker-OLD.py.txt (96%) create mode 100644 AnnularChecker/annular_checker-html.py.txt create mode 100644 AnnularChecker/annular_checker.py create mode 100644 AnnularChecker/annular_checker.pyc create mode 100644 AnnularChecker/annular_checker_html.py.txt create mode 100644 AnnularChecker/annular_ico.svg create mode 100644 AnnularChecker/dlg.py.txt create mode 100644 AnnularChecker/outDialog.fbp create mode 100644 AnnularChecker/tkinter-test.txt create mode 100644 FabricationPositions/__init__.py create mode 100644 FabricationPositions/__pycache__/__init__.cpython-36.pyc create mode 100644 FabricationPositions/__pycache__/fabrication_positions.cpython-36.pyc create mode 100644 FabricationPositions/fabricationPositions-old.png create mode 100644 FabricationPositions/fabricationPositions-old.svg create mode 100644 FabricationPositions/fabricationPositions.png create mode 100644 FabricationPositions/fabricationPositions.svg rename action_menu_positions.py => FabricationPositions/fabrication_positions.py (97%) create mode 100644 FabricationPositions/g888.png create mode 100644 MoveToLayer/Move2LayerDlg.py create mode 100644 MoveToLayer/__init__.py create mode 100644 MoveToLayer/__pycache__/Move2LayerDlg.cpython-36.pyc create mode 100644 MoveToLayer/__pycache__/__init__.cpython-36.pyc create mode 100644 MoveToLayer/__pycache__/move_to_layer.cpython-36.pyc create mode 100644 MoveToLayer/move2layer.png create mode 100644 MoveToLayer/move2layer.svg rename action_menu_move_to_layer.py => MoveToLayer/move_to_layer (copy).py (81%) create mode 100644 MoveToLayer/move_to_layer.py create mode 100644 MoveToLayer/move_to_layer_dlg-t.fbp create mode 100644 MoveToLayer/move_to_layer_dlg.fbp rename action_menu_pcb2dxf.py => PcbToDxf/action_menu_pcb2dxf.py (100%) delete mode 100644 action_menu_get3D_models.py diff --git a/AnnularChecker/AR-params - Copy.fbp b/AnnularChecker/AR-params - Copy.fbp new file mode 100644 index 0000000..715b6c3 --- /dev/null +++ b/AnnularChecker/AR-params - Copy.fbp @@ -0,0 +1,914 @@ + + + + + + C++ + 1 + source_name + 0 + 0 + res + UTF-8 + connect + + 1000 + none + + 0 + parametersDlg + + . + + 1 + 1 + 1 + 1 + UI + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + wxBOTH + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + + AR_Prm + + 320,193 + wxDEFAULT_DIALOG_STYLE + ; forward_declare + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer1 + wxVERTICAL + none + + 5 + wxEXPAND + 1 + + 2 + 0 + + gSizer2 + none + 0 + 0 + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + PHD margin + 0 + + 0 + + + 0 + + 1 + m_staticText11 + 1 + + + protected + 1 + -1,-1 + Resizable + 1 + + + ; forward_declare + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_textPHD + 1 + + + protected + 1 + + Resizable + 1 + + + ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + AR for pads + 0 + + 0 + + + 0 + + 1 + m_staticText1 + 1 + + + protected + 1 + -1,-1 + Resizable + 1 + + + ; forward_declare + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_textPHD1 + 1 + + + protected + 1 + + Resizable + 1 + + + ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + AR for vias + 0 + + 0 + + + 0 + + 1 + m_staticText12 + 1 + + + protected + 1 + -1,-1 + Resizable + 1 + + + ; forward_declare + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_textPHD11 + 1 + + + protected + 1 + + Resizable + 1 + + + ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 1 + + 2 + 0 + + gSizer1 + none + 0 + 0 + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + wxID_ANY + OK + + 0 + + 0 + + + 0 + + 1 + m_ok_btn + 1 + + + protected + 1 + + + + Resizable + 1 + + + ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + wxID_ANY + Cancel + + 0 + + 0 + + + 0 + + 1 + m_cancel_btn + 1 + + + protected + 1 + + + + Resizable + 1 + + + ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AnnularChecker/AR-params.fbp b/AnnularChecker/AR-params.fbp new file mode 100644 index 0000000..3a9e63a --- /dev/null +++ b/AnnularChecker/AR-params.fbp @@ -0,0 +1,1016 @@ + + + + + + Python + 1 + source_name + 0 + 0 + res + UTF-8 + connect + + 1000 + none + + 0 + parametersDlg + + . + + 1 + 1 + 1 + 1 + UI + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + wxBOTH + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + + AR_Prm + + 320,229 + wxDEFAULT_DIALOG_STYLE + ; forward_declare + Annular Checker + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer1 + wxVERTICAL + none + + 5 + wxEXPAND + 1 + + 2 + 0 + + gSizer2 + none + 0 + 0 + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + PHD margin + 0 + + 0 + + + 0 + + 1 + m_StaticTextPHD + 1 + + + protected + 1 + -1,-1 + Resizable + 1 + + + ; forward_declare + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_textPHD + 1 + + + protected + 1 + + Resizable + 1 + + + ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + AR for pads + 0 + + 0 + + + 0 + + 1 + m_StaticTextAR_SET + 1 + + + protected + 1 + -1,-1 + Resizable + 1 + + + ; forward_declare + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_textAR_SET + 1 + + + protected + 1 + + Resizable + 1 + + + ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + AR for vias + 0 + + 0 + + + 0 + + 1 + m_StaticTextAR_SET_V + 1 + + + protected + 1 + -1,-1 + Resizable + 1 + + + ; forward_declare + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_textAR_SET_V + 1 + + + protected + 1 + + Resizable + 1 + + + ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 1 + + + bSizer2 + wxVERTICAL + none + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Version + 0 + + 0 + + + 0 + + 1 + m_staticTextVersion + 1 + + + protected + 1 + + Resizable + 1 + + + ; forward_declare + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 1 + + 2 + 0 + + gSizer1 + none + 0 + 0 + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + wxID_ANY + OK + + 0 + + 0 + + + 0 + + 1 + m_ok_btn + 1 + + + protected + 1 + + + + Resizable + 1 + + + ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + wxID_ANY + Cancel + + 0 + + 0 + + + 0 + + 1 + m_cancel_btn + 1 + + + protected + 1 + + + + Resizable + 1 + + + ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AnnularChecker/__init__ (copy).py.txt b/AnnularChecker/__init__ (copy).py.txt new file mode 100644 index 0000000..7caa4ab --- /dev/null +++ b/AnnularChecker/__init__ (copy).py.txt @@ -0,0 +1,2 @@ +from .annular_checker import annular_check +annular_check().register() diff --git a/AnnularChecker/__init__.py b/AnnularChecker/__init__.py new file mode 100644 index 0000000..7caa4ab --- /dev/null +++ b/AnnularChecker/__init__.py @@ -0,0 +1,2 @@ +from .annular_checker import annular_check +annular_check().register() diff --git a/AnnularChecker/__init__.pyc b/AnnularChecker/__init__.pyc new file mode 100644 index 0000000000000000000000000000000000000000..1d8da1efc719defb257fd46d70c23bb9dd63888b GIT binary patch literal 265 zcmZ9H!3x4K42IL~B7*2Ec-zU{MZ_`Bf)g)NDWxm3ZtF_hfu244kUoPiV5)*(NWPGt zK>ozf(eU!P9|e3P!oI?CQ$&eLpaEzJ90(9e;0ADx0!bOcE{SnV5kwN;M+mNU-*C&s zNs4;&-tgmpUD^UA@B@UEm2zI%Bq%?)Espt|?wz!b_Q;%O#;m#LwCH%NbVX|=cuAeG q%J?vwrmvKCY4-7u2Mf}sj47==V>5GoUtF|Z**9_-JiGAmguDPdEkCpX literal 0 HcmV?d00001 diff --git a/AnnularChecker/__init__HTML.py.txt b/AnnularChecker/__init__HTML.py.txt new file mode 100644 index 0000000..ba81907 --- /dev/null +++ b/AnnularChecker/__init__HTML.py.txt @@ -0,0 +1,2 @@ +from .annular_checker_html import annular_check +annular_check().register() diff --git a/AnnularChecker/__init__OLD.py.txt b/AnnularChecker/__init__OLD.py.txt new file mode 100644 index 0000000..bae6b54 --- /dev/null +++ b/AnnularChecker/__init__OLD.py.txt @@ -0,0 +1,56 @@ +from annular_checker import annular_check +import pcbnew +import wx +import wx.aui +import threading +import time +import sys +import subprocess +import os +# import pcbnew +from pcbnew import * +# import base64 +from wx.lib.embeddedimage import PyEmbeddedImage + +plugin = annular_check() +plugin.register() + + +def check_for_annular_button(): + # From Miles McCoo's blog + # https://kicad.mmccoo.com/2017/03/05/adding-your-own-command-buttons-to-the-pcbnew-gui/ + def find_pcbnew_window(): + windows = wx.GetTopLevelWindows() + pcbnewwn = [w for w in windows if "Pcbnew" in w.GetTitle()] + if len(pcbnewwn) != 1: + return None + return pcbnewwn[0] + + def callback(_): + plugin.Run() + + import os + path = os.path.dirname(__file__) + bm = wx.Bitmap(path + '/annular.png', wx.BITMAP_TYPE_PNG) + button_wx_item_id = 1 + while True: + time.sleep(1.5) + pcbwin = find_pcbnew_window() + if not pcbwin: + continue + + top_tb = pcbwin.FindWindowById(pcbnew.ID_H_TOOLBAR) + if button_wx_item_id == 1 or not top_tb.FindTool(button_wx_item_id): + #top_tb.AddSeparator() + button_wx_item_id = wx.NewId() + top_tb.AddTool(button_wx_item_id, "aChecker", bm, + "PCB Annular Checker", wx.ITEM_NORMAL) + + top_tb.Bind(wx.EVT_TOOL, callback, id=button_wx_item_id) + top_tb.Realize() + + +if 0: + t = threading.Thread(target=check_for_annular_button) + t.daemon = True + t.start() diff --git a/AnnularChecker/__pycache__/__init__.cpython-36.pyc b/AnnularChecker/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..b16f157ff1b62e9521e6ce40031722dff70cec48 GIT binary patch literal 209 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 literal 0 HcmV?d00001 diff --git a/AnnularChecker/__pycache__/annular_checker_html.cpython-36.pyc b/AnnularChecker/__pycache__/annular_checker_html.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..16311f17fdd8aaba43b06433f322216f5547d3bd GIT binary patch literal 10294 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 aSMPv~5dF`jpGhYuAr5g+^~ng}-}_&9qKG&E literal 0 HcmV?d00001 diff --git a/AnnularChecker/annular.png b/AnnularChecker/annular.png new file mode 100644 index 0000000000000000000000000000000000000000..319fed8396ce00736c99669b3ccfc250b5fa68b8 GIT binary patch literal 1092 zcmV-K1iSl*P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGcKmY&;KmjPYOcDS902y>eSaefwW^{L9 za%BK;VQFr3E^cLXAT%y8E-^DS16z6k00Xp1L_t(YOSP40Y*bYg$KQ0Oi^E&)o0&J0 z21==wsuhh8N?5|u1_&lvBnHyf7>SV@wYB8~(XdI_LLr0%3P}kX!qTV^gqALJo9Z`> zpBf`J=?6oMF(lQDZJoBW9RKINb~XgF2Mu)gB&dTLT<%M#< z>_UmiC^rU0y?;=jVo~W`ojy9OQPT$+ovziW?vO^y?ugk%;-2ejEA%Cp<^jA&Vu3Wd ze?ULY6JNNe)6F+kifA&CY$vA3^mV0bF3h>!>)KB!(gsW~0B(@QE}I->*v-jhU+MH) zak?~>--{GlbJIG7I4K9Acmq5OcmYR3?&cR>7^jP+C z=Xt1J2a7>&LLLZ;M&7}(IxS5Q&iBXusoE78f4e#oUX&k89Y3>2HFvL_kcXlA46Fy< zB)LAw3901>tuBjDD1^M37#pK-MR_zi=Z`{#0s}f3ZCV)Bz69O7U@5Xqw}jU*`syuV z6UT9JG%{jIljG4NjM+;!TYG1~aR<5sU^OyLw}gTC*2{MJM7|NTZ0Rwvq`-KN zFD2}?ANTK*C#EdpTuipK*e-Qi$W+{knm-Uxy!STfE`Xg#HQf@PQWLM-vBfTrsS1t0 zQ!`CnT}0j8G`8pcNK~`u^l!9Yi#VtJ3DsS24s1`zefN~-kuY8UrJ$%fH}U`AOW)RF zl_aks=SQe+gJYmPAxAij2l_=$`gC9VTYnNq>orrXH!F|fXa`rpC*YN2xn%5=WPi8# zfs;Q*g9`aG3z;!PrpEo2*mqk@IK7TuD82!wKqXjclOwX+weEc1x1ygXJ=dYrfxQ~_ zY*OgqOA7s3s!-o1l@2wiRMMVkS|L}VvmbN09Q;b&1IxgIR5`axmn2Juc2+*_>ky+X z$OOiwY~8yNUq<=&NdL_N5m7hP|Ndxx^u{d3;t z-0vMZ(kXbwJI>h z;HlqGPT;1W2l$wUpcL@#R)Xa-fAa@I2f2XrO)-Zl=b!n3r2hchUN?hsv~= 4.0 -# annular.py release "1.5.1" -# -# annular.py checking PCB for Annular Ring in Vias and TH Pads -# (SMD, Connector and NPTH are skipped) -# default Annular Ring >= 0.15 both for TH Pads and Vias -# to change values modify: -# -# AR_SET = 0.150 #minimum annular accepted for pads -# AR_SET_V = 0.150 #minimum annular accepted for vias - -# annular.py - - -___version___="1.5.8x" - -global mm_ius, DRL_EXTRA, AR_SET, AR_SET_V, DRL_EXTRA_ius, MIN_AR_SIZE, MIN_AR_SIZE_V, found_violations, LogMsg -#wx.LogMessage("My message") -mm_ius = 1000000.0 -# (consider always drill +0.1) -DRL_EXTRA=0.1 -DRL_EXTRA_ius=DRL_EXTRA * mm_ius - -AR_SET = 0.125 #minimum annular accepted for pads -MIN_AR_SIZE = AR_SET * mm_ius - -AR_SET_V = 0.125 #minimum annular accepted for vias -MIN_AR_SIZE_V = AR_SET_V * mm_ius - -import sys -import wx -import wx.richtext -import subprocess -import os -import pcbnew -from pcbnew import * -import base64 -from wx.lib.embeddedimage import PyEmbeddedImage -sys.path.append(os.path.dirname(__file__)) - - -class annular_check( pcbnew.ActionPlugin ): - """ - A script to check for annular ring violations - both for TH pads and vias - requirements: KiCAD pcbnew >= 4.0 - annular.py release "1.5.1" - - annular.py checking PCB for Annular Ring in Vias and TH Pads - (SMD, Connector and NPTH are skipped) - default Annular Ring >= 0.15 both for TH Pads and Vias - to change values modify: - - AR_SET = 0.150 #minimum annular accepted for pads - AR_SET_V = 0.150 #minimum annular accepted for vias - """ - - def defaults( self ): - """ - Method defaults must be redefined - self.name should be the menu label to use - self.category should be the category (not yet used) - self.description should be a comprehensive description - of the plugin - """ - self.name = "Annular check" - self.category = "Checking PCB" - self.description = "Automaticaly check annular on an existing PCB" - - def Run( self ): - - ########################################################################### - ## Class AR_Prm - ########################################################################### - - class AR_Prm ( wx.Dialog ): - - def __init__( self, parent ): - wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = "AR parameters", pos = wx.DefaultPosition, size = wx.Size( 320,193 ), style = wx.DEFAULT_DIALOG_STYLE ) - - self.SetSizeHints( 500,500 ) - - self.SetIcon(PyEmbeddedImage(annular_ico_b64_data).GetIcon()) - - bSizer1 = wx.BoxSizer( wx.VERTICAL ) - - gSizer2 = wx.GridSizer( 0, 2, 0, 0 ) - - self.m_staticText11 = wx.StaticText( self, wx.ID_ANY, u"PHD margin", wx.Point( -1,-1 ), wx.DefaultSize, 0 ) - self.m_staticText11.Wrap( -1 ) - - gSizer2.Add( self.m_staticText11, 0, wx.ALL, 5 ) - - self.m_textPHD = wx.TextCtrl( self, wx.ID_ANY, str(DRL_EXTRA), wx.DefaultPosition, wx.DefaultSize, 0 ) - gSizer2.Add( self.m_textPHD, 0, wx.ALL, 5 ) - - self.m_staticText1 = wx.StaticText( self, wx.ID_ANY, u"AR for pads", wx.Point( -1,-1 ), wx.DefaultSize, 0 ) - self.m_staticText1.Wrap( -1 ) - - gSizer2.Add( self.m_staticText1, 0, wx.ALL, 5 ) - - self.m_textAR_SET = wx.TextCtrl( self, wx.ID_ANY, str(AR_SET), wx.DefaultPosition, wx.DefaultSize, 0 ) - gSizer2.Add( self.m_textAR_SET, 0, wx.ALL, 5 ) - - self.m_staticText12 = wx.StaticText( self, wx.ID_ANY, u"AR for vias", wx.Point( -1,-1 ), wx.DefaultSize, 0 ) - self.m_staticText12.Wrap( -1 ) - - gSizer2.Add( self.m_staticText12, 0, wx.ALL, 5 ) - - self.m_textAR_SET_V = wx.TextCtrl( self, wx.ID_ANY, str(AR_SET_V), wx.DefaultPosition, wx.DefaultSize, 0 ) - gSizer2.Add( self.m_textAR_SET_V, 0, wx.ALL, 5 ) - - - bSizer1.Add( gSizer2, 1, wx.EXPAND, 5 ) - - gSizer1 = wx.GridSizer( 0, 2, 0, 0 ) - - self.m_ok_btn = wx.Button( self, wx.ID_ANY, u"OK", wx.DefaultPosition, wx.DefaultSize, 0 ) - gSizer1.Add( self.m_ok_btn, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 5 ) - - # self.m_cancel_btn = wx.Button( self, wx.ID_ANY, u"Cancel", wx.DefaultPosition, wx.DefaultSize, 0 ) - # gSizer1.Add( self.m_cancel_btn, 0, wx.ALL, 5 ) - - - bSizer1.Add( gSizer1, 1, wx.EXPAND, 5 ) - - - self.SetSizer( bSizer1 ) - self.Layout() - - self.Centre( wx.BOTH ) - - #### ----- connections - # Connect Events - self.Bind(wx.EVT_BUTTON, self.OnClickOK, self.m_ok_btn) - # self.Bind(wx.EVT_BUTTON, self.OnClickCancel, self.m_cancel_btn) - # Tooltips - #self.m_cancel_btn.SetToolTip( wx.ToolTip(u"Cancel" )) - self.m_ok_btn.SetToolTip( wx.ToolTip(u"Confirm" )) - self.m_ok_btn.SetFocus() - self.m_staticText1.SetToolTip( wx.ToolTip(u"Annular Ring for Pads (mm)" )) - self.m_textAR_SET.SetToolTip( wx.ToolTip(u"Annular Ring for Pads (mm)" )) - self.m_textAR_SET_V.SetToolTip( wx.ToolTip(u"Annular Ring for Vias (mm)" )) - self.m_staticText12.SetToolTip( wx.ToolTip(u"Annular Ring for Vias (mm)" )) - self.m_textPHD.SetToolTip( wx.ToolTip(u"Drill extra margin (mm)" )) - self.m_staticText11.SetToolTip( wx.ToolTip(u"Drill extra margin (mm)" )) - - def __del__( self ): - pass - - def OnClickOK(self, event): - global mm_ius, DRL_EXTRA, AR_SET, AR_SET_V, DRL_EXTRA_ius, MIN_AR_SIZE, MIN_AR_SIZE_V - - self.m_ok_btn.SetLabel("Clicked") - phd = float(self.m_textPHD.GetValue().replace(',','.')) - ar = float(self.m_textAR_SET.GetValue().replace(',','.')) - arv = float(self.m_textAR_SET_V.GetValue().replace(',','.')) - DRL_EXTRA=phd - DRL_EXTRA_ius=DRL_EXTRA * mm_ius - - AR_SET = ar #minimum annular accepted for pads - MIN_AR_SIZE = AR_SET * mm_ius - - AR_SET_V = arv #minimum annular accepted for vias - MIN_AR_SIZE_V = AR_SET_V * mm_ius - self.Destroy() - - def OnClickCancel(self, event): - self.m_cancel_btn.SetLabel("Clicked") - self.Destroy() - - #wx.MessageDialog(self.frame,"ciao") - #subprocess.check_call(["C:\pathToYourProgram\yourProgram.exe", "your", "arguments", "comma", "separated"]) - #http://stackoverflow.com/questions/1811691/running-an-outside-program-executable-in-python - - ## class displayDialog(wx.Dialog): - ## """ - ## The default frame - ## http://stackoverflow.com/questions/3566603/how-do-i-make-wx-textctrl-multi-line-text-update-smoothly - ## """ - ## global mm_ius, DRL_EXTRA, AR_SET, AR_SET_V, DRL_EXTRA_ius, MIN_AR_SIZE, MIN_AR_SIZE_V, found_violations - ## #---------------------------------------------------------------------- - ## #def __init__(self): - ## # """Constructor""" - ## # wx.Frame.__init__(self, None, title="Display Frame", style=wx.DEFAULT_FRAME_STYLE, wx.ICON_INFORMATION) - ## # panel = wx.Panel(self) - ## def __init__(self, parent): - ## wx.Dialog.__init__(self, parent, id=-1, title="Annular Checker")# - ## #, style=wx.DEFAULT_DIALOG_STYLE, wx.ICON_INFORMATION) - ## #, style=wx.DEFAULT_DIALOG_STYLE, wx.ICON_INFORMATION) - ## #, pos=DefaultPosition, size=DefaultSize, style = wx.DEFAULT_FRAME_STYLE & (~wx.MAXIMIZE_BOX), name="fname") - ## #, wx.ICON_INFORMATION) #, title="Annular Check", style=wx.DEFAULT_FRAME_STYLE, wx.ICON_INFORMATION) - ## # - ## - ## self.SetIcon(PyEmbeddedImage(annular_ico_b64_data).GetIcon()) - ## #wx.IconFromBitmap(wx.Bitmap("icon.ico", wx.BITMAP_TYPE_ANY))) - ## self.panel = wx.Panel(self) - ## - ## if found_violations: - ## self.title = wx.StaticText(self.panel, label="") - ## #self.title.SetForegroundColour('#FF0000') - ## #self.title.SetBackgroundColour('#FF0000') - ## #font = wx.Font(wx.DEFAULT, wx.DECORATIVE, wx.ITALIC, wx.BOLD) - ## #self.title.SetFont(font) - ## else: - ## self.title = wx.StaticText(self.panel, label="") - ## #self.title.SetBackgroundColour('#00FF00') - ## #font = wx.Font(wx.DEFAULT, wx.DECORATIVE, wx.ITALIC, wx.BOLD) - ## #self.title.SetFont(font) - ## #self.result = wx.StaticText(self.panel, label="") - ## #self.result.SetForegroundColour('#FF0000') - ## #self.button = wx.Button(self.panel, label="Save") - ## #self.lblname = wx.StaticText(self.panel, label="Your name:") - ## #self.editname = wx.TextCtrl(self.panel, size=(140, -1)) - ## ##self.editname = wx.TextCtrl(self.panel, size = (400, 400), style = wx.TE_MULTILINE|wx.TE_READONLY) - ## self.m_richText1 = wx.richtext.RichTextCtrl( self.panel, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, size = (400, 400), style = 0|wx.VSCROLL|wx.HSCROLL|wx.WANTS_CHARS )# wx.TE_MULTILINE|wx.TE_READONLY) #0|wx.VSCROLL|wx.HSCROLL|wx.NO_BORDER|wx.WANTS_CHARS ) - ## #bSizer1.Add( self.m_richText1, 1, wx.EXPAND |wx.ALL, 5 ) - ## - ## - ## # Set sizer for the frame, so we can change frame size to match widgets - ## self.windowSizer = wx.BoxSizer() - ## self.windowSizer.Add(self.panel, 1, wx.ALL | wx.EXPAND) - ## - ## # Set sizer for the panel content - ## self.sizer = wx.GridBagSizer(5, 0) - ## self.sizer.Add(self.title, (0, 0)) - ## #self.sizer.Add(self.result, (1, 0)) - ## #self.sizer.Add(self.lblname, (1, 0)) - ## ## self.sizer.Add(self.editname, (1, 0)) - ## self.sizer.Add(self.m_richText1, (1, 0)) - ## #self.ok_btn = wx.Button( self, wx.ID_ANY, u"Copy errors", wx.DefaultPosition, wx.DefaultSize, 0 ) - ## #self.sizer.Add( self.ok_btn, 0, wx.ALL | wx.EXPAND) - ## #self.sizer.Add(self.ok_btn, (2, 0)) #, wx.ALL | flag=wx.EXPAND) - ## #self.sizer.Add( self.ok_btn, 0, wx.ALL, 5 ) - ## - ## #self.sizer.Add(self.ok_btn, (2, 0), (1, 2), flag=wx.EXPAND) - ## - ## # Set simple sizer for a nice border - ## self.border = wx.BoxSizer() - ## self.border.Add(self.sizer, 1, wx.ALL | wx.EXPAND, 5) - ## - ## #self.ok_btn = wx.Button( self, wx.ID_ANY, u"Copy errors", wx.DefaultPosition, wx.DefaultSize, 0 ) - ## #self.windowSizer.Add(self.ok_btn, 0, wx.ALL) - ## #self.sizer.Add( self.ok_btn, (2,0)) - ## #self.sizer.Add( self.ok_btn, 0, wx.ALL, 5 ) - ## # Use the sizers - ## self.panel.SetSizerAndFit(self.border) - ## self.SetSizerAndFit(self.windowSizer) - ## #self.result.SetLabel(msg) - ## # Set event handlers - ## #self.button.Bind(wx.EVT_BUTTON, self.OnButton) - ## #self.Show() - ## #self.Bind(wx.EVT_CLOSE,self.OnClose) - ## - ## #def OnClose(self,e): - ## # #wx.LogMessage("c") - ## # e.Skip() - ## #self.Close() - ########################################################################### - ## Class displayDialog - ########################################################################### - - class displayDialog ( wx.Dialog ): - - global mm_ius, DRL_EXTRA, AR_SET, AR_SET_V, DRL_EXTRA_ius, MIN_AR_SIZE, MIN_AR_SIZE_V, found_violations, LogMsg - - def __init__( self, parent ): - wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = u"Annular Checker", pos = wx.DefaultPosition, size = wx.Size( 450,521 ), style = wx.DEFAULT_DIALOG_STYLE ) - - self.SetSizeHints( 300,100 ) - self.SetIcon(PyEmbeddedImage(annular_ico_b64_data).GetIcon()) - - bSizer1 = wx.BoxSizer( wx.VERTICAL ) - - bSizer2 = wx.BoxSizer( wx.VERTICAL ) - - self.m_staticTitle = wx.StaticText( self, wx.ID_ANY, u"", wx.DefaultPosition, wx.DefaultSize, 0 ) - self.m_staticTitle.Wrap( -1 ) - - bSizer2.Add( self.m_staticTitle, 0, wx.ALL, 5 ) - - self.m_richText1 = wx.richtext.RichTextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_READONLY|wx.VSCROLL|wx.HSCROLL|wx.NO_BORDER|wx.WANTS_CHARS ) - self.m_richText1.SetMinSize( wx.Size( 400,400 ) ) - - bSizer2.Add( self.m_richText1, 1, wx.EXPAND |wx.ALL, 5 ) - - gSizer3 = wx.GridSizer( 0, 2, 0, 0 ) - - self.ok_btn = wx.Button( self, wx.ID_ANY, u"OK", wx.DefaultPosition, wx.DefaultSize, 0 ) - gSizer3.Add( self.ok_btn, 0, wx.ALL, 5 ) - - self.copy_btn = wx.Button( self, wx.ID_ANY, u"Copy Text", wx.DefaultPosition, wx.DefaultSize, 0 ) - gSizer3.Add( self.copy_btn, 0, wx.ALL, 5 ) - - - bSizer2.Add( gSizer3, 1, wx.EXPAND, 5 ) - - - bSizer1.Add( bSizer2, 1, wx.EXPAND, 5 ) - - - self.SetSizer( bSizer1 ) - self.Layout() - - self.Centre( wx.BOTH ) - - #### ----- connections - # Connect Events - self.Bind(wx.EVT_BUTTON, self.OnClickOK, self.ok_btn) - self.Bind(wx.EVT_BUTTON, self.OnClickCopy, self.copy_btn) - self.ok_btn.SetFocus() - # Tooltips - self.copy_btn.SetToolTip( wx.ToolTip(u"Copy Text to Clipboard" )) - self.ok_btn.SetToolTip( wx.ToolTip(u"Exit" )) - - def __del__( self ): - pass - - def OnClickOK(self, event): - self.Destroy() - - def OnClickCopy(self, event): - self.m_richText1.SelectAll() - self.m_richText1.Copy() - #global LogMsg - #copy2clip(LogMsg) - self.copy_btn.SetLabel("Text Copied") - - #def setMsg(self, t_msg): - # pass - #self.editname.SetValue(t_msg) - #self.m_richText1.BeginBold() - #self.m_richText1.WriteText(" You are in ") - #self.m_richText1.BeginTextColour('red') - #self.m_richText1.WriteText("danger ") - #self.m_richText1.EndTextColour() - #self.m_richText1.WriteText("at that spot!") - #self.m_richText1.EndBold() - #self.m_richText1.SetValue(t_msg) - #self.m_htmlWin1.SetPage(t_msg) - - - def annring_size(pad): - # valid for oval pad/drills - annrX=(pad.GetSize()[0] - (pad.GetDrillSize()[0]+DRL_EXTRA_ius))/2 - annrY=(pad.GetSize()[1] - (pad.GetDrillSize()[1]+DRL_EXTRA_ius))/2 - #annr=min(pad.GetSize()) - max(pad.GetDrillSize()) - #if annr < MIN_AR_SIZE: - #print annrX - #print annrY - #print pad.GetSize()[0]/mm_ius - #print pad.GetSize()[0]#/mm_ius - #print pad.GetDrillSize()[0]#/mm_ius - #print DRL_EXTRA_ius - #print pad.GetDrillSize()[0]/mm_ius - #print (pad.GetDrillSize()[0]+DRL_EXTRA_ius)/mm_ius - #print annrX/mm_ius - return min(annrX,annrY) - - def annringNP_size(pad): - # valid for oval pad/drills - annrX=(pad.GetSize()[0] - (pad.GetDrillSize()[0]))/2 - annrY=(pad.GetSize()[1] - (pad.GetDrillSize()[1]))/2 - #annr=min(pad.GetSize()) - max(pad.GetDrillSize()) - #if annr < MIN_AR_SIZE: - #print annrX - #print annrY - #print pad.GetSize()[0]/mm_ius - #print pad.GetSize()[0]#/mm_ius - #print pad.GetDrillSize()[0]#/mm_ius - #print DRL_EXTRA_ius - #print pad.GetDrillSize()[0]/mm_ius - #print (pad.GetDrillSize()[0]+DRL_EXTRA_ius)/mm_ius - #print annrX/mm_ius - #return min(annrX,annrY) - return annrX,annrY - - def vias_annring_size(via): - # calculating via annular - annr=(via.GetWidth() - (via.GetDrillValue()+DRL_EXTRA_ius))/2 - #print via.GetWidth() - #print via.GetDrillValue() - return annr - - def f_mm(raw): - return repr(raw/mm_ius) - - board = pcbnew.GetBoard() - PassC=FailC=0 - PassCV=FailCV=0 - - PassCN=FailCN=0 - PassCVN=FailCVN=0 - - fileName = GetBoard().GetFileName() - if len(fileName)==0: - wx.LogMessage("a board needs to be saved/loaded!") - else: - found_violations=False - frame1 = AR_Prm(None) - #frame = wx.Frame(None) - frame1.Center() - #frame.setMsg(LogMsg) - frame1.ShowModal() - frame1.Destroy() - - frame = displayDialog(None) - LogMsg="" - writeTxt= frame.m_richText1.WriteText - rt = frame.m_richText1 - rt.BeginItalic() - writeTxt("'action_menu_annular_check.py'\n") - #frame.m_richText1.WriteText("'action_menu_annular_check.py'\n") - msg="'action_menu_annular_check.py'\n" - msg+="version = "+___version___ - writeTxt("version = "+___version___) - msg+="\nTesting PCB for Annular Rings\nTH Pads >= "+repr(AR_SET)+" Vias >= "+repr(AR_SET_V)+"\nPHD margin on PTH = "+ repr(DRL_EXTRA) - writeTxt("\nTesting PCB for Annular Rings\nTH Pads >= "+repr(AR_SET)+" Vias >= "+repr(AR_SET_V)+"\nPHD margin on PTH = "+ repr(DRL_EXTRA)) - rt.EndItalic() - writeTxt('\n\n') - #print (msg) - LogMsg+=msg+'\n\n' - - # print "LISTING VIAS:" - for item in board.GetTracks(): - if type(item) is pcbnew.VIA: - pos = item.GetPosition() - drill = item.GetDrillValue() - width = item.GetWidth() - ARv = vias_annring_size(item) - if ARv < MIN_AR_SIZE_V: - # print("AR violation at %s." % (pad.GetPosition() / mm_ius )) Raw units, needs fixing - XYpair = item.GetPosition() - msg="AR Via violation of "+f_mm(ARv)+" at XY "+f_mm(XYpair[0])+","+f_mm(XYpair[1]) - rt.BeginTextColour('red') - writeTxt("AR Via violation of "+f_mm(ARv)+" at XY "+f_mm(XYpair[0])+","+f_mm(XYpair[1])+'\n') - rt.EndTextColour() - #print (msg) - LogMsg+=msg+'\n' - FailCV = FailCV+1 - else: - PassCV = PassCV+1 - #print type(item) - - msg="VIAS that Pass = "+repr(PassCV)+"; Fails = "+repr(FailCV) - if FailCV >0: - rt.BeginBold() - writeTxt("VIAS that Pass = "+repr(PassCV)+"; ") - if FailCV >0: - rt.BeginTextColour('red') - writeTxt("Fails = "+repr(FailCV)+'\n\n') - if FailCV >0: - rt.EndTextColour() - rt.EndBold() - print(msg) - LogMsg+=msg+'\n' - - for module in board.GetModules(): - try: - module_Pads=module.PadsList() - except: - module_Pads=module.Pads() - for pad in module_Pads: #print(pad.GetAttribute()) - if pad.GetAttribute() == PAD_ATTRIB_STANDARD: #TH pad - ARv = annring_size(pad) - #print(f_mm(ARv)) - if ARv < MIN_AR_SIZE: - # print("AR violation at %s." % (pad.GetPosition() / mm_ius )) Raw units, needs fixing - XYpair = pad.GetPosition() - msg="AR PTH violation of "+f_mm(ARv)+" at XY "+f_mm(XYpair[0])+","+f_mm(XYpair[1]) - rt.BeginTextColour('red') - writeTxt("AR PTH violation of "+f_mm(ARv)+" at XY "+f_mm(XYpair[0])+","+f_mm(XYpair[1])+'\n') - rt.EndTextColour() - #print (msg) - LogMsg+=msg+'\n' - FailC = FailC+1 - else: - PassC = PassC+1 - if pad.GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED: - ARvX, ARvY = annringNP_size(pad) - #print(f_mm(ARvX));print(f_mm(ARvY)) - if (ARvX) != 0 or ARvY != 0: - ARv = min(ARvX, ARvY) - if ARv < MIN_AR_SIZE: - # print("AR violation at %s." % (pad.GetPosition() / mm_ius )) Raw units, needs fixing - XYpair = pad.GetPosition() - msg="AR NPTH warning of "+f_mm(ARv)+" at XY "+f_mm(XYpair[0])+","+f_mm(XYpair[1]) - rt.BeginTextColour('red') - writeTxt("AR NPTH warning of "+f_mm(ARv)+" at XY "+f_mm(XYpair[0])+","+f_mm(XYpair[1])+'\n') - rt.EndTextColour() - #print (msg) - LogMsg+=msg+'\n' - FailCN = FailCN+1 - else: - PassCN = PassCN+1 - else: - PassCN = PassCN+1 - - #if FailCV >0: - #writeTxt('\n') - msg = "TH PADS that Pass = "+repr(PassC)+"; Fails = "+repr(FailC) - if FailC >0: - rt.BeginBold() - writeTxt("TH PADS that Pass = "+repr(PassC)+"; ") - if FailC >0: - rt.BeginTextColour('red') - writeTxt("Fails = "+repr(FailC)+'\n') - if FailC >0: - rt.EndTextColour() - rt.EndBold() - print(msg) - LogMsg+=msg+'\n' - - msg="NPTH PADS that Pass = "+repr(PassCN)+"; Fails = "+repr(FailCN) - #writeTxt('\n') - if FailCN >0: - rt.BeginBold() - writeTxt("NPTH PADS that Pass = "+repr(PassCN)+"; ") - if FailCN >0: - rt.BeginTextColour('red') - writeTxt("Fails = "+repr(FailCN)+'\n') - if FailC >0: - rt.EndTextColour() - rt.EndBold() - print(msg) - LogMsg+=msg+'\n' - - pcbName = (os.path.splitext(GetBoard().GetFileName())[0]) #filename no ext - #wx.LogMessage(pcbName)#LogMsg) - ##wx.LogMessage(LogMsg) - FC=r"C:\FreeCAD\bin\freecad.exe" - kSU=r"C:\Cad\Progetti_K\3D-FreeCad-tools\kicad-StepUp-tools.FCMacro" - #subprocess.check_call([FC, kSU, pcbName]) - ##p = subprocess.Popen([FC, kSU, pcbName]) - - #found_violations=False - if (FailC+FailCN+FailCV)>0: - found_violations=True - - if found_violations: - #frame.m_staticTitle = wx.StaticText(frame, label=" Check result: (Violations found)") - frame.m_staticTitle.SetLabel(" Check result: (Violations found)") - #self.title.SetForegroundColour('#FF0000') - frame.m_staticTitle.SetBackgroundColour('#FF0000') - font = wx.Font(wx.DEFAULT, wx.DECORATIVE, wx.ITALIC, wx.BOLD) - frame.m_staticTitle.SetFont(font) - else: - #frame.m_staticTitle = wx.StaticText(frame, label=" Annular Check result: OK") - frame.m_staticTitle.SetLabel(" Annular Check result: OK") - frame.m_staticTitle.SetBackgroundColour('#00FF00') - font = wx.Font(wx.DEFAULT, wx.DECORATIVE, wx.ITALIC, wx.BOLD) - frame.m_staticTitle.SetFont(font) - ##frame = displayDialog(None) - #frame = wx.Frame(None) - frame.Center() - #frame.setMsg(LogMsg) - frame.ShowModal() - frame.Destroy() - #frame = wx.wxFrame(None, 10110, 'T-Make', size=wx.wxSize(100,100), - # style=wx.wxSTAY_ON_TOP) - #frame.show() - -# annular_check().register() -if __name__ == "__main__": - import argparse - - parser = argparse.ArgumentParser( - description='KiCad PCB Annular Checker') - parser.add_argument('file', type=str, help="KiCad PCB file") - args = parser.parse_args() - print("Loading %s" % args.file) - main(pcbnew.LoadBoard(args.file)) - -else: - annular_check().register() - - -# "b64_data" is a variable containing your base64 encoded jpeg -annular_ico_b64_data =\ -""" -iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAIQQAACEEB+v+u9gAAABl0RVh0U29mdHdhcmUAd3d -3Lmlua3NjYXBlLm9yZ5vuPBoAAAJ5SURBVDiNdZRNSFRRFMd/976ZJjeBn2PUoiLaiMFkFkHIQ0NBdBUlJuIqiixmIYjQRgiJYKAMXRRSi6TCD5gaWiSFsy -gixOyDpDAog8wn9SISapz33m0x82bmzcOzO/97/r973nn3XqGUojjCybIm4BhQD9Rl5XlgDnhi6ObTYo8oBIWTZeXAMNDlalW/FJYmMLd5fONA1NBN0wcKJ -8sagftAZWRJcWHS5vCiouJ3pnClUvC8VnDtpOTTDgGwCpwydHM2Bwony8LAe6koH7hjc37aQXN8XwzAv5DgUo9krF0C/ABqDN1ck9n1G0D54C2H6OTmEICt -KcXQTZvTCQegAhgFEFWzpW1A4uAHRaLfQhbPXgD+/8HfkKBhNMDXMACtEmgC6L/reCBWKGAzMmLxbQWWl2Fw0FJavqIkpYhO2m7aHADqNQcOLXq3DcSuKnp -6Ajmhry8g1tfTxGJBVzryLuepl0Bk16qiJJUHKU0qOjryEDe6u4OF6e7vitCGAohIAFk0XCWEQkp8oWmeVAAiu78EFj5vF6S2iFyBtGxJPJ72gSYmPNpyOH -McgAUJzFkazO/zetLRc4J43MayIJWCsTHLuTzkaellTa7rOVE1W9oOPDz6VjF90fI14QQ1RzhKCNsRhfpGEBqvB1jaKQDapKGbCeDBs/2C8Rb/XGTalsUQg -Fin5kKmDN185DrPAD8HzmrcbpUony0flgZXujSGT+SuSC94L20LcA8obXiduSp1H8kdiz8l8KJWEOvUeLNXuJAuQzdnPKAsrBoYAY4DaA7sWck8I1+qKex0 -Cug1dHPNFcQmD1sL0AwcACJZeQF4BcwYuvm42PMfVgD11Y9MUIEAAAAASUVORK5CYII= -""" - - -# execfile("annular.py") -# annular.py Testing PCB for Annular Ring >= 0.15 -# AR violation of 0.1 at XY 172.974,110.744 -# VIAS that Pass = 100 Fails = 1 -# AR violation of 0.1 at XY 172.212,110.744 -# AR violation of 0.0 at XY 154.813,96.52 -# PADS that Pass = 49 Fails = 2 - +# -*- coding: utf-8 -*- +# +# A script to check for annular ring violations +# both for TH pads and vias +# requirements: KiCAD pcbnew >= 4.0 +# annular.py release "1.5.1" +# +# annular.py checking PCB for Annular Ring in Vias and TH Pads +# (SMD, Connector and NPTH are skipped) +# default Annular Ring >= 0.15 both for TH Pads and Vias +# to change values modify: +# +# AR_SET = 0.150 #minimum annular accepted for pads +# AR_SET_V = 0.150 #minimum annular accepted for vias + +# annular.py + + +___version___="1.6.0" + +global mm_ius, DRL_EXTRA, AR_SET, AR_SET_V, DRL_EXTRA_ius, MIN_AR_SIZE, MIN_AR_SIZE_V, found_violations, LogMsg +#wx.LogMessage("My message") +mm_ius = 1000000.0 +# (consider always drill +0.1) +DRL_EXTRA=0.1 +DRL_EXTRA_ius=DRL_EXTRA * mm_ius + +AR_SET = 0.125 #minimum annular accepted for pads +MIN_AR_SIZE = AR_SET * mm_ius + +AR_SET_V = 0.125 #minimum annular accepted for vias +MIN_AR_SIZE_V = AR_SET_V * mm_ius + +import sys +import wx +import wx.richtext +import subprocess +import os +import pcbnew +from pcbnew import * +import base64 +from wx.lib.embeddedimage import PyEmbeddedImage +sys.path.append(os.path.dirname(__file__)) + + +class annular_check( pcbnew.ActionPlugin ): + """ + A script to check for annular ring violations + both for TH pads and vias + requirements: KiCAD pcbnew >= 4.0 + annular.py release "1.5.1" + + annular.py checking PCB for Annular Ring in Vias and TH Pads + (SMD, Connector and NPTH are skipped) + default Annular Ring >= 0.15 both for TH Pads and Vias + to change values modify: + + AR_SET = 0.150 #minimum annular accepted for pads + AR_SET_V = 0.150 #minimum annular accepted for vias + """ + + def defaults( self ): + """ + Method defaults must be redefined + self.name should be the menu label to use + self.category should be the category (not yet used) + self.description should be a comprehensive description + of the plugin + """ + self.name = "Annular check" + self.category = "Checking PCB" + self.description = "Automaticaly check annular on an existing PCB" + #self.pcbnew_icon_support = hasattr(self, "show_toolbar_button") + self.show_toolbar_button = True + self.icon_file_name = os.path.join(os.path.dirname(__file__), 'annular.png') + + def Run( self ): + + ########################################################################### + ## Class AR_Prm + ########################################################################### + + class AR_Prm ( wx.Dialog ): + + def __init__( self, parent ): + wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = "AR parameters", pos = wx.DefaultPosition, size = wx.Size( 320,193 ), style = wx.DEFAULT_DIALOG_STYLE ) + + self.SetSizeHints( 500,500 ) + + self.SetIcon(PyEmbeddedImage(annular_ico_b64_data).GetIcon()) + + bSizer1 = wx.BoxSizer( wx.VERTICAL ) + + gSizer2 = wx.GridSizer( 0, 2, 0, 0 ) + + self.m_staticText11 = wx.StaticText( self, wx.ID_ANY, u"PHD margin", wx.Point( -1,-1 ), wx.DefaultSize, 0 ) + self.m_staticText11.Wrap( -1 ) + + gSizer2.Add( self.m_staticText11, 0, wx.ALL, 5 ) + + self.m_textPHD = wx.TextCtrl( self, wx.ID_ANY, str(DRL_EXTRA), wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer2.Add( self.m_textPHD, 0, wx.ALL, 5 ) + + self.m_staticText1 = wx.StaticText( self, wx.ID_ANY, u"AR for pads", wx.Point( -1,-1 ), wx.DefaultSize, 0 ) + self.m_staticText1.Wrap( -1 ) + + gSizer2.Add( self.m_staticText1, 0, wx.ALL, 5 ) + + self.m_textAR_SET = wx.TextCtrl( self, wx.ID_ANY, str(AR_SET), wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer2.Add( self.m_textAR_SET, 0, wx.ALL, 5 ) + + self.m_staticText12 = wx.StaticText( self, wx.ID_ANY, u"AR for vias", wx.Point( -1,-1 ), wx.DefaultSize, 0 ) + self.m_staticText12.Wrap( -1 ) + + gSizer2.Add( self.m_staticText12, 0, wx.ALL, 5 ) + + self.m_textAR_SET_V = wx.TextCtrl( self, wx.ID_ANY, str(AR_SET_V), wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer2.Add( self.m_textAR_SET_V, 0, wx.ALL, 5 ) + + + bSizer1.Add( gSizer2, 1, wx.EXPAND, 5 ) + + gSizer1 = wx.GridSizer( 0, 2, 0, 0 ) + + self.m_ok_btn = wx.Button( self, wx.ID_ANY, u"OK", wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer1.Add( self.m_ok_btn, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 5 ) + + # self.m_cancel_btn = wx.Button( self, wx.ID_ANY, u"Cancel", wx.DefaultPosition, wx.DefaultSize, 0 ) + # gSizer1.Add( self.m_cancel_btn, 0, wx.ALL, 5 ) + + + bSizer1.Add( gSizer1, 1, wx.EXPAND, 5 ) + + + self.SetSizer( bSizer1 ) + self.Layout() + + self.Centre( wx.BOTH ) + + #### ----- connections + # Connect Events + self.Bind(wx.EVT_BUTTON, self.OnClickOK, self.m_ok_btn) + # self.Bind(wx.EVT_BUTTON, self.OnClickCancel, self.m_cancel_btn) + # Tooltips + #self.m_cancel_btn.SetToolTip( wx.ToolTip(u"Cancel" )) + self.m_ok_btn.SetToolTip( wx.ToolTip(u"Confirm" )) + self.m_ok_btn.SetFocus() + self.m_staticText1.SetToolTip( wx.ToolTip(u"Annular Ring for Pads (mm)" )) + self.m_textAR_SET.SetToolTip( wx.ToolTip(u"Annular Ring for Pads (mm)" )) + self.m_textAR_SET_V.SetToolTip( wx.ToolTip(u"Annular Ring for Vias (mm)" )) + self.m_staticText12.SetToolTip( wx.ToolTip(u"Annular Ring for Vias (mm)" )) + self.m_textPHD.SetToolTip( wx.ToolTip(u"Drill extra margin (mm)" )) + self.m_staticText11.SetToolTip( wx.ToolTip(u"Drill extra margin (mm)" )) + + def __del__( self ): + pass + + def OnClickOK(self, event): + global mm_ius, DRL_EXTRA, AR_SET, AR_SET_V, DRL_EXTRA_ius, MIN_AR_SIZE, MIN_AR_SIZE_V + + self.m_ok_btn.SetLabel("Clicked") + phd = float(self.m_textPHD.GetValue().replace(',','.')) + ar = float(self.m_textAR_SET.GetValue().replace(',','.')) + arv = float(self.m_textAR_SET_V.GetValue().replace(',','.')) + DRL_EXTRA=phd + DRL_EXTRA_ius=DRL_EXTRA * mm_ius + + AR_SET = ar #minimum annular accepted for pads + MIN_AR_SIZE = AR_SET * mm_ius + + AR_SET_V = arv #minimum annular accepted for vias + MIN_AR_SIZE_V = AR_SET_V * mm_ius + self.Destroy() + + def OnClickCancel(self, event): + self.m_cancel_btn.SetLabel("Clicked") + self.Destroy() + + #wx.MessageDialog(self.frame,"ciao") + #subprocess.check_call(["C:\pathToYourProgram\yourProgram.exe", "your", "arguments", "comma", "separated"]) + #http://stackoverflow.com/questions/1811691/running-an-outside-program-executable-in-python + + ## class displayDialog(wx.Dialog): + ## """ + ## The default frame + ## http://stackoverflow.com/questions/3566603/how-do-i-make-wx-textctrl-multi-line-text-update-smoothly + ## """ + ## global mm_ius, DRL_EXTRA, AR_SET, AR_SET_V, DRL_EXTRA_ius, MIN_AR_SIZE, MIN_AR_SIZE_V, found_violations + ## #---------------------------------------------------------------------- + ## #def __init__(self): + ## # """Constructor""" + ## # wx.Frame.__init__(self, None, title="Display Frame", style=wx.DEFAULT_FRAME_STYLE, wx.ICON_INFORMATION) + ## # panel = wx.Panel(self) + ## def __init__(self, parent): + ## wx.Dialog.__init__(self, parent, id=-1, title="Annular Checker")# + ## #, style=wx.DEFAULT_DIALOG_STYLE, wx.ICON_INFORMATION) + ## #, style=wx.DEFAULT_DIALOG_STYLE, wx.ICON_INFORMATION) + ## #, pos=DefaultPosition, size=DefaultSize, style = wx.DEFAULT_FRAME_STYLE & (~wx.MAXIMIZE_BOX), name="fname") + ## #, wx.ICON_INFORMATION) #, title="Annular Check", style=wx.DEFAULT_FRAME_STYLE, wx.ICON_INFORMATION) + ## # + ## + ## self.SetIcon(PyEmbeddedImage(annular_ico_b64_data).GetIcon()) + ## #wx.IconFromBitmap(wx.Bitmap("icon.ico", wx.BITMAP_TYPE_ANY))) + ## self.panel = wx.Panel(self) + ## + ## if found_violations: + ## self.title = wx.StaticText(self.panel, label="") + ## #self.title.SetForegroundColour('#FF0000') + ## #self.title.SetBackgroundColour('#FF0000') + ## #font = wx.Font(wx.DEFAULT, wx.DECORATIVE, wx.ITALIC, wx.BOLD) + ## #self.title.SetFont(font) + ## else: + ## self.title = wx.StaticText(self.panel, label="") + ## #self.title.SetBackgroundColour('#00FF00') + ## #font = wx.Font(wx.DEFAULT, wx.DECORATIVE, wx.ITALIC, wx.BOLD) + ## #self.title.SetFont(font) + ## #self.result = wx.StaticText(self.panel, label="") + ## #self.result.SetForegroundColour('#FF0000') + ## #self.button = wx.Button(self.panel, label="Save") + ## #self.lblname = wx.StaticText(self.panel, label="Your name:") + ## #self.editname = wx.TextCtrl(self.panel, size=(140, -1)) + ## ##self.editname = wx.TextCtrl(self.panel, size = (400, 400), style = wx.TE_MULTILINE|wx.TE_READONLY) + ## self.m_richText1 = wx.richtext.RichTextCtrl( self.panel, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, size = (400, 400), style = 0|wx.VSCROLL|wx.HSCROLL|wx.WANTS_CHARS )# wx.TE_MULTILINE|wx.TE_READONLY) #0|wx.VSCROLL|wx.HSCROLL|wx.NO_BORDER|wx.WANTS_CHARS ) + ## #bSizer1.Add( self.m_richText1, 1, wx.EXPAND |wx.ALL, 5 ) + ## + ## + ## # Set sizer for the frame, so we can change frame size to match widgets + ## self.windowSizer = wx.BoxSizer() + ## self.windowSizer.Add(self.panel, 1, wx.ALL | wx.EXPAND) + ## + ## # Set sizer for the panel content + ## self.sizer = wx.GridBagSizer(5, 0) + ## self.sizer.Add(self.title, (0, 0)) + ## #self.sizer.Add(self.result, (1, 0)) + ## #self.sizer.Add(self.lblname, (1, 0)) + ## ## self.sizer.Add(self.editname, (1, 0)) + ## self.sizer.Add(self.m_richText1, (1, 0)) + ## #self.ok_btn = wx.Button( self, wx.ID_ANY, u"Copy errors", wx.DefaultPosition, wx.DefaultSize, 0 ) + ## #self.sizer.Add( self.ok_btn, 0, wx.ALL | wx.EXPAND) + ## #self.sizer.Add(self.ok_btn, (2, 0)) #, wx.ALL | flag=wx.EXPAND) + ## #self.sizer.Add( self.ok_btn, 0, wx.ALL, 5 ) + ## + ## #self.sizer.Add(self.ok_btn, (2, 0), (1, 2), flag=wx.EXPAND) + ## + ## # Set simple sizer for a nice border + ## self.border = wx.BoxSizer() + ## self.border.Add(self.sizer, 1, wx.ALL | wx.EXPAND, 5) + ## + ## #self.ok_btn = wx.Button( self, wx.ID_ANY, u"Copy errors", wx.DefaultPosition, wx.DefaultSize, 0 ) + ## #self.windowSizer.Add(self.ok_btn, 0, wx.ALL) + ## #self.sizer.Add( self.ok_btn, (2,0)) + ## #self.sizer.Add( self.ok_btn, 0, wx.ALL, 5 ) + ## # Use the sizers + ## self.panel.SetSizerAndFit(self.border) + ## self.SetSizerAndFit(self.windowSizer) + ## #self.result.SetLabel(msg) + ## # Set event handlers + ## #self.button.Bind(wx.EVT_BUTTON, self.OnButton) + ## #self.Show() + ## #self.Bind(wx.EVT_CLOSE,self.OnClose) + ## + ## #def OnClose(self,e): + ## # #wx.LogMessage("c") + ## # e.Skip() + ## #self.Close() + ########################################################################### + ## Class displayDialog + ########################################################################### + + class displayDialog ( wx.Dialog ): + + global mm_ius, DRL_EXTRA, AR_SET, AR_SET_V, DRL_EXTRA_ius, MIN_AR_SIZE, MIN_AR_SIZE_V, found_violations, LogMsg + + def __init__( self, parent ): + wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = u"Annular Checker", pos = wx.DefaultPosition, size = wx.Size( 450,521 ), style = wx.DEFAULT_DIALOG_STYLE ) + + self.SetSizeHints( 300,100 ) + self.SetIcon(PyEmbeddedImage(annular_ico_b64_data).GetIcon()) + + bSizer1 = wx.BoxSizer( wx.VERTICAL ) + + bSizer2 = wx.BoxSizer( wx.VERTICAL ) + + self.m_staticTitle = wx.StaticText( self, wx.ID_ANY, u"", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticTitle.Wrap( -1 ) + + bSizer2.Add( self.m_staticTitle, 0, wx.ALL, 5 ) + + self.m_richText1 = wx.richtext.RichTextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_READONLY|wx.VSCROLL|wx.HSCROLL|wx.NO_BORDER|wx.WANTS_CHARS ) + self.m_richText1.SetMinSize( wx.Size( 400,400 ) ) + + bSizer2.Add( self.m_richText1, 1, wx.EXPAND |wx.ALL, 5 ) + + gSizer3 = wx.GridSizer( 0, 2, 0, 0 ) + + self.ok_btn = wx.Button( self, wx.ID_ANY, u"OK", wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer3.Add( self.ok_btn, 0, wx.ALL, 5 ) + + self.copy_btn = wx.Button( self, wx.ID_ANY, u"Copy Text", wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer3.Add( self.copy_btn, 0, wx.ALL, 5 ) + + + bSizer2.Add( gSizer3, 1, wx.EXPAND, 5 ) + + + bSizer1.Add( bSizer2, 1, wx.EXPAND, 5 ) + + + self.SetSizer( bSizer1 ) + self.Layout() + + self.Centre( wx.BOTH ) + + #### ----- connections + # Connect Events + self.Bind(wx.EVT_BUTTON, self.OnClickOK, self.ok_btn) + self.Bind(wx.EVT_BUTTON, self.OnClickCopy, self.copy_btn) + self.ok_btn.SetFocus() + # Tooltips + self.copy_btn.SetToolTip( wx.ToolTip(u"Copy Text to Clipboard" )) + self.ok_btn.SetToolTip( wx.ToolTip(u"Exit" )) + + def __del__( self ): + pass + + def OnClickOK(self, event): + self.Destroy() + + def OnClickCopy(self, event): + self.m_richText1.SelectAll() + self.m_richText1.Copy() + #global LogMsg + #copy2clip(LogMsg) + self.copy_btn.SetLabel("Text Copied") + + #def setMsg(self, t_msg): + # pass + #self.editname.SetValue(t_msg) + #self.m_richText1.BeginBold() + #self.m_richText1.WriteText(" You are in ") + #self.m_richText1.BeginTextColour('red') + #self.m_richText1.WriteText("danger ") + #self.m_richText1.EndTextColour() + #self.m_richText1.WriteText("at that spot!") + #self.m_richText1.EndBold() + #self.m_richText1.SetValue(t_msg) + #self.m_htmlWin1.SetPage(t_msg) + + + def annring_size(pad): + # valid for oval pad/drills + annrX=(pad.GetSize()[0] - (pad.GetDrillSize()[0]+DRL_EXTRA_ius))/2 + annrY=(pad.GetSize()[1] - (pad.GetDrillSize()[1]+DRL_EXTRA_ius))/2 + #annr=min(pad.GetSize()) - max(pad.GetDrillSize()) + #if annr < MIN_AR_SIZE: + #print annrX + #print annrY + #print pad.GetSize()[0]/mm_ius + #print pad.GetSize()[0]#/mm_ius + #print pad.GetDrillSize()[0]#/mm_ius + #print DRL_EXTRA_ius + #print pad.GetDrillSize()[0]/mm_ius + #print (pad.GetDrillSize()[0]+DRL_EXTRA_ius)/mm_ius + #print annrX/mm_ius + return min(annrX,annrY) + + def annringNP_size(pad): + # valid for oval pad/drills + annrX=(pad.GetSize()[0] - (pad.GetDrillSize()[0]))/2 + annrY=(pad.GetSize()[1] - (pad.GetDrillSize()[1]))/2 + #annr=min(pad.GetSize()) - max(pad.GetDrillSize()) + #if annr < MIN_AR_SIZE: + #print annrX + #print annrY + #print pad.GetSize()[0]/mm_ius + #print pad.GetSize()[0]#/mm_ius + #print pad.GetDrillSize()[0]#/mm_ius + #print DRL_EXTRA_ius + #print pad.GetDrillSize()[0]/mm_ius + #print (pad.GetDrillSize()[0]+DRL_EXTRA_ius)/mm_ius + #print annrX/mm_ius + #return min(annrX,annrY) + return annrX,annrY + + def vias_annring_size(via): + # calculating via annular + annr=(via.GetWidth() - (via.GetDrillValue()+DRL_EXTRA_ius))/2 + #print via.GetWidth() + #print via.GetDrillValue() + return annr + + def f_mm(raw): + return repr(raw/mm_ius) + + board = pcbnew.GetBoard() + PassC=FailC=0 + PassCV=FailCV=0 + + PassCN=FailCN=0 + PassCVN=FailCVN=0 + + fileName = GetBoard().GetFileName() + if len(fileName)==0: + wx.LogMessage("a board needs to be saved/loaded!") + else: + found_violations=False + frame1 = AR_Prm(None) + #frame = wx.Frame(None) + frame1.Center() + #frame.setMsg(LogMsg) + frame1.ShowModal() + frame1.Destroy() + + frame = displayDialog(None) + LogMsg="" + writeTxt= frame.m_richText1.WriteText + rt = frame.m_richText1 + rt.BeginItalic() + writeTxt("'action_menu_annular_check.py'\n") + #frame.m_richText1.WriteText("'action_menu_annular_check.py'\n") + msg="'action_menu_annular_check.py'\n" + msg+="version = "+___version___ + writeTxt("version = "+___version___) + msg+="\nTesting PCB for Annular Rings\nTH Pads >= "+repr(AR_SET)+" Vias >= "+repr(AR_SET_V)+"\nPHD margin on PTH = "+ repr(DRL_EXTRA) + writeTxt("\nTesting PCB for Annular Rings\nTH Pads >= "+repr(AR_SET)+" Vias >= "+repr(AR_SET_V)+"\nPHD margin on PTH = "+ repr(DRL_EXTRA)) + rt.EndItalic() + writeTxt('\n\n') + #print (msg) + LogMsg+=msg+'\n\n' + + # print "LISTING VIAS:" + for item in board.GetTracks(): + if type(item) is pcbnew.VIA: + pos = item.GetPosition() + drill = item.GetDrillValue() + width = item.GetWidth() + ARv = vias_annring_size(item) + if ARv < MIN_AR_SIZE_V: + # print("AR violation at %s." % (pad.GetPosition() / mm_ius )) Raw units, needs fixing + XYpair = item.GetPosition() + msg="AR Via violation of "+f_mm(ARv)+" at XY "+f_mm(XYpair[0])+","+f_mm(XYpair[1]) + rt.BeginTextColour('red') + writeTxt("AR Via violation of "+f_mm(ARv)+" at XY "+f_mm(XYpair[0])+","+f_mm(XYpair[1])+'\n') + rt.EndTextColour() + #print (msg) + LogMsg+=msg+'\n' + FailCV = FailCV+1 + else: + PassCV = PassCV+1 + #print type(item) + + msg="VIAS that Pass = "+repr(PassCV)+"; Fails = "+repr(FailCV) + if FailCV >0: + rt.BeginBold() + writeTxt("VIAS that Pass = "+repr(PassCV)+"; ") + if FailCV >0: + rt.BeginTextColour('red') + writeTxt("Fails = "+repr(FailCV)+'\n\n') + if FailCV >0: + rt.EndTextColour() + rt.EndBold() + print(msg) + LogMsg+=msg+'\n' + + for module in board.GetModules(): + try: + module_Pads=module.PadsList() + except: + module_Pads=module.Pads() + for pad in module_Pads: #print(pad.GetAttribute()) + if pad.GetAttribute() == PAD_ATTRIB_STANDARD: #TH pad + ARv = annring_size(pad) + #print(f_mm(ARv)) + if ARv < MIN_AR_SIZE: + # print("AR violation at %s." % (pad.GetPosition() / mm_ius )) Raw units, needs fixing + XYpair = pad.GetPosition() + msg="AR PTH violation of "+f_mm(ARv)+" at XY "+f_mm(XYpair[0])+","+f_mm(XYpair[1]) + rt.BeginTextColour('red') + writeTxt("AR PTH violation of "+f_mm(ARv)+" at XY "+f_mm(XYpair[0])+","+f_mm(XYpair[1])+'\n') + rt.EndTextColour() + #print (msg) + LogMsg+=msg+'\n' + FailC = FailC+1 + else: + PassC = PassC+1 + if pad.GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED: + ARvX, ARvY = annringNP_size(pad) + #print(f_mm(ARvX));print(f_mm(ARvY)) + if (ARvX) != 0 or ARvY != 0: + ARv = min(ARvX, ARvY) + if ARv < MIN_AR_SIZE: + # print("AR violation at %s." % (pad.GetPosition() / mm_ius )) Raw units, needs fixing + XYpair = pad.GetPosition() + msg="AR NPTH warning of "+f_mm(ARv)+" at XY "+f_mm(XYpair[0])+","+f_mm(XYpair[1]) + rt.BeginTextColour('red') + writeTxt("AR NPTH warning of "+f_mm(ARv)+" at XY "+f_mm(XYpair[0])+","+f_mm(XYpair[1])+'\n') + rt.EndTextColour() + #print (msg) + LogMsg+=msg+'\n' + FailCN = FailCN+1 + else: + PassCN = PassCN+1 + else: + PassCN = PassCN+1 + + #if FailCV >0: + #writeTxt('\n') + msg = "TH PADS that Pass = "+repr(PassC)+"; Fails = "+repr(FailC) + if FailC >0: + rt.BeginBold() + writeTxt("TH PADS that Pass = "+repr(PassC)+"; ") + if FailC >0: + rt.BeginTextColour('red') + writeTxt("Fails = "+repr(FailC)+'\n') + if FailC >0: + rt.EndTextColour() + rt.EndBold() + print(msg) + LogMsg+=msg+'\n' + + msg="NPTH PADS that Pass = "+repr(PassCN)+"; Fails = "+repr(FailCN) + #writeTxt('\n') + if FailCN >0: + rt.BeginBold() + writeTxt("NPTH PADS that Pass = "+repr(PassCN)+"; ") + if FailCN >0: + rt.BeginTextColour('red') + writeTxt("Fails = "+repr(FailCN)+'\n') + if FailC >0: + rt.EndTextColour() + rt.EndBold() + print(msg) + LogMsg+=msg+'\n' + + pcbName = (os.path.splitext(GetBoard().GetFileName())[0]) #filename no ext + #wx.LogMessage(pcbName)#LogMsg) + ##wx.LogMessage(LogMsg) + FC=r"C:\FreeCAD\bin\freecad.exe" + kSU=r"C:\Cad\Progetti_K\3D-FreeCad-tools\kicad-StepUp-tools.FCMacro" + #subprocess.check_call([FC, kSU, pcbName]) + ##p = subprocess.Popen([FC, kSU, pcbName]) + + #found_violations=False + if (FailC+FailCN+FailCV)>0: + found_violations=True + + if found_violations: + #frame.m_staticTitle = wx.StaticText(frame, label=" Check result: (Violations found)") + frame.m_staticTitle.SetLabel(" Check result: (Violations found)") + #self.title.SetForegroundColour('#FF0000') + frame.m_staticTitle.SetBackgroundColour('#FF0000') + font = wx.Font(wx.DEFAULT, wx.DECORATIVE, wx.ITALIC, wx.BOLD) + frame.m_staticTitle.SetFont(font) + else: + #frame.m_staticTitle = wx.StaticText(frame, label=" Annular Check result: OK") + frame.m_staticTitle.SetLabel(" Annular Check result: OK") + frame.m_staticTitle.SetBackgroundColour('#00FF00') + font = wx.Font(wx.DEFAULT, wx.DECORATIVE, wx.ITALIC, wx.BOLD) + frame.m_staticTitle.SetFont(font) + ##frame = displayDialog(None) + #frame = wx.Frame(None) + frame.Center() + #frame.setMsg(LogMsg) + #frame.Show(True) + frame.ShowModal() + #frame.show() + frame.Destroy() + #frame = wx.wxFrame(None, 10110, 'T-Make', size=wx.wxSize(100,100), + # style=wx.wxSTAY_ON_TOP) + #frame.show() + +# annular_check().register() +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser( + description='KiCad PCB Annular Checker') + parser.add_argument('file', type=str, help="KiCad PCB file") + args = parser.parse_args() + print("Loading %s" % args.file) + main(pcbnew.LoadBoard(args.file)) + +else: + annular_check().register() + + +# "b64_data" is a variable containing your base64 encoded jpeg +annular_ico_b64_data =\ +""" +iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAIQQAACEEB+v+u9gAAABl0RVh0U29mdHdhcmUAd3d +3Lmlua3NjYXBlLm9yZ5vuPBoAAAJ5SURBVDiNdZRNSFRRFMd/976ZJjeBn2PUoiLaiMFkFkHIQ0NBdBUlJuIqiixmIYjQRgiJYKAMXRRSi6TCD5gaWiSFsy +gixOyDpDAog8wn9SISapz33m0x82bmzcOzO/97/r973nn3XqGUojjCybIm4BhQD9Rl5XlgDnhi6ObTYo8oBIWTZeXAMNDlalW/FJYmMLd5fONA1NBN0wcKJ +8sagftAZWRJcWHS5vCiouJ3pnClUvC8VnDtpOTTDgGwCpwydHM2Bwony8LAe6koH7hjc37aQXN8XwzAv5DgUo9krF0C/ABqDN1ck9n1G0D54C2H6OTmEICt +KcXQTZvTCQegAhgFEFWzpW1A4uAHRaLfQhbPXgD+/8HfkKBhNMDXMACtEmgC6L/reCBWKGAzMmLxbQWWl2Fw0FJavqIkpYhO2m7aHADqNQcOLXq3DcSuKnp +6Ajmhry8g1tfTxGJBVzryLuepl0Bk16qiJJUHKU0qOjryEDe6u4OF6e7vitCGAohIAFk0XCWEQkp8oWmeVAAiu78EFj5vF6S2iFyBtGxJPJ72gSYmPNpyOH +McgAUJzFkazO/zetLRc4J43MayIJWCsTHLuTzkaellTa7rOVE1W9oOPDz6VjF90fI14QQ1RzhKCNsRhfpGEBqvB1jaKQDapKGbCeDBs/2C8Rb/XGTalsUQg +Fin5kKmDN185DrPAD8HzmrcbpUony0flgZXujSGT+SuSC94L20LcA8obXiduSp1H8kdiz8l8KJWEOvUeLNXuJAuQzdnPKAsrBoYAY4DaA7sWck8I1+qKex0 +Cug1dHPNFcQmD1sL0AwcACJZeQF4BcwYuvm42PMfVgD11Y9MUIEAAAAASUVORK5CYII= +""" + + +# execfile("annular.py") +# annular.py Testing PCB for Annular Ring >= 0.15 +# AR violation of 0.1 at XY 172.974,110.744 +# VIAS that Pass = 100 Fails = 1 +# AR violation of 0.1 at XY 172.212,110.744 +# AR violation of 0.0 at XY 154.813,96.52 +# PADS that Pass = 49 Fails = 2 + diff --git a/AnnularChecker/annular_checker-html.py.txt b/AnnularChecker/annular_checker-html.py.txt new file mode 100644 index 0000000..d8fa608 --- /dev/null +++ b/AnnularChecker/annular_checker-html.py.txt @@ -0,0 +1,455 @@ +# -*- coding: utf-8 -*- +# +# A script to check for annular ring violations +# both for TH pads and vias +# requirements: KiCAD pcbnew >= 4.0 +# annular.py release "1.5.1" +# +# annular.py checking PCB for Annular Ring in Vias and TH Pads +# (SMD, Connector and NPTH are skipped) +# default Annular Ring >= 0.15 both for TH Pads and Vias +# to change values modify: +# +# AR_SET = 0.150 #minimum annular accepted for pads +# AR_SET_V = 0.150 #minimum annular accepted for vias + +# annular.py + + +___version___="1.5.7" + +global mm_ius, DRL_EXTRA, AR_SET, AR_SET_V, DRL_EXTRA_ius, MIN_AR_SIZE, MIN_AR_SIZE_V, found_violations +#wx.LogMessage("My message") +mm_ius = 1000000.0 +# (consider always drill +0.1) +DRL_EXTRA=0.1 +DRL_EXTRA_ius=DRL_EXTRA * mm_ius + +AR_SET = 0.125 #minimum annular accepted for pads +MIN_AR_SIZE = AR_SET * mm_ius + +AR_SET_V = 0.125 #minimum annular accepted for vias +MIN_AR_SIZE_V = AR_SET_V * mm_ius + +import sys +import wx +import wx.html +import subprocess +import os +import pcbnew +from pcbnew import * +import base64 +from wx.lib.embeddedimage import PyEmbeddedImage +sys.path.append(os.path.dirname(__file__)) + + + +class annular_check( pcbnew.ActionPlugin ): + """ + A script to check for annular ring violations + both for TH pads and vias + requirements: KiCAD pcbnew >= 4.0 + annular.py release "1.5.1" + + annular.py checking PCB for Annular Ring in Vias and TH Pads + (SMD, Connector and NPTH are skipped) + default Annular Ring >= 0.15 both for TH Pads and Vias + to change values modify: + + AR_SET = 0.150 #minimum annular accepted for pads + AR_SET_V = 0.150 #minimum annular accepted for vias + """ + + def defaults( self ): + """ + Method defaults must be redefined + self.name should be the menu label to use + self.category should be the category (not yet used) + self.description should be a comprehensive description + of the plugin + """ + self.name = "Annular check" + self.category = "Checking PCB" + self.description = "Automaticaly check annular on an existing PCB" + + def Run( self ): + + ########################################################################### + ## Class AR_Prm + ########################################################################### + + class AR_Prm ( wx.Dialog ): + + def __init__( self, parent ): + wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = "AR parameters", pos = wx.DefaultPosition, size = wx.Size( 320,193 ), style = wx.DEFAULT_DIALOG_STYLE ) + + self.SetSizeHints( 500,500 ) + + self.SetIcon(PyEmbeddedImage(annular_ico_b64_data).GetIcon()) + + bSizer1 = wx.BoxSizer( wx.VERTICAL ) + + gSizer2 = wx.GridSizer( 0, 2, 0, 0 ) + + self.m_staticText11 = wx.StaticText( self, wx.ID_ANY, u"PHD margin", wx.Point( -1,-1 ), wx.DefaultSize, 0 ) + self.m_staticText11.Wrap( -1 ) + + gSizer2.Add( self.m_staticText11, 0, wx.ALL, 5 ) + + self.m_textPHD = wx.TextCtrl( self, wx.ID_ANY, str(DRL_EXTRA), wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer2.Add( self.m_textPHD, 0, wx.ALL, 5 ) + + self.m_staticText1 = wx.StaticText( self, wx.ID_ANY, u"AR for pads", wx.Point( -1,-1 ), wx.DefaultSize, 0 ) + self.m_staticText1.Wrap( -1 ) + + gSizer2.Add( self.m_staticText1, 0, wx.ALL, 5 ) + + self.m_textAR_SET = wx.TextCtrl( self, wx.ID_ANY, str(AR_SET), wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer2.Add( self.m_textAR_SET, 0, wx.ALL, 5 ) + + self.m_staticText12 = wx.StaticText( self, wx.ID_ANY, u"AR for vias", wx.Point( -1,-1 ), wx.DefaultSize, 0 ) + self.m_staticText12.Wrap( -1 ) + + gSizer2.Add( self.m_staticText12, 0, wx.ALL, 5 ) + + self.m_textAR_SET_V = wx.TextCtrl( self, wx.ID_ANY, str(AR_SET_V), wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer2.Add( self.m_textAR_SET_V, 0, wx.ALL, 5 ) + + + bSizer1.Add( gSizer2, 1, wx.EXPAND, 5 ) + + gSizer1 = wx.GridSizer( 0, 2, 0, 0 ) + + self.m_ok_btn = wx.Button( self, wx.ID_ANY, u"OK", wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer1.Add( self.m_ok_btn, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 5 ) + + # self.m_cancel_btn = wx.Button( self, wx.ID_ANY, u"Cancel", wx.DefaultPosition, wx.DefaultSize, 0 ) + # gSizer1.Add( self.m_cancel_btn, 0, wx.ALL, 5 ) + + + bSizer1.Add( gSizer1, 1, wx.EXPAND, 5 ) + + + self.SetSizer( bSizer1 ) + self.Layout() + + self.Centre( wx.BOTH ) + + #### ----- connections + # Connect Events + self.Bind(wx.EVT_BUTTON, self.OnClickOK, self.m_ok_btn) + # self.Bind(wx.EVT_BUTTON, self.OnClickCancel, self.m_cancel_btn) + # Tooltips + #self.m_cancel_btn.SetToolTip( wx.ToolTip(u"Cancel" )) + self.m_ok_btn.SetToolTip( wx.ToolTip(u"Confirm" )) + self.m_staticText1.SetToolTip( wx.ToolTip(u"Annular Ring for Pads (mm)" )) + self.m_textAR_SET.SetToolTip( wx.ToolTip(u"Annular Ring for Pads (mm)" )) + self.m_textAR_SET_V.SetToolTip( wx.ToolTip(u"Annular Ring for Vias (mm)" )) + self.m_staticText12.SetToolTip( wx.ToolTip(u"Annular Ring for Vias (mm)" )) + self.m_textPHD.SetToolTip( wx.ToolTip(u"Drill extra margin (mm)" )) + self.m_staticText11.SetToolTip( wx.ToolTip(u"Drill extra margin (mm)" )) + + def __del__( self ): + pass + + def OnClickOK(self, event): + global mm_ius, DRL_EXTRA, AR_SET, AR_SET_V, DRL_EXTRA_ius, MIN_AR_SIZE, MIN_AR_SIZE_V + + self.m_ok_btn.SetLabel("Clicked") + phd = float(self.m_textPHD.GetValue().replace(',','.')) + ar = float(self.m_textAR_SET.GetValue().replace(',','.')) + arv = float(self.m_textAR_SET_V.GetValue().replace(',','.')) + DRL_EXTRA=phd + DRL_EXTRA_ius=DRL_EXTRA * mm_ius + + AR_SET = ar #minimum annular accepted for pads + MIN_AR_SIZE = AR_SET * mm_ius + + AR_SET_V = arv #minimum annular accepted for vias + MIN_AR_SIZE_V = AR_SET_V * mm_ius + self.Destroy() + + def OnClickCancel(self, event): + self.m_cancel_btn.SetLabel("Clicked") + self.Destroy() + + #wx.MessageDialog(self.frame,"ciao") + #subprocess.check_call(["C:\pathToYourProgram\yourProgram.exe", "your", "arguments", "comma", "separated"]) + #http://stackoverflow.com/questions/1811691/running-an-outside-program-executable-in-python + class displayDialog(wx.Dialog): + """ + The default frame + http://stackoverflow.com/questions/3566603/how-do-i-make-wx-textctrl-multi-line-text-update-smoothly + """ + global mm_ius, DRL_EXTRA, AR_SET, AR_SET_V, DRL_EXTRA_ius, MIN_AR_SIZE, MIN_AR_SIZE_V, found_violations + #---------------------------------------------------------------------- + #def __init__(self): + # """Constructor""" + # wx.Frame.__init__(self, None, title="Display Frame", style=wx.DEFAULT_FRAME_STYLE, wx.ICON_INFORMATION) + # panel = wx.Panel(self) + def __init__(self, parent): + wx.Dialog.__init__(self, parent, id=-1, title="Annular Checker")# + #, style=wx.DEFAULT_DIALOG_STYLE, wx.ICON_INFORMATION) + #, style=wx.DEFAULT_DIALOG_STYLE, wx.ICON_INFORMATION) + #, pos=DefaultPosition, size=DefaultSize, style = wx.DEFAULT_FRAME_STYLE & (~wx.MAXIMIZE_BOX), name="fname") + #, wx.ICON_INFORMATION) #, title="Annular Check", style=wx.DEFAULT_FRAME_STYLE, wx.ICON_INFORMATION) + # + + self.SetIcon(PyEmbeddedImage(annular_ico_b64_data).GetIcon()) + #wx.IconFromBitmap(wx.Bitmap("icon.ico", wx.BITMAP_TYPE_ANY))) + self.panel = wx.Panel(self) + + if found_violations: + self.title = wx.StaticText(self.panel, label="Check result: (Violations found)") + #self.title.SetForegroundColour('#FF0000') + self.title.SetBackgroundColour('#FF0000') + font = wx.Font(wx.DEFAULT, wx.DECORATIVE, wx.ITALIC, wx.BOLD) + self.title.SetFont(font) + else: + self.title = wx.StaticText(self.panel, label="Annular Check result: OK") + self.title.SetBackgroundColour('#00FF00') + #self.result = wx.StaticText(self.panel, label="") + #self.result.SetForegroundColour('#FF0000') + #self.button = wx.Button(self.panel, label="Save") + #self.lblname = wx.StaticText(self.panel, label="Your name:") + #self.editname = wx.TextCtrl(self.panel, size=(140, -1)) + self.editname = wx.TextCtrl(self.panel, size = (400, 400), style = wx.TE_MULTILINE|wx.TE_READONLY) + self.m_htmlWin1 = wx.html.HtmlWindow( self.panel, wx.ID_ANY, wx.DefaultPosition, wx.Size( 400,400 ), wx.html.HW_SCROLLBAR_AUTO ) + #bSizer1.Add( self.m_htmlWin1, 0, wx.ALL, 5 ) + + + # Set sizer for the frame, so we can change frame size to match widgets + self.windowSizer = wx.BoxSizer() + #self.windowSizer.Add(self.m_htmlWin1, 1, wx.ALL | wx.EXPAND) + self.windowSizer.Add(self.panel, 1, wx.ALL | wx.EXPAND) + + # Set sizer for the panel content + self.sizer = wx.GridBagSizer(5, 0) + self.sizer.Add(self.title, (0, 0)) + #self.sizer.Add(self.result, (1, 0)) + #self.sizer.Add(self.lblname, (1, 0)) + ##self.sizer.Add(self.m_htmlWin1, (1, 0)) + self.sizer.Add(self.editname, (1, 0)) + #self.sizer.Add(self.button, (2, 0), (1, 2), flag=wx.EXPAND) + + # Set simple sizer for a nice border + self.border = wx.BoxSizer() + self.border.Add(self.sizer, 1, wx.ALL | wx.EXPAND, 5) + + # Use the sizers + self.panel.SetSizerAndFit(self.border) + self.SetSizerAndFit(self.windowSizer) + #self.result.SetLabel(msg) + # Set event handlers + #self.button.Bind(wx.EVT_BUTTON, self.OnButton) + #self.Show() + #self.Bind(wx.EVT_CLOSE,self.OnClose) + + #def OnClose(self,e): + # #wx.LogMessage("c") + # e.Skip() + #self.Close() + + #def OnButton(self, e): + # self.result.SetLabel(self.editname.GetValue()) + def setMsg(self, t_msg): + self.editname.SetValue(t_msg) + self.m_htmlWin1.SetPage(t_msg) + + + def annring_size(pad): + # valid for oval pad/drills + annrX=(pad.GetSize()[0] - (pad.GetDrillSize()[0]+DRL_EXTRA_ius))/2 + annrY=(pad.GetSize()[1] - (pad.GetDrillSize()[1]+DRL_EXTRA_ius))/2 + #annr=min(pad.GetSize()) - max(pad.GetDrillSize()) + #if annr < MIN_AR_SIZE: + #print annrX + #print annrY + #print pad.GetSize()[0]/mm_ius + #print pad.GetSize()[0]#/mm_ius + #print pad.GetDrillSize()[0]#/mm_ius + #print DRL_EXTRA_ius + #print pad.GetDrillSize()[0]/mm_ius + #print (pad.GetDrillSize()[0]+DRL_EXTRA_ius)/mm_ius + #print annrX/mm_ius + return min(annrX,annrY) + + def annringNP_size(pad): + # valid for oval pad/drills + annrX=(pad.GetSize()[0] - (pad.GetDrillSize()[0]))/2 + annrY=(pad.GetSize()[1] - (pad.GetDrillSize()[1]))/2 + #annr=min(pad.GetSize()) - max(pad.GetDrillSize()) + #if annr < MIN_AR_SIZE: + #print annrX + #print annrY + #print pad.GetSize()[0]/mm_ius + #print pad.GetSize()[0]#/mm_ius + #print pad.GetDrillSize()[0]#/mm_ius + #print DRL_EXTRA_ius + #print pad.GetDrillSize()[0]/mm_ius + #print (pad.GetDrillSize()[0]+DRL_EXTRA_ius)/mm_ius + #print annrX/mm_ius + #return min(annrX,annrY) + return annrX,annrY + + def vias_annring_size(via): + # calculating via annular + annr=(via.GetWidth() - (via.GetDrillValue()+DRL_EXTRA_ius))/2 + #print via.GetWidth() + #print via.GetDrillValue() + return annr + + def f_mm(raw): + return repr(raw/mm_ius) + + board = pcbnew.GetBoard() + PassC=FailC=0 + PassCV=FailCV=0 + + PassCN=FailCN=0 + PassCVN=FailCVN=0 + + fileName = GetBoard().GetFileName() + if len(fileName)==0: + wx.LogMessage("a board needs to be saved/loaded!") + else: + + frame = AR_Prm(None) + #frame = wx.Frame(None) + frame.Center() + #frame.setMsg(LogMsg) + frame.ShowModal() + frame.Destroy() + + LogMsg="Hello, world!
" + msg="action_menu_annular_check.py
" + msg+="version = "+___version___ + msg+="
Testing PCB for Annular Rings
TH Pads = "+repr(AR_SET)+" Vias = "+repr(AR_SET_V)+"
PHD margin on PTH = "+ repr(DRL_EXTRA) + #print (msg) + LogMsg+=msg+"

" + + # print "LISTING VIAS:" + for item in board.GetTracks(): + if type(item) is pcbnew.VIA: + pos = item.GetPosition() + drill = item.GetDrillValue() + width = item.GetWidth() + ARv = vias_annring_size(item) + if ARv < MIN_AR_SIZE_V: + # print("AR violation at %s." % (pad.GetPosition() / mm_ius )) Raw units, needs fixing + XYpair = item.GetPosition() + msg="AR Via violation of "+f_mm(ARv)+" at XY "+f_mm(XYpair[0])+","+f_mm(XYpair[1]) + #print (msg) + LogMsg+=msg+"
" + FailCV = FailCV+1 + else: + PassCV = PassCV+1 + #print type(item) + + msg="VIAS that Pass = "+repr(PassCV)+"; Fails = "+repr(FailCV) + # print(msg) + LogMsg+=msg+"
" + + for module in board.GetModules(): + try: + module_Pads=module.PadsList() + except: + module_Pads=module.Pads() + for pad in module_Pads: #print(pad.GetAttribute()) + if pad.GetAttribute() == PAD_ATTRIB_STANDARD: #TH pad + ARv = annring_size(pad) + #print(f_mm(ARv)) + if ARv < MIN_AR_SIZE: + # print("AR violation at %s." % (pad.GetPosition() / mm_ius )) Raw units, needs fixing + XYpair = pad.GetPosition() + msg="AR PTH violation of "+f_mm(ARv)+" at XY "+f_mm(XYpair[0])+","+f_mm(XYpair[1]) + #print (msg) + LogMsg+=msg+"
" + FailC = FailC+1 + else: + PassC = PassC+1 + if pad.GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED: + ARvX, ARvY = annringNP_size(pad) + #print(f_mm(ARvX));print(f_mm(ARvY)) + if (ARvX) != 0 or ARvY != 0: + ARv = min(ARvX, ARvY) + if ARv < MIN_AR_SIZE: + # print("AR violation at %s." % (pad.GetPosition() / mm_ius )) Raw units, needs fixing + XYpair = pad.GetPosition() + msg="AR NPTH warning of "+f_mm(ARv)+" at XY "+f_mm(XYpair[0])+","+f_mm(XYpair[1]) + #print (msg) + LogMsg+=msg+"
" + FailCN = FailCN+1 + else: + PassCN = PassCN+1 + else: + PassCN = PassCN+1 + + msg = "TH PADS that Pass = "+repr(PassC)+"; Fails = "+repr(FailC) + print(msg) + LogMsg+=msg+"
" + + msg="NPTH PADS that Pass = "+repr(PassCN)+"; Fails = "+repr(FailCN) + print(msg) + LogMsg+=msg+"" + + pcbName = (os.path.splitext(GetBoard().GetFileName())[0]) #filename no ext + #wx.LogMessage(pcbName)#LogMsg) + ##wx.LogMessage(LogMsg) + FC=r"C:\FreeCAD\bin\freecad.exe" + #kSU=r"C:\Cad\Progetti_K\3D-FreeCad-tools\kicad-StepUp-tools.FCMacro" + #subprocess.check_call([FC, kSU, pcbName]) + ##p = subprocess.Popen([FC, kSU, pcbName]) + + found_violations=False + if (FailC+FailCN+FailCV)>0: + found_violations=True + + frame = displayDialog(None) + #frame = wx.Frame(None) + frame.Center() + frame.setMsg(LogMsg) + frame.ShowModal() + frame.Destroy() + #frame = wx.wxFrame(None, 10110, 'T-Make', size=wx.wxSize(100,100), + # style=wx.wxSTAY_ON_TOP) + #frame.show() + +# annular_check().register() +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser( + description='KiCad PCB Annular Checker') + parser.add_argument('file', type=str, help="KiCad PCB file") + args = parser.parse_args() + print("Loading %s" % args.file) + main(pcbnew.LoadBoard(args.file)) + +else: + annular_check().register() + + +# "b64_data" is a variable containing your base64 encoded jpeg +annular_ico_b64_data =\ +""" +iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAIQQAACEEB+v+u9gAAABl0RVh0U29mdHdhcmUAd3d +3Lmlua3NjYXBlLm9yZ5vuPBoAAAJ5SURBVDiNdZRNSFRRFMd/976ZJjeBn2PUoiLaiMFkFkHIQ0NBdBUlJuIqiixmIYjQRgiJYKAMXRRSi6TCD5gaWiSFsy +gixOyDpDAog8wn9SISapz33m0x82bmzcOzO/97/r973nn3XqGUojjCybIm4BhQD9Rl5XlgDnhi6ObTYo8oBIWTZeXAMNDlalW/FJYmMLd5fONA1NBN0wcKJ +8sagftAZWRJcWHS5vCiouJ3pnClUvC8VnDtpOTTDgGwCpwydHM2Bwony8LAe6koH7hjc37aQXN8XwzAv5DgUo9krF0C/ABqDN1ck9n1G0D54C2H6OTmEICt +KcXQTZvTCQegAhgFEFWzpW1A4uAHRaLfQhbPXgD+/8HfkKBhNMDXMACtEmgC6L/reCBWKGAzMmLxbQWWl2Fw0FJavqIkpYhO2m7aHADqNQcOLXq3DcSuKnp +6Ajmhry8g1tfTxGJBVzryLuepl0Bk16qiJJUHKU0qOjryEDe6u4OF6e7vitCGAohIAFk0XCWEQkp8oWmeVAAiu78EFj5vF6S2iFyBtGxJPJ72gSYmPNpyOH +McgAUJzFkazO/zetLRc4J43MayIJWCsTHLuTzkaellTa7rOVE1W9oOPDz6VjF90fI14QQ1RzhKCNsRhfpGEBqvB1jaKQDapKGbCeDBs/2C8Rb/XGTalsUQg +Fin5kKmDN185DrPAD8HzmrcbpUony0flgZXujSGT+SuSC94L20LcA8obXiduSp1H8kdiz8l8KJWEOvUeLNXuJAuQzdnPKAsrBoYAY4DaA7sWck8I1+qKex0 +Cug1dHPNFcQmD1sL0AwcACJZeQF4BcwYuvm42PMfVgD11Y9MUIEAAAAASUVORK5CYII= +""" + + +# execfile("annular.py") +# annular.py Testing PCB for Annular Ring >= 0.15 +# AR violation of 0.1 at XY 172.974,110.744 +# VIAS that Pass = 100 Fails = 1 +# AR violation of 0.1 at XY 172.212,110.744 +# AR violation of 0.0 at XY 154.813,96.52 +# PADS that Pass = 49 Fails = 2 + diff --git a/AnnularChecker/annular_checker.py b/AnnularChecker/annular_checker.py new file mode 100644 index 0000000..f123959 --- /dev/null +++ b/AnnularChecker/annular_checker.py @@ -0,0 +1,692 @@ +# -*- coding: utf-8 -*- +# +# A script to check for annular ring violations +# both for TH pads and vias +# requirements: KiCAD pcbnew >= 4.0 +# annular.py release "1.5.1" +# +# annular.py checking PCB for Annular Ring in Vias and TH Pads +# (SMD, Connector and NPTH are skipped) +# default Annular Ring >= 0.15 both for TH Pads and Vias +# to change values modify: +# +# AR_SET = 0.150 #minimum annular accepted for pads +# AR_SET_V = 0.150 #minimum annular accepted for vias + +# annular.py + + +___version___="AC version: 1.6.0" + +global mm_ius, DRL_EXTRA, AR_SET, AR_SET_V, DRL_EXTRA_ius, MIN_AR_SIZE, MIN_AR_SIZE_V, found_violations, LogMsg +#wx.LogMessage("My message") +mm_ius = 1000000.0 +# (consider always drill +0.1) +DRL_EXTRA=0.1 +DRL_EXTRA_ius=DRL_EXTRA * mm_ius + +AR_SET = 0.125 #minimum annular accepted for pads +MIN_AR_SIZE = AR_SET * mm_ius + +AR_SET_V = 0.125 #minimum annular accepted for vias +MIN_AR_SIZE_V = AR_SET_V * mm_ius + +import sys +import wx +import wx.richtext +import subprocess +import os +import pcbnew +from pcbnew import * +import base64 +from wx.lib.embeddedimage import PyEmbeddedImage +sys.path.append(os.path.dirname(__file__)) + + +class annular_check( pcbnew.ActionPlugin ): + """ + A script to check for annular ring violations + both for TH pads and vias + requirements: KiCAD pcbnew >= 4.0 + annular.py release "1.5.1" + + annular.py checking PCB for Annular Ring in Vias and TH Pads + (SMD, Connector and NPTH are skipped) + default Annular Ring >= 0.15 both for TH Pads and Vias + to change values modify: + + AR_SET = 0.150 #minimum annular accepted for pads + AR_SET_V = 0.150 #minimum annular accepted for vias + """ + + def defaults( self ): + """ + Method defaults must be redefined + self.name should be the menu label to use + self.category should be the category (not yet used) + self.description should be a comprehensive description + of the plugin + """ + self.name = "Annular check" + self.category = "Checking PCB" + self.description = "Automaticaly check annular on an existing PCB" + #self.pcbnew_icon_support = hasattr(self, "show_toolbar_button") + self.show_toolbar_button = True + self.icon_file_name = os.path.join(os.path.dirname(__file__), 'annular.png') + + def Run( self ): + + ########################################################################### + ## Class AR_Prm + ########################################################################### + + #class AR_Prm ( wx.Dialog ): + # + # def __init__( self, parent ): + # wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = "AR parameters", pos = wx.DefaultPosition, size = wx.Size( 320,193 ), style = wx.DEFAULT_DIALOG_STYLE ) + # + # self.SetSizeHints( 500,500 ) + # + # self.SetIcon(PyEmbeddedImage(annular_ico_b64_data).GetIcon()) + # + # bSizer1 = wx.BoxSizer( wx.VERTICAL ) + # + # gSizer2 = wx.GridSizer( 0, 2, 0, 0 ) + # + # self.m_staticText11 = wx.StaticText( self, wx.ID_ANY, u"PHD margin", wx.Point( -1,-1 ), wx.DefaultSize, 0 ) + # self.m_staticText11.Wrap( -1 ) + # + # gSizer2.Add( self.m_staticText11, 0, wx.ALL, 5 ) + # + # self.m_textPHD = wx.TextCtrl( self, wx.ID_ANY, str(DRL_EXTRA), wx.DefaultPosition, wx.DefaultSize, 0 ) + # gSizer2.Add( self.m_textPHD, 0, wx.ALL, 5 ) + # + # self.m_staticText1 = wx.StaticText( self, wx.ID_ANY, u"AR for pads", wx.Point( -1,-1 ), wx.DefaultSize, 0 ) + # self.m_staticText1.Wrap( -1 ) + # + # gSizer2.Add( self.m_staticText1, 0, wx.ALL, 5 ) + # + # self.m_textAR_SET = wx.TextCtrl( self, wx.ID_ANY, str(AR_SET), wx.DefaultPosition, wx.DefaultSize, 0 ) + # gSizer2.Add( self.m_textAR_SET, 0, wx.ALL, 5 ) + # + # self.m_staticText12 = wx.StaticText( self, wx.ID_ANY, u"AR for vias", wx.Point( -1,-1 ), wx.DefaultSize, 0 ) + # self.m_staticText12.Wrap( -1 ) + # + # gSizer2.Add( self.m_staticText12, 0, wx.ALL, 5 ) + # + # self.m_textAR_SET_V = wx.TextCtrl( self, wx.ID_ANY, str(AR_SET_V), wx.DefaultPosition, wx.DefaultSize, 0 ) + # gSizer2.Add( self.m_textAR_SET_V, 0, wx.ALL, 5 ) + # + # + # bSizer1.Add( gSizer2, 1, wx.EXPAND, 5 ) + # + # gSizer1 = wx.GridSizer( 0, 2, 0, 0 ) + # + # self.m_ok_btn = wx.Button( self, wx.ID_ANY, u"OK", wx.DefaultPosition, wx.DefaultSize, 0 ) + # gSizer1.Add( self.m_ok_btn, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 5 ) + # + # # self.m_cancel_btn = wx.Button( self, wx.ID_ANY, u"Cancel", wx.DefaultPosition, wx.DefaultSize, 0 ) + # # gSizer1.Add( self.m_cancel_btn, 0, wx.ALL, 5 ) + # + # + # bSizer1.Add( gSizer1, 1, wx.EXPAND, 5 ) + # + # + # self.SetSizer( bSizer1 ) + # self.Layout() + # + # self.Centre( wx.BOTH ) + # + # #### ----- connections + # # Connect Events + # self.Bind(wx.EVT_BUTTON, self.OnClickOK, self.m_ok_btn) + # # self.Bind(wx.EVT_BUTTON, self.OnClickCancel, self.m_cancel_btn) + # # Tooltips + # #self.m_cancel_btn.SetToolTip( wx.ToolTip(u"Cancel" )) + # self.m_ok_btn.SetToolTip( wx.ToolTip(u"Confirm" )) + # self.m_ok_btn.SetFocus() + # self.m_staticText1.SetToolTip( wx.ToolTip(u"Annular Ring for Pads (mm)" )) + # self.m_textAR_SET.SetToolTip( wx.ToolTip(u"Annular Ring for Pads (mm)" )) + # self.m_textAR_SET_V.SetToolTip( wx.ToolTip(u"Annular Ring for Vias (mm)" )) + # self.m_staticText12.SetToolTip( wx.ToolTip(u"Annular Ring for Vias (mm)" )) + # self.m_textPHD.SetToolTip( wx.ToolTip(u"Drill extra margin (mm)" )) + # self.m_staticText11.SetToolTip( wx.ToolTip(u"Drill extra margin (mm)" )) + class AR_Prm ( wx.Dialog ): + + def __init__( self, parent ): + wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = u"Annular Checker", pos = wx.DefaultPosition, size = wx.Size( 320,229 ), style = wx.DEFAULT_DIALOG_STYLE ) + + self.SetSizeHints( 320, 320 ) + + bSizer1 = wx.BoxSizer( wx.VERTICAL ) + + gSizer2 = wx.GridSizer( 0, 2, 0, 0 ) + + self.m_StaticTextPHD = wx.StaticText( self, wx.ID_ANY, u"PHD margin", wx.Point( -1,-1 ), wx.DefaultSize, 0 ) + self.m_StaticTextPHD.Wrap( -1 ) + + gSizer2.Add( self.m_StaticTextPHD, 0, wx.ALL, 5 ) + + self.m_textPHD = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer2.Add( self.m_textPHD, 0, wx.ALL, 5 ) + + self.m_StaticTextAR_SET = wx.StaticText( self, wx.ID_ANY, u"AR for pads", wx.Point( -1,-1 ), wx.DefaultSize, 0 ) + self.m_StaticTextAR_SET.Wrap( -1 ) + + gSizer2.Add( self.m_StaticTextAR_SET, 0, wx.ALL, 5 ) + + self.m_textAR_SET = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer2.Add( self.m_textAR_SET, 0, wx.ALL, 5 ) + + self.m_StaticTextAR_SET_V = wx.StaticText( self, wx.ID_ANY, u"AR for vias", wx.Point( -1,-1 ), wx.DefaultSize, 0 ) + self.m_StaticTextAR_SET_V.Wrap( -1 ) + + gSizer2.Add( self.m_StaticTextAR_SET_V, 0, wx.ALL, 5 ) + + self.m_textAR_SET_V = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer2.Add( self.m_textAR_SET_V, 0, wx.ALL, 5 ) + + bSizer2 = wx.BoxSizer( wx.VERTICAL ) + + self.m_staticTextVersion = wx.StaticText( self, wx.ID_ANY, u"Version", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticTextVersion.Wrap( -1 ) + + bSizer2.Add( self.m_staticTextVersion, 0, wx.ALL, 5 ) + + + gSizer2.Add( bSizer2, 1, wx.EXPAND, 5 ) + + + bSizer1.Add( gSizer2, 1, wx.EXPAND, 5 ) + + gSizer1 = wx.GridSizer( 0, 2, 0, 0 ) + + self.m_ok_btn = wx.Button( self, wx.ID_ANY, u"OK", wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer1.Add( self.m_ok_btn, 0, wx.ALL, 5 ) + + #self.m_cancel_btn = wx.Button( self, wx.ID_ANY, u"Cancel", wx.DefaultPosition, wx.DefaultSize, 0 ) + #gSizer1.Add( self.m_cancel_btn, 0, wx.ALL, 5 ) + + + bSizer1.Add( gSizer1, 1, wx.EXPAND, 5 ) + + + self.SetSizer( bSizer1 ) + self.Layout() + + self.Centre( wx.BOTH ) + + #### ----- connections + # Connect Events + self.Bind(wx.EVT_BUTTON, self.OnClickOK, self.m_ok_btn) + # self.Bind(wx.EVT_BUTTON, self.OnClickCancel, self.m_cancel_btn) + # Tooltips + #self.m_cancel_btn.SetToolTip( wx.ToolTip(u"Cancel" )) + self.m_ok_btn.SetToolTip( wx.ToolTip(u"Confirm" )) + self.m_ok_btn.SetFocus() + #self.m_staticText1.SetToolTip( wx.ToolTip(u"Annular Ring for Pads (mm)" )) + self.m_textAR_SET.SetValue(str(AR_SET)) + self.m_textAR_SET.SetToolTip( wx.ToolTip(u"Annular Ring for Pads (mm)" )) + self.m_textAR_SET_V.SetValue(str(AR_SET_V)) + self.m_textAR_SET_V.SetToolTip( wx.ToolTip(u"Annular Ring for Vias (mm)" )) + #self.m_staticText12.SetToolTip( wx.ToolTip(u"Annular Ring for Vias (mm)" )) + self.m_staticTextVersion.SetLabel(___version___) + self.m_textPHD.SetValue(str(DRL_EXTRA)) + self.m_textPHD.SetToolTip( wx.ToolTip(u"Drill extra margin (mm)" )) + #self.m_staticText11.SetToolTip( wx.ToolTip(u"Drill extra margin (mm)" )) + + def __del__( self ): + pass + + def OnClickOK(self, event): + global mm_ius, DRL_EXTRA, AR_SET, AR_SET_V, DRL_EXTRA_ius, MIN_AR_SIZE, MIN_AR_SIZE_V + + self.m_ok_btn.SetLabel("Clicked") + phd = float(self.m_textPHD.GetValue().replace(',','.')) + ar = float(self.m_textAR_SET.GetValue().replace(',','.')) + arv = float(self.m_textAR_SET_V.GetValue().replace(',','.')) + DRL_EXTRA=phd + DRL_EXTRA_ius=DRL_EXTRA * mm_ius + + AR_SET = ar #minimum annular accepted for pads + MIN_AR_SIZE = AR_SET * mm_ius + + AR_SET_V = arv #minimum annular accepted for vias + MIN_AR_SIZE_V = AR_SET_V * mm_ius + self.Destroy() + + def OnClickCancel(self, event): + self.m_cancel_btn.SetLabel("Clicked") + self.Destroy() + + #wx.MessageDialog(self.frame,"ciao") + #subprocess.check_call(["C:\pathToYourProgram\yourProgram.exe", "your", "arguments", "comma", "separated"]) + #http://stackoverflow.com/questions/1811691/running-an-outside-program-executable-in-python + + ## class displayDialog(wx.Dialog): + ## """ + ## The default frame + ## http://stackoverflow.com/questions/3566603/how-do-i-make-wx-textctrl-multi-line-text-update-smoothly + ## """ + ## global mm_ius, DRL_EXTRA, AR_SET, AR_SET_V, DRL_EXTRA_ius, MIN_AR_SIZE, MIN_AR_SIZE_V, found_violations + ## #---------------------------------------------------------------------- + ## #def __init__(self): + ## # """Constructor""" + ## # wx.Frame.__init__(self, None, title="Display Frame", style=wx.DEFAULT_FRAME_STYLE, wx.ICON_INFORMATION) + ## # panel = wx.Panel(self) + ## def __init__(self, parent): + ## wx.Dialog.__init__(self, parent, id=-1, title="Annular Checker")# + ## #, style=wx.DEFAULT_DIALOG_STYLE, wx.ICON_INFORMATION) + ## #, style=wx.DEFAULT_DIALOG_STYLE, wx.ICON_INFORMATION) + ## #, pos=DefaultPosition, size=DefaultSize, style = wx.DEFAULT_FRAME_STYLE & (~wx.MAXIMIZE_BOX), name="fname") + ## #, wx.ICON_INFORMATION) #, title="Annular Check", style=wx.DEFAULT_FRAME_STYLE, wx.ICON_INFORMATION) + ## # + ## + ## self.SetIcon(PyEmbeddedImage(annular_ico_b64_data).GetIcon()) + ## #wx.IconFromBitmap(wx.Bitmap("icon.ico", wx.BITMAP_TYPE_ANY))) + ## self.panel = wx.Panel(self) + ## + ## if found_violations: + ## self.title = wx.StaticText(self.panel, label="") + ## #self.title.SetForegroundColour('#FF0000') + ## #self.title.SetBackgroundColour('#FF0000') + ## #font = wx.Font(wx.DEFAULT, wx.DECORATIVE, wx.ITALIC, wx.BOLD) + ## #self.title.SetFont(font) + ## else: + ## self.title = wx.StaticText(self.panel, label="") + ## #self.title.SetBackgroundColour('#00FF00') + ## #font = wx.Font(wx.DEFAULT, wx.DECORATIVE, wx.ITALIC, wx.BOLD) + ## #self.title.SetFont(font) + ## #self.result = wx.StaticText(self.panel, label="") + ## #self.result.SetForegroundColour('#FF0000') + ## #self.button = wx.Button(self.panel, label="Save") + ## #self.lblname = wx.StaticText(self.panel, label="Your name:") + ## #self.editname = wx.TextCtrl(self.panel, size=(140, -1)) + ## ##self.editname = wx.TextCtrl(self.panel, size = (400, 400), style = wx.TE_MULTILINE|wx.TE_READONLY) + ## self.m_richText1 = wx.richtext.RichTextCtrl( self.panel, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, size = (400, 400), style = 0|wx.VSCROLL|wx.HSCROLL|wx.WANTS_CHARS )# wx.TE_MULTILINE|wx.TE_READONLY) #0|wx.VSCROLL|wx.HSCROLL|wx.NO_BORDER|wx.WANTS_CHARS ) + ## #bSizer1.Add( self.m_richText1, 1, wx.EXPAND |wx.ALL, 5 ) + ## + ## + ## # Set sizer for the frame, so we can change frame size to match widgets + ## self.windowSizer = wx.BoxSizer() + ## self.windowSizer.Add(self.panel, 1, wx.ALL | wx.EXPAND) + ## + ## # Set sizer for the panel content + ## self.sizer = wx.GridBagSizer(5, 0) + ## self.sizer.Add(self.title, (0, 0)) + ## #self.sizer.Add(self.result, (1, 0)) + ## #self.sizer.Add(self.lblname, (1, 0)) + ## ## self.sizer.Add(self.editname, (1, 0)) + ## self.sizer.Add(self.m_richText1, (1, 0)) + ## #self.ok_btn = wx.Button( self, wx.ID_ANY, u"Copy errors", wx.DefaultPosition, wx.DefaultSize, 0 ) + ## #self.sizer.Add( self.ok_btn, 0, wx.ALL | wx.EXPAND) + ## #self.sizer.Add(self.ok_btn, (2, 0)) #, wx.ALL | flag=wx.EXPAND) + ## #self.sizer.Add( self.ok_btn, 0, wx.ALL, 5 ) + ## + ## #self.sizer.Add(self.ok_btn, (2, 0), (1, 2), flag=wx.EXPAND) + ## + ## # Set simple sizer for a nice border + ## self.border = wx.BoxSizer() + ## self.border.Add(self.sizer, 1, wx.ALL | wx.EXPAND, 5) + ## + ## #self.ok_btn = wx.Button( self, wx.ID_ANY, u"Copy errors", wx.DefaultPosition, wx.DefaultSize, 0 ) + ## #self.windowSizer.Add(self.ok_btn, 0, wx.ALL) + ## #self.sizer.Add( self.ok_btn, (2,0)) + ## #self.sizer.Add( self.ok_btn, 0, wx.ALL, 5 ) + ## # Use the sizers + ## self.panel.SetSizerAndFit(self.border) + ## self.SetSizerAndFit(self.windowSizer) + ## #self.result.SetLabel(msg) + ## # Set event handlers + ## #self.button.Bind(wx.EVT_BUTTON, self.OnButton) + ## #self.Show() + ## #self.Bind(wx.EVT_CLOSE,self.OnClose) + ## + ## #def OnClose(self,e): + ## # #wx.LogMessage("c") + ## # e.Skip() + ## #self.Close() + ########################################################################### + ## Class displayDialog + ########################################################################### + + class displayDialog ( wx.Dialog ): + + global mm_ius, DRL_EXTRA, AR_SET, AR_SET_V, DRL_EXTRA_ius, MIN_AR_SIZE, MIN_AR_SIZE_V, found_violations, LogMsg + + def __init__( self, parent ): + wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = u"Annular Checker", pos = wx.DefaultPosition, size = wx.Size( 450,521 ), style = wx.DEFAULT_DIALOG_STYLE ) + + self.SetSizeHints( 300,100 ) + self.SetIcon(PyEmbeddedImage(annular_ico_b64_data).GetIcon()) + + bSizer1 = wx.BoxSizer( wx.VERTICAL ) + + bSizer2 = wx.BoxSizer( wx.VERTICAL ) + + self.m_staticTitle = wx.StaticText( self, wx.ID_ANY, u"", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticTitle.Wrap( -1 ) + + bSizer2.Add( self.m_staticTitle, 0, wx.ALL, 5 ) + + self.m_richText1 = wx.richtext.RichTextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_READONLY|wx.VSCROLL|wx.HSCROLL|wx.NO_BORDER|wx.WANTS_CHARS ) + self.m_richText1.SetMinSize( wx.Size( 400,400 ) ) + + bSizer2.Add( self.m_richText1, 1, wx.EXPAND |wx.ALL, 5 ) + + gSizer3 = wx.GridSizer( 0, 2, 0, 0 ) + + self.ok_btn = wx.Button( self, wx.ID_ANY, u"OK", wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer3.Add( self.ok_btn, 0, wx.ALL, 5 ) + + self.copy_btn = wx.Button( self, wx.ID_ANY, u"Copy Text", wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer3.Add( self.copy_btn, 0, wx.ALL, 5 ) + + + bSizer2.Add( gSizer3, 1, wx.EXPAND, 5 ) + + + bSizer1.Add( bSizer2, 1, wx.EXPAND, 5 ) + + + self.SetSizer( bSizer1 ) + self.Layout() + + self.Centre( wx.BOTH ) + + #### ----- connections + # Connect Events + self.Bind(wx.EVT_BUTTON, self.OnClickOK, self.ok_btn) + self.Bind(wx.EVT_BUTTON, self.OnClickCopy, self.copy_btn) + self.ok_btn.SetFocus() + # Tooltips + self.copy_btn.SetToolTip( wx.ToolTip(u"Copy Text to Clipboard" )) + self.ok_btn.SetToolTip( wx.ToolTip(u"Exit" )) + + def __del__( self ): + pass + + def OnClickOK(self, event): + self.Destroy() + + def OnClickCopy(self, event): + self.m_richText1.SelectAll() + self.m_richText1.Copy() + #global LogMsg + #copy2clip(LogMsg) + self.copy_btn.SetLabel("Text Copied") + + #def setMsg(self, t_msg): + # pass + #self.editname.SetValue(t_msg) + #self.m_richText1.BeginBold() + #self.m_richText1.WriteText(" You are in ") + #self.m_richText1.BeginTextColour('red') + #self.m_richText1.WriteText("danger ") + #self.m_richText1.EndTextColour() + #self.m_richText1.WriteText("at that spot!") + #self.m_richText1.EndBold() + #self.m_richText1.SetValue(t_msg) + #self.m_htmlWin1.SetPage(t_msg) + + + def annring_size(pad): + # valid for oval pad/drills + annrX=(pad.GetSize()[0] - (pad.GetDrillSize()[0]+DRL_EXTRA_ius))/2 + annrY=(pad.GetSize()[1] - (pad.GetDrillSize()[1]+DRL_EXTRA_ius))/2 + #annr=min(pad.GetSize()) - max(pad.GetDrillSize()) + #if annr < MIN_AR_SIZE: + #print annrX + #print annrY + #print pad.GetSize()[0]/mm_ius + #print pad.GetSize()[0]#/mm_ius + #print pad.GetDrillSize()[0]#/mm_ius + #print DRL_EXTRA_ius + #print pad.GetDrillSize()[0]/mm_ius + #print (pad.GetDrillSize()[0]+DRL_EXTRA_ius)/mm_ius + #print annrX/mm_ius + return min(annrX,annrY) + + def annringNP_size(pad): + # valid for oval pad/drills + annrX=(pad.GetSize()[0] - (pad.GetDrillSize()[0]))/2 + annrY=(pad.GetSize()[1] - (pad.GetDrillSize()[1]))/2 + #annr=min(pad.GetSize()) - max(pad.GetDrillSize()) + #if annr < MIN_AR_SIZE: + #print annrX + #print annrY + #print pad.GetSize()[0]/mm_ius + #print pad.GetSize()[0]#/mm_ius + #print pad.GetDrillSize()[0]#/mm_ius + #print DRL_EXTRA_ius + #print pad.GetDrillSize()[0]/mm_ius + #print (pad.GetDrillSize()[0]+DRL_EXTRA_ius)/mm_ius + #print annrX/mm_ius + #return min(annrX,annrY) + return annrX,annrY + + def vias_annring_size(via): + # calculating via annular + annr=(via.GetWidth() - (via.GetDrillValue()+DRL_EXTRA_ius))/2 + #print via.GetWidth() + #print via.GetDrillValue() + return annr + + def f_mm(raw): + return repr(raw/mm_ius) + + board = pcbnew.GetBoard() + PassC=FailC=0 + PassCV=FailCV=0 + + PassCN=FailCN=0 + PassCVN=FailCVN=0 + + fileName = GetBoard().GetFileName() + if len(fileName)==0: + wx.LogMessage("a board needs to be saved/loaded!") + else: + found_violations=False + frame1 = AR_Prm(None) + #frame = wx.Frame(None) + frame1.Center() + #frame.setMsg(LogMsg) + frame1.ShowModal() + frame1.Destroy() + + frame = displayDialog(None) + LogMsg="" + writeTxt= frame.m_richText1.WriteText + rt = frame.m_richText1 + rt.BeginItalic() + writeTxt("'action_menu_annular_check.py'\n") + #frame.m_richText1.WriteText("'action_menu_annular_check.py'\n") + msg="'action_menu_annular_check.py'\n" + msg+="version = "+___version___ + writeTxt("version = "+___version___) + msg+="\nTesting PCB for Annular Rings\nTH Pads >= "+repr(AR_SET)+" Vias >= "+repr(AR_SET_V)+"\nPHD margin on PTH = "+ repr(DRL_EXTRA) + writeTxt("\nTesting PCB for Annular Rings\nTH Pads >= "+repr(AR_SET)+" Vias >= "+repr(AR_SET_V)+"\nPHD margin on PTH = "+ repr(DRL_EXTRA)) + rt.EndItalic() + writeTxt('\n\n') + #print (msg) + LogMsg+=msg+'\n\n' + + # print "LISTING VIAS:" + for item in board.GetTracks(): + if type(item) is pcbnew.VIA: + pos = item.GetPosition() + drill = item.GetDrillValue() + width = item.GetWidth() + ARv = vias_annring_size(item) + if ARv < MIN_AR_SIZE_V: + # print("AR violation at %s." % (pad.GetPosition() / mm_ius )) Raw units, needs fixing + XYpair = item.GetPosition() + msg="AR Via violation of "+f_mm(ARv)+" at XY "+f_mm(XYpair[0])+","+f_mm(XYpair[1]) + rt.BeginTextColour('red') + writeTxt("AR Via violation of "+f_mm(ARv)+" at XY "+f_mm(XYpair[0])+","+f_mm(XYpair[1])+'\n') + rt.EndTextColour() + #print (msg) + LogMsg+=msg+'\n' + FailCV = FailCV+1 + else: + PassCV = PassCV+1 + #print type(item) + + msg="VIAS that Pass = "+repr(PassCV)+"; Fails = "+repr(FailCV) + if FailCV >0: + rt.BeginBold() + writeTxt("VIAS that Pass = "+repr(PassCV)+"; ") + if FailCV >0: + rt.BeginTextColour('red') + writeTxt("Fails = "+repr(FailCV)+'\n\n') + if FailCV >0: + rt.EndTextColour() + rt.EndBold() + print(msg) + LogMsg+=msg+'\n' + + for module in board.GetModules(): + try: + module_Pads=module.PadsList() + except: + module_Pads=module.Pads() + for pad in module_Pads: #print(pad.GetAttribute()) + if pad.GetAttribute() == PAD_ATTRIB_STANDARD: #TH pad + ARv = annring_size(pad) + #print(f_mm(ARv)) + if ARv < MIN_AR_SIZE: + # print("AR violation at %s." % (pad.GetPosition() / mm_ius )) Raw units, needs fixing + XYpair = pad.GetPosition() + msg="AR PTH violation of "+f_mm(ARv)+" at XY "+f_mm(XYpair[0])+","+f_mm(XYpair[1]) + rt.BeginTextColour('red') + writeTxt("AR PTH violation of "+f_mm(ARv)+" at XY "+f_mm(XYpair[0])+","+f_mm(XYpair[1])+'\n') + rt.EndTextColour() + #print (msg) + LogMsg+=msg+'\n' + FailC = FailC+1 + else: + PassC = PassC+1 + if pad.GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED: + ARvX, ARvY = annringNP_size(pad) + #print(f_mm(ARvX));print(f_mm(ARvY)) + if (ARvX) != 0 or ARvY != 0: + ARv = min(ARvX, ARvY) + if ARv < MIN_AR_SIZE: + # print("AR violation at %s." % (pad.GetPosition() / mm_ius )) Raw units, needs fixing + XYpair = pad.GetPosition() + msg="AR NPTH warning of "+f_mm(ARv)+" at XY "+f_mm(XYpair[0])+","+f_mm(XYpair[1]) + rt.BeginTextColour('red') + writeTxt("AR NPTH warning of "+f_mm(ARv)+" at XY "+f_mm(XYpair[0])+","+f_mm(XYpair[1])+'\n') + rt.EndTextColour() + #print (msg) + LogMsg+=msg+'\n' + FailCN = FailCN+1 + else: + PassCN = PassCN+1 + else: + PassCN = PassCN+1 + + #if FailCV >0: + #writeTxt('\n') + msg = "TH PADS that Pass = "+repr(PassC)+"; Fails = "+repr(FailC) + if FailC >0: + rt.BeginBold() + writeTxt("TH PADS that Pass = "+repr(PassC)+"; ") + if FailC >0: + rt.BeginTextColour('red') + writeTxt("Fails = "+repr(FailC)+'\n') + if FailC >0: + rt.EndTextColour() + rt.EndBold() + print(msg) + LogMsg+=msg+'\n' + + msg="NPTH PADS that Pass = "+repr(PassCN)+"; Fails = "+repr(FailCN) + #writeTxt('\n') + if FailCN >0: + rt.BeginBold() + writeTxt("NPTH PADS that Pass = "+repr(PassCN)+"; ") + if FailCN >0: + rt.BeginTextColour('red') + writeTxt("Fails = "+repr(FailCN)+'\n') + if FailC >0: + rt.EndTextColour() + rt.EndBold() + print(msg) + LogMsg+=msg+'\n' + + pcbName = (os.path.splitext(GetBoard().GetFileName())[0]) #filename no ext + #wx.LogMessage(pcbName)#LogMsg) + ##wx.LogMessage(LogMsg) + FC=r"C:\FreeCAD\bin\freecad.exe" + kSU=r"C:\Cad\Progetti_K\3D-FreeCad-tools\kicad-StepUp-tools.FCMacro" + #subprocess.check_call([FC, kSU, pcbName]) + ##p = subprocess.Popen([FC, kSU, pcbName]) + + #found_violations=False + if (FailC+FailCN+FailCV)>0: + found_violations=True + + if found_violations: + #frame.m_staticTitle = wx.StaticText(frame, label=" Check result: (Violations found)") + frame.m_staticTitle.SetLabel(" Check result: (Violations found)") + #self.title.SetForegroundColour('#FF0000') + frame.m_staticTitle.SetBackgroundColour('#FF0000') + font = wx.Font(wx.DEFAULT, wx.DECORATIVE, wx.ITALIC, wx.BOLD) + frame.m_staticTitle.SetFont(font) + else: + #frame.m_staticTitle = wx.StaticText(frame, label=" Annular Check result: OK") + frame.m_staticTitle.SetLabel(" Annular Check result: OK") + frame.m_staticTitle.SetBackgroundColour('#00FF00') + font = wx.Font(wx.DEFAULT, wx.DECORATIVE, wx.ITALIC, wx.BOLD) + frame.m_staticTitle.SetFont(font) + ##frame = displayDialog(None) + #frame = wx.Frame(None) + frame.Center() + #frame.setMsg(LogMsg) + #frame.Show(True) + frame.ShowModal() + #frame.show() + frame.Destroy() + #frame = wx.wxFrame(None, 10110, 'T-Make', size=wx.wxSize(100,100), + # style=wx.wxSTAY_ON_TOP) + #frame.show() + +# annular_check().register() +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser( + description='KiCad PCB Annular Checker') + parser.add_argument('file', type=str, help="KiCad PCB file") + args = parser.parse_args() + print("Loading %s" % args.file) + main(pcbnew.LoadBoard(args.file)) + +else: + annular_check().register() + + +# "b64_data" is a variable containing your base64 encoded jpeg +annular_ico_b64_data =\ +""" +iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAIQQAACEEB+v+u9gAAABl0RVh0U29mdHdhcmUAd3d +3Lmlua3NjYXBlLm9yZ5vuPBoAAAJ5SURBVDiNdZRNSFRRFMd/976ZJjeBn2PUoiLaiMFkFkHIQ0NBdBUlJuIqiixmIYjQRgiJYKAMXRRSi6TCD5gaWiSFsy +gixOyDpDAog8wn9SISapz33m0x82bmzcOzO/97/r973nn3XqGUojjCybIm4BhQD9Rl5XlgDnhi6ObTYo8oBIWTZeXAMNDlalW/FJYmMLd5fONA1NBN0wcKJ +8sagftAZWRJcWHS5vCiouJ3pnClUvC8VnDtpOTTDgGwCpwydHM2Bwony8LAe6koH7hjc37aQXN8XwzAv5DgUo9krF0C/ABqDN1ck9n1G0D54C2H6OTmEICt +KcXQTZvTCQegAhgFEFWzpW1A4uAHRaLfQhbPXgD+/8HfkKBhNMDXMACtEmgC6L/reCBWKGAzMmLxbQWWl2Fw0FJavqIkpYhO2m7aHADqNQcOLXq3DcSuKnp +6Ajmhry8g1tfTxGJBVzryLuepl0Bk16qiJJUHKU0qOjryEDe6u4OF6e7vitCGAohIAFk0XCWEQkp8oWmeVAAiu78EFj5vF6S2iFyBtGxJPJ72gSYmPNpyOH +McgAUJzFkazO/zetLRc4J43MayIJWCsTHLuTzkaellTa7rOVE1W9oOPDz6VjF90fI14QQ1RzhKCNsRhfpGEBqvB1jaKQDapKGbCeDBs/2C8Rb/XGTalsUQg +Fin5kKmDN185DrPAD8HzmrcbpUony0flgZXujSGT+SuSC94L20LcA8obXiduSp1H8kdiz8l8KJWEOvUeLNXuJAuQzdnPKAsrBoYAY4DaA7sWck8I1+qKex0 +Cug1dHPNFcQmD1sL0AwcACJZeQF4BcwYuvm42PMfVgD11Y9MUIEAAAAASUVORK5CYII= +""" + + +# execfile("annular.py") +# annular.py Testing PCB for Annular Ring >= 0.15 +# AR violation of 0.1 at XY 172.974,110.744 +# VIAS that Pass = 100 Fails = 1 +# AR violation of 0.1 at XY 172.212,110.744 +# AR violation of 0.0 at XY 154.813,96.52 +# PADS that Pass = 49 Fails = 2 + diff --git a/AnnularChecker/annular_checker.pyc b/AnnularChecker/annular_checker.pyc new file mode 100644 index 0000000000000000000000000000000000000000..55d33563c10b1c1abbb490d45b3b645ede05a70c GIT binary patch literal 14910 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 4.0 +# annular.py release "1.5.1" +# +# annular.py checking PCB for Annular Ring in Vias and TH Pads +# (SMD, Connector and NPTH are skipped) +# default Annular Ring >= 0.15 both for TH Pads and Vias +# to change values modify: +# +# AR_SET = 0.150 #minimum annular accepted for pads +# AR_SET_V = 0.150 #minimum annular accepted for vias + +# annular.py + + +___version___="1.5.7" + +global mm_ius, DRL_EXTRA, AR_SET, AR_SET_V, DRL_EXTRA_ius, MIN_AR_SIZE, MIN_AR_SIZE_V, found_violations +#wx.LogMessage("My message") +mm_ius = 1000000.0 +# (consider always drill +0.1) +DRL_EXTRA=0.1 +DRL_EXTRA_ius=DRL_EXTRA * mm_ius + +AR_SET = 0.125 #minimum annular accepted for pads +MIN_AR_SIZE = AR_SET * mm_ius + +AR_SET_V = 0.125 #minimum annular accepted for vias +MIN_AR_SIZE_V = AR_SET_V * mm_ius + +import sys +import wx +import wx.html +import subprocess +import os +import pcbnew +from pcbnew import * +import base64 +from wx.lib.embeddedimage import PyEmbeddedImage +sys.path.append(os.path.dirname(__file__)) + + + +class annular_check( pcbnew.ActionPlugin ): + """ + A script to check for annular ring violations + both for TH pads and vias + requirements: KiCAD pcbnew >= 4.0 + annular.py release "1.5.1" + + annular.py checking PCB for Annular Ring in Vias and TH Pads + (SMD, Connector and NPTH are skipped) + default Annular Ring >= 0.15 both for TH Pads and Vias + to change values modify: + + AR_SET = 0.150 #minimum annular accepted for pads + AR_SET_V = 0.150 #minimum annular accepted for vias + """ + + def defaults( self ): + """ + Method defaults must be redefined + self.name should be the menu label to use + self.category should be the category (not yet used) + self.description should be a comprehensive description + of the plugin + """ + self.name = "Annular check" + self.category = "Checking PCB" + self.description = "Automaticaly check annular on an existing PCB" + + def Run( self ): + + ########################################################################### + ## Class AR_Prm + ########################################################################### + + class AR_Prm ( wx.Dialog ): + + def __init__( self, parent ): + wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = "AR parameters", pos = wx.DefaultPosition, size = wx.Size( 320,193 ), style = wx.DEFAULT_DIALOG_STYLE ) + + self.SetSizeHints( 500,500 ) + + self.SetIcon(PyEmbeddedImage(annular_ico_b64_data).GetIcon()) + + bSizer1 = wx.BoxSizer( wx.VERTICAL ) + + gSizer2 = wx.GridSizer( 0, 2, 0, 0 ) + + self.m_staticText11 = wx.StaticText( self, wx.ID_ANY, u"PHD margin", wx.Point( -1,-1 ), wx.DefaultSize, 0 ) + self.m_staticText11.Wrap( -1 ) + + gSizer2.Add( self.m_staticText11, 0, wx.ALL, 5 ) + + self.m_textPHD = wx.TextCtrl( self, wx.ID_ANY, str(DRL_EXTRA), wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer2.Add( self.m_textPHD, 0, wx.ALL, 5 ) + + self.m_staticText1 = wx.StaticText( self, wx.ID_ANY, u"AR for pads", wx.Point( -1,-1 ), wx.DefaultSize, 0 ) + self.m_staticText1.Wrap( -1 ) + + gSizer2.Add( self.m_staticText1, 0, wx.ALL, 5 ) + + self.m_textAR_SET = wx.TextCtrl( self, wx.ID_ANY, str(AR_SET), wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer2.Add( self.m_textAR_SET, 0, wx.ALL, 5 ) + + self.m_staticText12 = wx.StaticText( self, wx.ID_ANY, u"AR for vias", wx.Point( -1,-1 ), wx.DefaultSize, 0 ) + self.m_staticText12.Wrap( -1 ) + + gSizer2.Add( self.m_staticText12, 0, wx.ALL, 5 ) + + self.m_textAR_SET_V = wx.TextCtrl( self, wx.ID_ANY, str(AR_SET_V), wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer2.Add( self.m_textAR_SET_V, 0, wx.ALL, 5 ) + + + bSizer1.Add( gSizer2, 1, wx.EXPAND, 5 ) + + gSizer1 = wx.GridSizer( 0, 2, 0, 0 ) + + self.m_ok_btn = wx.Button( self, wx.ID_ANY, u"OK", wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer1.Add( self.m_ok_btn, 0, wx.ALL|wx.ALIGN_CENTER_HORIZONTAL, 5 ) + + # self.m_cancel_btn = wx.Button( self, wx.ID_ANY, u"Cancel", wx.DefaultPosition, wx.DefaultSize, 0 ) + # gSizer1.Add( self.m_cancel_btn, 0, wx.ALL, 5 ) + + + bSizer1.Add( gSizer1, 1, wx.EXPAND, 5 ) + + + self.SetSizer( bSizer1 ) + self.Layout() + + self.Centre( wx.BOTH ) + + #### ----- connections + # Connect Events + self.Bind(wx.EVT_BUTTON, self.OnClickOK, self.m_ok_btn) + # self.Bind(wx.EVT_BUTTON, self.OnClickCancel, self.m_cancel_btn) + # Tooltips + #self.m_cancel_btn.SetToolTip( wx.ToolTip(u"Cancel" )) + self.m_ok_btn.SetToolTip( wx.ToolTip(u"Confirm" )) + self.m_staticText1.SetToolTip( wx.ToolTip(u"Annular Ring for Pads (mm)" )) + self.m_textAR_SET.SetToolTip( wx.ToolTip(u"Annular Ring for Pads (mm)" )) + self.m_textAR_SET_V.SetToolTip( wx.ToolTip(u"Annular Ring for Vias (mm)" )) + self.m_staticText12.SetToolTip( wx.ToolTip(u"Annular Ring for Vias (mm)" )) + self.m_textPHD.SetToolTip( wx.ToolTip(u"Drill extra margin (mm)" )) + self.m_staticText11.SetToolTip( wx.ToolTip(u"Drill extra margin (mm)" )) + + def __del__( self ): + pass + + def OnClickOK(self, event): + global mm_ius, DRL_EXTRA, AR_SET, AR_SET_V, DRL_EXTRA_ius, MIN_AR_SIZE, MIN_AR_SIZE_V + + self.m_ok_btn.SetLabel("Clicked") + phd = float(self.m_textPHD.GetValue().replace(',','.')) + ar = float(self.m_textAR_SET.GetValue().replace(',','.')) + arv = float(self.m_textAR_SET_V.GetValue().replace(',','.')) + DRL_EXTRA=phd + DRL_EXTRA_ius=DRL_EXTRA * mm_ius + + AR_SET = ar #minimum annular accepted for pads + MIN_AR_SIZE = AR_SET * mm_ius + + AR_SET_V = arv #minimum annular accepted for vias + MIN_AR_SIZE_V = AR_SET_V * mm_ius + self.Destroy() + + def OnClickCancel(self, event): + self.m_cancel_btn.SetLabel("Clicked") + self.Destroy() + + #wx.MessageDialog(self.frame,"ciao") + #subprocess.check_call(["C:\pathToYourProgram\yourProgram.exe", "your", "arguments", "comma", "separated"]) + #http://stackoverflow.com/questions/1811691/running-an-outside-program-executable-in-python + class displayDialog(wx.Dialog): + """ + The default frame + http://stackoverflow.com/questions/3566603/how-do-i-make-wx-textctrl-multi-line-text-update-smoothly + """ + global mm_ius, DRL_EXTRA, AR_SET, AR_SET_V, DRL_EXTRA_ius, MIN_AR_SIZE, MIN_AR_SIZE_V, found_violations + #---------------------------------------------------------------------- + #def __init__(self): + # """Constructor""" + # wx.Frame.__init__(self, None, title="Display Frame", style=wx.DEFAULT_FRAME_STYLE, wx.ICON_INFORMATION) + # panel = wx.Panel(self) + def __init__(self, parent): + wx.Dialog.__init__(self, parent, id=-1, title="Annular Checker")# + #, style=wx.DEFAULT_DIALOG_STYLE, wx.ICON_INFORMATION) + #, style=wx.DEFAULT_DIALOG_STYLE, wx.ICON_INFORMATION) + #, pos=DefaultPosition, size=DefaultSize, style = wx.DEFAULT_FRAME_STYLE & (~wx.MAXIMIZE_BOX), name="fname") + #, wx.ICON_INFORMATION) #, title="Annular Check", style=wx.DEFAULT_FRAME_STYLE, wx.ICON_INFORMATION) + # + + self.SetIcon(PyEmbeddedImage(annular_ico_b64_data).GetIcon()) + #wx.IconFromBitmap(wx.Bitmap("icon.ico", wx.BITMAP_TYPE_ANY))) + self.panel = wx.Panel(self) + + if found_violations: + self.title = wx.StaticText(self.panel, label="Check result: (Violations found)") + #self.title.SetForegroundColour('#FF0000') + self.title.SetBackgroundColour('#FF0000') + font = wx.Font(wx.DEFAULT, wx.DECORATIVE, wx.ITALIC, wx.BOLD) + self.title.SetFont(font) + else: + self.title = wx.StaticText(self.panel, label="Annular Check result: OK") + self.title.SetBackgroundColour('#00FF00') + #self.result = wx.StaticText(self.panel, label="") + #self.result.SetForegroundColour('#FF0000') + #self.button = wx.Button(self.panel, label="Save") + #self.lblname = wx.StaticText(self.panel, label="Your name:") + #self.editname = wx.TextCtrl(self.panel, size=(140, -1)) + self.editname = wx.TextCtrl(self.panel, size = (400, 400), style = wx.TE_MULTILINE|wx.TE_READONLY) + self.m_htmlWin1 = wx.html.HtmlWindow( self.panel, wx.ID_ANY, wx.DefaultPosition, wx.Size( 400,400 ), wx.html.HW_SCROLLBAR_AUTO ) + #bSizer1.Add( self.m_htmlWin1, 0, wx.ALL, 5 ) + + + # Set sizer for the frame, so we can change frame size to match widgets + self.windowSizer = wx.BoxSizer() + #self.windowSizer.Add(self.m_htmlWin1, 1, wx.ALL | wx.EXPAND) + self.windowSizer.Add(self.panel, 1, wx.ALL | wx.EXPAND) + + # Set sizer for the panel content + self.sizer = wx.GridBagSizer(5, 0) + self.sizer.Add(self.title, (0, 0)) + #self.sizer.Add(self.result, (1, 0)) + #self.sizer.Add(self.lblname, (1, 0)) + ##self.sizer.Add(self.m_htmlWin1, (1, 0)) + self.sizer.Add(self.editname, (1, 0)) + #self.sizer.Add(self.button, (2, 0), (1, 2), flag=wx.EXPAND) + + # Set simple sizer for a nice border + self.border = wx.BoxSizer() + self.border.Add(self.sizer, 1, wx.ALL | wx.EXPAND, 5) + + # Use the sizers + self.panel.SetSizerAndFit(self.border) + self.SetSizerAndFit(self.windowSizer) + #self.result.SetLabel(msg) + # Set event handlers + #self.button.Bind(wx.EVT_BUTTON, self.OnButton) + #self.Show() + #self.Bind(wx.EVT_CLOSE,self.OnClose) + + #def OnClose(self,e): + # #wx.LogMessage("c") + # e.Skip() + #self.Close() + + #def OnButton(self, e): + # self.result.SetLabel(self.editname.GetValue()) + def setMsg(self, t_msg): + self.editname.SetValue(t_msg) + self.m_htmlWin1.SetPage(t_msg) + + + def annring_size(pad): + # valid for oval pad/drills + annrX=(pad.GetSize()[0] - (pad.GetDrillSize()[0]+DRL_EXTRA_ius))/2 + annrY=(pad.GetSize()[1] - (pad.GetDrillSize()[1]+DRL_EXTRA_ius))/2 + #annr=min(pad.GetSize()) - max(pad.GetDrillSize()) + #if annr < MIN_AR_SIZE: + #print annrX + #print annrY + #print pad.GetSize()[0]/mm_ius + #print pad.GetSize()[0]#/mm_ius + #print pad.GetDrillSize()[0]#/mm_ius + #print DRL_EXTRA_ius + #print pad.GetDrillSize()[0]/mm_ius + #print (pad.GetDrillSize()[0]+DRL_EXTRA_ius)/mm_ius + #print annrX/mm_ius + return min(annrX,annrY) + + def annringNP_size(pad): + # valid for oval pad/drills + annrX=(pad.GetSize()[0] - (pad.GetDrillSize()[0]))/2 + annrY=(pad.GetSize()[1] - (pad.GetDrillSize()[1]))/2 + #annr=min(pad.GetSize()) - max(pad.GetDrillSize()) + #if annr < MIN_AR_SIZE: + #print annrX + #print annrY + #print pad.GetSize()[0]/mm_ius + #print pad.GetSize()[0]#/mm_ius + #print pad.GetDrillSize()[0]#/mm_ius + #print DRL_EXTRA_ius + #print pad.GetDrillSize()[0]/mm_ius + #print (pad.GetDrillSize()[0]+DRL_EXTRA_ius)/mm_ius + #print annrX/mm_ius + #return min(annrX,annrY) + return annrX,annrY + + def vias_annring_size(via): + # calculating via annular + annr=(via.GetWidth() - (via.GetDrillValue()+DRL_EXTRA_ius))/2 + #print via.GetWidth() + #print via.GetDrillValue() + return annr + + def f_mm(raw): + return repr(raw/mm_ius) + + board = pcbnew.GetBoard() + PassC=FailC=0 + PassCV=FailCV=0 + + PassCN=FailCN=0 + PassCVN=FailCVN=0 + + fileName = GetBoard().GetFileName() + if len(fileName)==0: + wx.LogMessage("a board needs to be saved/loaded!") + else: + + frame = AR_Prm(None) + #frame = wx.Frame(None) + frame.Center() + #frame.setMsg(LogMsg) + frame.ShowModal() + frame.Destroy() + + LogMsg="Hello, world!
" + msg="action_menu_annular_check.py
" + msg+="version = "+___version___ + msg+="
Testing PCB for Annular Rings
TH Pads = "+repr(AR_SET)+" Vias = "+repr(AR_SET_V)+"
PHD margin on PTH = "+ repr(DRL_EXTRA) + #print (msg) + LogMsg+=msg+"

" + + # print "LISTING VIAS:" + for item in board.GetTracks(): + if type(item) is pcbnew.VIA: + pos = item.GetPosition() + drill = item.GetDrillValue() + width = item.GetWidth() + ARv = vias_annring_size(item) + if ARv < MIN_AR_SIZE_V: + # print("AR violation at %s." % (pad.GetPosition() / mm_ius )) Raw units, needs fixing + XYpair = item.GetPosition() + msg="AR Via violation of "+f_mm(ARv)+" at XY "+f_mm(XYpair[0])+","+f_mm(XYpair[1]) + #print (msg) + LogMsg+=msg+"
" + FailCV = FailCV+1 + else: + PassCV = PassCV+1 + #print type(item) + + msg="VIAS that Pass = "+repr(PassCV)+"; Fails = "+repr(FailCV) + # print(msg) + LogMsg+=msg+"
" + + for module in board.GetModules(): + try: + module_Pads=module.PadsList() + except: + module_Pads=module.Pads() + for pad in module_Pads: #print(pad.GetAttribute()) + if pad.GetAttribute() == PAD_ATTRIB_STANDARD: #TH pad + ARv = annring_size(pad) + #print(f_mm(ARv)) + if ARv < MIN_AR_SIZE: + # print("AR violation at %s." % (pad.GetPosition() / mm_ius )) Raw units, needs fixing + XYpair = pad.GetPosition() + msg="AR PTH violation of "+f_mm(ARv)+" at XY "+f_mm(XYpair[0])+","+f_mm(XYpair[1]) + #print (msg) + LogMsg+=msg+"
" + FailC = FailC+1 + else: + PassC = PassC+1 + if pad.GetAttribute() == PAD_ATTRIB_HOLE_NOT_PLATED: + ARvX, ARvY = annringNP_size(pad) + #print(f_mm(ARvX));print(f_mm(ARvY)) + if (ARvX) != 0 or ARvY != 0: + ARv = min(ARvX, ARvY) + if ARv < MIN_AR_SIZE: + # print("AR violation at %s." % (pad.GetPosition() / mm_ius )) Raw units, needs fixing + XYpair = pad.GetPosition() + msg="AR NPTH warning of "+f_mm(ARv)+" at XY "+f_mm(XYpair[0])+","+f_mm(XYpair[1]) + #print (msg) + LogMsg+=msg+"
" + FailCN = FailCN+1 + else: + PassCN = PassCN+1 + else: + PassCN = PassCN+1 + + msg = "TH PADS that Pass = "+repr(PassC)+"; Fails = "+repr(FailC) + print(msg) + LogMsg+=msg+"
" + + msg="NPTH PADS that Pass = "+repr(PassCN)+"; Fails = "+repr(FailCN) + print(msg) + LogMsg+=msg+"" + + pcbName = (os.path.splitext(GetBoard().GetFileName())[0]) #filename no ext + #wx.LogMessage(pcbName)#LogMsg) + ##wx.LogMessage(LogMsg) + FC=r"C:\FreeCAD\bin\freecad.exe" + #kSU=r"C:\Cad\Progetti_K\3D-FreeCad-tools\kicad-StepUp-tools.FCMacro" + #subprocess.check_call([FC, kSU, pcbName]) + ##p = subprocess.Popen([FC, kSU, pcbName]) + + found_violations=False + if (FailC+FailCN+FailCV)>0: + found_violations=True + + frame = displayDialog(None) + #frame = wx.Frame(None) + frame.Center() + frame.setMsg(LogMsg) + frame.ShowModal() + frame.Destroy() + #frame = wx.wxFrame(None, 10110, 'T-Make', size=wx.wxSize(100,100), + # style=wx.wxSTAY_ON_TOP) + #frame.show() + +# annular_check().register() +if __name__ == "__main__": + import argparse + + parser = argparse.ArgumentParser( + description='KiCad PCB Annular Checker') + parser.add_argument('file', type=str, help="KiCad PCB file") + args = parser.parse_args() + print("Loading %s" % args.file) + main(pcbnew.LoadBoard(args.file)) + +else: + annular_check().register() + + +# "b64_data" is a variable containing your base64 encoded jpeg +annular_ico_b64_data =\ +""" +iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAIQQAACEEB+v+u9gAAABl0RVh0U29mdHdhcmUAd3d +3Lmlua3NjYXBlLm9yZ5vuPBoAAAJ5SURBVDiNdZRNSFRRFMd/976ZJjeBn2PUoiLaiMFkFkHIQ0NBdBUlJuIqiixmIYjQRgiJYKAMXRRSi6TCD5gaWiSFsy +gixOyDpDAog8wn9SISapz33m0x82bmzcOzO/97/r973nn3XqGUojjCybIm4BhQD9Rl5XlgDnhi6ObTYo8oBIWTZeXAMNDlalW/FJYmMLd5fONA1NBN0wcKJ +8sagftAZWRJcWHS5vCiouJ3pnClUvC8VnDtpOTTDgGwCpwydHM2Bwony8LAe6koH7hjc37aQXN8XwzAv5DgUo9krF0C/ABqDN1ck9n1G0D54C2H6OTmEICt +KcXQTZvTCQegAhgFEFWzpW1A4uAHRaLfQhbPXgD+/8HfkKBhNMDXMACtEmgC6L/reCBWKGAzMmLxbQWWl2Fw0FJavqIkpYhO2m7aHADqNQcOLXq3DcSuKnp +6Ajmhry8g1tfTxGJBVzryLuepl0Bk16qiJJUHKU0qOjryEDe6u4OF6e7vitCGAohIAFk0XCWEQkp8oWmeVAAiu78EFj5vF6S2iFyBtGxJPJ72gSYmPNpyOH +McgAUJzFkazO/zetLRc4J43MayIJWCsTHLuTzkaellTa7rOVE1W9oOPDz6VjF90fI14QQ1RzhKCNsRhfpGEBqvB1jaKQDapKGbCeDBs/2C8Rb/XGTalsUQg +Fin5kKmDN185DrPAD8HzmrcbpUony0flgZXujSGT+SuSC94L20LcA8obXiduSp1H8kdiz8l8KJWEOvUeLNXuJAuQzdnPKAsrBoYAY4DaA7sWck8I1+qKex0 +Cug1dHPNFcQmD1sL0AwcACJZeQF4BcwYuvm42PMfVgD11Y9MUIEAAAAASUVORK5CYII= +""" + + +# execfile("annular.py") +# annular.py Testing PCB for Annular Ring >= 0.15 +# AR violation of 0.1 at XY 172.974,110.744 +# VIAS that Pass = 100 Fails = 1 +# AR violation of 0.1 at XY 172.212,110.744 +# AR violation of 0.0 at XY 154.813,96.52 +# PADS that Pass = 49 Fails = 2 + diff --git a/AnnularChecker/annular_ico.svg b/AnnularChecker/annular_ico.svg new file mode 100644 index 0000000..e6c157e --- /dev/null +++ b/AnnularChecker/annular_ico.svg @@ -0,0 +1,214 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AnnularChecker/dlg.py.txt b/AnnularChecker/dlg.py.txt new file mode 100644 index 0000000..0711b2a --- /dev/null +++ b/AnnularChecker/dlg.py.txt @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- + +########################################################################### +## Python code generated with wxFormBuilder (version Jul 11 2018) +## http://www.wxformbuilder.org/ +## +## PLEASE DO *NOT* EDIT THIS FILE! +########################################################################### + +import wx +import wx.xrc + +########################################################################### +## Class AR_Prm +########################################################################### + +class AR_Prm ( wx.Dialog ): + + def __init__( self, parent ): + wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = u"Annular Checker", pos = wx.DefaultPosition, size = wx.Size( 320,229 ), style = wx.DEFAULT_DIALOG_STYLE ) + + self.SetSizeHints( wx.DefaultSize, wx.DefaultSize ) + + bSizer1 = wx.BoxSizer( wx.VERTICAL ) + + gSizer2 = wx.GridSizer( 0, 2, 0, 0 ) + + self.m_StaticTextPHD = wx.StaticText( self, wx.ID_ANY, u"PHD margin", wx.Point( -1,-1 ), wx.DefaultSize, 0 ) + self.m_StaticTextPHD.Wrap( -1 ) + + gSizer2.Add( self.m_StaticTextPHD, 0, wx.ALL, 5 ) + + self.m_textPHD = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer2.Add( self.m_textPHD, 0, wx.ALL, 5 ) + + self.m_StaticTextAR_SET = wx.StaticText( self, wx.ID_ANY, u"AR for pads", wx.Point( -1,-1 ), wx.DefaultSize, 0 ) + self.m_StaticTextAR_SET.Wrap( -1 ) + + gSizer2.Add( self.m_StaticTextAR_SET, 0, wx.ALL, 5 ) + + self.m_textAR_SET = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer2.Add( self.m_textAR_SET, 0, wx.ALL, 5 ) + + self.m_StaticTextAR_SET_V = wx.StaticText( self, wx.ID_ANY, u"AR for vias", wx.Point( -1,-1 ), wx.DefaultSize, 0 ) + self.m_StaticTextAR_SET_V.Wrap( -1 ) + + gSizer2.Add( self.m_StaticTextAR_SET_V, 0, wx.ALL, 5 ) + + self.m_textAR_SET_V = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer2.Add( self.m_textAR_SET_V, 0, wx.ALL, 5 ) + + bSizer2 = wx.BoxSizer( wx.VERTICAL ) + + self.m_staticTextVersion = wx.StaticText( self, wx.ID_ANY, u"Version", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticTextVersion.Wrap( -1 ) + + bSizer2.Add( self.m_staticTextVersion, 0, wx.ALL, 5 ) + + + gSizer2.Add( bSizer2, 1, wx.EXPAND, 5 ) + + + bSizer1.Add( gSizer2, 1, wx.EXPAND, 5 ) + + gSizer1 = wx.GridSizer( 0, 2, 0, 0 ) + + self.m_ok_btn = wx.Button( self, wx.ID_ANY, u"OK", wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer1.Add( self.m_ok_btn, 0, wx.ALL, 5 ) + + self.m_cancel_btn = wx.Button( self, wx.ID_ANY, u"Cancel", wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer1.Add( self.m_cancel_btn, 0, wx.ALL, 5 ) + + + bSizer1.Add( gSizer1, 1, wx.EXPAND, 5 ) + + + self.SetSizer( bSizer1 ) + self.Layout() + + self.Centre( wx.BOTH ) + + def __del__( self ): + pass + + diff --git a/AnnularChecker/outDialog.fbp b/AnnularChecker/outDialog.fbp new file mode 100644 index 0000000..87bc43d --- /dev/null +++ b/AnnularChecker/outDialog.fbp @@ -0,0 +1,540 @@ + + + + + + C++ + 1 + source_name + 0 + 0 + res + UTF-8 + connect + + 1000 + none + + 0 + parametersDlg + + . + + 1 + 1 + 1 + 1 + UI + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + wxBOTH + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + + displayDialog + + 320,521 + wxDEFAULT_DIALOG_STYLE + ; forward_declare + Annular Checker + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + bSizer1 + wxVERTICAL + none + + 5 + wxEXPAND + 1 + + + bSizer2 + wxVERTICAL + none + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + 0 + + + 0 + + 1 + m_staticTitle + 1 + + + protected + 1 + + Resizable + 1 + + + ; forward_declare + 0 + + + + + -1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND | wxALL + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + -1,400 + 1 + m_richText1 + 1 + + + protected + 1 + + Resizable + 1 + + wxTE_READONLY + ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + wxVSCROLL|wxHSCROLL|wxNO_BORDER|wxWANTS_CHARS + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxEXPAND + 1 + + 2 + 0 + + gSizer3 + none + 0 + 0 + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + wxID_ANY + OK + + 0 + + 0 + + + 0 + + 1 + ok_btn + 1 + + + protected + 1 + + + + Resizable + 1 + + + ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + wxID_ANY + Copy Text + + 0 + + 0 + + + 0 + + 1 + copy_btn + 1 + + + protected + 1 + + + + Resizable + 1 + + + ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/AnnularChecker/tkinter-test.txt b/AnnularChecker/tkinter-test.txt new file mode 100644 index 0000000..7ea4f49 --- /dev/null +++ b/AnnularChecker/tkinter-test.txt @@ -0,0 +1,2 @@ +import _tkinter +import tkSimpleDialog \ No newline at end of file diff --git a/FabricationPositions/__init__.py b/FabricationPositions/__init__.py new file mode 100644 index 0000000..2b343d1 --- /dev/null +++ b/FabricationPositions/__init__.py @@ -0,0 +1,2 @@ +from .fabrication_positions import generatePOS +generatePOS().register() diff --git a/FabricationPositions/__pycache__/__init__.cpython-36.pyc b/FabricationPositions/__pycache__/__init__.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..d39deea7a07932796b881bbacc7c2d43be543bbc GIT binary patch literal 238 zcmXr!<>j*2xG&a&fq~&M5W@izkmUfx#Zo{bg&~D8has0Sijk2am9d#2i>aA`ks*aC zm_d{IB_mLQCgUyc^whl6qQsKa0RLb=P39=kw8W&M%;dz9%>2Cgg8br45Tm#VWbQ4F zqSW-v;*!*&l?+8JKmjoE%SS&WKQ~oBH?dS-FB_;cCB7i1G(9t~SRc&OO-u$Ep<9xl spHr;whA=$WI0|-M{il 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 literal 0 HcmV?d00001 diff --git a/FabricationPositions/fabricationPositions-old.svg b/FabricationPositions/fabricationPositions-old.svg new file mode 100644 index 0000000..e3d3124 --- /dev/null +++ b/FabricationPositions/fabricationPositions-old.svg @@ -0,0 +1,308 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/FabricationPositions/fabricationPositions.png b/FabricationPositions/fabricationPositions.png new file mode 100644 index 0000000000000000000000000000000000000000..2aeec47cc2378cb450f92b27183796bf91574cd1 GIT binary patch literal 1332 zcmV-41JnIxpO}I&*&S$M zOQAo@&d>FsTPS6Lrln}&dHc@2bI-}#Z|+>+|A6xHas>d(7ZdX&y9~p4CwJVNOePOc z*FZL#eO^`7rGCGEQ#>9w#>e=?WX`#hh{8k^wrzXCbj8QvSzXs#D=RBK<4F+!N=ix! z1wmK<0GUjtfQTdjAfifHmX}UfnTSFFT#T`W!^6YBsH*y0DwWcJTwi2aULpuWG?&ka zs1yJgW0wJpOxFN_SwvJg=7)2Bf^%M{Y1*-yS}@aKjDd)<0MbD24fuS%0#Otn1OP!0 z3M|Xooy-3iK-*o=T?OC`#+V7<9DwDiRO$kdy90fFeS-kv01yg=iZYpu0RTjFM%VTD zbk$W=eH1_jz#oQTtjXyC$nzD<6c}Sad%fNl#)|`a=0HBwt*)!}X9J%Un1z`kamaaI zIKQQ3&yLUPYwA;S)^7p81*x}p`=0HKUR(A0UjJ49Lja(;&d$2(&Qx`FcK)bo+ULn+ z(wbO%QUG4u@mJ$v?)CMDxSpa10JC9OG=k@)Z>o z?F)y)v4)0*7pG*v>2xlyZ>X<4UYbDH0|#+bK8&?>Yv(pMH!q_S+Z7LVBOd66Tvl4p z+}ymZqN>8v6+DQp;6YsW4mg^cnyNQ%-n`7|bUu=={-jz^{a7{BpnB~b2m}BCZ*JKH z!x$ayPZvG`09gNaJ^K6m0RT_($1!{M?Az9!lmUmsflw%P6CVr)Z{oaOFT7sw4O}Xf zf@zxfP1(S7aNxiJBoYZQ#_W9Y8J`2`bQ+4HfO9_luNbJQsex&lFbqS^7oSuMbX`aL zt{<+Qm#nBp)x%YY?D!Ug{iB1eL@mb3+Lh@3=~tXQbP@o-M20o5uEyfUixG>(v?&=d z3`1ybZ8he6?~|%V7C@Jh*q_;NolTuJ6Z?-kR`=8~0Eo=rMbaE8+x1;cTJ!tM8~{23 zZP*vsmmM4&WJE+$GN5VNx@0o>(mL9(#cjFa6kRy-+tHTJL!B*^nR#EhEq4F_uB0#b zY}>YNQ?XgnsWzMgV=bZ71^8zvCTNf9pj6 zAkZNIu(;j&L2#=Z0BmNBn5H>!+u9R}+&*vu0Ep;aUDv;wjLE5j0YD%SD7J081wa`_ z+ftD;(90Mb!02AM@op?9w?tW4*?dJ&j*FtGbIvW!xy2akFD@=Vr6@{x#)QYi@AofK z6y=mCiUW+X0k7Blx1uOb_e6XQvMfI%%kl}2$Mct>DBoa)r=>8F7#bQn?eTbYNs=mD qE?4cPOP7p%58eyR_@DAWfqwuC2gyZ-AHQq>0000kB literal 0 HcmV?d00001 diff --git a/FabricationPositions/fabricationPositions.svg b/FabricationPositions/fabricationPositions.svg new file mode 100644 index 0000000..54ed9a2 --- /dev/null +++ b/FabricationPositions/fabricationPositions.svg @@ -0,0 +1,302 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/action_menu_positions.py b/FabricationPositions/fabrication_positions.py similarity index 97% rename from action_menu_positions.py rename to FabricationPositions/fabrication_positions.py index 5ac3b15..bd9529b 100644 --- a/action_menu_positions.py +++ b/FabricationPositions/fabrication_positions.py @@ -2,13 +2,13 @@ # # A script to generate POS file for kicad_pcb # requirements: KiCAD pcbnew >= 4.0 -# release "1.0.8" +# release "1.0.9" # copyright Maurice easyw # # main script from https://forum.kicad.info/t/pcba-wants-all-parts-in-the-pos-file-not-just-smd/10045/6 # -___version___="1.0.8" +___version___="1.2.0" #wx.LogMessage("My message") #mm_ius = 1000000.0 @@ -262,9 +262,12 @@ class generatePOS( pcbnew.ActionPlugin ): self.description should be a comprehensive description of the plugin """ - self.name = "Generate POS output" + self.name = "Generate Fabrication POS output\nversion "+___version___ self.category = "Fabrication Output" self.description = "Generate POS output for SMD, THD, Virtual" + #self.SetIcon(PyEmbeddedImage(getPos_ico_b64_data).GetIcon()) + self.icon_file_name = os.path.join(os.path.dirname(__file__), "./fabricationPositions.png") + self.show_toolbar_button = True def Run( self ): @@ -290,7 +293,7 @@ class generatePOS( pcbnew.ActionPlugin ): #, wx.ICON_INFORMATION) #, title="Annular Check", style=wx.DEFAULT_FRAME_STYLE, wx.ICON_INFORMATION) # - self.SetIcon(PyEmbeddedImage(round_ico_b64_data).GetIcon()) + self.SetIcon(PyEmbeddedImage(getPos_ico_b64_data).GetIcon()) #wx.IconFromBitmap(wx.Bitmap("icon.ico", wx.BITMAP_TYPE_ANY))) self.panel = wx.Panel(self) self.title = wx.StaticText(self.panel, label="Generate POS debug:") @@ -375,7 +378,7 @@ class generatePOS( pcbnew.ActionPlugin ): generatePOS().register() # "b64_data" is a variable containing your base64 encoded jpeg -round_ico_b64_data =\ +getPos_ico_b64_data =\ """ iVBORw0KGgoAAAANSUhEUgAAAEAAAAA/CAYAAABQHc7KAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAdDwAAHQ8Bjlx1kwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3Nj YXBlLm9yZ5vuPBoAAAw+SURBVGiB7Zt7cFTVHcc/d+9uSAIhklgIpiQ0tjxSkCKIQ1CpCO3wUKCW0cF0Bqf/VFTG6tROS/8QZuxYwZGHgqD4SjNToFFDjJjGB4LkyUPk diff --git a/FabricationPositions/g888.png b/FabricationPositions/g888.png new file mode 100644 index 0000000000000000000000000000000000000000..0db028e66a0d49d23f108eaea7007fd1c24ae71e GIT binary patch literal 831 zcmV-F1Hk-=P)HtaTOr`d64cTGv8!4;!f(|E|G z#~e1&D5Azg2`z*mxrkmYJxCD*Mev|=SXwQm^wbv9_SD)xB8irOfu_(HO2H1BwAtxo zoh04tZ+3R4hngVvBz=w#A1}P`rvQwa`K{-a#I@~9{xVtk+q~7;I^(%>TtxTUw*Q;ySw|v zh2IvGnkre2m&kwkx^ZayP|wWu>6j)3<@gHGYmLN_FFt#Detv$J5JEIZ$44o@R|cR* z?ZPxo+S}VvK34!p(?KqmL)UdiK0ZLbUI(B*^DfC`5+OuW09clVWm){2L?Xf9;2{5H zU|`@GSF6>8`i%Xb*tSijQb|5nP!vV@zK>~|YtI#=C<=u_0mpH63xK7iB^<}uKIrf7 z2Vik=aoeTSX$-?)Zf*`KB>=jvlh5br>gw`D7>4SFA1{)+W+8}i$GlDE>kOXf@yoGk zx;I|J7e2|+MGEg1IQQ*&-Yh+R!t}lwI*)Y{hM}tEa=E@o_dWA1_w5~NndL8WPfp$b z>Bj8r?8DxNec3nG2Gn>M=Tc?r?zQQwGp*(1o*}Ogdm*s6%H`E7H?RD>zP`So7K_C( z0_Emb1+A%7T-P1a=j}(i=F#!-N!Rkq~SW?g; zFXpxaL8H--zv|bkx7z+#m1-dBid+M5;`ignExQCzRjakF&?j~#7Ol)^-ogL?002ov JPDHLkV1mQ1i0l9W literal 0 HcmV?d00001 diff --git a/MoveToLayer/Move2LayerDlg.py b/MoveToLayer/Move2LayerDlg.py new file mode 100644 index 0000000..08838fe --- /dev/null +++ b/MoveToLayer/Move2LayerDlg.py @@ -0,0 +1,75 @@ +# -*- coding: utf-8 -*- + +########################################################################### +## Python code generated with wxFormBuilder (version Oct 26 2018) +## http://www.wxformbuilder.org/ +## +## PLEASE DO *NOT* EDIT THIS FILE! +########################################################################### + +import wx +import wx.xrc + +########################################################################### +## Class Move2LayerDlg +########################################################################### + +class Move2LayerDlg ( wx.Dialog ): + + def __init__( self, parent ): + wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = u"Move to Layer", pos = wx.DefaultPosition, size = wx.Size( 390,180 ), style = wx.CAPTION|wx.CLOSE_BOX|wx.DEFAULT_DIALOG_STYLE|wx.RESIZE_BORDER ) + + self.SetSizeHints( wx.DefaultSize, wx.DefaultSize ) + + bSizer3 = wx.BoxSizer( wx.VERTICAL ) + + self.m_comment = wx.StaticText( self, wx.ID_ANY, u"Select Objects to Move to Layer\n", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_comment.Wrap( -1 ) + + bSizer3.Add( self.m_comment, 0, wx.ALL|wx.EXPAND, 5 ) + + bSizer31 = wx.BoxSizer( wx.HORIZONTAL ) + + self.m_staticTextLayer = wx.StaticText( self, wx.ID_ANY, u"Layer", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticTextLayer.Wrap( -1 ) + + bSizer31.Add( self.m_staticTextLayer, 1, wx.ALL|wx.EXPAND, 5 ) + + m_comboBoxLayerChoices = [] + self.m_comboBoxLayer = wx.ComboBox( self, wx.ID_ANY, u"Combo!", wx.DefaultPosition, wx.DefaultSize, m_comboBoxLayerChoices, 0 ) + bSizer31.Add( self.m_comboBoxLayer, 0, wx.ALL, 5 ) + + + bSizer3.Add( bSizer31, 0, 0, 5 ) + + self.m_staticline1 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) + bSizer3.Add( self.m_staticline1, 0, wx.EXPAND |wx.ALL, 5 ) + + bSizer1 = wx.BoxSizer( wx.HORIZONTAL ) + + self.m_staticText101 = wx.StaticText( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText101.Wrap( -1 ) + + bSizer1.Add( self.m_staticText101, 1, wx.ALL|wx.EXPAND|wx.ALIGN_CENTER_VERTICAL, 5 ) + + self.m_buttonOK = wx.Button( self, wx.ID_OK, u"Apply", wx.DefaultPosition, wx.DefaultSize, 0 ) + + self.m_buttonOK.SetDefault() + bSizer1.Add( self.m_buttonOK, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + + self.m_buttonCancel = wx.Button( self, wx.ID_CANCEL, u"Cancel", wx.DefaultPosition, wx.DefaultSize, 0 ) + bSizer1.Add( self.m_buttonCancel, 0, wx.ALL|wx.ALIGN_CENTER_VERTICAL, 5 ) + + + bSizer3.Add( bSizer1, 0, wx.ALIGN_RIGHT|wx.EXPAND, 5 ) + + + self.SetSizer( bSizer3 ) + self.Layout() + + self.Centre( wx.BOTH ) + + def __del__( self ): + pass + + diff --git a/MoveToLayer/__init__.py b/MoveToLayer/__init__.py new file mode 100644 index 0000000..fd5fc58 --- /dev/null +++ b/MoveToLayer/__init__.py @@ -0,0 +1,2 @@ +from .move_to_layer import move_to_draw_layer +move_to_draw_layer().register() diff --git a/MoveToLayer/__pycache__/Move2LayerDlg.cpython-36.pyc b/MoveToLayer/__pycache__/Move2LayerDlg.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..34b0d7f221135865dda61ece8f49e1770fa6a8f0 GIT binary patch literal 1928 zcmZuyOK;mo5MDk-$(CiuuhdmtrAgBYMeI639|(#fl*CvAmOvh#vRalm9|5?d(!g6sIf9?Ck8!H;>(=9u$j(-M{|)@v{O#f1{-# zgZv06xeJUSA_U0JX2C|sh=@dbjfmu-zBrX^i3nX(m9M}SSuW%AA%gG-C@BN;kS!2o zi$oyeYXn{rkztmJ0^TYuei!}X-8Y>}FKz^9E^}o=1FU#R-}SAVFy0 zGLXWOnu;K)K)D*T(>Tx95Rre&321`)1<-FsPLQTbTdPk`@{U+R003JStWbie-rdK z{-+Ywi&-Tgy$`fpOBre@LoFp;#r^Du7ez-@hTOrzXM*2(}IW}Peh(K>fhy&QU4RL_>}1gf-AXhjrpCd zq)~-mOaE5^p{qaOB32bD`h+T}p9UV4hEYQ0#2JY zFOUi`AfYSH6p*OG1B}&r6rM)6Y(*aqgG;K`oq_8GSO3C@i(toDwMxa87gTNdP7s|@ z4P$@crx;VU)xf&-oR%8i3uhFhCsE?3el(zRm*tkL>nFWd+oJip+3p&+)_z8}8pdP& zsoBGgmTtC>akux}G-$D7bXzvF=roKDUF~`)LumSgG@&aqbdIUjq6;R(RQulO^jdY@ zq=jzkq`uqpE>b%mVmAsyZ;(>?hu9fXNhbuDX;RgAcA{GiT4=UAExT>?bkp7lak6mc zEuk83352mr9P2a!Iop~AGW~%^i)IVI0iwk@Pym}pbj1jV>18*K{lOUpoHs#^K0l({ zy4gCmaNV$aMh7qIP-~+!h3+ZP*7g&-5aLs=Xg`4xy>!+FnuqPWZq*IbUYna_=SEjD z;7;qf*`wO5i!oIp{b-cJ@j_o>kIJ=nuL)m3Iq`xQR2@1o>sRiSH}-+8&4eR+=UNBq z=aKJv$=vklcNBjKx8$GZ;dvB#hoLh%Jow3X9fF6!=*%A^hkSLh<*jKQmIgE)HVBCa7!v8LW z@%HsN+`@wQ3eRNVgdRMT0>)uPMgfyoG5&ev1lbO+HvanUP^_Ubix;yH8$b!mPZPB- nxTY${Yp|y3%Zr1H*o{lD;2TTnE-mUadS9#`0|UcjAcg}bAj<)Wi=}`>3PTEG4nrxyr<<5ul9`{UTaurjQ>^a` pv@9gw2V{$We0*kJW=VX!UP0w84x8Nkl+v73JCLi2LALQQ0s!F1KFt6C literal 0 HcmV?d00001 diff --git a/MoveToLayer/__pycache__/move_to_layer.cpython-36.pyc b/MoveToLayer/__pycache__/move_to_layer.cpython-36.pyc new file mode 100644 index 0000000000000000000000000000000000000000..4c18bdda4eabdcc3b87b79fe8a70d4491472e995 GIT binary patch literal 3209 zcmai0OOG4J5$>KBhePhlSlfEoKrk?3OQS?w?b=9SLlGqHdL_f!jSwmbLcnMYr+dkv zhBNE#rl=*jCkpU6_xyqUiTsHmm%irYe-P-DsveU2h=L4J#p*|OS66>kU0-f&MDPCj z_a8pIPsqQ>h0g=|5s)f?al&aqdi6i;QD{2_eOb4io&#;SaK~QHqjjI(^SRdxxX**4 zF%EknB~J-&@bE3+p$JFe!tFJor^zGei3A-*(9`0yOOn>lup>E2sG)a(U3+o*?YJ*E z7yP?%IuJ8|r?cDH`I*8b7~VLnUW?u5>8X(W#UMk~)#ok+^-B;0L9=_#Pvn?#%AL1i z&*d)n-jbg86Y)sThpm0HdA=OO^;T=(|B6%x8k&q?omWKOoRg6=r*miFP*QGbS9^1^ za7O;Bj}utgIhwg&b?zsDai(ufi?P>2s=O*0Gn-wZyZKB!%S)}gGvlktiI6gYO-%dZ zSmGK_iQ>5Ds@V>*T{wS}v8+gyV(f3^-LvgsH5S|Bbh6!fm1ikuC&grtmulO#?xmTA zJ@4tND%3Wf^kro|w><`#>53KT$Ii*AY{Bkd04X$s(1>n2vrjMkrgPD@%)xo^t(>$6 zvbqBdoJ|UH=4d*i;3zVu=qPZS3*;@xf+A+Di=@3%Up7EZ`oK)cSYGBDycEH1hk1#P zGL5IT#}ZezaaM4WN>S=sZxfX*yN1#Sz;=O_2MO^YH?ACHwF%q~Y*?mafg3V5uK1)t zxy{&*leDNutZ(ePW@G96OcVuU^{%*eLgfZ-ZY@9$eufrcNQGXzZLcMTByX`|uCp1deK$t<`;TnNI6VOCj_~6vOv8R+-FCbgZiw4;ptxAu=s^ zybrVUa&Si_IF=17CCa!I)7XL_zLUOA^CImRBCg8fG|tQT>Eq`~Y*&3&O;Ks0#5cBm zFGd@%3+60)0GzNCS{7IR5p<~KaF!NDtcN15L3vpT`_UAOz)M45_{GKIX%5Tu zh25W{0hiS>1QCAdR79gi;v*0+>;Z|CyPDnr1Tu$1s4|!Pz`Z#g`J=${h7K2$2Ma(e zYw`y0h&$l;aQ1I&8G89p=wStxiQ{xqXcdnqN~2X$Kv!N0zMhF8+d=5WYFJGQjJ;DtUTEYu$aPtaN-TG+qf$F6(kpLjZ#Pf_fTf)%Zk;p(s@j zcXwgdT0M@dK04RkASi;FdDOzT?corR2JU>2-U+g`ZeSH~? z|4SLb7pG+`-sDQd;#T#8j!jX!%luRhUM4N$R0<>TJe4xzp`T5-a*}CXIHU1SQav=@ zsLD&z;JK{lGIpHXAm7Y0$j7!}M?Qq>nnF^nihe3tf1-6&CXUTG@)p49Jlt)Zn{b7N z?_p43Vj^J(nKOXjO*;GZzhkzu(pY0Q0;M%(XVBmj@M@C)WKei=7UT@jyMQMLq-9#r zF6PmF$fP{DMb3Qf&;12G3+BNnSP;my!KeYV8}kOt3|~32ubVp3E&E`>t9%yn@T@s+ z@}_RzAipJNO`DISql2|w7*m+hxiL4K)LVgbBOMKhpZDjaH$pqP5WWPf8Yh?G3;ZrZS zkwJlk^#*-&_1*(D&2=_pFs?oUMjVg6?-$b97w^HqA0}nzK~X_+R1Z7#Qfs$k3d2{! zy8SZ{29UOX_~eX2nj!!K3j_`qFCl&X#re^LBt_*8OoUn?gje!qt_xxOqMCwtBc)TR zaf@N%7^ibz;=_35IPand1q2>uiiiJzVTCpXWOn!enSQQjbA=(Zk8h(6<^~(KAXLTe zXtquczl)O%YpgZ(NtH@&T43}6#N!dfv3<|Y79OKIc`jay;!khfb`*U``hW4y|s;=syQK0|h1_MF!~H(}rrkga{f zrsss$B=#lkx>Q1-n4q63@td#AN7FZ*BJX#^ve?eC*p`@#>THL`%de4PWwtC&AKQZR jg)K7e((kjwgb(UU>LD7hwt&?q4`~}b;5lt?>lgn4-p)m^ literal 0 HcmV?d00001 diff --git a/MoveToLayer/move2layer.png b/MoveToLayer/move2layer.png new file mode 100644 index 0000000000000000000000000000000000000000..3e5b8855a650ff7c2023ea8d0abf0b21d899f4c7 GIT binary patch literal 1501 zcmV<31tR*1P)wC#_Lw$|Gu{Q3M$u zZDP8yP1=PRqtT?*MaA(twM}YFT{LM->VkAtA6+OQ(4f#v8Wl)PV}Z)l6e?kW8F^G@ zhM9ZsIo*_zaRy}U!j0eR=Kufmf8YJjIro0RYrJBRgwgDMshLG<`_r=Ch{*^38RN8Z z&xzVS~oQ)+G06^56s5Ny3G&KUs81!Z8^Q7-@7$>@6 zGU>sBBRF?-2SkBKBKZA&+`M@+q9{s-rfC-dsIxhaC%iy z=&ohM1OC3whYt(|E7$HMgyZ6xPY!ltf4L6pin8Kzlu~qcb;0BDz~OK}k|ZMlBLD;f z0W>!^2SrhAR}|&_=u6K&$g-Rz2trdzN{Zc>X*c|M-;A{U@>ufWK9oxX_@u@jM;;D` z(caz;LI}2P+Xkc27|nUTUbM8d%xIeS9i{ZREX#B@7o7mdabK&dx?y5s0uvLzB6Wg; zZ@_?z)$0KOPoF#n8@K_E%%Hx$Gfn^@1a`X}<>lqz=1i`)x0l|zb0iHp0nfj|I_jg1%?9nt#w`y!gAu^3WD9yi`-v;hge z11^{AUB=jTyWMUoEiDyyXcVED$fC);UN4$jo6se8;dah#p5-v(f26SeZqu?xvOLBD z4S&T6ICJLAdB)hM7K}h{~yCzEh(_6!-!@)Th_OC;Fms%d`b$ z?y{g55nBaS*x%655S+U%I#?AP_(x5C8!9{r-}v>8WGGD~I7-;U!=IVs`Tx zn85HT^AW;aR68=gnJDQj2@^`bY`Wg`RibtC9cfbYUd`u}(7NujZi7$q%~LiH7*Nz- zM2jC5YlXRm?P+Og-k9`4exSO#+6n`%hD@Q2cPs81g681!B@04O1eA7{A~%xD?fGC2 zXSG@@XLB#~1G{(cE>=19=P_way8q67gT`w~kz1xM*nDp@*5$9mfsYSBr+fA|z|d`qcVVH&`&3wBZmH5%zvN_B!^Wsu}UM_EmP2hH4oMR064@N=34FY>57R0*5S7L6tC2j)VSztWP?0r00000NkvXXu0mjf DlcLO* literal 0 HcmV?d00001 diff --git a/MoveToLayer/move2layer.svg b/MoveToLayer/move2layer.svg new file mode 100644 index 0000000..efa0bd3 --- /dev/null +++ b/MoveToLayer/move2layer.svg @@ -0,0 +1,200 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/action_menu_move_to_layer.py b/MoveToLayer/move_to_layer (copy).py similarity index 81% rename from action_menu_move_to_layer.py rename to MoveToLayer/move_to_layer (copy).py index 75bd3b2..dc31ad2 100644 --- a/action_menu_move_to_layer.py +++ b/MoveToLayer/move_to_layer (copy).py @@ -24,7 +24,8 @@ import pcbnew from pcbnew import * import base64 from wx.lib.embeddedimage import PyEmbeddedImage -___version___="1.1.3" +import os +___version___="1.2.0" class move_to_draw_layer( pcbnew.ActionPlugin ): @@ -46,9 +47,12 @@ class move_to_draw_layer( pcbnew.ActionPlugin ): self.description should be a comprehensive description of the plugin """ - self.name = "Move Selected drawings to chosen Layer" - self.category = "Modify Drawing PCB" + import os + self.name = "Move Selected drawings to chosen Layer\nversion "+___version___ + self.category = "Modify PCB" self.description = "Move Selected drawings to chosen Layer on an existing PCB" + self.icon_file_name = os.path.join(os.path.dirname(__file__), "./move2layer.png") + self.show_toolbar_button = True def Run( self ): found_selected=False @@ -69,8 +73,6 @@ class move_to_draw_layer( pcbnew.ActionPlugin ): 'B_Fab' : pcbnew.B_Fab, 'F_SilkS' : pcbnew.F_SilkS, 'B_SilkS' : pcbnew.B_SilkS, - 'F_Mask' : pcbnew.F_Mask, - 'B_Mask' : pcbnew.B_Mask, }[x] class displayDialog(wx.Dialog): @@ -98,8 +100,7 @@ class move_to_draw_layer( pcbnew.ActionPlugin ): self.ct = 0 self.layerSelection = "Edge_Cuts" - layerList = ["Edge_Cuts", "Eco1_User", "Eco2_User", "Dwgs_User", "Cmts_User",\ - "Margin", "F_CrtYd", "B_CrtYd", "F_Fab", "B_Fab", "F_SilkS", "B_SilkS", "F_Mask", "B_Mask"] + layerList = ["Edge_Cuts", "Eco1_User", "Eco2_User", "Dwgs_User", "Cmts_User", "Margin", "F_CrtYd", "B_CrtYd", "F_Fab", "B_Fab", "F_SilkS", "B_SilkS"] self.combo = wx.ComboBox(self.panel, choices=layerList) self.combo.SetSelection(0) @@ -145,7 +146,7 @@ class move_to_draw_layer( pcbnew.ActionPlugin ): def OnClose(self,e): #wx.LogMessage("c") e.Skip() - self.Destroy() #Close() + self.Close() #self.result.SetLabel(msg) # Set event handlers #self.button.Bind(wx.EVT_BUTTON, self.OnButton) @@ -175,37 +176,45 @@ class move_to_draw_layer( pcbnew.ActionPlugin ): LogMsg='' msg="'move to layer tool'\n" msg+="version = "+___version___ - frame = displayDialog(None) - #frame = wx.Frame(None) - frame.Center() - #frame.setMsg(LogMsg) - frame.ShowModal() - frame.Destroy() + frame = displayDialog(None) + #frame = wx.Frame(None) + frame.Center() + #frame.setMsg(LogMsg) + frame.ShowModal() + #dlg.Destroy() + frame.Destroy() - #dlg=wx.MessageBox( 'Changing Layer for Selected?', 'Confirm', wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION ) - dlg=wx.MessageBox( 'Changing Layer for Selected '+frame.layerSelection+ '?', 'Confirm', wx.YES_NO | wx.NO_DEFAULT | wx.ICON_WARNING ) - if dlg == wx.YES: - #wx.LogMessage("YES") - #wx.LogMessage(str(board.IsModified())) - #board.SetModified() - #wx.LogMessage(str(board.IsModified())) - for drw in board.GetDrawings(): - if drw.IsSelected(): - drw.SetLayer(switch(frame.layerSelection)) - found_selected=True - - if found_selected!=True: - LogMsg="select lines to be moved to new layer\n" - LogMsg+="use GAL for selecting lines" - wx.LogMessage(LogMsg) - else: - pcbnew.Refresh() - LogMsg="selected drawings moved to "+frame.layerSelection+" layer" - wx.LogMessage(LogMsg) + #dlg=wx.MessageBox( 'Changing Layer for Selected?', 'Confirm', wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION ) + dlg=wx.MessageBox( 'Changing Layer for Selected '+frame.layerSelection+ '?', 'Confirm', wx.YES_NO | wx.NO_DEFAULT | wx.ICON_WARNING ) + if dlg == wx.YES: + #wx.LogMessage("YES") + #wx.LogMessage(str(board.IsModified())) + #board.SetModified() + #wx.LogMessage(str(board.IsModified())) + #try: + # board_drawings=board.GetDrawings() + #except: + # board_drawings=board.DrawingsList() + # + #for drw in board_drawings: + for drw in board.GetDrawings(): + if drw.IsSelected(): + drw.SetLayer(switch(frame.layerSelection)) + found_selected=True + + if found_selected!=True: + LogMsg="select lines to be moved to new layer\n" + LogMsg+="use GAL for selecting lines" + wx.LogMessage(LogMsg) + else: + pcbnew.Refresh() + LogMsg="selected drawings moved to "+frame.layerSelection+" layer" + wx.LogMessage(LogMsg) + -move_to_draw_layer().register() +#move_to_draw_layer().register() # "b64_data" is a variable containing your base64 encoded jpeg diff --git a/MoveToLayer/move_to_layer.py b/MoveToLayer/move_to_layer.py new file mode 100644 index 0000000..f36106f --- /dev/null +++ b/MoveToLayer/move_to_layer.py @@ -0,0 +1,288 @@ +# move_to_edge_cuts.py +# +# Copyright (C) 2017 KiCad Developers, see CHANGELOG.TXT for contributors. +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301, USA. +# +# + +import wx +import pcbnew +from pcbnew import * +import base64 +from wx.lib.embeddedimage import PyEmbeddedImage +import os +___version___="1.2.1" + +from . import Move2LayerDlg + +# Python plugin stuff + +class Move2Layer_Dlg(Move2LayerDlg.Move2LayerDlg): + # from https://github.com/MitjaNemec/Kicad_action_plugins + # hack for new wxFormBuilder generating code incompatible with old wxPython + # noinspection PyMethodOverriding + def SetSizeHints(self, sz1, sz2): + if wx.__version__ < '4.0': + self.SetSizeHintsSz(sz1, sz2) + else: + super(Move2Layer_Dlg, self).SetSizeHints(sz1, sz2) + + #def onApplyClick(self, event): + # return self.EndModal(wx.ID_OK) + # + #def onCancelClick(self, event): + # return self.EndModal(wx.ID_CANCEL) + + def __init__(self, parent): + import wx + Move2LayerDlg.Move2LayerDlg.__init__(self, parent) + #self.GetSizer().Fit(self) + self.SetMinSize(self.GetSize()) + # self.m_buttonDelete.Bind(wx.EVT_BUTTON, self.onDeleteClick) + # self.m_buttonReconnect.Bind(wx.EVT_BUTTON, self.onConnectClick) + # if wx.__version__ < '4.0': + # self.m_buttonReconnect.SetToolTipString( u"Select two converging Tracks to re-connect them\nor Select tracks including one round corner to be straighten" ) + # self.m_buttonRound.SetToolTipString( u"Select two connected Tracks to round the corner\nThen choose distance from intersection and the number of segments" ) + # else: + # self.m_buttonReconnect.SetToolTip( u"Select two converging Tracks to re-connect them\nor Select tracks including one round corner to be straighten" ) + # self.m_buttonRound.SetToolTip( u"Select two connected Tracks to round the corner\nThen choose distance from intersection and the number of segments" ) +# +class move_to_draw_layer( pcbnew.ActionPlugin ): + """ + A script to Move Selected Drawing(s) to chosen new Layer (available only in GAL) + How to use: + - move to GAL + - select some draw objects + - call the plugin + - select the new layer + - selected draw objects will be moved to new layer + """ + + def defaults( self ): + """ + Method defaults must be redefined + self.name should be the menu label to use + self.category should be the category (not yet used) + self.description should be a comprehensive description + of the plugin + """ + import os + self.name = "Move Selected drawings to chosen Layer\nversion "+___version___ + self.category = "Modify PCB" + self.description = "Move Selected drawings to chosen Layer on an existing PCB" + self.icon_file_name = os.path.join(os.path.dirname(__file__), "./move2layer.png") + self.show_toolbar_button = True + + def Run( self ): + found_selected=False + #wx.MessageDialog(self.frame,"ciao") + #subprocess.check_call(["C:\pathToYourProgram\yourProgram.exe", "your", "arguments", "comma", "separated"]) + #http://stackoverflow.com/questions/1811691/running-an-outside-program-executable-in-python + def switch(x): + return { + 'Edge_Cuts': pcbnew.Edge_Cuts, + 'Eco1_User': pcbnew.Eco1_User, + 'Eco2_User': pcbnew.Eco2_User, + 'Dwgs_User': pcbnew.Dwgs_User, + 'Cmts_User': pcbnew.Cmts_User, + 'Margin' : pcbnew.Margin, + 'F_CrtYd' : pcbnew.F_CrtYd, + 'B_CrtYd' : pcbnew.B_CrtYd, + 'F_Fab' : pcbnew.F_Fab, + 'B_Fab' : pcbnew.B_Fab, + 'F_SilkS' : pcbnew.F_SilkS, + 'B_SilkS' : pcbnew.B_SilkS, + }[x] + + # class displayDialog(wx.Dialog): + # """ + # The default frame + # http://stackoverflow.com/questions/3566603/how-do-i-make-wx-textctrl-multi-line-text-update-smoothly + # """ + # + # #---------------------------------------------------------------------- + # #def __init__(self): + # # """Constructor""" + # # wx.Frame.__init__(self, None, title="Display Frame", style=wx.DEFAULT_FRAME_STYLE, wx.ICON_INFORMATION) + # # panel = wx.Panel(self) + # def __init__(self, parent): + # wx.Dialog.__init__(self, parent, id=-1, title="Move to Layer")# + # #, style=wx.DEFAULT_DIALOG_STYLE, wx.ICON_INFORMATION) + # #, style=wx.DEFAULT_DIALOG_STYLE, wx.ICON_INFORMATION) + # #, pos=DefaultPosition, size=DefaultSize, style = wx.DEFAULT_FRAME_STYLE & (~wx.MAXIMIZE_BOX), name="fname") + # #, wx.ICON_INFORMATION) #, title="Annular Check", style=wx.DEFAULT_FRAME_STYLE, wx.ICON_INFORMATION) + # # + # + # self.SetIcon(PyEmbeddedImage(move_to_layer_ico_b64_data).GetIcon()) + # #wx.IconFromBitmap(wx.Bitmap("icon.ico", wx.BITMAP_TYPE_ANY))) + # self.panel = wx.Panel(self) + # + # self.ct = 0 + # self.layerSelection = "Edge_Cuts" + # layerList = ["Edge_Cuts", "Eco1_User", "Eco2_User", "Dwgs_User", "Cmts_User", "Margin", "F_CrtYd", "B_CrtYd", "F_Fab", "B_Fab", "F_SilkS", "B_SilkS"] + # self.combo = wx.ComboBox(self.panel, choices=layerList) + # self.combo.SetSelection(0) + # + # self.combo.Bind(wx.EVT_COMBOBOX, self.onCombo) + # + # self.title = wx.StaticText(self.panel, label="Move to Layer:") + # #self.result = wx.StaticText(self.panel, label="") + # #self.result.SetForegroundColour('#FF0000') + # #self.button = wx.Button(self.panel, label="Save") + # #self.lblname = wx.StaticText(self.panel, label="Your name:") + # #self.editname = wx.TextCtrl(self.panel, size=(140, -1)) + # ##self.editname = wx.TextCtrl(self.panel, size = (300, 400), style = wx.TE_MULTILINE|wx.TE_READONLY) + # + # + # # Set sizer for the frame, so we can change frame size to match widgets + # self.windowSizer = wx.BoxSizer() + # self.windowSizer.Add(self.panel, 1, wx.ALL | wx.EXPAND) + # + # # Set sizer for the panel content + # self.sizer = wx.GridBagSizer(5, 0) + # self.sizer.Add(self.title, (0, 0)) + # self.button = wx.Button(self.panel, label="OK") + # self.sizer.Add(self.button, (2, 0), (1, 2), flag=wx.EXPAND) + # #self.sizer.Add(self.result, (1, 0)) + # #self.sizer.Add(self.lblname, (1, 0)) + # ##self.sizer.Add(self.editname, (1, 0)) + # self.sizer.Add(self.combo, (1, 0)) + # #self.sizer.Add(self.button, (2, 0), (1, 2), flag=wx.EXPAND) + # + # # Set simple sizer for a nice border + # self.border = wx.BoxSizer() + # self.border.Add(self.sizer, 1, wx.ALL | wx.EXPAND, 5) + # + # # Use the sizers + # self.panel.SetSizerAndFit(self.border) + # self.SetSizerAndFit(self.windowSizer) + # + # #self.button = wx.Button(self.panel, label="Close") + # self.button.Bind(wx.EVT_BUTTON, self.OnClose) + # self.Bind(wx.EVT_CLOSE,self.OnClose) + # + # #---------------------------------------------------------------------- + # def OnClose(self,e): + # #wx.LogMessage("c") + # e.Skip() + # self.Close() + # #self.result.SetLabel(msg) + # # Set event handlers + # #self.button.Bind(wx.EVT_BUTTON, self.OnButton) + # #self.Show() + # #self.Bind(wx.EVT_CLOSE,self.OnClose) + # def onCombo(self, event): + # """ + # """ + # self.layerSelection = self.combo.GetValue() + # + # #def OnClose(self,e): + # # #wx.LogMessage("c") + # # e.Skip() + # #self.Close() + # + # #def OnButton(self, e): + # # self.result.SetLabel(self.editname.GetValue()) + # #def setMsg(self, t_msg): + # # self.editname.SetValue(t_msg) + + board = pcbnew.GetBoard() + #wx.MessageDialog(None, 'This is a message box.', 'Test', wx.OK | wx.ICON_INFORMATION).ShowModal() + fileName = GetBoard().GetFileName() + if 0: #len(fileName) == 0: + wx.LogMessage("A board needs to be saved/loaded\nto run the plugin!") + else: + #from https://github.com/MitjaNemec/Kicad_action_plugins + #hack wxFormBuilder py2/py3 + _pcbnew_frame = [x for x in wx.GetTopLevelWindows() if x.GetTitle().lower().startswith('pcbnew')][0] + aParameters = Move2Layer_Dlg(_pcbnew_frame) + aParameters.Show() + modal_result = aParameters.ShowModal() + if modal_result == wx.ID_OK: + MoveToLayer(pcb, Layer) + else: + None # Cancel + + LogMsg='' + msg="'move to layer tool'\n" + msg+="version = "+___version___ + #wx.LogMessage(LogMsg) + + # frame = displayDialog(None) + # #frame = wx.Frame(None) + # frame.Center() + # #frame.setMsg(LogMsg) + # frame.ShowModal() + # #dlg.Destroy() + # frame.Destroy() + + #dlg=wx.MessageBox( 'Changing Layer for Selected?', 'Confirm', wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION ) + #dlg=wx.MessageBox( 'Changing Layer for Selected '+frame.layerSelection+ '?', 'Confirm', wx.YES_NO | wx.NO_DEFAULT | wx.ICON_WARNING ) + #if dlg == wx.YES: + # #wx.LogMessage("YES") + # #wx.LogMessage(str(board.IsModified())) + # #board.SetModified() + # #wx.LogMessage(str(board.IsModified())) + # #try: + # # board_drawings=board.GetDrawings() + # #except: + # # board_drawings=board.DrawingsList() + # # + # #for drw in board_drawings: + # for drw in board.GetDrawings(): + # if drw.IsSelected(): + # drw.SetLayer(switch(frame.layerSelection)) + # found_selected=True + # + # if found_selected!=True: + # LogMsg="select lines to be moved to new layer\n" + # LogMsg+="use GAL for selecting lines" + # wx.LogMessage(LogMsg) + # else: + # pcbnew.Refresh() + # LogMsg="selected drawings moved to "+frame.layerSelection+" layer" + + + + + +#move_to_draw_layer().register() + +# pcbnew.F_Cu +# pcbnew.In1_Cu +# pcbnew.In2_Cu +#.. +# pcbnew.In30_Cu +# pcbnew.B_Cu +# pcbnew.B_Adhes +# pcbnew.F_Adhes +# pcbnew.B_Paste +# pcbnew.F_Paste +# pcbnew.B_SilkS +# pcbnew.F_SilkS +# pcbnew.B_Mask +# pcbnew.F_Mask +# pcbnew.Dwgs_User +# pcbnew.Cmts_User +# pcbnew.Eco1_User +# pcbnew.Eco2_User +# pcbnew.Edge_Cuts +# pcbnew.Margin +# pcbnew.B_CrtYd +# pcbnew.F_CrtYd +# pcbnew.B_Fab +# pcbnew.F_Fab diff --git a/MoveToLayer/move_to_layer_dlg-t.fbp b/MoveToLayer/move_to_layer_dlg-t.fbp new file mode 100644 index 0000000..824981f --- /dev/null +++ b/MoveToLayer/move_to_layer_dlg-t.fbp @@ -0,0 +1,1623 @@ + + + + + + Python + 1 + source_name + 0 + 0 + res + UTF-8 + connect + viafence_basedialogs + 1000 + none + + 0 + viafence_basedialogs + + .. + + 1 + 1 + 1 + 1 + UI + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + wxBOTH + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + -1,-1 + -1,-1 + MainDialogBase + + 503,567 + wxCAPTION|wxCLOSE_BOX|wxRESIZE_BORDER + + Via Fence Generator + + + + + OnInitDialog + + + mainSizer + wxVERTICAL + none + + 5 + wxEXPAND + 1 + + + wxBOTH + 0 + + 0 + + gbSizer4 + wxFLEX_GROWMODE_SPECIFIED + none + 0 + + 5 + 1 + 0 + wxEXPAND + 0 + 1 + + + bSizer23 + wxVERTICAL + none + + 5 + wxALL|wxEXPAND + 1 + + wxID_ANY + Via Settings + + sbSizer2 + wxVERTICAL + 1 + none + + 5 + wxEXPAND + 1 + + 2 + wxBOTH + 1 + + 0 + + fgSizer4 + wxFLEX_GROWMODE_SPECIFIED + none + 8 + 0 + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Offset (mm): + 0 + + 0 + + + 0 + + 1 + m_staticText11 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + -1,-1 + 1 + txtViaOffset + 1 + + + protected + 1 + + Resizable + 1 + + wxTE_PROCESS_ENTER|wxTE_RIGHT + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Pitch (mm): + 0 + + 0 + + + 0 + + 1 + m_staticText21 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + txtViaPitch + 1 + + + protected + 1 + + Resizable + 1 + + wxTE_PROCESS_ENTER|wxTE_RIGHT + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Via Drill (mm): + 0 + + 0 + + + 0 + + 1 + m_staticText13 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + txtViaDrill + 1 + + + protected + 1 + + Resizable + 1 + + wxTE_PROCESS_ENTER|wxTE_RIGHT + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Via Size (mm): + 0 + + 0 + + + 0 + + 1 + m_staticText14 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + 5 + wxEXPAND|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + txtViaSize + 1 + + + protected + 1 + + Resizable + 1 + + wxTE_PROCESS_ENTER|wxTE_RIGHT + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Via Net: + 0 + + 0 + + + 0 + + 1 + m_staticText23 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + 5 + wxEXPAND|wxALL + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + lstViaNet + 1 + + + protected + 1 + + Resizable + 0 + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + 5 + 1 + 1 + wxEXPAND|wxALL + 0 + 1 + + + bSizer21 + wxVERTICAL + none + + 5 + + 0 + + 9 + protected + 0 + + + + 5 + wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + bmpViafence + 1 + + + protected + 1 + + Resizable + 1 + + + 0 + + + + + + + + + + 5 + 1 + 0 + wxALL|wxEXPAND + 1 + 1 + + wxID_ANY + Input Tracks + + sbSizer411 + wxVERTICAL + 1 + none + + 5 + wxEXPAND + 0 + + 1 + 0 + + gSizer4 + none + 3 + 0 + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Include Selected + + 0 + + + 0 + + 1 + chkIncludeSelection + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Include Drawing Lines + + 0 + + + 0 + + 1 + chkIncludeDrawing + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + 5 + wxEXPAND|wxALIGN_CENTER_VERTICAL + 1 + + 2 + wxHORIZONTAL + 1 + + 10 + + fgSizer311 + wxFLEX_GROWMODE_SPECIFIED + none + 1 + 0 + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Net(s): + + 0 + + + 0 + + 1 + chkNetFilter + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + OnNetFilterCheckBox + + + + 5 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + + 1 + + 1 + 0 + Dock + 0 + Left + 0 + + 1 + + 0 + 0 + wxID_ANY + + 0 + -1,-1 + + 0 + + 1 + txtNetFilter + 1 + + + protected + 1 + + Resizable + -1 + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + Combo! + + + + + + + + + 5 + wxEXPAND|wxALIGN_CENTER_VERTICAL + 1 + + 2 + wxHORIZONTAL + 1 + + 10 + + fgSizer31 + wxFLEX_GROWMODE_SPECIFIED + none + 1 + 0 + + 5 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Layer: + + 0 + + + 0 + + 1 + chkLayer + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + OnLayerCheckBox + + + + 5 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + + 1 + + 1 + 0 + Dock + 0 + Left + 0 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + lstLayer + 1 + + + protected + 1 + + Resizable + 0 + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + 5 + 1 + 1 + wxEXPAND|wxALL + 1 + 1 + + wxID_ANY + Output VIAs + + sbSizer4 + wxVERTICAL + 1 + none + + 5 + wxEXPAND + 1 + + 1 + 0 + + gSizer2 + none + 3 + 0 + + 5 + wxEXPAND|wxALIGN_CENTER_VERTICAL|wxRIGHT|wxLEFT + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Keep VIAs in Via Net zones only + + 0 + + + 0 + + 1 + chkSameNetZoneViasOnly + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxEXPAND|wxRIGHT|wxLEFT + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Remove VIAs violating clearance rules + + 0 + + + 0 + + 1 + chkRemoveViasWithClearanceViolation + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + 5 + wxALIGN_CENTER|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + wxID_ANY + Delete Vias + + 0 + + 0 + + + 0 + + 1 + m_buttonDelete + 1 + + + protected + 1 + + + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + + + 5 + wxEXPAND + 0 + + + bSizer5 + wxHORIZONTAL + none + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Debug Dump + + 0 + + + 0 + + 1 + chkDebugDump + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + 5 + wxEXPAND|wxALL + 0 + + 0 + 1 + 0 + 0 + 0 + 1 + 0 + 0 + + m_sdbSizer1 + protected + + + + + + + + diff --git a/MoveToLayer/move_to_layer_dlg.fbp b/MoveToLayer/move_to_layer_dlg.fbp new file mode 100644 index 0000000..cd5db6e --- /dev/null +++ b/MoveToLayer/move_to_layer_dlg.fbp @@ -0,0 +1,536 @@ + + + + + + Python + 1 + source_name + 0 + 0 + res + UTF-8 + connect + Move2LayerDlg + 1000 + none + + 0 + Move2LayerDlg + + . + + 1 + 1 + 1 + 1 + UI + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + wxBOTH + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + + Move2LayerDlg + + 390,180 + wxCAPTION|wxCLOSE_BOX|wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER + + Move to Layer + + + + + + + bSizer3 + wxVERTICAL + none + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Select Objects to Move to Layer + 0 + + 0 + + + 0 + + 1 + m_comment + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + 5 + + 0 + + + bSizer31 + wxHORIZONTAL + none + + 5 + wxALL|wxEXPAND + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Layer + 0 + + 0 + + + 0 + + 1 + m_staticTextLayer + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_comboBoxLayer + 1 + + + protected + 1 + + Resizable + -1 + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + Combo! + + + + + + + + + 5 + wxEXPAND | wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_staticline1 + 1 + + + protected + 1 + + Resizable + 1 + + wxLI_HORIZONTAL + ; ; forward_declare + 0 + + + + + + + + 5 + wxALIGN_RIGHT|wxEXPAND + 0 + + + bSizer1 + wxHORIZONTAL + none + + 5 + wxALL|wxEXPAND|wxALIGN_CENTER_VERTICAL + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + 0 + + + 0 + + 1 + m_staticText101 + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + + 1 + 0 + 0 + + 1 + + 1 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + wxID_OK + Apply + + 0 + + 0 + + + 0 + + 1 + m_buttonOK + 1 + + + protected + 1 + + + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + 5 + wxALL|wxALIGN_CENTER_VERTICAL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + + 1 + 0 + 1 + + 1 + + 0 + 0 + + Dock + 0 + Left + 1 + + 1 + + + 0 + 0 + wxID_CANCEL + Cancel + + 0 + + 0 + + + 0 + + 1 + m_buttonCancel + 1 + + + protected + 1 + + + + Resizable + 1 + + + + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + + + diff --git a/action_menu_pcb2dxf.py b/PcbToDxf/action_menu_pcb2dxf.py similarity index 100% rename from action_menu_pcb2dxf.py rename to PcbToDxf/action_menu_pcb2dxf.py diff --git a/action_menu_get3D_models.py b/action_menu_get3D_models.py deleted file mode 100644 index 3dee3c0..0000000 --- a/action_menu_get3D_models.py +++ /dev/null @@ -1,15 +0,0 @@ -#!/usr/bin/python -# -*- coding: utf-8 -*- - - -# licence GPL 2 -# copyright easyw - -## todo: -# fix extra directory creation -# add force override as option check -# add OK-Cancel buttons -# add popen to elevate process - - -print "Work In Progress"