kicad-developers team mailing list archive
-
kicad-developers team
-
Mailing list archive
-
Message #12648
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