← Back to team overview

kicad-developers team mailing list archive

Re: Fwd: Python plugin for pcbnew

 

Dick,

No problem to publish patch and add it to your milestone (see attached
file).
I had also do a modification to let plugin working in cvpcb.
Since there is no scripting inside, I had just added some initialisation
inside OnInit of cvpcb.
Anyway I don't have found any place to put the de-init code (ie
Py_Finalize())

This is exactly what you tell about the topmost/sib-interpreter position...
Perhaps I have to handle this inside the adapter ?

Last point. Yes I want/can contribute more than that small patch.

Regards
Le 10/03/2014 16:18, Dick Hollenbeck a écrit :
> Jean-Samuel,
>
> I think you are on the correct path.
>
> I guess you now understand that the user can add options of his own choosing in the
> options editor, simply by adding a new row to that dialog under fp_lib_table dialog.
>
> We had to cross this bridge anyways as part of my "modular-kicad" blueprint milestone
> number C).
>
> The only think I see different about what you are doing, is that I was expecting to wrap
> the python C API calls into C++ classes.  I just spent a couple of hours seeing if boost
> python gives us any help in this regard, and IT DOES NOT.
>
>
> So before this is over KiCad may end up being one of the best places to look for how to
> embed python in a C++ program.  Right now, the XBMC program mixes together C++ and python
> in a very advanced way.  That source code may have good examples in it.  I have not had
> time yet, but you see its listed in milestone C).
>
>
> There are many things to consider, one of which is that you cannot know if you are the
> topmost, pre-eminent interpreter, or need to be a sub-interpreter, without first asking if
> such a main interpreter has already been created.
>
>
> Before I offer any additional help on this, I will ask that you submit a patch to the
> mailing list of your current work, or push a branch.  That will indicate your intention to
> contribute this work, and will justify my continued help, because as I said, I need this
> and more in milestone C).
>
>
> If you send a patch, I will put it into a new pushed branch, and be in a position to help.
>
>
> Thanks,
>
> Dick
>
>
> On 03/10/2014 08:44 AM, Jean-Samuel Reynaud wrote:
>> Hi Dick, Hi list
>>
>> I'm currently modify the plugin to met your points.
>>
>> => now the plugin don't load python module each time
>> => the lib path is now handled directly onto the python module and is
>> not used anymore as the python module path.
>>
>> => I had created a python sub-interpreter (associated into the adapter)
>> This sub interpreter is a property of the adapter. So there are one
>> interpreter per adapter instance.
>>
>> So on the contructor I have:
>> ...
>>     m_pythread = Py_NewInterpreter();
>> ...
>>
>> then when I use it I do
>> ....
>>     PyEval_AcquireLock();
>>     PyThreadState_Swap(m_pythread);
>> // speak with python/module...
>>      PyThreadState_Swap(g_PythonMainTState);
>>      PyEval_ReleaseLock();
>>
>> Do you think it is ok ?
>>
>> I understand that this is the opposite way of your idea (python at top
>> and C++ at the bottom) but I don't know if it's possible (in current
>> state) to do this by an other way.
>>
>> Regards
>>
>> Le 09/03/2014 20:22, Dick Hollenbeck a écrit :
>>> On 03/09/2014 01:59 PM, Dick Hollenbeck wrote:
>>>> Hi Jean-Samuel,
>>>>
>>>> I am assuming that you would like this work to be committed as part kicad source?  If not
>>>> please let me know.  With my assumption in place, we'll have to achieve a minimum amount
>>>> of general usability, and that may extend beyond what you need it for.
>>>>
>>>> Please be aware of this.
>>>>
>>>>
>>>>> Hi,
>>>>>
>>>>> For starting I had implement your b) option. For that I start from a copy of guthub
>>>>> plugin. So I add options in all
>>>>> needed files (CMakeLists.txt..), I had create a directory on pcbnew and put the code
>>>>> inside and an example.
>>>>>
>>>>> Out of this directory I had done some small changes:
>>>>> scripting/python_scripting.h
>>>>> => Adding global value g_PythonMainTState as extern (needed for python locks)
>>>>> pcbnew/plugin.cpp
>>>>> => move python_footprint_plugin option to my plugin (as comment suggest). I hope it's ok.
>>>>> pcbnew/io_mgr.h
>>>>> pcbnew/io_mgr.cpp
>>>>> => Declare my plugin on lists
>>>>>
>>>>> On the new directory:
>>>>>
>>>>> pcbnew/fppython/fppython_example.py
>>>>> A plugin example.
>>>>> It's a really basic:
>>>>>  - 2 statics foot prints
>>>>>  - footprint himself are hardcoded on the example
>>>>>  - fake save/delete
>>>>>
>>>>> pcbnew/fppython/fppython_plugin.(cpp|h)
>>>>> The plugin himself
>>>>>
>>>>> To use is:
>>>>> - add a new footprint lib entry,
>>>>> - specify the directory where the plugin is as library path
>>>> The Footprint*() library path argument should always only be used as a pointer to the
>>>> library, not as a pointer to the plugin.
>>>>
>>>>
>>>> A normal python environment variable should know where a python module is.  That should be
>>>> the fallback in any case, with possible augmentation as described below, which is only an
>>>> *optional* override for module loading.
>>>>
>>>> Additionally, you can look for an optional fp_lib_table property, one spelled identically
>>>> to a standard python environment variable name, which if present, can trump the
>>>> environment variable.
>>>>
>>>>
>>>>
>>>>> - specify python_footprint_plugin option as the plugin name (without .py, for example
>>>>> fppython_example)
>>>> good.
>>>>
>>>>
>>>>> Some limitations I found inside the core to allow more feature for plugins:
>>>>> - IsFootprintLibWritable don't have a PROPERTY parameter. So since the plugin module name
>>>>> is inside this, I'm unable to load
>>>>> the right python module to let it handle this method
>>>> Your python module loading should happen less frequently.
>>>>
>>>> 1) The the association between the C++ adapter instance and the python module can and
>>>> should be retentative up until
>>>> it cannot be.
>>>>
>>>> 2) The association between the module and the library can and should be retentative up
>>>> until it cannot be.
>>>>
>>>>
>>>> The easiest way to manage this is to have two member helpers in your adaptor:
>>>>
>>>> bool PYTHON_ADAPTOR::changedLibPath( const wxString& aCurrentLibPath )
>>>> {
>>>>     if( aCurrentLibPath != m_lib_path )
>>>>     {
>>>> 	m_lib_path = aCurrentLibPath;
>>>> 	return true;
>>>>     }
>>>>     return false;
>>>> }
>>>>
>>>> PLUS, a similar function for python module name.
>>>>
>>>> Then only change libraries, when changedLibPath() returns true.  Only change modules when
>>>> changedModule() returns true.
>>>>
>>>> This way your module and library can both be cached in the PYTHON_ADAPTER.  User's will
>>>> instantiate a new PYTHON_ADAPTER for any number of libraries.
>>>>
>>>> But 99% of the time, your python interpreter (or sub-interpreter) instantiation should
>>>> survive multiple calls to the adaptor, and the module should be left loaded, and the
>>>> library should be left open.
>>>>
>>>> We can add PROPERTIES argument to IsFootprintLibWritable() but if you follow my design
>>>> suggestions, you can survive without this additional argument for now.  You will have a
>>>> record of the m_module's last value already.
>>>>
>>>>
>>>>> - FootprintLibOptions: Same issue. So the plugin can't provide a list of parameters needed
>>>>> for his own usage. 
>>>> In the C++ why don't you refer people to the pydocs for the module.  I don't want to have
>>>> to load the python module in the OptionsEditor, where the plugin may instantiated just to
>>>> get the supported parameters.  Let's refer people to external documentation in the help
>>>> text for the certainly present python_footprint_plugin option.
>>>>
>>>> That way each python module can later query for the options it might expect.  The user can
>>>> supply additional options, even though they are not initially part of the supplied
>>>> choices, in the options editor.
>>>>
>>>>
>>>>
>>>>> To work arround this
>>>>> I hard code 4 parameters (fppython_parameter[1-4]) but it is not beautiful...
>>>> Those could be removed, if the user can add his own in the OptionEditor with more
>>>> meaningful names.
>>>>
>>>> I think we have to prove viability of this entire python plugin module first before we tip
>>>> the boat over.
>>>>
>>>>
>>>> Also, remember that its going to be impossible to have more than one running python
>>>> interpreter in a single process.  Python simply does not support that.  It does however
>>>> support "sub-interpreters".  As the top level Kicad GUI transforms into wxPython, you will
>>>> not be able to instantiate a brand new python interpreter in your PYTHON_ADAPTOR, for two
>>>> reasons:
>>>>
>>>> a) only one interpreter per process, and the main one is way above you.
>>>>
>>>> b) you should not assume that the user only has one PYTHON_ADAPTOR in the fp_lib_table,
>>>> and you are limited to only one interpreter per process.
>>>>
>>>> So if you are not already an expert on python "sub-interpreters", you need to make that
>>>> your highest priority, because your work will never get committed if you cannot conform to
>>>> the overall design constraints of the single python interpreter constraint.
>>> Jean-Samuel,
>>>
>>> Another thing to be aware of is that currently the FootprintLoad() function has to be
>>> thread safe.  This may not have been the case when I planted the seed for this idea you had.
>>>
>>> But FootprintLoad() is currently thread safe for all implementing PLUGINs.  For any
>>> instance of the PYTHON_ADAPTOR, you can be sure that only a single thread will call on
>>> each PYTHON_ADAPTOR instance at a time.
>>>
>>> The only place that I am aware of that causes this is
>>>
>>> bool FOOTPRINT_LIST::ReadFootprintFiles( FP_LIB_TABLE* aTable, const wxString* aNickname )
>>>
>>> near line 185 in common/footprint_info.cpp.  This code is called when the user presses the
>>> "Load All" button in the footprint picker.
>>>
>>> Its possible that a static MUTLOCK will give you the protection you need in the
>>> PYTHON_ADAPTOR.  But this is getting hairy now, and I'm not going to have the time to help
>>> you make python multithreaded.  The MUTLOCK may be a way out, but I don't have the brain
>>> cycles to worry about something as obscure as this.
>>>
>>> My vision has python on the top, and C++ underneath.  Your's is upside down from that.
>>>
>>>
>>> Dick
>>>
>>>
>>>
>>> _______________________________________________
>>> 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
>>
>> _______________________________________________
>> 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
>>
>
> _______________________________________________
> 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

