From c458b50fd99621f116e3eb7eb312a4c5ccb8d754 Mon Sep 17 00:00:00 2001 From: Ian McInerney Date: Sun, 9 Aug 2020 19:49:26 +0100 Subject: [PATCH] Ensure the environment variables are synchronized between KiCad and Python Before this, the environment variables inside Python wouldn't reflect the updates to them made after the interpreter was started in Pcbnew. This will call into Python and set the variables when they are changed, since Python can't synchronize itself when running in an embedded interpreter. Fixes https://gitlab.com/kicad/code/kicad/issues/5071 --- common/kiway.cpp | 26 ++++++++++++++++++-- common/settings/settings_manager.cpp | 11 +++++++++ common/single_top.cpp | 2 ++ include/eda_base_frame.h | 5 ++++ include/kiway.h | 6 +++++ include/settings/settings_manager.h | 11 +++++++++ kicad/kicad.cpp | 1 + pcbnew/pcb_edit_frame.cpp | 36 ++++++++++++++++++++++++++++ pcbnew/pcb_edit_frame.h | 14 +++++++++++ pcbnew/swig/python_scripting.cpp | 27 ++++++++++++++++++++- pcbnew/swig/python_scripting.h | 9 +++++++ 11 files changed, 145 insertions(+), 3 deletions(-) diff --git a/common/kiway.cpp b/common/kiway.cpp index e6cbbbdce8..d936bfec77 100644 --- a/common/kiway.cpp +++ b/common/kiway.cpp @@ -473,9 +473,9 @@ void KIWAY::SetLanguage( int aLanguage ) } } + void KIWAY::CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVarsChanged ) { -#if 1 if( m_ctl & KFCTL_CPP_PROJECT_SUITE ) { // A dynamic_cast could be better, but creates link issues @@ -486,7 +486,6 @@ void KIWAY::CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVarsChanged ) if( top ) top->CommonSettingsChanged( aEnvVarsChanged, aTextVarsChanged ); } -#endif for( unsigned i=0; i < KIWAY_PLAYER_COUNT; ++i ) { @@ -498,6 +497,29 @@ void KIWAY::CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVarsChanged ) } +void KIWAY::ProjectChanged() +{ + if( m_ctl & KFCTL_CPP_PROJECT_SUITE ) + { + // A dynamic_cast could be better, but creates link issues + // (some basic_frame functions not found) on some platforms, + // so a static_cast is used. + EDA_BASE_FRAME* top = static_cast( m_top ); + + if( top ) + top->ProjectChanged(); + } + + for( unsigned i=0; i < KIWAY_PLAYER_COUNT; ++i ) + { + KIWAY_PLAYER* frame = GetPlayerFrame( ( FRAME_T )i ); + + if( frame ) + frame->ProjectChanged(); + } +} + + bool KIWAY::ProcessEvent( wxEvent& aEvent ) { KIWAY_EXPRESS* mail = dynamic_cast( &aEvent ); diff --git a/common/settings/settings_manager.cpp b/common/settings/settings_manager.cpp index 780bfe063d..f6c85075aa 100644 --- a/common/settings/settings_manager.cpp +++ b/common/settings/settings_manager.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -53,6 +54,7 @@ const char* traceSettings = "SETTINGS"; SETTINGS_MANAGER::SETTINGS_MANAGER( bool aHeadless ) : m_headless( aHeadless ), + m_kiway( nullptr ), m_common_settings( nullptr ), m_migration_source() { @@ -715,6 +717,9 @@ bool SETTINGS_MANAGER::LoadProject( const wxString& aFullPath, bool aSetActive ) m_projects[fullPath]->setLocalSettings( settings ); settings->SetProject( m_projects[fullPath].get() ); + if( m_kiway ) + m_kiway->ProjectChanged(); + return success; } @@ -731,6 +736,12 @@ bool SETTINGS_MANAGER::UnloadProject( PROJECT* aProject, bool aSave ) m_projects.erase( aProject->GetProjectFullName() ); + // Remove the reference in the environment to the previous project + wxSetEnv( PROJECT_VAR_NAME, "" ); + + if( m_kiway ) + m_kiway->ProjectChanged(); + return true; } diff --git a/common/single_top.cpp b/common/single_top.cpp index 3293d13c53..e1e4d7fe16 100644 --- a/common/single_top.cpp +++ b/common/single_top.cpp @@ -359,6 +359,8 @@ bool PGM_SINGLE_TOP::OnPgmInit() } } + // Tell the settings manager about the current Kiway + GetSettingsManager().SetKiway( &Kiway ); // Use KIWAY to create a top window, which registers its existence also. // "TOP_FRAME" is a macro that is passed on compiler command line from CMake, diff --git a/include/eda_base_frame.h b/include/eda_base_frame.h index 8233056491..cc30128951 100644 --- a/include/eda_base_frame.h +++ b/include/eda_base_frame.h @@ -496,6 +496,11 @@ public: */ void CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVarsChanged ) override; + /** + * Notification event that the project has changed. + */ + virtual void ProjectChanged() {} + const wxString& GetAboutTitle() const { return m_AboutTitle; } /** diff --git a/include/kiway.h b/include/kiway.h index 57bf52fa60..c26436cbf2 100644 --- a/include/kiway.h +++ b/include/kiway.h @@ -382,6 +382,12 @@ public: */ VTBL_ENTRY void CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVarsChanged ); + /** + * Calls ProjectChanged() on all KIWAY_PLAYERs. + * Used after changing the project to ensure all players are updated correctly. + */ + VTBL_ENTRY void ProjectChanged(); + KIWAY( PGM_BASE* aProgram, int aCtlBits, wxFrame* aTop = NULL ); /** diff --git a/include/settings/settings_manager.h b/include/settings/settings_manager.h index 0d90956da6..ad4fc5026a 100644 --- a/include/settings/settings_manager.h +++ b/include/settings/settings_manager.h @@ -26,6 +26,7 @@ class COLOR_SETTINGS; class COMMON_SETTINGS; +class KIWAY; class PROJECT; class PROJECT_FILE; class REPORTER; @@ -43,6 +44,13 @@ public: */ bool IsOK() { return m_ok; } + /** + * Associate this setting manager with the given Kiway. + * + * @param aKiway is the kiway this settings manager should use + */ + void SetKiway( KIWAY* aKiway ) { m_kiway = aKiway; } + /** * Takes ownership of the pointer passed in * @param aSettings is a settings object to register @@ -350,6 +358,9 @@ private: /// True if running outside a UI context bool m_headless; + /// The kiway this settings manager interacts with + KIWAY* m_kiway; + std::vector> m_settings; std::unordered_map m_color_settings; diff --git a/kicad/kicad.cpp b/kicad/kicad.cpp index 37bcaf4d66..e64641b640 100644 --- a/kicad/kicad.cpp +++ b/kicad/kicad.cpp @@ -92,6 +92,7 @@ bool PGM_KICAD::OnPgmInit() m_bm.InitSettings( new KICAD_SETTINGS ); GetSettingsManager().RegisterSettings( PgmSettings() ); + GetSettingsManager().SetKiway( &Kiway ); m_bm.Init(); // Add search paths to feed the PGM_KICAD::SysSearch() function, diff --git a/pcbnew/pcb_edit_frame.cpp b/pcbnew/pcb_edit_frame.cpp index c6821ccaa7..b32d4d39ec 100644 --- a/pcbnew/pcb_edit_frame.cpp +++ b/pcbnew/pcb_edit_frame.cpp @@ -51,6 +51,7 @@ #include #include #include +#include #include #include #include @@ -323,6 +324,10 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) : InitExitKey(); + // Ensure the Python interpreter is up to date with its environment variables + PythonSyncEnvironmentVariables(); + PythonSyncProjectName(); + GetCanvas()->SwitchBackend( m_canvasType ); ActivateGalCanvas(); @@ -1265,6 +1270,27 @@ void PCB_EDIT_FRAME::PythonPluginsShowFolder() } +void PCB_EDIT_FRAME::PythonSyncEnvironmentVariables() +{ +#if defined( KICAD_SCRIPTING ) + COMMON_SETTINGS* settings = Pgm().GetCommonSettings(); + + for( auto& var : settings->m_Env.vars ) + pcbnewUpdatePythonEnvVar( var.first, var.second ); +#endif +} + + +void PCB_EDIT_FRAME::PythonSyncProjectName() +{ +#if defined( KICAD_SCRIPTING ) + wxString evValue; + wxGetEnv( PROJECT_VAR_NAME, &evValue ); + pcbnewUpdatePythonEnvVar( wxString( PROJECT_VAR_NAME ).ToStdString(), evValue ); +#endif +} + + void PCB_EDIT_FRAME::InstallFootprintPropertiesDialog( MODULE* Module ) { if( Module == NULL ) @@ -1338,11 +1364,21 @@ void PCB_EDIT_FRAME::CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVars if( aTextVarsChanged ) GetCanvas()->GetView()->UpdateAllItems( KIGFX::ALL ); + // Update the environment variables in the Python interpreter + if( aEnvVarsChanged ) + PythonSyncEnvironmentVariables(); + Layout(); SendSizeEvent(); } +void PCB_EDIT_FRAME::ProjectChanged() +{ + PythonSyncProjectName(); +} + + void PCB_EDIT_FRAME::LockModule( MODULE* aModule, bool aLocked ) { const wxString ModulesMaskSelection = wxT( "*" ); diff --git a/pcbnew/pcb_edit_frame.h b/pcbnew/pcb_edit_frame.h index f7aa2db494..5d22bd0f5e 100644 --- a/pcbnew/pcb_edit_frame.h +++ b/pcbnew/pcb_edit_frame.h @@ -304,6 +304,18 @@ public: */ void PythonPluginsShowFolder(); + /** + * Synchronize the environment variables from KiCad's environment into the Python interpreter. + * Do nothing if KICAD_SCRIPTING is not defined. + */ + void PythonSyncEnvironmentVariables(); + + /** + * Synchronize the project name from KiCad's environment into the Python interpreter. + * Do nothing if KICAD_SCRIPTING is not defined. + */ + void PythonSyncProjectName(); + /** * Update the layer manager and other widgets from the board setup * (layer and items visibility, colors ...) @@ -948,6 +960,8 @@ public: */ void CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVarsChanged ) override; + void ProjectChanged() override; + void SyncToolbars() override; wxString GetCurrentFileName() const override; diff --git a/pcbnew/swig/python_scripting.cpp b/pcbnew/swig/python_scripting.cpp index aef4076e3a..4f53afa1fc 100644 --- a/pcbnew/swig/python_scripting.cpp +++ b/pcbnew/swig/python_scripting.cpp @@ -210,7 +210,8 @@ bool pcbnewInitPythonScripting( const char * aUserScriptingPath ) { PyLOCK lock; - snprintf( cmd, sizeof( cmd ), "import sys, traceback\n" + // Load os so that we can modify the environment variables through python + snprintf( cmd, sizeof( cmd ), "import sys, os, traceback\n" "sys.path.append(\".\")\n" "import pcbnew\n" "pcbnew.LoadPlugins(\"%s\")", aUserScriptingPath ); @@ -318,6 +319,30 @@ void pcbnewFinishPythonScripting() } +void pcbnewUpdatePythonEnvVar( const std::string& aVar, const wxString& aValue ) +{ + char cmd[1024]; + + // Ensure the interpreter is initalized before we try to interact with it + if( !Py_IsInitialized() ) + return; + + snprintf( cmd, sizeof( cmd ), + "# coding=utf-8\n" // The values could potentially be UTF8 + "os.environ[\"%s\"]=\"%s\"\n", + aVar.c_str(), + TO_UTF8( aValue ) ); + + PyLOCK lock; + + int retv = PyRun_SimpleString( cmd ); + + if( retv != 0 ) + wxLogError( "Python error %d occurred running command:\n\n`%s`", retv, cmd ); +} + + + #if defined( KICAD_SCRIPTING_WXPYTHON ) void RedirectStdio() { diff --git a/pcbnew/swig/python_scripting.h b/pcbnew/swig/python_scripting.h index 369d50c8d4..c577734a30 100644 --- a/pcbnew/swig/python_scripting.h +++ b/pcbnew/swig/python_scripting.h @@ -78,6 +78,15 @@ void pcbnewGetScriptsSearchPaths( wxString& aNames ); */ void pcbnewGetWizardsBackTrace( wxString& aNames ); +/** + * Set an environment variable in the current Python interpreter. + * + * @param aVar is the variable to set + * @param aValue is the value to give it + */ +void pcbnewUpdatePythonEnvVar( const std::string& aVar, const wxString& aValue ); + + #ifdef KICAD_SCRIPTING_WXPYTHON void RedirectStdio(); wxWindow* CreatePythonShellWindow( wxWindow* parent, const wxString& aFramenameId );