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$
  • $\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$ – Ray Mairlot Nov 26 '17 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$ – sebastian_k Nov 26 '17 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 Nov 26 '17 at 19:31
  • $\begingroup$ Wow, that actually does work! And much simpler than I thought. Thank you so much! $\endgroup$ – sebastian_k Nov 26 '17 at 19:39
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$

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.