=== modified file 'CMakeLists.txt'
--- CMakeLists.txt	2014-02-24 01:02:41 +0000
+++ CMakeLists.txt	2014-03-09 08:19:50 +0000
@@ -83,6 +83,8 @@
 
 option( BUILD_GITHUB_PLUGIN "Build the GITHUB_PLUGIN for pcbnew." OFF )
 
+option( BUILD_FPPYTHON_PLUGIN "Build the FPPYTHON_PLUGIN for pcbnew." OFF )
+
 # This can be set to a custom name to brag about a particular branch in the "About" dialog:
 set( KICAD_REPO_NAME "product" CACHE STRING "Name of the tree from which this build came." )
 

=== modified file 'CMakeModules/config.h.cmake'
--- CMakeModules/config.h.cmake	2014-02-03 19:26:18 +0000
+++ CMakeModules/config.h.cmake	2014-03-08 08:51:17 +0000
@@ -67,6 +67,9 @@
 /// When defined, build the GITHUB_PLUGIN for pcbnew.
 #cmakedefine BUILD_GITHUB_PLUGIN
 
+/// When defined, build the FPPYTHON_PLUGIN for pcbnew.
+#cmakedefine BUILD_FPPYTHON_PLUGIN
+
 /// When defined, use KIWAY and KIFACE DSOs
 #cmakedefine USE_KIWAY_DLLS
 

