← Back to team overview

kicad-developers team mailing list archive

Re: Need guidance/hints on accessing list of available pcbnew footprints from python

 

On 3/14/2018 11:04 AM, miles mccoo wrote:
> Thanks for the tips, Orson. That gave me what I needed, sort of.
> Additional guidance needed.
> 
> 
> 
> Let me describe what I have working and everyone can tell me if I
> understood everything correctly. You may want things moved around a bit
> as well.
> 
> *high level view* is that I added pcbnew.GetFootprints(libname="") which
> returns a list of python dicts. like this:
> {'uniquepadcount': 1, 'padcount': 1, 'name':
> 'MountingHole_2.2mm_M2_DIN965_Pad', 'lib': 'MountingHole', 'doc':
> 'Mounting Hole 2.2mm, M2, DIN965', 'ordernum': 0, 'keywords': 'mounting
> hole 2.2mm m2 din965'}
> 
> 
> First, I'll mention that there already exists a footprint.i which has
> functions like FootprintLoad for getting an actual module instance.
> Couple issues there:
> 
>   * This is where I wanted to put GetFootprints, but *the code *(based
>     off Orson's hint) *wants a Kiway, which I only know to get
>     from PcbEditFrame*. pcbnew_scripting_helpers knows about
>     the PcbEditFrame so that's where I put it.
>   * footprints.i has a Footprint enumerate, but it wants a library path,
>     which I don't have. More important, *it hangs when I run it*. I've
>     CC'd JP Charras who authored that code (also one of the main Kicad
>     people?).
> 
> 
> *Additional wierdness*
> 
> The needed function should look like this:
> auto fp_info_list( FOOTPRINT_LIST::GetInstance( Kiway() ) );
> fp_info_list->ReadFootprintFiles( Prj().PcbFootprintLibs(), !nickname ?
> NULL : &nickname );
> for( auto& footprint : fp_info_list->GetList() ) {
> 
> }
> 
> fp_info_list is a local, which returns a list of unique footprint_info
> ptrs. The problems are:
> 
>   * footprint_info doesn't have a copy constructor. It's abstract
>     virtual. So I can't make a copy of the GetList
>   * I can't return GetList to SWIG since fp_info_list goes out of scope.
>   * so I return fp_info_list to SWIG via a unique_ptr[1]
> 
> 
> I then have a swig typemap which gets GetList and generates the list of
> dicts. See [2] for the main code
> 
> 
> So that's what I have based off Orson's suggestion.
> 
> 
> *More detail on the other path I had been exploring* (which avoids some
> of the unique ptr stuff)*:*
> 
> from pcb_edit_frame, I can get prj. from prj I can use FP_LIB_TABLE*
> PcbFootprintLibs. FP_LIB_TABLE is a LIB_TABLE which links to a fallback
> LIB_TABLE. That fallback is protected.*If I could expose it via a Get
> method, this path could work too*.

The footprint library table internally handles fallback library tables
so you can iterate over all of the same information available no matter
how deeply nested the fallback tables are from the project library table
without exposing the fallback table pointer.  If you cannot access the
contents of the entire library table without exposing the fallback table
pointer, then the python bindings for the library table are broken.  If
you need to access the global library table, you can load it directly by
creating a new FP_LIB_TABLE object and using
FP_LIB_TABLE::GetGlobalTableFileName() but that should not be necessary.

> 
> 
> the includefallback I was referring to is in lib_table_base.h
> 
> /**
>      * Return true if the table is empty.
>      *
>      * @param aIncludeFallback is used to determine if the fallback
> table should be
>      *                         included in the test.
>      *
>      * @return true if the footprint library table is empty.
>      */
>     bool IsEmpty( bool *aIncludeFallback* = true );
> 
> 
> these APIs don't have that:
> int GetCount()       { return rows.size(); }
> 
>     LIB_TABLE_ROW* At( int aIndex ) { return &rows[aIndex]; }
> 
> 
> 
> A GetFallback method could do the trick? If I could get to it, I could
> iterate over 
> 
> 
> Apologies for the long mail. *Getting the list of footprints seems
> harder than it should be? Anything I can do to help fix that? *Some
> refactoring, perhaps.
> 
> 
> Miles
> 
> 
> [1] swig doesn't play nicely with unique_ptrs.
> see  http://www.swig.org/Doc3.0/SWIGDocumentation.html#SWIGPlus_nn19
> and https://stackoverflow.com/a/27699663/23630
> 
> [2]
> // I am returing a shared_ptr to fp_info_list because that's the only way to
> // hold onto the list of footprint_infos long enough for the swig typename
> // to dictionary'ify it.
> // I can't just copy the footprint_infos; they as an abstract class
> (load()=0)
> // I don't like it, but this function should only be called by swig.
> std::unique_ptr<FOOTPRINT_LIST> GetFootprints(const wxString &libname)
> {
>     // retval is a local unique ptr. If I just return GetList, it will
> be filled
>     // with 0x0s. This is because when fp_info_list is desctructed, the
> unique_ptrs
>     // returned by it will disappear as well.
> 
>     std::unique_ptr<FOOTPRINT_LIST> retval;
> 
>     if( s_PcbEditFrame ) {        
>         PROJECT *prj =  &s_PcbEditFrame->Prj();
>         retval =  FOOTPRINT_LIST::GetInstance( s_PcbEditFrame->Kiway() );
>         retval->ReadFootprintFiles( prj->PcbFootprintLibs(), !libname ?
> NULL : &libname );
>     }
>     return retval;
> }
> 
> 
> 
> 
> 
> // http://www.swig.org/Doc3.0/SWIGDocumentation.html#SWIGPlus_nn19
> // theoretically, the following should suppress the use of SwigValueWrapper
> // but I couldn't get it to work:
> // %feature("novaluewrapper") std::unique_ptr<FOOTPRINT_LIST>;
> 
> // The template stuff below is based on this answer:
> // https://stackoverflow.com/a/27699663/23630
> // I can't say I understand how/why it works.
> namespace std {
>   %feature("novaluewrapper") unique_ptr;
>   template <typename Type>
>   struct unique_ptr {
>       // we're not actually using the template feature of
>       // swig to do anything. just want to avoid the use of SwigWrapper
>   };
> }
> %template(UNIQUE_PTR_FOOTPRINT_LIST) std::unique_ptr<FOOTPRINT_LIST>;
> 
> 
> %typemap(out) std::unique_ptr<FOOTPRINT_LIST> {
> 
>     PyObject * retval = $result = PyList_New(0);
> 
>     if ( !$1 ) return retval;
>     
>     for( auto& footprint : $1->GetList() ) {
> 
>         PyObject *fpobj = PyDict_New();
>         int fail = 0;
>         fail |= PyDict_SetItemString(fpobj, "name",   
>  PyString_FromString(footprint->GetFootprintName()));
>         fail |= PyDict_SetItemString(fpobj, "lib",     
> PyString_FromString(footprint->GetNickname()));
>         fail |= PyDict_SetItemString(fpobj, "doc",     
> PyString_FromString(footprint->GetDoc()));
>         fail |= PyDict_SetItemString(fpobj, "keywords",
> PyString_FromString(footprint->GetKeywords()));
> 
>         fail |= PyDict_SetItemString(fpobj, "padcount",     
>  PyInt_FromLong(footprint->GetPadCount()));
>         fail |= PyDict_SetItemString(fpobj, "uniquepadcount",
> PyInt_FromLong(footprint->GetUniquePadCount()));
>         fail |= PyDict_SetItemString(fpobj, "ordernum",     
>  PyInt_FromLong(footprint->GetOrderNum()));
> 
>         if (fail) {
>             SWIG_exception_fail(SWIG_TypeError, "unable to convert
> FOOTPRINT_INFO list");
>         }
>         PyList_Append(retval, fpobj);
>     }
> 
>  }
> 
> 
> On Tue, Mar 13, 2018 at 12:15 PM, Maciej Sumiński
> <maciej.suminski@xxxxxxx <mailto:maciej.suminski@xxxxxxx>> wrote:
> 
>     Hi Miles,
> 
>     Have you seen FOOTPRINT_VIEWER_FRAME::ReCreateFootprintList()
>     (pcbnew/footprint_viewer_frame.cpp)? It might be the easiest way to go.
>     Perhaps it could be wrapped in a function provided by the scripting
>     interface.
> 
>     I could not find 'includefallback' option you have mentioned, would you
>     point me to the relevant source code?
> 
>     Regards,
>     Orson
> 
>     On 03/13/2018 10:49 AM, miles mccoo wrote:
>     > In one of my python plugins, I want to know the list of available
>     > footprints (mounting holes, in this case)
>     >
>     > Digging through the code, I can't make heads or tails of how to get this
>     > information. There are a bunch of abstract class involved. impls....
>     >
>     > There are a couple possibilities, none of which seem clean.
>     >
>     > *Stuff from FOOTPRINT plugin*
>     > looking in footprint.i, I see some APIs that are close.
>     > If I have a directory path to a fp library, I can
>     > call pcbnew.FootprintEnumerate for a nice list.
>     >
>     > but for that, I first have to have a list of fp library paths.
>     *How do I
>     > get such a list? *This seems like the closest answer. I see my
>     config dir
>     > has a fp-lib-table file which would be easy to parse. But that's a hack.
>     >
>     >
>     >
>     > *FOOTPRINT_LIST_IMPL possibility*
>     > looking in load_select_footprint, I see it has a static FOOTPRINT_LIST_IMPL
>     > which does have the APIs I'd need to get to a nice list of footprint_infos.
>     > But it's static to that file. I could copy the relevant code to
>     > python_scripting_helpers. but that feels messy.
>     >
>     > PCB_BASE_FRAME does have a method for popping up a table for a user to
>     > choose a footprint, which is nice for UI. It could even be useful for
>     > python plugins with some GUI stuff. But many python scripts will just want
>     > a list of libraries and modules.
>     >
>     >
>     >
>     > *PROJECT possibility*
>     > I tried exposing PROJECT to python as it has a number of useful sounding
>     > API including PcbFootprintLibs which returns a FP_LIB_TABLE. By poking
>     > around in gdb, I see that FP_LIB_TABLE has GetCount and IsEmpty. IsEmpty
>     > returns false if I set includefallback to true. and the fallback list does
>     > indeed have stuff in it. But it's protected and I don't see how to get to
>     > it.
>     >
>     > Can I expose the fallback table via a public get method?
>     >
>     >
>     > Is that a path that has any hope?
>     >
>     >
>     >
>     >
>     >  *So what's the most straight forward way to get to the list of
>     libraries
>     > and modules within them?*
>     >
>     > Thanks
>     > Miles
>     >
>     >
>     >
>     > _______________________________________________
>     > Mailing list: https://launchpad.net/~kicad-developers
>     <https://launchpad.net/~kicad-developers>
>     > Post to     : kicad-developers@xxxxxxxxxxxxxxxxxxx
>     <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx>
>     > Unsubscribe : https://launchpad.net/~kicad-developers
>     <https://launchpad.net/~kicad-developers>
>     > More help   : https://help.launchpad.net/ListHelp
>     <https://help.launchpad.net/ListHelp>
>     >
> 
> 
> 
>     _______________________________________________
>     Mailing list: https://launchpad.net/~kicad-developers
>     <https://launchpad.net/~kicad-developers>
>     Post to     : kicad-developers@xxxxxxxxxxxxxxxxxxx
>     <mailto:kicad-developers@xxxxxxxxxxxxxxxxxxx>
>     Unsubscribe : https://launchpad.net/~kicad-developers
>     <https://launchpad.net/~kicad-developers>
>     More help   : https://help.launchpad.net/ListHelp
>     <https://help.launchpad.net/ListHelp>
> 
> 
> 
> 
> _______________________________________________
> 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
> 


Follow ups

References