Get lines from INFO area

I’m about to finish my pinning addon but I can’t seem to find this data:

http://i.imgur.com/2m7j62C.png

They are in bpy.context.window_manager.operators, but beware, you can’t fully resolve them with python:

import bpy
from collections import OrderedDict

def to_idname_py(idname):
    return ".".join(idname.split("_OT_", maxsplit=1)).lower()
    

def format_operator(op):
    idname = op.bl_idname
    idname_py = to_idname_py(idname)
    props = []
    for k, v in op.properties.items():
        if k != k.lower():
            print("Macros not supported")
            return
        if hasattr(v, '__getitem__'):
            props.append("%s=%s" % (k, tuple(v)))
        else:
            props.append("%s=%s" % (k, v))

    return "bpy.ops.%s(%s)" % (idname_py, ", ".join(props))

def main():
    for op in bpy.context.window_manager.operators:
        print(format_operator(op))

main()

Macros don’t seem to be supported (not de-referenced?), and booleans / enum items print as numbers and I don’t know if it’s possible to solve… bpy.types.TRANSFORM_OT_translate.bl_rna.properties[‘constraint_orientation’].enum_items is empty for instance, which makes quite some sense, because it’s a dynamic enum.

a bracket ) is missing, end of getattr … :slight_smile:

Hehe yeah, well spotted. I ended my scripting efforts halfway when I figured that it can’t be made working :confused:

Bug report: https://developer.blender.org/T40762

I actually tried that. I wanna avoid it for two reasons:

  1. Its much of a hassle to rebuild a proper “bpy.op” from it.
  2. Doesn’t suit some of my needs

You can get bools and enums this way though (Using attributes, not dictionary. It’s like Property types):


import bpy


op = bpy.context.window_manager.operators[-1]


print(op.bl_idname)
for k in op.properties.keys():
    if hasattr(op.properties, k):
        attr = getattr(op.properties, k)
        if isinstance(attr,(type(None),str,int,float,bool)):
            if isinstance(attr, str):
                print("%s='%s'" % (k, attr))
            else:
                print("%s=%s" % (k, attr))
        else:
            print("%s=%s" % (k, tuple(attr)))

But thank you for your response! :slight_smile:

Okay, it seems like there’s no “bpy” method to get lines from the info screen.

I have no choice but to use operator history.

This is the code I made, you can use it:

import bpy

def build_operator(op):
    op_dir = "bpy.ops.%s" % (".".join(op.bl_idname.lower().split("_ot_", 1)))
    op_args = "("
    
    for i, k in enumerate(op.properties.keys()):
        if hasattr(op.properties, k):
            if i != 0: op_args += ", "
            attr = getattr(op.properties, k)
            if isinstance(attr, str):
                op_args += "%s='%s'" % (k, attr)
            elif isinstance(attr,(type(None),int,float,bool)):
                op_args += "%s=%s" % (k, attr)
            else:
                if hasattr(bpy.types, k) is False:
                    op_args += "%s=%s" % (k, tuple(attr))
                else:
                    op_args += "%s={" % k
                    for ni, nk in enumerate(attr.bl_rna.properties.keys()):
                        if hasattr(attr, nk):
                            if nk == 'rna_type': continue
                            if ni > 1: op_args += ", "
                            nattr = getattr(attr, nk)
                            if isinstance(nattr, str):
                                value = "'%s'" % nattr
                            elif isinstance(nattr, (type(None),int,float,bool)):
                                value = str(nattr)
                            else:
                                value = str(tuple(nattr))
                            op_args += "\"%s\":%s" % (nk, value)
                    op_args += "}"
                    
    op_args += ")"
    return "%s%s" % (op_dir, op_args)
    
print("
Rebuild last used operator: 
%s" %
    build_operator(bpy.context.window_manager.operators[-1]))

Here’s my solution:

import bpy
from collections import Iterable
from mathutils import Vector, Matrix
from _bpy import StructMetaPropGroup

def main():
    for op in bpy.context.window_manager.operators:
        cmd = format_logged_op(op)
        print(cmd)

def format_logged_op(op):
    tmp = []
    for k in op.properties.keys():
        p = getattr(op.properties, k)
        if isinstance(type(p), StructMetaPropGroup):
            props = format_macro_props(p, p.bl_rna.properties.keys())
            tmp.append("%s={%s}" % (p.bl_rna.identifier, props))
        else:
            tmp.append(format_key_value(k, p))

    return "bpy.ops.%s(%s)" % (format_idname(op), ", ".join(tmp))

def format_macro_props(prop, keys):
    tmp = []
    for k in keys:
        if k == "rna_type":
            continue
        p = getattr(prop, k)
        tmp.append(format_key_value(k, p, fmt='"%s": %r'))
    return ", ".join(tmp)
    
def format_key_value(k, p, fmt="%s=%r"):
    if isinstance(p, Vector):
        p = p.to_tuple()
    elif isinstance(p, Matrix):
        p = "(%s)" % ", ".join(repr(vec.to_tuple()) for vec in p)
    elif not isinstance(p, str) and isinstance(p, Iterable):
        p = p[:]
    return fmt % (k, p)


def format_idname(op):
    return ".".join(op.bl_idname.split("_OT_", maxsplit=1)).lower()
    
main()

I think I discovered a real bug this time: macro op properties are all zero’d out, it’s completely default values, not what was actually used :frowning:

Interesting fact:
Trying a loop-cut iPLEOMAX script shows it
CoDEmaxN script complains:
nieuw run <---- my addition to split outputs …
MESH_OT_loopcut_slide
Traceback (most recent call last):
File “C:\Users\Peter\Documents\Blender estscripts.blend\Text.003”, line 17, in <module>
TypeError: ‘MESH_OT_loopcut’ object is not iterable
Error: Python script fail, look in the console for now…

Can’t reproduce the error here, works fine for loopcut and slide?

Can confirm too… :confused:

reason maybe used: Blender 2.70a ?