=== modified file 'Documentation/compiling/build-config.txt'
--- Documentation/compiling/build-config.txt	2014-02-22 23:17:36 +0000
+++ Documentation/compiling/build-config.txt	2014-03-09 09:28:13 +0000
@@ -212,6 +212,13 @@
 This option enables or disables building KiCad with a pcbnew plugin for loading
 footprints from a GitHub repository.
 
+BUILD_FPPYTHON_PLUGIN (ON/OFF)
+----------------------------
+Default: OFF
+
+This option enables or disables building KiCad with a pcbnew plugin for loading
+footprints from a python module.
+
 
 KICAD_REPO_NAME (STRING)
 ------------------------

=== modified file 'cvpcb/CMakeLists.txt'
--- cvpcb/CMakeLists.txt	2013-12-30 00:27:03 +0000
+++ cvpcb/CMakeLists.txt	2014-03-10 16:04:10 +0000
@@ -130,6 +130,13 @@
 # Must follow github_plugin
 target_link_libraries( cvpcb ${Boost_LIBRARIES} )
 
+if( BUILD_FPPYTHON_PLUGIN )
+    target_link_libraries( cvpcb fppython_plugin )
+    target_link_libraries( cvpcb ${PYTHON_LIBRARIES} )
+endif()
+
+
+
 
 ###
 # Add cvpcb as install target

=== modified file 'cvpcb/cvpcb.cpp'
--- cvpcb/cvpcb.cpp	2014-02-08 10:44:55 +0000
+++ cvpcb/cvpcb.cpp	2014-03-10 16:39:33 +0000
@@ -44,6 +44,10 @@
 
 #include <wx/snglinst.h>
 
+#ifdef BUILD_FPPYTHON_PLUGIN
+#include <Python.h>
+#endif
+
 // Colors for layers and items
 COLORS_DESIGN_SETTINGS g_ColorsSettings;
 
@@ -88,7 +92,9 @@
 // Create a new application object
 IMPLEMENT_APP( EDA_APP )
 
-
+#ifdef BUILD_FPPYTHON_PLUGIN
+PyThreadState* g_PythonMainTState;
+#endif
 /************************************/
 /* Called to initialize the program */
 /************************************/
@@ -101,6 +107,16 @@
 
     InitEDA_Appl( wxT( "CvPcb" ), APP_CVPCB_T );
 
+#ifdef BUILD_FPPYTHON_PLUGIN
+    Py_Initialize();
+
+    PyEval_InitThreads();
+    // Save the current Python thread state and release the
+    // Global Interpreter Lock.
+    g_PythonMainTState = PyThreadState_Get();
+    PyEval_ReleaseThread(g_PythonMainTState);
+#endif
+
     SetFootprintLibTablePath();
 
     // Set 3D shape path from environment variable KISYS3DMOD
@@ -148,3 +164,4 @@
 
     return true;
 }
+

=== modified file 'pcbnew/CMakeLists.txt'
--- pcbnew/CMakeLists.txt	2014-02-27 22:56:16 +0000
+++ pcbnew/CMakeLists.txt	2014-03-09 08:23:14 +0000
@@ -364,6 +364,10 @@
     set( GITHUB_PLUGIN_LIBRARIES github_plugin )
 endif()
 
+if( BUILD_FPPYTHON_PLUGIN )
+    set( FPPYTHON_PLUGIN_LIBRARIES fppython_plugin )
+endif()
+
 if( ( KICAD_SCRIPTING OR KICAD_SCRIPTING_MODULES ) AND NOT WIN32 AND NOT APPLE )
     list( APPEND PCBNEW_EXTRA_LIBS rt )
 endif()
@@ -384,6 +388,7 @@
         pcad2kicadpcb
         lib_dxf
         ${GITHUB_PLUGIN_LIBRARIES}
+        ${FPPYTHON_PLUGIN_LIBRARIES}
         polygon
         bitmaps
         gal
@@ -520,6 +525,11 @@
     add_dependencies( github_plugin lib-dependencies )
 endif()
 
+if( BUILD_FPPYTHON_PLUGIN )
+    add_subdirectory( fppython )
+    add_dependencies( fppython_plugin lib-dependencies )
+endif()
+
 
 ###
 # Create the pcbnew executable
@@ -554,6 +564,7 @@
     gal
     lib_dxf
     ${GITHUB_PLUGIN_LIBRARIES}
+    ${FPPYTHON_PLUGIN_LIBRARIES}
     ${wxWidgets_LIBRARIES}
     ${OPENGL_LIBRARIES}
     ${GDI_PLUS_LIBRARIES}

=== added directory 'pcbnew/fppython'
=== added file 'pcbnew/fppython/CMakeLists.txt'
--- pcbnew/fppython/CMakeLists.txt	1970-01-01 00:00:00 +0000
+++ pcbnew/fppython/CMakeLists.txt	2014-03-07 16:38:44 +0000
@@ -0,0 +1,50 @@
+#  This program source code file is part of KICAD, a free EDA CAD application.
+#
+#  Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <dick@xxxxxxxxxxx>
+#  Copyright (C) 2013 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
+
+
+
+# tone down the compiler warnings for avhttp header library:
+set( FPPYTHON_PLUGIN_SRCS
+    fppython_plugin.cpp
+    )
+
+add_library( fppython_plugin
+    fppython_plugin.cpp
+    )
+
+include_directories( . ${OPENSSL_INCLUDE_DIR} ${AVHTTP_INCLUDE_DIR} )
+
+# No, you don't get fppython without boost and openssl. Boost_LIBRARIES now moved up
+# into CMakeLists.txt for pcbnew and cvpcb:
+target_link_libraries( fppython_plugin
+    ${OPENSSL_LIBRARIES}
+    )
+
+if( MINGW )
+    target_link_libraries( fppython_plugin
+        ${wxWidgets_LIBRARIES}
+        ws2_32
+        )
+endif()
+
+add_dependencies( fppython_plugin boost )
+

=== added file 'pcbnew/fppython/fppython_example.py'
--- pcbnew/fppython/fppython_example.py	1970-01-01 00:00:00 +0000
+++ pcbnew/fppython/fppython_example.py	2014-03-10 16:42:00 +0000
@@ -0,0 +1,89 @@
+#!/usr/bin/python
+#
+# Example for a basic python library plugin
+#
+#
+
+#
+# Enumerate available footprint
+#
+# Must be implemented. Return a python list of strings corresponding
+# to the footprint name
+#
+# @param params parameters of the plugin. It's a dict type
+# @return python string list of footprint name
+def FootprintEnumerate(libpath,params):
+    print libpath
+    print params
+    return ['testfp1','testfp2']
+
+
+#
+# Load a footprint
+#
+# Must be implemented. Return a string test corresponding to the s-expression
+# of the footprint (the pretty format)
+#
+# @param library path
+# @param fp_name is the name of footprint
+# @param params parameters of the plugin. It's a dict type
+# @return the footprint (string in s-expression)
+def FootprintLoad(libpath,fp_name,params):
+    print libpath
+    print fp_name
+    print params
+    a = """
+(module Relay_SSOP4 (layer F.Cu)
+  (fp_text reference OC1 (at -0.65024 2.54762) (layer F.SilkS)
+    (effects (font (thickness 0.3048)))
+  )
+  (fp_text value PS2801-1-K (at 0 4.8006) (layer F.SilkS) hide
+    (effects (font (thickness 0.3048)))
+  )
+  (fp_circle (center -1.99898 -0.89916) (end -1.95072 -0.89916) (layer F.SilkS) (width 0.20066))
+  (fp_line (start -2.4003 -1.30048) (end 2.4003 -1.30048) (layer F.SilkS) (width 0.20066))
+  (fp_line (start 2.4003 -1.30048) (end 2.4003 1.30048) (layer F.SilkS) (width 0.20066))
+  (fp_line (start 2.4003 1.30048) (end -2.3495 1.30048) (layer F.SilkS) (width 0.20066))
+  (fp_line (start -2.3495 1.30048) (end -2.4003 1.30048) (layer F.SilkS) (width 0.20066))
+  (fp_line (start -2.4003 1.30048) (end -2.4003 -1.30048) (layer F.SilkS) (width 0.20066))
+  (pad 1 smd rect (at -3.2512 -0.65024) (size 1.50114 0.59944) (layers F.Cu F.Paste F.Mask))
+  (pad 2 smd rect (at -3.2512 0.65024) (size 1.50114 0.59944) (layers F.Cu F.Paste F.Mask))
+  (pad 3 smd rect (at 3.2512 0.65024) (size 1.50114 0.59944) (layers F.Cu F.Paste F.Mask))
+  (pad 4 smd rect (at 3.2512 -0.65024) (size 1.50114 0.59944) (layers F.Cu F.Paste F.Mask))
+)
+"""
+    return a
+
+
+#
+# Delete a footprint
+#
+# Should be implemented.
+#
+# @param fp_name is the name of footprint
+# @param params parameters of the plugin. It's a dict type
+# @return boolean for success operation
+def FootprintDelete(libpath,fp_name,params):
+    print libpath
+    print fp_name
+    print params
+    print "Try to delete %s\n"%(fp_name)
+    if 'testfp1' == fp_name:
+        return True
+    else:
+        return False
+
+#
+# Save a footprint
+#
+# Should be implemented.
+#
+# @param fp_name is the name of footprint
+# @param module_txt a string already formatted as a footprint (s-expression)
+# @param params parameters of the plugin. It's a dict type
+# @return boolean for success operation
+def FootprintSave(libpath,fp_name,module_txt,params):
+    print libpath
+    print params
+    print "Try to save %s (%s)\n"%(fp_name,module_txt)
+    return True

