← Back to team overview

kicad-developers team mailing list archive

[PATCH] Add/modify python APIs for querying available pcbnew footprints

 

Note: I have attempted to follow the kicad coding standards. Surely,
there's something  that I missed.
Please, instead of: "syntax error. Please see the coding guidelines",
please give an idea of the nature of things I missed. ;-)


This change adds the python apis: GetLogicalLibs and FootprintInfo. Extends
FootprintEnumerate

*GetLogicalLibs* will return the list of available footprint libraries
*FootprintEnumerate* lists the footprints in a library. It used to only
take a path to a .pretty library. Now,
it also excepts a library name
*FootprintInfo* will return basic information about a particular footprint
(num pads...)



*A couple of interesting ways in which this patch may be different from
other python interface patches:*
created the file autodoc.i which gives help messages (docstrings as they
are called in python) for the new APIs. The default docstrings generated by
SWIG are often not that helpful. knowing that a function takes a string is
not enough. What should that string contain. It seemed to me these messages
should be together.

created the file swig_typemaps.i: a vector<int>, by default is returned as
a point to a vector instance. in swig_typemaps.i should go directives for
when to expand stl templates as well as customized transformations of
helper classes.

exposed pcb_edit_frame pointer stored in pcbnew_scripting_helpers to other
parts of the SWIG files. This doesn't expose it to python. pcb_edit_frame
has a lot of interesting methods and also many that should be excluded.


Thank you to Orson (Maciej Sumiński) and Jeff Young for their hints in this
thread:
https://lists.launchpad.net/kicad-developers/msg34925.html




Miles



for personal reference: patch 7/8
From 3c898d25be636c9a8556c03de3158f4ac02d88b7 Mon Sep 17 00:00:00 2001
From: Miles McCoo <mail@xxxxxxxxxx>
Date: Fri, 16 Mar 2018 16:20:05 +0100
Subject: [PATCH 2/2] Add/modify python APIs for querying available pcbnew
 footprints
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------2.7.4"

This is a multi-part message in MIME format.
--------------2.7.4
Content-Type: text/plain; charset=UTF-8; format=fixed
Content-Transfer-Encoding: 8bit


This change adds the python apis: GetLogicalLibs and FootprintInfo. Extends FootprintEnumerate

GetLogicalLibs will return the list of available footprint libraries
FootprintEnumerate lists the footprints in a library. It used to only take a path to a .pretty library. Now,
it also excepts a library name
FootprintInfo will return basic information about a particular footprint (num pads...)

A couple of interesting ways in which this patch may be different from other python interface patches:
created the file autodoc.i which gives help messages (docstrings as they are called in python) for the new APIs. The default docstrings generated by SWIG are often not that helpful. knowing that a function takes a string is not enough. What should that string contain. It seemed to me these messages should be together.

created the file swig_typemaps.i: a vector<int>, by default is returned as a point to a vector instance. in swig_typemaps.i should go directives for when to expand stl templates as well as customized transformations of helper classes.

exposed pcb_edit_frame pointer stored in pcbnew_scripting_helpers to other parts of the SWIG files. This doesn't expose it to python. pcb_edit_frame has a lot of interesting methods and also many that should be excluded.

Thank you to Orson (Maciej Sumiński) and Jeff Young for their hints.
---
 pcbnew/CMakeLists.txt                    |   2 +
 pcbnew/swig/autodoc.i                    |  47 ++++++++++++
 pcbnew/swig/footprint.i                  | 125 ++++++++++++++++++++++++++++++-
 pcbnew/swig/pcbnew.i                     |   8 ++
 pcbnew/swig/pcbnew_scripting_helpers.cpp |   6 ++
 pcbnew/swig/pcbnew_scripting_helpers.h   |   1 +
 pcbnew/swig/swig_typemaps.i              |  96 ++++++++++++++++++++++++
 7 files changed, 281 insertions(+), 4 deletions(-)
 create mode 100644 pcbnew/swig/autodoc.i
 create mode 100644 pcbnew/swig/swig_typemaps.i


--------------2.7.4
Content-Type: text/x-patch; name="0002-Add-modify-python-APIs-for-querying-available-pcbnew.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="0002-Add-modify-python-APIs-for-querying-available-pcbnew.patch"

diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt
index 520dc11..310c79e 100644
--- a/pcbnew/CMakeLists.txt
+++ b/pcbnew/CMakeLists.txt
@@ -439,6 +439,7 @@ if( KICAD_SCRIPTING )   # Generate pcbnew.py and pcbnew_wrap.cxx using swig
         DEPENDS plotcontroller.h
         DEPENDS exporters/gendrill_Excellon_writer.h
         DEPENDS swig/pcbnew.i
+        DEPENDS swig/autodoc.i
         DEPENDS swig/board.i
         DEPENDS swig/board_connected_item.i
         DEPENDS swig/board_design_settings.i
@@ -456,6 +457,7 @@ if( KICAD_SCRIPTING )   # Generate pcbnew.py and pcbnew_wrap.cxx using swig
         DEPENDS swig/pad.i
         DEPENDS swig/pcb_text.i
         DEPENDS swig/plugins.i
+        DEPENDS swig/swig_typemaps.i
         DEPENDS swig/text_mod.i
         DEPENDS swig/track.i
         DEPENDS swig/units.i
diff --git a/pcbnew/swig/autodoc.i b/pcbnew/swig/autodoc.i
new file mode 100644
index 0000000..56cd5b3
--- /dev/null
+++ b/pcbnew/swig/autodoc.i
@@ -0,0 +1,47 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.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, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+
+// The purpose of this file is to contain the help message docstrings seens from
+// the python interface.
+// SWIG will generate a default docstring but that is limited to the signature
+// of the function.
+// By specifying these docstrings manually, we can include additional hints.
+
+%feature("autodoc", "return a list of modules in a library. 
+Takes either a library name or a path to a .pretty directory:
+mods = pcbnew.FootprintEnumerate('MountingHole')
+or
+mods = pcbnew.FootprintEnumerate( '/home/user/kicad/kicad-footprints/MountingHole.pretty' )
+") FootprintEnumerate;
+
+%feature("autodoc", "Returns information about a footprint.
+example: pcbnew.FootprintInfo('MountingHole:MountingHole_3.2mm_M3')
+The ':' delimiter between the library and modules is important.
+Example output:
+{ 'uniquepadcount': 0, 'padcount': 0, 'name': 'MountingHole_3.2mm_M3', 
+  'lib': 'MountingHole', 'doc': 'Mounting Hole 3.2mm, no annular, M3', 
+  'ordernum': 0, 'keywords': 'mounting hole 3.2mm no annular m3'}") FootprintInfo;
+
+%feature("autodoc", "returns a list of all available footprint libraries") GetLogicalLibs;
+
diff --git a/pcbnew/swig/footprint.i b/pcbnew/swig/footprint.i
index 844ea31..8c410e2 100644
--- a/pcbnew/swig/footprint.i
+++ b/pcbnew/swig/footprint.i
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2012 NBEE Embedded Systems, Miguel Angel Ajo <miguelangel@xxxxxxx>
- * Copyright (C) 1992-2017 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.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
@@ -97,15 +97,132 @@
     %}
 }
 
