2
$\begingroup$

I am trying to store the selected points of a Mask in the ClipEditor in a list, in order to retrieve it later, so to say as a temporary selection-set of mask controlpoints.

Here's what I have so far:

import bpy
layer = bpy.data.masks['Mask'].layers['MaskLayer']
bpy.context.scene["mask_storage"] = []
pointlist = []
for p in layer.splines.active.points:
   if p.select == True:
       pointlist.append(p)

That works so far. If I print that list it has all the selected points in it, such as:

<bpy_struct, MaskSplinePoint at 0x7f7f2fbb4d88>
<bpy_struct, MaskSplinePoint at 0x7f7f2fbb4e98>
<bpy_struct, MaskSplinePoint at 0x7f7f2fbb4fa8>

However, when I continue to do this:

bpy.context.scene["mask_storage"] = pointlist

I get an error:

TypeError: object of type 'MaskSplinePoint' has no len() The above exception was the direct cause of the following exception: SystemError: PyEval_EvalFrameEx returned a result with an error set

At first I tried to append the points directly to the "mask_storage" list directly, but that didn't work, it gave the error

AttributeError: 'IDPropertyArray' object has no attribute 'append'

That's why I tried to work around that by first appending to the "pointlist" and then assigning that to "mask_storage". But that obviously does not work.

I did do some googling already, and it seems I might need to do something with bpy.props.CollectionProperties, but I simply couldn't figure out how to do that exactly.

$\endgroup$
4
  • $\begingroup$ When you say you want to retrieve the data later, how much later? When the blend file has been closed and re-opened, later on in the script or later on in the same blender session? $\endgroup$ Commented Nov 26, 2017 at 17:07
  • $\begingroup$ @RayMairlot the most basic use would be during a blender session (same file), that would be probably enough. It’s to make rotoscoping easier. $\endgroup$ Commented Nov 26, 2017 at 17:32
  • 1
    $\begingroup$ You can do setattr(bpy, 'mask_storage', pointlist) to save them, then do bpy.mask_storage to access them later. $\endgroup$
    – Omar Emara
    Commented Nov 26, 2017 at 19:31
  • $\begingroup$ Wow, that actually does work! And much simpler than I thought. Thank you so much! $\endgroup$ Commented Nov 26, 2017 at 19:39

1 Answer 1

1
$\begingroup$

So, with @OmarAhmad's help I figured out a way to achieve what I want. Just for the sake of completeness I want to post the entire code here. On Twitter I got told that storing the points the way I did was dangerous, because if the masks got deleted and I try to access them anyway it would lead to a segfault. So far I did not notice any problems like that. Anyway, here's the script. I think it can make Rotoscoping a bit easier:

import bpy

def store_mask_points(context):
    mask = context.space_data.mask 
    layer = mask.layers.active
    pointlist = []
    # if the storage doesnt exist, create it, otherwise, reference it
    try:
        mydict = bpy.mask_storage
    except:
        mydict = {}

    # if there is an active spline, list all selected points
    if layer.splines.active:
        for p in layer.splines.active.points:
            if p.select == True:
                pointlist.append(p)
        # assign the pointlist to the layer
        mydict[layer] = pointlist
        # write the dictionary to the mask_storage
        # (I would rather write this list as an attribute of the masklayer,
        # but appartently I can only do that on bpy)
        setattr(bpy, "mask_storage", mydict)


def recover_mask_points(context):
        layer = context.space_data.mask.layers.active
        # if there is an active layer and 
        # if that layer is stored in mask_storage, select the points in it
        try:
            if layer and bpy.mask_storage and bpy.mask_storage.get(layer):
                for p in layer.splines.active.points:
                    p.select = False
                for p in bpy.mask_storage.get(layer):
                    p.select = True
        except:
            print("no mask storage yet")


class CLIP_mask_store_points(bpy.types.Operator):
    ''' Store the points of a the active spline as Selection Set'''
    bl_idname = "clip.mask_store_points"
    bl_label = "Store MaskPoints"

    @classmethod
    def poll(cls, context):
        space = context.space_data
        mask = space.mask
        return (space.type == "CLIP_EDITOR") and space.clip and mask

    def execute(self, context):
        store_mask_points(context)
        return {'FINISHED'}


class CLIP_mask_recover_points(bpy.types.Operator):
    ''' Select the Selection Set of the MaskPoints'''
    bl_idname = "clip.mask_recover_points"
    bl_label = "Recover MaskPoints"

    @classmethod
    def poll(cls, context):
        space = context.space_data
        mask = space.mask
        return (space.type == "CLIP_EDITOR") and space.clip and mask

    def execute(self, context):
        recover_mask_points(context)
        return {'FINISHED'}


def register():
    bpy.utils.register_class(CLIP_mask_store_points)
    bpy.utils.register_class(CLIP_mask_recover_points)

    wm = bpy.context.window_manager
    km = wm.keyconfigs.addon.keymaps.new(name='Clip Editor', space_type='CLIP_EDITOR')
    kmi = km.keymap_items.new('clip.mask_store_points', 'Y', 'PRESS', shift=True)
    kmi = km.keymap_items.new('clip.mask_recover_points', 'X', 'PRESS', shift=True)



def unregister():
    bpy.utils.unregister_class(CLIP_mask_store_points)
    bpy.utils.unregister_class(CLIP_mask_recover_points)

if __name__ == "__main__":
    register()
$\endgroup$

You must log in to answer this question.

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