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
This commit is contained in:
Ian McInerney 2020-08-09 19:49:26 +01:00
parent e57499fde9
commit c458b50fd9
11 changed files with 145 additions and 3 deletions

View File

@ -473,9 +473,9 @@ void KIWAY::SetLanguage( int aLanguage )
} }
} }
void KIWAY::CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVarsChanged ) void KIWAY::CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVarsChanged )
{ {
#if 1
if( m_ctl & KFCTL_CPP_PROJECT_SUITE ) if( m_ctl & KFCTL_CPP_PROJECT_SUITE )
{ {
// A dynamic_cast could be better, but creates link issues // A dynamic_cast could be better, but creates link issues
@ -486,7 +486,6 @@ void KIWAY::CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVarsChanged )
if( top ) if( top )
top->CommonSettingsChanged( aEnvVarsChanged, aTextVarsChanged ); top->CommonSettingsChanged( aEnvVarsChanged, aTextVarsChanged );
} }
#endif
for( unsigned i=0; i < KIWAY_PLAYER_COUNT; ++i ) 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<EDA_BASE_FRAME*>( 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 ) bool KIWAY::ProcessEvent( wxEvent& aEvent )
{ {
KIWAY_EXPRESS* mail = dynamic_cast<KIWAY_EXPRESS*>( &aEvent ); KIWAY_EXPRESS* mail = dynamic_cast<KIWAY_EXPRESS*>( &aEvent );

View File

@ -28,6 +28,7 @@
#include <confirm.h> #include <confirm.h>
#include <dialogs/dialog_migrate_settings.h> #include <dialogs/dialog_migrate_settings.h>
#include <gestfich.h> #include <gestfich.h>
#include <kiway.h>
#include <macros.h> #include <macros.h>
#include <project.h> #include <project.h>
#include <project/project_archiver.h> #include <project/project_archiver.h>
@ -53,6 +54,7 @@ const char* traceSettings = "SETTINGS";
SETTINGS_MANAGER::SETTINGS_MANAGER( bool aHeadless ) : SETTINGS_MANAGER::SETTINGS_MANAGER( bool aHeadless ) :
m_headless( aHeadless ), m_headless( aHeadless ),
m_kiway( nullptr ),
m_common_settings( nullptr ), m_common_settings( nullptr ),
m_migration_source() m_migration_source()
{ {
@ -715,6 +717,9 @@ bool SETTINGS_MANAGER::LoadProject( const wxString& aFullPath, bool aSetActive )
m_projects[fullPath]->setLocalSettings( settings ); m_projects[fullPath]->setLocalSettings( settings );
settings->SetProject( m_projects[fullPath].get() ); settings->SetProject( m_projects[fullPath].get() );
if( m_kiway )
m_kiway->ProjectChanged();
return success; return success;
} }
@ -731,6 +736,12 @@ bool SETTINGS_MANAGER::UnloadProject( PROJECT* aProject, bool aSave )
m_projects.erase( aProject->GetProjectFullName() ); 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; return true;
} }

View File

@ -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. // 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, // "TOP_FRAME" is a macro that is passed on compiler command line from CMake,

View File

@ -496,6 +496,11 @@ public:
*/ */
void CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVarsChanged ) override; void CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVarsChanged ) override;
/**
* Notification event that the project has changed.
*/
virtual void ProjectChanged() {}
const wxString& GetAboutTitle() const { return m_AboutTitle; } const wxString& GetAboutTitle() const { return m_AboutTitle; }
/** /**

View File

@ -382,6 +382,12 @@ public:
*/ */
VTBL_ENTRY void CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVarsChanged ); 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 ); KIWAY( PGM_BASE* aProgram, int aCtlBits, wxFrame* aTop = NULL );
/** /**

View File

@ -26,6 +26,7 @@
class COLOR_SETTINGS; class COLOR_SETTINGS;
class COMMON_SETTINGS; class COMMON_SETTINGS;
class KIWAY;
class PROJECT; class PROJECT;
class PROJECT_FILE; class PROJECT_FILE;
class REPORTER; class REPORTER;
@ -43,6 +44,13 @@ public:
*/ */
bool IsOK() { return m_ok; } 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 * Takes ownership of the pointer passed in
* @param aSettings is a settings object to register * @param aSettings is a settings object to register
@ -350,6 +358,9 @@ private:
/// True if running outside a UI context /// True if running outside a UI context
bool m_headless; bool m_headless;
/// The kiway this settings manager interacts with
KIWAY* m_kiway;
std::vector<std::unique_ptr<JSON_SETTINGS>> m_settings; std::vector<std::unique_ptr<JSON_SETTINGS>> m_settings;
std::unordered_map<wxString, COLOR_SETTINGS*> m_color_settings; std::unordered_map<wxString, COLOR_SETTINGS*> m_color_settings;

