Support for the C Macro system in Python.
authorMartin Poirier <theeth@yahoo.com>
Sat, 5 Dec 2009 19:27:26 +0000 (19:27 +0000)
committerMartin Poirier <theeth@yahoo.com>
Sat, 5 Dec 2009 19:27:26 +0000 (19:27 +0000)
Basic definition works like a python operator but you derive from "bpy.types.Macro" instead.
Operators are added to the macro after it has been added with "bpy.ops.add_macro" through the class method "define" which takes an operator id and returns an OperatorMacroType (new RNA type) for which properties can then be defined to be passed to the operator when run.

Example: http://blenderartists.org/~theeth/bf/macro.py

Using this system, it should be easy to add an operator to the console that converts selected lines into a macro or even a more generic record macro system.

release/scripts/modules/bpy/ops.py
release/scripts/modules/bpy_types.py
source/blender/makesrna/RNA_access.h
source/blender/makesrna/intern/rna_wm.c
source/blender/python/intern/bpy_operator.c
source/blender/python/intern/bpy_operator_wrap.c
source/blender/python/intern/bpy_operator_wrap.h
source/blender/windowmanager/WM_api.h
source/blender/windowmanager/intern/wm_operators.c
source/blenderplayer/bad_level_call_stubs/stubs.c

index 5b3009db2bf4ad3c6dccefbe00cb8ac5b3cba30c..c8218d6703c390c6d6b1cd2e1b7ad056a3ad7612 100644 (file)
@@ -23,6 +23,7 @@ from _bpy import ops as ops_module
 
 op_add = ops_module.add
 op_remove = ops_module.remove
+op_add_macro = ops_module.add_macro
 op_dir = ops_module.dir
 op_call = ops_module.call
 op_as_string = ops_module.as_string
@@ -58,6 +59,9 @@ class bpy_ops(object):
 
     def add(self, pyop):
         op_add(pyop)
+    
+    def add_macro(self, pyop):
+        op_add_macro(pyop)
 
     def remove(self, pyop):
         op_remove(pyop)
index 565f0e4da1b9e50b91117108dea01ec1749c8a0b..5a8d6ceedf629e690646468cab7cb58e7b04c293 100644 (file)
@@ -252,6 +252,13 @@ class OrderedMeta(type):
 class Operator(StructRNA, metaclass=OrderedMeta):
     pass
 
+class Macro(StructRNA, metaclass=OrderedMeta):
+    # bpy_types is imported before ops is defined
+    # so we have to do a local import on each run
+    @classmethod
+    def define(self, opname):
+        from _bpy import ops
+        return ops.macro_define(self, opname)
 
 class Menu(StructRNA):
     
index 36653f8c06e7ee89fb38bb3cb6f82368d21daf2b..959109f7bbb770cd3aaa3371c2079a5543f3a0c3 100644 (file)
@@ -323,6 +323,7 @@ extern StructRNA RNA_OperatorFileListElement;
 extern StructRNA RNA_OperatorMousePath;
 extern StructRNA RNA_OperatorProperties;
 extern StructRNA RNA_OperatorStrokeElement;
+extern StructRNA RNA_OperatorTypeMacro;
 extern StructRNA RNA_OrController;
 extern StructRNA RNA_OutflowFluidSettings;
 extern StructRNA RNA_PackedFile;
index 10e2781423680764d4ebbf9d87e24726c26c9329..35f2a31b7efcbf82b57576c1c9084c92d9761b66 100644 (file)
@@ -309,6 +309,12 @@ static PointerRNA rna_Operator_properties_get(PointerRNA *ptr)
        return rna_pointer_inherit_refine(ptr, op->type->srna, op->properties);
 }
 
+static PointerRNA rna_OperatorTypeMacro_properties_get(PointerRNA *ptr)
+{
+       wmOperatorTypeMacro *otmacro= (wmOperatorTypeMacro*)ptr->data;
+       wmOperatorType *ot = WM_operatortype_exists(otmacro->idname);
+       return rna_pointer_inherit_refine(ptr, ot->srna, otmacro->properties);
+}
 
 static void rna_Event_ascii_get(PointerRNA *ptr, char *value)
 {
@@ -593,6 +599,50 @@ static void rna_def_operator(BlenderRNA *brna)
        RNA_def_struct_idproperties_func(srna, "rna_OperatorProperties_idproperties");
 }
 
