I’m about to finish my pinning addon but I can’t seem to find this data:
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 …
Hehe yeah, well spotted. I ended my scripting efforts halfway when I figured that it can’t be made working
Bug report: https://developer.blender.org/T40762
I actually tried that. I wanna avoid it for two reasons:
- Its much of a hassle to rebuild a proper “bpy.op” from it.
- 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!
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
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…
reason maybe used: Blender 2.70a ?