You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

171 lines
4.6 KiB

  1. /*
  2. * This program source code file is part of KiCad, a free EDA CAD application.
  3. *
  4. * Copyright (C) 2021 KiCad Developers, see AUTHORS.TXT for contributors.
  5. *
  6. * This program is free software; you can redistribute it and/or
  7. * modify it under the terms of the GNU General Public License
  8. * as published by the Free Software Foundation; either version 3
  9. * of the License, or (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, you may find one here:
  18. * http://www.gnu.org/licenses/gpl-3.0.html
  19. * or you may search the http://www.gnu.org website for the version 3 license,
  20. * or you may write to the Free Software Foundation, Inc.,
  21. * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
  22. */
  23. #include "pcb_scripting_tool.h"
  24. #include <action_plugin.h>
  25. #include <kiface_ids.h>
  26. #include <kiway.h>
  27. #include <macros.h>
  28. #include <pgm_base.h>
  29. #include <python_scripting.h>
  30. #include <tools/pcb_actions.h>
  31. #include <pybind11/eval.h>
  32. #include <Python.h>
  33. #include <wx/string.h>
  34. #include <launch_ext.h>
  35. #ifdef KICAD_IPC_API
  36. #include <api/api_plugin_manager.h>
  37. #endif
  38. using initfunc = PyObject* (*)(void);
  39. SCRIPTING_TOOL::SCRIPTING_TOOL() :
  40. PCB_TOOL_BASE( "pcbnew.ScriptingTool" )
  41. {}
  42. SCRIPTING_TOOL::~SCRIPTING_TOOL()
  43. {}
  44. void SCRIPTING_TOOL::Reset( RESET_REASON aReason )
  45. {
  46. }
  47. bool SCRIPTING_TOOL::Init()
  48. {
  49. PyLOCK lock;
  50. std::string pymodule( "_pcbnew" );
  51. if( !SCRIPTING::IsModuleLoaded( pymodule ) )
  52. {
  53. KIFACE* kiface = frame()->Kiway().KiFACE( KIWAY::FACE_PCB );
  54. initfunc pcbnew_init = reinterpret_cast<initfunc>( kiface->IfaceOrAddress( KIFACE_SCRIPTING_LEGACY ) );
  55. PyImport_AddModule( pymodule.c_str() );
  56. PyObject* mod = pcbnew_init();
  57. PyObject* sys_mod = PyImport_GetModuleDict();
  58. PyDict_SetItemString( sys_mod, "_pcbnew", mod );
  59. Py_DECREF( mod );
  60. // plugins will be loaded later via ReloadPlugins()
  61. }
  62. return true;
  63. }
  64. void SCRIPTING_TOOL::ReloadPlugins()
  65. {
  66. // Reload Python plugins if they are newer than the already loaded, and load new plugins
  67. // Remove all action plugins so that we don't keep references to old versions
  68. ACTION_PLUGINS::UnloadAll();
  69. try
  70. {
  71. PyLOCK lock;
  72. callLoadPlugins();
  73. }
  74. catch( ... )
  75. {}
  76. }
  77. int SCRIPTING_TOOL::reloadPlugins( const TOOL_EVENT& aEvent )
  78. {
  79. // Reload Python plugins if they are newer than the already loaded, and load new plugins
  80. // Remove all action plugins so that we don't keep references to old versions
  81. ACTION_PLUGINS::UnloadAll();
  82. try
  83. {
  84. PyLOCK lock;
  85. callLoadPlugins();
  86. }
  87. catch( ... )
  88. {
  89. return -1;
  90. }
  91. #ifdef KICAD_IPC_API
  92. // TODO move this elsewhere when SWIG plugins are removed
  93. Pgm().GetPluginManager().ReloadPlugins();
  94. #endif
  95. if( !m_isFootprintEditor )
  96. {
  97. // Action plugins can be modified, therefore the plugins menu must be updated:
  98. frame()->ReCreateMenuBar();
  99. // Recreate top toolbar to add action plugin buttons
  100. frame()->ReCreateHToolbar();
  101. // Post a size event to force resizing toolbar by the AUI manager:
  102. frame()->PostSizeEvent();
  103. }
  104. return 0;
  105. }
  106. void SCRIPTING_TOOL::callLoadPlugins()
  107. {
  108. // Load pcbnew inside Python and load all the user plugins and package-based plugins
  109. using namespace pybind11::literals;
  110. auto locals = pybind11::dict(
  111. "sys_path"_a = TO_UTF8( SCRIPTING::PyScriptingPath( SCRIPTING::PATH_TYPE::STOCK ) ),
  112. "user_path"_a = TO_UTF8( SCRIPTING::PyScriptingPath( SCRIPTING::PATH_TYPE::USER ) ),
  113. "third_party_path"_a =
  114. TO_UTF8( SCRIPTING::PyPluginsPath( SCRIPTING::PATH_TYPE::THIRDPARTY ) ) );
  115. pybind11::exec( R"(
  116. import sys
  117. import pcbnew
  118. pcbnew.LoadPlugins( sys_path, user_path, third_party_path )
  119. )",
  120. pybind11::globals(), locals );
  121. }
  122. void SCRIPTING_TOOL::ShowPluginFolder()
  123. {
  124. wxString pluginpath( SCRIPTING::PyPluginsPath( SCRIPTING::PATH_TYPE::USER ) );
  125. LaunchExternal( pluginpath );
  126. }
  127. int SCRIPTING_TOOL::showPluginFolder( const TOOL_EVENT& aEvent )
  128. {
  129. ShowPluginFolder();
  130. return 0;
  131. }
  132. void SCRIPTING_TOOL::setTransitions()
  133. {
  134. Go( &SCRIPTING_TOOL::reloadPlugins, ACTIONS::pluginsReload.MakeEvent() );
  135. Go( &SCRIPTING_TOOL::showPluginFolder, PCB_ACTIONS::pluginsShowFolder.MakeEvent() );
  136. }