# Python: How to select one 'BoolVectorProperty' value at a time unless 'shift' key is held

I am trying to overwrite the bpy.ops.object.move_to_layer operator in Blender so that I can move other objects when the selected objects are moved.

When I try to move the selected objects by pressing the 'm' key and clicking on the new layer in the invoked props popup (from the 'layers' BoolVectorProperty), though, the object still remains in the old layer as well as being added to the new layer because both layers are active, as if I was holding the shift key in the original operator.

The following is my code to overwrite and replicate the bpy.ops.object.move_to_layer operator (I omitted extra code run when the operator is executed for simplicity):

# Blender imports
import bpy
from bpy.types import Operator
from bpy.props import *

class move_to_layer_override(Operator):
"""Move to Layer"""
bl_idname = "object.move_to_layer"
bl_label = "Move to Layer"
bl_options = {'REGISTER', 'INTERNAL', 'UNDO'}

################################################
# Blender Operator methods

@classmethod
def poll(self, context):
return True

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

def invoke(self, context, event):
# sets self.layers to active layers of selected objects
idxs = [i for i in range(20)]
for obj in context.selected_objects:
for i in idxs:
layer = obj.layers[i]
if layer:
self.layers[i] = True
idxs.remove(i)
# Run confirmation popup for delete action
return context.window_manager.invoke_props_popup(self, event)

###################################################
# class variables

# This does not behave like scn.layers.
# only one layer should be selected at a time unless shift is held
layers = BoolVectorProperty(
name="Layers",
subtype="LAYER",
description="Object Layers",
size=20,
)

################################################
# class methods

def runMove(self, context):
scn = context.scene
for obj in bpy.context.selected_objects:
obj.layers = self.layers
# insert extra code to run when objects are moved here

def register():
bpy.utils.register_module(__name__)

def unregister():

if __name__ == "__main__":
register()


It should behave exactly like the original operator – when a new layer is selected in the invoked props popup, the old layer(s) should be deselected unless the shift key is held. Could someone help me figure out how to do this?

• Related – batFINGER Jun 27 '18 at 5:56
• That article doesn't really address my question (at least as far as I understand), though it's similar. I'm asking about whether the shift key event can change the behavior of the BoolVectorProperty in the invoked popup, not whether it can change the behavior of the operator itself. – Christopher Gearhart Jun 27 '18 at 16:44

Use the event from invoking the operator

Using the method from the related answer here, since we can address the event in the invoke, can it be passed to the execute method of the popup as a class property. On very limited testing it appears so..

Added some more class properties to keep tabs of selection. Also copied the selected objects since if a non layer with non matching scene layer is selected context.selected_objects will vary those when op was invoked. (Btw obj.layers is gone in 2.8)

The big issue is the popup not redrawing correctly. I thought the operator check(..) method returning True would work, but not the case on my blender. Perhaps using the layers collection of the context object. when invoked, would be one way to solve this.

import bpy
from bpy.types import Operator
from bpy.props import BoolVectorProperty

class OBJECT_OT_move_to_layer2(Operator):
"""Move to Layer 2"""
# class properties
bl_idname = "object.move_to_layer2"
bl_label = "Move to Layer [TESTING]"
bl_options = {'REGISTER', 'UNDO'}

event = None
objects = []
prev_sel = []

# poll is a class method
@classmethod
def poll(self, context):
return True

def execute(self, context):
ev = []
event = self.event
if event.ctrl:
ev.append("Ctrl")
if event.shift:
ev.append("Shift")
if event.alt:
ev.append("Alt")
if event.oskey:
ev.append("OS")
changed = [i for i, (l, s) in
enumerate(zip(self.layers, self.prev_sel))
if l != s]

print("+".join(ev), event.type, event.value, changed)
# pick only the changed one
if not (event.ctrl or event.shift) and changed:
self.layers = [i in changed for i in range(20)]
self.prev_sel = self.layers[:]

self.runMove(context)
return {'FINISHED'}

def check(self, context):
return True # thought True updated.. not working

def invoke(self, context, event):
self.layers = [any(o.layers[i] for o in context.selected_objects)
for i in range(20)]
self.event = event
self.objects = [o.name for o in context.selected_objects]
self.prev_sel = self.layers[:]
return context.window_manager.invoke_props_popup(self, event)

# properties

layers = BoolVectorProperty(
name="Layers",
subtype="LAYER",
description="Object Layers",
size=20,
)

def runMove(self, context):
for name in self.objects:
obj = context.scene.objects.get(name)
obj.layers = self.layers

def register():
bpy.utils.register_class(OBJECT_OT_move_to_layer2)

def unregister():
bpy.utils.unregister_class(OBJECT_OT_move_to_layer2_name)

if __name__ == "__main__":
register()
bpy.ops.object.move_to_layer2('INVOKE_DEFAULT')


Test print to console

 LEFTMOUSE PRESS [0]
Ctrl LEFTMOUSE PRESS [11]
Ctrl+Shift LEFTMOUSE PRESS [12]
Shift LEFTMOUSE PRESS [1]


Note if you want to call from a shortcut, and have the hacky event handling, need to invoke from another operator. Edit made to simple operator to call op above (just like calling it from space menu)

class SimpleOperator(bpy.types.Operator):
"""Tooltip"""
bl_idname = "object.simple_operator"
bl_label = "Simple Object Operator"

def invoke(self, context, event):
return bpy.ops.object.move_to_layer2('INVOKE_DEFAULT')


• Thank you for spelling this out for me; I understand better now. Sadly, though, this only works for me when the operator is called via the script (as you've done in the last line). When invoked with the "m" key, though, the operator does not work correctly as the event object seems to hold faulty information. The printout to the console was seemingly random... Ctrl+Alt NONE ANY [2] or NONE ANY [12] or Alt NONE PRESS [1]. I was not holding down ctrl or alt when clicking on the layers. – Christopher Gearhart Jun 28 '18 at 0:58
• or the space bar search menu, .. in which case see edit above. – batFINGER Jun 28 '18 at 5:32