+
+%inline %{
+#include <project.h>
+#include <fp_lib_table.h>
+#include <kiway.h>
+#include <stdexcept>
+
+
+    /* This FootprintEnumerate replaces this old python code implementation:
+       def FootprintEnumerate( libname ):
+           plug = GetPluginForPath( libname )
+           return plug.FootprintEnumerate( libname )
+    */    
+    wxArrayString FootprintEnumerate( const wxString& libname_or_path )
+    {
+
+        /* The old behavior is to take a library directory path
+           and return the modules within that.
+           Here is an example of such a call:
+           mods = pcbnew.FootprintEnumerate( '/home/user/kicad/kicad-footprints/MountingHole.pretty' )
+           print( "dir path access has {} modules", len( mods ) )
+        */
+        wxFileName file( libname_or_path );
+        if ( file.DirExists() )
+        {
+            try
+            {
+                IO_MGR::PCB_FILE_T plugin_type = IO_MGR::GuessPluginTypeFromLibPath( libname_or_path );
+                PLUGIN* plugin = IO_MGR::PluginFind( plugin_type );
+                wxArrayString aFootprintNames;
+                plugin->FootprintEnumerate( aFootprintNames, libname_or_path );
+                return aFootprintNames;
+            }
+            catch ( IO_ERROR& e )
+            {
+                // this could happen if the user just happens to have a directory with the same
+                // name as the libname.
+                std::cout << "got IO_ERROR " << e.Problem() << " at " << e.Where() << std::endl;
+            }
+        }
+
+        /* here, we try to enumerate by the library's name (the nickname)
+           for example:
+           mods = pcbnew.FootprintEnumerate( 'MountingHole' )
+           print("lib has {} modules", len(mods))
+        */       
+        PCB_EDIT_FRAME* edit_frame = ScriptingGetPcbEditFrame();
+        if ( edit_frame )
+        {
+            PROJECT *prj =  &edit_frame->Prj();
+            FP_LIB_TABLE* fp_table = prj->PcbFootprintLibs( edit_frame->Kiway() );
+
+            try
+            {
+                wxArrayString aFootprintNames;
+                fp_table->FootprintEnumerate( aFootprintNames, libname_or_path );
+                return aFootprintNames;
+            }
+            catch ( IO_ERROR& e )
+            {
+                std::cout << "got IO_ERROR " << e.Problem() << " at " << e.Where() << std::endl;
+            }
+        }
+
+        return {};
+    }
+
+    
+    /* This function enables this python code:
+       for lib in pcbnew.GetLogicalLibs():
+           mods = pcbnew.FootprintEnumerate( lib )
+           print("lib {} has {} modules".format( lib, len( mods ) ) )
+    */
+    std::vector<wxString> GetLogicalLibs()
+    {
+        PCB_EDIT_FRAME* edit_frame = ScriptingGetPcbEditFrame();
+        if ( edit_frame )
+        {
+            PROJECT *prj =  &edit_frame->Prj();
+            FP_LIB_TABLE* fp_table = prj->PcbFootprintLibs( edit_frame->Kiway() );
+            return fp_table->GetLogicalLibs();
+        }
+
+        return {};
+    }
+
+    /* This function returns information about a footprint. As an example. this:
+       pcbnew.FootprintInfo( 'MountingHole:MountingHole_3.2mm_M3' )
+       yields this:
+       { 'uniquepadcount': 0, 'padcount': 0, 'name': 'MountingHole_3.2mm_M3', 
+         'lib': 'MountingHole', 'doc': 'Mounting Hole 3.2mm, no annular, M3', 
+         'ordernum': 0, 'keywords': 'mounting hole 3.2mm no annular m3'}
+    */
+    SWIG_HELPER_CLASSES::FOOTPRINT_INFO FootprintInfo( const wxString &modname )
+    {
+        // I'm using BeforeLast because it returns the empty string if ':' is
+        // not found. makes it easy to check for success.
+        wxString libname = modname.BeforeLast( ':' );
+        if ( libname.IsEmpty() )
+        {
+            throw std::invalid_argument( std::string( "expected module name of format libname:modname. got ") +
+                                         modname.ToStdString() );
+        }
+
+        
+        PCB_EDIT_FRAME* edit_frame = ScriptingGetPcbEditFrame();
+        if ( edit_frame )
+        {
+            PROJECT *prj =  &edit_frame->Prj();
+            std::unique_ptr<FOOTPRINT_LIST> fpl = FOOTPRINT_LIST::GetInstance( edit_frame->Kiway() );
+            fpl->ReadFootprintFiles( prj->PcbFootprintLibs(), &libname );
+            FOOTPRINT_INFO* footprint = fpl->GetModuleInfo( modname );
+            if ( footprint ) {
+                return SWIG_HELPER_CLASSES::FOOTPRINT_INFO( *footprint );
+            }
+        }
+        return SWIG_HELPER_CLASSES::FOOTPRINT_INFO();
+    }
+%}
+
 %pythoncode
 %{
     def GetPluginForPath(libname):
         plugin_type = IO_MGR.GuessPluginTypeFromLibPath( libname );
         return IO_MGR.PluginFind(plugin_type)
 
-    def FootprintEnumerate(libname):
-        plug = GetPluginForPath(libname)
-        return plug.FootprintEnumerate(libname)
 
     def FootprintLoad(libname,name):
         plug = GetPluginForPath(libname)
diff --git a/pcbnew/swig/pcbnew.i b/pcbnew/swig/pcbnew.i
index 3181df7..da48781 100644
--- a/pcbnew/swig/pcbnew.i
+++ b/pcbnew/swig/pcbnew.i
@@ -35,6 +35,12 @@
 %include "docstrings.i"
 #endif
 
+// docstrings.i above comes from doxygen. The docstrings contained
+// in autodoc.i are manually writen for the python world.
+%include autodoc.i
+
+%include swig_typemaps.i
+
 // support for wchar_t
 %include "cwstring.i"
 
@@ -94,6 +100,8 @@ Therefore please help gather the subset of C++ functions for this class that do
 throw and add them here, before the class declarations.
 
 */
+HANDLE_EXCEPTIONS(FootprintEnumerate)
+HANDLE_EXCEPTIONS(FootprintInfo)
 HANDLE_EXCEPTIONS(PLUGIN::Load)
 HANDLE_EXCEPTIONS(PLUGIN::Save)
 HANDLE_EXCEPTIONS(PLUGIN::FootprintEnumerate)
diff --git a/pcbnew/swig/pcbnew_scripting_helpers.cpp b/pcbnew/swig/pcbnew_scripting_helpers.cpp
index 065c029..21cb612 100644
--- a/pcbnew/swig/pcbnew_scripting_helpers.cpp
+++ b/pcbnew/swig/pcbnew_scripting_helpers.cpp
@@ -59,6 +59,12 @@ void ScriptingSetPcbEditFrame( PCB_EDIT_FRAME* aPcbEditFrame )
 }
 
 
+PCB_EDIT_FRAME* ScriptingGetPcbEditFrame()
+{
+    return s_PcbEditFrame;
+}
+
+
 BOARD* LoadBoard( wxString& aFileName )
 {
     if( aFileName.EndsWith( wxT( ".kicad_pcb" ) ) )
diff --git a/pcbnew/swig/pcbnew_scripting_helpers.h b/pcbnew/swig/pcbnew_scripting_helpers.h
index 6233b3a..f200936 100644
--- a/pcbnew/swig/pcbnew_scripting_helpers.h
+++ b/pcbnew/swig/pcbnew_scripting_helpers.h
@@ -35,6 +35,7 @@
 #ifndef SWIG
 void    ScriptingSetPcbEditFrame( PCB_EDIT_FRAME* aPCBEdaFrame );
 
+PCB_EDIT_FRAME* ScriptingGetPcbEditFrame();
 #endif
 
 // For Python scripts: return the current board.
diff --git a/pcbnew/swig/swig_typemaps.i b/pcbnew/swig/swig_typemaps.i
new file mode 100644
index 0000000..403ce15
--- /dev/null
+++ b/pcbnew/swig/swig_typemaps.i
@@ -0,0 +1,96 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 1992-2018 KiCad Developers, see AUTHORS.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, you may find one here:
+ * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
+ * or you may search the http://www.gnu.org website for the version 2 license,
+ * or you may write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
+ */
+
+
+%{
+#include <vector>
+#include <memory>
+#include <footprint_info.h>
+%}
+
+%include "std_vector.i"
+namespace std
+{
+    %template( vector_wxstring ) vector<wxString>;
+}
+
+%{
+    class SWIG_HELPER_CLASSES {
+      public:
+        // I can't pass a FOOTPRINT_INFO asis because it's owned
+        // by a unique_ptr which will go out of scope by the time
+        // it gets to the swig wrap stuff.
+        // I can't copy construct FOOTPRINT_INFO because it is abstract.
+        // This struct is just to hold the data long enough to pythonify it.
+        struct FOOTPRINT_INFO {
+            FOOTPRINT_INFO() : initialized(false) { /* empty */ };
+            FOOTPRINT_INFO( ::FOOTPRINT_INFO &footprint )
+                : name( footprint.GetFootprintName() ),
+                  nickname( footprint.GetNickname() ),
+                  doc( footprint.GetDoc() ),
+                  keywords( footprint.GetKeywords() ),
+                  pads( footprint.GetPadCount() ),
+                  uniquepads( footprint.GetUniquePadCount() ),
+                  ordernum( footprint.GetOrderNum() ),
+                  initialized( true ) { /* empty */ };
+            
+            std::string name;
+            std::string nickname;
+            std::string doc;
+            std::string keywords;
+            int pads;
+            int uniquepads;
+            int ordernum;
+
+            // for when we're returning "null"
+            bool initialized;
+        };
+
+
+    };
+
+%}
+
+%typemap(out) SWIG_HELPER_CLASSES::FOOTPRINT_INFO {
+    SWIG_HELPER_CLASSES::FOOTPRINT_INFO footprint = $1;
+    if ( !footprint.initialized )
+    {
+        Py_INCREF( Py_None );            
+        $result = Py_None;
+    }
+    else
+    {    
+        PyObject *retval = $result = PyDict_New();
+
+        PyDict_SetItemString( retval, "name",     PyString_FromString( footprint.name.c_str() ) );
+        // I find nickname to be misleading. it's the name of the lib.
+        PyDict_SetItemString( retval, "lib",      PyString_FromString( footprint.nickname.c_str() ) );
+        PyDict_SetItemString( retval, "doc",      PyString_FromString( footprint.doc.c_str() ) );
+        PyDict_SetItemString( retval, "keywords", PyString_FromString( footprint.keywords.c_str() ) );
+
+        PyDict_SetItemString( retval, "padcount",       PyInt_FromLong( footprint.pads ) );
+        PyDict_SetItemString( retval, "uniquepadcount", PyInt_FromLong( footprint.uniquepads ) );
+        PyDict_SetItemString( retval, "ordernum",       PyInt_FromLong( footprint.ordernum ) );
+    }
+ }
+

--------------2.7.4--



Follow ups