Blender文档翻译:Operators tutorial(操作教程)
原文:https://wiki.blender.org/index.php/Dev:2.5/Source/Architecture/Operators/Tutorial
逐行解釋操作如何工作的。首先解釋網格細分(mesh subdivide),一個相對簡單的算子。接下來,我們將解釋一個更復雜的模態操作,3D視圖縮放。
網絡細分(Mesh Subdivide)
?注冊
我們必須做的第一件事是向窗口管理器注冊操作符類型。為此,我們定義了一個函數,在啟動時由窗口管理器調用。
1 void MESH_OT_subdivide(wmOperatorType *ot) 2 { 3 PropertyRNA *prop; 4 5 /* identifiers */ 6 ot->name = "Subdivide"; 7 ot->description = "Subdivide selected edges"; 8 ot->idname = "MESH_OT_subdivide"; 9 10 /* api callbacks */ 11 ot->exec = edbm_subdivide_exec; 12 ot->poll = ED_operator_editmesh; 13 14 /* flags */ 15 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; 16 17 /* properties */ 18 prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 100, "Number of Cuts", "", 1, 10); 19 /* avoid re-using last var because it can cause _very_ high poly meshes and annoy users (or worse crash) */ 20 RNA_def_property_flag(prop, PROP_SKIP_SAVE); 21 }讓我們從第一行開始:
void MESH_OT_subdivide(wmOperatorType *ot)MESH定義了操作類別,_OT_(操作類型)是操作ID名稱的標準部分。函數的目的是填充wmOperatorType。
/* identifiers */ot->name = "Subdivide";ot->description = "Subdivide selected edges";ot->idname = "MESH_OT_subdivide";ot->name值表示將在用戶界面中使用的字符串,它是操作的可讀名稱。該描述用于工具提示。idname應與函數的名稱相同,它是該操作的唯一標識符。
/* api callbacks */ot->exec = edbm_subdivide_exec;ot->poll = ED_operator_editmesh;API回調函數定義操作實際運行的方式。將運行poll回調來測試操作符是否可以執行,而exec回調將實際執行操作。我們稍后會詳細討論這些問題。
/* flags */ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;操作標志向窗口管理器提供如何使用操作的信息。在這里,OPTYPE_REGISTER意味著操作應在歷史堆棧注冊。OPTYPE_UNDO表明操作完成后應(譯者:push 到undo??原文:OPTYPE_UNDO indicates that an undo push should be done after the operator has finished.)。
/* properties */prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 100, "Number of Cuts", "", 1, 10);/* avoid re-using last var because it can cause _very_ high poly meshes and annoy users (or worse crash) */RNA_def_property_flag(prop, PROP_SKIP_SAVE);操作可以定義多個屬性。這些屬性然后可以由用戶設置,并且由操作用來修改其行為。這些是RNA屬性,因此有關如何定義它們的更多信息,請參閱RNA文檔。在這種情況下,我們將簡單地定義一個整數,指示切口的數量。
WM
void ED_operatortypes_mesh(void) {...WM_operatortype_append(MESH_OT_subdivide);... }?我們需要確保WindowManager將調用此注冊函數。為此,每個操作類別都有一個函數將注冊函數放入其中。
Poll
poll回調需要驗證要運行操作的正確上下文是否有效。通常,許多操作將使用相同的poll回調。本例中,我們使用由大多數網格編輯操作使用的ED_operator_editmesh函數。
int ED_operator_editmesh(bContext *C) {Object *obedit = CTX_data_edit_object(C);if(obedit && obedit->type == OB_MESH)return NULL != ((Mesh *)obedit->data)->edit_mesh;return 0; }?此函數從上下文中獲取編輯對象,并驗證它是否是網格,且edit_mesh指針是否已設置。
如果輪詢函數失敗,就可以給用戶一個簡單的警告,解釋原因。
可以更改前面的示例來完成:
int ED_operator_editmesh(bContext *C) {...CTX_wm_operator_poll_msg_set(C, "selected object isn't a mesh or not in editmode");return 0; }?Exec
exec回調用于在沒有用戶交互的情況下執行操作(與典型的變換操作相反)。該函數如下所示:
static int edbm_subdivide_exec(bContext *C, wmOperator *op) {Object *obedit = CTX_data_edit_object(C);BMEditMesh *em = BKE_editmesh_from_object(obedit);const int cuts = RNA_int_get(op->ptr, "number_cuts");float smooth = RNA_float_get(op->ptr, "smoothness");const float fractal = RNA_float_get(op->ptr, "fractal") / 2.5f;const float along_normal = RNA_float_get(op->ptr, "fractal_along_normal");if (RNA_boolean_get(op->ptr, "quadtri") && RNA_enum_get(op->ptr, "quadcorner") == SUBD_CORNER_STRAIGHT_CUT){RNA_enum_set(op->ptr, "quadcorner", SUBD_CORNER_INNERVERT);}BM_mesh_esubdivide(em->bm, BM_ELEM_SELECT,smooth, SUBD_FALLOFF_LIN, false,fractal, along_normal,cuts,SUBDIV_SELECT_ORIG, RNA_enum_get(op->ptr, "quadcorner"),RNA_boolean_get(op->ptr, "quadtri"), true, false,RNA_int_get(op->ptr, "seed"));EDBM_update_generic(em, true, true);return OPERATOR_FINISHED; }讓我們從函數聲明開始。
static int edbm_subdivide_exec(bContext *C, wmOperator *op)此函數獲取兩個參數、從中獲取數據的上下文和操作的實例。wmOperator?是當前運行的操作,并存儲其狀態和屬性(不要與用于創建wmOperator的wmOoperatorType相混淆)。
函數返回值用于指示運算符是否成功完成或取消。
Object *obedit = CTX_data_edit_object(C);BMEditMesh *em = BKE_editmesh_from_object(obedit);通常,在執行操作符時,首先要做的就是從上下文中獲取相關數據。在這里,我們獲得了場景,編輯對象和編輯網格。
const int cuts = RNA_int_get(op->ptr, "number_cuts");float smooth = RNA_float_get(op->ptr, "smoothness");const float fractal = RNA_float_get(op->ptr, "fractal") / 2.5f;const float along_normal = RNA_float_get(op->ptr, "fractal_along_normal");接下來,我們使用RNA訪問器函數獲得操作屬性。
BM_mesh_esubdivide(...);此函數實際上將更改編輯并執行細分。如何工作的細節與當前不相關。
EDBM_update_generic(em, true, true);請參閱此函數的源代碼。
void EDBM_update_generic(BMEditMesh *em, const bool do_tessface, const bool is_destructive) {Object *ob = em->ob;/* order of calling isn't important */DAG_id_tag_update(ob->data, OB_RECALC_DATA);WM_main_add_notifier(NC_GEOM | ND_DATA, ob->data);if (do_tessface) {BKE_editmesh_tessface_calc(em);}if (is_destructive) {/* TODO. we may be able to remove this now! - Campbell */// BM_mesh_elem_table_free(em->bm, BM_ALL_NOLOOP); }else {/* in debug mode double check we didn't need to recalculate */BLI_assert(BM_mesh_elem_table_check(em->bm) == true);}/* don't keep stale derivedMesh data around, see: [#38872] */BKE_editmesh_free_derivedmesh(em);#ifdef DEBUG{BMEditSelection *ese;for (ese = em->bm->selected.first; ese; ese = ese->next) {BLI_assert(BM_elem_flag_test(ese->ele, BM_ELEM_SELECT));}} #endif }執行操作后,我們需要更新依賴的圖并發送通知。我們將呼叫依賴圖并告訴它數據已改變,這將導致任何依賴于該網格幾何體內容的,例如修飾器重新執行。
notifier調用用于更新用戶界面的其他部分。在這里,我們表明我們已經改變了一個物體的幾何數據。例如,3D視圖將接收此notifier并請求重繪。
return OPERATOR_FINISHED;最后,我們返回操作符已經成功完成。在其他情況下,我們可能希望返回OPERATOR_CANCELLED,以指示什么都沒有做。因為我們返回OPERATOR_FINISHED,這將導致撤銷推送,并意味著將注冊該操作。
?重新執行
這個操作可以從最后一個操作面板重新執行。這是自動實現的,因為操作有一個exec回調。對于交互式操作來說,還需要更多的服務,我們將在下面看到這一點。
3D View Zoom(3D視圖綻放)
注冊
void VIEW3D_OT_zoom(wmOperatorType *ot) {/* identifiers */ot->name = "Zoom view";ot->description = "Zoom in/out in the view.";ot->idname = "VIEW3D_OT_zoom";/* api callbacks */ot->invoke = viewzoom_invoke;ot->exec = viewzoom_exec;ot->modal = viewzoom_modal;ot->poll = ED_operator_view3d_active;/* flags */ot->flag = OPTYPE_BLOCKING;/* properties */RNA_def_int(ot->srna, "delta", 0, INT_MIN, INT_MAX, "Delta", "", INT_MIN, INT_MAX); }這與網格細分操作非常相似,但我們將討論兩個不同之處。
/* api callbacks */ot->invoke = viewzoom_invoke;ot->exec = viewzoom_exec;ot->modal = viewzoom_modal;ot->poll = ED_operator_view3d_active;除了exec和poll回調之外,這個操作符還具有invoke和modal回調。這些是用來使操作符交互,對像鼠標移動這樣的事件作出反應。我們稍后再討論這些問題。
/* flags */ot->flag = OPTYPE_BLOCKING;flag是不同的。我們不希望在歷史堆棧中注冊這個操作,也不希望它導致撤銷推送。OPTYPE_BLOCKING標志指示這個操作應該捕獲所有鼠標移動,即使它超出了窗口。
Poll
int ED_operator_view3d_active(bContext *C) {if(ED_operator_areaactive(C)) {SpaceLink *sl = (SpaceLink *)CTX_wm_space_data(C);return sl && (sl->spacetype == SPACE_VIEW3D);}return 0; }這里的輪詢回調不測試數據,但確保我們處于正確的空間類型,因為這是我們將要編輯的內容。
Invoke
static int viewzoom_invoke(bContext *C, wmOperator *op, wmEvent *event) {if(RNA_property_is_set(op->ptr, "delta")) {return viewzoom_exec(C, op);}else {/* makes op->customdata */viewops_data(C, op, event);/* add temp handler */WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);return OPERATOR_RUNNING_MODAL;} }invoke函數在運行時由用戶調用,如果它不存在則使用exec。
static int viewzoom_invoke(bContext *C, wmOperator *op, wmEvent *event)?與exec回調相較不同之處在于事件。例如,這是導致調用操作的事件,它可以用來獲取鼠標坐標。
if(RNA_property_is_set(op->ptr, "delta")) {return viewzoom_exec(C, op);}?首先,如果已經設置了所有屬性,則操作員嘗試執行exec。這不是必需的行為,但在某些情況下可能很方便。
else {/* makes op->customdata */viewops_data(C, op, event);?否則,我們將開始一個modal操作。使用事件當前鼠標的位置,初始狀態將被保存在OP -> customdata。這是一個可以用來存儲任何數據的void *屬性,用來存儲操作時間。存放具體數據的細節在這里并不重要。
/* add temp handler */WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op);?接下來,我們將自身注冊為窗口級別的modal處理器。這意味著此窗口中的所有事件都將首先通過該操作,從而阻止所有其他事件處理器。
return OPERATOR_RUNNING_MODAL;}?最后,我們標示操作現在正在運行modal,因此尚未完成。
Modal
static int viewzoom_modal(bContext *C, wmOperator *op, wmEvent *event) {ViewOpsData *vod = op->customdata;/* execute the events */switch(event->type) {case MOUSEMOVE:viewzoom_apply(vod, event->x, event->y);break;default:/* origkey may be zero when invoked from a button */if(ELEM3(event->type, ESCKEY, LEFTMOUSE, RIGHTMOUSE) || (event->type==vod->origkey && event->val==0)) {request_depth_update(CTX_wm_region_view3d(C));MEM_freeN(vod);op->customdata = NULL;return OPERATOR_FINISHED;}}return OPERATOR_RUNNING_MODAL; }modal回調可在任何事件上調用,然后我們可以決定是否處理。
ViewOpsData *vod = op->customdata;首先,我們獲取invoke中創建customdata。在其他方面,這是用來獲取原始的鼠標位置,以便我們知道鼠標如何移動的。
/* execute the events */switch(event->type) {case MOUSEMOVE:viewzoom_apply(vod, event->x, event->y);break;?接下來,我們將尋找感興趣的事件。如果鼠標移動,我們將傳遞鼠標坐標并應用縮放。函數的內部運作在這里也與我們無關。
default:/* origkey may be zero when invoked from a button */if(ELEM3(event->type, ESCKEY, LEFTMOUSE, RIGHTMOUSE) || (event->type==vod->origkey && event->val==0)) {?這一行檢查事件以停止操作。退出時,鼠標左鍵和右鍵都會取消。另外,釋放我們最初按下的鍵(如果操作被綁在鍵盤上而不是鼠標上),將停止操作。
request_depth_update(CTX_wm_region_view3d(C));MEM_freeN(vod);op->customdata = NULL;?我們請求3D視圖更新,因為我們改變了它。我們也需要釋放我們臨時儲存的customdata。
return OPERATOR_FINISHED;}?標示此修飾器已完成操作,其處理器現在可移除。
return OPERATOR_RUNNING_MODAL;如果操作尚未完成,則執行此行,標示我們要繼續接收事件。
Exec
static int viewzoom_exec(bContext *C, wmOperator *op) {View3D *v3d = CTX_wm_view3d(C);RegionView3D *rv3d = CTX_wm_region_view3d(C);int delta = RNA_int_get(op->ptr, "delta");...request_depth_update(CTX_wm_region_view3d(C));ED_region_tag_redraw(CTX_wm_region(C));return OPERATOR_FINISHED; }?這很類似網格細分exec。我們從上下文中獲取一些數據,獲得操作屬性。接著我們執行操作,然后發出一些信號來更新和重繪。
如果我們希望操作是可重復的,我們需要在invokel回調實現后,接著實現exec回調回,如果不能,我們可以把它放到一邊。注意,modal回調應該在完成操作時設置delta(在我們的例子中,它在每次鼠標移動中設置它),這樣重復執行可以使用它來縮放相同的數量。
Category:?Script轉載于:https://www.cnblogs.com/jiaping/p/8228252.html
總結
以上是生活随笔為你收集整理的Blender文档翻译:Operators tutorial(操作教程)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 用pyhton 写一个简单的三级列表
- 下一篇: X509证书认证流程介绍