[Bf-blender-cvs] SVN commit: /data/svn/bf-blender [11257] branches/2-44-stable/blender/ source/blender/python/api2_2x/Draw.c: bugfix

Campbell Barton cbarton at metavr.com
Thu Jul 12 22:14:16 CEST 2007


Revision: 11257
          http://projects.blender.org/plugins/scmsvn/viewcvs.php?view=rev&root=bf-blender&revision=11257
Author:   campbellbarton
Date:     2007-07-12 22:14:16 +0200 (Thu, 12 Jul 2007)

Log Message:
-----------
bugfix
* sliders with realtime option enabled did not have the correct event offset. (probably my fault when adding UIBlock/callbacks)
* callbacks could crash if they were free'd before the script ran them,
  solved this by using weakrefs to the python function rather then the function its self in the buttons callback value.
  Also store a list these weakref's in the Draw module's C file that are removed when the callback function is deallocated by python
  (using pythons own weakref callback) - if the weakref is not in the weakref callback list then we know it has been deallocated and can report an error that it needs to be made global.

Tested this to work in UIBlock and normal button drawing.
  
Some other small cleanup also.

Modified Paths:
--------------
    branches/2-44-stable/blender/source/blender/python/api2_2x/Draw.c

Modified: branches/2-44-stable/blender/source/blender/python/api2_2x/Draw.c
===================================================================
--- branches/2-44-stable/blender/source/blender/python/api2_2x/Draw.c	2007-07-12 18:35:12 UTC (rev 11256)
+++ branches/2-44-stable/blender/source/blender/python/api2_2x/Draw.c	2007-07-12 20:14:16 UTC (rev 11257)
@@ -103,10 +103,10 @@
 static void spacescript_do_pywin_buttons( SpaceScript * sc,
 					  unsigned short event );
 
-static PyObject *Method_Exit( PyObject * self, PyObject * args );
+static PyObject *Method_Exit( PyObject * self );
 static PyObject *Method_Register( PyObject * self, PyObject * args );
 static PyObject *Method_Redraw( PyObject * self, PyObject * args );
-static PyObject *Method_Draw( PyObject * self, PyObject * args );
+static PyObject *Method_Draw( PyObject * self );
 static PyObject *Method_Create( PyObject * self, PyObject * args );
 static PyObject *Method_UIBlock( PyObject * self, PyObject * args );
 
@@ -134,12 +134,19 @@
 /* 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 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)";
@@ -389,11 +396,11 @@
 	MethodDef( PupStrInput ),
 	MethodDef( PupBlock ),
 	MethodDef( Image ),
-	MethodDef( Exit ),
+	{"Exit", (PyCFunction)Method_Exit, METH_NOARGS, Method_Exit_doc},
 	MethodDef( Redraw ),
-	MethodDef( Draw ),
+	{"Draw", (PyCFunction)Method_Draw, METH_NOARGS, Method_Draw_doc},
 	MethodDef( Register ),
-	{"PushButton", Method_Button, METH_VARARGS, Method_Button_doc},
+	{"PushButton", (PyCFunction)Method_Button, METH_VARARGS, Method_Button_doc},
 	MethodDef( BeginAlign ),
 	MethodDef( EndAlign),
 	{NULL, NULL, 0, NULL}
@@ -591,11 +598,7 @@
 
 static Button *newbutton( void )
 {
-	Button *but = NULL;
-	
-	but = ( Button * ) PyObject_NEW( Button, &Button_Type );
-
-	return but;
+	return ( Button * ) PyObject_NEW( Button, &Button_Type );
 }
 
 /* GUI interface routines */
@@ -710,7 +713,10 @@
 
 		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 */
+			 * 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 */
 			if (val >= EXPP_BUTTON_EVENTS_OFFSET && val < 0x4000)
 				spacescript_do_pywin_buttons(sc, val - EXPP_BUTTON_EVENTS_OFFSET);
 			return;
@@ -748,6 +754,14 @@
 	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:
@@ -814,13 +828,34 @@
 	Py_XDECREF( result );
 }
 
+PyObject * pycallback_weakref_dealloc(PyObject *self, PyObject *weakref)
+{
+	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;
+	}
+	PySequence_DelItem(callback_list, i);
+	Py_RETURN_NONE;
+}
 static void set_pycallback(uiBut *ubut, PyObject *callback)
 {
+	PyObject *weakref;
 	if (!callback || !PyCallable_Check(callback)) return;
-	uiButSetFunc(ubut, exec_but_callback, callback, ubut);
+	
+	/* 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);
 }
 
-static PyObject *Method_Exit( PyObject * self, PyObject * args )
+static PyObject *Method_Exit( PyObject * self )
 {
 	SpaceScript *sc;
 	Script *script;
@@ -832,10 +867,6 @@
 	else
 		Py_RETURN_NONE;
 
-	if( !PyArg_ParseTuple( args, "" ) )
-		return EXPP_ReturnPyObjError( PyExc_AttributeError,
-					      "expected empty argument list" );
-
 	exit_pydraw( sc, 0 );
 
 	script = sc->script;
@@ -950,7 +981,7 @@
 	Py_RETURN_NONE;
 }
 
-static PyObject *Method_Draw( PyObject * self, PyObject * args )
+static PyObject *Method_Draw( PyObject * self )
 {
 	/*@ If forced drawing is disable queue a redraw event instead */
 	if( EXPP_disable_force_draw ) {
@@ -958,10 +989,6 @@
 		Py_RETURN_NONE;
 	}
 
-	if( !PyArg_ParseTuple( args, "" ) )
-		return EXPP_ReturnPyObjError( PyExc_AttributeError,
-					      "expected empty argument list" );
-
 	scrarea_do_windraw( curarea );
 
 	screen_swapbuffers(  );
@@ -1200,13 +1227,13 @@
 	disable_where_script( 1 );
 
 	spacescript_do_pywin_buttons( curarea->spacedata.first,
-		(unsigned short)uiButGetRetVal( but ) );
+		(unsigned short)uiButGetRetVal( but ) -  EXPP_BUTTON_EVENTS_OFFSET);
 
 	/* XXX useless right now, investigate better before a bcon 5 */
 	ret = M_Window_Redraw( 0, ref );
 
 	Py_DECREF(ref);
-	if (ret) { Py_DECREF(ret); }
+	Py_XDECREF(ret);
 
 	disable_where_script( 0 );
 
@@ -1232,6 +1259,9 @@
 
 	UI_METHOD_ERRORCHECK;
 	
+	if(realtime && uiblock)
+		realtime = 0; /* realtime dosnt work with UIBlock */
+	
 	but = newbutton(  );
 
 	if( PyFloat_Check( inio ) ) {
@@ -1966,17 +1996,26 @@
 
 }
 
+static PyMethodDef bpycallback_weakref_dealloc[] = {
+	{"pycallback_weakref_dealloc", pycallback_weakref_dealloc, METH_O, ""}
+};
+
 PyObject *Draw_Init( void )
 {
 	PyObject *submodule, *dict;
-
+	
+	/* Weakref management - used for callbacks so we can
+	 * tell when a callback has been removed that a UI button referenced */
+	callback_list = PyList_New(0);
+	pycallback_weakref_dealloc__pyfunc = PyCFunction_New(bpycallback_weakref_dealloc, NULL);
+	
 	if( PyType_Ready( &Button_Type) < 0)
 		Py_RETURN_NONE;
 
 	submodule = Py_InitModule3( "Blender.Draw", Draw_methods, Draw_doc );
 
 	dict = PyModule_GetDict( submodule );
-
+	
 #define EXPP_ADDCONST(x) \
 	EXPP_dict_set_item_str(dict, #x, PyInt_FromLong(x))
 





More information about the Bf-blender-cvs mailing list