[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [11387] trunk/blender/source/blender: = Draw Module Fixed=

Joseph Eagar joeedh at gmail.com
Fri Jul 27 08:14:25 CEST 2007


Revision: 11387
          http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=11387
Author:   joeedh
Date:     2007-07-27 08:14:25 +0200 (Fri, 27 Jul 2007)

Log Message:
-----------
=Draw Module Fixed=

This commit fixes the Draw module.  All buttons/widgets created via the Draw
module in a SpaceScript area are now inserted into a global list attached to
the SpaceScript data.  This list is cleared before each draw, when freeing
the space, and when the area is switched to another space.c

This is necessary to prevent Blender's internal UI code from getting invalid
pointers to python data.  In addition, it allows storing widget tooltips
inside the python Button objects, which solves that little bit of stupidity.

Note that this reverts the previous weaklist solution.  In fact, I had to go
over each previous commit by Campbell after this code originally branched
before the weaklist commit and re-add each commit.  So if anything is
missing, just tell me, or feel free to re-add it.

Modified Paths:
--------------
    trunk/blender/source/blender/blenloader/intern/writefile.c
    trunk/blender/source/blender/makesdna/DNA_space_types.h
    trunk/blender/source/blender/python/BPY_extern.h
    trunk/blender/source/blender/python/api2_2x/Draw.c
    trunk/blender/source/blender/python/api2_2x/Draw.h
    trunk/blender/source/blender/src/drawscript.c
    trunk/blender/source/blender/src/space.c

Modified: trunk/blender/source/blender/blenloader/intern/writefile.c
===================================================================
--- trunk/blender/source/blender/blenloader/intern/writefile.c	2007-07-27 05:06:05 UTC (rev 11386)
+++ trunk/blender/source/blender/blenloader/intern/writefile.c	2007-07-27 06:14:25 UTC (rev 11387)
@@ -1582,6 +1582,8 @@
 					writestruct(wd, DATA, "SpaceText", 1, sl);
 				}
 				else if(sl->spacetype==SPACE_SCRIPT) {
+					SpaceScript *sc = (SpaceScript*)sl;
+					sc->but_refs = NULL;
 					writestruct(wd, DATA, "SpaceScript", 1, sl);
 				}
 				else if(sl->spacetype==SPACE_ACTION) {

Modified: trunk/blender/source/blender/makesdna/DNA_space_types.h
===================================================================
--- trunk/blender/source/blender/makesdna/DNA_space_types.h	2007-07-27 05:06:05 UTC (rev 11386)
+++ trunk/blender/source/blender/makesdna/DNA_space_types.h	2007-07-27 06:14:25 UTC (rev 11387)
@@ -292,9 +292,10 @@
 	struct ScrArea *area;
 	struct Script *script;
 
-	int pad2;
 	short flags, menunr;
-
+	int pad1;
+	
+	void *but_refs;
 } SpaceScript;
 
 typedef struct SpaceTime {

Modified: trunk/blender/source/blender/python/BPY_extern.h
===================================================================
--- trunk/blender/source/blender/python/BPY_extern.h	2007-07-27 05:06:05 UTC (rev 11386)
+++ trunk/blender/source/blender/python/BPY_extern.h	2007-07-27 06:14:25 UTC (rev 11387)
@@ -51,6 +51,25 @@
 extern "C" {
 #endif
 
+	/*These two next functions are important for making sure the Draw module
+	  works correctly.  Before calling any gui callback using the Draw module,
+	  the following code must be executed:
+	  
+		if (some_drawspace_pylist) {
+			BPy_Set_DrawButtonsList(some_drawspace_pylist->but_refs);
+			BPy_Free_DrawButtonsList();
+		}
+		some_drawspace_pylist = PyList_New(0);
+		BPy_Set_DrawButtonsList(some_drawspace_pylist);
+
+      Also, BPy_Free_DrawButtonsList() must be called as necassary when a drawspace
+      with python callbacks is destroyed.
+      
+      This is necassary to avoid blender buttons storing invalid pointers to freed
+      python data.*/
+	void BPy_Set_DrawButtonsList(void *list);
+	void BPy_Free_DrawButtonsList(void);
+	
 	void BPY_pyconstraint_eval(struct bPythonConstraint *con, float ownermat[][4], float targetmat[][4]);
 	void BPY_pyconstraint_settings(void *arg1, void *arg2);
 	int BPY_pyconstraint_targets(struct bPythonConstraint *con, float targetmat[][4]);

Modified: trunk/blender/source/blender/python/api2_2x/Draw.c
===================================================================
--- trunk/blender/source/blender/python/api2_2x/Draw.c	2007-07-27 05:06:05 UTC (rev 11386)
+++ trunk/blender/source/blender/python/api2_2x/Draw.c	2007-07-27 06:14:25 UTC (rev 11387)
@@ -134,19 +134,13 @@
 /* CLEVER NUMBUT */
 static PyObject *Method_PupBlock( PyObject * self, PyObject * args );
 
-PyObject * pycallback_weakref_dealloc(PyObject *self, PyObject *weakref);
-/* python callable */
-PyObject * pycallback_weakref_dealloc__pyfunc;
+static uiBlock *Get_uiBlock( void );
 
-static uiBlock *Get_uiBlock( void );
 static void py_slider_update( void *butv, void *data2_unused );
 
 /* hack to get 1 block for the UIBlock, only ever 1 at a time */
 static uiBlock *uiblock=NULL;
 
-/* store weakref's to callbacks here */
-static PyObject *callback_list; 
-
 static char Draw_doc[] = "The Blender.Draw submodule";
 
 static char Method_UIBlock_doc[] = "(drawfunc, x,y) - Popup dialog where buttons can be drawn (expemental)";
@@ -362,6 +356,12 @@
 
 static char Method_Exit_doc[] = "() - Exit the windowing interface";
 
+/*This is needed for button callbacks.  Any button that uses a callback gets added to this list.
+  On the C side of drawing begin, this list should be cleared.
+  Each entry is a tuple of the form (button, callback py object)
+*/
+PyObject *M_Button_List = NULL;
+
 /*
 * here we engage in some macro trickery to define the PyMethodDef table
 */
@@ -602,7 +602,13 @@
 
 static Button *newbutton( void )
 {
-	return ( Button * ) PyObject_NEW( Button, &Button_Type );
+	Button *but = NULL;
+	
+	but = ( Button * ) PyObject_NEW( Button, &Button_Type );
+	but->tooltip[0] = 0; /*NULL-terminate tooltip string*/
+	but->tooltip[255] = 0; /*necassary to insure we always have a NULL-terminated string, as
+	                         according to the docs strncpy doesn't do this for us.*/
+	return but;
 }
 
 /* GUI interface routines */
@@ -623,6 +629,10 @@
 		scrarea_queue_redraw( sc->area );
 	}
 
+	BPy_Set_DrawButtonsList(sc->but_refs);
+	BPy_Free_DrawButtonsList(); /*clear all temp button references*/
+	sc->but_refs = NULL;
+	
 	Py_XDECREF( ( PyObject * ) script->py_draw );
 	Py_XDECREF( ( PyObject * ) script->py_event );
 	Py_XDECREF( ( PyObject * ) script->py_button );
@@ -676,6 +686,13 @@
 			    UI_HELV, curarea->win );
 
 	if( script->py_draw ) {
+		if (sc->but_refs) {
+			BPy_Set_DrawButtonsList(sc->but_refs);
+			BPy_Free_DrawButtonsList(); /*clear all temp button references*/
+		}
+		sc->but_refs = PyList_New(0);
+		BPy_Set_DrawButtonsList(sc->but_refs);
+		
 		glPushAttrib( GL_ALL_ATTRIB_BITS );
 		exec_callback( sc, script->py_draw, Py_BuildValue( "()" ) );
 		glPopAttrib(  );
@@ -717,10 +734,7 @@
 
 		if (event == UI_BUT_EVENT) {
 			/* check that event is in free range for script button events;
-			 * read the comment before check_button_event() below to understand
-			 * 
-			 * This will never run from UIBlock so no need to check if uiblock==NULL
-			 * And only sub EXPP_BUTTON_EVENTS_OFFSET in that case */
+			 * read the comment before check_button_event() below to understand */
 			if (val >= EXPP_BUTTON_EVENTS_OFFSET && val < 0x4000)
 				spacescript_do_pywin_buttons(sc, val - EXPP_BUTTON_EVENTS_OFFSET);
 			return;
@@ -758,14 +772,6 @@
 	if (callback==NULL || callback == Py_None)
 		return;
 	
-	if (callback) {
-		if (!PySequence_Contains(callback_list, callback)) {
-			printf("Error, the callback is out of scope.\n\tmake the callback global to resolve this.\n");
-			return;
-		}
-		callback = PyWeakref_GetObject(callback);
-	}
-
 	/* Button types support
 	case MENU:	
 	case TEX:
@@ -832,33 +838,51 @@
 	Py_XDECREF( result );
 }
 
-PyObject * pycallback_weakref_dealloc(PyObject *self, PyObject *weakref)
+/*note that this function populates the drawbutton ref lists.*/
+static void set_pycallback(uiBut *ubut, PyObject *callback, Button *but)
 {
-	int i = PySequence_Index(callback_list, weakref);
-	if (i==-1) {
-		printf("callback weakref internal error, weakref not in list\n\tthis should never happen.\n");
-		return NULL;
+	PyObject *tuple;
+	if (!callback || !PyCallable_Check(callback)) {
+		if (M_Button_List && but) {
+			PyList_Append(M_Button_List, (PyObject*)but);
+		}
+		return;
 	}
-	PySequence_DelItem(callback_list, i);
-	Py_RETURN_NONE;
+	
+	if (M_Button_List) {
+		if (but) tuple = PyTuple_New(2);
+		else tuple = PyTuple_New(1);
+		
+		/*the tuple API mandates this*/
+		Py_XINCREF(callback);
+		Py_XINCREF(but); /*this checks for NULL*/
+		
+		PyTuple_SET_ITEM(tuple, 0, callback);
+		if (but) PyTuple_SET_ITEM(tuple, 1, (PyObject*)but);
+		
+		PyList_Append(M_Button_List, tuple);
+		Py_DECREF(tuple); /*we have to do this to aovid double references.*/
+		
+		uiButSetFunc(ubut, exec_but_callback, callback, ubut);
+	}
 }
-static void set_pycallback(uiBut *ubut, PyObject *callback)
+
+void BPy_Set_DrawButtonsList(void *list)
 {
-	PyObject *weakref;
-	if (!callback || !PyCallable_Check(callback)) return;
-	
-	/* This works in most cases except where there are local functions
-	 * that are deallocated so we must use weakrefs, will complain rather then crashing */
-	/*uiButSetFunc(ubut, exec_but_callback, callback, ubut);*/
-	
-	weakref = PyWeakref_NewRef(callback, pycallback_weakref_dealloc__pyfunc);
-	PyList_Append(callback_list, weakref);
-	Py_DECREF(weakref);
-	
-	/*printf("adding weakref, totlength %i\n", PyList_Size(callback_list));*/
-	uiButSetFunc(ubut, exec_but_callback, weakref, ubut);
+	M_Button_List = list;
 }
 
+/*this MUST be called after doing UI stuff.*/
+void BPy_Free_DrawButtonsList(void)
+{
+	/*Clear the list.*/
+	if (M_Button_List) {
+		PyList_SetSlice(M_Button_List, 0, PyList_Size(M_Button_List), NULL);
+		Py_DECREF(M_Button_List);
+		M_Button_List = NULL;
+	}
+}
+
 static PyObject *Method_Exit( PyObject * self )
 {
 	SpaceScript *sc;
@@ -1047,6 +1071,7 @@
 	return (PyObject*) but;
 }
 
+
 static PyObject *Method_UIBlock( PyObject * self, PyObject * args )
 {
 	PyObject *val = NULL;
@@ -1060,13 +1085,15 @@
 	if (uiblock)
 		return EXPP_ReturnPyObjError( PyExc_RuntimeError,
 	      "cannot run more then 1 UIBlock at a time" );
+
+	BPy_Set_DrawButtonsList(PyList_New(0));
 	
 	mywinset(G.curscreen->mainwin);
 	uiblock= uiNewBlock(&listb, "numbuts", UI_EMBOSS, UI_HELV, G.curscreen->mainwin);
 	
 	uiBlockSetFlag(uiblock, UI_BLOCK_LOOP|UI_BLOCK_REDRAW);
 	result = PyObject_CallObject( val, Py_BuildValue( "()" ) );
-
+	
 	if (!result) {
 		PyErr_Print(  );
 		error( "Python script error: check console" );
@@ -1076,11 +1103,17 @@
 	}
 	uiFreeBlocks(&listb);
 	uiblock = NULL;
+	BPy_Free_DrawButtonsList(); /*clear all temp button references*/
 	
 	Py_XDECREF( result );
 	Py_RETURN_NONE;
 }
 
+void Set_uiBlock(uiBlock *block)
+{
+	uiblock = block;
+}
+
 static uiBlock *Get_uiBlock( void )
 {
 	char butblock[32];
@@ -1149,7 +1182,7 @@
 	block = Get_uiBlock(  );
 	if( block ) {
 		uiBut *ubut = uiDefBut( block, BUT, event, name, (short)x, (short)y, (short)w, (short)h, 0, 0, 0, 0, 0, tip );
-		set_pycallback(ubut, callback);
+		set_pycallback(ubut, callback, NULL);
 	}
 	Py_RETURN_NONE;
 }
@@ -1173,12 +1206,13 @@
 	but = newbutton(  );
 	but->type = BINT_TYPE;
 	but->val.asint = def;
-
+	if (tip) strncpy(but->tooltip, tip, BPY_MAX_TOOLTIP);
+	
 	block = Get_uiBlock(  );
 	if( block ) {
 		uiBut *ubut = uiDefButI( block, MENU, event, name, (short)x, (short)y, (short)w, (short)h,
-			   &but->val.asint, 0, 0, 0, 0, tip );
-		set_pycallback(ubut, callback);
+			   &but->val.asint, 0, 0, 0, 0, but->tooltip );
+		set_pycallback(ubut, callback, but);
 	}
 	return ( PyObject * ) but;
 }
@@ -1202,12 +1236,13 @@
 	but = newbutton(  );
 	but->type = BINT_TYPE;
 	but->val.asint = def;
-
+	if (tip) strncpy(but->tooltip, tip, BPY_MAX_TOOLTIP);
+	
 	block = Get_uiBlock(  );
 	if( block ) {
 		uiBut *ubut = uiDefButI( block, TOG, event, name, (short)x, (short)y, (short)w, (short)h,
-			   &but->val.asint, 0, 0, 0, 0, tip );
-		set_pycallback(ubut, callback);

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list