Merge ~pointhi/kicad:wx-python into ~kicad-product-committers/kicad:master

Proposed by Thomas Pointhuber
Status: Merged
Merge reported by: Maciej Suminski
Merged at revision: 00511f2a7f7811e1fe8cfbbaa1664edaaba25d91
Proposed branch: ~pointhi/kicad:wx-python
Merge into: ~kicad-product-committers/kicad:master
Diff against target: 274 lines (+95/-43)
6 files modified
CMakeLists.txt (+26/-2)
Documentation/development/compiling.md (+5/-0)
common/dialog_about/dialog_about.cpp (+7/-0)
pcbnew/python/kicad_pyshell/__init__.py (+5/-0)
pcbnew/swig/python_scripting.cpp (+47/-40)
pcbnew/swig/python_scripting.h (+5/-1)
Reviewer Review Type Date Requested Status
Wayne Stambaugh Approve
Maciej Suminski Approve
Seth Hillbrand Approve
Review via email: mp+357605@code.launchpad.net

Commit message

Add optional wxPhoenix support

Description of the change

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

It adds wxPhoenix support to KiCad while keeping support for the old wxPython binding. All known are resolved now. In my case testing took place with wx3.0.4+GTK3 on Python2+wxPython as well as Python3+wxPhoenix on Linux. It would be nice to get testing done on Windows as well as Mac at least with the old wxPython version to ensure nothing breaks. While there are currently some compiler problems to get wxPhoenix running on Windows, adding this would benefit especially Linux users on Python3 only systems to get shell support.

To post a comment you must log in.
Revision history for this message
Thomas Pointhuber (pointhi) wrote :
Revision history for this message
Seth Hillbrand (sethh) wrote :

Looks good. Tested on Debian Buster and works smoothly.

review: Approve
Revision history for this message
Maciej Suminski (orsonmmz) wrote :

I have briefly tested the branch with Phoenix/Python3/GTK3 and wxPython/Python2/GTK3 on Linux - no problems observed so far.

review: Approve
Revision history for this message
Wayne Stambaugh (stambaughw) wrote :

It doesn't build on msys2 when python 3 is enabled. It finds the python 3 interpreter without any issue but it fails finding the python3 library path. Here is the config error:

-- Found PythonInterp: C:/msys64/mingw64/bin/python3.exe (found suitable version "3.7.1", minimum required is "3.3")
-- Check for installed Python Interpreter -- found
-- Python module install path: lib/python3.7/site-packages
CMake Error at C:/msys64/mingw64/share/cmake-3.12/Modules/FindPackageHandleStandardArgs.cmake:137 (message):
  Could NOT find PythonLibs (missing: PYTHON_INCLUDE_DIRS) (Required is at
  least version "3.3")
Call Stack (most recent call first):
  C:/msys64/mingw64/share/cmake-3.12/Modules/FindPackageHandleStandardArgs.cmake:378 (_FPHSA_FAILURE_MESSAGE)
  CMakeModules/FindPythonLibs.cmake:233 (find_package_handle_standard_args)
  CMakeLists.txt:750 (find_package)

-- Configuring incomplete, errors occurred!

review: Needs Fixing
Revision history for this message
Thomas Pointhuber (pointhi) wrote :

> It doesn't build on msys2 when python 3 is enabled. It finds the python 3
> interpreter without any issue but it fails finding the python3 library path.
> Here is the config error:
>
> -- Found PythonInterp: C:/msys64/mingw64/bin/python3.exe (found suitable
> version "3.7.1", minimum required is "3.3")
> -- Check for installed Python Interpreter -- found
> -- Python module install path: lib/python3.7/site-packages
> CMake Error at C:/msys64/mingw64/share/cmake-3.12/Modules/FindPackageHandleSta
> ndardArgs.cmake:137 (message):
> Could NOT find PythonLibs (missing: PYTHON_INCLUDE_DIRS) (Required is at
> least version "3.3")
> Call Stack (most recent call first):
> C:/msys64/mingw64/share/cmake-3.12/Modules/FindPackageHandleStandardArgs.cma
> ke:378 (_FPHSA_FAILURE_MESSAGE)
> CMakeModules/FindPythonLibs.cmake:233 (find_package_handle_standard_args)
> CMakeLists.txt:750 (find_package)
>
>
> -- Configuring incomplete, errors occurred!

