Commit 4d2def35 authored by Miguel Angel Ajo's avatar Miguel Angel Ajo

scripting + pcbnew: Merged with scripting branch, now it supports...

scripting + pcbnew: Merged with scripting branch, now it supports -DKICAD_SCRIPTING_WXPYTHON for full wx integration with python
parents ef39c5f3 89dd0748
......@@ -56,6 +56,9 @@ option(KICAD_SCRIPTING_MODULES
"set this option ON to build kicad modules that can be used from scripting languages"
)
option(KICAD_SCRIPTING_WXPYTHON
"set this option ON to build wxpython implementation for wx interface building in python and py.shell"
)
#Set version option (stable or testing)
......@@ -137,6 +140,11 @@ if(KICAD_SCRIPTING_MODULES)
add_definitions(-DKICAD_SCRIPTING_MODULES)
endif(KICAD_SCRIPTING_MODULES)
if(KICAD_SCRIPTING_WXPYTHON)
add_definitions(-DKICAD_SCRIPTING_WXPYTHON)
endif(KICAD_SCRIPTING_WXPYTHON)
if(USE_WX_GRAPHICS_CONTEXT)
add_definitions(-DUSE_WX_GRAPHICS_CONTEXT)
......@@ -248,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()
......
......@@ -59,6 +59,7 @@ class wxHtmlHelpController;
*/
class EDA_APP : public wxApp
{
protected:
/// Used mainly to handle default paths libs m_Id = APP_EESCHEMA_T, APP_PCBNEW_T ...
EDA_APP_T m_Id;
......@@ -115,7 +116,7 @@ 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
wxHtmlHelpController* GetHtmlHelpController() { return m_HtmlCtrl; }
......@@ -408,6 +409,7 @@ public:
* @param aIndex = insertion point
*/
void InsertLibraryPath( const wxString& aPaths, size_t aIndex );
};
/*
......
......@@ -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
......
......@@ -1072,6 +1072,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_
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}
)
......
......@@ -53,6 +53,9 @@
#include <dialog_SVG_print.h>
#include <dialog_helpers.h>
#ifdef KICAD_SCRIPTING
#include <python_scripting.h>
#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
......
......@@ -29,6 +29,7 @@
*/
#ifdef KICAD_SCRIPTING
#include <python_scripting.h>
#include <pcbnew_scripting_helpers.h>
#endif
#include <fctsys.h>
......@@ -51,11 +52,6 @@
#include <wildcards_and_files_ext.h>
#include <class_board.h>
#include <dialogs/dialog_scripting.h>
#ifdef KICAD_SCRIPTING
#include <python_scripting.h>
#endif
// Colors for layers and items
COLORS_DESIGN_SETTINGS g_ColorsSettings;
......@@ -87,6 +83,7 @@ wxString g_DocModulesFileName = wxT( "footprints_doc/footprints.pdf" );
wxArrayString g_LibraryNames;
// wxWindow* DoPythonStuff(wxWindow* parent); // declaration
IMPLEMENT_APP( EDA_APP )
......@@ -105,17 +102,18 @@ void EDA_APP::MacOpenFile( const wxString& fileName )
frame->LoadOnePcbFile( fileName, false );
}
bool EDA_APP::OnInit()
{
wxFileName fn;
PCB_EDIT_FRAME* frame = NULL;
#ifdef KICAD_SCRIPTING
pcbnewInitPythonScripting();
if ( !pcbnewInitPythonScripting() )
{
return false;
}
#endif
InitEDA_Appl( wxT( "Pcbnew" ), APP_PCBNEW_T );
if( m_Checker && m_Checker->IsAnotherRunning() )
......@@ -212,3 +210,23 @@ Changing extension to .brd." ), GetChars( fn.GetFullPath() ) );
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_WXPYTHON
pcbnewFinishPythonScripting();
#endif
return 0;
}
#endif
......@@ -4,6 +4,8 @@
*/
#include "pcbnew_footprint_wizards.h"
#include <python_scripting.h>
#include <stdio.h>
PYTHON_FOOTPRINT_WIZARD::PYTHON_FOOTPRINT_WIZARD(PyObject *aWizard)
......@@ -28,6 +30,8 @@ PyObject* PYTHON_FOOTPRINT_WIZARD::CallMethod(const char* aMethod, PyObject *aAr
{
PyObject *result;
PY_BLOCK_THREADS( blocked );
result = PyObject_CallObject( pFunc, aArglist );
if ( PyErr_Occurred() )
......@@ -39,6 +43,8 @@ PyObject* PYTHON_FOOTPRINT_WIZARD::CallMethod(const char* aMethod, PyObject *aAr
printf ( " : %s\n", PyString_AsString( PyObject_Str(b) ) );
}
PY_UNBLOCK_THREADS( blocked );
if ( result )
{
Py_XDECREF( pFunc );
......@@ -65,9 +71,12 @@ wxString PYTHON_FOOTPRINT_WIZARD::CallRetStrMethod( const char* aMethod, PyObjec
PyObject *result = CallMethod( aMethod, aArglist );
if ( result )
{
PY_BLOCK_THREADS( blocked );
const char *str_res = PyString_AsString( result );
ret = wxString::FromUTF8( str_res );
Py_DECREF( result );
PY_UNBLOCK_THREADS( blocked );
}
return ret;
}
......@@ -90,6 +99,9 @@ wxArrayString PYTHON_FOOTPRINT_WIZARD::CallRetArrayStrMethod
"result is not a list"),1 );
return ret;
}
PY_BLOCK_THREADS( blocked );
int list_size = PyList_Size( result );
for ( int n=0; n<list_size; n++ )
......@@ -102,6 +114,8 @@ wxArrayString PYTHON_FOOTPRINT_WIZARD::CallRetArrayStrMethod
}
Py_DECREF( result );
PY_UNBLOCK_THREADS( blocked );
}
......@@ -137,8 +151,13 @@ int PYTHON_FOOTPRINT_WIZARD::GetNumParameterPages()
{
if ( !PyInt_Check( result ) )
return -1;
PY_BLOCK_THREADS( blocked );
ret = PyInt_AsLong( result );
Py_DECREF( result );
PY_UNBLOCK_THREADS( blocked );
}
return ret;
}
......@@ -156,9 +175,13 @@ wxString PYTHON_FOOTPRINT_WIZARD::GetParameterPageName( int aPage )
if ( result )
{
PY_BLOCK_THREADS( blocked );
const char *str_res = PyString_AsString( result );
ret = wxString::FromUTF8( str_res );
Py_DECREF( result );
PY_UNBLOCK_THREADS( blocked );
}
return ret;
}
......@@ -276,9 +299,11 @@ MODULE *PYTHON_FOOTPRINT_WIZARD::GetModule()
if (!result)
return NULL;
PY_BLOCK_THREADS( blocked );
obj = PyObject_GetAttrString( result, "this" );
if (PyErr_Occurred())
if ( PyErr_Occurred() )
{
/*
PyObject *t, *v, *b;
......@@ -289,8 +314,11 @@ MODULE *PYTHON_FOOTPRINT_WIZARD::GetModule()
*/
PyErr_Print();
}
PY_UNBLOCK_THREADS( blocked );
return PyModule_to_MODULE(obj);
MODULE *mod = PyModule_to_MODULE( obj );
return mod;
}
......@@ -300,9 +328,10 @@ void PYTHON_FOOTPRINT_WIZARDS::register_wizard(PyObject* aPyWizard)
fw = new PYTHON_FOOTPRINT_WIZARD( aPyWizard );
printf( "Registered python footprint wizard '%s'\n",
(const char*)fw->GetName().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
......
......@@ -30,6 +30,14 @@
#include <python_scripting.h>
#include <stdlib.h>
#include <string.h>
#ifdef __GNUG__
#pragma implementation
#endif
#include <fctsys.h>
#include <wxstruct.h>
#include <common.h>
#include <colors.h>
/* init functions defined by swig */
......@@ -37,7 +45,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 +120,175 @@ 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()
PyThreadState *g_PythonMainTState;
bool pcbnewInitPythonScripting()
{
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();
#ifdef KICAD_SCRIPTING_WXPYTHON
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.
g_PythonMainTState = wxPyBeginAllowThreads();
// load pcbnew inside python, and load all the user plugins, TODO: add system wide plugins
PY_BLOCK_THREADS( blocked );
#endif
PyRun_SimpleString( "import sys\n"
"sys.path.append(\".\")\n"
"import pcbnew\n"
"pcbnew.LoadPlugins()"
);
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
#ifndef __PYTHON_SCRIPTING_H
#define __PYTHON_SCRIPTING_H
#include <Python.h>
// 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 <Python.h>
#ifndef NO_WXPYTHON_EXTENSION_HEADERS
#ifdef KICAD_SCRIPTING_WXPYTHON
#include <wx/wxPython/wxPython.h>
#endif
#endif
/* Function pcbnewInitPythonScripting
* Initializes the Python engine inside pcbnew
*/
void pcbnewInitPythonScripting( void );
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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment