Double Python initialization fixed

This commit is contained in:
Miguel Angel Ajo 2012-08-01 13:54:20 +02:00
parent 474128a445
commit beec323603
6 changed files with 304 additions and 12 deletions

View File

@ -56,6 +56,9 @@ option(KICAD_SCRIPTING_MODULES
"set this option ON to build kicad modules that can be used from scripting languages" "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) #Set version option (stable or testing)
@ -137,6 +140,11 @@ if(KICAD_SCRIPTING_MODULES)
add_definitions(-DKICAD_SCRIPTING_MODULES) add_definitions(-DKICAD_SCRIPTING_MODULES)
endif(KICAD_SCRIPTING_MODULES) endif(KICAD_SCRIPTING_MODULES)
if(KICAD_SCRIPTING_EXPERIMENT)
add_definitions(-DKICAD_SCRIPTING_EXPERIMENT)
endif(KICAD_SCRIPTING_EXPERIMENT)
if(USE_WX_GRAPHICS_CONTEXT) if(USE_WX_GRAPHICS_CONTEXT)
add_definitions(-DUSE_WX_GRAPHICS_CONTEXT) add_definitions(-DUSE_WX_GRAPHICS_CONTEXT)

View File

@ -36,7 +36,10 @@
#include <wx/config.h> #include <wx/config.h>
#include <wx/filename.h> #include <wx/filename.h>
#include <param_config.h> #include <param_config.h>
#ifdef KICAD_SCRIPTING_EXPERIMENT
#include <python2.7/Python.h>
#include <wx/wxPython/wxPython.h>
#endif
enum EDA_APP_T { enum EDA_APP_T {
APP_UNKNOWN_T, APP_UNKNOWN_T,
@ -59,6 +62,10 @@ class wxHtmlHelpController;
*/ */
class EDA_APP : public wxApp class EDA_APP : public wxApp
{ {
#ifdef KICAD_SCRIPTING_EXPERIMENT
private:
PyThreadState* m_mainTState;
#endif
protected: protected:
/// Used mainly to handle default paths libs m_Id = APP_EESCHEMA_T, APP_PCBNEW_T ... /// Used mainly to handle default paths libs m_Id = APP_EESCHEMA_T, APP_PCBNEW_T ...
EDA_APP_T m_Id; EDA_APP_T m_Id;
@ -115,7 +122,13 @@ public:
* this is the first executed function (like main() ) * this is the first executed function (like main() )
* @return true if the application can be started. * @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; } wxHtmlHelpController* GetHtmlHelpController() { return m_HtmlCtrl; }

View File

@ -470,7 +470,9 @@ public:
void SetShowBorderAndTitleBlock( bool aShow ) { m_showBorderAndTitleBlock = aShow; } void SetShowBorderAndTitleBlock( bool aShow ) { m_showBorderAndTitleBlock = aShow; }
EDA_DRAW_PANEL* GetCanvas() { return m_canvas; } EDA_DRAW_PANEL* GetCanvas() { return m_canvas; }
// HACK
// EDA_MSG_PANEL* GetMessagePanel() { return m_messagePanel; }
// end hack
virtual wxString GetScreenDesc(); virtual wxString GetScreenDesc();
/** /**

View File

@ -57,6 +57,7 @@
#include <python_scripting.h> #include <python_scripting.h>
#endif #endif
// Colors for layers and items // Colors for layers and items
COLORS_DESIGN_SETTINGS g_ColorsSettings; COLORS_DESIGN_SETTINGS g_ColorsSettings;
@ -87,6 +88,7 @@ wxString g_DocModulesFileName = wxT( "footprints_doc/footprints.pdf" );
wxArrayString g_LibraryNames; wxArrayString g_LibraryNames;
// wxWindow* DoPythonStuff(wxWindow* parent); // declaration
IMPLEMENT_APP( EDA_APP ) IMPLEMENT_APP( EDA_APP )
@ -105,6 +107,33 @@ void EDA_APP::MacOpenFile( const wxString& fileName )
frame->LoadOnePcbFile( fileName, false ); 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() bool EDA_APP::OnInit()
{ {
@ -112,9 +141,15 @@ bool EDA_APP::OnInit()
PCB_EDIT_FRAME* frame = NULL; PCB_EDIT_FRAME* frame = NULL;
#ifdef KICAD_SCRIPTING #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 #endif
InitEDA_Appl( wxT( "Pcbnew" ), APP_PCBNEW_T ); InitEDA_Appl( wxT( "Pcbnew" ), APP_PCBNEW_T );
@ -209,6 +244,217 @@ Changing extension to .brd." ), GetChars( fn.GetFullPath() ) );
*/ */
frame->SetFocus(); frame->SetFocus();
frame->GetCanvas()->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; 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

View File

@ -28,6 +28,7 @@
*/ */
#include <python_scripting.h> #include <python_scripting.h>
#include <wx/wxPython/wxPython.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -37,7 +38,7 @@ extern "C" void init_kicad( void );
extern "C" void init_pcbnew( 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 // modules that we want to add into the list
@ -112,21 +113,43 @@ static void swigSwitchPythonBuiltin()
/* Function pcbnewInitPythonScripting /* Function pcbnewInitPythonScripting
* Initializes all the python environment and publish our interface inside it * 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 swigAddBuiltin(); // add builtin functions
swigAddModules(); // add our own modules swigAddModules(); // add our own modules
swigSwitchPythonBuiltin(); // switch the python builtin modules to our new list 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 // load pcbnew inside python, and load all the user plugins, TODO: add system wide plugins
wxPyBlock_t blocked = wxPyBeginBlockThreads();
PyRun_SimpleString( "import sys\n" PyRun_SimpleString( "import sys\n"
"sys.path.append(\".\")\n" "sys.path.append(\".\")\n"
"import pcbnew\n" "import pcbnew\n"
"pcbnew.LoadPlugins()" "pcbnew.LoadPlugins()"
); );
wxPyEndBlockThreads(blocked);
return true;
} }

View File

@ -7,6 +7,6 @@
* Initializes the Python engine inside pcbnew * Initializes the Python engine inside pcbnew
*/ */
void pcbnewInitPythonScripting( void ); bool pcbnewInitPythonScripting(PyThreadState** aMainTState);
#endif #endif