=== added file 'pcbnew/fppython/fppython_plugin.cpp'
--- pcbnew/fppython/fppython_plugin.cpp	1970-01-01 00:00:00 +0000
+++ pcbnew/fppython/fppython_plugin.cpp	2014-03-10 16:22:24 +0000
@@ -0,0 +1,452 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <dick@xxxxxxxxxxx>
+ * Copyright (C) 2013 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, 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 <Python.h>
+#include <io_mgr.h>
+#include <fppython_plugin.h>
+#include <pcb_parser.h>
+#include <macros.h>
+#include <class_module.h>
+
+#include <python_scripting.h>
+
+using namespace std;
+
+
+FPPYTHON_PLUGIN::FPPYTHON_PLUGIN()
+{
+    m_parser = NULL;
+    m_pythread = NULL;
+    m_module = NULL;
+    m_parser =  new PCB_PARSER();
+    m_pythread = Py_NewInterpreter();
+    m_module = NULL;
+}
+
+
+FPPYTHON_PLUGIN::~FPPYTHON_PLUGIN()
+{
+    if (m_module)
+    {
+        Py_DECREF(m_module);
+    }
+    PyEval_AcquireLock();
+    PyThreadState_Swap(m_pythread);
+    Py_EndInterpreter(m_pythread);
+    PyThreadState_Swap(g_PythonMainTState);
+    PyEval_ReleaseLock();
+    delete m_parser;
+}
+
+
+const wxString FPPYTHON_PLUGIN::PluginName() const
+{
+    return wxT( "FpPython" );
+}
+
+
+const wxString FPPYTHON_PLUGIN::GetFileExtension() const
+{
+    return wxEmptyString;
+}
+
+
+PyObject *FPPYTHON_PLUGIN::buildParams(const PROPERTIES* aProperties )
+{
+    PyObject *pArgs = NULL;
+    if (aProperties != NULL)
+    {
+        pArgs = PyDict_New();
+        for( PROPERTIES::const_iterator it = aProperties->begin();  it != aProperties->end();  ++it )
+        {
+            PyDict_SetItem(pArgs,
+                           PyString_FromString(it->first.c_str()),
+                           PyString_FromString(it->second.c_str()));
+        }
+    }
+    return pArgs;
+}
+
+
+bool FPPYTHON_PLUGIN::changedLibPath( const wxString& aCurrentLibPath )
+{
+    if( aCurrentLibPath != m_lib_path )
+    {
+        m_lib_path = aCurrentLibPath;
+        return true;
+    }
+    return false;
+}
+
+bool FPPYTHON_PLUGIN::changedPluginName( const wxString& aCurrentPluginName )
+{
+    if( aCurrentPluginName != m_plugin_name )
+    {
+        m_plugin_name = aCurrentPluginName;
+        return true;
+    }
+    return false;
+}
+
+
+PyObject *FPPYTHON_PLUGIN::prepareCall(const char *aFunctionName,
+                                       const wxString& aLibraryPath,
+                                       const PROPERTIES* aProperties)
+{
+    PyObject *pName, *pFunc;
+
+    UTF8 fpName;
+    if (aProperties == NULL || !(aProperties->Value("python_footprint_plugin",&fpName)))
+    {
+        THROW_IO_ERROR( _("Configuration error: missing python_footprint_plugin option (module name)") );
+        return NULL; // NOP...
+    }
+
+
+    if (m_module && (changedPluginName(fpName) | changedLibPath(aLibraryPath)))
+    {
+        // Completly reset module loading
+        if (m_module)
+        {
+            Py_DECREF(m_module);
+        }
+        PyEval_AcquireLock();
+        PyThreadState_Swap(m_pythread);
+        Py_EndInterpreter(m_pythread);
+        PyThreadState_Swap(g_PythonMainTState);
+        PyEval_ReleaseLock();
+
+        m_pythread = Py_NewInterpreter();
+        m_module = NULL;
+    }
+
+    PyEval_AcquireLock();
+    PyThreadState_Swap(m_pythread);
+
+    if (m_module == NULL)
+    {
+// TODO: using fp_lib_table property as an option path for loading the module
+//        wxString cmd;
+//        cmd.Append(UTF8("import sys; sys.path.append(\".\"); print sys.path\n"));
+//        cmd.Append(aLibraryPath);
+//        cmd.Append(UTF8("\")\n"));
+//        PyRun_SimpleString(UTF8(cmd));
+        pName = wx2PyString(fpName);
+        if( pName == NULL )
+        {
+            PyThreadState_Swap(g_PythonMainTState);
+            PyEval_ReleaseLock();
+            THROW_IO_ERROR( _("Error on string...") );
+            return NULL;// NOP...
+        }
+
+        m_module = PyImport_Import(pName);
+        Py_DECREF(pName);
+        if( m_module == NULL )
+        {
+            PyThreadState_Swap(g_PythonMainTState);
+            PyEval_ReleaseLock();
+            THROW_IO_ERROR( wxString::Format( _( "Unable to find python module") ));
+        }
+    }
+    pFunc = PyObject_GetAttrString(m_module, aFunctionName);
+    if( !(pFunc && PyCallable_Check(pFunc)) )
+    {
+        Py_XDECREF(pFunc);
+        PyThreadState_Swap(g_PythonMainTState);
+        PyEval_ReleaseLock();
+        THROW_IO_ERROR( _("Unable to find Footprint function"));
+    }
+
+    return pFunc;
+}
+
+wxArrayString FPPYTHON_PLUGIN::FootprintEnumerate(
+        const wxString& aLibraryPath, const PROPERTIES* aProperties )
+{
+    PyObject *pArgs;
+    PyObject *pValue;
+
+#ifdef KICAD_SCRIPTING
+
+    PyObject *pFunc = prepareCall("FootprintEnumerate",aLibraryPath,aProperties);
+
+    pArgs = PyTuple_New(2);
+    PyTuple_SetItem(pArgs, 0, wx2PyString(aLibraryPath));
+    PyTuple_SetItem(pArgs, 1, buildParams(aProperties));
+    pValue = PyObject_CallObject(pFunc,pArgs); // pArgs
+    Py_DECREF(pArgs);
+    Py_DECREF(pFunc);
+    if( pValue != NULL )
+    {
+        if( PyObject_TypeCheck(pValue,&PyList_Type) )
+        {
+            wxArrayString ret = pyArrayStringToWx(pValue );
+            Py_DECREF(pValue);
+            PyThreadState_Swap(g_PythonMainTState);
+            PyEval_ReleaseLock();
+            return ret;
+        }
+        else
+        {
+            Py_DECREF(pValue);
+            PyThreadState_Swap(g_PythonMainTState);
+            PyEval_ReleaseLock();
+            THROW_IO_ERROR( _("Invalid return type for FootprintEnumerate") );
+        }
+    }
+    else
+    {
+        PyThreadState_Swap(g_PythonMainTState);
+        PyEval_ReleaseLock();
+        THROW_IO_ERROR( _("Null type for FootprintEnumerate: invalid function") );
+    }
+
+#endif
+    wxArrayString ret;
+    return ret;
+}
+
+
+MODULE* FPPYTHON_PLUGIN::FootprintLoad( const wxString& aLibraryPath,
+        const wxString& aFootprintName, const PROPERTIES* aProperties )
+{
+    PyObject *pArgs, *pValue;
+
+#ifdef KICAD_SCRIPTING
+    // Getting python module name to use
+
+
+    PyObject *pFunc = prepareCall("FootprintLoad",aLibraryPath,aProperties);
+
+    pArgs = PyTuple_New(3);
+    PyTuple_SetItem(pArgs, 0, wx2PyString(aLibraryPath));
+    PyTuple_SetItem(pArgs, 1, wx2PyString(aFootprintName));
+    PyTuple_SetItem(pArgs, 2, buildParams(aProperties));
+
+    pValue = PyObject_CallObject(pFunc,pArgs); // pArgs
+    Py_DECREF(pArgs);
+    Py_DECREF(pFunc);
+    if( pValue != NULL )
+    {
+        if( PyObject_TypeCheck(pValue,&PyString_Type))
+        {
+            STRING_LINE_READER  reader( PyString_AsString(pValue), wxT( "clipboard" ) );
+            m_parser->SetLineReader( &reader );
+            MODULE*     footprint = (MODULE*) m_parser->Parse();
+            Py_DECREF(pValue);
+            PyThreadState_Swap(g_PythonMainTState);
+            PyEval_ReleaseLock();
+            return footprint;
+        }
+        else
+        {
+            Py_DECREF(pValue);
+            PyThreadState_Swap(g_PythonMainTState);
+            PyEval_ReleaseLock();
+            THROW_IO_ERROR( _("Invalid return type for FootprintLoad") );
+        }
+    }
+    else
+    {
+        PyThreadState_Swap(g_PythonMainTState);
+        PyEval_ReleaseLock();
+        THROW_IO_ERROR( _("Null type for FootprintLoad") );
+    }
+
+
+#endif
+    return NULL;
+}
+
+
+bool FPPYTHON_PLUGIN::IsFootprintLibWritable( const wxString& aLibraryPath )
+{
+    // Alway return true since we don't have aProperties to know the module name...
+    // The module should manage himself on save/delete functions...
+    return true;
+}
+
+
+void FPPYTHON_PLUGIN::FootprintSave( const wxString& aLibraryPath,
+        const MODULE* aFootprint, const PROPERTIES* aProperties )
+{
+
+    PyObject *pArgs, *pValue;
+
+#ifdef KICAD_SCRIPTING
+    // Getting python module name to use
+
+
+    PyObject *pFunc = prepareCall("FootprintSave",aLibraryPath,aProperties);
+
+    STRING_FORMATTER stf;
+    SetOutputFormatter((OUTPUTFORMATTER*) &stf);
+    Format((BOARD_ITEM*) aFootprint,1);
+
+    pArgs = PyTuple_New(4);
+    PyTuple_SetItem(pArgs, 0, wx2PyString(aLibraryPath));
+    PyTuple_SetItem(pArgs, 1, PyString_FromString(((aFootprint->GetFPID()).Format() ).c_str()));
+    PyTuple_SetItem(pArgs, 2, PyString_FromString(stf.GetString().c_str()));
+    PyTuple_SetItem(pArgs, 3, buildParams(aProperties));
+
+
+    pValue = PyObject_CallObject(pFunc,pArgs); // pArgs
+    Py_DECREF(pArgs);
+    Py_DECREF(pFunc);
+    if( pValue != NULL )
+    {
+        if( PyObject_TypeCheck(pValue,&PyBool_Type))
+        {
+            if( PyInt_AsLong(pValue) )
+            {
+                Py_DECREF(pValue);
+                PyThreadState_Swap(g_PythonMainTState);
+                PyEval_ReleaseLock();
+                return ;
+            }
+            else
+            {
+                Py_DECREF(pValue);
+                PyThreadState_Swap(g_PythonMainTState);
+                PyEval_ReleaseLock();
+                THROW_IO_ERROR( _("Unable to save footprint") );
+            }
+        }
+        else
+        {
+            Py_DECREF(pValue);
+            PyThreadState_Swap(g_PythonMainTState);
+            PyEval_ReleaseLock();
+            THROW_IO_ERROR( _("Invalid return type for FootprintSave") );
+        }
+    }
+    else
+    {
+        PyThreadState_Swap(g_PythonMainTState);
+        PyEval_ReleaseLock();
+        THROW_IO_ERROR( _("Null type for FootprintSave") );
+    }
+
+
+#endif
+
+}
+
+
+void FPPYTHON_PLUGIN::FootprintDelete( const wxString& aLibraryPath,
+                                       const wxString& aFootprintName,
+                                       const PROPERTIES* aProperties )
+{
+    PyObject *pArgs, *pValue;
+
+#ifdef KICAD_SCRIPTING
+    // Getting python module name to use
+
+
+    PyObject *pFunc = prepareCall("FootprintDelete",aLibraryPath,aProperties);
+
+    pArgs = PyTuple_New(3);
+    PyTuple_SetItem(pArgs, 0, wx2PyString(aLibraryPath));
+    PyTuple_SetItem(pArgs, 1, wx2PyString(aFootprintName));
+    PyTuple_SetItem(pArgs, 2, buildParams(aProperties));
+
+    pValue = PyObject_CallObject(pFunc,pArgs); // pArgs
+    Py_DECREF(pArgs);
+    Py_DECREF(pFunc);
+    if( pValue != NULL )
+    {
+        if( PyObject_TypeCheck(pValue,&PyBool_Type))
+        {
+            if( PyInt_AsLong(pValue) )
+            {
+                Py_DECREF(pValue);
+                PyThreadState_Swap(g_PythonMainTState);
+                PyEval_ReleaseLock();
+                return ;
+            } else
+            {
+                Py_DECREF(pValue);
+                PyThreadState_Swap(g_PythonMainTState);
+                PyEval_ReleaseLock();
+                THROW_IO_ERROR( _("Unable to delete footprint") );
+            }
+        }
+        else
+        {
+            Py_DECREF(pValue);
+            PyThreadState_Swap(g_PythonMainTState);
+            PyEval_ReleaseLock();
+            THROW_IO_ERROR( _("Invalid return type for FootprintDelete") );
+        }
+    }
+    else
+    {
+        PyThreadState_Swap(g_PythonMainTState);
+        PyEval_ReleaseLock();
+        THROW_IO_ERROR( _("Null type for FootprintDelete") );
+    }
+
+
+#endif
+
+}
+
+
+
+void FPPYTHON_PLUGIN::FootprintLibOptions( PROPERTIES* aListToAppendTo ) const
+{
+
+    // inherit options supported by all PLUGINs.
+    PLUGIN::FootprintLibOptions( aListToAppendTo );
+
+    // Suitable for a C++ to python PLUGIN::Footprint*() adapter, moved to the adapter...
+    (*aListToAppendTo)["python_footprint_plugin"] = UTF8( _(
+        "Enter the python module which implements the PLUGIN::Footprint*() functions."
+        ));
+
+}
+
+
+
+
+wxArrayString FPPYTHON_PLUGIN::pyArrayStringToWx( PyObject* aArrayString )
+{
+    wxArrayString   ret;
+
+    int             list_size = PyList_Size( aArrayString );
+
+    for( int n = 0; n<list_size; n++ )
+    {
+        PyObject* element = PyList_GetItem( aArrayString, n );
+
+        ret.Add( FROM_UTF8( PyString_AsString( element ) ), 1 );
+    }
+
+    return ret;
+}
+
+
+
+

