diff --git a/pcbnew/python/scripting/pcb_scripting_tool.cpp b/pcbnew/python/scripting/pcb_scripting_tool.cpp index 37faf7e691..2146347ab4 100644 --- a/pcbnew/python/scripting/pcb_scripting_tool.cpp +++ b/pcbnew/python/scripting/pcb_scripting_tool.cpp @@ -69,20 +69,7 @@ bool SCRIPTING_TOOL::Init() Py_DECREF( mod ); } - // Load pcbnew inside Python and load all the user plugins and package-based plugins - { - using namespace pybind11::literals; - - auto locals = pybind11::dict( "sys_path"_a = TO_UTF8( SCRIPTING::PyScriptingPath( false ) ), - "user_path"_a = TO_UTF8( SCRIPTING::PyScriptingPath( true ) ) ); - - pybind11::exec( R"( -import sys -import pcbnew -pcbnew.LoadPlugins( sys_path, user_path ) - )", pybind11::globals(), locals ); - - } + callLoadPlugins(); return true; } @@ -97,18 +84,7 @@ int SCRIPTING_TOOL::reloadPlugins( const TOOL_EVENT& aEvent ) { PyLOCK lock; - std::string sys_path = SCRIPTING::PyScriptingPath( false ).ToStdString(); - std::string user_path = SCRIPTING::PyScriptingPath( true ).ToStdString(); - - using namespace pybind11::literals; - auto locals = pybind11::dict( "sys_path"_a = sys_path, - "user_path"_a = user_path ); - - pybind11::exec( R"( -import sys -import pcbnew -pcbnew.LoadPlugins( sys_path, user_path ) - )", pybind11::globals(), locals ); + callLoadPlugins(); } if( !m_isFootprintEditor ) @@ -123,9 +99,29 @@ pcbnew.LoadPlugins( sys_path, user_path ) } +void SCRIPTING_TOOL::callLoadPlugins() +{ + // Load pcbnew inside Python and load all the user plugins and package-based plugins + using namespace pybind11::literals; + + auto locals = pybind11::dict( "sys_path"_a = TO_UTF8( SCRIPTING::PyScriptingPath( + SCRIPTING::PATH_TYPE::STOCK ) ), + "user_path"_a = TO_UTF8( SCRIPTING::PyScriptingPath( + SCRIPTING::PATH_TYPE::USER ) ), + "third_party_path"_a = TO_UTF8( SCRIPTING::PyPluginsPath( + SCRIPTING::PATH_TYPE::THIRDPARTY ) ) ); + + pybind11::exec( R"( +import sys +import pcbnew +pcbnew.LoadPlugins( sys_path, user_path, third_party_path ) + )", pybind11::globals(), locals ); +} + + int SCRIPTING_TOOL::showPluginFolder( const TOOL_EVENT& aEvent ) { - wxString pluginpath( SCRIPTING::PyPluginsPath( true ) ); + wxString pluginpath( SCRIPTING::PyPluginsPath( SCRIPTING::PATH_TYPE::USER ) ); LaunchExternal( pluginpath ); return 0; diff --git a/pcbnew/python/scripting/pcb_scripting_tool.h b/pcbnew/python/scripting/pcb_scripting_tool.h index a0d1775f51..fd7de52c1d 100644 --- a/pcbnew/python/scripting/pcb_scripting_tool.h +++ b/pcbnew/python/scripting/pcb_scripting_tool.h @@ -49,6 +49,10 @@ private: ///< Reload Python plugins and reset toolbar (if in pcbnew) int reloadPlugins( const TOOL_EVENT& aEvent ); + ///< Call LoadPlugins method of the scripting module with apropriate paths + ///< Must be called under PyLOCK + void callLoadPlugins(); + ///< Open the user's plugin folder in the system browser int showPluginFolder( const TOOL_EVENT& aEvent ); diff --git a/scripting/kicadplugins.i b/scripting/kicadplugins.i index d93fc34ca6..0ec1e7b414 100644 --- a/scripting/kicadplugins.i +++ b/scripting/kicadplugins.i @@ -165,7 +165,7 @@ def LoadPluginModule(Dirname, ModuleName, FileName): FULL_BACK_TRACE += traceback.format_exc() -def LoadPlugins(bundlepath=None, userpath=None): +def LoadPlugins(bundlepath=None, userpath=None, thirdpartypath=None): """ Initialise Scripting/Plugin python environment and load plugins. @@ -192,6 +192,8 @@ def LoadPlugins(bundlepath=None, userpath=None): / /plugins/ + The plugins from 3rd party packages: + $KICAD_3RD_PARTY/plugins/ """ import os import sys @@ -207,8 +209,9 @@ def LoadPlugins(bundlepath=None, userpath=None): So convert these utf8 encoding to unicode strings to avoid any encoding issue. """ try: - bundlepath = bundlepath.decode( 'UTF-8' ); - userpath = userpath.decode( 'UTF-8' ); + bundlepath = bundlepath.decode( 'UTF-8' ) + userpath = userpath.decode( 'UTF-8' ) + thirdpartypath = thirdpartypath.decode( 'UTF-8' ) except AttributeError: pass @@ -220,7 +223,10 @@ def LoadPlugins(bundlepath=None, userpath=None): to the windows separator, although using '/' works """ if sys.platform.startswith('win32'): - bundlepath = bundlepath.replace("/","\\") + if bundlepath: + bundlepath = bundlepath.replace("/","\\") + if thirdpartypath: + thirdpartypath = thirdpartypath.replace("/","\\") if bundlepath: plugin_directories.append(bundlepath) @@ -234,6 +240,9 @@ def LoadPlugins(bundlepath=None, userpath=None): plugin_directories.append(userpath) plugin_directories.append(os.path.join(userpath, 'plugins')) + if thirdpartypath: + plugin_directories.append(thirdpartypath) + global PLUGIN_DIRECTORIES_SEARCH PLUGIN_DIRECTORIES_SEARCH="" for plugins_dir in plugin_directories: # save search path list for later use diff --git a/scripting/kipython_frame.cpp b/scripting/kipython_frame.cpp index ca55df8720..acc1b89add 100644 --- a/scripting/kipython_frame.cpp +++ b/scripting/kipython_frame.cpp @@ -38,7 +38,7 @@ void KIPYTHON_FRAME::SetupPythonEditor() PyLOCK lock; // Make sure the kicad_pyshell module is in the path - wxString sysPath = SCRIPTING::PyScriptingPath( false ); + wxString sysPath = SCRIPTING::PyScriptingPath( SCRIPTING::PATH_TYPE::STOCK ); auto locals = pybind11::dict( "stock_path"_a = sysPath.ToStdString() ); pybind11::exec( R"( diff --git a/scripting/python_scripting.cpp b/scripting/python_scripting.cpp index 55dccd0c2f..cd450d0ec6 100644 --- a/scripting/python_scripting.cpp +++ b/scripting/python_scripting.cpp @@ -209,7 +209,7 @@ bool SCRIPTING::scriptingSetup() #endif - wxFileName path( PyPluginsPath( true ) + wxT( "/" ) ); + wxFileName path( PyPluginsPath( SCRIPTING::PATH_TYPE::USER ) + wxT( "/" ) ); // Ensure the user plugin path exists, and create it if not. // However, if it cannot be created, this is not a fatal error. @@ -452,18 +452,25 @@ wxString PyErrStringWithTraceback() /** * Find the Python scripting path. */ -wxString SCRIPTING::PyScriptingPath( bool aUserPath ) +wxString SCRIPTING::PyScriptingPath( PATH_TYPE aPathType ) { wxString path; //@todo This should this be a user configurable variable eg KISCRIPT? - if( aUserPath ) + switch( aPathType ) { - path = PATHS::GetUserScriptingPath(); - } - else - { - path = PATHS::GetStockScriptingPath(); + case STOCK: path = PATHS::GetStockScriptingPath(); break; + case USER: path = PATHS::GetUserScriptingPath(); break; + case THIRDPARTY: + const ENV_VAR_MAP& env = Pgm().GetLocalEnvVariables(); + auto it = env.find( "KICAD6_3RD_PARTY" ); + + if( it != env.end() && !it->second.GetValue().IsEmpty() ) + path = it->second.GetValue(); + else + path = PATHS::GetDefault3rdPartyPath(); + + break; } wxFileName scriptPath( path ); @@ -479,9 +486,9 @@ wxString SCRIPTING::PyScriptingPath( bool aUserPath ) } -wxString SCRIPTING::PyPluginsPath( bool aUserPath ) +wxString SCRIPTING::PyPluginsPath( PATH_TYPE aPathType ) { // Note we are using unix path separator, because window separator sometimes // creates issues when passing a command string to a python method by PyRun_SimpleString - return PyScriptingPath( aUserPath ) + '/' + "plugins"; + return PyScriptingPath( aPathType ) + '/' + "plugins"; } diff --git a/scripting/python_scripting.h b/scripting/python_scripting.h index 689b929cf8..a6df3c32a9 100644 --- a/scripting/python_scripting.h +++ b/scripting/python_scripting.h @@ -59,8 +59,15 @@ public: static bool IsModuleLoaded( std::string& aModule ); - static wxString PyScriptingPath( bool aUserPath = false ); - static wxString PyPluginsPath( bool aUserPath = false ); + enum PATH_TYPE + { + STOCK, + USER, + THIRDPARTY + }; + + static wxString PyScriptingPath( PATH_TYPE aPathType = STOCK ); + static wxString PyPluginsPath( PATH_TYPE aPathType = STOCK ); private: