Finalize Python Frame

This commit is contained in:
Seth Hillbrand 2021-03-15 14:56:13 -07:00
parent 256f259627
commit 192fbfd5e5
21 changed files with 1337 additions and 257 deletions

View File

@ -761,6 +761,8 @@ include( ${SWIG_USE_FILE} )
# pybind11 is header-only, so include the subdir
add_subdirectory(thirdparty/pybind11)
include_directories( SYSTEM ${PYBIND11_INCLUDE_DIR} )
set( PythonInterp_FIND_VERSION 3.6 )
set( PythonLibs_FIND_VERSION 3.6 )

View File

@ -19,7 +19,6 @@ include_directories(
${INC_AFTER}
)
if( NOT APPLE ) # windows and linux use openssl under curl
find_package( OpenSSL REQUIRED )
include_directories( SYSTEM ${OPENSSL_INCLUDE_DIR} )
@ -484,6 +483,7 @@ target_link_libraries( common
kimath
kiplatform
gal
pybind11::embed
${Boost_LIBRARIES}
${CURL_LIBRARIES}
${OPENSSL_LIBRARIES} # empty on Apple

View File

@ -102,28 +102,24 @@ BEGIN_EVENT_TABLE( EDA_BASE_FRAME, wxFrame )
EVT_SYS_COLOUR_CHANGED( EDA_BASE_FRAME::onSystemColorChange )
END_EVENT_TABLE()
EDA_BASE_FRAME::EDA_BASE_FRAME( wxWindow* aParent, FRAME_T aFrameType,
const wxString& aTitle, const wxPoint& aPos, const wxSize& aSize,
long aStyle, const wxString& aFrameName, KIWAY* aKiway ) :
wxFrame( aParent, wxID_ANY, aTitle, aPos, aSize, aStyle, aFrameName ),
TOOLS_HOLDER(),
KIWAY_HOLDER( aKiway, KIWAY_HOLDER::FRAME ),
m_ident( aFrameType ),
m_maximizeByDefault( false ),
m_infoBar( nullptr ),
m_settingsManager( nullptr ),
m_fileHistory( nullptr ),
m_hasAutoSave( false ),
m_autoSaveState( false ),
m_autoSaveInterval(-1 ),
m_undoRedoCountMax( DEFAULT_MAX_UNDO_ITEMS ),
m_userUnits( EDA_UNITS::MILLIMETRES ),
m_isClosing( false ),
m_isNonUserClose( false )
void EDA_BASE_FRAME::commonInit( FRAME_T aFrameType )
{
m_autoSaveTimer = new wxTimer( this, ID_AUTO_SAVE_TIMER );
m_mruPath = PATHS::GetDefaultUserProjectsPath();
m_frameSize = defaultSize( aFrameType );
m_ident = aFrameType;
m_maximizeByDefault = false;
m_infoBar = nullptr;
m_settingsManager = nullptr;
m_fileHistory = nullptr;
m_hasAutoSave = false;
m_autoSaveState = false;
m_autoSaveInterval = -1;
m_undoRedoCountMax = DEFAULT_MAX_UNDO_ITEMS;
m_userUnits = EDA_UNITS::MILLIMETRES;
m_isClosing = false;
m_isNonUserClose = false;
m_autoSaveTimer = new wxTimer( this, ID_AUTO_SAVE_TIMER );
m_mruPath = PATHS::GetDefaultUserProjectsPath();
m_frameSize = defaultSize( aFrameType );
m_auimgr.SetArtProvider( new WX_AUI_DOCK_ART() );
@ -143,6 +139,25 @@ EDA_BASE_FRAME::EDA_BASE_FRAME( wxWindow* aParent, FRAME_T aFrameType,
Connect( wxEVT_CLOSE_WINDOW, wxCloseEventHandler( EDA_BASE_FRAME::windowClosing ) );
initExitKey();
}
EDA_BASE_FRAME::EDA_BASE_FRAME( FRAME_T aFrameType, KIWAY* aKiway ) :
wxFrame(),
TOOLS_HOLDER(),
KIWAY_HOLDER( aKiway, KIWAY_HOLDER::FRAME )
{
commonInit( aFrameType );
}
EDA_BASE_FRAME::EDA_BASE_FRAME( wxWindow* aParent, FRAME_T aFrameType,
const wxString& aTitle, const wxPoint& aPos, const wxSize& aSize,
long aStyle, const wxString& aFrameName, KIWAY* aKiway ) :
wxFrame( aParent, wxID_ANY, aTitle, aPos, aSize, aStyle, aFrameName ),
TOOLS_HOLDER(),
KIWAY_HOLDER( aKiway, KIWAY_HOLDER::FRAME )
{
commonInit( aFrameType );
}

View File

@ -27,7 +27,7 @@
#include <thread>
#include <eda_dde.h>
#include <eda_draw_frame.h>
#include <kiway_player.h>
#include <id.h>
#include <wx/wx.h>
@ -45,7 +45,7 @@ static char client_ipc_buffer[IPC_BUF_SIZE];
/* Function to initialize a server socket
*/
void EDA_DRAW_FRAME::CreateServer( int service, bool local )
void KIWAY_PLAYER::CreateServer( int service, bool local )
{
wxIPV4address addr;
@ -67,7 +67,7 @@ void EDA_DRAW_FRAME::CreateServer( int service, bool local )
/* Function called on every client request.
*/
void EDA_DRAW_FRAME::OnSockRequest( wxSocketEvent& evt )
void KIWAY_PLAYER::OnSockRequest( wxSocketEvent& evt )
{
size_t len;
wxSocketBase* sock = evt.GetSocket();
@ -99,7 +99,7 @@ void EDA_DRAW_FRAME::OnSockRequest( wxSocketEvent& evt )
/* Function called when a connection is requested by a client.
*/
void EDA_DRAW_FRAME::OnSockRequestServer( wxSocketEvent& evt )
void KIWAY_PLAYER::OnSockRequestServer( wxSocketEvent& evt )
{
wxSocketBase* socket;
wxSocketServer* server = (wxSocketServer*) evt.GetSocket();

View File

@ -23,7 +23,6 @@
*/
#include <algorithm>
#include <pybind11/pybind11.h>
#include <bitmaps.h>
#include <eda_item.h>

View File

@ -264,9 +264,9 @@ KIFACE* KIWAY::KiFACE( FACE_T aFaceId, bool doLoad )
}
else
{
KIFACE_GETTER_FUNC* getter = (KIFACE_GETTER_FUNC*) addr;
KIFACE_GETTER_FUNC* ki_getter = (KIFACE_GETTER_FUNC*) addr;
KIFACE* kiface = getter( &m_kiface_version[aFaceId], KIFACE_VERSION, m_program );
KIFACE* kiface = ki_getter( &m_kiface_version[aFaceId], KIFACE_VERSION, m_program );
// KIFACE_GETTER_FUNC function comment (API) says the non-NULL is unconditional.
wxASSERT_MSG( kiface,

View File

@ -47,7 +47,8 @@ KIWAY_PLAYER::KIWAY_PLAYER( KIWAY* aKiway, wxWindow* aParent, FRAME_T aFrameType
m_modal( false ),
m_modal_loop( nullptr ),
m_modal_resultant_parent( nullptr ),
m_modal_ret_val( false )
m_modal_ret_val( false ),
m_socketServer( nullptr )
{
}
@ -59,11 +60,11 @@ KIWAY_PLAYER::KIWAY_PLAYER( wxWindow* aParent, wxWindowID aId, const wxString& a
m_modal( false ),
m_modal_loop( nullptr ),
m_modal_resultant_parent( nullptr ),
m_modal_ret_val( false )
m_modal_ret_val( false ),
m_socketServer( nullptr )
{
}
KIWAY_PLAYER::~KIWAY_PLAYER() throw() {}

View File

@ -295,12 +295,12 @@ bool PGM_SINGLE_TOP::OnPgmInit()
// i.e. they are single part link images so don't need to load a *.kiface.
// Get the getter, it is statically linked into this binary image.
KIFACE_GETTER_FUNC* getter = &KIFACE_GETTER;
KIFACE_GETTER_FUNC* ki_getter = &KIFACE_GETTER;
int kiface_version;
// Get the KIFACE.
KIFACE* kiface = getter( &kiface_version, KIFACE_VERSION, this );
KIFACE* kiface = ki_getter( &kiface_version, KIFACE_VERSION, this );
// Trick the KIWAY into thinking it loaded a KIFACE, by recording the KIFACE
// in the KIWAY. It needs to be there for KIWAY::OnKiwayEnd() anyways.

View File

@ -106,6 +106,8 @@ public:
const wxPoint& aPos, const wxSize& aSize, long aStyle,
const wxString& aFrameName, KIWAY* aKiway );
EDA_BASE_FRAME( FRAME_T aFrameType, KIWAY* aKiway );
~EDA_BASE_FRAME();
/**
@ -650,6 +652,11 @@ private:
*/
void windowClosing( wxCloseEvent& event );
/**
* Collect common initialization functions used in all CTORs
*/
void commonInit( FRAME_T aFrameType );
wxWindow* findQuasiModalDialog();
/**

View File

@ -2,7 +2,7 @@
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2007-2014 Jean-Pierre Charras, jp.charras at wanadoo.fr
* Copyright (C) 1992-2014 KiCad Developers, see CHANGELOG.TXT for contributors.
* Copyright (C) 1992-2021 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
@ -42,6 +42,9 @@
///< Eeschema listens on this port for commands from Pcbnew
#define KICAD_SCH_PORT_SERVICE_NUMBER 4243
///< Scripting window listens for commands for other apps
#define KICAD_PY_PORT_SERVICE_NUMBER 4244
#define MSG_TO_PCB KICAD_PCB_PORT_SERVICE_NUMBER
#define MSG_TO_SCH KICAD_SCH_PORT_SERVICE_NUMBER

View File

@ -191,14 +191,6 @@ public:
*/
virtual BASE_SCREEN* GetScreen() const { return m_currentScreen; }
/**
* Execute a remote command sent via socket (to port KICAD_PCB_PORT_SERVICE_NUMBER,
* currently 4242).
*
* Subclasses should override to implement actual command handlers.
*/
virtual void ExecuteRemoteCommand( const char* cmdline ){}
void EraseMsgBox();
void ReCreateMenuBar() override { }
@ -346,11 +338,6 @@ public:
*/
virtual void DisplayGridMsg();
/* interprocess communication */
void CreateServer( int service, bool local = true );
void OnSockRequest( wxSocketEvent& evt );
void OnSockRequestServer( wxSocketEvent& evt );
void LoadSettings( APP_SETTINGS_BASE* aCfg ) override;
void SaveSettings( APP_SETTINGS_BASE* aCfg ) override;

View File

@ -32,7 +32,6 @@
#include <kiway_holder.h>
#include <eda_base_frame.h>
class KIWAY;
class PROJECT;
struct KIFACE;
@ -43,6 +42,9 @@ class KIWAY_EXPRESS;
#define WX_EVENT_LOOP wxGUIEventLoop
class WX_EVENT_LOOP;
class wxSocketServer;
class wxSocketBase;
/**
* A wxFrame capable of the OpenProjectFiles function, meaning it can load a portion of
@ -179,6 +181,20 @@ public:
void DismissModal( bool aRetVal, const wxString& aResult = wxEmptyString );
/* interprocess communication */
void CreateServer( int service, bool local = true );
void OnSockRequest( wxSocketEvent& evt );
void OnSockRequestServer( wxSocketEvent& evt );
/**
* Execute a remote command sent via socket (to port KICAD_PCB_PORT_SERVICE_NUMBER,
* currently 4242).
*
* Subclasses should override to implement actual command handlers.
*/
virtual void ExecuteRemoteCommand( const char* cmdline ){}
protected:
/// event handler, routes to derivative specific virtual KiwayMailIn()
@ -198,6 +214,9 @@ protected:
wxString m_modal_string;
bool m_modal_ret_val; // true if a selection was made
wxSocketServer* m_socketServer;
std::vector<wxSocketBase*> m_sockets; ///< interprocess communication
#ifndef SWIG
DECLARE_EVENT_TABLE()
#endif

View File

@ -38,6 +38,7 @@
#include <wx/filename.h>
#include <wx/gdicmn.h>
#include <pybind11/embed.h>
class wxSingleInstanceChecker;
class wxApp;
@ -329,6 +330,7 @@ protected:
/// Flag to indicate if the environment variable overwrite warning dialog should be shown.
bool m_show_env_var_dialog;
};

View File

@ -1299,6 +1299,10 @@ void PCB_EDIT_FRAME::ScriptingConsoleEnableDisable()
{
frame = Kiway().Player( FRAME_PYTHON, true, this );
// If we received an error in the CTOR due to Python-ness, don't crash
if( !frame )
return;
if( !frame->IsVisible() )
frame->Show( true );

View File

@ -7,8 +7,6 @@ add_library( scripting STATIC
${KIPYTHON_SRCS}
)
add_subdirectory( pybind11 )
target_link_libraries( scripting
${wxWidgets_LIBRARIES} # wxLogDebug, wxASSERT
${Boost_LIBRARIES} # Because of the OPT types
@ -32,6 +30,7 @@ target_include_directories( scripting PRIVATE
# Setup the KIFACE
add_library( scripting_kiface MODULE
kicad_scripting_main.cpp
kipython_frame.cpp
${KIPYTHON_SRCS}
)
@ -39,7 +38,7 @@ set_source_files_properties( kicad_scripting_main.cpp PROPERTIES
# The KIFACE is in kicad_scripting_main.cpp, export it:
COMPILE_DEFINITIONS "BUILD_KIWAY_DLL;COMPILING_DLL"
)
target_include_directories( scripting_kiface PRIVATE
${PROJECT_SOURCE_DIR}/bitmaps_png/include
${PROJECT_SOURCE_DIR}/include

View File

@ -15,17 +15,16 @@ import wx
import sys
import os
from wx.py import crust, editor, version, dispatcher
from wx.py.editor import EditorNotebook
from wx.py import crust, version, dispatcher
from .kicad_pyeditor import KiCadEditorNotebookFrame, KiCadEditorNotebook
import pcbnew
INTRO = "KiCad - Python Shell"
class KiCadPyShell(editor.EditorNotebookFrame):
"""The KiCad Pythonshell based on wxPyShell"""
class KiCadPyShell(KiCadEditorNotebookFrame):
def __init__(self, parent):
KiCadEditorNotebookFrame.__init__(self, parent)
def _setup_startup(self):
"""Initialise the startup script."""
@ -58,7 +57,7 @@ class KiCadPyShell(editor.EditorNotebookFrame):
Called automatically by base class during init.
"""
self.notebook = EditorNotebook(parent=self)
self.notebook = KiCadEditorNotebook(parent=self.parent)
intro = 'Py %s' % version.VERSION
import imp
import builtins
@ -81,11 +80,6 @@ class KiCadPyShell(editor.EditorNotebookFrame):
self.autoSaveHistory = False
self.LoadSettings()
# in case of wxPhoenix we may need to create a wxApp first and store it
# to prevent removal by garbage collector
if 'phoenix' in wx.PlatformInfo:
self.theApp = wx.GetApp() or wx.App()
self.crust = crust.Crust(parent=self.notebook,
intro=intro, locals=namespace,
rootLabel="locals()",
@ -94,9 +88,9 @@ class KiCadPyShell(editor.EditorNotebookFrame):
self.shell = self.crust.shell
# Override the filling so that status messages go to the status bar.
self.crust.filling.tree.setStatusText = self.SetStatusText
self.crust.filling.tree.setStatusText = self.parent.SetStatusText
# Override the shell so that status messages go to the status bar.
self.shell.setStatusText = self.SetStatusText
self.shell.setStatusText = self.parent.SetStatusText
# Fix a problem with the sash shrinking to nothing.
self.crust.filling.SetSashPosition(200)
self.notebook.AddPage(page=self.crust, text='*Shell*', select=True)
@ -115,7 +109,7 @@ class KiCadPyShell(editor.EditorNotebookFrame):
"wxPython Version: %s\n" % wx.VERSION_STRING + \
("\t(%s)\n" % ", ".join(wx.PlatformInfo[1:]))
dialog = wx.MessageDialog(self, text, title,
dialog = wx.MessageDialog(self.parent, text, title,
wx.OK | wx.ICON_INFORMATION)
dialog.ShowModal()
dialog.Destroy()
@ -127,7 +121,7 @@ class KiCadPyShell(editor.EditorNotebookFrame):
def LoadSettings(self):
"""Load settings for the shell."""
if self.config is not None:
editor.EditorNotebookFrame.LoadSettings(self, self.config)
KiCadEditorNotebookFrame.LoadSettings(self.parent, self.config)
self.autoSaveSettings = \
self.config.ReadBool('Options/AutoSaveSettings', False)
self.execStartupScript = \
@ -150,7 +144,7 @@ class KiCadPyShell(editor.EditorNotebookFrame):
self.config.WriteBool('Options/AutoSaveSettings',
self.autoSaveSettings)
if self.autoSaveSettings or force:
editor.EditorNotebookFrame.SaveSettings(self, self.config)
KiCadEditorNotebookFrame.SaveSettings(self, self.config)
self.config.WriteBool('Options/AutoSaveHistory',
self.autoSaveHistory)
@ -209,7 +203,7 @@ class KiCadPyShell(editor.EditorNotebookFrame):
d.Destroy()
def makePcbnewShellWindow(parent=None):
def makePcbnewShellWindow(parentid):
"""
Create a new Shell Window and return its handle.
@ -219,6 +213,6 @@ def makePcbnewShellWindow(parent=None):
Returns:
The handle to the new window.
"""
pyshell = KiCadPyShell(parent, id=-1, title=INTRO)
pyshell.Show()
return pyshell
parent = wx.FindWindowById( parentid )
return KiCadPyShell(parent)

File diff suppressed because it is too large Load Diff

View File

@ -17,16 +17,16 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <eda_dde.h>
#include <kiface_i.h>
#include <kiface_ids.h>
#include <kipython_frame.h>
#include <kipython_settings.h>
#include <kiway.h>
#include <pgm_base.h>
#include <python_scripting.h>
#include <settings/settings_manager.h>
#include <kipython_settings.h>
#include <python_scripting.h>
#include <sstream>
//-----<KIFACE>-----------------------------------------------------------------
@ -41,84 +41,15 @@ static struct IFACE : public KIFACE_I
wxWindow* CreateWindow( wxWindow* aParent, int aClassId, KIWAY* aKiway, int aCtlBits = 0 ) override
{
InitSettings( new KIPYTHON_SETTINGS );
Pgm().GetSettingsManager().RegisterSettings( KifaceSettings() );
KIPYTHON_FRAME* frame = new KIPYTHON_FRAME( aKiway, aParent );
// passing window ids instead of pointers is because wxPython is not
// exposing the needed c++ apis to make that possible.
std::stringstream pcbnew_pyshell_one_step;
pcbnew_pyshell_one_step << "import kicad_pyshell\n";
pcbnew_pyshell_one_step << "import wx\n";
pcbnew_pyshell_one_step << "\n";
// parent is actually *PCB_EDIT_FRAME
if( aParent )
if( Kiface().IsSingle() )
{
pcbnew_pyshell_one_step << "parent = wx.FindWindowById( " << aParent->GetId() << " )\n";
pcbnew_pyshell_one_step << "newshell = kicad_pyshell.makePcbnewShellWindow( parent )\n";
}
else
{
pcbnew_pyshell_one_step << "newshell = kicad_pyshell.makePcbnewShellWindow()\n";
// only run this under single_top, not under a project manager.
frame->CreateServer( KICAD_PY_PORT_SERVICE_NUMBER );
}
pcbnew_pyshell_one_step << "newshell.SetName( \"KiCad Shell\" )\n";
// return value goes into a "global". It's not actually global, but rather
// the dict that is passed to PyRun_String
pcbnew_pyshell_one_step << "retval = newshell.GetId()\n";
// As always, first grab the GIL
PyLOCK lock;
// Now make a dictionary to serve as the global namespace when the code is
// executed. Put a reference to the builtins module in it.
PyObject* globals = PyDict_New();
PyObject* builtins = PyImport_ImportModule( "builtins" );
wxASSERT( builtins );
PyDict_SetItemString( globals, "__builtins__", builtins );
Py_DECREF( builtins );
// Execute the code to make the makeWindow function we defined above
PyObject* result = PyRun_String( pcbnew_pyshell_one_step.str().c_str(), Py_file_input,
globals, globals );
// Was there an exception?
if( !result )
{
PyErr_Print();
return NULL;
}
Py_DECREF( result );
result = PyDict_GetItemString( globals, "retval" );
if( !PyLong_Check( result ) )
{
wxLogError( "creation of scripting window didn't return a number" );
return NULL;
}
const long windowId = PyLong_AsLong( result );
// It's important not to decref globals before extracting the window id.
// If you do it early, globals, and the retval int it contains, may/will be garbage collected.
// We do not need to decref result, because GetItemString returns a borrowed reference.
Py_DECREF( globals );
wxWindow* window = wxWindow::FindWindowById( windowId );
if( !window )
{
wxLogError( "unable to find pyshell window with id %d", windowId );
return NULL;
}
return window;
return frame;
}
/**
@ -180,6 +111,8 @@ PGM_BASE* PgmOrNull()
bool IFACE::OnKifaceStart( PGM_BASE* aProgram, int aCtlBits )
{
InitSettings( new KIPYTHON_SETTINGS );
Pgm().GetSettingsManager().RegisterSettings( KifaceSettings() );
ScriptingSetup();
return start_common( aCtlBits );

View File

@ -0,0 +1,91 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 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 3
* 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/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#include <frame_type.h>
#include <kipython_frame.h>
#include <kiway_player.h>
#include <python_scripting.h>
#include <pybind11/embed.h>
#include <sstream>
#include <wx/wx.h>
#include <pybind11/embed.h>
void KIPYTHON_FRAME::SetupPythonEditor()
{
// passing window ids instead of pointers is because wxPython is not
// exposing the needed c++ apis to make that possible.
std::stringstream pcbnew_pyshell_one_step;
pcbnew_pyshell_one_step << "import kicad_pyshell\n";
pcbnew_pyshell_one_step << "newshell = kicad_pyshell.makePcbnewShellWindow( " << GetId() << " )\n";
// As always, first grab the GIL
PyLOCK lock;
// Execute the code to make the makeWindow function we defined above
PyRun_SimpleString( pcbnew_pyshell_one_step.str().c_str() );
}
void KIPYTHON_FRAME::redirectStdio()
{
// This is a helpful little tidbit to help debugging and such. It
// redirects Python's stdout and stderr to a window that will popup
// only on demand when something is printed, like a traceback.
PyLOCK lock;
using namespace pybind11::literals;
int id = GetId();
auto locals = pybind11::dict( "parent_id"_a= id );
pybind11::exec( R"(
import sys
import wx
output = wx.PyOnDemandOutputWindow()
sys.stderr = output
parent = wx.Window.FindWindowById( parent_id )
output.SetParent( parent )
)", pybind11::globals(), locals );
}
KIPYTHON_FRAME::KIPYTHON_FRAME( KIWAY* aKiway, wxWindow* aParent ) :
KIWAY_PLAYER( aKiway, aParent, FRAME_PYTHON, wxT( "KiPython" ), wxDefaultPosition,
wxDefaultSize, KICAD_DEFAULT_DRAWFRAME_STYLE, wxT( "KiPython" ) )
{
m_stdio = 0;
CallAfter( [&](){ SetupPythonEditor(); } );
redirectStdio();
}
KIPYTHON_FRAME::~KIPYTHON_FRAME()
{
wxWindow* stdio_window = wxWindow::FindWindowById( m_stdio );
if( stdio_window )
stdio_window->Close( true );
}

View File

@ -0,0 +1,63 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2021 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 3
* 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/gpl-3.0.html
* or you may search the http://www.gnu.org website for the version 3 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifndef SCRIPTING_KIPYTHON_FRAME_H_
#define SCRIPTING_KIPYTHON_FRAME_H_
#include <kiway_player.h>
class wxWindow;
class APP_SETTINGS_BASE;
class KIWAY_EXPRESS;
class KIPYTHON_FRAME : public KIWAY_PLAYER
{
public:
KIPYTHON_FRAME( KIWAY* aKiway, wxWindow* aParent );
~KIPYTHON_FRAME() override;
void LoadSettings( APP_SETTINGS_BASE* aCfg ) override {}
void SaveSettings( APP_SETTINGS_BASE* aCfg ) override {}
wxWindow* GetToolCanvas() const override { return nullptr;}
void ExecuteRemoteCommand( const char* cmdline ) override {}
void KiwayMailIn( KIWAY_EXPRESS& aEvent ) override {}
void ProjectChanged() override {}
void SetupPythonEditor();
private:
void redirectStdio();
bool canCloseWindow( wxCloseEvent& aCloseEvent ) override { return true; }
void doCloseWindow() override {}
long m_stdio;
};
#endif /* SCRIPTING_KIPYTHON_FRAME_H_ */

View File

@ -54,17 +54,6 @@
extern "C" PyObject* PyInit__pcbnew( void );
#define EXTRA_PYTHON_MODULES 10 // this is the number of python
// modules that we want to add into the list
/* python inittab that links module names to module init functions
* we will rebuild it to include the original python modules plus
* our own ones
*/
struct _inittab* SwigImportInittab;
static int SwigNumModules = 0;
/// True if the wxPython scripting layer was successfully loaded.
static bool wxPythonLoaded = false;
@ -75,70 +64,6 @@ bool IsWxPythonLoaded()
return wxPythonLoaded;
}
/**
* Add a name + initfuction to our SwigImportInittab
*/
static void swigAddModule( const char* name, PyObject* (* initfunc)() )
{
SwigImportInittab[SwigNumModules].name = (char*) name;
SwigImportInittab[SwigNumModules].initfunc = initfunc;
SwigNumModules++;
SwigImportInittab[SwigNumModules].name = (char*) 0;
SwigImportInittab[SwigNumModules].initfunc = 0;
}
/**
* Add the builtin python modules
*/
static void swigAddBuiltin()
{
int i = 0;
/* discover the length of the pyimport inittab */
while( PyImport_Inittab[i].name )
i++;
/* allocate memory for the python module table */
SwigImportInittab = (struct _inittab*) malloc(
sizeof( struct _inittab ) * ( i + EXTRA_PYTHON_MODULES ) );
/* copy all pre-existing python modules into our newly created table */
i = 0;
while( PyImport_Inittab[i].name )
{
swigAddModule( PyImport_Inittab[i].name, PyImport_Inittab[i].initfunc );
i++;
}
}
/**
* Add the internal modules to the python scripting so they will be available to the scripts.
*/
static void swigAddModules()
{
swigAddModule( "_pcbnew", PyInit__pcbnew );
// finally it seems better to include all in just one module
// but in case we needed to include any other modules,
// it must be done like this:
// swigAddModule( "_kicad", init_kicad );
}
/**
* Switch the python module table to the Pcbnew built one.
*/
static void swigSwitchPythonBuiltin()
{
PyImport_Inittab = SwigImportInittab;
}
PyThreadState* g_PythonMainTState;
@ -245,9 +170,7 @@ bool InitPythonScripting( const char* aStockScriptingPath, const char* aUserScri
int retv;
char cmd[1024];
swigAddBuiltin(); // add builtin functions
swigAddModules(); // add our own modules
swigSwitchPythonBuiltin(); // switch the python builtin modules to our new list
PyImport_AppendInittab( "_pcbnew", PyInit__pcbnew );
#ifdef _MSC_VER
// Under vcpkg/msvc, we need to explicitly set the python home
@ -266,18 +189,16 @@ bool InitPythonScripting( const char* aStockScriptingPath, const char* aUserScri
Py_SetPythonHome( pyHome.GetFullPath().c_str() );
}
#endif
//
// Py_Initialize();
pybind11::initialize_interpreter();
Py_Initialize();
PySys_SetArgv( Pgm().App().argc, Pgm().App().argv );
// PySys_SetArgv( Pgm().App().argc, Pgm().App().argv );
#if PY_VERSION_HEX < 0x03070000 // PyEval_InitThreads() is called by Py_Initialize() starting with version 3.7
PyEval_InitThreads();
#endif // if PY_VERSION_HEX < 0x03070000
#if defined( DEBUG )
RedirectStdio();
#endif
wxPythonLoaded = true;
// Save the current Python thread state and release the
@ -374,7 +295,7 @@ static void RunPythonMethodWithReturnedString( const char* aMethodName, wxString
void FinishPythonScripting()
{
PyEval_RestoreThread( g_PythonMainTState );
Py_Finalize();
pybind11::finalize_interpreter();
}
@ -426,25 +347,6 @@ void UpdatePythonEnvVar( const wxString& aVar, const wxString& aValue )
wxLogError( "Python error %d occurred running command:\n\n`%s`", retv, cmd );
}
void RedirectStdio()
{
// This is a helpful little tidbit to help debugging and such. It
// redirects Python's stdout and stderr to a window that will popup
// only on demand when something is printed, like a traceback.
const char* python_redirect =
"import sys\n"
"import wx\n"
"output = wx.PyOnDemandOutputWindow()\n"
"sys.stderr = output\n";
PyLOCK lock;
int retv = PyRun_SimpleString( python_redirect );
if( retv != 0 )
wxLogError( "Python error %d occurred running command:\n\n`%s`", retv, python_redirect );
}
wxString PyStringToWx( PyObject* aString )
{