Browse Source
Pcbnew: Add ACTION_PLUGINS class. It allows loading python scripts in footprint editor and run them from the Tools/External Plugins menu
Pcbnew: Add ACTION_PLUGINS class. It allows loading python scripts in footprint editor and run them from the Tools/External Plugins menu
This plugin mechanism is enabled only if option -DKICAD_SCRIPTING_ACTION_MENU=ON (it is off by default) It imply -DKICAD_SCRIPTING=0N This is currently for testing purposes only for developers, not yet for users.pull/3/merge
committed by
jean-pierre charras
12 changed files with 737 additions and 5 deletions
-
14CMakeLists.txt
-
22include/wxPcbStruct.h
-
2pcbnew/CMakeLists.txt
-
155pcbnew/class_action_plugin.cpp
-
173pcbnew/class_action_plugin.h
-
21pcbnew/menubar_pcbframe.cpp
-
9pcbnew/pcbframe.cpp
-
3pcbnew/pcbnew_id.h
-
242pcbnew/swig/pcbnew_action_plugins.cpp
-
62pcbnew/swig/pcbnew_action_plugins.h
-
9pcbnew/swig/plugins.i
-
30scripting/kicadplugins.i
@ -0,0 +1,155 @@ |
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2017 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 |
|||
*/ |
|||
|
|||
|
|||
/**
|
|||
* @file class_action_plugin.cpp |
|||
* @brief Class ACTION_PLUGIN and ACTION_PLUGINS |
|||
*/ |
|||
|
|||
#include "class_action_plugin.h"
|
|||
|
|||
|
|||
ACTION_PLUGIN::~ACTION_PLUGIN() |
|||
{ |
|||
} |
|||
|
|||
|
|||
void ACTION_PLUGIN::register_action() |
|||
{ |
|||
ACTION_PLUGINS::register_action( this ); |
|||
} |
|||
|
|||
|
|||
std::vector<ACTION_PLUGIN*> ACTION_PLUGINS::m_Actions; |
|||
std::vector<int> ACTION_PLUGINS::m_ActionsMenu; |
|||
|
|||
|
|||
ACTION_PLUGIN* ACTION_PLUGINS::GetAction( int aIndex ) |
|||
{ |
|||
return m_Actions[aIndex]; |
|||
} |
|||
|
|||
|
|||
ACTION_PLUGIN* ACTION_PLUGINS::GetActionByMenu( int menu ) |
|||
{ |
|||
int max = GetActionsCount(); |
|||
|
|||
for( int i = 0; i<max; i++ ) |
|||
{ |
|||
if( m_ActionsMenu[i] == menu ) |
|||
return m_Actions[i]; |
|||
} |
|||
|
|||
return NULL; |
|||
} |
|||
|
|||
|
|||
void ACTION_PLUGINS::SetActionMenu( int aIndex, int idMenu ) |
|||
{ |
|||
m_ActionsMenu[aIndex] = idMenu; |
|||
} |
|||
|
|||
|
|||
int ACTION_PLUGINS::GetActionMenu( int aIndex ) |
|||
{ |
|||
return m_ActionsMenu[aIndex]; |
|||
} |
|||
|
|||
|
|||
ACTION_PLUGIN* ACTION_PLUGINS::GetAction( wxString aName ) |
|||
{ |
|||
int max = GetActionsCount(); |
|||
|
|||
for( int i = 0; i<max; i++ ) |
|||
{ |
|||
ACTION_PLUGIN* action = GetAction( i ); |
|||
|
|||
wxString name = action->GetName(); |
|||
|
|||
if( name.Cmp( aName )==0 ) |
|||
return action; |
|||
} |
|||
|
|||
return NULL; |
|||
} |
|||
|
|||
|
|||
int ACTION_PLUGINS::GetActionsCount() |
|||
{ |
|||
return m_Actions.size(); |
|||
} |
|||
|
|||
|
|||
void ACTION_PLUGINS::register_action( ACTION_PLUGIN* aAction ) |
|||
{ |
|||
int updatedMenu = 0; |
|||
|
|||
// Search for this entry do not register twice this action:
|
|||
for( int ii = 0; ii < GetActionsCount(); ii++ ) |
|||
{ |
|||
if( aAction == GetAction( ii ) ) // Already registered
|
|||
return; |
|||
} |
|||
|
|||
// Search for a action with the same name, and remove it if found
|
|||
for( int ii = 0; ii < GetActionsCount(); ii++ ) |
|||
{ |
|||
ACTION_PLUGIN* action = GetAction( ii ); |
|||
|
|||
if( action->GetName() == aAction->GetName() ) |
|||
{ |
|||
updatedMenu = GetActionMenu( ii ); |
|||
m_Actions.erase( m_Actions.begin() + ii ); |
|||
m_ActionsMenu.erase( m_ActionsMenu.begin() + ii ); |
|||
|
|||
delete action; |
|||
|
|||
break; |
|||
} |
|||
} |
|||
|
|||
m_Actions.push_back( aAction ); |
|||
m_ActionsMenu.push_back( updatedMenu ); |
|||
} |
|||
|
|||
|
|||
bool ACTION_PLUGINS::deregister_object( void* aObject ) |
|||
{ |
|||
int max = GetActionsCount(); |
|||
|
|||
for( int i = 0; i<max; i++ ) |
|||
{ |
|||
ACTION_PLUGIN* action = GetAction( i ); |
|||
|
|||
if( action->GetObject() == aObject ) |
|||
{ |
|||
m_Actions.erase( m_Actions.begin() + i ); |
|||
m_ActionsMenu.erase( m_ActionsMenu.begin() + i ); |
|||
delete action; |
|||
return true; |
|||
} |
|||
} |
|||
|
|||
return false; |
|||
} |
@ -0,0 +1,173 @@ |
|||
/* |
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2017 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 |
|||
*/ |
|||
|
|||
|
|||
/** |
|||
* @file class_action_plugin.h |
|||
* @brief Class PCBNEW_ACTION_PLUGINS |
|||
*/ |
|||
|
|||
#ifndef CLASS_ACTION_PLUGIN_H |
|||
#define CLASS_ACTION_PLUGIN_H |
|||
#include <vector> |
|||
#include <wxPcbStruct.h> |
|||
|
|||
/** |
|||
* Class ACTION_PLUGIN |
|||
* This is the parent class from where any action plugin class must |
|||
* derive |
|||
*/ |
|||
class ACTION_PLUGIN |
|||
{ |
|||
public: |
|||
ACTION_PLUGIN() {} |
|||
virtual ~ACTION_PLUGIN(); |
|||
|
|||
/** |
|||
* Function GetCategoryName |
|||
* @return the category name of the action (to be able to group action under the same submenu) |
|||
*/ |
|||
virtual wxString GetCategoryName() = 0; |
|||
|
|||
/** |
|||
* Function GetName |
|||
* @return the name of the action |
|||
*/ |
|||
|
|||
virtual wxString GetName() = 0; |
|||
|
|||
/** |
|||
* Function GetDescription |
|||
* @return a description of the action plugin |
|||
*/ |
|||
virtual wxString GetDescription() = 0; |
|||
|
|||
/** |
|||
* Function GetObject |
|||
* This method gets the pointer to the object from where this action constructs |
|||
* @return it's a void pointer, as it could be a PyObject or any other |
|||
*/ |
|||
virtual void* GetObject() = 0; |
|||
|
|||
/** |
|||
* Function Run |
|||
* This method the the action |
|||
*/ |
|||
virtual void Run() = 0; |
|||
|
|||
/** |
|||
* Function register_action |
|||
* It's the standard method of a "ACTION_PLUGIN" to register itself into |
|||
* the ACTION_PLUGINS singleton manager |
|||
*/ |
|||
void register_action(); |
|||
}; |
|||
|
|||
|
|||
/** |
|||
* Class ACTION_PLUGINS |
|||
* Mainly static. Storing all plugins informations. |
|||
*/ |
|||
class ACTION_PLUGINS |
|||
{ |
|||
private: |
|||
/** |
|||
* ACTION_PLUGIN system wide static list |
|||
*/ |
|||
static std::vector<ACTION_PLUGIN*> m_Actions; |
|||
|
|||
/** |
|||
* system wide static association between Plugin and menu id |
|||
*/ |
|||
static std::vector<int> m_ActionsMenu; |
|||
|
|||
public: |
|||
|
|||
/** |
|||
* Function register_action |
|||
* An action calls this static method when it wants to register itself |
|||
* into the system actions |
|||
* |
|||
* @param aAction is the action plugin to be registered |
|||
*/ |
|||
static void register_action( ACTION_PLUGIN* aAction ); |
|||
|
|||
/** |
|||
* Function deregister_object |
|||
* Anyone calls this method to deregister an object which builds a action, |
|||
* it will lookup on the vector calling GetObject until find, then removed |
|||
* and deleted |
|||
* |
|||
* @param aObject is the action plugin object to be deregistered |
|||
*/ |
|||
static bool deregister_object( void* aObject ); |
|||
|
|||
/** |
|||
* Function GetAction |
|||
* @param aName is the action plugin name |
|||
* @return a action object by it's name or NULL if it isn't available. |
|||
*/ |
|||
static ACTION_PLUGIN* GetAction( wxString aName ); |
|||
|
|||
/** |
|||
* Function SetActionMenu |
|||
* Associate a menu id to an action plugin |
|||
* @param aInded is the action index |
|||
* @param idMenu is the associated menuitem id |
|||
*/ |
|||
static void SetActionMenu( int aIndex, int idMenu ); |
|||
|
|||
|
|||
/** |
|||
* Function GetActionMenu |
|||
* Provide menu id for a plugin index |
|||
* @param aIndex is the action index |
|||
* @return associated menuitem id |
|||
*/ |
|||
static int GetActionMenu( int aIndex ); |
|||
|
|||
|
|||
/** |
|||
* Function GetActionByMenu |
|||
* find action plugin associated to a menu id |
|||
* @param menu is the menu id (defined with SetActionMenu) |
|||
* @return the associated ACTION_PLUGIN (or null if not found) |
|||
*/ |
|||
static ACTION_PLUGIN* GetActionByMenu( int menu ); |
|||
|
|||
|
|||
/** |
|||
* Function GetAction |
|||
* @return a action object by it's number or NULL if it isn't available. |
|||
* @param aIndex is the action index in list |
|||
*/ |
|||
static ACTION_PLUGIN* GetAction( int aIndex ); |
|||
|
|||
/** |
|||
* Function GetActionsCount |
|||
* @return the number of actions available into the system |
|||
*/ |
|||
static int GetActionsCount(); |
|||
}; |
|||
|
|||
#endif /* PCBNEW_ACTION_PLUGINS_H */ |
@ -0,0 +1,242 @@ |
|||
/*
|
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2017 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 |
|||
*/ |
|||
|
|||
/**
|
|||
* @file pcbnew_action_plugins.cpp |
|||
* @brief Class PCBNEW_PYTHON_ACTION_PLUGINS |
|||
*/ |
|||
|
|||
#include "pcbnew_action_plugins.h"
|
|||
#include <python_scripting.h>
|
|||
#include <stdio.h>
|
|||
#include <macros.h>
|
|||
#include <pcbnew_id.h>
|
|||
#include <menus_helpers.h>
|
|||
#include <class_drawpanel.h> // m_canvas
|
|||
#include <class_board.h>
|
|||
#include <class_module.h>
|
|||
#include <class_track.h>
|
|||
#include <board_commit.h>
|
|||
#include <kicad_device_context.h>
|
|||
|
|||
PYTHON_ACTION_PLUGIN::PYTHON_ACTION_PLUGIN( PyObject* aAction ) |
|||
{ |
|||
PyLOCK lock; |
|||
|
|||
this->m_PyAction = aAction; |
|||
Py_XINCREF( aAction ); |
|||
} |
|||
|
|||
|
|||
PYTHON_ACTION_PLUGIN::~PYTHON_ACTION_PLUGIN() |
|||
{ |
|||
PyLOCK lock; |
|||
|
|||
Py_XDECREF( this->m_PyAction ); |
|||
} |
|||
|
|||
|
|||
PyObject* PYTHON_ACTION_PLUGIN::CallMethod( const char* aMethod, PyObject* aArglist ) |
|||
{ |
|||
PyLOCK lock; |
|||
|
|||
PyErr_Clear(); |
|||
// pFunc is a new reference to the desired method
|
|||
PyObject* pFunc = PyObject_GetAttrString( this->m_PyAction, aMethod ); |
|||
|
|||
if( pFunc && PyCallable_Check( pFunc ) ) |
|||
{ |
|||
PyObject* result = PyObject_CallObject( pFunc, aArglist ); |
|||
|
|||
if( PyErr_Occurred() ) |
|||
{ |
|||
wxMessageBox( PyErrStringWithTraceback(), |
|||
wxT( "Exception on python action plugin code" ), |
|||
wxICON_ERROR | wxOK ); |
|||
} |
|||
|
|||
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 PYTHON_ACTION_PLUGIN::CallRetStrMethod( const char* aMethod, PyObject* aArglist ) |
|||
{ |
|||
wxString ret; |
|||
PyLOCK lock; |
|||
|
|||
PyObject* result = CallMethod( aMethod, aArglist ); |
|||
|
|||
if( result ) |
|||
{ |
|||
const char* str_res = PyString_AsString( result ); |
|||
ret = FROM_UTF8( str_res ); |
|||
Py_DECREF( result ); |
|||
} |
|||
|
|||
return ret; |
|||
} |
|||
|
|||
|
|||
wxString PYTHON_ACTION_PLUGIN::GetCategoryName() |
|||
{ |
|||
PyLOCK lock; |
|||
|
|||
return CallRetStrMethod( "GetCategoryName" ); |
|||
} |
|||
|
|||
|
|||
wxString PYTHON_ACTION_PLUGIN::GetName() |
|||
{ |
|||
PyLOCK lock; |
|||
|
|||
return CallRetStrMethod( "GetName" ); |
|||
} |
|||
|
|||
|
|||
wxString PYTHON_ACTION_PLUGIN::GetDescription() |
|||
{ |
|||
PyLOCK lock; |
|||
|
|||
return CallRetStrMethod( "GetDescription" ); |
|||
} |
|||
|
|||
|
|||
void PYTHON_ACTION_PLUGIN::Run() |
|||
{ |
|||
PyLOCK lock; |
|||
|
|||
CallMethod( "Run" ); |
|||
} |
|||
|
|||
|
|||
void* PYTHON_ACTION_PLUGIN::GetObject() |
|||
{ |
|||
return (void*) m_PyAction; |
|||
} |
|||
|
|||
|
|||
void PYTHON_ACTION_PLUGINS::register_action( PyObject* aPyAction ) |
|||
{ |
|||
PYTHON_ACTION_PLUGIN* fw = new PYTHON_ACTION_PLUGIN( aPyAction ); |
|||
|
|||
fw->register_action(); |
|||
} |
|||
|
|||
|
|||
void PYTHON_ACTION_PLUGINS::deregister_action( PyObject* aPyAction ) |
|||
{ |
|||
// deregister also destroyes the previously created "PYTHON_ACTION_PLUGIN object"
|
|||
ACTION_PLUGINS::deregister_object( (void*) aPyAction ); |
|||
} |
|||
|
|||
|
|||
#if defined(KICAD_SCRIPTING) && defined(KICAD_SCRIPTING_ACTION_MENU)
|
|||
|
|||
void PCB_EDIT_FRAME::OnActionPlugin( wxCommandEvent& aEvent ) |
|||
{ |
|||
int id = aEvent.GetId(); |
|||
|
|||
ACTION_PLUGIN* actionPlugin = ACTION_PLUGINS::GetActionByMenu( id ); |
|||
|
|||
if( actionPlugin ) |
|||
{ |
|||
// TODO: Adding recovery point for jobs
|
|||
// BOARD_COMMIT commit( this );
|
|||
// commit.Push( _( "External plugin" ) );
|
|||
|
|||
actionPlugin->Run(); |
|||
|
|||
OnModify(); |
|||
|
|||
if( IsGalCanvasActive() ) |
|||
{ |
|||
UseGalCanvas( GetGalCanvas() ); |
|||
} |
|||
else |
|||
{ |
|||
GetScreen()->SetModify(); |
|||
Refresh(); |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
void PCB_EDIT_FRAME::OnActionPluginRefresh( wxCommandEvent& aEvent ) |
|||
{ |
|||
char cmd[1024]; |
|||
|
|||
snprintf( cmd, sizeof(cmd), |
|||
"pcbnew.LoadPlugins(\"%s\")", TO_UTF8( PyScriptingPath() ) ); |
|||
|
|||
PyLOCK lock; |
|||
// ReRun the Python method pcbnew.LoadPlugins (already called when starting Pcbnew)
|
|||
PyRun_SimpleString( cmd ); |
|||
|
|||
initActionPluginMenus(); |
|||
} |
|||
|
|||
|
|||
void PCB_EDIT_FRAME::initActionPluginMenus() |
|||
{ |
|||
wxMenu* actionMenu = GetMenuBar()->FindItem( ID_TOOLBARH_PCB_ACTION_PLUGIN )->GetSubMenu(); |
|||
|
|||
for( int i = 0; i < ACTION_PLUGINS::GetActionsCount(); i++ ) |
|||
{ |
|||
// Init menu only for not already created Items
|
|||
if( ACTION_PLUGINS::GetActionMenu( i ) == 0 ) |
|||
{ |
|||
wxMenuItem* item = AddMenuItem( actionMenu, wxID_ANY, |
|||
ACTION_PLUGINS::GetAction( i )->GetName(), |
|||
ACTION_PLUGINS::GetAction( i )->GetDescription(), |
|||
KiBitmap( hammer_xpm ) ); |
|||
|
|||
ACTION_PLUGINS::SetActionMenu( i, item->GetId() ); |
|||
|
|||
Connect( |
|||
item->GetId(), wxEVT_COMMAND_MENU_SELECTED, |
|||
(wxObjectEventFunction) (wxEventFunction) (wxCommandEventFunction) & |
|||
PCB_EDIT_FRAME::OnActionPlugin ); |
|||
} |
|||
|
|||
// Delete is not handled by plugins system (yet)
|
|||
} |
|||
} |
|||
|
|||
|
|||
#endif
|
@ -0,0 +1,62 @@ |
|||
/* |
|||
* This program source code file is part of KiCad, a free EDA CAD application. |
|||
* |
|||
* Copyright (C) 2017 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 |
|||
*/ |
|||
|
|||
/** |
|||
* @file pcbnew_action_plugins.h |
|||
* @brief Class PCBNEW_ACTION_PLUGINS |
|||
*/ |
|||
|
|||
#ifndef PCBNEW_ACTION_PLUGINS_H |
|||
#define PCBNEW_ACTION_PLUGINS_H |
|||
#include <Python.h> |
|||
#include <vector> |
|||
#include <class_action_plugin.h> |
|||
|
|||
|
|||
class PYTHON_ACTION_PLUGIN : public ACTION_PLUGIN |
|||
{ |
|||
PyObject* m_PyAction; |
|||
PyObject* CallMethod( const char* aMethod, |
|||
PyObject* aArglist = NULL ); |
|||
wxString CallRetStrMethod( const char* aMethod, |
|||
PyObject* aArglist = NULL ); |
|||
|
|||
public: |
|||
PYTHON_ACTION_PLUGIN( PyObject* action ); |
|||
~PYTHON_ACTION_PLUGIN(); |
|||
wxString GetCategoryName() override; |
|||
wxString GetName() override; |
|||
wxString GetDescription() override; |
|||
void Run() override; |
|||
void* GetObject() override; |
|||
}; |
|||
|
|||
|
|||
class PYTHON_ACTION_PLUGINS |
|||
{ |
|||
public: |
|||
static void register_action( PyObject* aPyAction ); |
|||
static void deregister_action( PyObject* aPyAction ); |
|||
}; |
|||
|
|||
#endif /* PCBNEW_ACTION_PLUGINS_H */ |
Write
Preview
Loading…
Cancel
Save
Reference in new issue