diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..06703cd --- /dev/null +++ b/.gitignore @@ -0,0 +1,51 @@ +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files, back +*.cab +*.msi +*.msm +*.msp +*.bak + +# Windows shortcuts +*.lnk + +# Python executable +*.pyc + +# ========================= +# Operating System Files +# ========================= + +# OSX +# ========================= + +.DS_Store +.AppleDouble +.LSOverride + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk diff --git a/AnnularChecker/.gitignore b/AnnularChecker/.gitignore new file mode 100644 index 0000000..06703cd --- /dev/null +++ b/AnnularChecker/.gitignore @@ -0,0 +1,51 @@ +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files, back +*.cab +*.msi +*.msm +*.msp +*.bak + +# Windows shortcuts +*.lnk + +# Python executable +*.pyc + +# ========================= +# Operating System Files +# ========================= + +# OSX +# ========================= + +.DS_Store +.AppleDouble +.LSOverride + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk diff --git a/AnnularChecker/AnnularDlg.fbp b/AnnularChecker/AnnularDlg.fbp new file mode 100644 index 0000000..0b33042 --- /dev/null +++ b/AnnularChecker/AnnularDlg.fbp @@ -0,0 +1,870 @@ + + + + + + Python + 1 + source_name + 0 + 0 + res + UTF-8 + connect + AnnularDlg + 1000 + none + + 0 + AnnularDlg + + . + + 1 + 1 + 1 + 1 + UI + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + wxBOTH + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + + AnnularDlg + + 380,317 + wxCAPTION|wxCLOSE_BOX|wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER + + Annular Ring Checker + + + + + + + bSizer3 + wxVERTICAL + none + + 5 + wxALIGN_CENTER_HORIZONTAL|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_bitmapAR + 1 + + + protected + 1 + + Resizable + 1 + + ; ; forward_declare + 0 + + + + + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Check annular ring + 0 + + 0 + + + 0 + + 1 + m_LabelTitle + 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 + m_staticline8 + 1 + + + protected + 1 + + Resizable + 1 + + wxLI_HORIZONTAL + ; ; forward_declare + 0 + + + + + + + + 5 + wxALIGN_CENTER|wxALL + 1 + + 2 + 0 + + gSizer1 + none + 0 + 0 + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + AR Vias (mm) + 0 + + 0 + + + 0 + + 1 + m_staticTextARV + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + 5 + wxALIGN_RIGHT|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_textCtrlARV + 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 Pads (mm) + 0 + + 0 + + + 0 + + 1 + m_staticTextARP + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + 5 + wxALIGN_RIGHT|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_textCtrlARP + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + 5 + wxALL + 1 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + + 1 + + 0 + 0 + wxID_ANY + PH Drill margin (mm) + 0 + + 0 + + + 0 + + 1 + m_staticTextPHD + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + 5 + wxALIGN_RIGHT|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 0 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + + 0 + + 1 + m_textCtrlPHD + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + use drill size as finished hole size + + 0 + + + 0 + + 1 + m_checkBoxPHD + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + + + + + + + + + 5 + wxALIGN_RIGHT|wxEXPAND + 0 + + + bSizer1 + wxHORIZONTAL + none + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL + 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 + wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL + 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 + + + + + + + + + + 5 + wxEXPAND + 1 + + + bSizer4 + wxVERTICAL + none + + + + + + diff --git a/AnnularChecker/AnnularDlg.py b/AnnularChecker/AnnularDlg.py new file mode 100644 index 0000000..98a071c --- /dev/null +++ b/AnnularChecker/AnnularDlg.py @@ -0,0 +1,103 @@ +# -*- 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 AnnularDlg +########################################################################### + +class AnnularDlg ( wx.Dialog ): + + def __init__( self, parent ): + wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = u"Annular Ring Checker", pos = wx.DefaultPosition, size = wx.Size( 380,317 ), 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_bitmapAR = wx.StaticBitmap( self, wx.ID_ANY, wx.NullBitmap, wx.DefaultPosition, wx.DefaultSize, 0 ) + bSizer3.Add( self.m_bitmapAR, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALL, 5 ) + + self.m_LabelTitle = wx.StaticText( self, wx.ID_ANY, u"Check annular ring", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_LabelTitle.Wrap( -1 ) + + bSizer3.Add( self.m_LabelTitle, 0, wx.ALL|wx.EXPAND, 5 ) + + self.m_staticline8 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) + bSizer3.Add( self.m_staticline8, 0, wx.EXPAND |wx.ALL, 5 ) + + gSizer1 = wx.GridSizer( 0, 2, 0, 0 ) + + self.m_staticTextARV = wx.StaticText( self, wx.ID_ANY, u"AR Vias (mm)", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticTextARV.Wrap( -1 ) + + gSizer1.Add( self.m_staticTextARV, 0, wx.ALL, 5 ) + + self.m_textCtrlARV = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer1.Add( self.m_textCtrlARV, 0, wx.ALIGN_RIGHT|wx.ALL, 5 ) + + self.m_staticTextARP = wx.StaticText( self, wx.ID_ANY, u"AR Pads (mm)", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticTextARP.Wrap( -1 ) + + gSizer1.Add( self.m_staticTextARP, 0, wx.ALL, 5 ) + + self.m_textCtrlARP = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer1.Add( self.m_textCtrlARP, 0, wx.ALIGN_RIGHT|wx.ALL, 5 ) + + self.m_staticTextPHD = wx.StaticText( self, wx.ID_ANY, u"PH Drill margin (mm)", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticTextPHD.Wrap( -1 ) + + self.m_staticTextPHD.Enable( False ) + + gSizer1.Add( self.m_staticTextPHD, 1, wx.ALL, 5 ) + + self.m_textCtrlPHD = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_textCtrlPHD.Enable( False ) + + gSizer1.Add( self.m_textCtrlPHD, 0, wx.ALIGN_RIGHT|wx.ALL, 5 ) + + self.m_checkBoxPHD = wx.CheckBox( self, wx.ID_ANY, u"use drill size as finished hole size", wx.DefaultPosition, wx.DefaultSize, 0 ) + gSizer1.Add( self.m_checkBoxPHD, 0, wx.ALL, 5 ) + + + bSizer3.Add( gSizer1, 1, wx.ALIGN_CENTER|wx.ALL, 5 ) + + bSizer1 = wx.BoxSizer( wx.HORIZONTAL ) + + + bSizer1.Add( ( 0, 0), 1, wx.EXPAND, 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.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT|wx.ALL, 5 ) + + self.m_buttonCancel = wx.Button( self, wx.ID_CANCEL, u"Cancel", wx.DefaultPosition, wx.DefaultSize, 0 ) + bSizer1.Add( self.m_buttonCancel, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT|wx.ALL, 5 ) + + + bSizer3.Add( bSizer1, 0, wx.ALIGN_RIGHT|wx.EXPAND, 5 ) + + bSizer4 = wx.BoxSizer( wx.VERTICAL ) + + + bSizer3.Add( bSizer4, 1, wx.EXPAND, 5 ) + + + self.SetSizer( bSizer3 ) + self.Layout() + + self.Centre( wx.BOTH ) + + def __del__( self ): + pass + + diff --git a/AnnularChecker/AnnularResultDlg.fbp b/AnnularChecker/AnnularResultDlg.fbp new file mode 100644 index 0000000..5b31e95 --- /dev/null +++ b/AnnularChecker/AnnularResultDlg.fbp @@ -0,0 +1,356 @@ + + + + + + Python + 1 + source_name + 0 + 0 + res + UTF-8 + connect + AnnularResultDlg + 1000 + none + + 0 + AnnularResultDlg + + . + + 1 + 1 + 1 + 1 + UI + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + wxBOTH + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + + AnnularResultDlg + + 383,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_richTextResult + 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/AnnularResultDlg.py b/AnnularChecker/AnnularResultDlg.py new file mode 100644 index 0000000..71d10a4 --- /dev/null +++ b/AnnularChecker/AnnularResultDlg.py @@ -0,0 +1,62 @@ +# -*- 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 +import wx.richtext + +########################################################################### +## Class AnnularResultDlg +########################################################################### + +class AnnularResultDlg ( 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( 383,521 ), style = wx.DEFAULT_DIALOG_STYLE ) + + self.SetSizeHints( wx.DefaultSize, wx.DefaultSize ) + + bSizer1 = wx.BoxSizer( wx.VERTICAL ) + + bSizer2 = wx.BoxSizer( wx.VERTICAL ) + + self.m_staticTitle = wx.StaticText( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticTitle.Wrap( -1 ) + + bSizer2.Add( self.m_staticTitle, 0, wx.ALL, 5 ) + + self.m_richTextResult = 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_richTextResult.SetMinSize( wx.Size( -1,400 ) ) + + bSizer2.Add( self.m_richTextResult, 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 ) + + def __del__( self ): + pass + + 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/annular.png b/AnnularChecker/annular.png new file mode 100644 index 0000000..319fed8 Binary files /dev/null and b/AnnularChecker/annular.png differ diff --git a/AnnularChecker/annular_checker.py b/AnnularChecker/annular_checker.py new file mode 100644 index 0000000..25254e6 --- /dev/null +++ b/AnnularChecker/annular_checker.py @@ -0,0 +1,464 @@ +# -*- 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 +# + +#### plugins errors +#import pcbnew;pcbnew.GetWizardsBackTrace() + +global mm_ius, DRL_EXTRA, AR_SET, AR_SET_V, DRL_EXTRA_ius, MIN_AR_SIZE, MIN_AR_SIZE_V, found_violations, LogMsg, ___version___ + +___version___="1.6.3" + +#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 + +from . import AnnularDlg +from . import AnnularResultDlg + +sys.path.append(os.path.dirname(__file__)) + +debug = False +def wxLogDebug(msg,dbg): + """printing messages only if show is omitted or True""" + if dbg == True: + wx.LogMessage(msg) +# + +class AnnularResult_Dlg(AnnularResultDlg.AnnularResultDlg): + # 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(AnnularResult_Dlg, self).SetSizeHints(sz1, sz2) + + def onOK(self, event): + self.Destroy() + #return self.EndModal(wx.ID_OK) # if modal_result == wx.ID_OK: + + def OnClickCopy(self, event): + self.m_richTextResult.SelectAll() + self.m_richTextResult.Copy() + #global LogMsg + #copy2clip(LogMsg) + self.copy_btn.SetLabel("Text Copied") + + # def onDeleteClick(self, event): + # return self.EndModal(wx.ID_DELETE) + # + # def onConnectClick(self, event): + # return self.EndModal(wx.ID_REVERT) + + def __init__(self, parent): + import wx + AnnularResultDlg.AnnularResultDlg.__init__(self, parent) + #self.GetSizer().Fit(self) + self.SetMinSize(self.GetSize()) + #### ----- connections + # Connect Events + self.Bind(wx.EVT_BUTTON, self.onOK, self.ok_btn) + #self.ok_btn.Bind(wx.EVT_BUTTON, self.EndModal(wx.ID_OK)) + 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 onOK(self, event): + # return self.EndModal(wx.ID_OK) # if modal_result == wx.ID_OK: + + #def onConnectClick(self, event): + # return self.EndModal(wx.ID_REVERT) + + #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 Annular_Dlg(AnnularDlg.AnnularDlg): + # 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(Annular_Dlg, self).SetSizeHints(sz1, sz2) + + def __init__(self, parent): + import wx + AnnularDlg.AnnularDlg.__init__(self, parent) + #self.GetSizer().Fit(self) + self.SetMinSize(self.GetSize()) + #c1.Bind(wx.EVT_CHECKBOX, self.OntextMetric, c1) + #self.m_checkBoxPHD.Bind(wx.EVT_CHECKBOX, self.OnClickCheck, self.m_checkBoxPHD) + self.m_checkBoxPHD.Bind(wx.EVT_CHECKBOX, self.OnClickCheck) + self.m_bitmapAR.SetBitmap(wx.Bitmap(os.path.join(os.path.dirname(__file__), "./annular.png"))) + + #self.Bind(wx.EVT_CHECKBOX, self.OnClickCheck) + #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" ) + def OnClickCheck(self, event): + #self.Destroy() + if self.m_checkBoxPHD.IsChecked(): + #self.Destroy() + self.m_staticTextPHD.Enable() + self.m_textCtrlPHD.Enable() + else: + self.m_staticTextPHD.Disable() + self.m_textCtrlPHD.Disable() + + # def onDeleteClick(self, event): + # return self.EndModal(wx.ID_DELETE) + # + # def onConnectClick(self, event): + # return self.EndModal(wx.ID_REVERT) + + +# Python plugin stuff +class annular_check( pcbnew.ActionPlugin ): + """ + A script to check for annular ring violations + both for TH pads and vias + requirements: KiCAD pcbnew >= 4.0 + AR_SET minimum annular accepted for pads + AR_SET_V minimum annular accepted for vias + """ + global ___version___ + 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 checker \nversion "+___version___ + 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 ): + import sys,os + #mm_ius = 1000000.0 + _pcbnew_frame = [x for x in wx.GetTopLevelWindows() if x.GetTitle().lower().startswith('pcbnew')][0] + #aParameters = RoundTrackDlg(None) + aParameters = Annular_Dlg(_pcbnew_frame) + aParameters.m_LabelTitle.SetLabel("Check annular ring: version: "+___version___) + aParameters.m_textCtrlARP.SetToolTip( wx.ToolTip(u"Annular Ring for Pads (mm)" )) + aParameters.m_staticTextPHD.SetToolTip( wx.ToolTip(u"Drill extra margin (mm)" )) + aParameters.m_textCtrlARV.SetToolTip( wx.ToolTip(u"Annular Ring for Vias (mm)" )) + aParameters.m_staticTextARV.SetToolTip( wx.ToolTip(u"Annular Ring for Vias (mm)" )) + aParameters.m_textCtrlPHD.SetToolTip( wx.ToolTip(u"Drill extra margin (mm)" )) + aParameters.m_staticTextARP.SetToolTip( wx.ToolTip(u"Drill extra margin (mm)" )) + aParameters.m_checkBoxPHD.SetToolTip( wx.ToolTip(u"use drill size as finished hole size\nadding an extra drill margin" )) + aParameters.m_textCtrlPHD.SetValue('0.1') + aParameters.m_textCtrlARP.SetValue('0.125') + aParameters.m_textCtrlARV.SetValue('0.125') + aParameters.Show() + modal_result = aParameters.ShowModal() + #import pcbnew;pcbnew.GetWizardsBackTrace() + if modal_result == wx.ID_OK: + global mm_ius, DRL_EXTRA, AR_SET, AR_SET_V, DRL_EXTRA_ius, MIN_AR_SIZE, MIN_AR_SIZE_V + phd = float(aParameters.m_textCtrlPHD.GetValue().replace(',','.')) + ar = float(aParameters.m_textCtrlARP.GetValue().replace(',','.')) + arv = float(aParameters.m_textCtrlARV.GetValue().replace(',','.')) + if aParameters.m_checkBoxPHD.IsChecked(): + DRL_EXTRA=phd + DRL_EXTRA_ius=DRL_EXTRA * mm_ius + else: + DRL_EXTRA=0 + 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 + #snap2grid(gridSizeMM,use_grid_origin) + calculate_AR() + else: + None # Cancel + + +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) + + +def calculate_AR(): + global mm_ius, DRL_EXTRA, AR_SET, AR_SET_V, DRL_EXTRA_ius, MIN_AR_SIZE, MIN_AR_SIZE_V + 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 + _pcbnew_frame = [x for x in wx.GetTopLevelWindows() if x.GetTitle().lower().startswith('pcbnew')][0] + aResult = AnnularResult_Dlg(_pcbnew_frame) + #import pcbnew;pcbnew.GetWizardsBackTrace() + writeTxt= aResult.m_richTextResult.WriteText + rt = aResult.m_richTextResult + rt.BeginItalic() + writeTxt("'action_menu_annular_check.py'\n") + #frame.m_richText1.WriteText("'action_menu_annular_check.py'\n") + LogMsg="" + 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)") + aResult.m_staticTitle.SetLabel(" Check result: (Violations found)") + #self.title.SetForegroundColour('#FF0000') + aResult.m_staticTitle.SetBackgroundColour('#FF0000') + font = wx.Font(wx.DEFAULT, wx.DECORATIVE, wx.ITALIC, wx.BOLD) + aResult.m_staticTitle.SetFont(font) + else: + #frame.m_staticTitle = wx.StaticText(frame, label=" Annular Check result: OK") + aResult.m_staticTitle.SetLabel(" Annular Check result: OK") + aResult.m_staticTitle.SetBackgroundColour('#00FF00') + font = wx.Font(wx.DEFAULT, wx.DECORATIVE, wx.ITALIC, wx.BOLD) + aResult.m_staticTitle.SetFont(font) + + + aResult.Show() + #modal_result = aResult.ShowModal() + #if modal_result == wx.ID_OK: + # aResult.Destroy() + #if modal_result == wx.ID_OK: + # aResult.Destroy() + + +# 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() + + +# 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/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/.gitignore b/FabricationPositions/.gitignore new file mode 100644 index 0000000..06703cd --- /dev/null +++ b/FabricationPositions/.gitignore @@ -0,0 +1,51 @@ +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files, back +*.cab +*.msi +*.msm +*.msp +*.bak + +# Windows shortcuts +*.lnk + +# Python executable +*.pyc + +# ========================= +# Operating System Files +# ========================= + +# OSX +# ========================= + +.DS_Store +.AppleDouble +.LSOverride + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk diff --git a/FabricationPositions/PositionsDlg.fbp b/FabricationPositions/PositionsDlg.fbp new file mode 100644 index 0000000..6eeb1cf --- /dev/null +++ b/FabricationPositions/PositionsDlg.fbp @@ -0,0 +1,553 @@ + + + + + + Python + 1 + source_name + 0 + 0 + res + UTF-8 + connect + PositionsDlg + 1000 + none + + 0 + PositionsDlg + + . + + 1 + 1 + 1 + 1 + UI + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + wxBOTH + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + + PositionsDlg + + 499,296 + wxCAPTION|wxCLOSE_BOX|wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER + + Generating Fab Positions + + + + + + + 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 + Generating Modules Fab Positions + 0 + + 0 + + + 0 + + 1 + m_comment + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + 5 + wxALIGN_CENTER_HORIZONTAL|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_bitmapFab + 1 + + + protected + 1 + + Resizable + 1 + + ; ; forward_declare + 0 + + + + + + + + 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_staticline2 + 1 + + + protected + 1 + + Resizable + 1 + + wxLI_HORIZONTAL + ; ; forward_declare + 0 + + + + + + + + 5 + + 0 + + + bSizer31 + wxHORIZONTAL + none + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + Output DIR + 0 + + 0 + + + 0 + + 1 + m_staticText4 + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 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 + m_textCtrlDir + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + grb + + + + + + + + + 5 + wxALIGN_RIGHT|wxEXPAND + 0 + + + bSizer1 + wxHORIZONTAL + none + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL + 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 + wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL + 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 + + + + + + + + + + 5 + wxEXPAND + 1 + + + bSizer4 + wxVERTICAL + none + + + + + + diff --git a/FabricationPositions/PositionsDlg.py b/FabricationPositions/PositionsDlg.py new file mode 100644 index 0000000..36cd0c3 --- /dev/null +++ b/FabricationPositions/PositionsDlg.py @@ -0,0 +1,80 @@ +# -*- 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 PositionsDlg +########################################################################### + +class PositionsDlg ( wx.Dialog ): + + def __init__( self, parent ): + wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = u"Generating Fab Positions", pos = wx.DefaultPosition, size = wx.Size( 499,296 ), 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"Generating Modules Fab Positions", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_comment.Wrap( -1 ) + + bSizer3.Add( self.m_comment, 0, wx.ALL|wx.EXPAND, 5 ) + + self.m_bitmapFab = wx.StaticBitmap( self, wx.ID_ANY, wx.NullBitmap, wx.DefaultPosition, wx.DefaultSize, 0 ) + bSizer3.Add( self.m_bitmapFab, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.ALL, 5 ) + + self.m_staticline2 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) + bSizer3.Add( self.m_staticline2, 0, wx.EXPAND |wx.ALL, 5 ) + + bSizer31 = wx.BoxSizer( wx.HORIZONTAL ) + + self.m_staticText4 = wx.StaticText( self, wx.ID_ANY, u"Output DIR", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticText4.Wrap( -1 ) + + bSizer31.Add( self.m_staticText4, 0, wx.ALL, 5 ) + + self.m_textCtrlDir = wx.TextCtrl( self, wx.ID_ANY, u"grb", wx.DefaultPosition, wx.DefaultSize, 0 ) + bSizer31.Add( self.m_textCtrlDir, 0, wx.ALL|wx.EXPAND, 5 ) + + + bSizer3.Add( bSizer31, 0, 0, 5 ) + + bSizer1 = wx.BoxSizer( wx.HORIZONTAL ) + + + bSizer1.Add( ( 0, 0), 1, wx.EXPAND, 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.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT|wx.ALL, 5 ) + + self.m_buttonCancel = wx.Button( self, wx.ID_CANCEL, u"Cancel", wx.DefaultPosition, wx.DefaultSize, 0 ) + bSizer1.Add( self.m_buttonCancel, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT|wx.ALL, 5 ) + + + bSizer3.Add( bSizer1, 0, wx.ALIGN_RIGHT|wx.EXPAND, 5 ) + + bSizer4 = wx.BoxSizer( wx.VERTICAL ) + + + bSizer3.Add( bSizer4, 1, wx.EXPAND, 5 ) + + + self.SetSizer( bSizer3 ) + self.Layout() + + self.Centre( wx.BOTH ) + + def __del__( self ): + pass + + 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/fabrication.png b/FabricationPositions/fabrication.png new file mode 100644 index 0000000..ddad942 Binary files /dev/null and b/FabricationPositions/fabrication.png differ diff --git a/FabricationPositions/fabrication.svg b/FabricationPositions/fabrication.svg new file mode 100644 index 0000000..fead669 --- /dev/null +++ b/FabricationPositions/fabrication.svg @@ -0,0 +1,100 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/FabricationPositions/fabricationPositions-old.png b/FabricationPositions/fabricationPositions-old.png new file mode 100644 index 0000000..f3bbcdb Binary files /dev/null and b/FabricationPositions/fabricationPositions-old.png differ 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 0000000..2aeec47 Binary files /dev/null and b/FabricationPositions/fabricationPositions.png differ 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/FabricationPositions/fabrication_positions.py b/FabricationPositions/fabrication_positions.py new file mode 100644 index 0000000..98c1eb3 --- /dev/null +++ b/FabricationPositions/fabrication_positions.py @@ -0,0 +1,387 @@ +# -*- coding: utf-8 -*- +# +# A script to generate POS file for kicad_pcb +# requirements: KiCAD pcbnew >= 4.0 +# release "1.1.1" +# copyright Maurice easyw +# +# main script from https://forum.kicad.info/t/pcba-wants-all-parts-in-the-pos-file-not-just-smd/10045/6 +# + +### plugins errors +#import pcbnew +#pcbnew.GetWizardsBackTrace() + + +___version___="1.2.2" +#wx.LogMessage("My message") +#mm_ius = 1000000.0 + +import sys, os +import pcbnew +import datetime +import wx +from pcbnew import * +import base64 +from wx.lib.embeddedimage import PyEmbeddedImage + +""" +execfile ("C:/kicad-wb-1602/msys64/home/userC/out3Dm/pack-x86_64/share/kicad/scripting/plugins/getpos.py") +""" + +def generate_POS(dir): + import os + mm_ius = 1000000.0 + + my_board = pcbnew.GetBoard() + + fileName = pcbnew.GetBoard().GetFileName() + + dirpath = os.path.abspath(os.path.expanduser(fileName)) + path, fname = os.path.split(dirpath) + ext = os.path.splitext(os.path.basename(fileName))[1] + name = os.path.splitext(os.path.basename(fileName))[0] + #wx.LogMessage(dir) + #lsep=os.linesep + lsep='\n' + + if len(dir)>0: + dir = dir.rstrip('\\').rstrip('/') + if not os.path.exists(path+os.sep+dir): + #create dir + os.mkdir(path+os.sep+dir) + dir = dir+os.sep + #wx.LogMessage(dir) + else: + dir = dir+os.sep + #LogMsg1=lsep+"reading from:" + lsep + dirpath + lsep + lsep + out_filename_top_SMD=path+os.sep+dir+name+"_POS_top_SMD.txt" + out_filename_bot_SMD=path+os.sep+dir+name+"_POS_bot_SMD.txt" + out_filename_top_THD=path+os.sep+dir+name+"_POS_top_THD.txt" + out_filename_bot_THD=path+os.sep+dir+name+"_POS_bot_THD.txt" + out_filename_top_VIRTUAL=path+os.sep+dir+name+"_POS_top_Virtual.txt" + out_filename_bot_VIRTUAL=path+os.sep+dir+name+"_POS_bot_Virtual.txt" + out_filename_ALL=path+os.sep+dir+name+"_POS_All.txt" + + Header_1="### Module positions - created on " + datetime.datetime.now().strftime("%Y-%m-%d %H:%M")+lsep + Header_1+="### Printed by pcb_positions plugin"+lsep + Header_1+="## Unit = mm, Angle = deg."+lsep + #LogMsg+="## Side : All"+lsep + Header_2="## Board Aux Origin: " + str(my_board.GetAuxOrigin())+lsep + + Header_2+="{0:<10}".format("# Ref")+"{0:<20}".format("Val")+"{0:<20}".format("Package")+\ + "{0:<11}".format("PosX")+"{0:<11}".format("PosY")+"{0:<8}".format(" Rot")+\ + "{0:<10}".format(" Side")+" Type"+lsep + content_top_SMD='' + content_bot_SMD='' + content_top_THD='' + content_bot_THD='' + content_top_VIRTUAL='' + content_bot_VIRTUAL='' + content_ALL='' + SMD_pads = 0 + TH_pads = 0 + Virt_pads = 0 + TH_top_cnt = 0 + TH_bot_cnt = 0 + SMD_top_cnt = 0 + SMD_bot_cnt = 0 + Virt_top_cnt = 0 + Virt_bot_cnt = 0 + + bb = my_board.GetBoardEdgesBoundingBox() + pcb_height = bb.GetHeight() / mm_ius + pcb_width = bb.GetWidth() / mm_ius + #to add relative position to + #print ("Board Aux Origin: " + str(my_board.GetAuxOrigin())) + + #'{0:<10} {1:<10} {2:<10}'.format(1.0, 2.2, 4.4)) + + tracks = my_board.GetTracks() + vias = [] + for via in tracks: + if type(via) is pcbnew.VIA: + vias.append(via) + vias_cnt = len(vias) + + for module in my_board.GetModules(): + #print ("%s \"%s\" %s %1.3f %1.3f %1.3f %s" % ( module.GetReference(), + #Nchars = 20 + # RefL = 10; ValL = 20 + + md="" + if module.GetAttributes() == 0: # PTH=0, SMD=1, Virtual = 2 + md = "THD" + TH_pads+=module.GetPadCount() + elif module.GetAttributes() == 1: + md = "SMD" + SMD_pads+=module.GetPadCount() + else: + md = "VIRTUAL" + Virt_pads+=module.GetPadCount() + + #Reference=str(module.GetReference()).ljust(Nchars/2) + #Value=str(module.GetValue()).ljust(Nchars) + #Package=str(module.GetFPID().GetLibItemName()).ljust(Nchars) + #X_POS=str(pcbnew.ToMM(module.GetPosition().x - my_board.GetAuxOrigin().x )) + #Y_POS=str(-1*pcbnew.ToMM(module.GetPosition().y - my_board.GetAuxOrigin().y)) + #Rotation=str(module.GetOrientation()/10) + #Layer="top".ljust(Nchars) if module.GetLayer() == 0 else "bottom".ljust(Nchars) + #Type=md + Reference="{0:<10}".format(str(module.GetReference())) + Value = str(module.GetValue()) + Value=(Value[:17] + '..') if len(Value) > 19 else Value + Value="{0:<20}".format(Value) + Package = str(module.GetFPID().GetLibItemName()) + Package=(Package[:17] + '..') if len(Package) > 19 else Package + Package="{0:<20}".format(Package) + #Package="{0:<20}".format(str(module.GetFPID().GetLibItemName())) + X_POS='{0:.4f}'.format(pcbnew.ToMM(module.GetPosition().x - my_board.GetAuxOrigin().x )) + X_POS="{0:<11}".format(X_POS) + Y_POS='{0:.4f}'.format(-1*pcbnew.ToMM(module.GetPosition().y - my_board.GetAuxOrigin().y)) + Y_POS="{0:<11}".format(Y_POS) + Rotation='{0:.1f}'.format((module.GetOrientation()/10)) + Rotation="{0:>6}".format(Rotation)+' ' + if module.GetLayer() == 0: + Layer=" top" + else: + Layer=" bottom" + #Side="## Side :"+Layer+lsep + Layer="{0:<10}".format(Layer) + Type=' '+md + content=Reference + content+=Value + content+=Package + content+=X_POS + content+=Y_POS + content+=Rotation + content+=Layer + content+=Type+lsep + if 'top' in Layer and 'SMD' in Type: + content_top_SMD+=content + SMD_top_cnt+=1 + elif 'bot' in Layer and 'SMD' in Type: + content_bot_SMD+=content + SMD_bot_cnt+=1 + elif 'top' in Layer and 'THD' in Type: + content_top_THD+=content + TH_top_cnt+=1 + elif 'bot' in Layer and 'THD' in Type: + content_bot_THD+=content + TH_bot_cnt+=1 + elif 'top' in Layer and 'VIRTUAL' in Type: + content_top_VIRTUAL+=content + Virt_top_cnt+=1 + elif 'bot' in Layer and 'VIRTUAL' in Type: + content_bot_VIRTUAL+=content + Virt_bot_cnt+=1 + content_ALL+=content + #print ("%s %s %s %1.4f %1.4f %1.4f %s %s" % ( str(module.GetReference()).ljust(Nchars), + # str(module.GetValue()).ljust(Nchars), + # str(module.GetFPID().GetLibItemName()).ljust(Nchars), + # pcbnew.ToMM(module.GetPosition().x - my_board.GetAuxOrigin().x ), + # -1*pcbnew.ToMM(module.GetPosition().y - my_board.GetAuxOrigin().y), + # module.GetOrientation()/10, + # "top".ljust(Nchars) if module.GetLayer() == 0 else "bottom".ljust(Nchars), + # md + # )) + +#"top" if module.GetLayer() == 0 else "bottom" + + #LogMsg+="## End"+lsep + + Header=Header_1+"## Side : top"+lsep+Header_2 + content=Header+content_top_SMD+"## End"+lsep + with open(out_filename_top_SMD,'w') as f_out: + f_out.write(content) + Header=Header_1+"## Side : bottom"+lsep+Header_2 + content=Header+content_bot_SMD+"## End"+lsep + with open(out_filename_bot_SMD,'w') as f_out: + f_out.write(content) + Header=Header_1+"## Side : top"+lsep+Header_2 + content=Header+content_top_THD+"## End"+lsep + with open(out_filename_top_THD,'w') as f_out: + f_out.write(content) + Header=Header_1+"## Side : bottom"+lsep+Header_2 + content=Header+content_bot_THD+"## End"+lsep + with open(out_filename_bot_THD,'w') as f_out: + f_out.write(content) + Header=Header_1+"## Side : top"+lsep+Header_2 + content=Header+content_top_VIRTUAL+"## End"+lsep + with open(out_filename_top_VIRTUAL,'w') as f_out: + f_out.write(content) + Header=Header_1+"## Side : bottom"+lsep+Header_2 + content=Header+content_bot_VIRTUAL+"## End"+lsep + with open(out_filename_bot_VIRTUAL,'w') as f_out: + f_out.write(content) + Header=Header_1+"## Side : ALL"+lsep+Header_2 + content=Header+content_ALL+"## End"+lsep + content = content + '## '+ str(SMD_pads) + ' SMD pads' +lsep + content = content + '## '+ str(TH_pads) + ' TH pads' +lsep + content = content + '## '+ str(Virt_pads) + ' Virtual pads' +lsep + content = content + '## '+ str( TH_top_cnt) + ' Top TH modules' + lsep + content = content + '## '+ str( TH_bot_cnt) + ' Bot TH modules' + lsep + content = content + '## '+ str( SMD_top_cnt) + ' Top SMD modules' + lsep + content = content + '## '+ str( SMD_bot_cnt) + ' Bot SMD modules' + lsep + content = content + '## '+ str( Virt_top_cnt) + ' Top Virtual modules' + lsep + content = content + '## '+ str( Virt_bot_cnt) + ' Bot Virtual modules' + lsep + + with open(out_filename_ALL,'w') as f_out: + f_out.write(content) + + #with open(out_filename,'w') as f_out: + # f_out.write(LogMsg) + #LogMsg="" + #f = open(out_filename,'w') + #f.write(LogMsg) + #f.close() + LogMsg1="reading from:" + lsep + dirpath + lsep + LogMsg1+= lsep + 'Pads:' + lsep + LogMsg1+= 'SMD pads ' + str(SMD_pads) + lsep + LogMsg1+= 'TH pads ' + str(TH_pads) +lsep + LogMsg1+= 'Virtual pads ' + str(Virt_pads) + lsep + LogMsg1+= 'Vias ' + str( vias_cnt) + lsep + LogMsg1+= lsep + 'Modules:' + lsep + LogMsg1+= 'Top TH modules ' + str( TH_top_cnt) + lsep + LogMsg1+= 'Bot TH modules ' + str( TH_bot_cnt) + lsep + LogMsg1+= 'Top SMD modules ' + str( SMD_top_cnt) + lsep + LogMsg1+= 'Bot SMD modules ' + str( SMD_bot_cnt) + lsep + LogMsg1+= 'Top Virtual modules ' + str( Virt_top_cnt) + lsep + LogMsg1+= 'Bot Virtual modules ' + str( Virt_bot_cnt) + lsep + LogMsg1+= lsep + 'PCB Geometry:' + lsep + LogMsg1+= 'Pcb Height ' +'{0:.3f}'.format( pcb_height ) + 'mm, Pcb Width ' + '{0:.3f}'.format( pcb_width ) + 'mm' +lsep+'[based on Edge bounding box]' +lsep + LogMsg1+= lsep + #LogMsg1+=lsep+"reading from:" + lsep + dirpath + lsep + lsep + if 0: + LogMsg1+="written to:" + lsep + out_filename_top_SMD + lsep + LogMsg1+=out_filename_bot_SMD + lsep + LogMsg1+=out_filename_top_THD + lsep + LogMsg1+=out_filename_bot_THD + lsep + LogMsg1+=out_filename_top_VIRTUAL + lsep + LogMsg1+=out_filename_bot_VIRTUAL + lsep + LogMsg1+=out_filename_ALL + lsep + else: + LogMsg1+="written to:" + lsep + path+os.sep+dir + lsep + + return LogMsg1 + #return LogMsg1+LogMsg + + +# Python plugin stuff +from . import PositionsDlg + +class Positions_Dlg(PositionsDlg.PositionsDlg): + # 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(Positions_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 + PositionsDlg.PositionsDlg.__init__(self, parent) + #self.GetSizer().Fit(self) + self.SetMinSize(self.GetSize()) + self.m_bitmapFab.SetBitmap(wx.Bitmap(os.path.join(os.path.dirname(__file__), "./fabrication.png"))) + # 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 generatePOS( pcbnew.ActionPlugin ): + """ + A script to generate POS file for kicad_pcb + requirements: KiCAD pcbnew >= 4.0 + release "1.0.1" + + """ + + 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 = "Fabrication Footprint Position \nversion "+___version___ + self.category = "Fabrication Output" + self.description = "Generate POS output for SMD, THD, Virtual\nand Board Statistics" + #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 ): + + #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 + #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 = Positions_Dlg(_pcbnew_frame) + aParameters.Show() + modal_result = aParameters.ShowModal() + if modal_result == wx.ID_OK: + DirName = aParameters.m_textCtrlDir.GetValue() + #wx.LogMessage(DirName) + #wx.LogMessage(LayerName+';'+str(LayerIndex)+';'+LayerStdName) + GenPos(DirName) + else: + None # Cancel + + +def GenPos(dir): + def f_mm(raw): + return repr(raw/mm_ius) + + board = pcbnew.GetBoard() + + #fileName = GetBoard().GetFileName() + fileName = pcbnew.GetBoard().GetFileName() + if len(fileName)==0: + wx.MessageBox("a board needs to be saved/loaded!") + else: + LogMsg='' + # msg="'get_pos.py'"+os.linesep + msg="Generate POS output: version = "+___version___+os.linesep + #msg+="Generate POS output"+os.linesep + #print (msg) + #LogMsg=msg+'\n\n' + + #print(msg) + LogMsg+=msg + reply=generate_POS(dir) + LogMsg+=reply + wx.LogMessage(LogMsg) + + if 0: + 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() + +#generatePOS().register() + +# execfile("round_tracks.py") + diff --git a/MoveToLayer/.gitignore b/MoveToLayer/.gitignore new file mode 100644 index 0000000..06703cd --- /dev/null +++ b/MoveToLayer/.gitignore @@ -0,0 +1,51 @@ +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files, back +*.cab +*.msi +*.msm +*.msp +*.bak + +# Windows shortcuts +*.lnk + +# Python executable +*.pyc + +# ========================= +# Operating System Files +# ========================= + +# OSX +# ========================= + +.DS_Store +.AppleDouble +.LSOverride + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk diff --git a/MoveToLayer/Move2LayerDlg.fbp b/MoveToLayer/Move2LayerDlg.fbp new file mode 100644 index 0000000..dafcfc7 --- /dev/null +++ b/MoveToLayer/Move2LayerDlg.fbp @@ -0,0 +1,663 @@ + + + + + + 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 + + 568,263 + 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 + Selected Objects will be Moved to chosen 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|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + -1,-1 + 0 + -1,-1 + 1 + m_comboBoxLayer + 1 + + + protected + 1 + + Resizable + -1 + 1 + 250,-1 + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + Combo! + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_bitmapLayers + 1 + + + protected + 1 + + Resizable + 1 + + ; ; forward_declare + 0 + + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_bitmapDwgs + 1 + + + protected + 1 + + Resizable + 1 + + ; ; forward_declare + 0 + + + + + + + + + + 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 + + + + + + + + + + 5 + wxEXPAND + 1 + + + bSizer4 + wxVERTICAL + none + + + + + + diff --git a/MoveToLayer/Move2LayerDlg.py b/MoveToLayer/Move2LayerDlg.py new file mode 100644 index 0000000..ad184af --- /dev/null +++ b/MoveToLayer/Move2LayerDlg.py @@ -0,0 +1,86 @@ +# -*- 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( 568,263 ), 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"Selected Objects will be Moved to chosen Layer", 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.Size( 250,-1 ), m_comboBoxLayerChoices, 0 ) + bSizer31.Add( self.m_comboBoxLayer, 0, wx.ALL|wx.EXPAND, 5 ) + + self.m_bitmapLayers = wx.StaticBitmap( self, wx.ID_ANY, wx.NullBitmap, wx.DefaultPosition, wx.DefaultSize, 0 ) + bSizer31.Add( self.m_bitmapLayers, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5 ) + + self.m_bitmapDwgs = wx.StaticBitmap( self, wx.ID_ANY, wx.NullBitmap, wx.DefaultPosition, wx.DefaultSize, 0 ) + bSizer31.Add( self.m_bitmapDwgs, 0, wx.ALIGN_CENTER_VERTICAL|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 ) + + bSizer4 = wx.BoxSizer( wx.VERTICAL ) + + + bSizer3.Add( bSizer4, 1, wx.EXPAND, 5 ) + + + self.SetSizer( bSizer3 ) + self.Layout() + + self.Centre( wx.BOTH ) + + def __del__( self ): + pass + + diff --git a/MoveToLayer/Move2LayerDlg.py.bak b/MoveToLayer/Move2LayerDlg.py.bak new file mode 100644 index 0000000..3f5f403 --- /dev/null +++ b/MoveToLayer/Move2LayerDlg.py.bak @@ -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( 325,190 ), 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"Selected Objects to Move to chosen Layer", 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.Size( 250,-1 ), m_comboBoxLayerChoices, 0 ) + bSizer31.Add( self.m_comboBoxLayer, 0, wx.ALL|wx.EXPAND, 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/add_polygon.png b/MoveToLayer/add_polygon.png new file mode 100644 index 0000000..a2384a5 Binary files /dev/null and b/MoveToLayer/add_polygon.png differ diff --git a/MoveToLayer/add_polygon.svg b/MoveToLayer/add_polygon.svg new file mode 100644 index 0000000..9ab7fbc --- /dev/null +++ b/MoveToLayer/add_polygon.svg @@ -0,0 +1,109 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff --git a/MoveToLayer/move2layer.png b/MoveToLayer/move2layer.png new file mode 100644 index 0000000..3e5b885 Binary files /dev/null and b/MoveToLayer/move2layer.png differ 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/MoveToLayer/move_to_layer.py b/MoveToLayer/move_to_layer.py new file mode 100644 index 0000000..86b0299 --- /dev/null +++ b/MoveToLayer/move_to_layer.py @@ -0,0 +1,162 @@ +# 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. +# +# + +### plugins errors +#import pcbnew +#pcbnew.GetWizardsBackTrace() + + +import wx +import pcbnew +from pcbnew import * +import base64 +from wx.lib.embeddedimage import PyEmbeddedImage +import os +___version___="1.2.3" + +from . import Move2LayerDlg + + +def MoveToLayer(pcb,layerId): + found_selected=False + for drw in pcb.GetDrawings(): + if drw.IsSelected(): + drw.SetLayer(layerId) + found_selected=True + + if found_selected!=True: + LogMsg="select drawings to be moved to new layer\n" + LogMsg+="use GAL for selecting lines" + wx.LogMessage(LogMsg) + else: + pcbnew.Refresh() + layerName = pcbnew.GetBoard().GetLayerName(layerId) + LogMsg="selected drawings moved to "+layerName+" layer" + wx.LogMessage(LogMsg) +# + + + +# 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_bitmapLayers.SetBitmap(wx.Bitmap(os.path.join(os.path.dirname(__file__), "./add_polygon.png"))) + self.m_bitmapDwgs.SetBitmap(wx.Bitmap(os.path.join(os.path.dirname(__file__), "./move2layer.png"))) + # 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 + + board = pcbnew.GetBoard() + fileName = GetBoard().GetFileName() + # # dicts for converting layer name to id, used by _get_layer + # _std_layer_dict = {pcbnew.BOARD_GetStandardLayerName(n): n + # for n in range(pcbnew.PCB_LAYER_ID_COUNT)} + # #_std_layer_names = {s: n for n, s in _std_layer_dict.iteritems()} + # _std_layer_names = {s: n for n, s in _std_layer_dict.items()} + # _brd_layer_dict = {pcbnew.GetBoard().GetLayerName(n): n + # for n in range(pcbnew.PCB_LAYER_ID_COUNT)} + # #_std_layer_names = {s: n for n, s in _std_layer_dict.iteritems()} + # _brd_layer_names = {s: n for n, s in _brd_layer_dict.items()} + + + 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() + for l in range(pcbnew.PCB_LAYER_ID_COUNT): + aParameters.m_comboBoxLayer.Append(pcbnew.GetBoard().GetLayerName(l)) + aParameters.m_comboBoxLayer.Select(44) + modal_result = aParameters.ShowModal() + if modal_result == wx.ID_OK: + LayerName = aParameters.m_comboBoxLayer.GetStringSelection() + LayerIndex = aParameters.m_comboBoxLayer.FindString(LayerName) + LayerStdName = pcbnew.BOARD_GetStandardLayerName(LayerIndex) + #wx.LogMessage(LayerName+';'+str(LayerIndex)+';'+LayerStdName) + MoveToLayer(board, LayerIndex) + else: + None # Cancel + + LogMsg='' + msg="'move to layer tool'\n" + msg+="version = "+___version___ + + +#move_to_draw_layer().register() + diff --git a/MoveToLayer/move_to_layer.py.bak b/MoveToLayer/move_to_layer.py.bak new file mode 100644 index 0000000..6a1a950 --- /dev/null +++ b/MoveToLayer/move_to_layer.py.bak @@ -0,0 +1,154 @@ +# 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.3" + +from . import Move2LayerDlg + + +def MoveToLayer(pcb,layerId): + found_selected=False + for drw in pcb.GetDrawings(): + if drw.IsSelected(): + drw.SetLayer(layerId) + found_selected=True + + if found_selected!=True: + LogMsg="select drawings to be moved to new layer\n" + LogMsg+="use GAL for selecting lines" + wx.LogMessage(LogMsg) + else: + pcbnew.Refresh() + layerName = pcbnew.GetBoard().GetLayerName(layerId) + LogMsg="selected drawings moved to "+layerName+" layer" + wx.LogMessage(LogMsg) +# + + + +# 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 + + board = pcbnew.GetBoard() + fileName = GetBoard().GetFileName() + # # dicts for converting layer name to id, used by _get_layer + # _std_layer_dict = {pcbnew.BOARD_GetStandardLayerName(n): n + # for n in range(pcbnew.PCB_LAYER_ID_COUNT)} + # #_std_layer_names = {s: n for n, s in _std_layer_dict.iteritems()} + # _std_layer_names = {s: n for n, s in _std_layer_dict.items()} + # _brd_layer_dict = {pcbnew.GetBoard().GetLayerName(n): n + # for n in range(pcbnew.PCB_LAYER_ID_COUNT)} + # #_std_layer_names = {s: n for n, s in _std_layer_dict.iteritems()} + # _brd_layer_names = {s: n for n, s in _brd_layer_dict.items()} + + + 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() + for l in range(pcbnew.PCB_LAYER_ID_COUNT): + aParameters.m_comboBoxLayer.Append(pcbnew.GetBoard().GetLayerName(l)) + aParameters.m_comboBoxLayer.Select(44) + modal_result = aParameters.ShowModal() + if modal_result == wx.ID_OK: + LayerName = aParameters.m_comboBoxLayer.GetStringSelection() + LayerIndex = aParameters.m_comboBoxLayer.FindString(LayerName) + LayerStdName = pcbnew.BOARD_GetStandardLayerName(LayerIndex) + #wx.LogMessage(LayerName+';'+str(LayerIndex)+';'+LayerStdName) + MoveToLayer(board, LayerIndex) + else: + None # Cancel + + LogMsg='' + msg="'move to layer tool'\n" + msg+="version = "+___version___ + + +#move_to_draw_layer().register() + diff --git a/MoveToLayer/move_to_layer_dlg.fbp b/MoveToLayer/move_to_layer_dlg.fbp new file mode 100644 index 0000000..66f4aa8 --- /dev/null +++ b/MoveToLayer/move_to_layer_dlg.fbp @@ -0,0 +1,547 @@ + + + + + + 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 + + 568,263 + 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 + Selected Objects will Move to chosen 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|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + -1,-1 + 0 + -1,-1 + 1 + m_comboBoxLayer + 1 + + + protected + 1 + + Resizable + -1 + 1 + 250,-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 + + + + + + + + + + 5 + wxEXPAND + 1 + + + bSizer4 + wxVERTICAL + none + + + + + + diff --git a/MoveToLayer/move_to_layer_dlg.fbp.bak b/MoveToLayer/move_to_layer_dlg.fbp.bak new file mode 100644 index 0000000..43a4da8 --- /dev/null +++ b/MoveToLayer/move_to_layer_dlg.fbp.bak @@ -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 + + 325,190 + 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 + Selected Objects will Move to chosen 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|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + -1,-1 + 0 + -1,-1 + 1 + m_comboBoxLayer + 1 + + + protected + 1 + + Resizable + -1 + 1 + 250,-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/PcbToDxf/.gitignore b/PcbToDxf/.gitignore new file mode 100644 index 0000000..06703cd --- /dev/null +++ b/PcbToDxf/.gitignore @@ -0,0 +1,51 @@ +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files, back +*.cab +*.msi +*.msm +*.msp +*.bak + +# Windows shortcuts +*.lnk + +# Python executable +*.pyc + +# ========================= +# Operating System Files +# ========================= + +# OSX +# ========================= + +.DS_Store +.AppleDouble +.LSOverride + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk diff --git a/PcbToDxf/__init__.py b/PcbToDxf/__init__.py new file mode 100644 index 0000000..68def39 --- /dev/null +++ b/PcbToDxf/__init__.py @@ -0,0 +1,2 @@ +from .action_menu_pcb2dxf import pcb2dxf +pcb2dxf().register() diff --git a/action_menu_pcb2dxf.py b/PcbToDxf/action_menu_pcb2dxf.py similarity index 98% rename from action_menu_pcb2dxf.py rename to PcbToDxf/action_menu_pcb2dxf.py index 82e382c..035b260 100644 --- a/action_menu_pcb2dxf.py +++ b/PcbToDxf/action_menu_pcb2dxf.py @@ -24,13 +24,15 @@ # Created: 14.04.2016 # Copyright (C) 2016, Manfred Moitzi # License: MIT License + +#import pcbnew;pcbnew.GetWizardsBackTrace() from __future__ import unicode_literals dxf_parser="r12writer from ezdxf 0.7.6" __author__ = "mozman " script_name="kicadpcb2dxf" __author_script__="easyw Maurice" -___version___="3.8x" +___version___="3.8.1" from contextlib import contextmanager @@ -199,8 +201,12 @@ def dxf_tag(code, value): ################################################################### ##real python code easyw import sys -reload(sys) -sys.setdefaultencoding('utf8') #to accept utf8 chars +if sys.version_info[0] == 2: #if py2: + reload(sys) + sys.setdefaultencoding('utf8') #to accept utf8 chars +#else: +# import importlib +# importlib.reload(sys) import re, os from math import sqrt, atan2, degrees, sin, cos, radians @@ -221,9 +227,11 @@ class pcb2dxf( pcbnew.ActionPlugin ): self.description should be a comprehensive description of the plugin """ - self.name = "export technical layers of pcb to DXF (saved board)" + self.name = "Export pcb technical layers to DXF \nversion "+___version___ self.category = "export PCB" self.description = "export technical layers of pcb to DXF (saved board)" + self.show_toolbar_button = True + self.icon_file_name = os.path.join(os.path.dirname(__file__), './dxf_icon.png') def Run( self ): fileName = GetBoard().GetFileName() @@ -249,7 +257,7 @@ class pcb2dxf( pcbnew.ActionPlugin ): #found_selected=False #board = pcbnew.GetBoard() - dlg=wx.MessageBox( 'Only SAVED board file will be exported to DXF file', 'Confirm', wx.OK | wx.CANCEL | wx.ICON_INFORMATION ) + dlg=wx.MessageBox( 'Exporting technical layers of pcb to DXF\nOnly SAVED board file will be exported to DXF file\n\nversion '+___version___, 'Confirm', wx.OK | wx.CANCEL | wx.ICON_INFORMATION ) if dlg == wx.OK: if os.path.isfile(out_filename): dlg=wx.MessageBox( 'Overwrite DXF file?', 'Confirm', wx.YES_NO | wx.NO_DEFAULT | wx.ICON_QUESTION ) diff --git a/PcbToDxf/dxf_icon.png b/PcbToDxf/dxf_icon.png new file mode 100644 index 0000000..dad5797 Binary files /dev/null and b/PcbToDxf/dxf_icon.png differ diff --git a/README.md b/README.md index 325e2b6..0c1181b 100644 --- a/README.md +++ b/README.md @@ -1,94 +1,71 @@ -# kicad-action-plugins -#### kicad action plugin tools - -- ### action_menu_annular_check.py -A script to check for annular ring violations -for PTH, NPTH pads and vias - -- ### action_menu_pcb2dxf -A script to export technical layers of kicad PCB to DXF -DXF generated file has single line draw as it should be for mechanical interchange (this option is missing in pcbnew plot) - -- ### action_menu_move_to_layer -A script to Move Selected Drawing(s) to chosen new Layer (available only in GAL) - -- ### action_positions -A script to Generate Position Files for SMD, THD, Virtual, ALL - ---- -## action_menu_annular_check.py -A script to check for annular ring violations -for PTH, NPTH pads and vias - -requirements: KiCAD pcbnew > 4.0 built with KICAD_SCRIPTING_ACTION_MENU option activated -release "1.5.3" - -'action_menu_annular_check.py' checking PCB for Annular Ring in PTH, NPTH and Vias -(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 - DRL_EXTRA = 0.100 #extra drill margin size for production - -Launch the Annular Check script in pcbnew from Tools menu: -![Annular Check](screenshots/annular-checker.gif) - -### todo (annular_check) -- [ ] add colors to output list - ---- -## action_menu_pcb2dxf -**kicadpcb2dxf** -_dxf exporter for mechanical layers of a kicad_pcb board_ -- "Dwgs", "Cmts", "Edge", "Eco1", "Eco2", "F.Fab", "B.Fab", "F.CrtYd", "B.CrtYd" -- the dxf generated has single line draw as it should be for mechanical interchange (this option is missing in pcbnew plot) - -creates DXF file of technical layers of the selected kicad pcb board - -![kicad pcb2dxf](screenshots/export-pcb2dxf.png) - -(this is a part of kicad StepUp tools; please refer to kicad StepUp tools for the full licence) - - kicadpcb2dxf: Copyright (c) 2015 Maurice easyw - dxf_parser="r12writer from ezdxf 0.7.6": Copyright (C) 2016, Manfred Moitzi with MIT License - -done: -- [x] added line, circle, arc primitives -- [x] added footprint support -- [x] fixed negative arc case -- [x] added text support (mirror & alignement not supported) -- [x] added multiline text -- [x] add quote support - -### todo (kicadpcb2dxf) -- [ ] tbd - ---- -## action_menu_move_to_layer -A script to Move Selected Drawing(s) to chosen new Layer (available only in GAL) - -requirements: KiCAD pcbnew > 4.0 built with KICAD_SCRIPTING_ACTION_MENU option activated -release "1.1.0" - - 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 - -Launch the 'Move Selected drawings to chosen Layer' script in pcbnew from Tools menu -![Move Selected drawings to chosen Layer](screenshots/move_to_layer.png) - -- ### action_positions -A script to Generate Position Files for SMD, THD, Virtual, ALL - -The script will generate Fabrication POS files for: - -- SMD -- THD -- VIRTUAL -- ALL +# kicad-action-tools +#### A suite of kicad action plugin tools + +- ### Annular Ring Checker +Check your kicad_pcb for annular ring violations: +PTH Plated Trough Hole, NPTH Non Plated Trough Hole Pads and Vias + +- ### Snap Selected Footprint(s) to Grid +Tool to move the selected footprint module(s) to the Grid. +The Modules can be easily aligned to GridOrigin or to Auxiliary Origin. + +- ### Fabrication Footprint Position +Tool for the creation of the necessary files for the production of the printed circuit board. +Generate Position Files for SMD, THD, Virtual and ALL components, referred to Auxiliary Origin. + +- ### Move Selected Drawings to chosen Layer +Tool to Move Selected Drawing(s) to the chosen new Layer. + +- ### Export pcb technical layers to DXF +Tool to export technical layers of kicad PCB to DXF. +The DXF generated file has single line draw and different layers for each pcb technical layer. + +--- +## Annular Ring Checker +Check your kicad_pcb for annular ring violations: +PTH Plated Trough Hole, NPTH Non Plated Trough Hole Pads and Vias + +Launch the Annular Check script in pcbnew from Tools menu: +![Annular Check](screenshots/annular-checker.gif) + +--- +## Export pcb technical layers to DXF +**kicadpcb2dxf** +_dxf exporter for mechanical layers of a kicad_pcb board_ +- "Dwgs", "Cmts", "Edge", "Eco1", "Eco2", "F.Fab", "B.Fab", "F.CrtYd", "B.CrtYd" +- the dxf generated has single line draw as it should be for mechanical interchange (this option is missing in pcbnew plot) + +creates DXF file of technical layers of the selected kicad pcb board + +![kicad pcb2dxf](screenshots/export-pcb2dxf.png) + +(this is a part of kicad StepUp tools; please refer to kicad StepUp tools for the full licence) + + kicadpcb2dxf: Copyright (c) 2015 Maurice easyw + dxf_parser="r12writer from ezdxf 0.7.6": Copyright (C) 2016, Manfred Moitzi with MIT License + +done: +- [x] added line, circle, arc primitives +- [x] added footprint support +- [x] fixed negative arc case +- [x] added text support (mirror & alignement not supported) +- [x] added multiline text +- [x] add quote support + +--- +## Move Selected Drawings to chosen Layer +Tool to Move Selected Drawing(s) to chosen new Layer + +requirements: KiCAD pcbnew > 4.0 built with KICAD_SCRIPTING_ACTION_MENU option activated +release "1.1.0" + + 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 + +Launch the 'Move Selected drawings to chosen Layer' script in pcbnew from Tools menu +![Move Selected drawings to chosen Layer](screenshots/move_to_layer.png) diff --git a/Snap2Grid/.gitignore b/Snap2Grid/.gitignore new file mode 100644 index 0000000..06703cd --- /dev/null +++ b/Snap2Grid/.gitignore @@ -0,0 +1,51 @@ +# Windows image file caches +Thumbs.db +ehthumbs.db + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files, back +*.cab +*.msi +*.msm +*.msp +*.bak + +# Windows shortcuts +*.lnk + +# Python executable +*.pyc + +# ========================= +# Operating System Files +# ========================= + +# OSX +# ========================= + +.DS_Store +.AppleDouble +.LSOverride + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk diff --git a/Snap2Grid/Snap2GridDlg.fbp b/Snap2Grid/Snap2GridDlg.fbp new file mode 100644 index 0000000..723beb8 --- /dev/null +++ b/Snap2Grid/Snap2GridDlg.fbp @@ -0,0 +1,740 @@ + + + + + + Python + 1 + source_name + 0 + 0 + res + UTF-8 + connect + Snap2GridDlg + 1000 + none + + 0 + Snap2GridDlg + + . + + 1 + 1 + 1 + 1 + UI + 0 + 0 + + 0 + wxAUI_MGR_DEFAULT + + wxBOTH + + 1 + 1 + impl_virtual + + + + 0 + wxID_ANY + + + Snap2GridDlg + + 499,394 + wxCAPTION|wxCLOSE_BOX|wxDEFAULT_DIALOG_STYLE|wxRESIZE_BORDER + + Snap to Grid + + + + + + + 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 + Selected Model(s) to Snap to the Grid + 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 + Grid + 0 + + 0 + + + 0 + + 1 + m_staticTextGrid + 1 + + + protected + 1 + + Resizable + 1 + + + + 0 + + + + + -1 + + + + 5 + wxALL|wxEXPAND + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + "1.0mm (39.37mils)" "0.5mm (19.69mils)" "0.25mm (9.84mils)" "0.1mm (3.94mils)" "2.54mm (100mils)" "1.27mm (50mils)" "0.635mm (25mils)" "0.508mm (20mils)" "0.254mm (10mils)" "0.127mm (5mils)" + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + -1,-1 + 0 + -1,-1 + 1 + m_comboBoxGrid + 1 + + + protected + 1 + + Resizable + 0 + 1 + -1,-1 + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + 1.0mm (39.37mils) + + + + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + + 0 + + + 0 + + 1 + m_bitmapS2G + 1 + + + protected + 1 + + Resizable + 1 + + ; ; forward_declare + 0 + + + + + + + + + + 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 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + GridOrigin reference + + 0 + + + 0 + + 1 + m_radioBtnGO + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + 0 + + + + + + + 5 + wxALL + 0 + + 1 + 1 + 1 + 1 + + + + + + + + 1 + 0 + 1 + + 1 + 0 + Dock + 0 + Left + 1 + + 1 + + 0 + 0 + wxID_ANY + AuxOrigin reference + + 0 + + + 0 + + 1 + m_radioBtnAO + 1 + + + protected + 1 + + Resizable + 1 + + + ; ; forward_declare + 0 + + + wxFILTER_NONE + wxDefaultValidator + + 0 + + + + + + + 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_staticline2 + 1 + + + protected + 1 + + Resizable + 1 + + wxLI_HORIZONTAL + ; ; forward_declare + 0 + + + + + + + + 5 + wxALIGN_RIGHT|wxEXPAND + 0 + + + bSizer1 + wxHORIZONTAL + none + + 5 + wxEXPAND + 1 + + 0 + protected + 0 + + + + 5 + wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL + 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 + wxALIGN_CENTER_VERTICAL|wxALIGN_RIGHT|wxALL + 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 + + + + + + + + + + 5 + wxEXPAND + 1 + + + bSizer4 + wxVERTICAL + none + + + + + + diff --git a/Snap2Grid/Snap2GridDlg.py b/Snap2Grid/Snap2GridDlg.py new file mode 100644 index 0000000..a870395 --- /dev/null +++ b/Snap2Grid/Snap2GridDlg.py @@ -0,0 +1,91 @@ +# -*- 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 Snap2GridDlg +########################################################################### + +class Snap2GridDlg ( wx.Dialog ): + + def __init__( self, parent ): + wx.Dialog.__init__ ( self, parent, id = wx.ID_ANY, title = u"Snap to Grid", pos = wx.DefaultPosition, size = wx.Size( 499,394 ), 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"Selected Model(s) to Snap\nto the Grid", 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_staticTextGrid = wx.StaticText( self, wx.ID_ANY, u"Grid", wx.DefaultPosition, wx.DefaultSize, 0 ) + self.m_staticTextGrid.Wrap( -1 ) + + bSizer31.Add( self.m_staticTextGrid, 1, wx.ALL|wx.EXPAND, 5 ) + + m_comboBoxGridChoices = [ u"1.0mm (39.37mils)", u"0.5mm (19.69mils)", u"0.25mm (9.84mils)", u"0.1mm (3.94mils)", u"2.54mm (100mils)", u"1.27mm (50mils)", u"0.635mm (25mils)", u"0.508mm (20mils)", u"0.254mm (10mils)", u"0.127mm (5mils)" ] + self.m_comboBoxGrid = wx.ComboBox( self, wx.ID_ANY, u"1.0mm (39.37mils)", wx.DefaultPosition, wx.Size( -1,-1 ), m_comboBoxGridChoices, 0 ) + self.m_comboBoxGrid.SetSelection( 0 ) + bSizer31.Add( self.m_comboBoxGrid, 0, wx.ALL|wx.EXPAND, 5 ) + + self.m_bitmapS2G = wx.StaticBitmap( self, wx.ID_ANY, wx.NullBitmap, wx.DefaultPosition, wx.DefaultSize, 0 ) + bSizer31.Add( self.m_bitmapS2G, 0, wx.ALIGN_CENTER_VERTICAL|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 ) + + self.m_radioBtnGO = wx.RadioButton( self, wx.ID_ANY, u"GridOrigin reference", wx.DefaultPosition, wx.DefaultSize, 0 ) + bSizer3.Add( self.m_radioBtnGO, 0, wx.ALL, 5 ) + + self.m_radioBtnAO = wx.RadioButton( self, wx.ID_ANY, u"AuxOrigin reference", wx.DefaultPosition, wx.DefaultSize, 0 ) + bSizer3.Add( self.m_radioBtnAO, 0, wx.ALL, 5 ) + + self.m_staticline2 = wx.StaticLine( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, wx.LI_HORIZONTAL ) + bSizer3.Add( self.m_staticline2, 0, wx.EXPAND |wx.ALL, 5 ) + + bSizer1 = wx.BoxSizer( wx.HORIZONTAL ) + + + bSizer1.Add( ( 0, 0), 1, wx.EXPAND, 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.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT|wx.ALL, 5 ) + + self.m_buttonCancel = wx.Button( self, wx.ID_CANCEL, u"Cancel", wx.DefaultPosition, wx.DefaultSize, 0 ) + bSizer1.Add( self.m_buttonCancel, 0, wx.ALIGN_CENTER_VERTICAL|wx.ALIGN_RIGHT|wx.ALL, 5 ) + + + bSizer3.Add( bSizer1, 0, wx.ALIGN_RIGHT|wx.EXPAND, 5 ) + + bSizer4 = wx.BoxSizer( wx.VERTICAL ) + + + bSizer3.Add( bSizer4, 1, wx.EXPAND, 5 ) + + + self.SetSizer( bSizer3 ) + self.Layout() + + self.Centre( wx.BOTH ) + + def __del__( self ): + pass + + diff --git a/Snap2Grid/__init__.py b/Snap2Grid/__init__.py new file mode 100644 index 0000000..07cb020 --- /dev/null +++ b/Snap2Grid/__init__.py @@ -0,0 +1,2 @@ +from .snap2grid import snap_to_grid +snap_to_grid().register() diff --git a/Snap2Grid/snap2grid.png b/Snap2Grid/snap2grid.png new file mode 100644 index 0000000..d812a83 Binary files /dev/null and b/Snap2Grid/snap2grid.png differ diff --git a/Snap2Grid/snap2grid.py b/Snap2Grid/snap2grid.py new file mode 100644 index 0000000..9c68e7d --- /dev/null +++ b/Snap2Grid/snap2grid.py @@ -0,0 +1,226 @@ +# -*- coding: utf-8 -*- +# +# A script to Snap modules to selected Grid for kicad_pcb +# requirements: KiCAD pcbnew >= 4.0 +# copyright Maurice easyw +# +# + +#import snaptogrid; import importlib; importlib.reload(snaptogrid) + +### plugins errors +#import pcbnew +#pcbnew.GetWizardsBackTrace() + +__version__ = '1.0.2' +import sys, os +import pcbnew +import datetime +import wx +from pcbnew import * + +use_grid_origin = True + +gridReference = 0.1 #1.27 #mm pcbnew.FromMM(1.0) #0.1mm + +gridSizeMM = gridReference + +from . import Snap2GridDlg + +sys.path.append(os.path.dirname(__file__)) + +debug = False +def wxLogDebug(msg,dbg): + """printing messages only if show is omitted or True""" + if dbg == True: + wx.LogMessage(msg) +# + +class Snap2Grid_Dlg(Snap2GridDlg.Snap2GridDlg): + # 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(Snap2Grid_Dlg, self).SetSizeHints(sz1, sz2) + + # def onDeleteClick(self, event): + # return self.EndModal(wx.ID_DELETE) + # + # def onConnectClick(self, event): + # return self.EndModal(wx.ID_REVERT) + + def __init__(self, parent): + import wx + Snap2GridDlg.Snap2GridDlg.__init__(self, parent) + #self.GetSizer().Fit(self) + self.SetMinSize(self.GetSize()) + self.m_bitmapS2G.SetBitmap(wx.Bitmap(os.path.join(os.path.dirname(__file__), "./snap2grid.png"))) + #self.SetIcon(wx.IconFromBitmap(wx.Bitmap(os.path.join(os.path.dirname(__file__), "./snap2grid.png")))) + #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" ) + +# Python plugin stuff +class snap_to_grid( pcbnew.ActionPlugin ): + """ + A plugin to Snap modules to selected Grid for kicad_pcb + requirements: KiCAD pcbnew >= 4.0 + + """ + 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 = "Snap Selected Footprint(s) to Grid \nversion "+__version__ + self.category = "Modify PCB" + self.description = "Automaticaly Snap Selected Footprint Module(s) to Grid 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__), './snap2grid.png') + # + + def Run(self): + #self.pcb = GetBoard() + import sys,os + #mm_ius = 1000000.0 + _pcbnew_frame = [x for x in wx.GetTopLevelWindows() if x.GetTitle().lower().startswith('pcbnew')][0] + #aParameters = RoundTrackDlg(None) + aParameters = Snap2Grid_Dlg(_pcbnew_frame) + gridIndex = aParameters.m_comboBoxGrid.FindString('0.1mm (3.94mils)') + aParameters.m_comboBoxGrid.SetSelection(gridIndex) + #aParameters.m_comboBoxGrid.Append('0.1mm (3.94mils)') + aParameters.m_radioBtnGO.SetValue(True) + aParameters.Show() + + modal_result = aParameters.ShowModal() + if modal_result == wx.ID_OK: + grid = aParameters.m_comboBoxGrid.GetStringSelection() + gridSizeMM = float(grid.split('mm')[0]) + if aParameters.m_radioBtnGO.GetValue(): + use_grid_origin = True + else: + use_grid_origin = False + snap2grid(gridSizeMM,use_grid_origin) + else: + None # Cancel +## + +def snap2grid(gridSizeMM,use_grid_origin): + + pcb = pcbnew.GetBoard() + gridOrigin = pcb.GetGridOrigin() + auxOrigin = pcb.GetAuxOrigin() + content='' + locked_fp='' + #wxPoint(77470000, 135890000) + for module in pcb.GetModules(): + if module.IsSelected(): + if use_grid_origin: + mpx = module.GetPosition().x - gridOrigin.x + mpy = module.GetPosition().y - gridOrigin.y + #print(mpx,mpy) + mpxOnG = int(mpx/FromMM(gridSizeMM))*FromMM(gridSizeMM)+ gridOrigin.x + mpyOnG = int(mpy/FromMM(gridSizeMM))*FromMM(gridSizeMM)+ gridOrigin.y + #print(mpxOnG,mpyOnG) + locked='' + if not module.IsLocked(): + module.SetPosition(wxPoint(mpxOnG,mpyOnG)) + else: + locked='LOCKED' + X_POS=str(module.GetPosition().x) # - gridOrigin.x) + #X_POS='{0:.4f}'.format(pcbnew.ToMM(module.GetPosition().x - gridOrigin.x )) + X_POS="{0:<11}".format(X_POS) + Y_POS=str(module.GetPosition().y) # - gridOrigin.y) + Y_POS="{0:<11}".format(Y_POS) + ## mpOnGx = PutOnGridMM(module.GetPosition().x, gridSizeMM) + ## mpOnGy = PutOnGridMM(module.GetPosition().y, gridSizeMM) + ## module.SetPosition(wxPoint(mpOnGx,mpOnGy)) + #module.SetPosition(wxPoint(mpOnGx+FromMM(100.0),mpOnGy+FromMM(2.0))) + #module.SetOrientation(10) + #Y_POS='{0:.4f}'.format(-1*pcbnew.ToMM(module.GetPosition().y - gridOrigin.y)) + else: # AuxOrigin + mpx = module.GetPosition().x - auxOrigin.x + mpy = module.GetPosition().y - auxOrigin.y + #print(mpx,mpy) + mpxOnG = int(mpx/FromMM(gridSizeMM))*FromMM(gridSizeMM)+ auxOrigin.x + mpyOnG = int(mpy/FromMM(gridSizeMM))*FromMM(gridSizeMM)+ auxOrigin.y + #print(mpxOnG,mpyOnG) + locked='' + if not module.IsLocked(): + module.SetPosition(wxPoint(mpxOnG,mpyOnG)) + else: + locked='LOCKED' + X_POS=str(module.GetPosition().x) # - gridOrigin.x) + #X_POS='{0:.4f}'.format(pcbnew.ToMM(module.GetPosition().x - gridOrigin.x )) + X_POS="{0:<11}".format(X_POS) + Y_POS=str(module.GetPosition().y) # - gridOrigin.y) + Y_POS="{0:<11}".format(Y_POS) + ## mpOnGx = PutOnGridMM(module.GetPosition().x, gridSizeMM) + ## mpOnGy = PutOnGridMM(module.GetPosition().y, gridSizeMM) + ## module.SetPosition(wxPoint(mpOnGx,mpOnGy)) + #module.SetPosition(wxPoint(mpOnGx+FromMM(100.0),mpOnGy+FromMM(2.0))) + #module.SetOrientation(10) + #Y_POS='{0:.4f}'.format(-1*pcbnew.ToMM(module.GetPosition().y - gridOrigin.y)) + Reference="{0:<10}".format(str(module.GetReference())) + Value = str(module.GetValue()) + Value=(Value[:17] + '..') if len(Value) > 19 else Value + Value="{0:<20}".format(Value) + Rotation='{0:.1f}'.format((module.GetOrientation()/10)) + Rotation="{0:>6}".format(Rotation)+' ' + if module.GetLayer() == 0: + Layer=" top" + else: + Layer=" bottom" + #Side="## Side :"+Layer+lsep + Layer="{0:<10}".format(Layer) + content+=Reference + if 'LOCKED' in locked: + locked_fp+=Reference + ' LOCKED'+'\n' #os.linesep + #content+=Value + content+=X_POS + content+=Y_POS + #content+=str(mpOnGx) + #content+=str(mpOnGy) + content+=str(mpxOnG) + content+=str(mpyOnG) + content+=Layer+'\n' #os.linesep + if len(content)>0: + content+=str(pcbnew.FromMM(gridSizeMM))+'\n' + info='Snapped to grid: '+str(gridSizeMM)+'mm\n' + if use_grid_origin: + content+="Using GridOrigin as Ref"+'\n' + info+="Using GridOrigin as Ref"+'\n' + else: + content+="Using AuxOrigin as Ref"+'\n' + info+="Using AuxOrigin as Ref"+'\n' + if debug: + wxLogDebug(content,debug) + #else: + wxLogDebug(info,True) + if len (locked_fp)>0: + locked_fp+='\n'+'NOT Moved (Locked fp)' + locked_fp+='\n'+info + wxLogDebug(locked_fp,True) + else: + wxLogDebug(info,True) + else: + wxLogDebug('No Modules Selected',True) + Refresh() + #return content + + # wxLogDebug('showing Selected Tracks',debug) + # wx.LogMessage('Select Tracks to calculate the Length\nor One Pad to select connected Tracks') +# + diff --git a/Snap2Grid/snap2grid.py.bak b/Snap2Grid/snap2grid.py.bak new file mode 100644 index 0000000..15ecdfc --- /dev/null +++ b/Snap2Grid/snap2grid.py.bak @@ -0,0 +1,200 @@ +# -*- coding: utf-8 -*- +# +# A script to Snap modules to selected Grid for kicad_pcb +# requirements: KiCAD pcbnew >= 4.0 +# copyright Maurice easyw +# +# + +#import snaptogrid; import importlib; importlib.reload(snaptogrid) + +__version__ = '1.0.1' +import sys, os +import pcbnew +import datetime +import wx +from pcbnew import * + +use_grid_origin = True + +gridReference = 2.54 #1.27 #mm pcbnew.FromMM(1.0) #0.1mm + +gridSizeMM = gridReference + +#from . import Send2GridDlg + +sys.path.append(os.path.dirname(__file__)) + +debug = False +def wxLogDebug(msg,dbg): + """printing messages only if show is omitted or True""" + if dbg == True: + wx.LogMessage(msg) +# + +#class Snap2Grid_Dlg(Snap2GridDlg.Snap2GridDlg): +# # 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(RoundTrack_Dlg, self).SetSizeHints(sz1, sz2) +# +# # def onDeleteClick(self, event): +# # return self.EndModal(wx.ID_DELETE) +# # +# # def onConnectClick(self, event): +# # return self.EndModal(wx.ID_REVERT) +# +# def __init__(self, parent): +# import wx +# Send2GridDlg.Send2GridDlg.__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" ) +# +# Python plugin stuff +class snap_to_grid( pcbnew.ActionPlugin ): + """ + A plugin to Snap modules to selected Grid for kicad_pcb + requirements: KiCAD pcbnew >= 4.0 + + """ + 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 = "Snap Selected Module(s) to Grid\nversion "+__version__ + self.category = "Modify PCB" + self.description = "Automaticaly Snap Selected Module(s) to Grid 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__), './snap2grid.png') + # + + def Run(self): + #self.pcb = GetBoard() + import sys,os + #mm_ius = 1000000.0 + + pcb = pcbnew.GetBoard() + gridOrigin = pcb.GetGridOrigin() + auxOrigin = pcb.GetAuxOrigin() + content='' + locked_fp='' + #wxPoint(77470000, 135890000) + for module in pcb.GetModules(): + if module.IsSelected(): + if use_grid_origin: + mpx = module.GetPosition().x - gridOrigin.x + mpy = module.GetPosition().y - gridOrigin.y + print(mpx,mpy) + mpxOnG = int(mpx/FromMM(gridSizeMM))*FromMM(gridSizeMM)+ gridOrigin.x + mpyOnG = int(mpy/FromMM(gridSizeMM))*FromMM(gridSizeMM)+ gridOrigin.y + print(mpxOnG,mpyOnG) + locked='' + if not module.IsLocked(): + module.SetPosition(wxPoint(mpxOnG,mpyOnG)) + else: + locked='LOCKED' + X_POS=str(module.GetPosition().x) # - gridOrigin.x) + #X_POS='{0:.4f}'.format(pcbnew.ToMM(module.GetPosition().x - gridOrigin.x )) + X_POS="{0:<11}".format(X_POS) + Y_POS=str(module.GetPosition().y) # - gridOrigin.y) + Y_POS="{0:<11}".format(Y_POS) + ## mpOnGx = PutOnGridMM(module.GetPosition().x, gridSizeMM) + ## mpOnGy = PutOnGridMM(module.GetPosition().y, gridSizeMM) + ## module.SetPosition(wxPoint(mpOnGx,mpOnGy)) + #module.SetPosition(wxPoint(mpOnGx+FromMM(100.0),mpOnGy+FromMM(2.0))) + #module.SetOrientation(10) + #Y_POS='{0:.4f}'.format(-1*pcbnew.ToMM(module.GetPosition().y - gridOrigin.y)) + # else: + # mpx = module.GetPosition().x - auxOrigin().x + # mpy = module.GetPosition().y - auxOrigin().y + # X_POS='{0:.4f}'.format(pcbnew.ToMM(module.GetPosition().x - auxOrigin().x )) + # X_POS="{0:<11}".format(X_POS) + # Y_POS='{0:.4f}'.format(-1*pcbnew.ToMM(module.GetPosition().y - auxOrigin().y)) + # Y_POS="{0:<11}".format(Y_POS) + Reference="{0:<10}".format(str(module.GetReference())) + Value = str(module.GetValue()) + Value=(Value[:17] + '..') if len(Value) > 19 else Value + Value="{0:<20}".format(Value) + Rotation='{0:.1f}'.format((module.GetOrientation()/10)) + Rotation="{0:>6}".format(Rotation)+' ' + if module.GetLayer() == 0: + Layer=" top" + else: + Layer=" bottom" + #Side="## Side :"+Layer+lsep + Layer="{0:<10}".format(Layer) + content+=Reference + if 'LOCKED' in locked: + locked_fp+=Reference + ' LOCKED'+'\n' #os.linesep + #content+=Value + content+=X_POS + content+=Y_POS + #content+=str(mpOnGx) + #content+=str(mpOnGy) + content+=str(mpxOnG) + content+=str(mpyOnG) + content+=Layer+'\n' #os.linesep + if len(content)>0: + content+=str(pcbnew.FromMM(gridReference)) + wxLogDebug(content,debug) + if len (locked_fp)>0: + locked_fp+='\n'+'NOT Moved' + wxLogDebug(locked_fp,True) + else: + wxLogDebug('No Modules Selected',True) + Refresh() + #return content + + #if 0: + # #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 = RoundTrackDlg(None) + # aParameters = RoundTrack_Dlg(_pcbnew_frame) + # #aParameters = RoundTrack_DlgEx(_pcbnew_frame) + # aParameters.Show() + # #end hack + # aParameters.m_distanceMM.SetValue("5") + # aParameters.m_segments.SetValue("16") + # aParameters.m_bitmap1.SetBitmap(wx.Bitmap( os.path.join(os.path.dirname(os.path.realpath(__file__)), "round_track_help.png") ) ) + # modal_result = aParameters.ShowModal() + # segments = self.CheckSegmentsInput( + # aParameters.m_segments.GetValue(), "number of segments") + # distI = FromMM(self.CheckDistanceInput(aParameters.m_distanceMM.GetValue(), "distance from intersection")) + # if segments is not None and distI is not None: + # if modal_result == wx.ID_OK: + # Round_Selection(pcb, distI, segments) + # elif modal_result == wx.ID_DELETE: + # Delete_Segments(pcb) + # #wx.LogMessage('Round Segments on Track Net Deleted') + # elif modal_result == wx.ID_REVERT: + # wxLogDebug('Connecting Tracks',debug) + # Connect_Segments(pcb) + # else: + # None # Cancel + # else: + # None # Invalid input + # aParameters.Destroy() + + + # wxLogDebug('showing Selected Tracks',debug) + # wx.LogMessage('Select Tracks to calculate the Length\nor One Pad to select connected Tracks') +# + diff --git a/Snap2Grid/snap2grid.svg b/Snap2Grid/snap2grid.svg new file mode 100644 index 0000000..3875f66 --- /dev/null +++ b/Snap2Grid/snap2grid.svg @@ -0,0 +1,242 @@ + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Snap2Grid/snaptogrid_script.py b/Snap2Grid/snaptogrid_script.py new file mode 100644 index 0000000..d78ceb8 --- /dev/null +++ b/Snap2Grid/snaptogrid_script.py @@ -0,0 +1,96 @@ +# -*- coding: utf-8 -*- +# +# A script to Snap modules to selected Grid for kicad_pcb +# requirements: KiCAD pcbnew >= 4.0 +# copyright Maurice easyw +# +# + +#import snaptogrid; import importlib; importlib.reload(snaptogrid) +import sys, os +import pcbnew +import datetime +import wx +from pcbnew import * + +use_grid_origin = True +gridReference = 0.127 #1.27 #mm pcbnew.FromMM(1.0) #0.1mm + + +# def PutOnGridMM(value, gridSizeMM): +# thresh = FromMM(gridSizeMM) +# return round(value/thresh)*thresh +# +# def PutOnGridMils(value, gridSizeMils): +# thresh = FromMils(gridSizeMils) +# return round(value/thresh)*thresh +#def SetPosition(self, p): +# """SetPosition(wxRect self, wxPoint p)""" +# return _pcbnew.wxRect_SetPosition(self, p) + + +def Snap2Grid(gridSizeMM,use_grid_origin): + import sys,os + #mm_ius = 1000000.0 + + pcb = pcbnew.GetBoard() + gridOrigin = pcb.GetGridOrigin() + auxOrigin = pcb.GetAuxOrigin() + content='' + #wxPoint(77470000, 135890000) + for module in pcb.GetModules(): + if module.IsSelected(): + if use_grid_origin: + mpx = module.GetPosition().x - gridOrigin.x + mpy = module.GetPosition().y - gridOrigin.y + print(mpx,mpy) + mpxOnG = int(mpx/FromMM(gridSizeMM))*FromMM(gridSizeMM)+ gridOrigin.x + mpyOnG = int(mpy/FromMM(gridSizeMM))*FromMM(gridSizeMM)+ gridOrigin.y + print(mpxOnG,mpyOnG) + module.SetPosition(wxPoint(mpxOnG,mpyOnG)) + X_POS=str(module.GetPosition().x) # - gridOrigin.x) + #X_POS='{0:.4f}'.format(pcbnew.ToMM(module.GetPosition().x - gridOrigin.x )) + X_POS="{0:<11}".format(X_POS) + Y_POS=str(module.GetPosition().y) # - gridOrigin.y) + Y_POS="{0:<11}".format(Y_POS) + ## mpOnGx = PutOnGridMM(module.GetPosition().x, gridSizeMM) + ## mpOnGy = PutOnGridMM(module.GetPosition().y, gridSizeMM) + ## module.SetPosition(wxPoint(mpOnGx,mpOnGy)) + #module.SetPosition(wxPoint(mpOnGx+FromMM(100.0),mpOnGy+FromMM(2.0))) + #module.SetOrientation(10) + #Y_POS='{0:.4f}'.format(-1*pcbnew.ToMM(module.GetPosition().y - gridOrigin.y)) + # else: + # mpx = module.GetPosition().x - auxOrigin().x + # mpy = module.GetPosition().y - auxOrigin().y + # X_POS='{0:.4f}'.format(pcbnew.ToMM(module.GetPosition().x - auxOrigin().x )) + # X_POS="{0:<11}".format(X_POS) + # Y_POS='{0:.4f}'.format(-1*pcbnew.ToMM(module.GetPosition().y - auxOrigin().y)) + # Y_POS="{0:<11}".format(Y_POS) + Reference="{0:<10}".format(str(module.GetReference())) + Value = str(module.GetValue()) + Value=(Value[:17] + '..') if len(Value) > 19 else Value + Value="{0:<20}".format(Value) + Rotation='{0:.1f}'.format((module.GetOrientation()/10)) + Rotation="{0:>6}".format(Rotation)+' ' + if module.GetLayer() == 0: + Layer=" top" + else: + Layer=" bottom" + #Side="## Side :"+Layer+lsep + Layer="{0:<10}".format(Layer) + content+=Reference + #content+=Value + content+=X_POS + content+=Y_POS + #content+=str(mpOnGx) + #content+=str(mpOnGy) + content+=str(mpxOnG) + content+=str(mpyOnG) + content+=Layer+os.linesep + content+=str(pcbnew.FromMM(gridReference)) + Refresh() + return content + +reply=Snap2Grid(gridReference,use_grid_origin) +#LogMsg=reply +wx.LogMessage(reply) diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..8a147a8 --- /dev/null +++ b/__init__.py @@ -0,0 +1,14 @@ +# pcbnew loads this folder as a package using import +# thus __init__.py (this file) is executed +# We import the plugin class here and register it to pcbnew + +from . import AnnularChecker + +from . import FabricationPositions + +from . import MoveToLayer + +from . import PcbToDxf + +from . import Snap2Grid + diff --git a/action_menu_annular_check.py b/action_menu_annular_check.py deleted file mode 100644 index faaecbf..0000000 --- a/action_menu_annular_check.py +++ /dev/null @@ -1,604 +0,0 @@ -# -*- 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.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 - 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" diff --git a/action_menu_move_to_layer.py b/action_menu_move_to_layer.py deleted file mode 100644 index 75bd3b2..0000000 --- a/action_menu_move_to_layer.py +++ /dev/null @@ -1,239 +0,0 @@ -# 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 -___version___="1.1.3" - - -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 - """ - self.name = "Move Selected drawings to chosen Layer" - self.category = "Modify Drawing PCB" - self.description = "Move Selected drawings to chosen Layer on an existing PCB" - - 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, - 'F_Mask' : pcbnew.F_Mask, - 'B_Mask' : pcbnew.B_Mask, - }[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", "F_Mask", "B_Mask"] - 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.Destroy() #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 len(fileName)==0: - wx.LogMessage("a board needs to be saved/loaded!") - else: - 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() - - #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) - - - -move_to_draw_layer().register() - - -# "b64_data" is a variable containing your base64 encoded jpeg -move_to_layer_ico_b64_data =\ -""" -iVBORw0KGgoAAAANSUhEUgAAABIAAAASCAYAAABWzo5XAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAKYQAACmEB/MxKJQAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3NjYXBlLm9yZ5vuPBoAAAJrSURBVDiNnZRRSFNxFMa/s3vlLje3y0bpdKkwrcTCxbChQUIPUWBJ0IvQy3STCIkgoehFJIKCHkSCkLRBVNJT0EPQQ4W9bFmJDS0TSmwbc65ku7ZrY96dHsQ1yQfnB+flcPj9/985h0O+iz5nVtJGwGwjprEHg6N92IHErKSNTB9fcKmmDPa9qb3gdt9dmJg48hIQIswudaOwt7fXmhZXbzOhTdDgr7ZW3+rv78/lQWC2qaYMQIBSnjasrBqGAAawBqJ3ywDCACInzlod2dOT+xO1CtUFbVeFcHQawPM8iJjGGt7aPakK1Vg1Y02FYnu8AMkA7IVRJqfqv9YqlBNzWHIkTXLc0F4IImZGT09Pa06CU/udfeL3+5Nb9aD7Uvf1ZVv62pIjWbY3aP9jTgtHh4eHJzeBtqOBgQFdNB5t/x6xD4aCLksiUbGb2ZXNFzBzUQEEO4AgA8GOwrxuW9/ZJPEFgGUA5wuzRYPW7dBTAO1EH807Bq1LewRAD2jnCl4orkcbIUnj887me3M+n6+Vmbc/tUJ5PB45q9eHY01xo5zYlTT/0o8WZY1oSiYKnJybP/Q45lw0LDYmMNv2Q2biThHAlgtJBB3wvgHQWgBqAdAC4ACgo2+zdVl3JKSh4adYqkgAUUzsutx9R6lWPakK1Vg5VX7DaHx1P502HgbgBmAGCACiAAIAjwC6QDxeMmldEW5antV3gihWkhG8IhN3fjkWsYCA3JpgqXEsXPkcavwAwA9wABACzM3h/42e6gOQPzkiiGKlilSpmjIwL5UqVU1TZ2Y+dY0XOwCxJCN4D76u+XfYHg4VDQGAvyJXT3w3dEsJAAAAAElFTkSuQmCC""" - -# 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/action_menu_positions.py b/action_menu_positions.py deleted file mode 100644 index 5ac3b15..0000000 --- a/action_menu_positions.py +++ /dev/null @@ -1,420 +0,0 @@ -# -*- coding: utf-8 -*- -# -# A script to generate POS file for kicad_pcb -# requirements: KiCAD pcbnew >= 4.0 -# release "1.0.8" -# 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" -#wx.LogMessage("My message") -#mm_ius = 1000000.0 - -import sys, os -import pcbnew -import datetime -import wx -from pcbnew import * -import base64 -from wx.lib.embeddedimage import PyEmbeddedImage - -""" -execfile ("C:/kicad-wb-1602/msys64/home/userC/out3Dm/pack-x86_64/share/kicad/scripting/plugins/getpos.py") -""" - -def generate_POS(): - import os - mm_ius = 1000000.0 - - my_board = pcbnew.GetBoard() - - fileName = pcbnew.GetBoard().GetFileName() - - dirpath = os.path.abspath(os.path.expanduser(fileName)) - path, fname = os.path.split(dirpath) - ext = os.path.splitext(os.path.basename(fileName))[1] - name = os.path.splitext(os.path.basename(fileName))[0] - - #lsep=os.linesep - lsep='\n' - - LogMsg1=lsep+"reading from:" + lsep + dirpath + lsep + lsep - out_filename_top_SMD=path+os.sep+name+"_POS_top_SMD.txt" - out_filename_bot_SMD=path+os.sep+name+"_POS_bot_SMD.txt" - out_filename_top_THD=path+os.sep+name+"_POS_top_THD.txt" - out_filename_bot_THD=path+os.sep+name+"_POS_bot_THD.txt" - out_filename_top_VIRTUAL=path+os.sep+name+"_POS_top_Virtual.txt" - out_filename_bot_VIRTUAL=path+os.sep+name+"_POS_bot_Virtual.txt" - out_filename_ALL=path+os.sep+name+"_POS_All.txt" - #out_filename=path+os.sep+name+"_POS.txt" - LogMsg1+="written to:" + lsep + out_filename_top_SMD + lsep - LogMsg1+= out_filename_bot_SMD + lsep - LogMsg1+= out_filename_top_THD + lsep - LogMsg1+= out_filename_bot_THD + lsep - LogMsg1+= out_filename_top_VIRTUAL + lsep - LogMsg1+= out_filename_bot_VIRTUAL + lsep - LogMsg1+= out_filename_ALL + lsep - # print (LogMsg) - # - # print ("### Module positions - created on %s ###" % datetime.datetime.now().strftime("%Y-%m-%d %H:%M")) - # print ("### Printed by get_pos v1") - # print ("## Unit = mm, Angle = deg.") - # print ("## Side : All") - # print ("# Ref Val Package PosX PosY Rot Side Type") - - Header_1="### Module positions - created on " + datetime.datetime.now().strftime("%Y-%m-%d %H:%M")+lsep - Header_1+="### Printed by get_pos v1"+lsep - Header_1+="## Unit = mm, Angle = deg."+lsep - #LogMsg+="## Side : All"+lsep - Header_2="## Board Aux Origin: " + str(my_board.GetAuxOrigin())+lsep - - Header_2+="{0:<10}".format("# Ref")+"{0:<20}".format("Val")+"{0:<20}".format("Package")+\ - "{0:<11}".format("PosX")+"{0:<11}".format("PosY")+"{0:<8}".format(" Rot")+\ - "{0:<10}".format(" Side")+" Type"+lsep - content_top_SMD='' - content_bot_SMD='' - content_top_THD='' - content_bot_THD='' - content_top_VIRTUAL='' - content_bot_VIRTUAL='' - content_ALL='' - SMD_pads = 0 - TH_pads = 0 - Virt_pads = 0 - TH_top_cnt = 0 - TH_bot_cnt = 0 - SMD_top_cnt = 0 - SMD_bot_cnt = 0 - Virt_top_cnt = 0 - Virt_bot_cnt = 0 - - bb = my_board.GetBoardEdgesBoundingBox() - pcb_height = bb.GetHeight() / mm_ius - pcb_width = bb.GetWidth() / mm_ius - #to add relative position to - #print ("Board Aux Origin: " + str(my_board.GetAuxOrigin())) - - #'{0:<10} {1:<10} {2:<10}'.format(1.0, 2.2, 4.4)) - - for module in my_board.GetModules(): - #print ("%s \"%s\" %s %1.3f %1.3f %1.3f %s" % ( module.GetReference(), - #Nchars = 20 - # RefL = 10; ValL = 20 - - md="" - if module.GetAttributes() == 0: # PTH=0, SMD=1, Virtual = 2 - md = "THD" - TH_pads+=module.GetPadCount() - elif module.GetAttributes() == 1: - md = "SMD" - SMD_pads+=module.GetPadCount() - else: - md = "VIRTUAL" - Virt_pads+=module.GetPadCount() - - #Reference=str(module.GetReference()).ljust(Nchars/2) - #Value=str(module.GetValue()).ljust(Nchars) - #Package=str(module.GetFPID().GetLibItemName()).ljust(Nchars) - #X_POS=str(pcbnew.ToMM(module.GetPosition().x - my_board.GetAuxOrigin().x )) - #Y_POS=str(-1*pcbnew.ToMM(module.GetPosition().y - my_board.GetAuxOrigin().y)) - #Rotation=str(module.GetOrientation()/10) - #Layer="top".ljust(Nchars) if module.GetLayer() == 0 else "bottom".ljust(Nchars) - #Type=md - Reference="{0:<10}".format(str(module.GetReference())) - Value = str(module.GetValue()) - Value=(Value[:17] + '..') if len(Value) > 19 else Value - Value="{0:<20}".format(Value) - Package = str(module.GetFPID().GetLibItemName()) - Package=(Package[:17] + '..') if len(Package) > 19 else Package - Package="{0:<20}".format(Package) - #Package="{0:<20}".format(str(module.GetFPID().GetLibItemName())) - X_POS='{0:.4f}'.format(pcbnew.ToMM(module.GetPosition().x - my_board.GetAuxOrigin().x )) - X_POS="{0:<11}".format(X_POS) - Y_POS='{0:.4f}'.format(-1*pcbnew.ToMM(module.GetPosition().y - my_board.GetAuxOrigin().y)) - Y_POS="{0:<11}".format(Y_POS) - Rotation='{0:.1f}'.format((module.GetOrientation()/10)) - Rotation="{0:>6}".format(Rotation)+' ' - if module.GetLayer() == 0: - Layer=" top" - else: - Layer=" bottom" - #Side="## Side :"+Layer+lsep - Layer="{0:<10}".format(Layer) - Type=' '+md - content=Reference - content+=Value - content+=Package - content+=X_POS - content+=Y_POS - content+=Rotation - content+=Layer - content+=Type+lsep - if 'top' in Layer and 'SMD' in Type: - content_top_SMD+=content - SMD_top_cnt+=1 - elif 'bot' in Layer and 'SMD' in Type: - content_bot_SMD+=content - SMD_bot_cnt+=1 - elif 'top' in Layer and 'THD' in Type: - content_top_THD+=content - TH_top_cnt+=1 - elif 'bot' in Layer and 'THD' in Type: - content_bot_THD+=content - TH_bot_cnt+=1 - elif 'top' in Layer and 'VIRTUAL' in Type: - content_top_VIRTUAL+=content - Virt_top_cnt+=1 - elif 'bot' in Layer and 'VIRTUAL' in Type: - content_bot_VIRTUAL+=content - Virt_bot_cnt+=1 - content_ALL+=content - #print ("%s %s %s %1.4f %1.4f %1.4f %s %s" % ( str(module.GetReference()).ljust(Nchars), - # str(module.GetValue()).ljust(Nchars), - # str(module.GetFPID().GetLibItemName()).ljust(Nchars), - # pcbnew.ToMM(module.GetPosition().x - my_board.GetAuxOrigin().x ), - # -1*pcbnew.ToMM(module.GetPosition().y - my_board.GetAuxOrigin().y), - # module.GetOrientation()/10, - # "top".ljust(Nchars) if module.GetLayer() == 0 else "bottom".ljust(Nchars), - # md - # )) - -#"top" if module.GetLayer() == 0 else "bottom" - - #LogMsg+="## End"+lsep - - Header=Header_1+"## Side : top"+lsep+Header_2 - content=Header+content_top_SMD+"## End"+lsep - with open(out_filename_top_SMD,'w') as f_out: - f_out.write(content) - Header=Header_1+"## Side : bottom"+lsep+Header_2 - content=Header+content_bot_SMD+"## End"+lsep - with open(out_filename_bot_SMD,'w') as f_out: - f_out.write(content) - Header=Header_1+"## Side : top"+lsep+Header_2 - content=Header+content_top_THD+"## End"+lsep - with open(out_filename_top_THD,'w') as f_out: - f_out.write(content) - Header=Header_1+"## Side : bottom"+lsep+Header_2 - content=Header+content_bot_THD+"## End"+lsep - with open(out_filename_bot_THD,'w') as f_out: - f_out.write(content) - Header=Header_1+"## Side : top"+lsep+Header_2 - content=Header+content_top_VIRTUAL+"## End"+lsep - with open(out_filename_top_VIRTUAL,'w') as f_out: - f_out.write(content) - Header=Header_1+"## Side : bottom"+lsep+Header_2 - content=Header+content_bot_VIRTUAL+"## End"+lsep - with open(out_filename_bot_VIRTUAL,'w') as f_out: - f_out.write(content) - Header=Header_1+"## Side : ALL"+lsep+Header_2 - content=Header+content_ALL+"## End"+lsep - content = content + '## '+ str(SMD_pads) + ' SMD pads' +lsep - content = content + '## '+ str(TH_pads) + ' TH pads' +lsep - content = content + '## '+ str(Virt_pads) + ' Virtual pads' +lsep - content = content + '## '+ str( TH_top_cnt) + ' Top TH modules' + lsep - content = content + '## '+ str( TH_bot_cnt) + ' Bot TH modules' + lsep - content = content + '## '+ str( SMD_top_cnt) + ' Top SMD modules' + lsep - content = content + '## '+ str( SMD_bot_cnt) + ' Bot SMD modules' + lsep - content = content + '## '+ str( Virt_top_cnt) + ' Top Virtual modules' + lsep - content = content + '## '+ str( Virt_bot_cnt) + ' Bot Virtual modules' + lsep - - with open(out_filename_ALL,'w') as f_out: - f_out.write(content) - - #with open(out_filename,'w') as f_out: - # f_out.write(LogMsg) - #LogMsg="" - #f = open(out_filename,'w') - #f.write(LogMsg) - #f.close() - LogMsg1+= lsep + str(SMD_pads) + ' SMD pads' +lsep - LogMsg1+= str(TH_pads) + ' TH pads' +lsep - LogMsg1+= str(Virt_pads) + ' Virtual pads' +lsep - LogMsg1+= str( TH_top_cnt) + ' Top TH modules' + lsep - LogMsg1+= str( TH_bot_cnt) + ' Bot TH modules' + lsep - LogMsg1+= str( SMD_top_cnt) + ' Top SMD modules' + lsep - LogMsg1+= str( SMD_bot_cnt) + ' Bot SMD modules' + lsep - LogMsg1+= str( Virt_top_cnt) + ' Top Virtual modules' + lsep - LogMsg1+= str( Virt_bot_cnt) + ' Bot Virtual modules' + lsep - LogMsg1+= '{0:.3f}'.format( pcb_height ) + 'mm Pcb Height, ' + '{0:.3f}'.format( pcb_width ) + 'mm Pcb Width [based on Edge bounding box]' +lsep - - return LogMsg1 - #return LogMsg1+LogMsg - - - - -class generatePOS( pcbnew.ActionPlugin ): - """ - A script to generate POS file for kicad_pcb - requirements: KiCAD pcbnew >= 4.0 - release "1.0.1" - - """ - - 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 = "Generate POS output" - self.category = "Fabrication Output" - self.description = "Generate POS output for SMD, THD, Virtual" - - def Run( self ): - - #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 - """ - - #---------------------------------------------------------------------- - #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="Generate POS output")# - #, 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(round_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:") - #self.result = wx.StaticText(self.panel, label="") - #self.result.SetForegroundColour('#FF0000') - self.button = wx.Button(self.panel, label="Close") - #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 = (600, 500), 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.sizer.Add(self.result, (1, 0)) - #self.sizer.Add(self.lblname, (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.Show() - 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.Destroy() - - #def OnButton(self, e): - # self.result.SetLabel(self.editname.GetValue()) - def setMsg(self, t_msg): - self.editname.SetValue(t_msg) - - - - def f_mm(raw): - return repr(raw/mm_ius) - - board = pcbnew.GetBoard() - - #fileName = GetBoard().GetFileName() - fileName = pcbnew.GetBoard().GetFileName() - if len(fileName)==0: - wx.LogMessage("a board needs to be saved/loaded!") - else: - LogMsg='' - # msg="'get_pos.py'"+os.linesep - msg="Generate POS output: version = "+___version___+os.linesep - #msg+="Generate POS output"+os.linesep - #print (msg) - #LogMsg=msg+'\n\n' - - print(msg) - LogMsg+=msg - reply=generate_POS() - LogMsg+=reply - - 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() - -generatePOS().register() - -# "b64_data" is a variable containing your base64 encoded jpeg -round_ico_b64_data =\ -""" -iVBORw0KGgoAAAANSUhEUgAAAEAAAAA/CAYAAABQHc7KAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAdDwAAHQ8Bjlx1kwAAABl0RVh0U29mdHdhcmUAd3d3Lmlua3Nj -YXBlLm9yZ5vuPBoAAAw+SURBVGiB7Zt7cFTVHcc/d+9uSAIhklgIpiQ0tjxSkCKIQ1CpCO3wUKCW0cF0Bqf/VFTG6tROS/8QZuxYwZGHgqD4SjNToFFDjJjGB4LkyUPk -FUhClETShCRssslu9u599I/Nvbm7e/eRENCZ8p3Zufec8zvn/H7f+zu/87h34QZu4Ab+nyGEyYsD7NdZl2sNGZAAzZxpZWQiMAu4FXBce72uC3xAPVAF9JgLrAiwA+lP -PfXU4hUrVky9Dspdc+zdu/fkpk2bCrCw14oAAbBNmjTp5pkzZ97a5epGEPoL9IQQdA25D2k1qMxKVgisFakNHZs2bWLlypWMHj3awhQYNmwYJ0+ebAZsVk3aLGv1QVYU -ZFnu+yl9aQVFUcjLyyM/Px9ZllEUf17Afd9VUVUUVUVVFFRVRQ2TNvKs8vWfXm6SO1Jdzdx77uHMmTP9ciZZTVUjmRiZAPCTLghCwBXg788/zz9eeCHAE0LuBQGhL23c -h0kbeWHyjTLzr0/HxsZG5t17L18cOBBaNwoiEmAoa1xDGw2QsboPJiFc2pQXjQizDIDNZsPtdrNs2TLy8vJCZQdLQCjrFsxaPeUo95ZpU14AOWYiTOXBdXXXX716NX96 -9lk0TQvwkkERoFcODnghgW8gJERKR/IGKzJMOgJomn+K375tG7/LzaXX44lifpTFTllZGUePHQtkURDQNA2Px4NXknjzzTdDys3khc0PJxemXjjZhoYGw3Az9u3bx8KF -CyksLIzYfEQCSkpKeOeddyzLuru7EQSBDRs2hFXuesDpdKJaRHpN06iurubJJ59kwYIFYetHJOCOWbPw9PZaPuFdb7yBzWbj4Yceiv40YygfMIF9bX5YVERtbW2IFwiC -QE5ODjt37mTv3r1hm4lIwJIlS5g3b56/QZOiGrB7924cdjvr1q8PKAsxKJZ7q7TJyEjkfNPQQH19PYqimKoJLFy4kPz8fJKSkiKZGJmAkCCoK6pp/WVm5a+WBFNfQZkD -8pBHHnmEHTt2YLfbo64Fou74QmYAQLOYBgdNAhZGRzA4mkFPPPEEL730UkyLIIhGgIXxugcEK3S1JMTsDRZQFMUIyGvWrImpjo7YPcCkpOEBkQwWBPaXfMTxmhNBDcK0 -n03lgcUPAHDoy0N8fuSLkH4zR2fwaO4qwD/VfVBaiKwFRvvh9gRUVcVut/P666+Tm5trlFlNjVYYdAwISAfJ6XmHK8v4IvEoTROvGG2m142is7yTpUuWAnDsq2Mc6Kyg -7hethszNl5K4/VAWj+auQhAE6uvrKTtRQfn8OkPGIYnMLrwVURQpKChg0aJFgboPyRCwMp7AGBCJBAToSZFoHe8y5Ee2J8AVk6wg4B4ZKOPw2UOMUIcRIBPn8cs899xz -TJo0KSZjrRDTsVfI+Da7l8Uw6Ffc+ikIxP6EoiE5Ofmq6kf3AAgxMpwHfPbZZ5w9e9You9zaCumhbbZ3tLNlyxYA6urqIDNUxuv1snnzZgBaWlqQZdlSv3fffZf4+Hhy -cnKYNWtWqP5RiI56HhBxmgvKLy8vB2DOnDnMmTOHESNGWDaZmJhITk4OOTk5pKamWsrY7aIhk5WVhc1mrer06dNxOp0cPXo0qimW/UQsDWN82BggCKSnpzN79mwA3vvw -fX5SO5oxnaMM+cTLdoYnDDdkyivL+XFtKjd9OtKQGeYUcTjiDBmXy0V8pYPZX04wZGyyYBBQW1s7KOMhllnAioQoMUDHY7//Ay0tLSHtms/vVj68ktkNs0NkUlJSjPs7 -77zTckkr/lIMaGswcWVIY0Dw3JuVlUVWVlbE9tPS0khLS4sok5ycbHjDUGPoZgH8Z3NNTU0kJSWxePHiIYv01xLRCQjakem7QUxpv5hARkYGY8aMoaioCFmWWbZs2VDq -GkXN2A5BgxH9TLDvqndivpo715GamsqMGTM4ffr0gJW5FriqaTDY9TFfgzoQBAFJkqitreXgwYPGOcIPHbEFQQgbBP3Z/nRHRwc+n4+1a9cyefLkodU0Bpif9pBshkwt -+y96MkynaWlpZGdnfy/GB2PINkNWcSBWdq8nNE2zPBeMhoENgQgNW+VVH6nmxNmvQ/LHpY3j1wt+BcCpU6c4fLwcgpRPSUphxfLfAtDW1kZxSTGKpgTIxIlxLH9guZHW -D0bMOl31kVhfS0aDA8H+0o8pvXKISxM6jbyUS8O5rTzLIKCiooLdp9+n9vb+FePI9gR+/tEtBgHnzp2jqORDjt9zsb9xDW7/TwazZvg3QKqqGgQEzFaCQKTXo7EvhPT7 -viEQ7rX4qVOnyM/PB6C5uRlnhocLt102ymVRwdPgMWTOnz+Pe6QUcGjyo0YZrUwzZJqampBFNaAdnYCSkhIaGhrIzMzE4/EYBNhsNmw2G6IjLuIb4tgXQmGevjn/wQcf -5MKFC0ZaFMWwdRITE/0K2MOoIEBCQgIADocj3NECmqYxY8YMJk6cSG9vbwgBDg1kNXzMijkG9GluGGCFKVOmMHVq/0clJ8+dspSLj483Vont7e183lQd2q8gcP/99+Pz -+SgrK+PLM+WWbU2bNo2MjAyAAA/QMUzRkP3vDFSCvg+CQU6D1wterxdZlvH5fBaq98u43W4jrSgKPp8PSZKwO+LA5ux5b++ec8Bl/B9KBSC2Y/GArP5pMNKS2F9P4Obm -EUyuGmvkJbckGC3q8qNaEpl+qP9YKL7LDhpIkoTP50OWZWwSTKxMM7XtR3d3Nx0dHciyTG9vLz6fj4SEBNLSxqKiKo89trqg/PDh94BKoJ+pWAiwcv+B4Df3L2dq7ZTA -zHQYO7afkAULFpCZ6Tde0zQjmjvmOAwC0tPTmTf7XuZIXhRZ//RGwX6Hnbi4OGRZZtSoUaSmppCcfBM9PT18d+k75fHVj79TVVX1NvAV0M2ghkCM7m/lDdnZ2WRnZ4ct -B8jMzGTcuHGG8ZIkBfz0YZCamsrSpUsRRRGHw2F4ob4AkmUfvb1eOjs7udh4UXn6j0+/VVVVtR2oATxWxsdGwDWGboDfCNkY8/pPkiR6enpwu93Ex8cbdfSrqqrIsg+P -x4PT6eSbb79R/vqXtTurqqq2Ahew+DjSjAHNAkN1wGG1adE0zfjSLJgISZJCluC6vM/nw+12c+VKB3X1dfL6deu2VlRUvQpcxB/0Iq7bIxKQl5fHli1bjFfPuuKaptHW -1oYgCMycOTOknsPh4OWXXyYnJyfEYDOCjTf/9E/uZFnG6/WGPH1ZlpEkL93dPbS1X+ZszVnpxRc3bDxSdWQb0IJFxB8wAbm5uRw4cCDsVyIAJ06cCMlbv369YXw06O5v -/sZP6fsGUFEUJEnC7XaTkpISMFS83l66uly0Xm7h5Mmvezdv2bru+NHjbwFtgBKtXx2RZwFBYNu2bfT09FBQUBB1FygIAqtWrWLt2rWx9m/ATIB5OHi9XlwuF9nZ2X2k -yLjdHjo7nTT/t5nyyrIru3a+9cyZM2c+AJxEcflgRI0Boijy9ttv09XVRWlpaVgSbDYb8+fPZ/v27TF3HhzJzd6g30uShMvlYvz48fh8Em63hytXOmhsaqSoqLAm/5// -WtPa2lpBmGkuGmKaBRwOB7t372bRokVUVlaGfJQkiiJTpkxhz5494df2MSB4T6+qKh6Ph+TkZETRRleXi/b2Nuov1Klv7Nr10b4Piv4GnAd6GYTxMIBpMDExkX379nHf -ffdx+vRpIzCKosjYsWMpLi4O+yosVpjX8fpY7+rqYsKECTidnbRebuHoseM9217ZuvX48RM7gO+IIdJHQkwEaPinw+TkZIqLi5k7dy4XL15E0zSGDx/O/v37o77ciITA -r1H7SXC73Xg8Hm65ZSyNTRcpeP+D8zu3v/Z8V1dXCQMMduEwYH8dM2YMpaWl3HXXXbS3t1NYWDjo9/Pmud1mswUQoCgKLpeLn06YQH3DBWXjxo3FpSWlm4FjQCdX8dTN -iDoLmE9X9PuMjAw++eQTampquPvuu43ywUAQBASbDVEUsdsdKKqKqCgomkB80k1aw7ffOv/8zNM72tvb/41/WetmiIwHawI0QK2pqWmrqKioj1Q5LS2NysrKq1ZC1TQ0 -DXyyik9V8PlkXG43Bz8tbXzt1VcKgI/xr+x8DKHxYL3HGc73/58hAX9wawaq8e/lr3q8h+vIKu+H8q8xBf9mJvLfPm7gBm7gBgaJ/wGnp66JbeWfegAAAABJRU5ErkJg -gg== -""" - - - -# execfile("round_tracks.py") -