From ea0562c124332cf7037eec41474dc81a472809e2 Mon Sep 17 00:00:00 2001 From: Miguel Angel Ajo Date: Sat, 5 May 2012 22:18:47 +0200 Subject: [PATCH] KiCad scripting plugin architecture, footprint wizards first --- pcbnew/CMakeLists.txt | 8 +- pcbnew/scripting/pcbnew.i | 2 + pcbnew/scripting/pcbnew_footprint_wizards.cpp | 179 ++++++++++++++++++ pcbnew/scripting/pcbnew_footprint_wizards.h | 45 +++++ pcbnew/scripting/plugins.i | 11 ++ .../scripting/plugins/fpc_footprint_wizard.py | 83 ++++++++ scripting/kicad.i | 1 + scripting/kicadplugins.i | 147 ++++++++++++++ scripting/python_scripting.cpp | 13 +- 9 files changed, 476 insertions(+), 13 deletions(-) create mode 100644 pcbnew/scripting/pcbnew_footprint_wizards.cpp create mode 100644 pcbnew/scripting/pcbnew_footprint_wizards.h create mode 100644 pcbnew/scripting/plugins.i create mode 100644 pcbnew/scripting/plugins/fpc_footprint_wizard.py create mode 100644 scripting/kicadplugins.i diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index c33a73184c..d20fa8afb0 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -231,14 +231,15 @@ set(PCBNEW_COMMON_SRCS # Scripting sources ## -if (KICAD_SCRIPTING) - - set(PCBNEW_SCRIPTING_PYTHON_HELPERS +set(PCBNEW_SCRIPTING_PYTHON_HELPERS ../scripting/wx_python_helpers.cpp ../scripting/python_scripting.cpp scripting/pcbnew_scripting_helpers.cpp + scripting/pcbnew_footprint_wizards.cpp ) +if (KICAD_SCRIPTING) + set(PCBNEW_SCRIPTING_SRCS pcbnew_wrap.cxx ${PCBNEW_SCRIPTING_PYTHON_HELPERS} @@ -279,6 +280,7 @@ if (KICAD_SCRIPTING) DEPENDS scripting/board.i DEPENDS scripting/board_item.i DEPENDS scripting/module.i + DEPENDS scripting/plugins.i DEPENDS scripting/units.i DEPENDS ../scripting/dlist.i DEPENDS ../scripting/kicad.i diff --git a/pcbnew/scripting/pcbnew.i b/pcbnew/scripting/pcbnew.i index f7280cff4c..393df32a8f 100644 --- a/pcbnew/scripting/pcbnew.i +++ b/pcbnew/scripting/pcbnew.i @@ -133,5 +133,7 @@ %include "board.i" %include "module.i" +%include "plugins.i" %include "units.i" + \ No newline at end of file diff --git a/pcbnew/scripting/pcbnew_footprint_wizards.cpp b/pcbnew/scripting/pcbnew_footprint_wizards.cpp new file mode 100644 index 0000000000..8bd60af79e --- /dev/null +++ b/pcbnew/scripting/pcbnew_footprint_wizards.cpp @@ -0,0 +1,179 @@ +/** + * @file pcbnew_footprint_wizards.cpp + * @brief Class PCBNEW_FOOTPRINT_WIZARDS + */ + +#include "pcbnew_footprint_wizards.h" +#include + +FOOTPRINT_WIZARD::FOOTPRINT_WIZARD(PyObject *aWizard) +{ + this->py_wizard = aWizard; + Py_XINCREF(aWizard); +} + +FOOTPRINT_WIZARD::~FOOTPRINT_WIZARD() +{ + Py_XDECREF(this->py_wizard); +} + +PyObject* FOOTPRINT_WIZARD::CallMethod(const char* aMethod, PyObject *aArglist) +{ + PyObject *pFunc; + + /* pFunc is a new reference to the desired method */ + pFunc = PyObject_GetAttrString(this->py_wizard, aMethod); + + if (pFunc && PyCallable_Check(pFunc)) + { + PyObject *result; + + result = PyObject_CallObject(pFunc, aArglist); + + if (PyErr_Occurred()) + { + PyObject *t, *v, *b; + PyErr_Fetch(&t, &v, &b); + printf ("calling %s()\n",aMethod); + printf ("Exception: %s\n",PyString_AsString(PyObject_Str(v))); + printf (" : %s\n",PyString_AsString(PyObject_Str(b))); + } + + + if (result) + { + Py_XDECREF(pFunc); + return result; + } + + } + else + { + printf("method not found, or not callable: %s\n",aMethod); + } + + if (pFunc) Py_XDECREF(pFunc); + + return NULL; +} + +wxString FOOTPRINT_WIZARD::CallRetStrMethod(const char* aMethod, PyObject *aArglist) +{ + wxString ret; + PyObject *result = CallMethod(aMethod,aArglist); + if (result) + { + const char *str_res = PyString_AsString(result); + ret = wxString::FromUTF8(str_res); + Py_DECREF(result); + } + else + { + printf("method not found, or not callable: %s\n",aMethod); + } + + return ret; +} +wxString FOOTPRINT_WIZARD::GetName() +{ + return CallRetStrMethod("GetName"); +} + +wxString FOOTPRINT_WIZARD::GetImage() +{ + return CallRetStrMethod("GetImage"); +} + +wxString FOOTPRINT_WIZARD::GetDescription() +{ + return CallRetStrMethod("GetDescription"); +} + +int FOOTPRINT_WIZARD::GetNumParameterPages() +{ + int ret; + PyObject *result; + + /* Time to call the callback */ + result = CallMethod("GetNumParameterPages",NULL); + + if (result) + { + if (!PyInt_Check(result)) return -1; + ret = PyInt_AsLong(result); + Py_DECREF(result); + } + return ret; +} + +wxString FOOTPRINT_WIZARD::GetParameterPageName(int aPage) +{ + wxString ret; + PyObject *arglist; + PyObject *result; + + /* Time to call the callback */ + arglist = Py_BuildValue("(i)", aPage); + result = CallMethod("GetParameterPageName",arglist); + Py_DECREF(arglist); + + if (result) + { + const char *str_res = PyString_AsString(result); + ret = wxString::FromUTF8(str_res); + Py_DECREF(result); + } + return ret; +} + +wxArrayString FOOTPRINT_WIZARD::GetParameterNames(int aPage) +{ + wxArrayString a; + return a; +} + +wxArrayString FOOTPRINT_WIZARD::GetParameterValues(int aPage) +{ + wxArrayString a; + return a; +} + +wxString FOOTPRINT_WIZARD::SetParameterValues(int aPage,wxArrayString& aValues) +{ + wxString ret; + return ret; +} + +MODULE FOOTPRINT_WIZARD::*GetModule() +{ + return NULL; +} + +std::vector FOOTPRINT_WIZARDS::m_FootprintWizards; + +void FOOTPRINT_WIZARDS::register_wizard(PyObject* wizard) +{ + FOOTPRINT_WIZARD *fw; + + fw = new FOOTPRINT_WIZARD(wizard); + m_FootprintWizards.push_back(fw); + + printf("Registered python footprint wizard '%s'\n", + (const char*)fw->GetName().mb_str()); + + int pages = fw->GetNumParameterPages(); + printf(" %d pages\n",pages); + + for (int n=0; n'%s'\n",n, + (const char*)fw->GetParameterPageName(n).mb_str()); + } + + + +} + + + + diff --git a/pcbnew/scripting/pcbnew_footprint_wizards.h b/pcbnew/scripting/pcbnew_footprint_wizards.h new file mode 100644 index 0000000000..37349cd2d3 --- /dev/null +++ b/pcbnew/scripting/pcbnew_footprint_wizards.h @@ -0,0 +1,45 @@ +/** + * @file pcbnew_footprint_wizards.h + * @brief Class PCBNEW_FOOTPRINT_WIZARDS + */ + +#ifndef PCBNEW_FOOTPRINT_WIZARDS_H +#define PCBNEW_FOOTPRINT_WIZARDS_H +#include +#include +#include + +class FOOTPRINT_WIZARD +{ + + PyObject *py_wizard; + PyObject *CallMethod(const char *aMethod, PyObject *aArglist=NULL); + wxString CallRetStrMethod(const char *aMethod, PyObject *aArglist=NULL); +public: + FOOTPRINT_WIZARD(PyObject *wizard); + ~FOOTPRINT_WIZARD(); + wxString GetName(); + wxString GetImage(); + wxString GetDescription(); + int GetNumParameterPages(); + wxString GetParameterPageName(int aPage); + wxArrayString GetParameterNames(int aPage); + wxArrayString GetParameterValues(int aPage); + wxString SetParameterValues(int aPage,wxArrayString& aValues); + MODULE *GetModule(); + +}; + + +class FOOTPRINT_WIZARDS +{ +private: + static std::vector m_FootprintWizards; + +public: + static void register_wizard(PyObject *wizard); + +}; + +#endif /* PCBNEW_FOOTPRINT_WIZARDS_H */ + diff --git a/pcbnew/scripting/plugins.i b/pcbnew/scripting/plugins.i new file mode 100644 index 0000000000..591ba79884 --- /dev/null +++ b/pcbnew/scripting/plugins.i @@ -0,0 +1,11 @@ + +%{ +#include +%} + +class FOOTPRINT_WIZARDS +{ +public: + static void register_wizard(PyObject *wizard); + +}; diff --git a/pcbnew/scripting/plugins/fpc_footprint_wizard.py b/pcbnew/scripting/plugins/fpc_footprint_wizard.py new file mode 100644 index 0000000000..725da1a026 --- /dev/null +++ b/pcbnew/scripting/plugins/fpc_footprint_wizard.py @@ -0,0 +1,83 @@ +#!/usr/bin/python + +from pcbnew import * + +class FPCFootprintWizard(FootprintWizardPlugin): + def __init__(self): + FootprintWizardPlugin.__init__(self) + self.name = "FPC" + self.description = "FPC Footprint Wizard" + self.parameters = { + "Pads": + {"n":40,"pitch":0.5,"width":0.25,"height":1.6}, + "Shield": + {"shield_to_pad":1.6,"from_top":1.3,"width":1.5,"height":2} + } + + def smdRectPad(self,module,size,pos,name): + pad = D_PAD(module) + pad.SetSize(size) + pad.SetShape(PAD_RECT) + pad.SetAttribute(PAD_SMD) + pad.SetLayerMask(PAD_SMD_DEFAULT_LAYERS) + pad.SetPos0(pos) + pad.SetPadName(name) + return pad + + def BuildFootprint(self): + + pads = self.parameters["Pads"]["n"] + pad_width = self.parameters["Pads"]["width"] + pad_height = self.parameters["Pads"]["height"] + pad_pitch = self.parameters["Pads"]["pitch"] + shl_width = self.parameters["Shield"]["width"] + shl_height = self.parameters["Shield"]["height"] + shl_to_pad = self.parameters["Shield"]["shield_to_pad"] + shl_from_top = self.parameters["Shield"]["from_top"] + + size_pad = wxSizeMM(pad_width,pad_height) + size_shld = wxSizeMM(shl_width,shl_height) + + + # create a new module, it's parent is our previously created pcb + module = MODULE(None) + module.SetReference("FPC"+str(pads)) # give it a reference name + module.m_Reference.SetPos0(wxPointMM(-1,-1)) + + # create a pad array and add it to the module + + for n in range (0,pads): + pad = self.smdRectPad(module,size_pad,wxPointMM(pad_pitch*n,0),str(n+1)) + module.Add(pad) + + + pad_s0 = self.smdRectPad(module, + size_shld, + wxPointMM(-shl_to_pad,shl_from_top), + "0") + pad_s1 = self.smdRectPad(module, + size_shld, + wxPointMM((pads-1)*pad_pitch+shl_to_pad,shl_from_top), + "0") + + module.Add(pad_s0) + module.Add(pad_s1) + + e = EDGE_MODULE(module) + e.SetStart0(wxPointMM(-1,0)) + e.SetEnd0(wxPointMM(0,0)) + e.SetWidth(FromMM(0.2)) + e.SetLayer(EDGE_LAYER) + e.SetShape(S_SEGMENT) + module.Add(e) + + # save the PCB to disk + module.SetLibRef("FPC"+str(pads)) + + self.module = module + +# create our footprint wizard +fpc_wizard = FPCFootprintWizard() + +# register it into pcbnew +fpc_wizard.register() diff --git a/scripting/kicad.i b/scripting/kicad.i index 9784ede559..7029044e1d 100644 --- a/scripting/kicad.i +++ b/scripting/kicad.i @@ -110,3 +110,4 @@ /* std template mappings */ %template(intVector) std::vector; +%include "kicadplugins.i" diff --git a/scripting/kicadplugins.i b/scripting/kicadplugins.i new file mode 100644 index 0000000000..084561c711 --- /dev/null +++ b/scripting/kicadplugins.i @@ -0,0 +1,147 @@ +/* + * This program source code file is part of KiCad, a free EDA CAD application. + * + * Copyright (C) 2012 NBEE Embedded Systems, Miguel Angel Ajo + * Copyright (C) 1992-2012 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 + */ + +%pythoncode +{ + + +# KiCadPlugin base class will register any plugin into the right place +class KiCadPlugin: + def __init__(self): + pass + + def register(self): + if isinstance(self,FilePlugin): + pass # register to file plugins in C++ + if isinstance(self,FootprintWizardPlugin): + FOOTPRINT_WIZARDS.register_wizard(self) + return + + if isinstance(self,ActionPlugin): + pass # register to action plugins in C++ + + return + + + + +# This will be the file io scripting based plugins class +class FilePlugin(KiCadPlugin): + def __init__(self): + KiCadPlugin.__init__(self) + + +# Scriping footprint wizards +class FootprintWizardPlugin(KiCadPlugin): + def __init__(self): + KiCadPlugin.__init__(self) + self.defaults() + + def defaults(self): + self.module = None + self.parameters = {} + self.name = "Undefined Footprint Wizard plugin" + self.description = "" + self.image = "" + + def GetName(self): + return self.name + + def GetImage(self): + return self.image + + def GetDescription(self): + return self.description + + + def GetNumParameterPages(self): + return len(self.parameters) + + def GetParameterPageName(self,page_n): + return self.parameters.keys()[page_n] + + def GetParameterNames(self,page_n): + name = self.GetParameterPageName(page_n) + return self.parameters[name].keys() + + def GetParameterValues(self,page_n): + name = self.GetParameterPageName(page_n) + return self.parameters[name].keys() + + def GetParameterValues(self,page_n): + name = self.GetParameterPageName(page_n) + return self.parameters[name].values() + + + def SetParameterValues(self,page_n,values): + name = self.GetParameterPageName(pagen_n) + keys = self.parameters[name].values() + n=0 + for key in keys: + self.parameters[name][key] = values[n] + n+=1 + + def GetModule(self): + self.BuildFootprint() + return self.module + + def BuildFootprint(self): + return + + def Show(self): + print "Footprint Wizard Name: ",self.GetName() + print "Footprint Wizard Description: ",self.GetDescription() + n_pages = self.GetNumParameterPages() + print " setup pages: ",n_pages + for page in range(0,n_pages): + name = self.GetParameterPageName(page) + values = self.GetParameterValues(page) + names = self.GetParameterNames(page) + print "page %d) %s"%(page,name) + for n in range (0,len(values)): + print "\t%s\t:\t%s"%(names[n],values[n]) + +class ActionPlugin(KiCadPlugin): + def __init__(self): + KiCadPlugin.__init__(self) + + +def LoadPlugins(): + import os + import sys + + plugins_dir = os.environ['HOME']+'/.kicad_plugins/' + + sys.path.append(plugins_dir) + + for module in os.listdir(plugins_dir): + if os.path.isdir(plugins_dir+module): + __import__(module, locals(), globals()) + + if module == '__init__.py' or module[-3:] != '.py': + continue + __import__(module[:-3], locals(), globals()) + + +} diff --git a/scripting/python_scripting.cpp b/scripting/python_scripting.cpp index d8f07e67b3..8abd092e24 100644 --- a/scripting/python_scripting.cpp +++ b/scripting/python_scripting.cpp @@ -90,15 +90,6 @@ void pcbnewInitPythonScripting() swigAddModules(); swigSwitchPythonBuiltin(); -#if 0 - /* print the list of modules available from python */ - while(PyImport_Inittab[i].name) - { - printf("name[%d]=>%s\n",i,PyImport_Inittab[i].name); - i++; - } -#endif - Py_Initialize(); /* setup the scripting path, we may need to add the installation path @@ -106,7 +97,9 @@ void pcbnewInitPythonScripting() PyRun_SimpleString("import sys\n" "sys.path.append(\".\")\n" - "import pcbnew\n"); + "import pcbnew\n" + "pcbnew.LoadPlugins()" + ); }