При выполнении некоторых действий может возникнуть необходимость запретить курсору мышки выходить за границы определенного окна Blender. Например, при захвате и перетаскивании меша мышкой, захваченный меш не должен покидать пределов рабочей области вьюпорта, чтобы не оказаться над рабочей областью другого типа, например над редактором нодов или текстовым редактором.
Самый простой способ добиться нужного поведения указателя мышки – добавить в параметр bl_options оператора значения GRAB_CURSOR и BLOCKING:
1 |
bl_options = {'GRAB_CURSOR', 'BLOCKING'} |
Теперь, если сделать оператор модальным, при его выполнении курсор будет ограничен только той рабочей областью, из которой оператор был вызван.
Определим модальный оператор с блокировкой указателя мышки через параметры оператора:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
import bpy class BlockCursorOperator(bpy.types.Operator): bl_idname = 'block_cursor.operator' bl_label = 'Block Cursor Operator' bl_options = {'GRAB_CURSOR', 'BLOCKING'} def modal(self, context, event): if event.type in {'RIGHTMOUSE', 'ESC'}: # exit print('stop') return {'FINISHED'} return {'RUNNING_MODAL'} def invoke(self, context, event): print('start') context.window_manager.modal_handler_add(self) return {'RUNNING_MODAL'} bpy.utils.register_class(BlockCursorOperator) |
Теперь, если выполнить данный оператор командой
1 |
bpy.ops.block_cursor.operator('INVOKE_DEFAULT') |
курсор мышки нельзя будет увести за пределы текущей рабочей области.
Для того чтобы вернуть курсору возможность свободно перемещаться, нужно прервать действие модального оператора, кликнув правой кнопкой мышки или нажав клавишу ESC.
Такой способ блокировки курсора прост и удобен, однако он включает, так называемый “продолжительный граб” – в тот момент, когда указатель мышки выходит за границу рабочей области с одной стороны, он сразу же возвращается в эту рабочую область, но – с противоположной стороны.
Такое поведение удобно, например, при перетаскивании объектов или изменении масштаба. Однако иногда бывает нужно просто остановить курсор на границе рабочей области, никуда больше его не перемещая.
Реализовать такое поведение для указателя мышки можно установив постоянный контроль за координатами курсора, чтобы при приближении значения этих координат к границам рабочей области “замораживать” их.
Изменим код нашего модального оператора следующим образом:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
import bpy class BlockCursorOperator(bpy.types.Operator): bl_idname = 'block_cursor.operator' bl_label = 'Block Cursor Operator' def modal(self, context, event): if event.type == 'MOUSEMOVE': region = context.region if event.mouse_region_x < 10: context.window.cursor_warp(region.x + 10, event.mouse_y) elif event.mouse_region_x > region.width - 10: context.window.cursor_warp(region.x + region.width - 10, event.mouse_y) if event.mouse_region_y < 10: context.window.cursor_warp(event.mouse_x, region.y + 10) elif event.mouse_region_y > region.height - 10: context.window.cursor_warp(event.mouse_x, region.y + region.height - 10) if event.type in {'RIGHTMOUSE', 'ESC'}: # exit print('stop') return {'FINISHED'} return {'RUNNING_MODAL'} def invoke(self, context, event): print('start') context.window_manager.modal_handler_add(self) return {'RUNNING_MODAL'} bpy.utils.register_class(BlockCursorOperator) |
Здесь мы убрали установленные ранее значения в параметре bl_options оператора и добавили контроль события MOUSEMOVE – контроль за перемещением курсора мышки в функции modal.
Обработка события MOUSEMOVE позволяет нам получить координаты курсора мышки внутри текущей рабочей области через переменные event.mouse_region_x – X координата курсора и event.mouse_region_y – Y координата курсора мышки.
Размеры текущей рабочей области изменяются от 0 до context.region.width по ширине, и до context.region.height по высоте.
Следовательно, мы в любой момент можем сравнить координаты курсора и размеры текущей рабочей области, чтобы проверить, не вышел ли указатель мышки за них.
Координаты курсора можно сравнивать с нулем и с точными значениями ширины и высоты рабочей области, или же, как в данном примере, оставив отступ в 10 пикселей от края.
Если координаты курсора выходят за размеры рабочей области, вернуть курсор обратно можно установив ему нужные значения при помощи функции cursor_warp.
Например, вызов функции cursor_warp с параметрами (0, 0)
1 |
bpy.context.window.cursor_warp(r0, 0) |
установит курсор в точку на текущей рабочей области с координатами (0, 0).
В нашем же случае мы устанавливаем для курсора координаты на границе рабочей области, к которой он приближается меньше чем на 10 пикселей.
Если мы выполним данный оператор командой
1 |
bpy.ops.block_cursor.operator('INVOKE_DEFAULT') |
курсор, при приближении к границам области, будет всегда оставаться внутри и, в отличие от первого варианта, не будет перескакивать на противоположную сторону.
Вернуть курсору стандартное поведение можно все так же, нажав на правую кнопку мышки или клавишу ESC на клавиатуре.