Add initial support for Phoenix (new wxPython binding)
Based on the work of @mmccoo: https://kicad.mmccoo.com/2017/11/23/learnings-from-moving-kicad-to-wxpython-4-0/ and this additional patchset to remove wxpy_api.h dependency: http://mmccoo.com/random/0001-Remove-dependence-on-pywx_api.h.patch Please note CreatePythonShellWindow changed quite a lot. Throughful testing should be made for the old as well as new wxPython version on all platforms
This commit is contained in:
parent
633bc7f2d5
commit
0e0b4d52a2
|
@ -82,6 +82,10 @@ option( KICAD_SCRIPTING_WXPYTHON
|
|||
"Build wxPython implementation for wx interface building in Python and py.shell (default ON)."
|
||||
ON )
|
||||
|
||||
option( KICAD_SCRIPTING_WXPYTHON_PHOENIX
|
||||
"Use new wxPython binding (default OFF)."
|
||||
OFF )
|
||||
|
||||
option( KICAD_SCRIPTING_ACTION_MENU
|
||||
"Build a tools menu with registered python plugins: actions plugins (default ON)."
|
||||
ON )
|
||||
|
@ -393,6 +397,10 @@ if( KICAD_SCRIPTING_WXPYTHON )
|
|||
add_definitions( -DKICAD_SCRIPTING_WXPYTHON )
|
||||
endif()
|
||||
|
||||
if( KICAD_SCRIPTING_WXPYTHON_PHOENIX )
|
||||
add_definitions( -DKICAD_SCRIPTING_WXPYTHON_PHOENIX )
|
||||
endif()
|
||||
|
||||
if( KICAD_SCRIPTING_ACTION_MENU )
|
||||
add_definitions( -DKICAD_SCRIPTING_ACTION_MENU )
|
||||
endif()
|
||||
|
@ -751,8 +759,24 @@ if( KICAD_SCRIPTING OR KICAD_SCRIPTING_MODULES )
|
|||
if( KICAD_SCRIPTING_WXPYTHON )
|
||||
# Check to see if the correct version of wxPython is installed based on the version of
|
||||
# wxWidgets found. At least the major an minor version should match.
|
||||
set( _wxpy_version "${wxWidgets_VERSION_MAJOR}.${wxWidgets_VERSION_MINOR}" )
|
||||
set( _py_cmd "import wxversion;print(wxversion.checkInstalled('${_wxpy_version}'))" )
|
||||
if( KICAD_SCRIPTING_WXPYTHON_PHOENIX )
|
||||
set( _py_cmd "import wx;print(wx.version())" )
|
||||
execute_process( COMMAND ${PYTHON_EXECUTABLE} -c "${_py_cmd}"
|
||||
RESULT_VARIABLE WXPYTHON_VERSION_RESULT
|
||||
OUTPUT_VARIABLE WXPYTHON_VERSION_FOUND
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE
|
||||
)
|
||||
# Check to see if any version of wxPython is installed on the system.
|
||||
if( WXPYTHON_VERSION_RESULT GREATER 0 )
|
||||
message( FATAL_ERROR "wxPython does not appear to be installed on the system." )
|
||||
endif()
|
||||
set( _wxpy_version ${WXPYTHON_VERSION_FOUND} )
|
||||
set( _py_cmd "import wx;import re;print(re.search('wxWidgets ${wxWidgets_VERSION_MAJOR}.${wxWidgets_VERSION_MINOR}', wx.wxWidgets_version) != None)" )
|
||||
else()
|
||||
set( _wxpy_version "${wxWidgets_VERSION_MAJOR}.${wxWidgets_VERSION_MINOR}" )
|
||||
set( _py_cmd "import wxversion;print(wxversion.checkInstalled('${_wxpy_version}'))" )
|
||||
endif()
|
||||
|
||||
|
||||
# Add user specified Python site package path.
|
||||
if( PYTHON_SITE_PACKAGE_PATH )
|
||||
|
|
|
@ -166,6 +166,11 @@ of Python 2. This option is disabled by default.
|
|||
The KICAD_SCRIPTING_WXPYTHON option is used to enable building the wxPython interface into
|
||||
Pcbnew including the wxPython console. This option is enabled by default.
|
||||
|
||||
## wxPython Phoenix Scripting Support ## {#wxpython_phoenix}
|
||||
|
||||
The KICAD_SCRIPTING_WXPYTHON_PHOENIX option is used to enable building the wxPython interface with
|
||||
the new Phoenix binding instead of the legacy one. This option is disabled by default.
|
||||
|
||||
## GitHub Plugin ## {#github_opt}
|
||||
|
||||
The BUILD_GITHUB_PLUGIN option is used to control if the GitHub plug in is built. This option is
|
||||
|
|
|
@ -554,6 +554,13 @@ void DIALOG_ABOUT::buildVersionInfoData( wxString& aMsg, bool aFormatHtml )
|
|||
aMsg << OFF;
|
||||
#endif
|
||||
|
||||
aMsg << indent4 << "KICAD_SCRIPTING_WXPYTHON_PHOENIX=";
|
||||
#ifdef KICAD_SCRIPTING_WXPYTHON_PHOENIX
|
||||
aMsg << ON;
|
||||
#else
|
||||
aMsg << OFF;
|
||||
#endif
|
||||
|
||||
aMsg << indent4 << "KICAD_SCRIPTING_ACTION_MENU=";
|
||||
#ifdef KICAD_SCRIPTING_ACTION_MENU
|
||||
aMsg << ON;
|
||||
|
|
|
@ -86,6 +86,11 @@ class PcbnewPyShell(editor.EditorNotebookFrame):
|
|||
self.autoSaveHistory = False
|
||||
self.LoadSettings()
|
||||
|
||||
# in case of wxPhoenix we need to create a wxApp first and store it
|
||||
# to prevent removal by gabage collector
|
||||
if 'phoenix' in wx.PlatformInfo:
|
||||
self.theApp = wx.App()
|
||||
|
||||
self.crust = crust.Crust(parent=self.notebook,
|
||||
intro=intro, locals=namespace,
|
||||
rootLabel="locals()",
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include <python_scripting.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sstream>
|
||||
|
||||
#include <fctsys.h>
|
||||
#include <eda_base_frame.h>
|
||||
|
@ -162,6 +163,7 @@ bool pcbnewInitPythonScripting( const char * aUserScriptingPath )
|
|||
#ifdef KICAD_SCRIPTING_WXPYTHON
|
||||
PyEval_InitThreads();
|
||||
|
||||
#ifndef KICAD_SCRIPTING_WXPYTHON_PHOENIX
|
||||
#ifndef __WINDOWS__ // import wxversion.py currently not working under winbuilder, and not useful.
|
||||
char cmd[1024];
|
||||
// Make sure that that the correct version of wxPython is loaded. In systems where there
|
||||
|
@ -190,13 +192,14 @@ bool pcbnewInitPythonScripting( const char * aUserScriptingPath )
|
|||
Py_Finalize();
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
wxPythonLoaded = true;
|
||||
|
||||
// Save the current Python thread state and release the
|
||||
// Global Interpreter Lock.
|
||||
g_PythonMainTState = PyEval_SaveThread();
|
||||
|
||||
g_PythonMainTState = wxPyBeginAllowThreads();
|
||||
#endif // ifdef KICAD_SCRIPTING_WXPYTHON
|
||||
|
||||
// load pcbnew inside python, and load all the user plugins, TODO: add system wide plugins
|
||||
|
@ -298,7 +301,7 @@ void pcbnewGetWizardsBackTrace( wxString& aNames )
|
|||
void pcbnewFinishPythonScripting()
|
||||
{
|
||||
#ifdef KICAD_SCRIPTING_WXPYTHON
|
||||
wxPyEndAllowThreads( g_PythonMainTState );
|
||||
PyEval_RestoreThread( g_PythonMainTState );
|
||||
#endif
|
||||
Py_Finalize();
|
||||
}
|
||||
|
@ -325,15 +328,25 @@ void RedirectStdio()
|
|||
|
||||
wxWindow* CreatePythonShellWindow( wxWindow* parent, const wxString& aFramenameId )
|
||||
{
|
||||
const char* pcbnew_pyshell =
|
||||
"import kicad_pyshell\n"
|
||||
"\n"
|
||||
"def makeWindow(parent):\n"
|
||||
" return kicad_pyshell.makePcbnewShellWindow(parent)\n"
|
||||
"\n";
|
||||
// parent is actually *PCB_EDIT_FRAME
|
||||
const int parentId = parent->GetId();
|
||||
{
|
||||
wxWindow* parent2 = wxWindow::FindWindowById( parentId );
|
||||
wxASSERT( parent2 == parent );
|
||||
}
|
||||
|
||||
wxWindow* window = NULL;
|
||||
PyObject* result;
|
||||
// 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";
|
||||
pcbnew_pyshell_one_step << "parent = wx.FindWindowById( " << parentId << " )\n";
|
||||
pcbnew_pyshell_one_step << "newshell = kicad_pyshell.makePcbnewShellWindow( parent )\n";
|
||||
pcbnew_pyshell_one_step << "newshell.SetName( \"" << aFramenameId << "\" )\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;
|
||||
|
@ -354,7 +367,7 @@ wxWindow* CreatePythonShellWindow( wxWindow* parent, const wxString& aFramenameI
|
|||
Py_DECREF( builtins );
|
||||
|
||||
// Execute the code to make the makeWindow function we defined above
|
||||
result = PyRun_String( pcbnew_pyshell, Py_file_input, globals, globals );
|
||||
PyObject* result = PyRun_String( pcbnew_pyshell_one_step.str().c_str(), Py_file_input, globals, globals );
|
||||
|
||||
// Was there an exception?
|
||||
if( !result )
|
||||
|
@ -365,42 +378,36 @@ wxWindow* CreatePythonShellWindow( wxWindow* parent, const wxString& aFramenameI
|
|||
|
||||
Py_DECREF( result );
|
||||
|
||||
// Now there should be an object named 'makeWindow' in the dictionary that
|
||||
// we can grab a pointer to:
|
||||
PyObject* func = PyDict_GetItemString( globals, "makeWindow" );
|
||||
wxASSERT( PyCallable_Check( func ) );
|
||||
result = PyDict_GetItemString( globals, "retval" );
|
||||
|
||||
// Now build an argument tuple and call the Python function. Notice the
|
||||
// use of another wxPython API to take a wxWindows object and build a
|
||||
// wxPython object that wraps it.
|
||||
|
||||
PyObject* arg = wxPyMake_wxObject( parent, false );
|
||||
wxASSERT( arg != NULL );
|
||||
|
||||
PyObject* tuple = PyTuple_New( 1 );
|
||||
PyTuple_SET_ITEM( tuple, 0, arg );
|
||||
|
||||
result = PyEval_CallObject( func, tuple );
|
||||
|
||||
// Was there an exception?
|
||||
if( !result )
|
||||
PyErr_Print();
|
||||
else
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
if( !PyLong_Check( result ) )
|
||||
#else
|
||||
if( !PyInt_Check( result ) )
|
||||
#endif
|
||||
{
|
||||
// Otherwise, get the returned window out of Python-land and
|
||||
// into C++-ville...
|
||||
bool success = wxPyConvertSwigPtr( result, (void**) &window, "wxWindow" );
|
||||
(void) success;
|
||||
|
||||
wxASSERT_MSG( success, "Returned object was not a wxWindow!" );
|
||||
Py_DECREF( result );
|
||||
|
||||
window->SetName( aFramenameId );
|
||||
wxLogError("creation of scripting window didn't return a number");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Release the python objects we still have
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
const long windowId = PyLong_AsLong( result );
|
||||
#else
|
||||
const long windowId = PyInt_AsLong( result );
|
||||
#endif
|
||||
|
||||
// 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 );
|
||||
Py_DECREF( tuple );
|
||||
|
||||
wxWindow* window = wxWindow::FindWindowById( windowId );
|
||||
|
||||
if( !window )
|
||||
{
|
||||
wxLogError("unable to find pyshell window with id %d", windowId);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return window;
|
||||
}
|
||||
|
|
|
@ -39,7 +39,11 @@
|
|||
|
||||
#ifndef NO_WXPYTHON_EXTENSION_HEADERS
|
||||
#ifdef KICAD_SCRIPTING_WXPYTHON
|
||||
#include <wx/wxPython/wxPython.h>
|
||||
#ifdef KICAD_SCRIPTING_WXPYTHON_PHOENIX
|
||||
#include <wx/window.h>
|
||||
#else
|
||||
#include <wx/wxPython/wxPython.h>
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
|
|
Loading…
Reference in New Issue