=== added file 'pcbnew/fppython/fppython_plugin.h'
--- pcbnew/fppython/fppython_plugin.h	1970-01-01 00:00:00 +0000
+++ pcbnew/fppython/fppython_plugin.h	2014-03-10 16:16:17 +0000
@@ -0,0 +1,93 @@
+/*
+ * This program source code file is part of KiCad, a free EDA CAD application.
+ *
+ * Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <dick@xxxxxxxxxxx>
+ * Copyright (C) 2013 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, 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
+ */
+
+#ifndef FPPYTHON_PLUGIN_H_
+#define FPPYTHON_PLUGIN_H_
+
+#include <kicad_plugin.h>
+#include <Python.h>
+
+/**
+ * Class FPPYTHON_PLUGIN
+ * is a plugin for loading python plugins
+ *
+ * Extend @PLUGIN class and implement a wrapper to a footprint library manager
+ * written in python. A python class example is in fppython_example.py
+ *
+ *
+ */
+
+class FPPYTHON_PLUGIN : public PCB_IO
+{
+public:
+    //-----<PLUGIN API>----------------------------------------------------------
+    const wxString PluginName() const;
+
+    const wxString GetFileExtension() const;
+
+    wxArrayString FootprintEnumerate( const wxString& aLibraryPath,
+            const PROPERTIES* aProperties = NULL );
+
+    MODULE* FootprintLoad( const wxString& aLibraryPath,
+            const wxString& aFootprintName, const PROPERTIES* aProperties );
+
+    void FootprintSave( const wxString& aLibraryPath, const MODULE* aFootprint,
+            const PROPERTIES* aProperties = NULL );
+
+    void FootprintDelete( const wxString& aLibraryPath, const wxString& aFootprintName,
+            const PROPERTIES* aProperties = NULL );
+
+    bool IsFootprintLibWritable( const wxString& aLibraryPath );
+
+    void FootprintLibOptions( PROPERTIES* aListToAppendTo ) const;
+
+    //-----</PLUGIN API>---------------------------------------------------------
+
+    FPPYTHON_PLUGIN();        // constructor, if any, must be zero arg
+    ~FPPYTHON_PLUGIN();
+ private:
+    /// A pcb parser instance to be able to parse data from python plugin
+    PCB_PARSER*      m_parser;
+    /// local thread
+    PyThreadState*   m_pythread;
+    /// Python module loaded
+    PyObject*        m_module;
+
+    wxString m_lib_path;
+    wxString m_plugin_name;
+
+    /// build parameters of the lib as expected format for python
+    PyObject *buildParams(const PROPERTIES* aProperties );
+
+    /// prepare the call of a python function (various checks, loading of python module...)
+    PyObject *prepareCall(const char *aFunctionName,const wxString& aLibraryPath,const PROPERTIES* aProperties);
+    bool changedLibPath( const wxString& aCurrentLibPath );
+    bool changedPluginName( const wxString& aCurrentPluginName );
+
+    wxArrayString pyArrayStringToWx( PyObject* aArrayString );
+
+};
+
+
+#endif // fppython_PLUGIN_H_

=== modified file 'pcbnew/io_mgr.cpp'
--- pcbnew/io_mgr.cpp	2014-01-02 02:17:07 +0000
+++ pcbnew/io_mgr.cpp	2014-03-07 17:53:17 +0000
@@ -37,6 +37,10 @@
  #include <github/github_plugin.h>
 #endif
 
+#if defined(BUILD_FPPYTHON_PLUGIN)
+ #include <fppython/fppython_plugin.h>
+#endif
+
 #include <wildcards_and_files_ext.h>
 
 #define FMT_UNIMPLEMENTED   _( "Plugin '%s' does not implement the '%s' function." )
@@ -98,6 +102,13 @@
 #else
         THROW_IO_ERROR( "BUILD_GITHUB_PLUGIN not enabled in cmake build environment" );
 #endif
+    case FPPYTHON:
+#if defined(BUILD_FPPYTHON_PLUGIN)
+        return new FPPYTHON_PLUGIN();
+#else
+#error "ici"
+        THROW_IO_ERROR( "BUILD_FPPYTHON_PLUGIN not enabled in cmake build environment" );
+#endif
     }
 
     return NULL;
@@ -142,6 +153,9 @@
 
     case GITHUB:
         return wxString( wxT( "Github" ) );
+
+    case FPPYTHON:
+        return wxString( wxT( "Fppython" ) );
     }
 }
 