View File

@ -92,6 +92,7 @@ bool PGM_KICAD::OnPgmInit()
m_bm.InitSettings( new KICAD_SETTINGS ); m_bm.InitSettings( new KICAD_SETTINGS );
GetSettingsManager().RegisterSettings( PgmSettings() ); GetSettingsManager().RegisterSettings( PgmSettings() );
GetSettingsManager().SetKiway( &Kiway );
m_bm.Init(); m_bm.Init();
// Add search paths to feed the PGM_KICAD::SysSearch() function, // Add search paths to feed the PGM_KICAD::SysSearch() function,

View File

@ -51,6 +51,7 @@
#include <functional> #include <functional>
#include <project/project_file.h> #include <project/project_file.h>
#include <project/net_settings.h> #include <project/net_settings.h>
#include <settings/common_settings.h>
#include <settings/settings_manager.h> #include <settings/settings_manager.h>
#include <swig/python_scripting.h> #include <swig/python_scripting.h>
#include <tool/tool_manager.h> #include <tool/tool_manager.h>
@ -323,6 +324,10 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
InitExitKey(); InitExitKey();
// Ensure the Python interpreter is up to date with its environment variables
PythonSyncEnvironmentVariables();
PythonSyncProjectName();
GetCanvas()->SwitchBackend( m_canvasType ); GetCanvas()->SwitchBackend( m_canvasType );
ActivateGalCanvas(); 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 ) void PCB_EDIT_FRAME::InstallFootprintPropertiesDialog( MODULE* Module )
{ {
if( Module == NULL ) if( Module == NULL )
@ -1338,11 +1364,21 @@ void PCB_EDIT_FRAME::CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVars
if( aTextVarsChanged ) if( aTextVarsChanged )
GetCanvas()->GetView()->UpdateAllItems( KIGFX::ALL ); GetCanvas()->GetView()->UpdateAllItems( KIGFX::ALL );
// Update the environment variables in the Python interpreter
if( aEnvVarsChanged )
PythonSyncEnvironmentVariables();
Layout(); Layout();
SendSizeEvent(); SendSizeEvent();
} }
void PCB_EDIT_FRAME::ProjectChanged()
{
PythonSyncProjectName();
}
void PCB_EDIT_FRAME::LockModule( MODULE* aModule, bool aLocked ) void PCB_EDIT_FRAME::LockModule( MODULE* aModule, bool aLocked )
{ {
const wxString ModulesMaskSelection = wxT( "*" ); const wxString ModulesMaskSelection = wxT( "*" );

View File

@ -304,6 +304,18 @@ public:
*/ */
void PythonPluginsShowFolder(); 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 * Update the layer manager and other widgets from the board setup
* (layer and items visibility, colors ...) * (layer and items visibility, colors ...)
@ -948,6 +960,8 @@ public:
*/ */
void CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVarsChanged ) override; void CommonSettingsChanged( bool aEnvVarsChanged, bool aTextVarsChanged ) override;
void ProjectChanged() override;
void SyncToolbars() override; void SyncToolbars() override;
wxString GetCurrentFileName() const override; wxString GetCurrentFileName() const override;

View File

@ -210,7 +210,8 @@ bool pcbnewInitPythonScripting( const char * aUserScriptingPath )
{ {
PyLOCK lock; 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" "sys.path.append(\".\")\n"
"import pcbnew\n" "import pcbnew\n"
"pcbnew.LoadPlugins(\"%s\")", aUserScriptingPath ); "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 ) #if defined( KICAD_SCRIPTING_WXPYTHON )
void RedirectStdio() void RedirectStdio()
{ {

View File

@ -78,6 +78,15 @@ void pcbnewGetScriptsSearchPaths( wxString& aNames );
*/ */
void pcbnewGetWizardsBackTrace( 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 #ifdef KICAD_SCRIPTING_WXPYTHON
void RedirectStdio(); void RedirectStdio();
wxWindow* CreatePythonShellWindow( wxWindow* parent, const wxString& aFramenameId ); wxWindow* CreatePythonShellWindow( wxWindow* parent, const wxString& aFramenameId );