+static void rna_def_macro_operator(BlenderRNA *brna)
+{
+       StructRNA *srna;
+       PropertyRNA *prop;
+
+       srna= RNA_def_struct(brna, "Macro", NULL);
+       RNA_def_struct_ui_text(srna, "Macro Operator", "Storage of a macro operator being executed, or registered after execution.");
+       RNA_def_struct_sdna(srna, "wmOperator");
+
+       prop= RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+       RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+       RNA_def_property_string_funcs(prop, "rna_Operator_name_get", "rna_Operator_name_length", NULL);
+       RNA_def_property_ui_text(prop, "Name", "");
+       RNA_def_struct_name_property(srna, prop);
+
+       prop= RNA_def_property(srna, "properties", PROP_POINTER, PROP_NONE);
+       RNA_def_property_flag(prop, PROP_NEVER_NULL);
+       RNA_def_property_struct_type(prop, "OperatorProperties");
+       RNA_def_property_ui_text(prop, "Properties", "");
+       RNA_def_property_pointer_funcs(prop, "rna_Operator_properties_get", NULL, NULL);
+}
+
+static void rna_def_operator_type_macro(BlenderRNA *brna)
+{
+       StructRNA *srna;
+       PropertyRNA *prop;
+
+       srna= RNA_def_struct(brna, "OperatorTypeMacro", NULL);
+       RNA_def_struct_ui_text(srna, "OperatorTypeMacro", "Storage of a sub operator in a macro after it has been added.");
+       RNA_def_struct_sdna(srna, "wmOperatorTypeMacro");
+
+//     prop= RNA_def_property(srna, "name", PROP_STRING, PROP_NONE);
+//     RNA_def_property_clear_flag(prop, PROP_EDITABLE);
+//     RNA_def_property_string_sdna(prop, NULL, "idname");
+//     RNA_def_property_ui_text(prop, "Name", "Name of the sub operator.");
+//     RNA_def_struct_name_property(srna, prop);
+
+       prop= RNA_def_property(srna, "properties", PROP_POINTER, PROP_NONE);
+       RNA_def_property_flag(prop, PROP_NEVER_NULL);
+       RNA_def_property_struct_type(prop, "OperatorProperties");
+       RNA_def_property_ui_text(prop, "Properties", "");
+       RNA_def_property_pointer_funcs(prop, "rna_OperatorTypeMacro_properties_get", NULL, NULL);
+}
+
 static void rna_def_operator_utils(BlenderRNA *brna)
 {
        StructRNA *srna;
@@ -911,6 +961,8 @@ void RNA_def_wm(BlenderRNA *brna)
        rna_def_operator(brna);
        rna_def_operator_utils(brna);
        rna_def_operator_filelist_element(brna);
+       rna_def_macro_operator(brna);
+       rna_def_operator_type_macro(brna);
        rna_def_event(brna);
        rna_def_window(brna);
        rna_def_windowmanager(brna);
index 6ae63f2ab6569deaa7bc1ec5c8ba454e0077b339..b4a2fb36a978d8bbf320f781cc9c1cb1ab0c609b 100644 (file)
@@ -60,7 +60,7 @@ static PyObject *pyop_call( PyObject * self, PyObject * args)
        if (!PyArg_ParseTuple(args, "sO|O!i:_bpy.ops.call", &opname, &context_dict, &PyDict_Type, &kw, &context))
                return NULL;
 
-       ot= WM_operatortype_find(opname, TRUE);
+       ot= WM_operatortype_exists(opname);
 
        if (ot == NULL) {
                PyErr_Format( PyExc_SystemError, "_bpy.ops.call: operator \"%s\"could not be found", opname);
@@ -245,6 +245,8 @@ PyObject *BPY_operator_module( void )
        static PyMethodDef pyop_dir_meth =              {"dir", (PyCFunction) pyop_dir, METH_NOARGS, NULL};
        static PyMethodDef pyop_getrna_meth =   {"get_rna", (PyCFunction) pyop_getrna, METH_O, NULL};
        static PyMethodDef pyop_add_meth =              {"add", (PyCFunction) PYOP_wrap_add, METH_O, NULL};
+       static PyMethodDef pyop_add_macro_meth ={"add_macro", (PyCFunction) PYOP_wrap_add_macro, METH_O, NULL};
+       static PyMethodDef pyop_macro_def_meth ={"macro_define", (PyCFunction) PYOP_wrap_macro_define, METH_VARARGS, NULL};
        static PyMethodDef pyop_remove_meth =   {"remove", (PyCFunction) PYOP_wrap_remove, METH_O, NULL};
 
        PyObject *submodule = PyModule_New("_bpy.ops");
@@ -255,6 +257,8 @@ PyObject *BPY_operator_module( void )
        PyModule_AddObject( submodule, "dir",           PyCFunction_New(&pyop_dir_meth,         NULL) );
        PyModule_AddObject( submodule, "get_rna",       PyCFunction_New(&pyop_getrna_meth,      NULL) );
        PyModule_AddObject( submodule, "add",           PyCFunction_New(&pyop_add_meth,         NULL) );
+       PyModule_AddObject( submodule, "add_macro",     PyCFunction_New(&pyop_add_macro_meth,           NULL) );
+       PyModule_AddObject( submodule, "macro_define",PyCFunction_New(&pyop_macro_def_meth,             NULL) );
        PyModule_AddObject( submodule, "remove",        PyCFunction_New(&pyop_remove_meth,      NULL) );
 
        return submodule;
index ff49d381dd1440bbee26804ea8022ed2a3df6182..cd5b07565312227875fe63a1f7fe8c0f8cbe3ea5 100644 (file)
@@ -342,6 +342,80 @@ void PYTHON_OT_wrapper(wmOperatorType *ot, void *userdata)
        }
 }
 
+void PYTHON_OT_MACRO_wrapper(wmOperatorType *ot, void *userdata)
+{
+       PyObject *py_class = (PyObject *)userdata;
+       PyObject *item;
+
+       /* identifiers */
+       item= PyObject_GetAttrString(py_class, PYOP_ATTR_IDNAME_BL);
+       ot->idname= _PyUnicode_AsString(item);
+       Py_DECREF(item);
+
+       item= PyObject_GetAttrString(py_class, PYOP_ATTR_UINAME);
+       if (item) {
+               ot->name= _PyUnicode_AsString(item);
+               Py_DECREF(item);
+       }
+       else {
+               ot->name= ot->idname;
+               PyErr_Clear();
+       }
+
+       item= PyObject_GetAttrString(py_class, PYOP_ATTR_DESCRIPTION);
+       ot->description= (item && PyUnicode_Check(item)) ? _PyUnicode_AsString(item):"undocumented python operator";
+       Py_XDECREF(item);
+
+       if (PyObject_HasAttrString(py_class, "poll"))
+               ot->pyop_poll= PYTHON_OT_poll;
+       if (PyObject_HasAttrString(py_class, "draw"))
+               ot->ui= PYTHON_OT_draw;
+
+       ot->pyop_data= userdata;
+
+       /* flags */
+       ot->flag= OPTYPE_MACRO; /* macro at least */
+
+       item= PyObject_GetAttrString(py_class, PYOP_ATTR_REGISTER);
+       if (item) {
+               ot->flag |= PyObject_IsTrue(item)!=0 ? OPTYPE_REGISTER:0;
+               Py_DECREF(item);
+       }
+       else {
+               PyErr_Clear();
+       }
+       item= PyObject_GetAttrString(py_class, PYOP_ATTR_UNDO);
+       if (item) {
+               ot->flag |= PyObject_IsTrue(item)!=0 ? OPTYPE_UNDO:0;
+               Py_DECREF(item);
+       }
+       else {
+               PyErr_Clear();
+       }
+
+       /* Can't use this because it returns a dict proxy
+        *
+        * item= PyObject_GetAttrString(py_class, "__dict__");
+        */
+       item= ((PyTypeObject*)py_class)->tp_dict;
+       if(item) {
+               /* only call this so pyrna_deferred_register_props gives a useful error
+                * WM_operatortype_append_macro_ptr will call RNA_def_struct_identifier
+                * later */
+               RNA_def_struct_identifier(ot->srna, ot->idname);
+
+               if(pyrna_deferred_register_props(ot->srna, item)!=0) {
+                       /* failed to register operator props */
+                       PyErr_Print();
+                       PyErr_Clear();
+
+               }
+       }
+       else {
+               PyErr_Clear();
+       }
+}
+
 
 /* pyOperators - Operators defined IN Python */
 PyObject *PYOP_wrap_add(PyObject *self, PyObject *py_class)
@@ -407,6 +481,116 @@ PyObject *PYOP_wrap_add(PyObject *self, PyObject *py_class)
        Py_RETURN_NONE;
 }
 
+/* pyOperators - Macro Operators defined IN Python */
+PyObject *PYOP_wrap_add_macro(PyObject *self, PyObject *py_class)
+{
+       PyObject *base_class, *item;
+       wmOperatorType *ot;
+
+
+       char *idname= NULL;
+       char idname_bl[OP_MAX_TYPENAME]; /* converted to blender syntax */
+
+       static struct BPY_class_attr_check pyop_class_attr_values[]= {
+               {PYOP_ATTR_IDNAME,              's', -1, OP_MAX_TYPENAME-3,     0}, /* -3 because a.b -> A_OT_b */
+               {PYOP_ATTR_UINAME,              's', -1,-1,     BPY_CLASS_ATTR_OPTIONAL},
+               {PYOP_ATTR_DESCRIPTION, 's', -1,-1,     BPY_CLASS_ATTR_NONE_OK},
+               {"poll",                                'f', 2, -1, BPY_CLASS_ATTR_OPTIONAL},
+               {"draw",                                'f', 2, -1, BPY_CLASS_ATTR_OPTIONAL},
+               {NULL, 0, 0, 0}
+       };
+
+       //PyObject bpy_mod= PyDict_GetItemString(PyEval_GetGlobals(), "bpy");
+       PyObject *bpy_mod= PyImport_ImportModuleLevel("bpy", NULL, NULL, NULL, 0);
+       base_class = PyObject_GetAttrStringArgs(bpy_mod, 2, "types", "Macro");
+       Py_DECREF(bpy_mod);
+
+       if(BPY_class_validate("Macro", py_class, base_class, pyop_class_attr_values, NULL) < 0) {
+               return NULL; /* BPY_class_validate sets the error */
+       }
+       Py_DECREF(base_class);
+
+       /* class name is used for operator ID - this can be changed later if we want */
+       item= PyObject_GetAttrString(py_class, PYOP_ATTR_IDNAME);
+       idname =  _PyUnicode_AsString(item);
+
+
+       /* annoying conversion! */
+       WM_operator_bl_idname(idname_bl, idname);
+       Py_DECREF(item);
+
+       item= PyUnicode_FromString(idname_bl);
+       PyObject_SetAttrString(py_class, PYOP_ATTR_IDNAME_BL, item);
+       idname =  _PyUnicode_AsString(item);
+       Py_DECREF(item);
+       /* end annoying conversion! */
+
+
+       /* remove if it already exists */
+       if ((ot=WM_operatortype_exists(idname))) {
+               if(ot->pyop_data) {
+                       Py_XDECREF((PyObject*)ot->pyop_data);
+               }
+               WM_operatortype_remove(idname);
+       }
+
+       Py_INCREF(py_class);
+       WM_operatortype_append_macro_ptr(PYTHON_OT_MACRO_wrapper, py_class);
+
+       Py_RETURN_NONE;
+}
+
+PyObject *PYOP_wrap_macro_define(PyObject *self, PyObject *args)
+{
+       wmOperatorType *ot;
+       wmOperatorTypeMacro *otmacro;
+       PyObject *macro;
+       PyObject *item;
+       PointerRNA ptr_otmacro;
+
+       char *opname;
+       char *macroname;
+
+       if (!PyArg_ParseTuple(args, "Os:_bpy.ops.macro_define", &macro, &opname))
+               return NULL;
+
+       if (WM_operatortype_exists(opname) == NULL) {
+               PyErr_Format(PyExc_ValueError, "Macro Define: '%s' is not a valid operator id", opname);
+               return NULL;
+       }
+
+       /* identifiers */
+       item= PyObject_GetAttrString(macro, PYOP_ATTR_IDNAME_BL);
+
+       if (!item) {
+               item= PyObject_GetAttrString(macro, PYOP_ATTR_IDNAME);
+
+               if (!item) {
+                       PyErr_Format(PyExc_ValueError, "Macro Define: not a valid Macro class");
+               } else {
+                       macroname= _PyUnicode_AsString(item);
+                       PyErr_Format(PyExc_ValueError, "Macro Define: '%s' hasn't been registered yet", macroname);
+               }
+               return NULL;
+       }
+
+       macroname= _PyUnicode_AsString(item);
+
+       ot = WM_operatortype_exists(macroname);
+
+       if (!ot) {
+               PyErr_Format(PyExc_ValueError, "Macro Define: '%s' is not a valid macro or hasn't been registered yet", macroname);
+               return NULL;
+       }
+
+       otmacro = WM_operatortype_macro_define(ot, opname);
+
+       RNA_pointer_create(NULL, &RNA_OperatorTypeMacro, otmacro, &ptr_otmacro);
+
+       return pyrna_struct_CreatePyObject(&ptr_otmacro);
+}
+
+
 PyObject *PYOP_wrap_remove(PyObject *self, PyObject *value)
 {
        PyObject *py_class;
index 2929d57ab8277cd5c652d09d1c0a9f6e978c14a3..939fedd1f2b474e8fc2f419a12d608e53b775612 100644 (file)
@@ -29,6 +29,8 @@
 
 /* these are used for operator methods, used by bpy_operator.c */
 PyObject *PYOP_wrap_add(PyObject *self, PyObject *args);
+PyObject *PYOP_wrap_add_macro(PyObject *self, PyObject *args);
+PyObject *PYOP_wrap_macro_define(PyObject *self, PyObject *args);
 PyObject *PYOP_wrap_remove(PyObject *self, PyObject *args);
 
 #endif
index a270b75eea1caf25a4b6cf8c88bb2b92e401c4cd..549ef11c14ecce84426ed56269a774227932906f 100644 (file)
@@ -180,6 +180,7 @@ wmOperatorType *WM_operatortype_exists(const char *idname);
 wmOperatorType *WM_operatortype_first(void);
 void           WM_operatortype_append  (void (*opfunc)(wmOperatorType*));
 void           WM_operatortype_append_ptr      (void (*opfunc)(wmOperatorType*, void *), void *userdata);
+void           WM_operatortype_append_macro_ptr        (void (*opfunc)(wmOperatorType*, void *), void *userdata);
 int                    WM_operatortype_remove(const char *idname);
 
 wmOperatorType *WM_operatortype_append_macro(char *idname, char *name, int flag);
index 9cee2140fc72875c97ee59fccab04f002e81d8a4..582410c436845c9f430e259fe85b92a0d1f07f85 100644 (file)
@@ -353,6 +353,27 @@ wmOperatorType *WM_operatortype_append_macro(char *idname, char *name, int flag)
        return ot;
 }
 
+void WM_operatortype_append_macro_ptr(void (*opfunc)(wmOperatorType*, void*), void *userdata)
+{
+       wmOperatorType *ot;
+
+       ot= MEM_callocN(sizeof(wmOperatorType), "operatortype");
+       ot->srna= RNA_def_struct(&BLENDER_RNA, "", "OperatorProperties");
+
+       ot->exec= wm_macro_exec;
+       ot->invoke= wm_macro_invoke;
+       ot->modal= wm_macro_modal;
+       ot->cancel= wm_macro_cancel;
+       ot->poll= NULL;
+
+       opfunc(ot, userdata);
+
+       RNA_def_struct_ui_text(ot->srna, ot->name, ot->description ? ot->description:"(undocumented operator)");
+       RNA_def_struct_identifier(ot->srna, ot->idname);
+
+       BLI_addtail(&global_ops, ot);
+}
+
 wmOperatorTypeMacro *WM_operatortype_macro_define(wmOperatorType *ot, const char *idname)
 {
        wmOperatorTypeMacro *otmacro= MEM_callocN(sizeof(wmOperatorTypeMacro), "wmOperatorTypeMacro");
index 7f9928262c3997612e96246f6410d03202ea3447..9abd518ff6e96b2dcb1408d58f1f69fb35a7c903 100644 (file)
@@ -232,6 +232,7 @@ void WM_operator_properties_free(struct PointerRNA *ptr){}
 void WM_operator_properties_create(struct PointerRNA *ptr, const char *opstring){}
 void WM_operator_properties_create_ptr(struct PointerRNA *ptr, struct wmOperatorType *ot){}
 void WM_operatortype_append_ptr(void (*opfunc)(struct wmOperatorType*, void*), void *userdata){}
+void WM_operatortype_append_macro_ptr(void (*opfunc)(struct wmOperatorType*, void*), void *userdata){}
 void WM_operator_bl_idname(char *to, const char *from){}
 void WM_operator_py_idname(char *to, const char *from){}
 short insert_keyframe (struct ID *id, struct bAction *act, const char group[], const char rna_path[], int array_index, float cfra, short flag){return 0;}