diff options
-rw-r--r-- | source/blender/blenkernel/BKE_bad_level_calls.h | 7 | ||||
-rw-r--r-- | source/blender/blenkernel/bad_level_call_stubs/stubs.c | 10 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/depsgraph.c | 44 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/ipo.c | 13 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_curve_types.h | 8 | ||||
-rw-r--r-- | source/blender/python/BPY_extern.h | 5 | ||||
-rw-r--r-- | source/blender/python/BPY_interface.c | 190 | ||||
-rw-r--r-- | source/blender/python/api2_2x/EXPP_interface.c | 99 | ||||
-rw-r--r-- | source/blender/python/api2_2x/EXPP_interface.h | 13 | ||||
-rw-r--r-- | source/blender/python/api2_2x/Object.c | 13 | ||||
-rw-r--r-- | source/blender/src/drawipo.c | 51 | ||||
-rw-r--r-- | source/blender/src/usiblender.c | 6 |
12 files changed, 437 insertions, 22 deletions
diff --git a/source/blender/blenkernel/BKE_bad_level_calls.h b/source/blender/blenkernel/BKE_bad_level_calls.h index f261f786dd4..10248fad220 100644 --- a/source/blender/blenkernel/BKE_bad_level_calls.h +++ b/source/blender/blenkernel/BKE_bad_level_calls.h @@ -57,13 +57,20 @@ struct Sequence; struct ListBase; void build_seqar(struct ListBase *seqbase, struct Sequence ***seqar, int *totseq); +/* BPython API */ struct ID; struct Script; struct Text; +struct IpoDriver; /* DNA_curve_types.h */ +struct Object; void BPY_do_pyscript (struct ID *id, short int event); void BPY_clear_script (struct Script *script); void BPY_free_compiled_text (struct Text *text); void BPY_free_screen_spacehandlers (struct bScreen *sc); +/* ipo.c: */ +float BPY_pydriver_eval(struct IpoDriver *driver); +/* depsgraph.c: */ +struct Object **BPY_pydriver_get_objects(struct IpoDriver *driver); /* writefile.c */ struct Oops; diff --git a/source/blender/blenkernel/bad_level_call_stubs/stubs.c b/source/blender/blenkernel/bad_level_call_stubs/stubs.c index 7bc4e664429..3e858aca631 100644 --- a/source/blender/blenkernel/bad_level_call_stubs/stubs.c +++ b/source/blender/blenkernel/bad_level_call_stubs/stubs.c @@ -109,6 +109,16 @@ void BPY_do_pyscript(ID *id, short int event){} void BPY_clear_script(Script *script){} void BPY_free_compiled_text(struct Text *text){} void BPY_free_screen_spacehandlers (struct bScreen *sc){} +float BPY_pydriver_eval(struct IpoDriver *driver) +{ + return 0; +} + +/* depsgraph.c: */ +struct Object **BPY_pydriver_get_objects(struct IpoDriver *driver) +{ + return 0; +} /* writefile.c */ /* struct Oops; */ diff --git a/source/blender/blenkernel/intern/depsgraph.c b/source/blender/blenkernel/intern/depsgraph.c index b6a85bbe910..42898bc8976 100644 --- a/source/blender/blenkernel/intern/depsgraph.c +++ b/source/blender/blenkernel/intern/depsgraph.c @@ -75,7 +75,8 @@ #include "MEM_guardedalloc.h" #include "blendef.h" - +#include "BPY_extern.h" + #include "depsgraph_private.h" /* Queue and stack operations for dag traversal @@ -302,12 +303,41 @@ static void dag_add_driver_relation(Ipo *ipo, DagForest *dag, DagNode *node, int DagNode *node1; for(icu= ipo->curve.first; icu; icu= icu->next) { - if(icu->driver && icu->driver->ob) { - node1 = dag_get_node(dag, icu->driver->ob); - if(icu->driver->blocktype==ID_AR) - dag_add_relation(dag, node1, node, isdata?DAG_RL_DATA_DATA:DAG_RL_DATA_OB); - else - dag_add_relation(dag, node1, node, isdata?DAG_RL_OB_DATA:DAG_RL_OB_OB); + if(icu->driver) { + + if (icu->driver->type == IPO_DRIVER_TYPE_PYTHON) { + + if ((icu->driver->flag & IPO_DRIVER_FLAG_INVALID) || (icu->driver->name[0] == '\0')) + continue; /* empty or invalid expression */ + else { + /* now we need refs to all objects mentioned in this + * pydriver expression, to call 'dag_add_relation' + * for each of them */ + Object **obarray = BPY_pydriver_get_objects(icu->driver); + if (obarray) { + Object *ob, **oba = obarray; + + while (*oba) { + ob = *oba; + node1 = dag_get_node(dag, ob); + if (ob->type == OB_ARMATURE) + dag_add_relation(dag, node1, node, isdata?DAG_RL_DATA_DATA:DAG_RL_DATA_OB); + else + dag_add_relation(dag, node1, node, isdata?DAG_RL_OB_DATA:DAG_RL_OB_OB); + oba++; + } + + MEM_freeN(obarray); + } + } + } + else if (icu->driver->ob) { + node1 = dag_get_node(dag, icu->driver->ob); + if(icu->driver->blocktype==ID_AR) + dag_add_relation(dag, node1, node, isdata?DAG_RL_DATA_DATA:DAG_RL_DATA_OB); + else + dag_add_relation(dag, node1, node, isdata?DAG_RL_OB_DATA:DAG_RL_OB_OB); + } } } } diff --git a/source/blender/blenkernel/intern/ipo.c b/source/blender/blenkernel/intern/ipo.c index 9efb74ffa35..27bf9c1744f 100644 --- a/source/blender/blenkernel/intern/ipo.c +++ b/source/blender/blenkernel/intern/ipo.c @@ -75,6 +75,7 @@ #include "BKE_main.h" #include "BKE_mesh.h" #include "BKE_object.h" +#include "BPY_extern.h" /* for BPY_pydriver_eval() */ #define SMALL -1.0e-10 @@ -739,12 +740,18 @@ void berekenx(float *f, float *o, int b) static float eval_driver(IpoDriver *driver) { - if(driver->flag & IPO_DRIVER_PYTHON) { - printf("Execute %s\n", driver->name); + if(driver->type == IPO_DRIVER_TYPE_PYTHON) { + /* check for empty or invalid expression */ + if ((driver->name[0] == '\0') || + (driver->flag & IPO_DRIVER_FLAG_INVALID)) + return 0.0f; + /* this evals the expression and returns its result: + * (on errors it reports, then returns 0.0f) */ + return BPY_pydriver_eval(driver); } else { Object *ob= driver->ob; - + if(ob==NULL) return 0.0f; if(driver->blocktype==ID_OB) { diff --git a/source/blender/makesdna/DNA_curve_types.h b/source/blender/makesdna/DNA_curve_types.h index a400fad0530..174b17792ce 100644 --- a/source/blender/makesdna/DNA_curve_types.h +++ b/source/blender/makesdna/DNA_curve_types.h @@ -254,9 +254,13 @@ typedef struct IpoCurve { /* *************** driver ****************** */ -/* driver->flag */ -#define IPO_DRIVER_PYTHON 1 +/* driver->type */ +#define IPO_DRIVER_TYPE_NORMAL 0 +#define IPO_DRIVER_TYPE_PYTHON 1 +/* driver->flag */ +/* invalid flag: currently only used for buggy pydriver expressions: */ +#define IPO_DRIVER_FLAG_INVALID 1 #endif diff --git a/source/blender/python/BPY_extern.h b/source/blender/python/BPY_extern.h index ffb5f258d8d..cc187ea3a0d 100644 --- a/source/blender/python/BPY_extern.h +++ b/source/blender/python/BPY_extern.h @@ -37,6 +37,8 @@ extern char bprogname[]; /* holds a copy of argv[0], from creator.c */ struct Text; /* defined in DNA_text_types.h */ struct ID; /* DNA_ID.h */ +struct Object; /* DNA_object_types.h */ +struct IpoDriver; /* DNA_curve_types.h */ struct ScriptLink; /* DNA_scriptlink_types.h */ struct ListBase; /* DNA_listBase.h */ struct SpaceText; /* DNA_space_types.h */ @@ -79,6 +81,9 @@ extern "C" { int BPY_do_spacehandlers(struct ScrArea *sa, unsigned short event, unsigned short space_event); + float BPY_pydriver_eval(struct IpoDriver *driver); + struct Object **BPY_pydriver_get_objects(struct IpoDriver *driver); + /* format importer hook */ int BPY_call_importloader( char *name ); diff --git a/source/blender/python/BPY_interface.c b/source/blender/python/BPY_interface.c index ff262b1a581..6a79e673345 100644 --- a/source/blender/python/BPY_interface.c +++ b/source/blender/python/BPY_interface.c @@ -43,6 +43,7 @@ #include "BKE_library.h" #include "BKE_object.h" /* during_scriptlink() */ #include "BKE_text.h" +#include "DNA_curve_types.h" /* for struct IpoDriver */ #include "DNA_screen_types.h" #include "DNA_userdef_types.h" /* for U.pythondir */ #include "MEM_guardedalloc.h" @@ -73,6 +74,9 @@ */ //#include "api2_2x/Registry.h" +/* for pydrivers (ipo drivers defined by one-line Python expressions) */ +PyObject *bpy_pydriver_Dict = NULL; + /*Declares the modules and their initialization functions *These are TOP-LEVEL modules e.g. import `module` - there is no *support for packages here e.g. import `package.module` */ @@ -175,6 +179,11 @@ void BPY_end_python( void ) bpy_registryDict = NULL; } + if( bpy_pydriver_Dict ) { + Py_DECREF( bpy_pydriver_Dict ); + bpy_pydriver_Dict = NULL; + } + Py_Finalize( ); BPyMenu_RemoveAllEntries( ); /* freeing bpymenu mem */ @@ -919,6 +928,187 @@ void BPY_clear_script( Script * script ) unlink_script( script ); } +/* PyDrivers */ + +/* PyDrivers are Ipo Drivers governed by expressions written in Python. + * Expressions here are one-liners that evaluate to a float value. */ + +/* For faster execution we keep a special dictionary for pydrivers, with + * the needed modules and aliases. */ +static int bpy_pydriver_create_dict(void) +{ + PyObject *d, *mod; + + if (bpy_pydriver_Dict) return -1; + + d = PyDict_New(); + if (!d) return -1; + + bpy_pydriver_Dict = d; + + /* import some modules: builtins, Blender, math, Blender.noise */ + + PyDict_SetItemString(d, "__builtins__", PyEval_GetBuiltins()); + + mod = PyImport_ImportModule("Blender"); + if (mod) { + PyDict_SetItemString(d, "Blender", mod); + PyDict_SetItemString(d, "b", mod); + Py_DECREF(mod); + } + + mod = PyImport_ImportModule("math"); + if (mod) { + PyDict_SetItemString(d, "math", mod); + PyDict_SetItemString(d, "m", mod); + Py_DECREF(mod); + } + + mod = PyImport_ImportModule("Blender.Noise"); + if (mod) { + PyDict_SetItemString(d, "noise", mod); + PyDict_SetItemString(d, "n", mod); + Py_DECREF(mod); + } + + /* If there's a Blender text called pydrivers.py, import it. + * Users can add their own functions to this module. */ + mod = importText("pydrivers"); /* can also use PyImport_Import() */ + if (mod) { + PyDict_SetItemString(d, "pydrivers", mod); + PyDict_SetItemString(d, "p", mod); + Py_DECREF(mod); + } + else + PyErr_Clear(); + + /* short aliases for some Get() functions: */ + + /* ob(obname) == Blender.Object.Get(obname) */ + mod = PyImport_ImportModule("Blender.Object"); + if (mod) { + PyObject *fcn = PyObject_GetAttrString(mod, "Get"); + Py_DECREF(mod); + if (fcn) + PyDict_SetItemString(d, "ob", fcn); + } + + /* me(meshname) == Blender.Mesh.Get(meshname) */ + mod = PyImport_ImportModule("Blender.Mesh"); + if (mod) { + PyObject *fcn = PyObject_GetAttrString(mod, "Get"); + Py_DECREF(mod); + if (fcn) + PyDict_SetItemString(d, "me", fcn); + } + + /* ma(matname) == Blender.Material.Get(matname) */ + mod = PyImport_ImportModule("Blender.Material"); + if (mod) { + PyObject *fcn = PyObject_GetAttrString(mod, "Get"); + Py_DECREF(mod); + if (fcn) + PyDict_SetItemString(d, "ma", fcn); + } + + return 0; +} + +/* error return function for BPY_eval_pydriver */ +static float pydriver_error(IpoDriver *driver) { + + if (bpy_pydriver_oblist) + bpy_pydriver_freeList(); + + if (bpy_pydriver_Dict) { /* free the global dict used by pydrivers */ + Py_DECREF(bpy_pydriver_Dict); + bpy_pydriver_Dict = NULL; + } + + driver->flag |= IPO_DRIVER_FLAG_INVALID; /* py expression failed */ + + fprintf(stderr, "\nError in Ipo Driver: Object %s\nThis is the failed Python expression:\n'%s'\n\n", driver->ob->id.name+2, driver->name); + + PyErr_Print(); + + return 0.0f; +} + +/* for depsgraph.c, runs py expr once to collect all refs. made + * to objects (self refs. to the object that owns the py driver + * are not allowed). */ +struct Object **BPY_pydriver_get_objects(IpoDriver *driver) +{ + /*if (!driver || !driver->ob || driver->name[0] == '\0') + return NULL;*/ + + /*PyErr_Clear();*/ + + /* clear the flag that marks invalid python expressions */ + driver->flag &= ~IPO_DRIVER_FLAG_INVALID; + + /* tell we're running a pydriver, so Get() functions know they need + * to add the requested obj to our list */ + bpy_pydriver_running(1); + + /* append driver owner object as the 1st ob in the list; + * we put it there to make sure it is not itself referenced in + * its pydriver expression */ + bpy_pydriver_appendToList(driver->ob); + + /* this will append any other ob referenced in expr (driver->name) + * or set the driver's error flag if driver's py expression fails */ + BPY_pydriver_eval(driver); + + bpy_pydriver_running(0); /* ok, we're done */ + + return bpy_pydriver_obArrayFromList(); /* NULL if eval failed */ +} + +/* This evals py driver expressions, 'expr' is a Python expression that + * should evaluate to a float number, which is returned. */ +float BPY_pydriver_eval(IpoDriver *driver) +{ + char *expr = NULL; + PyObject *retval, *floatval; + float result = 0.0f; /* default return */ + + if (!driver) return result; + + expr = driver->name; /* the py expression to be evaluated */ + if (!expr || expr[0]=='\0') return result; + + if (!bpy_pydriver_Dict) { + if (bpy_pydriver_create_dict() != 0) { + fprintf(stderr, "Pydriver error: couldn't create Python dictionary"); + return result; + } + } + + retval = PyRun_String(expr, Py_eval_input, bpy_pydriver_Dict, + bpy_pydriver_Dict); + + if (retval == NULL) { + return pydriver_error(driver); + } + else { + floatval = PyNumber_Float(retval); + Py_DECREF(retval); + } + + if (floatval == NULL) + return pydriver_error(driver); + else { + result = (float)PyFloat_AsDouble(floatval); + Py_DECREF(floatval); + } + + /* all fine, make sure the "invalid expression" flag is cleared */ + driver->flag &= ~IPO_DRIVER_FLAG_INVALID; + + return result; +} + /*****************************************************************************/ /* ScriptLinks */ /*****************************************************************************/ diff --git a/source/blender/python/api2_2x/EXPP_interface.c b/source/blender/python/api2_2x/EXPP_interface.c index 9f20db03c07..ee8b04ebde9 100644 --- a/source/blender/python/api2_2x/EXPP_interface.c +++ b/source/blender/python/api2_2x/EXPP_interface.c @@ -34,6 +34,9 @@ #include "EXPP_interface.h" #include "BLI_blenlib.h" +#include "MEM_guardedalloc.h" +#include "BLI_linklist.h" /* linked list: LinkNode struct and functions */ +#include "DNA_object_types.h" #include "DNA_space_types.h" /* for FILE_MAXDIR, FILE_MAXFILE */ #include "Blender.h" @@ -129,3 +132,99 @@ char *bpy_gethome(int append_scriptsdir) return NULL; } + +/* PyDrivers */ + +/* + * Pydrivers are Blender Ipo Drivers defined by Python expressions. + * We need to tell DAG about objects used in these expressions, so we + * eval each expression to collect the ob refs. in it. + */ + +/* these are checked for example in Object.c: M_Object_Get (Object.Get()) + * to collect the refs. */ +static int pydriver_running = 0; + +int bpy_during_pydriver(void) +{ + return pydriver_running; +} + +void bpy_pydriver_running(int state) +{ + pydriver_running = state; +} + +/* Obj references are collected in this extern linked list: */ +LinkNode *bpy_pydriver_oblist = NULL; + +void bpy_pydriver_freeList(void) +{ + BLI_linklist_free(bpy_pydriver_oblist, NULL); + bpy_pydriver_oblist = NULL; +} + +void bpy_pydriver_appendToList(struct Object *ob) +{ + LinkNode *ln = bpy_pydriver_oblist; + + /* check that the expression is not referencing its owner object */ + +/* XXX COMMENTED OUT TO TEST IF WE REALLY NEED TO IMPOSE THIS RESTRICTION + if (ln && ln->link) { + if (ob == (Object *)ln->link) { + PyErr_SetString(PyExc_AttributeError, + "Python driver expression can't reference its own object"); + return; + } + else + ln = ln->next; + } +*/ + while (ln) { /* is ob already in list? ... */ + if (ob == (Object *)ln->link) + break; + ln = ln->next; + } + + if (!ln) /* ... not yet, append it */ + BLI_linklist_append(&bpy_pydriver_oblist, (void *)ob); + + return; +} + +/* Get an array from our linked list of objs referenced in the + * current pydriver. The first node in the list is discarded, + * since it is the actual pydriver owner, which shouldn't be + * passed to the depsgraph (no self references). */ +struct Object **bpy_pydriver_obArrayFromList(void) +{ + Object **obarray = NULL; + + if (bpy_pydriver_oblist) { + int i; + short len = BLI_linklist_length(bpy_pydriver_oblist); + + if (len > 1) { + + obarray = (Object **)MEM_mallocN(sizeof(Object*)*len, + "pydriver array"); + + if (obarray) { + LinkNode *ln = bpy_pydriver_oblist; + ln = ln->next; /* skip first ob, which is the pydriver owner */ + + for (i = 0; i < len-1; i++) { + obarray[i] = (Object *)ln->link; + ln = ln->next; + } + + obarray[len-1] = NULL; /* NULL-terminated array */ + } + } + bpy_pydriver_freeList(); + } + + return obarray; +} + diff --git a/source/blender/python/api2_2x/EXPP_interface.h b/source/blender/python/api2_2x/EXPP_interface.h index dfcf7abf1da..80767af1c01 100644 --- a/source/blender/python/api2_2x/EXPP_interface.h +++ b/source/blender/python/api2_2x/EXPP_interface.h @@ -33,11 +33,24 @@ #ifndef EXPP_INTERFACE_H #define EXPP_INTERFACE_H +struct Object; struct Script; +struct LinkNode; + +extern struct LinkNode *bpy_pydriver_oblist; void initBlenderApi2_2x( void ); char *bpy_gethome( int append_scriptsdir ); void discardFromBDict( char *key ); void EXPP_Library_Close( void ); /* in Library.c, used by BPY_end_python */ +/* PyDrivers */ + +void bpy_pydriver_freeList(void); +void bpy_pydriver_appendToList(struct Object *ob); +struct Object **bpy_pydriver_obArrayFromList(void); + +int bpy_during_pydriver(void); +void bpy_pydriver_running(int state); + #endif /* EXPP_INTERFACE_H */ diff --git a/source/blender/python/api2_2x/Object.c b/source/blender/python/api2_2x/Object.c index db012729da6..1132f07a511 100644 --- a/source/blender/python/api2_2x/Object.c +++ b/source/blender/python/api2_2x/Object.c @@ -110,6 +110,7 @@ struct rctf; #include "Group.h" #include "Modifier.h" #include "gen_utils.h" +#include "EXPP_interface.h" #include "BIF_editkey.h" /* Defines for insertIpoKey */ @@ -787,6 +788,10 @@ PyObject *M_Object_Get( PyObject * self, PyObject * args ) buffer ); } + /* objects used in pydriver expressions need this */ + if (bpy_during_pydriver()) + bpy_pydriver_appendToList(object); + return Object_CreatePyObject( object ); } else { /* No argument has been given. Return a list of all objects. */ @@ -794,11 +799,17 @@ PyObject *M_Object_Get( PyObject * self, PyObject * args ) Link *link; int index; + /* do not allow Get() (w/o arguments) inside pydriver, otherwise + * we'd have to update all objects in the DAG */ + if (bpy_during_pydriver()) + return EXPP_ReturnPyObjError( PyExc_AttributeError, + "Object.Get requires an argument when used in pydrivers" ); + obj_list = PyList_New( BLI_countlist( &( G.main->object ) ) ); if( !obj_list ) return EXPP_ReturnPyObjError( PyExc_SystemError, - "List creation failed." ); + "List creation failed." ); link = G.main->object.first; index = 0; diff --git a/source/blender/src/drawipo.c b/source/blender/src/drawipo.c index 642a7a0602e..ffbc8a67f9d 100644 --- a/source/blender/src/drawipo.c +++ b/source/blender/src/drawipo.c @@ -84,6 +84,8 @@ #include "BSE_editipo_types.h" #include "BSE_editnla_types.h" +#include "BPY_extern.h" + #include "mydevice.h" #include "blendef.h" #include "butspace.h" // shouldnt be... @@ -1561,6 +1563,7 @@ static void draw_key(SpaceIpo *sipo, int visible) #define B_IPO_DRIVER 3405 #define B_IPO_REDR 3406 #define B_IPO_DEPCHANGE 3407 +#define B_IPO_DRIVERTYPE 3408 static float hspeed= 0; @@ -1755,7 +1758,12 @@ void do_ipobuts(unsigned short event) ei= get_active_editipo(); if(ei) { if(ei->icu->driver) { - if(G.sipo->blocktype==ID_KE || G.sipo->blocktype==ID_AC) + if (ei->icu->driver->type == IPO_DRIVER_TYPE_PYTHON) { + /* eval user's expression once for validity */ + BPY_pydriver_eval(ei->icu->driver); + DAG_scene_sort(G.scene); + } + else if(G.sipo->blocktype==ID_KE || G.sipo->blocktype==ID_AC) DAG_object_flush_update(G.scene, ob, OB_RECALC_DATA); else DAG_object_flush_update(G.scene, ob, OB_RECALC_OB); @@ -1814,14 +1822,39 @@ void do_ipobuts(unsigned short event) BIF_undo_push("Add/Remove Ipo driver"); } break; + case B_IPO_DRIVERTYPE: + ei= get_active_editipo(); + if(ei) { + if(ei->icu->driver) { + IpoDriver *driver= ei->icu->driver; + + if(driver->type == IPO_DRIVER_TYPE_PYTHON) { + /* pydriver expression shouldn't reference own ob, + * so we need to store ob ptr to check against it */ + driver->ob= ob; + } + else { + driver->ob= NULL; + driver->blocktype= ID_OB; + driver->adrcode= OB_LOC_X; + driver->flag &= ~IPO_DRIVER_FLAG_INVALID; + } + } + allqueue(REDRAWVIEW3D, 0); + allqueue(REDRAWIPO, 0); + allqueue(REDRAWBUTSEDIT, 0); + DAG_scene_sort(G.scene); + + BIF_undo_push("Change Ipo driver type"); + } + break; case B_IPO_DEPCHANGE: ei= get_active_editipo(); if(ei) { if(ei->icu->driver) { IpoDriver *driver= ei->icu->driver; - if(driver->flag & IPO_DRIVER_PYTHON) { - driver->ob= NULL; + if(driver->type == IPO_DRIVER_TYPE_PYTHON) { } else { if(driver->ob) { @@ -1918,14 +1951,18 @@ static void ipo_panel_properties(short cntrl) // IPO_HANDLER_PROPERTIES if(ei->icu && ei->icu->driver) { IpoDriver *driver= ei->icu->driver; - + uiDefBut(block, BUT, B_IPO_DRIVER, "Remove", 210,265,100,20, NULL, 0.0f, 0.0f, 0, 0, "Remove Driver for this Ipo Channel"); uiBlockBeginAlign(block); - uiDefIconButBitS(block, TOG, IPO_DRIVER_PYTHON, B_IPO_DEPCHANGE, ICON_PYTHON, 10,240,25,20, &driver->flag, 0, 0, 0, 0, "Use a one-line Python Expression as Driver"); - - if(driver->flag & IPO_DRIVER_PYTHON) { + uiDefIconButS(block, TOG, B_IPO_DRIVERTYPE, ICON_PYTHON, 10,240,25,20, &driver->type, (float)IPO_DRIVER_TYPE_NORMAL, (float)IPO_DRIVER_TYPE_PYTHON, 0, 0, "Use a one-line Python Expression as Driver"); + + if(driver->type == IPO_DRIVER_TYPE_PYTHON) { uiDefBut(block, TEX, B_IPO_REDR, "", 35,240,275,20, driver->name, 0, 127, 0, 0, "Python Expression"); + if(driver->flag & IPO_DRIVER_FLAG_INVALID) { + uiDefBut(block, LABEL, 0, "Error: invalid Python expression", + 5,215,230,19, NULL, 0, 0, 0, 0, ""); + } uiBlockEndAlign(block); } else { diff --git a/source/blender/src/usiblender.c b/source/blender/src/usiblender.c index e20fbf6fbad..8bfa493364a 100644 --- a/source/blender/src/usiblender.c +++ b/source/blender/src/usiblender.c @@ -758,6 +758,10 @@ void exit_usiblender(void) free_editArmature(); free_posebuf(); + /* before free_blender so py's gc happens while library still exists */ + /* needed at least for a rare sigsegv that can happen in pydrivers */ + BPY_end_python(); + free_blender(); /* blender.c, does entire library */ free_matcopybuf(); free_ipocopybuf(); @@ -784,8 +788,6 @@ void exit_usiblender(void) #ifdef WITH_QUICKTIME quicktime_exit(); #endif - - BPY_end_python(); if (!G.background) { BIF_resources_free(); |