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$
2
  • $\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, 2013 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, 2014 at 19:20

2 Answers 2

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$
3
$\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$
2
  • $\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, 2013 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, 2013 at 3:38

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .