Python experimental UI API
authorCampbell Barton <ideasman42@gmail.com>
Sat, 28 Feb 2009 13:27:45 +0000 (13:27 +0000)
committerCampbell Barton <ideasman42@gmail.com>
Sat, 28 Feb 2009 13:27:45 +0000 (13:27 +0000)
Can draw panels in the scripts space containing RNA and operator buttons.
* Added bpyui.register() so scripts can draw buttons and panels into the scripts space type.
* wrapped drawBlock, drawPanels and matchPanelsView2d
* Operator buttons take a python dictionary used to set the button defaults.
* BPY_getFileAndNum utility function to get the filename and line number python is currently running.

source/blender/editors/space_script/space_script.c
source/blender/python/BPY_extern.h
source/blender/python/intern/bpy_interface.c
source/blender/python/intern/bpy_operator.h
source/blender/python/intern/bpy_ui.c
source/blender/python/intern/bpy_util.c
source/blender/python/intern/bpy_util.h

index 52ef016497dc3955016f8df2caad67e6813d38c3..8fedcd8761fce188c88267879c7aa1ccecf8f456 100644 (file)
 
 #include "script_intern.h"     // own include
 
+
+//static script_run_python(char *funcname, )
+
+
 /* ******************** default callbacks for script space ***************** */
 
 static SpaceLink *script_new(const bContext *C)
@@ -139,7 +143,7 @@ static void script_main_area_init(wmWindowManager *wm, ARegion *ar)
 static void script_main_area_draw(const bContext *C, ARegion *ar)
 {
        /* draw entirely, view changes should be handled here */
-       // SpaceScript *sscript= (SpaceScript*)CTX_wm_space_data(C);
+       SpaceScript *sscript= (SpaceScript*)CTX_wm_space_data(C);
        View2D *v2d= &ar->v2d;
        float col[3];
        
@@ -151,7 +155,12 @@ static void script_main_area_draw(const bContext *C, ARegion *ar)
        UI_view2d_view_ortho(C, v2d);
                
        /* data... */
+       // BPY_run_python_script(C, "/root/blender-svn/blender25/test.py");
        
+       if (sscript->script) {
+               //BPY_run_python_script_space(scpt->script.filename);
+               BPY_run_script_space_draw(C, sscript);
+       }
        
        /* reset view matrix */
        UI_view2d_view_restore(C);
@@ -190,6 +199,8 @@ static void script_header_area_draw(const bContext *C, ARegion *ar)
 static void script_main_area_listener(ARegion *ar, wmNotifier *wmn)
 {
        /* context changes */
+       // XXX - Todo, need the ScriptSpace accessible to get the python script to run.
+       // BPY_run_script_space_listener()
 }
 
 /* only called once, from space/spacetypes.c */
@@ -213,7 +224,7 @@ void ED_spacetype_script(void)
        art->init= script_main_area_init;
        art->draw= script_main_area_draw;
        art->listener= script_main_area_listener;
-       art->keymapflag= ED_KEYMAP_VIEW2D;
+       art->keymapflag= ED_KEYMAP_VIEW2D|   ED_KEYMAP_UI|ED_KEYMAP_FRAMES; // XXX need to further test this ED_KEYMAP_UI is needed for button interaction
 
        BLI_addhead(&st->regiontypes, art);
        
index 35dba84d5535958db8f3f7969c225de4841e9ae3..219ab2d3c410a6077b3ae120acaad582bda0a7a6 100644 (file)
@@ -95,7 +95,14 @@ extern "C" {
        int BPY_menu_do_python( short menutype, int event );
        int BPY_menu_do_shortcut( short menutype, unsigned short key, unsigned short modifiers );
        int BPY_menu_invoke( struct BPyMenu *pym, short menutype );
-       void BPY_run_python_script( struct bContext *C, const char *filename );
+       
+       /* 2.5 UI Scripts */
+       void BPY_run_python_script( struct bContext *C, const char *filename ); // 2.5 working
+       int BPY_run_script_space_draw(struct bContext *C, struct SpaceScript * sc); // 2.5 working
+//     int BPY_run_script_space_listener(struct bContext *C, struct SpaceScript * sc, struct ARegion *ar, struct wmNotifier *wmn); // 2.5 working
+       
+       
+       
        int BPY_run_script(struct Script *script);
        void BPY_free_compiled_text( struct Text *text );
 
index 17802ec3a293d5b9fcf10e602836d7e22395ac8c..fcf748c6887befda4ef3253a6f5c8d5e34e70a3f 100644 (file)
@@ -12,6 +12,9 @@
 
 #include "bpy_rna.h"
 #include "bpy_operator.h"
+#include "bpy_ui.h"
+
+#include "DNA_space_types.h"
 
 
 /*****************************************************************************
@@ -107,3 +110,130 @@ void BPY_run_python_script( bContext *C, const char *fn )
        
        //BPY_end_python();
 }
+
+
+/* TODO - move into bpy_space.c ? */
+/* GUI interface routines */
+
+/* Copied from Draw.c */
+static void exit_pydraw( SpaceScript * sc, short err )
+{
+       Script *script = NULL;
+
+       if( !sc || !sc->script )
+               return;
+
+       script = sc->script;
+
+       if( err ) {
+               PyErr_Print(  );
+               script->flags = 0;      /* mark script struct for deletion */
+               SCRIPT_SET_NULL(script);
+               script->scriptname[0] = '\0';
+               script->scriptarg[0] = '\0';
+// XXX 2.5             error_pyscript();
+// XXX 2.5             scrarea_queue_redraw( sc->area );
+       }
+
+#if 0 // XXX 2.5
+       BPy_Set_DrawButtonsList(sc->but_refs);
+       BPy_Free_DrawButtonsList(); /*clear all temp button references*/
+#endif
+
+       sc->but_refs = NULL;
+       
+       Py_XDECREF( ( PyObject * ) script->py_draw );
+       Py_XDECREF( ( PyObject * ) script->py_event );
+       Py_XDECREF( ( PyObject * ) script->py_button );
+
+       script->py_draw = script->py_event = script->py_button = NULL;
+}
+
+static int bpy_run_script_init(bContext *C, SpaceScript * sc)
+{
+       if (sc->script==NULL) 
+               return 0;
+       
+       if (sc->script->py_draw==NULL && sc->script->scriptname[0] != '\0')
+               BPY_run_python_script(C, sc->script->scriptname);
+               
+       if (sc->script->py_draw==NULL)
+               return 0;
+       
+       return 1;
+}
+
+int BPY_run_script_space_draw(bContext *C, SpaceScript * sc)
+{
+       if (bpy_run_script_init(C, sc)) {
+               PyGILState_STATE gilstate = PyGILState_Ensure();
+               PyObject *result = PyObject_CallObject( sc->script->py_draw, NULL );
+               PyGILState_Release(gilstate);
+               
+               if (result==NULL)
+                       exit_pydraw(sc, 1);
+       }
+       return 1;
+}
+
+// XXX - not used yet, listeners dont get a context
+int BPY_run_script_space_listener(bContext *C, SpaceScript * sc)
+{
+       if (bpy_run_script_init(C, sc)) {
+               PyGILState_STATE gilstate = PyGILState_Ensure();
+               
+               PyObject *result = PyObject_CallObject( sc->script->py_draw, NULL );
+               PyGILState_Release(gilstate);
+               
+               if (result==NULL)
+                       exit_pydraw(sc, 1);
+       }
+       return 1;
+}
+
+#if 0
+/* called from the the scripts window, assume context is ok */
+int BPY_run_python_script_space(const char *modulename, const char *func)
+{
+       PyObject *py_dict, *py_result= NULL;
+       char pystring[512];
+       PyGILState_STATE gilstate;
+       
+       /* for calling the module function */
+       PyObject *py_func, 
+       
+       gilstate = PyGILState_Ensure();
+       
+       py_dict = CreateGlobalDictionary(C);
+       
+       PyObject *module = PyImport_ImportModule(scpt->script.filename);
+       if (module==NULL) {
+               PyErr_SetFormat(PyExc_SystemError, "could not import '%s'", scpt->script.filename);
+       }
+       else {
+               py_func = PyObject_GetAttrString(modulename, func);
+               if (py_func==NULL) {
+                       PyErr_SetFormat(PyExc_SystemError, "module has no function '%s.%s'\n", scpt->script.filename, func);
+               }
+               else {
+                       if (!PyCallable_Check(py_func)) {
+                               PyErr_SetFormat(PyExc_SystemError, "module item is not callable '%s.%s'\n", scpt->script.filename, func);
+                       }
+                       else {
+                               py_result= PyObject_CallObject(py_func, NULL); // XXX will need args eventually
+                       }
+               }
+       }
+       
+       if (!py_result)
+               PyErr_Print();
+       else
+               Py_DECREF( py_result );
+       
+       Py_XDECREF(module);
+       
+       
+       PyGILState_Release(gilstate);
+       return 1;
+}
+#endif
index fa12857fe19ecfec75b57db05b15f685b8792898..2dfd17bb01b9f7774a8751f38b8475a6d99183a5 100644 (file)
@@ -35,8 +35,9 @@
 extern PyTypeObject pyop_base_Type;
 extern PyTypeObject pyop_func_Type;
 
+#define BPy_OperatorBase_Check(v)      (PyObject_TypeCheck(v, &pyop_base_Type))
 #define BPy_OperatorFunc_Check(v)      (PyObject_TypeCheck(v, &pyop_func_Type))
-#define BPy_PropertyRNA_Check(v)       (PyObject_TypeCheck(v, &pyop_func_Type))
+
 
 typedef struct {
        PyObject_HEAD /* required python macro   */
index 946d9ec6185edf9ec125b11cd1447a6deaa1e8d4..a8b57e1aab6f8b1db0b4e3a0098082c0e215f655 100644 (file)
  */
 
 #include "bpy_ui.h"
+#include "bpy_util.h"
 #include "bpy_rna.h" /* for rna buttons */
+#include "bpy_operator.h" /* for setting button operator properties */
 #include "bpy_compat.h"
+#include "WM_types.h" /* for WM_OP_INVOKE_DEFAULT & friends */
 
 #include "BLI_dynstr.h"
 
@@ -33,6 +36,7 @@
 #include "BKE_context.h"
 
 #include "DNA_screen_types.h"
+#include "DNA_space_types.h" /* only for SpaceLink */
 #include "UI_interface.h"
 #include "WM_api.h"
 
@@ -74,14 +78,23 @@ static PyObject *Method_menuItemO( PyObject * self, PyObject * args )
 
 static PyObject *Method_defButO( PyObject * self, PyObject * args )
 {
-       PyObject *py_block;
+       uiBut *but;
+       PyObject *py_block, *py_keywords= NULL;
        char *opname, *butname, *tip;
        int exec, xco, yco, width, height;
        
-       if( !PyArg_ParseTuple( args, "O!sisiiiis:defButO", &PyCObject_Type, &py_block, &opname, &exec, &butname, &xco, &yco, &width, &height, &tip))
+       if( !PyArg_ParseTuple( args, "O!sisiiiis|O!:defButO", &PyCObject_Type, &py_block, &opname, &exec, &butname, &xco, &yco, &width, &height, &tip, &PyDict_Type, &py_keywords))
                return NULL;
        
-       return PyCObject_FromVoidPtr(uiDefButO(PyCObject_AsVoidPtr(py_block), BUT, opname, exec, butname, xco, yco, width, height, tip), NULL );
+       but= uiDefButO(PyCObject_AsVoidPtr(py_block), BUT, opname, exec, butname, xco, yco, width, height, tip);
+       
+       /* Optional python doctionary used to set python properties, just like how keyword args are used */
+       if (py_keywords && PyDict_Size(py_keywords)) {
+               if (PYOP_props_from_dict(uiButGetOperatorPtrRNA(but), py_keywords) == -1)
+                       return NULL;
+       }
+       
+       return PyCObject_FromVoidPtr(but, NULL);
 }
 
 static PyObject *Method_defAutoButR( PyObject * self, PyObject * args )
@@ -165,6 +178,40 @@ static PyObject *Method_endBlock( PyObject * self, PyObject * args )
        Py_RETURN_NONE;
 }
 
+static PyObject *Method_drawBlock( PyObject * self, PyObject * args )
+{
+       PyObject *py_context, *py_block;
+       
+       if( !PyArg_ParseTuple( args, "O!O!:drawBlock", &PyCObject_Type, &py_context, &PyCObject_Type, &py_block) )
+               return NULL;
+       
+       uiDrawBlock(PyCObject_AsVoidPtr(py_context), PyCObject_AsVoidPtr(py_block));
+       Py_RETURN_NONE;
+}
+
+static PyObject *Method_drawPanels( PyObject * self, PyObject * args )
+{
+       PyObject *py_context;
+       int align;
+       
+       if( !PyArg_ParseTuple( args, "O!i:drawPanels", &PyCObject_Type, &py_context, &align) )
+               return NULL;
+       
+       uiDrawPanels(PyCObject_AsVoidPtr(py_context), align);
+       Py_RETURN_NONE;
+}
+
+static PyObject *Method_matchPanelsView2d( PyObject * self, PyObject * args )
+{
+       PyObject *py_ar;
+       
+       if( !PyArg_ParseTuple( args, "O!:matchPanelsView2d", &PyCObject_Type, &py_ar) )
+               return NULL;
+       
+       uiMatchPanelsView2d(PyCObject_AsVoidPtr(py_ar));
+       Py_RETURN_NONE;
+}
+
 static PyObject *Method_popupBoundsBlock( PyObject * self, PyObject * args )
 {
        PyObject *py_block;
@@ -223,6 +270,55 @@ static PyObject *Method_newPanel( PyObject * self, PyObject * args )
        return PyLong_FromSize_t(uiNewPanel(PyCObject_AsVoidPtr(py_context), PyCObject_AsVoidPtr(py_area), PyCObject_AsVoidPtr(py_block), panelname, tabname, ofsx, ofsy, sizex, sizey));
 }
 
+/* similar to Draw.c */
+static PyObject *Method_register( PyObject * self, PyObject * args )
+{
+       PyObject *py_sl, *py_draw_func;
+       SpaceLink *sl;
+       if( !PyArg_ParseTuple( args, "O!O:register", &PyCObject_Type, &py_sl, &py_draw_func) )
+               return NULL;
+       
+       sl = PyCObject_AsVoidPtr(py_sl);
+       
+       if(sl->spacetype!=SPACE_SCRIPT) { // XXX todo - add a script space when needed
+               PyErr_SetString(PyExc_ValueError, "can only register in a script space");
+               return NULL;
+       }
+       else {
+               SpaceScript *scpt= (SpaceScript *)sl;
+               char *filename = NULL;
+               
+               if (scpt->script==NULL) {
+                       scpt->script = MEM_callocN(sizeof(Script), "ScriptRegister");
+               }
+               
+               BPY_getFileAndNum(&filename, NULL);
+               
+               if (filename) {
+                       strncpy(scpt->script->scriptname, filename, sizeof(scpt->script->scriptname));
+#if 0
+                       char *dot;
+                       dot = strchr(scpt->script->scriptname, '.'); /* remove extension */
+                       if (dot)
+                               *dot= '\0';
+#endif
+                       Py_XINCREF( py_draw_func );
+                       scpt->script->py_draw= (void *)py_draw_func;
+               }
+               else {
+                       return NULL; /* BPY_getFileAndNum sets the error */
+               }
+
+               if (filename==NULL) {
+                       return NULL;
+               }
+       }
+
+       Py_RETURN_NONE;
+}
+
+
+
 /* internal use only */
 static bContext *get_py_context__internal(void)
 {
@@ -255,6 +351,14 @@ static PyObject *Method_getScreenPtr( PyObject * self )
        return PyCObject_FromVoidPtr(screen, NULL);
 }
 
+static PyObject *Method_getSpacePtr( PyObject * self )
+{
+       bContext *C= get_py_context__internal();
+       
+       SpaceLink *sl= CTX_wm_space_data(C);
+       return PyCObject_FromVoidPtr(sl, NULL);
+}
+
 static PyObject *Method_getWindowPtr( PyObject * self )
 {
        bContext *C= get_py_context__internal();
@@ -272,20 +376,25 @@ static struct PyMethodDef ui_methods[] = {
        {"pupBlock", (PyCFunction)Method_pupBlock, METH_VARARGS, ""},
        {"beginBlock", (PyCFunction)Method_beginBlock, METH_VARARGS, ""},
        {"endBlock", (PyCFunction)Method_endBlock, METH_VARARGS, ""},
+       {"drawBlock", (PyCFunction)Method_drawBlock, METH_VARARGS, ""},
        {"popupBoundsBlock", (PyCFunction)Method_popupBoundsBlock, METH_VARARGS, ""},
        {"blockBeginAlign", (PyCFunction)Method_blockBeginAlign, METH_VARARGS, ""},
        {"blockEndAlign", (PyCFunction)Method_blockEndAlign, METH_VARARGS, ""},
        {"blockSetFlag", (PyCFunction)Method_blockSetFlag, METH_VARARGS, ""},
        {"newPanel", (PyCFunction)Method_newPanel, METH_VARARGS, ""},
+       {"drawPanels", (PyCFunction)Method_drawPanels, METH_VARARGS, ""},
+       {"matchPanelsView2d", (PyCFunction)Method_matchPanelsView2d, METH_VARARGS, ""},
+       
+       {"register", (PyCFunction)Method_register, METH_VARARGS, ""}, // XXX not sure about this - registers current script with the ScriptSpace, like Draw.Register()
        
        {"getRegonPtr", (PyCFunction)Method_getRegonPtr,        METH_NOARGS, ""}, // XXX Nasty, we really need to improve dealing with context!
        {"getAreaPtr", (PyCFunction)Method_getAreaPtr,          METH_NOARGS, ""},
        {"getScreenPtr", (PyCFunction)Method_getScreenPtr, METH_NOARGS, ""},
+       {"getSpacePtr", (PyCFunction)Method_getSpacePtr, METH_NOARGS, ""},
        {"getWindowPtr", (PyCFunction)Method_getWindowPtr, METH_NOARGS, ""},
        {NULL, NULL, 0, NULL}
 };
 
-#if PY_VERSION_HEX >= 0x03000000
 static struct PyModuleDef ui_module = {
        PyModuleDef_HEAD_INIT,
        "bpyui",
@@ -294,7 +403,6 @@ static struct PyModuleDef ui_module = {
        ui_methods,
        NULL, NULL, NULL, NULL
 };
-#endif
 
 PyObject *BPY_ui_module( void )
 {
@@ -316,6 +424,16 @@ PyObject *BPY_ui_module( void )
        PyModule_AddObject( submodule, "UI_BLOCK_KEEP_OPEN", PyLong_FromSize_t(UI_BLOCK_KEEP_OPEN) );
        PyModule_AddObject( submodule, "UI_BLOCK_POPUP", PyLong_FromSize_t(UI_BLOCK_POPUP) );
        
+       /* for executing operators (XXX move elsewhere) */
+       PyModule_AddObject( submodule, "WM_OP_INVOKE_DEFAULT", PyLong_FromSize_t(WM_OP_INVOKE_DEFAULT) );
+       PyModule_AddObject( submodule, "WM_OP_INVOKE_REGION_WIN", PyLong_FromSize_t(WM_OP_INVOKE_REGION_WIN) );
+       PyModule_AddObject( submodule, "WM_OP_INVOKE_AREA", PyLong_FromSize_t(WM_OP_INVOKE_AREA) );
+       PyModule_AddObject( submodule, "WM_OP_INVOKE_SCREEN", PyLong_FromSize_t(WM_OP_INVOKE_SCREEN) );
+       PyModule_AddObject( submodule, "WM_OP_EXEC_DEFAULT", PyLong_FromSize_t(WM_OP_EXEC_DEFAULT) );
+       PyModule_AddObject( submodule, "WM_OP_EXEC_REGION_WIN", PyLong_FromSize_t(WM_OP_EXEC_REGION_WIN) );
+       PyModule_AddObject( submodule, "WM_OP_EXEC_AREA", PyLong_FromSize_t(WM_OP_EXEC_AREA) );
+       PyModule_AddObject( submodule, "WM_OP_EXEC_SCREEN", PyLong_FromSize_t(WM_OP_EXEC_SCREEN) );
+       
        return submodule;
 }
 
index 63112295d18699bb6e5de993f38a8ab74af21157..31d15d8a69e0438bfe318832a83dc2d445b5dafb 100644 (file)
@@ -161,3 +161,43 @@ void PyObSpit(char *name, PyObject *var) {
        }
        fprintf(stderr, "\n");
 }
+
+void BPY_getFileAndNum(char **filename, int *lineno)
+{
+       PyObject *getframe, *frame;
+       PyObject *f_lineno, *f_code, *co_filename;
+       
+       if (filename)   *filename= NULL;
+       if (lineno)             *lineno = -1;
+       
+       getframe = PySys_GetObject("_getframe"); // borrowed
+       if (getframe) {
+               frame = PyObject_CallObject(getframe, NULL);
+               if (frame) {
+                       f_lineno= PyObject_GetAttrString(frame, "f_lineno");
+                       f_code= PyObject_GetAttrString(frame, "f_code");
+                       if (f_lineno && f_code) {
+                               co_filename= PyObject_GetAttrString(f_code, "co_filename");
+                               if (co_filename) {
+                                       
+                                       if (filename)   *filename = _PyUnicode_AsString(co_filename);
+                                       if (lineno)             *lineno = (int)PyLong_AsSsize_t(f_lineno);
+                                       
+                                       Py_DECREF(f_lineno);
+                                       Py_DECREF(f_code);
+                                       Py_DECREF(co_filename);
+                                       Py_DECREF(frame);
+                                       
+                                       return;
+                               }
+                       }
+               }
+       }
+       
+       Py_XDECREF(co_filename);
+       Py_XDECREF(f_lineno);
+       Py_XDECREF(f_code);
+       Py_XDECREF(frame);
+       
+       PyErr_SetString(PyExc_SystemError, "Could not access sys._getframe().f_code.co_filename");
+}
index 82cc602fcaba6854aca0b3199d458805fd988db4..4b0af97502572b8a11acde4e3bbfbfc087b655cf 100644 (file)
@@ -35,4 +35,4 @@ PyObject *BPY_flag_to_list(BPY_flag_def *flagdef, int flag);
 int BPY_flag_from_seq(BPY_flag_def *flagdef, PyObject *seq, int *flag);
 
 void PyObSpit(char *name, PyObject *var);
-
+void BPY_getFileAndNum(char **filename, int *lineno);