4
$\begingroup$

This is a follow up of How could a single Python script run when Blender is started?.

Simply adding a method to a newly created file in Blender\scripts\startup didn't work:

Warning! 'C:\Program Files\Blender Foundation\Blender\2.68\scripts\startup\global.py' has no register function, this is now a requirement for registerable scripts.

I used snippets from:

The script current looks like (Uses Ctrl-R to toggle between rendered and textured mode in Cycles) and adds a dump method I really need):

import bpy

class U(bpy.types.Operator):
    bl_idname = "object.util"
    bl_label = "Toggle Render Mode"

    def execute(self, context):
        # bpy.ops.sculpt.sculptmode_toggle()
        for area in bpy.context.screen.areas:
            if area.type == 'VIEW_3D':
                if area.spaces[0].viewport_shade == 'RENDERED':
                    area.spaces[0].viewport_shade = 'TEXTURED'
                else:
                    area.spaces[0].viewport_shade = 'RENDERED'
        return {'FINISHED'}

    def dump( obj, ctx="", level=0):
        for attr in dir(obj):
            print( "%s.%s = %s" % (ctx, attr, getattr(obj, attr)))

def menu_func(self, context):
    self.layout.operator(U.bl_idname)

addon_kmaps = []

def register():
    bpy.utils.register_class(U)

    wm = bpy.context.window_manager
    km = wm.keyconfigs.addon.keymaps.new(name='Object Mode', space_type='EMPTY')
    kmi = km.keymap_items.new(U.bl_idname, 'R', 'PRESS', ctrl=True, shift=False)
    addon_kmaps.append(km)

def unregister():
    bpy.utils.unregister_class(U)
    bpy.types.VIEW3D_MT_object.remove(menu_func)

    wm = bpy.context.window_manager
    for km in addon_kmaps:
        wm.keyconfigs.addon.kmaps.remove(km)
    del addon_kmaps[:]

if __name__ == "__main__":
    register()

The toggle button works, but how can the dump() method be invoked from the python console? I noobishly tried U.dump(obj) and bpy.U.dump(obj)

EDIT:

I finally modified the existing scripts/modules/console_python.py and added (starts add line 100):

# weak! - but highly convenient
namespace["C"] = bpy.context
namespace["D"] = bpy.data
namespace['dump'] = bpy.types.OBJECT_OT_util.dump # my one
$\endgroup$
  • $\begingroup$ Better not call the module global.py since global is a Python keyword and it would make importing and working with this module from another script impossible. $\endgroup$ – ideasman42 Sep 7 '13 at 21:37
  • $\begingroup$ I think the unintuitive part is that a class (Operator) gets reduced to a single operation, like a function. So then how do you make classes and use polymorphism and encapsulation? My advice is to make more operators call other modules from there. $\endgroup$ – Ben L Apr 22 '14 at 19:20
5
$\begingroup$

To answer your question:

Operator types are automatically added to bpy.types on registration.

This is how a method on an operator your register can be called.

bpy.types.OBJECT_OT_util.dump(bpy.context.object)

Note that it is very rare to ever access operator types like this, its more of an internal implementation detail.

normally you would access the operator via bpy.ops.object_utils() but this is only for calling the operator and won't expose its original class.

Suggested alternative

In this case I would suggest not to having dump() as an operator class method at all. If you have a function that can run outside of the operator, you're better off having it as a utility function in a module other scripts can import and use, then have the both the operator and external scripts use it.

my_module.py:

def dump(...):
   ... do stuff ...

class MyOperator(Operator):
    ...
    def execute(self, context):
        dump()

So now any script can do:

import my_module
my_module.dump()

This way we bypass Blender/Python API internals where its not helpful and let Python modules interact as they would typically (outside of Blender).

$\endgroup$
2
$\begingroup$

It seem what your really after is a way to define your own convenience imports/variables. Looking at scripts/modules/console_python.py, I came up with the following hack (save as startup script for use):

import inspect
from functools import wraps
from pprint import pprint
from console_python import replace_help
import console_python

@wraps(inspect.getmembers)
def getmembers(object, predicate=None):
    pprint(inspect.getmembers(object, predicate))

@wraps(replace_help)
def custom_convenience(namspace):
    """Add custom convenience definitions."""
    replace_help(namspace)
    namspace["getmembers"] = getmembers

def register():
    console_python.replace_help = custom_convenience

def unregister():
    console_python.replace_help = replace_help

Example usage

$\endgroup$
  • $\begingroup$ Thanks for your time, I didn't know that this will be complicated, I currently get "AttributeError: 'module' object has no attribute 'getmembers'" I need to read up more documentation. $\endgroup$ – stacker Sep 7 '13 at 20:11
  • $\begingroup$ This is doing some interesting stuff, but not sure its really the most direct solution to the question. $\endgroup$ – ideasman42 Sep 8 '13 at 3:38

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.