From f4c8abb1b45c07b5dd9452e5fca28844bef31fe9 Mon Sep 17 00:00:00 2001 From: Miguel Angel Ajo Date: Wed, 1 Aug 2012 13:54:20 +0200 Subject: [PATCH 1/4] Double Python initialization fixed --- CMakeLists.txt | 12 +- include/appl_wxstruct.h | 19 ++- include/wxstruct.h | 4 +- pcbnew/pcbnew.cpp | 250 ++++++++++++++++++++++++++++++++- scripting/python_scripting.cpp | 29 +++- scripting/python_scripting.h | 2 +- 6 files changed, 304 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fca69e0902..215ac4e65c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,7 +56,10 @@ option(KICAD_SCRIPTING_MODULES "set this option ON to build kicad modules that can be used from scripting languages" ) - +option(KICAD_SCRIPTING_EXPERIMENT + "set this option ON to build Edwin's TEST code with linking to python and pycrust shell" + ) + #Set version option (stable or testing) @@ -136,7 +139,12 @@ endif(KICAD_SCRIPTING) if(KICAD_SCRIPTING_MODULES) add_definitions(-DKICAD_SCRIPTING_MODULES) endif(KICAD_SCRIPTING_MODULES) - + +if(KICAD_SCRIPTING_EXPERIMENT) + add_definitions(-DKICAD_SCRIPTING_EXPERIMENT) + + +endif(KICAD_SCRIPTING_EXPERIMENT) if(USE_WX_GRAPHICS_CONTEXT) add_definitions(-DUSE_WX_GRAPHICS_CONTEXT) diff --git a/include/appl_wxstruct.h b/include/appl_wxstruct.h index a30674c117..a97852a437 100644 --- a/include/appl_wxstruct.h +++ b/include/appl_wxstruct.h @@ -36,7 +36,10 @@ #include #include #include - +#ifdef KICAD_SCRIPTING_EXPERIMENT +#include +#include +#endif enum EDA_APP_T { APP_UNKNOWN_T, @@ -59,6 +62,10 @@ class wxHtmlHelpController; */ class EDA_APP : public wxApp { +#ifdef KICAD_SCRIPTING_EXPERIMENT + private: + PyThreadState* m_mainTState; +#endif protected: /// Used mainly to handle default paths libs m_Id = APP_EESCHEMA_T, APP_PCBNEW_T ... EDA_APP_T m_Id; @@ -115,8 +122,14 @@ public: * this is the first executed function (like main() ) * @return true if the application can be started. */ - bool OnInit(); - + bool OnInit(); // should this be virtual +#ifdef KICAD_SCRIPTING_EXPERIMENT + bool Init_wxPython(); +#endif + // This is only called if OnInit() returned true so it's a good place to do + // any cleanup matching the initializations done there. + //// virtual int OnExit() ;// { return(0) ; }; // TODO FIX THIS HACK MUST BE VIRTUAL + wxHtmlHelpController* GetHtmlHelpController() { return m_HtmlCtrl; } void SetHtmlHelpController( wxHtmlHelpController* aController ); diff --git a/include/wxstruct.h b/include/wxstruct.h index b50bc105f2..2bc471d242 100644 --- a/include/wxstruct.h +++ b/include/wxstruct.h @@ -470,7 +470,9 @@ public: void SetShowBorderAndTitleBlock( bool aShow ) { m_showBorderAndTitleBlock = aShow; } EDA_DRAW_PANEL* GetCanvas() { return m_canvas; } - + // HACK + // EDA_MSG_PANEL* GetMessagePanel() { return m_messagePanel; } + // end hack virtual wxString GetScreenDesc(); /** diff --git a/pcbnew/pcbnew.cpp b/pcbnew/pcbnew.cpp index 20afea14b5..ed4bf1e820 100644 --- a/pcbnew/pcbnew.cpp +++ b/pcbnew/pcbnew.cpp @@ -57,6 +57,7 @@ #include #endif + // Colors for layers and items COLORS_DESIGN_SETTINGS g_ColorsSettings; @@ -87,6 +88,7 @@ wxString g_DocModulesFileName = wxT( "footprints_doc/footprints.pdf" ); wxArrayString g_LibraryNames; +// wxWindow* DoPythonStuff(wxWindow* parent); // declaration IMPLEMENT_APP( EDA_APP ) @@ -105,6 +107,33 @@ void EDA_APP::MacOpenFile( const wxString& fileName ) frame->LoadOnePcbFile( fileName, false ); } +#ifdef KICAD_SCRIPTING_EXPERIMENT + +class MyFrame : public wxFrame { + public: + MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size); + void RedirectStdio(); + wxWindow* DoPythonStuff(wxWindow* parent); + void OnExit(wxCommandEvent& event); + void OnPyFrame(wxCommandEvent& event); + void ScriptingSetPcbEditFrame(PCB_EDIT_FRAME *aPCBEdaFrame); + private: + DECLARE_EVENT_TABLE() + PCB_EDIT_FRAME *PcbEditFrame ; +}; + +enum + { + ID_EXIT=1001, + ID_PYFRAME + }; + +BEGIN_EVENT_TABLE(MyFrame, wxFrame) + EVT_MENU(ID_EXIT, MyFrame::OnExit) + EVT_MENU(ID_PYFRAME, MyFrame::OnPyFrame) +END_EVENT_TABLE() + +#endif bool EDA_APP::OnInit() { @@ -112,10 +141,16 @@ bool EDA_APP::OnInit() PCB_EDIT_FRAME* frame = NULL; #ifdef KICAD_SCRIPTING - pcbnewInitPythonScripting(); + if ( !pcbnewInitPythonScripting(&m_mainTState) ) + { + return false; + } #endif +#ifdef KICAD_SCRIPTING_EXPERIMENT + MyFrame *zz = new MyFrame(_T("Embedded wxPython Test"),wxDefaultPosition, wxSize(700, 600)); + zz->Show(true); +#endif - InitEDA_Appl( wxT( "Pcbnew" ), APP_PCBNEW_T ); if( m_Checker && m_Checker->IsAnotherRunning() ) @@ -209,6 +244,217 @@ Changing extension to .brd." ), GetChars( fn.GetFullPath() ) ); */ frame->SetFocus(); frame->GetCanvas()->SetFocus(); +#ifdef KICAD_SCRIPTING_EXPERIMENT + zz->ScriptingSetPcbEditFrame(frame); // make the frame available to my python thing + // now to find a way to use it for something useful +#endif + return true; +} +#if 0 +// for some reason KiCad classes do not implement OnExit +// if I add it in the declaration, I need to fix it in every application +// so for now make a note TODO TODO +// we need to clean up python when the application exits +int EDA_APP::OnExit() { + // Restore the thread state and tell Python to cleanup after itself. + // wxPython will do its own cleanup as part of that process. This is done + // in OnExit instead of ~MyApp because OnExit is only called if OnInit is + // successful. +#if KICAD_SCRIPTING_EXPERIMENT + wxPyEndAllowThreads(m_mainTState); + Py_Finalize(); +#endif + return 0; +} +#endif + +#ifdef KICAD_SCRIPTING_EXPERIMENT +// stuff copied from WxPython examples +bool EDA_APP::Init_wxPython() { + // Initialize Python + Py_Initialize(); + PyEval_InitThreads(); + + // Load the wxPython core API. Imports the wx._core_ module and sets a + // local pointer to a function table located there. The pointer is used + // internally by the rest of the API functions. + if ( ! wxPyCoreAPI_IMPORT() ) { + wxLogError(wxT("***** Error importing the wxPython API! *****")); + PyErr_Print(); + Py_Finalize(); + return false; + } + + // Save the current Python thread state and release the + // Global Interpreter Lock. + m_mainTState = wxPyBeginAllowThreads(); return true; } + + +// +MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) + : wxFrame(NULL, -1, title, pos, size, + wxDEFAULT_FRAME_STYLE|wxNO_FULL_REPAINT_ON_RESIZE) +{ +// SetIcon(wxICON(mondrian)); + + wxMenuBar* mbar = new wxMenuBar; + wxMenu* menu = new wxMenu; + menu->Append(ID_PYFRAME, _T("Make wx&Python frame")); + menu->AppendSeparator(); + menu->Append(ID_EXIT, _T("&Close Frame\tAlt-X")); + mbar->Append(menu, _T("&File")); + SetMenuBar(mbar); + + CreateStatusBar(); + RedirectStdio(); + + // Make some child windows from C++ + wxSplitterWindow* sp = new wxSplitterWindow(this, -1); + wxPanel* p1 = new wxPanel(sp, -1, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER); + + new wxStaticText(p1, -1, + _T("The frame, menu, splitter, this panel and this text were created in C++..."), + wxPoint(10,10)); + + // And get a panel from Python + wxWindow* p2 = DoPythonStuff(sp); + + if (p2) + sp->SplitHorizontally(p1, p2, GetClientSize().y/4); +} + +void MyFrame::OnExit(wxCommandEvent& event) +{ + Close(); +} + +void MyFrame::ScriptingSetPcbEditFrame(PCB_EDIT_FRAME *aPCBEdaFrame) +{ + PcbEditFrame = aPCBEdaFrame; +} + +//---------------------------------------------------------------------- +// This is where the fun begins... + + +const char* python_code1 = "\ +import wx\n\ +f = wx.Frame(None, -1, 'Hello from wxPython!', size=(250, 150))\n\ +f.Show()\n\ +"; + +void MyFrame::OnPyFrame(wxCommandEvent& event) +{ + // For simple Python code that doesn't have to interact with the + // C++ code in any way, you can execute it with PyRun_SimpleString. + + + // First, whenever you do anything with Python objects or code, you + // *MUST* aquire the Global Interpreter Lock and block other + // Python threads from running. + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + + // Execute the code in the __main__ module + PyRun_SimpleString(python_code1); + + // Finally, release the GIL and let other Python threads run. + wxPyEndBlockThreads(blocked); +} + + +void MyFrame::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.stdin = sys.stderr = output\n"; + + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + PyRun_SimpleString(python_redirect); + wxPyEndBlockThreads(blocked); +} + +const char* python_code2 = "\ +import sys\nimport os\n\ +sys.path.append(os.path.expanduser('~/.kicad_plugins'))\n\ +import embedded_sample\n\ +\n\ +def makeWindow(parent):\n\ + win = embedded_sample.MyPanel(parent)\n\ + return win\n\ +"; + +wxWindow* MyFrame::DoPythonStuff(wxWindow* parent) +{ + // More complex embedded situations will require passing C++ objects to + // Python and/or returning objects from Python to be used in C++. This + // sample shows one way to do it. NOTE: The above code could just have + // easily come from a file, or the whole thing could be in the Python + // module that is imported and manipulated directly in this C++ code. See + // the Python API for more details. + + wxWindow* window = NULL; + PyObject* result; + + // As always, first grab the GIL + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + + // Now make a dictionary to serve as the global namespace when the code is + // executed. Put a reference to the builtins module in it. (Yes, the + // names are supposed to be different, I don't know why...) + PyObject* globals = PyDict_New(); + PyObject* builtins = PyImport_ImportModule("__builtin__"); + PyDict_SetItemString(globals, "__builtins__", builtins); + Py_DECREF(builtins); + + // Execute the code to make the makeWindow function + result = PyRun_String(python_code2, Py_file_input, globals, globals); + // Was there an exception? + if (! result) { + PyErr_Print(); + wxPyEndBlockThreads(blocked); + return NULL; + } + 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)); + + // 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 { + // Otherwise, get the returned window out of Python-land and + // into C++-ville... + bool success = wxPyConvertSwigPtr(result, (void**)&window, _T("wxWindow")); + wxASSERT_MSG(success, _T("Returned object was not a wxWindow!")); + Py_DECREF(result); + } + + // Release the python objects we still have + Py_DECREF(globals); + Py_DECREF(tuple); + + // Finally, after all Python stuff is done, release the GIL + wxPyEndBlockThreads(blocked); + + return window; +} + +#endif diff --git a/scripting/python_scripting.cpp b/scripting/python_scripting.cpp index 90b6807e7d..8c3ec062c8 100644 --- a/scripting/python_scripting.cpp +++ b/scripting/python_scripting.cpp @@ -28,6 +28,7 @@ */ #include +#include #include #include @@ -37,7 +38,7 @@ extern "C" void init_kicad( void ); extern "C" void init_pcbnew( void ); -#define EXTRA_PYTHON_MODULES 2 // this is the number of python +#define EXTRA_PYTHON_MODULES 10 // this is the number of python // modules that we want to add into the list @@ -112,21 +113,43 @@ static void swigSwitchPythonBuiltin() /* Function pcbnewInitPythonScripting * Initializes all the python environment and publish our interface inside it + * initializes all the wxpython interface, and returns the python thread control structure * */ -void pcbnewInitPythonScripting() +bool pcbnewInitPythonScripting(PyThreadState** aMainTState) { + swigAddBuiltin(); // add builtin functions swigAddModules(); // add our own modules swigSwitchPythonBuiltin(); // switch the python builtin modules to our new list - Py_Initialize(); // call the python library to get it initialized + Py_Initialize(); + PyEval_InitThreads(); + + // Load the wxPython core API. Imports the wx._core_ module and sets a + // local pointer to a function table located there. The pointer is used + // internally by the rest of the API functions. + if ( ! wxPyCoreAPI_IMPORT() ) { + wxLogError(wxT("***** Error importing the wxPython API! *****")); + PyErr_Print(); + Py_Finalize(); + return false; + } + + // Save the current Python thread state and release the + // Global Interpreter Lock. + + *aMainTState = wxPyBeginAllowThreads(); // load pcbnew inside python, and load all the user plugins, TODO: add system wide plugins + wxPyBlock_t blocked = wxPyBeginBlockThreads(); PyRun_SimpleString( "import sys\n" "sys.path.append(\".\")\n" "import pcbnew\n" "pcbnew.LoadPlugins()" ); + wxPyEndBlockThreads(blocked); + + return true; } diff --git a/scripting/python_scripting.h b/scripting/python_scripting.h index 9ebafd0126..420c65b79d 100644 --- a/scripting/python_scripting.h +++ b/scripting/python_scripting.h @@ -7,6 +7,6 @@ * Initializes the Python engine inside pcbnew */ -void pcbnewInitPythonScripting( void ); +bool pcbnewInitPythonScripting(PyThreadState** aMainTState); #endif From 1960a8768eab4d1c365131eb2987f61adb6451f7 Mon Sep 17 00:00:00 2001 From: Miguel Angel Ajo Date: Wed, 1 Aug 2012 14:50:21 +0200 Subject: [PATCH 2/4] pcbnew: wxPyBeginBlockThreads before every python call --- include/appl_wxstruct.h | 2 +- pcbnew/scripting/pcbnew_footprint_wizards.cpp | 41 +++++++++++++++---- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/include/appl_wxstruct.h b/include/appl_wxstruct.h index a97852a437..06823b5c68 100644 --- a/include/appl_wxstruct.h +++ b/include/appl_wxstruct.h @@ -37,7 +37,7 @@ #include #include #ifdef KICAD_SCRIPTING_EXPERIMENT -#include +#include #include #endif diff --git a/pcbnew/scripting/pcbnew_footprint_wizards.cpp b/pcbnew/scripting/pcbnew_footprint_wizards.cpp index 3dd77c0305..c14b8c20c7 100644 --- a/pcbnew/scripting/pcbnew_footprint_wizards.cpp +++ b/pcbnew/scripting/pcbnew_footprint_wizards.cpp @@ -4,6 +4,7 @@ */ #include "pcbnew_footprint_wizards.h" +#include #include PYTHON_FOOTPRINT_WIZARD::PYTHON_FOOTPRINT_WIZARD(PyObject *aWizard) @@ -27,7 +28,9 @@ PyObject* PYTHON_FOOTPRINT_WIZARD::CallMethod(const char* aMethod, PyObject *aAr if ( pFunc && PyCallable_Check( pFunc ) ) { PyObject *result; - + + wxPyBlock_t blocked = wxPyBeginBlockThreads(); + result = PyObject_CallObject( pFunc, aArglist ); if ( PyErr_Occurred() ) @@ -38,6 +41,8 @@ PyObject* PYTHON_FOOTPRINT_WIZARD::CallMethod(const char* aMethod, PyObject *aAr printf ( "Exception: %s\n", PyString_AsString( PyObject_Str(v) ) ); printf ( " : %s\n", PyString_AsString( PyObject_Str(b) ) ); } + + wxPyEndBlockThreads( blocked ); if ( result ) { @@ -65,9 +70,12 @@ wxString PYTHON_FOOTPRINT_WIZARD::CallRetStrMethod( const char* aMethod, PyObjec PyObject *result = CallMethod( aMethod, aArglist ); if ( result ) { + wxPyBlock_t blocked = wxPyBeginBlockThreads(); const char *str_res = PyString_AsString( result ); ret = wxString::FromUTF8( str_res ); Py_DECREF( result ); + + wxPyEndBlockThreads( blocked ); } return ret; } @@ -90,6 +98,8 @@ wxArrayString PYTHON_FOOTPRINT_WIZARD::CallRetArrayStrMethod "result is not a list"),1 ); return ret; } + + wxPyBlock_t blocked = wxPyBeginBlockThreads(); int list_size = PyList_Size( result ); for ( int n=0; nGetName().mb_str() - ); + + //printf( "Registered python footprint wizard '%s'\n", + // (const char*)fw->GetName().mb_str() + // ); // this get the wizard registered in the common // FOOTPRINT_WIZARDS class From 02ced9a339e3ca6aa3662286d4ec9a1d04aa1d89 Mon Sep 17 00:00:00 2001 From: Miguel Angel Ajo Date: Thu, 2 Aug 2012 09:47:30 +0200 Subject: [PATCH 3/4] scripting: fixed kicad compilation, cleanups --- CMakeLists.txt | 13 ++++++ include/appl_wxstruct.h | 17 ++----- pcbnew/CMakeLists.txt | 6 --- pcbnew/pcbnew.cpp | 85 +++++++++++++++------------------- scripting/python_scripting.cpp | 15 +++++- scripting/python_scripting.h | 18 ++++++- 6 files changed, 82 insertions(+), 72 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 215ac4e65c..42addd2866 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -256,6 +256,19 @@ if(WIN32 AND USE_WX_GRAPHICS_CONTEXT) check_find_package_result(GDI_PLUS_FOUND "GDI+") endif(WIN32 AND USE_WX_GRAPHICS_CONTEXT) +# Find Python and other scripting resources +if (KICAD_SCRIPTING OR KICAD_SCRIPTING_MODULES) + + execute_process(COMMAND python2 -c "import sys;print\"%s.%s\"%sys.version_info[0:2]" OUTPUT_VARIABLE PYTHON_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) + set(PYTHON_DEST "lib/python${PYTHON_VERSION}/dist-packages" ) + find_package(PythonLibs) + include_directories(${PYTHON_INCLUDE_PATH} + ./scripting) + +endif(KICAD_SCRIPTING OR KICAD_SCRIPTING_MODULES) + + + # Automagically create version header file. include(CreateBzrVersionHeader) create_bzr_version_header() diff --git a/include/appl_wxstruct.h b/include/appl_wxstruct.h index 06823b5c68..8358d8c74f 100644 --- a/include/appl_wxstruct.h +++ b/include/appl_wxstruct.h @@ -36,10 +36,7 @@ #include #include #include -#ifdef KICAD_SCRIPTING_EXPERIMENT -#include -#include -#endif + enum EDA_APP_T { APP_UNKNOWN_T, @@ -62,10 +59,7 @@ class wxHtmlHelpController; */ class EDA_APP : public wxApp { -#ifdef KICAD_SCRIPTING_EXPERIMENT - private: - PyThreadState* m_mainTState; -#endif + protected: /// Used mainly to handle default paths libs m_Id = APP_EESCHEMA_T, APP_PCBNEW_T ... EDA_APP_T m_Id; @@ -123,12 +117,6 @@ public: * @return true if the application can be started. */ bool OnInit(); // should this be virtual -#ifdef KICAD_SCRIPTING_EXPERIMENT - bool Init_wxPython(); -#endif - // This is only called if OnInit() returned true so it's a good place to do - // any cleanup matching the initializations done there. - //// virtual int OnExit() ;// { return(0) ; }; // TODO FIX THIS HACK MUST BE VIRTUAL wxHtmlHelpController* GetHtmlHelpController() { return m_HtmlCtrl; } @@ -421,6 +409,7 @@ public: * @param aIndex = insertion point */ void InsertLibraryPath( const wxString& aPaths, size_t aIndex ); + }; /* diff --git a/pcbnew/CMakeLists.txt b/pcbnew/CMakeLists.txt index 26106ffdf6..17ccf619cb 100644 --- a/pcbnew/CMakeLists.txt +++ b/pcbnew/CMakeLists.txt @@ -1,14 +1,9 @@ add_definitions(-DPCBNEW) if (KICAD_SCRIPTING OR KICAD_SCRIPTING_MODULES) - EXECUTE_PROCESS(COMMAND python2 -c "import sys;print\"%s.%s\"%sys.version_info[0:2]" OUTPUT_VARIABLE PYTHON_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE) - SET(PYTHON_DEST "lib/python${PYTHON_VERSION}/dist-packages" ) file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/scripting) FIND_PACKAGE(SWIG REQUIRED) INCLUDE(${SWIG_USE_FILE}) - FIND_PACKAGE(PythonLibs) - INCLUDE_DIRECTORIES(${PYTHON_INCLUDE_PATH}) - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -DKICAD_SCRIPTING -DKICAD_SCRIPTING_MODULES") endif(KICAD_SCRIPTING OR KICAD_SCRIPTING_MODULES) @@ -25,7 +20,6 @@ include_directories( ../polygon ../common/dialogs ./scripting - ../scripting ${INC_AFTER} ) diff --git a/pcbnew/pcbnew.cpp b/pcbnew/pcbnew.cpp index ed4bf1e820..de0e01cf8b 100644 --- a/pcbnew/pcbnew.cpp +++ b/pcbnew/pcbnew.cpp @@ -29,6 +29,7 @@ */ #ifdef KICAD_SCRIPTING +#include #include #endif #include @@ -51,12 +52,6 @@ #include #include -#include - -#ifdef KICAD_SCRIPTING -#include -#endif - // Colors for layers and items COLORS_DESIGN_SETTINGS g_ColorsSettings; @@ -141,15 +136,11 @@ bool EDA_APP::OnInit() PCB_EDIT_FRAME* frame = NULL; #ifdef KICAD_SCRIPTING - if ( !pcbnewInitPythonScripting(&m_mainTState) ) + if ( !pcbnewInitPythonScripting() ) { - return false; + return false; } #endif -#ifdef KICAD_SCRIPTING_EXPERIMENT - MyFrame *zz = new MyFrame(_T("Embedded wxPython Test"),wxDefaultPosition, wxSize(700, 600)); - zz->Show(true); -#endif InitEDA_Appl( wxT( "Pcbnew" ), APP_PCBNEW_T ); @@ -245,6 +236,11 @@ Changing extension to .brd." ), GetChars( fn.GetFullPath() ) ); frame->SetFocus(); frame->GetCanvas()->SetFocus(); #ifdef KICAD_SCRIPTING_EXPERIMENT + + MyFrame *zz = new MyFrame(_T("Embedded wxPython Test"),wxDefaultPosition, wxSize(700, 600)); + zz->Show(true); + + zz->ScriptingSetPcbEditFrame(frame); // make the frame available to my python thing // now to find a way to use it for something useful #endif @@ -261,39 +257,14 @@ int EDA_APP::OnExit() { // in OnExit instead of ~MyApp because OnExit is only called if OnInit is // successful. #if KICAD_SCRIPTING_EXPERIMENT - wxPyEndAllowThreads(m_mainTState); - Py_Finalize(); + pcbnewFinishPythonScripting(); #endif return 0; } #endif #ifdef KICAD_SCRIPTING_EXPERIMENT -// stuff copied from WxPython examples -bool EDA_APP::Init_wxPython() { - // Initialize Python - Py_Initialize(); - PyEval_InitThreads(); - // Load the wxPython core API. Imports the wx._core_ module and sets a - // local pointer to a function table located there. The pointer is used - // internally by the rest of the API functions. - if ( ! wxPyCoreAPI_IMPORT() ) { - wxLogError(wxT("***** Error importing the wxPython API! *****")); - PyErr_Print(); - Py_Finalize(); - return false; - } - - // Save the current Python thread state and release the - // Global Interpreter Lock. - m_mainTState = wxPyBeginAllowThreads(); - - return true; -} - - -// MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) : wxFrame(NULL, -1, title, pos, size, wxDEFAULT_FRAME_STYLE|wxNO_FULL_REPAINT_ON_RESIZE) @@ -379,18 +350,35 @@ sys.stdin = sys.stderr = output\n"; wxPyEndBlockThreads(blocked); } -const char* python_code2 = "\ -import sys\nimport os\n\ -sys.path.append(os.path.expanduser('~/.kicad_plugins'))\n\ -import embedded_sample\n\ -\n\ -def makeWindow(parent):\n\ - win = embedded_sample.MyPanel(parent)\n\ - return win\n\ -"; + wxWindow* MyFrame::DoPythonStuff(wxWindow* parent) { + +const char* pycrust_panel = "\ +import wx\n\ +from wx.py import shell, version\n\ +\n\ +class MyPanel(wx.Panel):\n\ +\tdef __init__(self, parent):\n\ +\t\twx.Panel.__init__(self, parent, -1, style=wx.SUNKEN_BORDER)\n\ +\t\t\n\ +\t\ttext = wx.StaticText(self, -1,\n\ +\t\t\t\t\"Everything on this side of the splitter comes from Python.\")\n\ +\t\t\n\ +\t\tintro = \"Welcome To PyCrust %s - KiCad's Python Shell\" % version.VERSION\n\ +\t\tpycrust = shell.Shell(self, -1, introText=intro)\n\ +\t\t\n\ +\t\tsizer = wx.BoxSizer(wx.VERTICAL)\n\n\ +\t\tsizer.Add(text, 0, wx.EXPAND|wx.ALL, 10)\n\n\ +\t\tsizer.Add(pycrust, 1, wx.EXPAND|wx.BOTTOM|wx.LEFT|wx.RIGHT, 10)\n\n\ +\t\tself.SetSizer(sizer)\n\n\ +\n\ +def makeWindow(parent):\n\ + win = MyPanel(parent)\n\ + return win\n\ +"; + // More complex embedded situations will require passing C++ objects to // Python and/or returning objects from Python to be used in C++. This // sample shows one way to do it. NOTE: The above code could just have @@ -413,7 +401,7 @@ wxWindow* MyFrame::DoPythonStuff(wxWindow* parent) Py_DECREF(builtins); // Execute the code to make the makeWindow function - result = PyRun_String(python_code2, Py_file_input, globals, globals); + result = PyRun_String(pycrust_panel, Py_file_input, globals, globals); // Was there an exception? if (! result) { PyErr_Print(); @@ -443,6 +431,7 @@ wxWindow* MyFrame::DoPythonStuff(wxWindow* parent) // Otherwise, get the returned window out of Python-land and // into C++-ville... bool success = wxPyConvertSwigPtr(result, (void**)&window, _T("wxWindow")); + (void)success; wxASSERT_MSG(success, _T("Returned object was not a wxWindow!")); Py_DECREF(result); } diff --git a/scripting/python_scripting.cpp b/scripting/python_scripting.cpp index 8c3ec062c8..d76b79633c 100644 --- a/scripting/python_scripting.cpp +++ b/scripting/python_scripting.cpp @@ -116,7 +116,10 @@ static void swigSwitchPythonBuiltin() * initializes all the wxpython interface, and returns the python thread control structure * */ -bool pcbnewInitPythonScripting(PyThreadState** aMainTState) + +PyThreadState *g_PythonMainTState; + +bool pcbnewInitPythonScripting() { swigAddBuiltin(); // add builtin functions @@ -139,7 +142,7 @@ bool pcbnewInitPythonScripting(PyThreadState** aMainTState) // Save the current Python thread state and release the // Global Interpreter Lock. - *aMainTState = wxPyBeginAllowThreads(); + g_PythonMainTState = wxPyBeginAllowThreads(); // load pcbnew inside python, and load all the user plugins, TODO: add system wide plugins @@ -153,3 +156,11 @@ bool pcbnewInitPythonScripting(PyThreadState** aMainTState) return true; } + +void pcbnewFinishPythonScripting() +{ + + wxPyEndAllowThreads(g_PythonMainTState); + Py_Finalize(); + +} diff --git a/scripting/python_scripting.h b/scripting/python_scripting.h index 420c65b79d..6d28be4b10 100644 --- a/scripting/python_scripting.h +++ b/scripting/python_scripting.h @@ -1,12 +1,26 @@ #ifndef __PYTHON_SCRIPTING_H #define __PYTHON_SCRIPTING_H -#include + // undefs explained here: https://bugzilla.redhat.com/show_bug.cgi?id=427617 + + #ifdef _POSIX_C_SOURCE + #undef _POSIX_C_SOURCE + #endif + #ifdef _XOPEN_SOURCE + #undef _XOPEN_SOURCE + #endif + + #include + #ifndef NO_WXPYTHON_EXTENSION_HEADERS + #include + #endif + /* Function pcbnewInitPythonScripting * Initializes the Python engine inside pcbnew */ -bool pcbnewInitPythonScripting(PyThreadState** aMainTState); +bool pcbnewInitPythonScripting(); +void pcbnewFinishPythonScripting(); #endif From e469a6b3d8b76846485726a1e74065755067d640 Mon Sep 17 00:00:00 2001 From: Miguel Angel Ajo Date: Thu, 2 Aug 2012 19:24:53 +0200 Subject: [PATCH 4/4] pcbnew scripting: cleanup, renamed KICAD_SCRIPTING_EXPERIMENT to KICAD_SCRIPTING_WXPYTHON, now every flavour compiles --- CMakeLists.txt | 10 +- include/wxPcbStruct.h | 5 + include/wxstruct.h | 13 + pcbnew/pcbframe.cpp | 23 +- pcbnew/pcbnew.cpp | 225 +----------------- pcbnew/scripting/pcbnew_footprint_wizards.cpp | 30 ++- scripting/python_scripting.cpp | 136 ++++++++++- scripting/python_scripting.h | 19 ++ 8 files changed, 217 insertions(+), 244 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 42addd2866..81be2b1b93 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,8 +56,8 @@ option(KICAD_SCRIPTING_MODULES "set this option ON to build kicad modules that can be used from scripting languages" ) -option(KICAD_SCRIPTING_EXPERIMENT - "set this option ON to build Edwin's TEST code with linking to python and pycrust shell" +option(KICAD_SCRIPTING_WXPYTHON + "set this option ON to build wxpython implementation for wx interface building in python and py.shell" ) @@ -140,11 +140,11 @@ if(KICAD_SCRIPTING_MODULES) add_definitions(-DKICAD_SCRIPTING_MODULES) endif(KICAD_SCRIPTING_MODULES) -if(KICAD_SCRIPTING_EXPERIMENT) - add_definitions(-DKICAD_SCRIPTING_EXPERIMENT) +if(KICAD_SCRIPTING_WXPYTHON) + add_definitions(-DKICAD_SCRIPTING_WXPYTHON) -endif(KICAD_SCRIPTING_EXPERIMENT) +endif(KICAD_SCRIPTING_WXPYTHON) if(USE_WX_GRAPHICS_CONTEXT) add_definitions(-DUSE_WX_GRAPHICS_CONTEXT) diff --git a/include/wxPcbStruct.h b/include/wxPcbStruct.h index e6ba96a238..086e604889 100644 --- a/include/wxPcbStruct.h +++ b/include/wxPcbStruct.h @@ -86,6 +86,11 @@ class PCB_EDIT_FRAME : public PCB_BASE_FRAME protected: +#ifdef KICAD_SCRIPTING_WXPYTHON + // Panel used to let user talk with internal scripting + wxWindow* m_pythonPanel; +#endif + PCB_LAYER_WIDGET* m_Layers; DRC* m_drc; ///< the DRC controller, see drc.cpp diff --git a/include/wxstruct.h b/include/wxstruct.h index 2bc471d242..3b18590ef1 100644 --- a/include/wxstruct.h +++ b/include/wxstruct.h @@ -1074,6 +1074,19 @@ public: return *this; } + /** + * Function ScriptingToolbarPane + * Change *this to a scripting toolbar for KiCad. + */ + EDA_PANEINFO& ScriptingToolbarPane() + { + CloseButton( false ); + Floatable( true ); + Resizable( true ); + return *this; + } + + }; #endif // WXSTRUCT_H_ diff --git a/pcbnew/pcbframe.cpp b/pcbnew/pcbframe.cpp index 2af5e03aff..0e6f8c6ff7 100644 --- a/pcbnew/pcbframe.cpp +++ b/pcbnew/pcbframe.cpp @@ -53,6 +53,9 @@ #include #include +#ifdef KICAD_SCRIPTING +#include +#endif // Keys used in read/write config #define OPTKEY_DEFAULT_LINEWIDTH_VALUE wxT( "PlotLineWidth" ) @@ -280,7 +283,9 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( wxWindow* parent, const wxString& title, m_RecordingMacros = -1; m_microWaveToolBar = NULL; m_autoPlaceModeId = 0; - +#ifdef KICAD_SCRIPTING_WXPYTHON + m_pythonPanel = NULL; +#endif for ( int i = 0; i < 10; i++ ) m_Macros[i].m_Record.clear(); @@ -397,6 +402,22 @@ PCB_EDIT_FRAME::PCB_EDIT_FRAME( wxWindow* parent, const wxString& title, m_auimgr.AddPane( m_messagePanel, wxAuiPaneInfo( mesg ).Name( wxT( "MsgPanel" ) ).Bottom().Layer(10) ); + + #ifdef KICAD_SCRIPTING_WXPYTHON + // Add the scripting panel + EDA_PANEINFO pythonAuiInfo; + pythonAuiInfo.ScriptingToolbarPane(); + pythonAuiInfo.Caption( wxT( "Python Scripting" ) ); + pythonAuiInfo.MinSize( wxSize( 200, 100 ) ); + pythonAuiInfo.BestSize( wxSize( GetClientSize().x/2, 200 ) ); + + m_pythonPanel = CreatePythonShellWindow( this ); + m_auimgr.AddPane( m_pythonPanel, + pythonAuiInfo.Name( wxT( "PythonPanel" ) ).Bottom().Layer(9) ); + + #endif + + ReFillLayerWidget(); // this is near end because contents establish size m_Layers->ReFillRender(); // Update colors in Render after the config is read diff --git a/pcbnew/pcbnew.cpp b/pcbnew/pcbnew.cpp index de0e01cf8b..bfa325e1ed 100644 --- a/pcbnew/pcbnew.cpp +++ b/pcbnew/pcbnew.cpp @@ -102,34 +102,6 @@ void EDA_APP::MacOpenFile( const wxString& fileName ) frame->LoadOnePcbFile( fileName, false ); } -#ifdef KICAD_SCRIPTING_EXPERIMENT - -class MyFrame : public wxFrame { - public: - MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size); - void RedirectStdio(); - wxWindow* DoPythonStuff(wxWindow* parent); - void OnExit(wxCommandEvent& event); - void OnPyFrame(wxCommandEvent& event); - void ScriptingSetPcbEditFrame(PCB_EDIT_FRAME *aPCBEdaFrame); - private: - DECLARE_EVENT_TABLE() - PCB_EDIT_FRAME *PcbEditFrame ; -}; - -enum - { - ID_EXIT=1001, - ID_PYFRAME - }; - -BEGIN_EVENT_TABLE(MyFrame, wxFrame) - EVT_MENU(ID_EXIT, MyFrame::OnExit) - EVT_MENU(ID_PYFRAME, MyFrame::OnPyFrame) -END_EVENT_TABLE() - -#endif - bool EDA_APP::OnInit() { wxFileName fn; @@ -235,17 +207,10 @@ Changing extension to .brd." ), GetChars( fn.GetFullPath() ) ); */ frame->SetFocus(); frame->GetCanvas()->SetFocus(); -#ifdef KICAD_SCRIPTING_EXPERIMENT - MyFrame *zz = new MyFrame(_T("Embedded wxPython Test"),wxDefaultPosition, wxSize(700, 600)); - zz->Show(true); - - - zz->ScriptingSetPcbEditFrame(frame); // make the frame available to my python thing - // now to find a way to use it for something useful -#endif return true; } + #if 0 // for some reason KiCad classes do not implement OnExit // if I add it in the declaration, I need to fix it in every application @@ -256,194 +221,12 @@ int EDA_APP::OnExit() { // wxPython will do its own cleanup as part of that process. This is done // in OnExit instead of ~MyApp because OnExit is only called if OnInit is // successful. -#if KICAD_SCRIPTING_EXPERIMENT +#if KICAD_SCRIPTING_WXPYTHON pcbnewFinishPythonScripting(); #endif return 0; } -#endif - -#ifdef KICAD_SCRIPTING_EXPERIMENT - -MyFrame::MyFrame(const wxString& title, const wxPoint& pos, const wxSize& size) - : wxFrame(NULL, -1, title, pos, size, - wxDEFAULT_FRAME_STYLE|wxNO_FULL_REPAINT_ON_RESIZE) -{ -// SetIcon(wxICON(mondrian)); - - wxMenuBar* mbar = new wxMenuBar; - wxMenu* menu = new wxMenu; - menu->Append(ID_PYFRAME, _T("Make wx&Python frame")); - menu->AppendSeparator(); - menu->Append(ID_EXIT, _T("&Close Frame\tAlt-X")); - mbar->Append(menu, _T("&File")); - SetMenuBar(mbar); - - CreateStatusBar(); - RedirectStdio(); - - // Make some child windows from C++ - wxSplitterWindow* sp = new wxSplitterWindow(this, -1); - wxPanel* p1 = new wxPanel(sp, -1, wxDefaultPosition, wxDefaultSize, wxSUNKEN_BORDER); - - new wxStaticText(p1, -1, - _T("The frame, menu, splitter, this panel and this text were created in C++..."), - wxPoint(10,10)); - - // And get a panel from Python - wxWindow* p2 = DoPythonStuff(sp); - - if (p2) - sp->SplitHorizontally(p1, p2, GetClientSize().y/4); -} - -void MyFrame::OnExit(wxCommandEvent& event) -{ - Close(); -} - -void MyFrame::ScriptingSetPcbEditFrame(PCB_EDIT_FRAME *aPCBEdaFrame) -{ - PcbEditFrame = aPCBEdaFrame; -} - -//---------------------------------------------------------------------- -// This is where the fun begins... - - -const char* python_code1 = "\ -import wx\n\ -f = wx.Frame(None, -1, 'Hello from wxPython!', size=(250, 150))\n\ -f.Show()\n\ -"; - -void MyFrame::OnPyFrame(wxCommandEvent& event) -{ - // For simple Python code that doesn't have to interact with the - // C++ code in any way, you can execute it with PyRun_SimpleString. - - - // First, whenever you do anything with Python objects or code, you - // *MUST* aquire the Global Interpreter Lock and block other - // Python threads from running. - wxPyBlock_t blocked = wxPyBeginBlockThreads(); - - // Execute the code in the __main__ module - PyRun_SimpleString(python_code1); - - // Finally, release the GIL and let other Python threads run. - wxPyEndBlockThreads(blocked); -} - - -void MyFrame::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.stdin = sys.stderr = output\n"; - - wxPyBlock_t blocked = wxPyBeginBlockThreads(); - PyRun_SimpleString(python_redirect); - wxPyEndBlockThreads(blocked); -} - - - -wxWindow* MyFrame::DoPythonStuff(wxWindow* parent) -{ - -const char* pycrust_panel = "\ -import wx\n\ -from wx.py import shell, version\n\ -\n\ -class MyPanel(wx.Panel):\n\ -\tdef __init__(self, parent):\n\ -\t\twx.Panel.__init__(self, parent, -1, style=wx.SUNKEN_BORDER)\n\ -\t\t\n\ -\t\ttext = wx.StaticText(self, -1,\n\ -\t\t\t\t\"Everything on this side of the splitter comes from Python.\")\n\ -\t\t\n\ -\t\tintro = \"Welcome To PyCrust %s - KiCad's Python Shell\" % version.VERSION\n\ -\t\tpycrust = shell.Shell(self, -1, introText=intro)\n\ -\t\t\n\ -\t\tsizer = wx.BoxSizer(wx.VERTICAL)\n\n\ -\t\tsizer.Add(text, 0, wx.EXPAND|wx.ALL, 10)\n\n\ -\t\tsizer.Add(pycrust, 1, wx.EXPAND|wx.BOTTOM|wx.LEFT|wx.RIGHT, 10)\n\n\ -\t\tself.SetSizer(sizer)\n\n\ -\n\ -def makeWindow(parent):\n\ - win = MyPanel(parent)\n\ - return win\n\ -"; - - // More complex embedded situations will require passing C++ objects to - // Python and/or returning objects from Python to be used in C++. This - // sample shows one way to do it. NOTE: The above code could just have - // easily come from a file, or the whole thing could be in the Python - // module that is imported and manipulated directly in this C++ code. See - // the Python API for more details. - - wxWindow* window = NULL; - PyObject* result; - - // As always, first grab the GIL - wxPyBlock_t blocked = wxPyBeginBlockThreads(); - - // Now make a dictionary to serve as the global namespace when the code is - // executed. Put a reference to the builtins module in it. (Yes, the - // names are supposed to be different, I don't know why...) - PyObject* globals = PyDict_New(); - PyObject* builtins = PyImport_ImportModule("__builtin__"); - PyDict_SetItemString(globals, "__builtins__", builtins); - Py_DECREF(builtins); - - // Execute the code to make the makeWindow function - result = PyRun_String(pycrust_panel, Py_file_input, globals, globals); - // Was there an exception? - if (! result) { - PyErr_Print(); - wxPyEndBlockThreads(blocked); - return NULL; - } - 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)); - - // 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 { - // Otherwise, get the returned window out of Python-land and - // into C++-ville... - bool success = wxPyConvertSwigPtr(result, (void**)&window, _T("wxWindow")); - (void)success; - wxASSERT_MSG(success, _T("Returned object was not a wxWindow!")); - Py_DECREF(result); - } - - // Release the python objects we still have - Py_DECREF(globals); - Py_DECREF(tuple); - - // Finally, after all Python stuff is done, release the GIL - wxPyEndBlockThreads(blocked); - - return window; -} #endif + + diff --git a/pcbnew/scripting/pcbnew_footprint_wizards.cpp b/pcbnew/scripting/pcbnew_footprint_wizards.cpp index c14b8c20c7..3c59c2502a 100644 --- a/pcbnew/scripting/pcbnew_footprint_wizards.cpp +++ b/pcbnew/scripting/pcbnew_footprint_wizards.cpp @@ -4,7 +4,8 @@ */ #include "pcbnew_footprint_wizards.h" -#include +#include + #include PYTHON_FOOTPRINT_WIZARD::PYTHON_FOOTPRINT_WIZARD(PyObject *aWizard) @@ -29,7 +30,7 @@ PyObject* PYTHON_FOOTPRINT_WIZARD::CallMethod(const char* aMethod, PyObject *aAr { PyObject *result; - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + PY_BLOCK_THREADS( blocked ); result = PyObject_CallObject( pFunc, aArglist ); @@ -42,7 +43,7 @@ PyObject* PYTHON_FOOTPRINT_WIZARD::CallMethod(const char* aMethod, PyObject *aAr printf ( " : %s\n", PyString_AsString( PyObject_Str(b) ) ); } - wxPyEndBlockThreads( blocked ); + PY_UNBLOCK_THREADS( blocked ); if ( result ) { @@ -70,12 +71,12 @@ wxString PYTHON_FOOTPRINT_WIZARD::CallRetStrMethod( const char* aMethod, PyObjec PyObject *result = CallMethod( aMethod, aArglist ); if ( result ) { - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + PY_BLOCK_THREADS( blocked ); const char *str_res = PyString_AsString( result ); ret = wxString::FromUTF8( str_res ); Py_DECREF( result ); - wxPyEndBlockThreads( blocked ); + PY_UNBLOCK_THREADS( blocked ); } return ret; } @@ -99,7 +100,8 @@ wxArrayString PYTHON_FOOTPRINT_WIZARD::CallRetArrayStrMethod return ret; } - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + PY_BLOCK_THREADS( blocked ); + int list_size = PyList_Size( result ); for ( int n=0; n -#include #include #include +#ifdef __GNUG__ +#pragma implementation +#endif + +#include +#include +#include +#include /* init functions defined by swig */ @@ -127,6 +134,8 @@ bool pcbnewInitPythonScripting() swigSwitchPythonBuiltin(); // switch the python builtin modules to our new list Py_Initialize(); + + #ifdef KICAD_SCRIPTING_WXPYTHON PyEval_InitThreads(); // Load the wxPython core API. Imports the wx._core_ module and sets a @@ -146,21 +155,140 @@ bool pcbnewInitPythonScripting() // load pcbnew inside python, and load all the user plugins, TODO: add system wide plugins - wxPyBlock_t blocked = wxPyBeginBlockThreads(); + PY_BLOCK_THREADS( blocked ); + #endif PyRun_SimpleString( "import sys\n" "sys.path.append(\".\")\n" "import pcbnew\n" "pcbnew.LoadPlugins()" ); - wxPyEndBlockThreads(blocked); + PY_UNBLOCK_THREADS( blocked ); return true; } void pcbnewFinishPythonScripting() { - +#ifdef KICAD_SCRIPTING_WXPYTHON wxPyEndAllowThreads(g_PythonMainTState); +#endif Py_Finalize(); } + +#ifdef KICAD_SCRIPTING_WXPYTHON + +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\ +c sys.stderr = output\n"; + + PY_BLOCK_THREADS( blocked ); + PyRun_SimpleString( python_redirect ); + PY_UNBLOCK_THREADS( blocked ); +} + + + +wxWindow* CreatePythonShellWindow(wxWindow* parent) +{ + +const char* pycrust_panel = "\ +import wx\n\ +from wx.py import shell, version\n\ +\n\ +class PyCrustPanel(wx.Panel):\n\ +\tdef __init__(self, parent):\n\ +\t\twx.Panel.__init__(self, parent, -1, style=wx.SUNKEN_BORDER)\n\ +\t\t\n\ +\t\t\n\ +\t\tintro = \"Welcome To PyCrust %s - KiCAD Python Shell\" % version.VERSION\n\ +\t\tpycrust = shell.Shell(self, -1, introText=intro)\n\ +\t\t\n\ +\t\tsizer = wx.BoxSizer(wx.VERTICAL)\n\n\ +\t\tsizer.Add(pycrust, 1, wx.EXPAND|wx.BOTTOM|wx.LEFT|wx.RIGHT, 10)\n\n\ +\t\tself.SetSizer(sizer)\n\n\ +\n\ +def makeWindow(parent):\n\ + win = PyCrustPanel(parent)\n\ + return win\n\ +"; + + + wxWindow* window = NULL; + PyObject* result; + + // As always, first grab the GIL + PY_BLOCK_THREADS( blocked ); + + // 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( "__builtin__" ); + PyDict_SetItemString( globals, "__builtins__", builtins ); + Py_DECREF(builtins); + + // Execute the code to make the makeWindow function we defined above + result = PyRun_String( pycrust_panel, Py_file_input, globals, globals ); + + // Was there an exception? + if ( !result ) + { + PyErr_Print(); + PY_UNBLOCK_THREADS( blocked ); + return NULL; + } + 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 ) ); + + // 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 + { + // Otherwise, get the returned window out of Python-land and + // into C++-ville... + bool success = wxPyConvertSwigPtr(result, (void**)&window, _T("wxWindow") ); + (void)success; + wxASSERT_MSG(success, _T("Returned object was not a wxWindow!") ); + Py_DECREF(result); + } + + // Release the python objects we still have + Py_DECREF( globals ); + Py_DECREF( tuple ); + + // Finally, after all Python stuff is done, release the GIL + PY_UNBLOCK_THREADS( blocked ); + + return window; +} +#endif + + + + + + + diff --git a/scripting/python_scripting.h b/scripting/python_scripting.h index 6d28be4b10..2964157668 100644 --- a/scripting/python_scripting.h +++ b/scripting/python_scripting.h @@ -12,8 +12,10 @@ #include #ifndef NO_WXPYTHON_EXTENSION_HEADERS + #ifdef KICAD_SCRIPTING_WXPYTHON #include #endif + #endif /* Function pcbnewInitPythonScripting @@ -23,4 +25,21 @@ bool pcbnewInitPythonScripting(); void pcbnewFinishPythonScripting(); + + +#ifdef KICAD_SCRIPTING_WXPYTHON + +void RedirectStdio(); +wxWindow* CreatePythonShellWindow(wxWindow* parent); + +#define PY_BLOCK_THREADS(name) wxPyBlock_t name = wxPyBeginBlockThreads() +#define PY_UNBLOCK_THREADS(name) wxPyEndBlockThreads(name) + +#else + +#define PY_BLOCK_THREADS(name) +#define PY_UNBLOCK_THREADS(name) + +#endif + #endif