← Back to team overview

kicad-developers team mailing list archive

Re: [PATCH] Add python apis: GetLogicalLibs, FootprintsInLib and FootprintInfo

 

Thank you, Jeff, for taking the time to look at my patch.

attached is a new version of the patch. I've changed a couple things:

   - fixed the curly brace issue
   - added blanks before each of the if statements
   - I added a short test python function to the checkin comments[1]. It
   exercises the new functionality[2]
   - I added an additional function to instantiate a footprint. The only
   other such function I'm aware of wants a full path to a library. This one
   just takes the name of the lib


I've also posted the patch here:
http://mmccoo.com/random/0001-Add-the-python-apis-GetLogicalLibs-FootprintsInLib-I.patch


Miles

[1] There's a thread on the developer list complaining about the poor
comments. That thread focuses on the symbol or footprint libraries. I'd
like to take this opportunity to comment that check comment and comments in
general in the kicad project.... they kinda suck. The code itself contains
very little in the way of explanation. The checkin comments are terse. I
haven't found any kind of design documents or discussion on the mail list.

Hopefully someone will correct me.


[2] does kicad have anything in the way of a regression? It would be nice
to have some python code to be periodically executed. Compile errors is one
thing, but a couple APIs have disappeared in the last year. Such scripting
might have caught that.



On Tue, May 8, 2018 at 5:36 PM, Jeff Young <jeff@xxxxxxxxx> wrote:

> Hi Miles,
>
> The only coding style issue I saw was that there should be blank lines
> above “if” statements.
>
> Oh, and there’s still a K&R-style brace.  Search for "if ( footprint ) {“.
>  (The brace should be down on its own line.)
>
> I didn’t correct these and merge your patch as I can’t get scripting to
> build on my Mac so there’s no way for me to do a quick sanity test.
>
> Cheers,
> Jeff.
>
>
> On 4 May 2018, at 09:17, miles mccoo <mail@xxxxxxxxxx> wrote:
>
>
>
> I submitted this patch before, but it fell through the cracks.
>
> This patch makes it possible to query pcbnew for a list of footprint
> libraries, and the footprints contained therein. (without reading fp-lib
> myself)
>
>
> my previous attempt: https://lists.launchpad.net/kicad-
> developers/msg35236.html
> It has some implementation notes that may be of interest.
>
>
> I believe I have followed the coding guidelines which I have read several
> times. If I missed something, please indicate more than "follow the
> guidelines". At least a theme. (trailing space, curlies,...)
>
>
> Since attachments don't seem to make it to the launchpad site, I've also
> made the patch file available here:
> http://mmccoo.com/random/0001-Add-the-python-apis-GetLogicalLibs-
> FootprintsInLib-a.patch
>
>
> Miles
> <0001-Add-the-python-apis-GetLogicalLibs-FootprintsInLib-a.patch>______
> _________________________________________
> Mailing list: https://launchpad.net/~kicad-developers
> Post to     : kicad-developers@xxxxxxxxxxxxxxxxxxx
> Unsubscribe : https://launchpad.net/~kicad-developers
> More help   : https://help.launchpad.net/ListHelp
>
>
>
From c4ef67fad0622cd664e96285b33e133cd892e5f9 Mon Sep 17 00:00:00 2001
From: Miles McCoo <mail@xxxxxxxxxx>
Date: Wed, 9 May 2018 14:18:35 +0200
Subject: [PATCH] Add the python apis: GetLogicalLibs, FootprintsInLib,
 InstantiateFootprint and FootprintInfo.
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary="------------2.17.0"

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


GetLogicalLibs will return the list of available footprint libraries
FootprintsInLib lists the footprints in a library based on the library's name.
FootprintInfo will return basic information about a particular footprint (num pads...)
InstantiateFootprint will instantiate a footprint for placement in a board.

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. Other reasons to have a typemap is to automagically convert classes into something like a python tuple, dict, or list.

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 Suminski) and Jeff Young for their hints.

This thread may be of interest: https://lists.launchpad.net/kicad-developers/msg34925.html

Here is a quick test proc:
import pcbnew

def test_fp_stuff():
    libs = pcbnew.GetLogicalLibs()

    # I take the strings to get rid of the wxstring formatting. ie: wxString('TestPoint')
    firstthreelibs = [str(l) for l in libs[0:3]]
    print("you have {} footprint libs. the first three are {}".format(len(libs), firstthreelibs))

    firstlib = libs[0]
    footprints = pcbnew.FootprintsInLib(firstlib)
    firstthreefeet = footprints[0:3]
    print("the first of these libs {}, has {} footprints. the first three of these are: {}".format(firstlib, len(footprints), firstthreefeet))

    firstfoot = footprints[0]
    fpi = pcbnew.FootprintInfo(str(firstlib) + ":" + str(firstfoot))
    print("here's some info about the first of this footprints {} {}".format(firstfoot, fpi))

    print("now I'll instantiate it for you. check your GUI")
    newmod = pcbnew.InstantiateFootprint(firstlib, firstfoot)
    board = pcbnew.GetBoard()
    board.Add(newmod)

    pcbnew.Refresh()

test_fp_stuff()
---
 pcbnew/CMakeLists.txt                    |   2 +
 pcbnew/swig/autodoc.i                    |  44 ++++++++
 pcbnew/swig/footprint.i                  | 137 ++++++++++++++++++++++-
 pcbnew/swig/pcbnew.i                     |  13 ++-
 pcbnew/swig/pcbnew_scripting_helpers.cpp |   8 +-
 pcbnew/swig/pcbnew_scripting_helpers.h   |   3 +-
 pcbnew/swig/swig_typemaps.i              |  34 ++++++
 7 files changed, 235 insertions(+), 6 deletions(-)
 create mode 100644 pcbnew/swig/autodoc.i
 create mode 100644 pcbnew/swig/swig_typemaps.i


--------------2.17.0
Content-Type: text/x-patch; name="0001-Add-the-python-apis-GetLogicalLibs-FootprintsInLib-I.patch"
Content-Transfer-Encoding: 8bit
Content-Disposition: attachment; filename="0001-Add-the-python-apis-GetLogicalLibs-FootprintsInLib-I.patch"

diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt
index 04cad9b35..614074271 100644
--- a/pcbnew/CMakeLists.txt
+++ b/pcbnew/CMakeLists.txt
@@ -441,6 +441,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
@@ -458,6 +459,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 000000000..ded5a43f6
--- /dev/null
+++ b/pcbnew/swig/autodoc.i
@@ -0,0 +1,44 @@
+/*
+ * 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 a library name:
+mods = pcbnew.FootprintsInLib('MountingHole')
+") FootprintsInLib;
+
+%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 844ea3146..266e140e3 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
@@ -36,6 +36,11 @@
 %template(MODULE_List)  DLIST<MODULE>;
 %{
 #include <class_module.h>
+#include <project.h>
+#include <footprint_info.h>
+#include <fp_lib_table.h>
+#include <kiway.h>
+#include <stdexcept>
 %}
 
 
@@ -97,6 +102,136 @@
     %}
 }
 
+
+%inline %{
+    wxArrayString FootprintsInLib( const wxString& libname )
+    {
+        /* here, we enumerate by the library's name (the nickname)
+           for example:
+           mods = pcbnew.FootprintInLib( '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 );
+                return aFootprintNames;
+            }
+            catch ( IO_ERROR& e )
+            {
+                std::cout << "got IO_ERROR " << e.Problem() << " at " << e.Where() << std::endl;
+            }
+        }
+
+        return {};
+    }
+%}
+
+
+%inline %{
+    /* This function enables this python code:
+       for lib in pcbnew.GetLogicalLibs():
+           mods = pcbnew.FootprintsInLib( 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 {};
+    }
+%}
+
+%inline %{
+    // This function should really be called FootprintLoad but that's already taken below.
+    MODULE* InstantiateFootprint( const wxString &libname, const wxString &modname )
+    {
+        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->FootprintLoad( libname, modname );
+        }
+        return 0x0;
+    }
+
+%}
+
+
+%inline %{
+    /* 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'}
+
+       This function is different from most of the others:
+       * first, it's in an inline block.
+       * second, and more interesting, is that it returns a PyObject directly.
+         This is because the lookedup instance FOOTPRINT_INFO will go out of scope
+         when we leave this function. So we create a PyObject (actually a dict) here.
+    */
+    PyObject *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();
+            FOOTPRINT_LIST* fpl = FOOTPRINT_LIST::GetInstance( edit_frame->Kiway() );
+            fpl->ReadFootprintFiles( prj->PcbFootprintLibs(), &libname );
+            FOOTPRINT_INFO* footprint = fpl->GetModuleInfo( modname );
+
+            if ( footprint )
+            {
+                PyObject *retval = PyDict_New();
+
+                PyDict_SetItemString( retval, "name",     PyString_FromString( footprint->GetFootprintName() ) );
+                // I find nickname to be misleading. it's the name of the lib.
+                PyDict_SetItemString( retval, "lib",      PyString_FromString( footprint->GetNickname() ) );
+                PyDict_SetItemString( retval, "doc",      PyString_FromString( footprint->GetDoc() ) );
+                PyDict_SetItemString( retval, "keywords", PyString_FromString( footprint->GetKeywords() ) );
+
+                PyDict_SetItemString( retval, "padcount",       PyInt_FromLong( footprint->GetPadCount() ) );
+                PyDict_SetItemString( retval, "uniquepadcount", PyInt_FromLong( footprint->GetUniquePadCount() ) );
+                PyDict_SetItemString( retval, "ordernum",       PyInt_FromLong( footprint->GetOrderNum() ) );
+
+                return retval;
+            }
+        }
+
+        Py_INCREF( Py_None );
+        return Py_None;
+    }
+%}
+
 %pythoncode
 %{
     def GetPluginForPath(libname):
diff --git a/pcbnew/swig/pcbnew.i b/pcbnew/swig/pcbnew.i
index 3181df762..8bf797f0f 100644
--- a/pcbnew/swig/pcbnew.i
+++ b/pcbnew/swig/pcbnew.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
@@ -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,9 @@ 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(footprintPyEnumerate)
 HANDLE_EXCEPTIONS(PLUGIN::Load)
 HANDLE_EXCEPTIONS(PLUGIN::Save)
 HANDLE_EXCEPTIONS(PLUGIN::FootprintEnumerate)
@@ -124,5 +133,3 @@ HANDLE_EXCEPTIONS(LoadBoard)
 %include footprint.i
 %include plugins.i
 %include units.i
-
-
diff --git a/pcbnew/swig/pcbnew_scripting_helpers.cpp b/pcbnew/swig/pcbnew_scripting_helpers.cpp
index 065c029f7..3ec2f39a6 100644
--- a/pcbnew/swig/pcbnew_scripting_helpers.cpp
+++ b/pcbnew/swig/pcbnew_scripting_helpers.cpp
@@ -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
@@ -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 6233b3ac5..8d9bea831 100644
--- a/pcbnew/swig/pcbnew_scripting_helpers.h
+++ b/pcbnew/swig/pcbnew_scripting_helpers.h
@@ -2,7 +2,7 @@
  * This program source code file is part of KiCad, a free EDA CAD application.
  *
  * Copyright (C) 2013 NBEE Embedded Systems SL, Miguel Angel Ajo <miguelangel@xxxxxx>
- * Copyright (C) 2013-2017 KiCad Developers, see AUTHORS.txt for contributors.
+ * Copyright (C) 2013-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
@@ -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 000000000..8c370c8d8
--- /dev/null
+++ b/pcbnew/swig/swig_typemaps.i
@@ -0,0 +1,34 @@
+/*
+ * 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 <wx/string.h>
+#include <vector>
+%}
+
+%include "std_vector.i"
+namespace std
+{
+    %template( vector_wxstring ) vector<wxString>;
+}

--------------2.17.0--



Follow ups

References