That's weird because this patch should not change Python3 stuff. Can you verify this problem does not exist upstream?

Revision history for this message
Nick Østergaard (nickoe) wrote :

@Wayne, try to add -DPYTHON_INCLUDE_DIR=${MINGW_PREFIX}/include/python3.6m

Revision history for this message
Nick Østergaard (nickoe) wrote :

Or probably 3.7m in your case.

Revision history for this message
Wayne Stambaugh (stambaughw) wrote :

@Nick, thanks for the tip. That did it. Do you have any idea why the 'm' suffix is added to the python include path? This should be easy enough to fix by updating our FindPythonLib.cmake file to look in the python version path with the 'm' suffix. I also built and tested the python 3 support and to make sure the python 2 support still works on msys2/mingw32/mingw64 so I approve merging this.

review: Approve
Revision history for this message
Maciej Suminski (orsonmmz) wrote :

Thank you Thomas!

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/CMakeLists.txt b/CMakeLists.txt
index cf55d49..42e4c60 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -82,6 +82,10 @@ option( KICAD_SCRIPTING_WXPYTHON
82 "Build wxPython implementation for wx interface building in Python and py.shell (default ON)."82 "Build wxPython implementation for wx interface building in Python and py.shell (default ON)."
83 ON )83 ON )
8484
85option( KICAD_SCRIPTING_WXPYTHON_PHOENIX
86 "Use new wxPython binding (default OFF)."
87 OFF )
88
85option( KICAD_SCRIPTING_ACTION_MENU89option( KICAD_SCRIPTING_ACTION_MENU
86 "Build a tools menu with registered python plugins: actions plugins (default ON)."90 "Build a tools menu with registered python plugins: actions plugins (default ON)."
87 ON )91 ON )
@@ -389,6 +393,10 @@ if( KICAD_SCRIPTING_WXPYTHON )
389 add_definitions( -DKICAD_SCRIPTING_WXPYTHON )393 add_definitions( -DKICAD_SCRIPTING_WXPYTHON )
390endif()394endif()
391395
396if( KICAD_SCRIPTING_WXPYTHON_PHOENIX )
397 add_definitions( -DKICAD_SCRIPTING_WXPYTHON_PHOENIX )
398endif()
399
392if( KICAD_SCRIPTING_ACTION_MENU )400if( KICAD_SCRIPTING_ACTION_MENU )
393 add_definitions( -DKICAD_SCRIPTING_ACTION_MENU )401 add_definitions( -DKICAD_SCRIPTING_ACTION_MENU )
394endif()402endif()
@@ -747,8 +755,24 @@ if( KICAD_SCRIPTING OR KICAD_SCRIPTING_MODULES )
747 if( KICAD_SCRIPTING_WXPYTHON )755 if( KICAD_SCRIPTING_WXPYTHON )
748 # Check to see if the correct version of wxPython is installed based on the version of756 # Check to see if the correct version of wxPython is installed based on the version of
749 # wxWidgets found. At least the major an minor version should match.757 # wxWidgets found. At least the major an minor version should match.
750 set( _wxpy_version "${wxWidgets_VERSION_MAJOR}.${wxWidgets_VERSION_MINOR}" )758 if( KICAD_SCRIPTING_WXPYTHON_PHOENIX )
751 set( _py_cmd "import wxversion;print(wxversion.checkInstalled('${_wxpy_version}'))" )759 set( _py_cmd "import wx;print(wx.version())" )
760 execute_process( COMMAND ${PYTHON_EXECUTABLE} -c "${_py_cmd}"
761 RESULT_VARIABLE WXPYTHON_VERSION_RESULT
762 OUTPUT_VARIABLE WXPYTHON_VERSION_FOUND
763 OUTPUT_STRIP_TRAILING_WHITESPACE
764 )
765 # Check to see if any version of wxPython is installed on the system.
766 if( WXPYTHON_VERSION_RESULT GREATER 0 )
767 message( FATAL_ERROR "wxPython does not appear to be installed on the system." )
768 endif()
769 set( _wxpy_version ${WXPYTHON_VERSION_FOUND} )
770 set( _py_cmd "import wx;import re;print(re.search('wxWidgets ${wxWidgets_VERSION_MAJOR}.${wxWidgets_VERSION_MINOR}', wx.wxWidgets_version) != None)" )
771 else()
772 set( _wxpy_version "${wxWidgets_VERSION_MAJOR}.${wxWidgets_VERSION_MINOR}" )
773 set( _py_cmd "import wxversion;print(wxversion.checkInstalled('${_wxpy_version}'))" )
774 endif()
775
752776
753 # Add user specified Python site package path.777 # Add user specified Python site package path.
754 if( PYTHON_SITE_PACKAGE_PATH )778 if( PYTHON_SITE_PACKAGE_PATH )
diff --git a/Documentation/development/compiling.md b/Documentation/development/compiling.md
index 4de0def..9af2fe5 100644
--- a/Documentation/development/compiling.md
+++ b/Documentation/development/compiling.md
@@ -166,6 +166,11 @@ of Python 2. This option is disabled by default.
166The KICAD_SCRIPTING_WXPYTHON option is used to enable building the wxPython interface into166The KICAD_SCRIPTING_WXPYTHON option is used to enable building the wxPython interface into
167Pcbnew including the wxPython console. This option is enabled by default.167Pcbnew including the wxPython console. This option is enabled by default.
168168
169## wxPython Phoenix Scripting Support ## {#wxpython_phoenix}
170
171The KICAD_SCRIPTING_WXPYTHON_PHOENIX option is used to enable building the wxPython interface with
172the new Phoenix binding instead of the legacy one. This option is disabled by default.
173
169## GitHub Plugin ## {#github_opt}174## GitHub Plugin ## {#github_opt}
170175
171The BUILD_GITHUB_PLUGIN option is used to control if the GitHub plug in is built. This option is176The BUILD_GITHUB_PLUGIN option is used to control if the GitHub plug in is built. This option is
diff --git a/common/dialog_about/dialog_about.cpp b/common/dialog_about/dialog_about.cpp
index 0af42f8..9fc8233 100644
--- a/common/dialog_about/dialog_about.cpp
+++ b/common/dialog_about/dialog_about.cpp
@@ -554,6 +554,13 @@ void DIALOG_ABOUT::buildVersionInfoData( wxString& aMsg, bool aFormatHtml )
554 aMsg << OFF;554 aMsg << OFF;
555#endif555#endif
556556
557 aMsg << indent4 << "KICAD_SCRIPTING_WXPYTHON_PHOENIX=";
558#ifdef KICAD_SCRIPTING_WXPYTHON_PHOENIX
559 aMsg << ON;
560#else
561 aMsg << OFF;
562#endif
563
557 aMsg << indent4 << "KICAD_SCRIPTING_ACTION_MENU=";564 aMsg << indent4 << "KICAD_SCRIPTING_ACTION_MENU=";
558#ifdef KICAD_SCRIPTING_ACTION_MENU565#ifdef KICAD_SCRIPTING_ACTION_MENU
559 aMsg << ON;566 aMsg << ON;
diff --git a/pcbnew/python/kicad_pyshell/__init__.py b/pcbnew/python/kicad_pyshell/__init__.py
index 7f476d5..91bca5b 100644
--- a/pcbnew/python/kicad_pyshell/__init__.py
+++ b/pcbnew/python/kicad_pyshell/__init__.py
@@ -86,6 +86,11 @@ class PcbnewPyShell(editor.EditorNotebookFrame):
86 self.autoSaveHistory = False86 self.autoSaveHistory = False
87 self.LoadSettings()87 self.LoadSettings()
8888
89 # in case of wxPhoenix we need to create a wxApp first and store it
90 # to prevent removal by gabage collector
91 if 'phoenix' in wx.PlatformInfo:
92 self.theApp = wx.App()
93
89 self.crust = crust.Crust(parent=self.notebook,94 self.crust = crust.Crust(parent=self.notebook,
90 intro=intro, locals=namespace,95 intro=intro, locals=namespace,
91 rootLabel="locals()",96 rootLabel="locals()",
diff --git a/pcbnew/swig/python_scripting.cpp b/pcbnew/swig/python_scripting.cpp
index 4577406..17c3ee0 100644
--- a/pcbnew/swig/python_scripting.cpp
+++ b/pcbnew/swig/python_scripting.cpp
@@ -30,6 +30,7 @@
30#include <python_scripting.h>30#include <python_scripting.h>
31#include <stdlib.h>31#include <stdlib.h>
32#include <string.h>32#include <string.h>
33#include <sstream>
3334
34#include <fctsys.h>35#include <fctsys.h>
35#include <eda_base_frame.h>36#include <eda_base_frame.h>
@@ -162,6 +163,7 @@ bool pcbnewInitPythonScripting( const char * aUserScriptingPath )
162#ifdef KICAD_SCRIPTING_WXPYTHON163#ifdef KICAD_SCRIPTING_WXPYTHON
163 PyEval_InitThreads();164 PyEval_InitThreads();
164165
166#ifndef KICAD_SCRIPTING_WXPYTHON_PHOENIX
165#ifndef __WINDOWS__ // import wxversion.py currently not working under winbuilder, and not useful.167#ifndef __WINDOWS__ // import wxversion.py currently not working under winbuilder, and not useful.
166 char cmd[1024];168 char cmd[1024];
167 // Make sure that that the correct version of wxPython is loaded. In systems where there169 // Make sure that that the correct version of wxPython is loaded. In systems where there
@@ -190,13 +192,14 @@ bool pcbnewInitPythonScripting( const char * aUserScriptingPath )
190 Py_Finalize();192 Py_Finalize();
191 return false;193 return false;
192 }194 }
195#endif
193196
194 wxPythonLoaded = true;197 wxPythonLoaded = true;
195198
196 // Save the current Python thread state and release the199 // Save the current Python thread state and release the
197 // Global Interpreter Lock.200 // Global Interpreter Lock.
201 g_PythonMainTState = PyEval_SaveThread();
198202
199 g_PythonMainTState = wxPyBeginAllowThreads();
200#endif // ifdef KICAD_SCRIPTING_WXPYTHON203#endif // ifdef KICAD_SCRIPTING_WXPYTHON
201204
202 // load pcbnew inside python, and load all the user plugins, TODO: add system wide plugins205 // load pcbnew inside python, and load all the user plugins, TODO: add system wide plugins
@@ -298,7 +301,7 @@ void pcbnewGetWizardsBackTrace( wxString& aNames )
298void pcbnewFinishPythonScripting()301void pcbnewFinishPythonScripting()
299{302{
300#ifdef KICAD_SCRIPTING_WXPYTHON303#ifdef KICAD_SCRIPTING_WXPYTHON
301 wxPyEndAllowThreads( g_PythonMainTState );304 PyEval_RestoreThread( g_PythonMainTState );
302#endif305#endif
303 Py_Finalize();306 Py_Finalize();
304}307}
@@ -325,15 +328,25 @@ void RedirectStdio()
325328
326wxWindow* CreatePythonShellWindow( wxWindow* parent, const wxString& aFramenameId )329wxWindow* CreatePythonShellWindow( wxWindow* parent, const wxString& aFramenameId )
327{330{
328 const char* pcbnew_pyshell =331 // parent is actually *PCB_EDIT_FRAME
329 "import kicad_pyshell\n"332 const int parentId = parent->GetId();
330 "\n"333 {
331 "def makeWindow(parent):\n"334 wxWindow* parent2 = wxWindow::FindWindowById( parentId );
332 " return kicad_pyshell.makePcbnewShellWindow(parent)\n"335 wxASSERT( parent2 == parent );
333 "\n";336 }
334337
335 wxWindow* window = NULL;338 // passing window ids instead of pointers is because wxPython is not
336 PyObject* result;339 // exposing the needed c++ apis to make that possible.
340 std::stringstream pcbnew_pyshell_one_step;
341 pcbnew_pyshell_one_step << "import kicad_pyshell\n";
342 pcbnew_pyshell_one_step << "import wx\n";
343 pcbnew_pyshell_one_step << "\n";
344 pcbnew_pyshell_one_step << "parent = wx.FindWindowById( " << parentId << " )\n";
345 pcbnew_pyshell_one_step << "newshell = kicad_pyshell.makePcbnewShellWindow( parent )\n";
346 pcbnew_pyshell_one_step << "newshell.SetName( \"" << aFramenameId << "\" )\n";
347 // return value goes into a "global". It's not actually global, but rather
348 // the dict that is passed to PyRun_String
349 pcbnew_pyshell_one_step << "retval = newshell.GetId()\n";
337350
338 // As always, first grab the GIL351 // As always, first grab the GIL
339 PyLOCK lock;352 PyLOCK lock;
@@ -354,7 +367,7 @@ wxWindow* CreatePythonShellWindow( wxWindow* parent, const wxString& aFramenameI
354 Py_DECREF( builtins );367 Py_DECREF( builtins );
355368
356 // Execute the code to make the makeWindow function we defined above369 // Execute the code to make the makeWindow function we defined above
357 result = PyRun_String( pcbnew_pyshell, Py_file_input, globals, globals );370 PyObject* result = PyRun_String( pcbnew_pyshell_one_step.str().c_str(), Py_file_input, globals, globals );
358371
359 // Was there an exception?372 // Was there an exception?
360 if( !result )373 if( !result )
@@ -365,43 +378,37 @@ wxWindow* CreatePythonShellWindow( wxWindow* parent, const wxString& aFramenameI
365378
366 Py_DECREF( result );379 Py_DECREF( result );
367380
368 // Now there should be an object named 'makeWindow' in the dictionary that381 result = PyDict_GetItemString( globals, "retval" );
369 // we can grab a pointer to:
370 PyObject* func = PyDict_GetItemString( globals, "makeWindow" );
371 wxASSERT( PyCallable_Check( func ) );
372382
373 // Now build an argument tuple and call the Python function. Notice the383#if PY_MAJOR_VERSION >= 3
374 // use of another wxPython API to take a wxWindows object and build a384 if( !PyLong_Check( result ) )
375 // wxPython object that wraps it.385#else
386 if( !PyInt_Check( result ) )
387#endif
388 {
389 wxLogError("creation of scripting window didn't return a number");
390 return NULL;
391 }
376392
377 PyObject* arg = wxPyMake_wxObject( parent, false );393#if PY_MAJOR_VERSION >= 3
378 wxASSERT( arg != NULL );394 const long windowId = PyLong_AsLong( result );
395#else
396 const long windowId = PyInt_AsLong( result );
397#endif
379398
380 PyObject* tuple = PyTuple_New( 1 );399 // It's important not to decref globals before extracting the window id.
381 PyTuple_SET_ITEM( tuple, 0, arg );400 // If you do it early, globals, and the retval int it contains, may/will be garbage collected.
401 // We do not need to decref result, because GetItemString returns a borrowed reference.
402 Py_DECREF( globals );
382403
383 result = PyEval_CallObject( func, tuple );404 wxWindow* window = wxWindow::FindWindowById( windowId );
384405
385 // Was there an exception?406 if( !window )
386 if( !result )
387 PyErr_Print();
388 else
389 {407 {
390 // Otherwise, get the returned window out of Python-land and408 wxLogError("unable to find pyshell window with id %d", windowId);
391 // into C++-ville...409 return NULL;
392 bool success = wxPyConvertSwigPtr( result, (void**) &window, "wxWindow" );
393 (void) success;
394
395 wxASSERT_MSG( success, "Returned object was not a wxWindow!" );
396 Py_DECREF( result );
397
398 window->SetName( aFramenameId );
399 }410 }
400411
401 // Release the python objects we still have
402 Py_DECREF( globals );
403 Py_DECREF( tuple );
404
405 return window;412 return window;
406}413}
407414
diff --git a/pcbnew/swig/python_scripting.h b/pcbnew/swig/python_scripting.h
index 373cc66..2cc53b8 100644
--- a/pcbnew/swig/python_scripting.h
+++ b/pcbnew/swig/python_scripting.h
@@ -39,7 +39,11 @@
3939
40#ifndef NO_WXPYTHON_EXTENSION_HEADERS40#ifndef NO_WXPYTHON_EXTENSION_HEADERS
41#ifdef KICAD_SCRIPTING_WXPYTHON41#ifdef KICAD_SCRIPTING_WXPYTHON
42 #include <wx/wxPython/wxPython.h>42 #ifdef KICAD_SCRIPTING_WXPYTHON_PHOENIX
43 #include <wx/window.h>
44 #else
45 #include <wx/wxPython/wxPython.h>
46 #endif
43#endif47#endif
44#endif48#endif
4549

Subscribers

People subscribed via source and target branches

to status/vote changes: