From c30149991c9417106577e2d96112b16433910215 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sun, 20 Feb 2011 23:39:29 +0000 Subject: Experimental option to build blender as a python module, rather then blender embedding python. CMake build option WITH_PYTHON_MODULE, will build ./bin/bpy.so This allows 'bpy' to be imported from python or other applications/IDE's which embed python, eg: python -c "import bpy ; bpy.ops.render.render(write_still=True)" This runs in background mode and has similar restrictions to running a script: blender --background --python test.py TODO: - install to site-packages with blender scripts - add support for imp.reload() --- source/blender/editors/render/render_internal.c | 2 +- source/blender/python/intern/CMakeLists.txt | 4 ++ source/blender/python/intern/bpy.c | 10 ++-- source/blender/python/intern/bpy.h | 2 +- source/blender/python/intern/bpy_interface.c | 64 +++++++++++++++++++++++-- source/creator/CMakeLists.txt | 17 ++++++- source/creator/creator.c | 24 ++++++++-- 7 files changed, 109 insertions(+), 14 deletions(-) (limited to 'source') diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index 17d05bed24a..568d08d4c91 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -800,7 +800,7 @@ void RENDER_OT_render(wmOperatorType *ot) ot->modal= screen_render_modal; ot->exec= screen_render_exec; - ot->poll= ED_operator_screenactive; + /*ot->poll= ED_operator_screenactive;*/ /* this isnt needed, causes failer in background mode */ RNA_def_boolean(ot->srna, "animation", 0, "Animation", "Render files from the animation range of this scene"); RNA_def_boolean(ot->srna, "write_still", 0, "Write Image", "Save rendered the image to the output path (used only when animation is disabled)"); diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt index 5d1a086bc75..b73c9949ada 100644 --- a/source/blender/python/intern/CMakeLists.txt +++ b/source/blender/python/intern/CMakeLists.txt @@ -67,4 +67,8 @@ if(WITH_BUILDINFO) add_definitions(-DBUILD_DATE) endif() +if(WITH_PYTHON_MODULE) + add_definitions(-DWITH_PYTHON_MODULE) +endif() + blender_add_lib(bf_python "${SRC}" "${INC}") diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c index 8076515b94b..02073f368e8 100644 --- a/source/blender/python/intern/bpy.c +++ b/source/blender/python/intern/bpy.c @@ -53,6 +53,8 @@ #include "AUD_PyInit.h" +PyObject *bpy_package_py= NULL; + static char bpy_script_paths_doc[] = ".. function:: script_paths()\n" "\n" @@ -161,7 +163,7 @@ static PyMethodDef meth_bpy_script_paths = {"script_paths", (PyCFunction)bpy_scr static PyMethodDef meth_bpy_blend_paths = {"blend_paths", (PyCFunction)bpy_blend_paths, METH_VARARGS|METH_KEYWORDS, bpy_blend_paths_doc}; static PyMethodDef meth_bpy_user_resource = {"user_resource", (PyCFunction)bpy_user_resource, METH_VARARGS|METH_KEYWORDS, NULL}; -static void bpy_import_test(const char *modname) +static PyObject *bpy_import_test(const char *modname) { PyObject *mod= PyImport_ImportModuleLevel((char *)modname, NULL, NULL, NULL, 0); if(mod) { @@ -170,7 +172,9 @@ static void bpy_import_test(const char *modname) else { PyErr_Print(); PyErr_Clear(); - } + } + + return mod; } /***************************************************************************** @@ -235,5 +239,5 @@ void BPy_init_modules( void ) PyModule_AddObject(mod, meth_bpy_unregister_class.ml_name, (PyObject *)PyCFunction_New(&meth_bpy_unregister_class, NULL)); /* add our own modules dir, this is a python package */ - bpy_import_test("bpy"); + bpy_package_py= bpy_import_test("bpy"); } diff --git a/source/blender/python/intern/bpy.h b/source/blender/python/intern/bpy.h index 76eef6ea4b9..90f21cb07b1 100644 --- a/source/blender/python/intern/bpy.h +++ b/source/blender/python/intern/bpy.h @@ -22,4 +22,4 @@ * ***** END GPL LICENSE BLOCK ***** */ void BPy_init_modules( void ); - +extern PyObject *bpy_package_py; diff --git a/source/blender/python/intern/bpy_interface.c b/source/blender/python/intern/bpy_interface.c index 4ae1b41bb89..d1773e5fd7a 100644 --- a/source/blender/python/intern/bpy_interface.c +++ b/source/blender/python/intern/bpy_interface.c @@ -157,6 +157,7 @@ void BPY_modules_update(bContext *C) } /* must be called before Py_Initialize */ +#ifndef WITH_PYTHON_MODULE static void bpy_python_start_path(void) { char *py_path_bundle= BLI_get_folder(BLENDER_PYTHON, NULL); @@ -195,8 +196,7 @@ static void bpy_python_start_path(void) // printf("found python (wchar_t) '%ls'\n", py_path_bundle_wchar); } } - - +#endif void BPY_context_set(bContext *C) { @@ -219,8 +219,9 @@ static struct _inittab bpy_internal_modules[]= { /* call BPY_context_set first */ void BPY_python_start(int argc, const char **argv) { +#ifndef WITH_PYTHON_MODULE PyThreadState *py_tstate = NULL; - + /* not essential but nice to set our name */ static wchar_t bprogname_wchar[FILE_MAXDIR+FILE_MAXFILE]; /* python holds a reference */ utf8towchar(bprogname_wchar, bprogname); @@ -252,8 +253,13 @@ void BPY_python_start(int argc, const char **argv) /* Initialize thread support (also acquires lock) */ PyEval_InitThreads(); +#else + (void)argc; + (void)argv; - + PyImport_ExtendInittab(bpy_internal_modules); +#endif + /* bpy.* and lets us import it */ BPy_init_modules(); @@ -281,8 +287,10 @@ void BPY_python_start(int argc, const char **argv) pyrna_alloc_types(); +#ifndef WITH_PYTHON_MODULE py_tstate = PyGILState_GetThisThreadState(); PyEval_ReleaseThread(py_tstate); +#endif } void BPY_python_end(void) @@ -659,3 +667,51 @@ int BPY_context_member_get(bContext *C, const char *member, bContextDataResult * return done; } + +#ifdef WITH_PYTHON_MODULE +/* TODO, reloading the module isnt functional at the moment. */ + +extern int main_python(int argc, const char **argv); +static struct PyModuleDef bpy_proxy_def = { + PyModuleDef_HEAD_INIT, + "bpy", /* m_name */ + NULL, /* m_doc */ + 0, /* m_size */ + NULL, /* m_methods */ + NULL, /* m_reload */ + NULL, /* m_traverse */ + NULL, /* m_clear */ + NULL, /* m_free */ +}; + +PyMODINIT_FUNC +PyInit_bpy(void) +{ + int argc= 0; + const char *argv[]={NULL}; + + main_python(argc, argv); + + /* initialized in BPy_init_modules() */ + if(bpy_package_py) { + /* Problem: + * 1) this init function is expected to have a private member defined - 'md_def' + * but this is only set for C defined modules (not py packages) + * so we cant return 'bpy_package_py' as is. + * + * 2) there is a 'bpy' C module for python to load which is basically all of blender, + * and there is scripts/bpy/__init__.py, + * we may end up having to rename this module so there is no naming conflict here eg: + * 'from blender import bpy' */ + PyObject *bpy_proxy= PyModule_Create(&bpy_proxy_def); + PyDict_Update(PyModule_GetDict(bpy_proxy), PyModule_GetDict(bpy_package_py)); + return bpy_proxy; + } + else { + PyErr_SetString(PyExc_RuntimeError, "could not import internal bpy package"); + return NULL; + } + + +} +#endif diff --git a/source/creator/CMakeLists.txt b/source/creator/CMakeLists.txt index 9e52f188195..f74dc655764 100644 --- a/source/creator/CMakeLists.txt +++ b/source/creator/CMakeLists.txt @@ -127,8 +127,21 @@ if(WITH_BUILDINFO) endif() # message(STATUS "Configuring blender") - -add_executable(blender ${EXETYPE} ${SRC}) +if(WITH_PYTHON_MODULE) + add_definitions(-DWITH_PYTHON_MODULE) + + # creates ./bin/bpy.so which can be imported as a python module. + add_library(blender SHARED ${SRC}) + set_target_properties( + blender + PROPERTIES + PREFIX "" + OUTPUT_NAME bpy + LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin/ + ) +else() + add_executable(blender ${EXETYPE} ${SRC}) +endif() # Post build steps for bundling/packaging. diff --git a/source/creator/creator.c b/source/creator/creator.c index 8dd1ed86e3a..48157de91eb 100644 --- a/source/creator/creator.c +++ b/source/creator/creator.c @@ -154,6 +154,7 @@ static void fpe_handler(int UNUSED(sig)) } #endif +#ifdef WITH_PYTHON_MODULE /* handling ctrl-c event in console */ static void blender_esc(int sig) { @@ -170,6 +171,7 @@ static void blender_esc(int sig) count++; } } +#endif /* buildinfo can have quotes */ #ifdef BUILD_DATE @@ -1111,12 +1113,21 @@ static void setupArguments(bContext *C, bArgs *ba, SYS_SystemHandle *syshandle) } -int main(int argc, char **argv) +#ifdef WITH_PYTHON_MODULE +/* allow python module to call main */ +#define main main_python +#endif + +int main(int argc, const char **argv) { SYS_SystemHandle syshandle; bContext *C= CTX_create(); bArgs *ba; +#ifdef WITH_PYTHON_MODULE +#undef main +#endif + #ifdef WITH_BINRELOC br_init( NULL ); #endif @@ -1192,10 +1203,13 @@ int main(int argc, char **argv) setuid(getuid()); /* end superuser */ #endif - +#ifdef WITH_PYTHON_MODULE + G.background= 1; /* python module mode ALWAYS runs in background mode (for now) */ +#else /* for all platforms, even windos has it! */ if(G.background) signal(SIGINT, blender_esc); /* ctrl c out bg render */ - +#endif + /* background render uses this font too */ BKE_font_register_builtin(datatoc_Bfont, datatoc_Bfont_size); @@ -1248,6 +1262,10 @@ int main(int argc, char **argv) BLI_argsFree(ba); +#ifdef WITH_PYTHON_MODULE + return 0; /* keep blender in background mode running */ +#endif + if(G.background) { /* actually incorrect, but works for now (ton) */ WM_exit(C); -- cgit v1.2.3