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