Skip to content
Snippets Groups Projects
bpy_ops.py 15.1 KiB
Newer Older
  • Learn to ignore specific revisions
  • # ##### BEGIN GPL LICENSE BLOCK #####
    #
    #  This program is free software; you can redistribute it and/or
    #  modify it under the terms of the GNU General Public License
    #  as published by the Free Software Foundation; either version 2
    #  of the License, or (at your option) any later version.
    # 
    #  This program is distributed in the hope that it will be useful,
    #  but WITHOUT ANY WARRANTY; without even the implied warranty of
    #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    #  GNU General Public License for more details.
    # 
    #  You should have received a copy of the GNU General Public License
    #  along with this program; if not, write to the Free Software Foundation,
    #  Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
    #
    # ##### END GPL LICENSE BLOCK #####
    
    Campbell Barton's avatar
    Campbell Barton committed
    # for slightly faster access
    
    from bpy.__ops__ import add        as op_add
    from bpy.__ops__ import remove     as op_remove
    from bpy.__ops__ import dir        as op_dir
    from bpy.__ops__ import call       as op_call
    from bpy.__ops__ import as_string  as op_as_string
    from bpy.__ops__ import get_rna    as op_get_rna
    
        'INVOKE_DEFAULT': 0,
        'INVOKE_REGION_WIN': 1,
        'INVOKE_AREA': 2,
        'INVOKE_SCREEN': 3,
        'EXEC_DEFAULT': 4,
        'EXEC_REGION_WIN': 5,
        'EXEC_AREA': 6,
        'EXEC_SCREEN': 7,
    
        '''
        Fake module like class.
    
         bpy.ops
        '''
    
        def __getattr__(self, module):
            '''
            gets a bpy.ops submodule
            '''
            if module.startswith('__'):
                raise AttributeError(module)
            return bpy_ops_submodule(module)
    
        def add(self, pyop):
            op_add(pyop)
    
        def remove(self, pyop):
            op_remove(pyop)
    
        def __dir__(self):
    
            submodules = set()
    
            # add this classes functions
            for id_name in dir(self.__class__):
                if not id_name.startswith('__'):
                    submodules.add(id_name)
    
            for id_name in op_dir():
                id_split = id_name.split('_OT_', 1)
    
                if len(id_split) == 2:
                    submodules.add(id_split[0].lower())
                else:
                    submodules.add(id_split[0])
    
            return list(submodules)
    
        def __repr__(self):
            return "<module like class 'bpy.ops'>"
    
        '''
        Utility class to fake submodules.
    
        eg. bpy.ops.object
        '''
        __keys__ = ('module',)
    
        def __init__(self, module):
            self.module = module
    
        def __getattr__(self, func):
            '''
            gets a bpy.ops.submodule function
            '''
            return bpy_ops_submodule_op(self.module, func)
    
        def __dir__(self):
    
            functions = set()
    
            module_upper = self.module.upper()
    
            for id_name in op_dir():
                id_split = id_name.split('_OT_', 1)
                if len(id_split) == 2 and module_upper == id_split[0]:
                    functions.add(id_split[1])
    
            return list(functions)
    
        def __repr__(self):
            return "<module like class 'bpy.ops.%s'>" % self.module
    
    
        '''
        Utility class to fake submodule operators.
    
        eg. bpy.ops.object.somefunc
        '''
    
        __keys__ = ('module', 'func')
    
        def __init__(self, module, func):
            self.module = module
            self.func = func
    
        def idname(self):
            # submod.foo -> SUBMOD_OT_foo
            return self.module.upper() + '_OT_' + self.func
    
        def __call__(self, *args, **kw):
    
            # Get the operator from blender
            if len(args) > 2:
                raise ValueError("1 or 2 args execution context is supported")
    
            C_dict = None
    
            if args:
    
                C_exec = 'EXEC_DEFAULT'
    
                if len(args) == 2:
                    C_exec = args[0]
                    C_dict = args[1]
                else:
                    if type(args[0]) != str:
                        C_dict = args[0]
                    else:
                        C_exec = args[0]
    
                try:
                    context = context_dict[C_exec]
                except:
                    raise ValueError("Expected a single context argument in: " + \
                     str(list(context_dict.keys())))
    
                if len(args) == 2:
                    C_dict = args[1]
    
                return op_call(self.idname(), C_dict, kw, context)
    
            else:
                return op_call(self.idname(), C_dict, kw)
    
        def get_rna(self):
            '''
    
            '''
            return op_get_rna(self.idname())
    
        def __repr__(self): # useful display, repr(op)
            return op_as_string(self.idname())
    
        def __str__(self): # used for print(...)
            return "<function bpy.ops.%s.%s at 0x%x'>" % \
                    (self.module, self.func, id(self))
    
    Campbell Barton's avatar
    Campbell Barton committed
    import bpy
    
    from bpy.props import *
    
    
    
    class MESH_OT_delete_edgeloop(bpy.types.Operator):
    
        '''Export a single object as a stanford PLY with normals,
        colours and texture coordinates.'''
    
        bl_idname = "mesh.delete_edgeloop"
        bl_label = "Delete Edge Loop"
    
    
        def execute(self, context):
            bpy.ops.tfm.edge_slide(value=1.0)
            bpy.ops.mesh.select_more()
            bpy.ops.mesh.remove_doubles()
            return ('FINISHED',)
    
    
    rna_path_prop = StringProperty(name="Context Attributes",
    
            description="rna context string", maxlen=1024, default="")
    
    
    rna_reverse_prop = BoolProperty(name="Reverse",
    
            description="Cycle backwards", default=False)
    
    
        import sys
        try:
            value = eval("context.%s" % path)
        except AttributeError:
            if "'NoneType'" in str(sys.exc_info()[1]):
                # One of the items in the rna path is None, just ignore this
                value = NullPathMember
            else:
                # We have a real error in the rna path, dont ignore that
                raise
    
        return value
    
    
    def execute_context_assign(self, context):
    
        if context_path_validate(context, self.path) == NullPathMember:
            return ('PASS_THROUGH',)
    
        exec("context.%s=self.value" % self.path)
        return ('FINISHED',)
    
    
    class WM_OT_context_set_boolean(bpy.types.Operator):
    
        '''Set a context value.'''
    
        bl_idname = "wm.context_set_boolean"
        bl_label = "Context Set"
    
        path = rna_path_prop
        value = BoolProperty(name="Value",
                description="Assignment value", default=True)
    
        execute = execute_context_assign
    
    
    class WM_OT_context_set_int(bpy.types.Operator): # same as enum
    
        '''Set a context value.'''
    
        bl_idname = "wm.context_set_int"
        bl_label = "Context Set"
    
        path = rna_path_prop
        value = IntProperty(name="Value", description="Assign value", default=0)
    
        execute = execute_context_assign
    
    
    
    class WM_OT_context_set_float(bpy.types.Operator): # same as enum
    
        '''Set a context value.'''
    
        bl_idname = "wm.context_set_int"
        bl_label = "Context Set"
    
    
        path = rna_path_prop
        value = FloatProperty(name="Value",
                description="Assignment value", default=0.0)
    
    
        execute = execute_context_assign
    
    
    
    class WM_OT_context_set_string(bpy.types.Operator): # same as enum
    
        '''Set a context value.'''
    
        bl_idname = "wm.context_set_string"
        bl_label = "Context Set"
    
        path = rna_path_prop
        value = StringProperty(name="Value",
                description="Assign value", maxlen=1024, default="")
    
    
        execute = execute_context_assign
    
    
    
    class WM_OT_context_set_enum(bpy.types.Operator):
    
        '''Set a context value.'''
    
        bl_idname = "wm.context_set_enum"
        bl_label = "Context Set"
    
    
        path = rna_path_prop
        value = StringProperty(name="Value",
    
                description="Assignment value (as a string)",
    
    
        execute = execute_context_assign
    
    
    class WM_OT_context_toggle(bpy.types.Operator):
    
        '''Toggle a context value.'''
    
        bl_idname = "wm.context_toggle"
        bl_label = "Context Toggle"
    
    
        def execute(self, context):
    
            if context_path_validate(context, self.path) == NullPathMember:
                return ('PASS_THROUGH',)
    
            exec("context.%s=not (context.%s)" % (self.path, self.path))
            return ('FINISHED',)
    
    
    class WM_OT_context_toggle_enum(bpy.types.Operator):
    
        '''Toggle a context value.'''
    
        bl_idname = "wm.context_toggle_enum"
        bl_label = "Context Toggle Values"
    
        path = rna_path_prop
        value_1 = StringProperty(name="Value", \
                    description="Toggle enum", maxlen=1024, default="")
    
        value_2 = StringProperty(name="Value", \
                    description="Toggle enum", maxlen=1024, default="")
    
    
        def execute(self, context):
    
            if context_path_validate(context, self.path) == NullPathMember:
                return ('PASS_THROUGH',)
    
            exec("context.%s = ['%s', '%s'][context.%s!='%s']" % \
                (self.path, self.value_1, self.value_2, self.path, self.value_2))
    
            return ('FINISHED',)
    
    
        '''Set a context value. Useful for cycling active material,
        vertex keys, groups' etc.'''
    
        bl_idname = "wm.context_cycle_int"
        bl_label = "Context Int Cycle"
    
        path = rna_path_prop
        reverse = rna_reverse_prop
    
    
        def execute(self, context):
    
            value = context_path_validate(context, self.path)
            if value == NullPathMember:
                return ('PASS_THROUGH',)
    
            self.value = value
            if self.reverse:
                self.value -= 1
            else:
                self.value += 1
            execute_context_assign(self, context)
    
            if self.value != eval("context.%s" % self.path):
                # relies on rna clamping int's out of the range
                if self.reverse:
                    self.value = (1 << 32)
                else:
                    self.value = - (1 << 32)
                execute_context_assign(self, context)
    
            return ('FINISHED',)
    
    
    class WM_OT_context_cycle_enum(bpy.types.Operator):
    
        '''Toggle a context value.'''
    
        bl_idname = "wm.context_cycle_enum"
        bl_label = "Context Enum Cycle"
    
    
        path = rna_path_prop
        reverse = rna_reverse_prop
    
    
        def execute(self, context):
    
            value = context_path_validate(context, self.path)
            if value == NullPathMember:
                return ('PASS_THROUGH',)
    
            orig_value = value
    
            # Have to get rna enum values
            rna_struct_str, rna_prop_str = self.path.rsplit('.', 1)
            i = rna_prop_str.find('[')
    
            # just incse we get "context.foo.bar[0]"
            if i != -1:
                rna_prop_str = rna_prop_str[0:i]
    
            rna_struct = eval("context.%s.rna_type" % rna_struct_str)
    
            rna_prop = rna_struct.properties[rna_prop_str]
    
            if type(rna_prop) != bpy.types.EnumProperty:
                raise Exception("expected an enum property")
    
            enums = rna_struct.properties[rna_prop_str].items.keys()
            orig_index = enums.index(orig_value)
    
            # Have the info we need, advance to the next item
            if self.reverse:
                if orig_index == 0:
                    advance_enum = enums[-1]
                else:
                    advance_enum = enums[orig_index-1]
            else:
                if orig_index == len(enums) - 1:
                    advance_enum = enums[0]
                else:
                    advance_enum = enums[orig_index + 1]
    
            # set the new value
            exec("context.%s=advance_enum" % self.path)
            return ('FINISHED',)
    
    
    doc_id = StringProperty(name="Doc ID",
    
            description="ID for the documentation", maxlen=1024, default="")
    
    
    doc_new = StringProperty(name="Doc New",
    
            description="", maxlen=1024, default="")
    
        '''Load online reference docs'''
    
        bl_idname = "wm.doc_view"
        bl_label = "View Documentation"
    
        _prefix = 'http://www.blender.org/documentation/250PythonDoc'
    
        def _nested_class_string(self, class_string):
            ls = []
    
            class_obj = getattr(bpy.types, class_string, None).bl_rna
    
            while class_obj:
                ls.insert(0, class_obj)
                class_obj = class_obj.nested
            return '.'.join([class_obj.identifier for class_obj in ls])
    
        def execute(self, context):
            id_split = self.doc_id.split('.')
            if len(id_split) == 1: # rna, class
                url = '%s/bpy.types.%s-class.html' % (self._prefix, id_split[0])
            elif len(id_split) == 2: # rna, class.prop
                class_name, class_prop = id_split
    
                # It so happens that epydoc nests these
                class_name_full = self._nested_class_string(class_name)
    
                if hasattr(bpy.types, class_name.upper() + '_OT_' + class_prop):
                    url = '%s/bpy.ops.%s-module.html#%s' % \
                            (self._prefix, class_name_full, class_prop)
                else:
                    url = '%s/bpy.types.%s-class.html#%s' % \
                            (self._prefix, class_name_full, class_prop)
    
            else:
                return ('PASS_THROUGH',)
    
            import webbrowser
            webbrowser.open(url)
    
            return ('FINISHED',)
    
        '''Load online reference docs'''
    
        bl_idname = "wm.doc_edit"
        bl_label = "Edit Documentation"
    
        _url = "http://www.mindrones.com/blender/svn/xmlrpc.php"
    
        def _send_xmlrpc(self, data_dict):
            print("sending data:", data_dict)
    
            import xmlrpc.client
            user = 'blenderuser'
            pwd = 'blender>user'
    
            docblog = xmlrpc.client.ServerProxy(self._url)
            docblog.metaWeblog.newPost(1, user, pwd, data_dict, 1)
    
        def execute(self, context):
    
            class_name, class_prop = self.doc_id.split('.')
    
            if not self.doc_new:
                return 'OPERATOR_CANCELLED'
    
            # check if this is an operator
            op_name = class_name.upper() + '_OT_' + class_prop
            op_class = getattr(bpy.types, op_name, None)
    
            # Upload this to the web server
            upload = {}
    
            if op_class:
    
                doc_orig = rna.description
                if doc_orig == self.doc_new:
                    return 'OPERATOR_CANCELLED'
    
                print("op - old:'%s' -> new:'%s'" % (doc_orig, self.doc_new))
                upload["title"] = 'OPERATOR %s:%s' % (self.doc_id, doc_orig)
                upload["description"] = self.doc_new
    
                self._send_xmlrpc(upload)
    
            else:
    
                rna = getattr(bpy.types, class_name).bl_rna
    
                doc_orig = rna.properties[class_prop].description
                if doc_orig == self.doc_new:
                    return 'OPERATOR_CANCELLED'
    
                print("rna - old:'%s' -> new:'%s'" % (doc_orig, self.doc_new))
                upload["title"] = 'RNA %s:%s' % s(self.doc_id, doc_orig)
    
            upload["description"] = self.doc_new
    
            self._send_xmlrpc(upload)
    
            return ('FINISHED',)
    
        def invoke(self, context, event):
            wm = context.manager
    
            return ('RUNNING_MODAL',)
    
    bpy.ops.add(WM_OT_context_set_boolean)
    bpy.ops.add(WM_OT_context_set_int)
    bpy.ops.add(WM_OT_context_set_float)
    bpy.ops.add(WM_OT_context_set_string)
    bpy.ops.add(WM_OT_context_set_enum)
    
    bpy.ops.add(WM_OT_context_toggle_enum)
    
    bpy.ops.add(WM_OT_context_cycle_enum)
    
    bpy.ops.add(WM_OT_doc_view)
    bpy.ops.add(WM_OT_doc_edit)