@@ -170,6 +184,9 @@
     if( aType == wxT( "Github" ) )
         return GITHUB;
 
+    if( aType == wxT( "Fppython" ) )
+        return FPPYTHON;
+
     // wxASSERT( blow up here )
 
     return PCB_FILE_T( -1 );

=== modified file 'pcbnew/io_mgr.h'
--- pcbnew/io_mgr.h	2014-01-02 02:17:07 +0000
+++ pcbnew/io_mgr.h	2014-03-07 17:45:53 +0000
@@ -78,6 +78,7 @@
         PCAD,
         GEDA_PCB,       ///< Geda PCB file formats.
         GITHUB,         ///< Read only http://github.com repo holding pretty footprints
+        FPPYTHON,         ///< Fppython plugin
 
         // add your type here.
 

=== modified file 'pcbnew/plugin.cpp'
--- pcbnew/plugin.cpp	2014-01-19 13:12:57 +0000
+++ pcbnew/plugin.cpp	2014-03-09 08:16:46 +0000
@@ -136,12 +136,5 @@
         ));
 #endif
 
-#if 1
-    // Suitable for a C++ to python PLUGIN::Footprint*() adapter, move it to the adapter
-    // if and when implemented.
-    (*aListToAppendTo)["python_footprint_plugin"] = UTF8( _(
-        "Enter the python module which implements the PLUGIN::Footprint*() functions."
-        ));
-#endif
 }
 

=== modified file 'scripting/python_scripting.h'
--- scripting/python_scripting.h	2013-07-19 18:27:22 +0000
+++ scripting/python_scripting.h	2014-03-08 22:02:03 +0000
@@ -27,6 +27,7 @@
 bool        pcbnewInitPythonScripting( const char * aUserPluginsPath );
 void        pcbnewFinishPythonScripting();
 
+extern PyThreadState* g_PythonMainTState;
 
 #ifdef KICAD_SCRIPTING_WXPYTHON
 


References