原文链接
【Blender开发】示例:模拟鼠标移动物体 | MODEL模式
该代码估计不是这位作者写的,代码注释有一些错误,经过相关查证我进行了修改,并对一些部分进行补充。
正文
效果图
-
首先将下节中的代码保存在插件目录中,然后在 Blender 的 Add-on 选项中勾选
-
按 F3 搜索该自定义操作
-
物体跟随鼠标移动
-
物体跟随属性变化
脚本代码与详细注释
import bpy
bl_info = {
"name" : "test",
"author" : "yl",
"description" : "",
"blender" : (3, 0, 0),
"version" : (0, 0, 1),
"location" : "",
"warning" : "",
"category" : "Yuelili",
"doc_url":"https://yuelili.com"
}
# 自定义类
class MoveCubeOperator(bpy.types.Operator):
bl_idname="cube.movecube" # ID, 必须。命名必须要带,以小写字母,下划线 数字(my_operator.my_class_name)
bl_label = "Test Move Cube" # 标签,显示名称
bl_description = "Description About This Tool" # 描述
bl_options = {'REGISTER', 'UNDO'} # 选项,UNDO 是左下角 UNDO 面板
# 定义 RNA 属性,鼠标的 X 和 Y 位置
mouseX : bpy.props.FloatProperty(name="float",default=0.0)
mouseY : bpy.props.FloatProperty(name="float",default=0.0)
# 定义初始化 X 和 Y 属性
defX = 0.0
defy = 0.0
# 定义状态,为了左键退出
isOK = False
@classmethod
def poll(cls,context):
# 如果选择的物体不为空,并且是 3D 视窗下的 “物体模式”,则满足运行条件
if context.area.ui_type == "VIEW_3D":
if context.active_object is not None:
if context.object.mode == "OBJECT":
return True
def execute(self,context):
# 获取对象,并把鼠标坐标赋给对象的位置
# 鼠标横向运动,对象沿 X 轴移动
# 鼠标纵向运动,对象沿 Y 轴移动
obj = context.object
obj.location[0] = self.mouseX
obj.location[1] = self.mouseY
# 如果左键点击了,则 OK 为真,退出
if self.isOK:
return {"FINISHED"}
def invoke(self,context,event):
# 记住物体初始位置
self.defX = context.object.location[0]
self.defY = context.object.location[1]
# *在窗口管理器中添加一个模态处理程序,用于给定的模态操作符【见下一节】
# (在返回 {'RUNNING_MODAL'} 之前由 invoke() 搭配 self 调用)
context.window_manager.modal_handler_add(self)
return {"RUNNING_MODAL"}
def modal(self, context, event):
# 获取鼠标信息,并用 execute 移动物体
self.mouseX = event.mouse_x / 50
self.mouseY = event.mouse_y / 50
self.execute(context)
# 事件类:https://docs.blender.org/api/current/bpy.types.Event.html?highlight=event
# 事件类型:https://docs.blender.org/api/current/bpy_types_enum_items/event_type_items.html#rna-enum-event-type-items
# 右键可以取消,并把物体位置还原
if event.type == "RIGHTMOUSE":
print("cancel")
self.mouseX = self.defX
self.mouseY = self.defY
self.execute(context)
return {"FINISHED"}
# 左键就生效,并退出
if event.type == "LEFTMOUSE":
self.isOK = True
print("ok")
return {"FINISHED"}
# 有四大返回值 {'RUNNING_MODAL', 'CANCELLED', 'FINISHED', 'PASS_THROUGH'}
return {"RUNNING_MODAL"}
def register():
print("塔塔开")
# 注册
bpy.utils.register_class(MoveCubeOperator)
...
def unregister():
print("已经不用再战斗了")
#注销
bpy.utils.unregister_class(MoveCubeOperator)
...
Blender 相关 C++ 源码
为给定的模态操作符添加模态处理程序
1. 相关函数
func = RNA_def_function(srna, "modal_handler_add", "rna_event_modal_handler_add");
RNA_def_function_ui_description(
func,
"Add a modal handler to the window manager, for the given modal operator "
"(called by invoke() with self, just before returning {'RUNNING_MODAL'})");
RNA_def_function_flag(func, FUNC_NO_SELF | FUNC_USE_CONTEXT);
parm = RNA_def_pointer(func, "operator", "Operator", "", "Operator to call");
RNA_def_parameter_flags(parm, 0, PARM_REQUIRED);
RNA_def_function_return(
func, RNA_def_boolean(func, "handle", 1, "", "Whether adding the handler was successful"));
static bool rna_event_modal_handler_add(struct bContext *C, struct wmOperator *operator)
{
return WM_event_add_modal_handler(C, operator) != NULL;
}
wmEventHandler_Op *WM_event_add_modal_handler(bContext *C, wmOperator *op)
{
wmEventHandler_Op *handler = MEM_cnew<wmEventHandler_Op>(__func__);
handler->head.type = WM_HANDLER_TYPE_OP;
wmWindow *win = CTX_wm_window(C);
/* Operator was part of macro. */
if (op->opm) {
/* Give the mother macro to the handler. */
handler->op = op->opm;
/* Mother macro `opm` becomes the macro element. */
handler->op->opm = op;
}
else {
handler->op = op;
}
handler->context.area = CTX_wm_area(C); /* Means frozen screen context for modal handlers! */
handler->context.region = CTX_wm_region(C);
handler->context.region_type = handler->context.region ? handler->context.region->regiontype :
-1;
// *
BLI_addhead(&win->modalhandlers, handler);
if (op->type->modalkeymap) {
WM_window_status_area_tag_redraw(win);
}
return handler;
}
void BLI_addhead(ListBase *listbase, void *vlink)
{
Link *link = vlink;
if (link == NULL) {
return;
}
link->next = listbase->first;
link->prev = NULL;
if (listbase->first) {
((Link *)listbase->first)->prev = link;
}
if (listbase->last == NULL) {
listbase->last = link;
}
listbase->first = link;
}
2. 相关结构体
/** #WM_HANDLER_TYPE_OP */
typedef struct wmEventHandler_Op {
wmEventHandler head;
/** Operator can be NULL. */
wmOperator *op;
/** Hack, special case for file-select. */
bool is_fileselect;
/** Store context for this handler for derived/modal handlers. */
struct {
/* To override the window, and hence the screen. Set for few cases only, usually window/screen
* can be taken from current context. */
struct wmWindow *win;
struct ScrArea *area;
struct ARegion *region;
short region_type;
} context;
} wmEventHandler_Op;
/**
* This one is the operator itself, stored in files for macros etc.
* operator + operator-type should be able to redo entirely, but for different context's.
*/
typedef struct wmOperator {
struct wmOperator *next, *prev;
/* saved */
/** Used to retrieve type pointer. */
char idname[64];
/** Saved, user-settable properties. */
IDProperty *properties;
/* runtime */
/** Operator type definition from idname. */
struct wmOperatorType *type;
/** Custom storage, only while operator runs. */
void *customdata;
/** Python stores the class instance here. */
void *py_instance;
/** Rna pointer to access properties. */
struct PointerRNA *ptr;
/** Errors and warnings storage. */
struct ReportList *reports;
/** List of operators, can be a tree. */
ListBase macro;
/** Current running macro, not saved. */
struct wmOperator *opm;
/** Runtime for drawing. */
struct uiLayout *layout;
short flag;
char _pad[6];
} wmOperator;
struct bContext {
int thread;
/* windowmanager context */
struct {
struct wmWindowManager *manager;
struct wmWindow *window;
struct WorkSpace *workspace;
struct bScreen *screen;
struct ScrArea *area;
struct ARegion *region;
struct ARegion *menu;
struct wmGizmoGroup *gizmo_group;
struct bContextStore *store;
/* Operator poll. */
/**
* Store the reason the poll function fails (static string, not allocated).
* For more advanced formatting use `operator_poll_msg_dyn_params`.
*/
const char *operator_poll_msg;
/**
* Store values to dynamically to create the string (called when a tool-tip is shown).
*/
struct bContextPollMsgDyn_Params operator_poll_msg_dyn_params;
} wm;
/* data context */
struct {
struct Main *main;
struct Scene *scene;
int recursion;
/** True if python is initialized. */
bool py_init;
void *py_context;
/**
* If we need to remove members, do so in a copy
* (keep this to check if the copy needs freeing).
*/
void *py_context_orig;
} data;
};