diff options
author | Campbell Barton <ideasman42@gmail.com> | 2011-03-12 19:06:37 +0300 |
---|---|---|
committer | Campbell Barton <ideasman42@gmail.com> | 2011-03-12 19:06:37 +0300 |
commit | 8c526e79e31d40d56a6fecce9343c74bd9fe62d8 (patch) | |
tree | e632e6c2ed852e67f862ca96dfb3326e9a9e948b /source/blender/python/intern | |
parent | ce5c1466a1224ac2e2c7ba65c740e277cff8a0d7 (diff) |
library loading api.
this is not well suited to RNA so this is a native python api.
This uses:
bpy.data.libraries.load(filepath, link=False, relative=False)
however the return value needs to use pythons context manager, this means the library loading is confined to a block of code and python cant leave a half loaded library state.
eg, load a single scene we know the name of:
with bpy.data.libraries.load(filepath) as (data_from, data_to):
data_to.scenes = ["Scene"]
eg, load all scenes:
with bpy.data.libraries.load(filepath) as (data_from, data_to):
data_to.scenes = data_from.scenes
eg, load all objects starting with 'A'
with bpy.data.libraries.load(filepath) as (data_from, data_to):
data_to.objects = [name for name in data_from.objects if name.startswith("A")]
As you can see gives 2 objects like 'bpy.data', but containing lists of strings which can be moved from one into another.
Diffstat (limited to 'source/blender/python/intern')
-rw-r--r-- | source/blender/python/intern/CMakeLists.txt | 1 | ||||
-rw-r--r-- | source/blender/python/intern/bpy.c | 5 | ||||
-rw-r--r-- | source/blender/python/intern/bpy_library.c | 331 |
3 files changed, 336 insertions, 1 deletions
diff --git a/source/blender/python/intern/CMakeLists.txt b/source/blender/python/intern/CMakeLists.txt index fbf2741533e..ab08dc14f07 100644 --- a/source/blender/python/intern/CMakeLists.txt +++ b/source/blender/python/intern/CMakeLists.txt @@ -42,6 +42,7 @@ set(SRC bpy_app.c bpy_driver.c bpy_interface.c + bpy_library.c bpy_operator.c bpy_operator_wrap.c bpy_props.c diff --git a/source/blender/python/intern/bpy.c b/source/blender/python/intern/bpy.c index 3c3c2be7798..9abe4c9d94b 100644 --- a/source/blender/python/intern/bpy.c +++ b/source/blender/python/intern/bpy.c @@ -190,6 +190,7 @@ static PyObject *bpy_import_test(const char *modname) void BPy_init_modules( void ) { extern BPy_StructRNA *bpy_context_module; + extern int bpy_lib_init(PyObject *); PointerRNA ctx_ptr; PyObject *mod; @@ -219,7 +220,9 @@ void BPy_init_modules( void ) PyModule_AddObject( mod, "types", BPY_rna_types() ); /* needs to be first so bpy_types can run */ PyModule_AddObject(mod, "StructMetaPropGroup", (PyObject *)&pyrna_struct_meta_idprop_Type); /* metaclass for idprop types, bpy_types.py needs access */ - + + bpy_lib_init(mod); /* adds '_bpy._library_load', must be called before 'bpy_types' which uses it */ + bpy_import_test("bpy_types"); PyModule_AddObject( mod, "data", BPY_rna_module() ); /* imports bpy_types by running this */ bpy_import_test("bpy_types"); diff --git a/source/blender/python/intern/bpy_library.c b/source/blender/python/intern/bpy_library.c new file mode 100644 index 00000000000..20855a31421 --- /dev/null +++ b/source/blender/python/intern/bpy_library.c @@ -0,0 +1,331 @@ +/* + * $Id: + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Contributor(s): Campbell Barton + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/python/intern/bpy_library.c + * \ingroup pythonintern + */ + +#include <Python.h> +#include <stddef.h> + +#include "BLO_readfile.h" + +#include "BKE_global.h" +#include "BKE_main.h" +#include "BKE_library.h" +#include "BKE_idcode.h" +#include "BKE_report.h" + +#include "BLI_utildefines.h" +#include "BLI_string.h" +#include "BLI_linklist.h" +#include "BLI_path_util.h" +#include "BLI_listbase.h" + +#include "DNA_space_types.h" /* FILE_LINK, FILE_RELPATH */ + +#include "bpy_util.h" + +typedef struct { + PyObject_HEAD /* required python macro */ + /* collection iterator spesific parts */ + char relpath[FILE_MAX]; + char abspath[FILE_MAX]; /* absolute path */ + BlendHandle *blo_handle; + int flag; + PyObject *dict; +} BPy_Library; + +static PyObject *bpy_lib_load(PyObject *self, PyObject *args, PyObject *kwds); +static PyObject *bpy_lib_enter(BPy_Library *self, PyObject *args); +static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *args); + +static PyMethodDef bpy_lib_methods[] = { + {"__enter__", (PyCFunction)bpy_lib_enter, METH_NOARGS}, + {"__exit__", (PyCFunction)bpy_lib_exit, METH_VARARGS}, + {NULL} /* sentinel */ +}; + +static void bpy_lib_dealloc(BPy_Library *self) +{ + Py_XDECREF(self->dict); + Py_TYPE(self)->tp_free(self); +} + + +PyTypeObject bpy_lib_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "bpy_lib", /* tp_name */ + sizeof(BPy_Library), /* tp_basicsize */ + 0, /* tp_itemsize */ + /* methods */ + (destructor)bpy_lib_dealloc,/* tp_dealloc */ + NULL, /* printfunc tp_print; */ + NULL, /* getattrfunc tp_getattr; */ + NULL, /* setattrfunc tp_setattr; */ + NULL, /* tp_compare */ /* DEPRECATED in python 3.0! */ + NULL, /* tp_repr */ + + /* Method suites for standard classes */ + + NULL, /* PyNumberMethods *tp_as_number; */ + NULL, /* PySequenceMethods *tp_as_sequence; */ + NULL, /* PyMappingMethods *tp_as_mapping; */ + + /* More standard operations (here for binary compatibility) */ + + NULL, /* hashfunc tp_hash; */ + NULL, /* ternaryfunc tp_call; */ + NULL, /* reprfunc tp_str; */ + + /* will only use these if this is a subtype of a py class */ + PyObject_GenericGetAttr, /* getattrofunc tp_getattro; */ + NULL, /* setattrofunc tp_setattro; */ + + /* Functions to access object as input/output buffer */ + NULL, /* PyBufferProcs *tp_as_buffer; */ + + /*** Flags to define presence of optional/expanded features ***/ + Py_TPFLAGS_DEFAULT, /* long tp_flags; */ + + NULL, /* char *tp_doc; Documentation string */ + /*** Assigned meaning in release 2.0 ***/ + /* call function for all accessible objects */ + NULL, /* traverseproc tp_traverse; */ + + /* delete references to contained objects */ + NULL, /* inquiry tp_clear; */ + + /*** Assigned meaning in release 2.1 ***/ + /*** rich comparisons ***/ + NULL, /* subclassed */ /* richcmpfunc tp_richcompare; */ + + /*** weak reference enabler ***/ + 0, + /*** Added in release 2.2 ***/ + /* Iterators */ + NULL, /* getiterfunc tp_iter; */ + NULL, /* iternextfunc tp_iternext; */ + + /*** Attribute descriptor and subclassing stuff ***/ + bpy_lib_methods, /* struct PyMethodDef *tp_methods; */ + NULL, /* struct PyMemberDef *tp_members; */ + NULL, /* struct PyGetSetDef *tp_getset; */ + NULL, /* struct _typeobject *tp_base; */ + NULL, /* PyObject *tp_dict; */ + NULL, /* descrgetfunc tp_descr_get; */ + NULL, /* descrsetfunc tp_descr_set; */ + offsetof(BPy_Library, dict),/* long tp_dictoffset; */ + NULL, /* initproc tp_init; */ + NULL, /* allocfunc tp_alloc; */ + NULL, /* newfunc tp_new; */ + /* Low-level free-memory routine */ + NULL, /* freefunc tp_free; */ + /* For PyObject_IS_GC */ + NULL, /* inquiry tp_is_gc; */ + NULL, /* PyObject *tp_bases; */ + /* method resolution order */ + NULL, /* PyObject *tp_mro; */ + NULL, /* PyObject *tp_cache; */ + NULL, /* PyObject *tp_subclasses; */ + NULL, /* PyObject *tp_weaklist; */ + NULL +}; + + +static PyObject *bpy_lib_load(PyObject *UNUSED(self), PyObject *args, PyObject *kwds) +{ + static const char *kwlist[] = {"filepath", "link", "relative", NULL}; + BPy_Library *ret; + const char* filename = NULL; + int is_rel= 0, is_link= 0; + + if(!PyArg_ParseTupleAndKeywords(args, kwds, "s|ii:load", (char **)kwlist, &filename, &is_link, &is_rel)) + return NULL; + + ret= PyObject_New(BPy_Library, &bpy_lib_Type); + + BLI_strncpy(ret->relpath, filename, sizeof(BPy_Library)); + BLI_strncpy(ret->abspath, filename, sizeof(BPy_Library)); + BLI_path_abs(ret->abspath, G.main->name); + + ret->blo_handle= NULL; + ret->flag= (is_link ? FILE_LINK : 0) | + (is_rel ? FILE_RELPATH : 0); + + ret->dict= PyDict_New(); + + return (PyObject *)ret; +} + +static PyObject *_bpy_names(BPy_Library *self, int blocktype) +{ + PyObject *list; + LinkNode *l, *names; + int totnames; + + names= BLO_blendhandle_get_datablock_names(self->blo_handle, blocktype, &totnames); + + if(names) { + int counter = 0; + list = PyList_New(totnames); + for(l = names; l; l = l->next) { + PyList_SET_ITEM(list, counter, PyUnicode_FromString((char * )l->link)); + counter++; + } + BLI_linklist_free(names, free); /* free linklist *and* each node's data */ + } + else { + list= PyList_New(0); + } + + return list; +} + +static PyObject *bpy_lib_enter(BPy_Library *self, PyObject *UNUSED(args)) +{ + PyObject *ret; + BPy_Library *self_from; + PyObject *from_dict= PyDict_New(); + ReportList reports; + + BKE_reports_init(&reports, RPT_STORE); + + self->blo_handle= BLO_blendhandle_from_file(self->abspath, &reports); + + if(self->blo_handle == NULL) { + if(BPy_reports_to_error(&reports, PyExc_IOError, TRUE) != -1) { + PyErr_Format(PyExc_IOError, "load: %s failed to open blend file", self->abspath); + } + return NULL; + } + else { + int i= 0, code; + while((code= BKE_idcode_iter_step(&i))) { + if(BKE_idcode_is_linkable(code)) { + const char *name_plural= BKE_idcode_to_name_plural(code); + PyObject *str= PyUnicode_FromString(name_plural); + PyDict_SetItem(self->dict, str, PyList_New(0)); + PyDict_SetItem(from_dict, str, _bpy_names(self, code)); + Py_DECREF(str); + } + } + } + + /* create a dummy */ + self_from= PyObject_New(BPy_Library, &bpy_lib_Type); + BLI_strncpy(self_from->relpath, self->relpath, sizeof(BPy_Library)); + BLI_strncpy(self_from->abspath, self->abspath, sizeof(BPy_Library)); + + self_from->blo_handle= NULL; + self_from->flag= 0; + self_from->dict= from_dict; /* owns the dict */ + + /* return pair */ + ret= PyTuple_New(2); + + PyTuple_SET_ITEM(ret, 0, (PyObject *)self_from); + + PyTuple_SET_ITEM(ret, 1, (PyObject *)self); + Py_INCREF(self); + + BKE_reports_clear(&reports); + + return ret; +} + +static PyObject *bpy_lib_exit(BPy_Library *self, PyObject *UNUSED(args)) +{ + Main *mainl= NULL; + + flag_all_listbases_ids(LIB_PRE_EXISTING, 1); + + /* here appending/linking starts */ + mainl = BLO_library_append_begin(BPy_GetContext(), &(self->blo_handle), self->relpath); + + { + int i= 0, code; + while((code= BKE_idcode_iter_step(&i))) { + if(BKE_idcode_is_linkable(code)) { + const char *name_plural= BKE_idcode_to_name_plural(code); + PyObject *ls= PyDict_GetItemString(self->dict, name_plural); + // printf("lib: %s\n", name_plural); + if(ls && PyList_Check(ls)) { + /* loop */ + Py_ssize_t size= PyList_GET_SIZE(ls); + Py_ssize_t i; + PyObject *item; + const char *item_str; + + for(i= 0; i < size; i++) { + item= PyList_GET_ITEM(ls, i); + item_str= _PyUnicode_AsString(item); + + // printf(" %s\n", item_str); + + if(item_str) { + BLO_library_append_named_part(NULL, mainl, &(self->blo_handle), item_str, code, self->flag); + } + else { + /* XXX, could complain about this */ + PyErr_Clear(); + } + } + } + } + } + } + + BLO_library_append_end(NULL, mainl, &(self->blo_handle), 0, self->flag); + BLO_blendhandle_close(self->blo_handle); + + { /* copied from wm_operator.c */ + /* mark all library linked objects to be updated */ + recalc_all_library_objects(G.main); + + /* append, rather than linking */ + if((self->flag & FILE_LINK)==0) { + Library *lib= BLI_findstring(&G.main->library, self->abspath, offsetof(Library, name)); + if(lib) all_local(lib, 1); + else BLI_assert(!"cant find name of just added library!"); + } + } + + flag_all_listbases_ids(LIB_PRE_EXISTING, 0); + + self->blo_handle= NULL; + + Py_RETURN_NONE; +} + +int bpy_lib_init(PyObject *mod_par) +{ + static PyMethodDef load_meth= {"load", (PyCFunction)bpy_lib_load, METH_STATIC|METH_VARARGS|METH_KEYWORDS}; + PyModule_AddObject(mod_par, "_library_load", PyCFunction_New(&load_meth, NULL)); + + if(PyType_Ready(&bpy_lib_Type) < 0) + return -1; + + return 0; +} |