HDK
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
ObjNode_setSelectable.C
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2018
3  * Side Effects Software Inc. All rights reserved.
4  *
5  * Redistribution and use of Houdini Development Kit samples in source and
6  * binary forms, with or without modification, are permitted provided that the
7  * following conditions are met:
8  * 1. Redistributions of source code must retain the above copyright notice,
9  * this list of conditions and the following disclaimer.
10  * 2. The name of Side Effects Software may not be used to endorse or
11  * promote products derived from this software without specific prior
12  * written permission.
13  *
14  * THIS SOFTWARE IS PROVIDED BY SIDE EFFECTS SOFTWARE `AS IS' AND ANY EXPRESS
15  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
16  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
17  * NO EVENT SHALL SIDE EFFECTS SOFTWARE BE LIABLE FOR ANY DIRECT, INDIRECT,
18  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
19  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
20  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
21  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
22  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
23  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  *
25  *----------------------------------------------------------------------------
26  */
27 
28 //****************************************************************************
29 //****************************************************************************
30 //****************************************************************************
31 //
32 // WARNING
33 //
34 // This example is deprecated, and is largely replaced by the inlinecpp
35 // Python module. Except for the ability to raise exceptions, you can
36 // implement this example with the following Python code:
37 //
38 // inlinecpp.extendClass(
39 // hou.ObjNode,
40 // "node_methods",
41 // includes="#include <UT/UT_UndoManager.h>",
42 // function_sources=["""
43 // void setSelectable(OBJ_Node *obj_node, int selectable)
44 // {
45 // if (!obj_node->canAccess(PRM_WRITE_OK))
46 // return;
47 //
48 // UT_AutoUndoBlock undo_block("Setting selectable flag", ANYLEVEL);
49 // obj_node->setPickable(selectable != 0);
50 // }
51 // """])
52 //
53 //****************************************************************************
54 //****************************************************************************
55 //****************************************************************************
56 
57 // This sample shows how to extend HOM using C++. It adds a
58 // hou.ObjNode.setSelectable method that makes an object node selectable/
59 // unselectable in the viewport.
60 
61 // Include Houdini's equivalent of python/Python.h. It creates wrappers of
62 // Python's C API functions, so that Houdini can determine at runtime which
63 // version of Python to use. This file needs to be included first to avoid
64 // gcc warnings. See PY_CPythonAPI.h for more information about the API
65 // wrapper. Note that you are limited to the Python API functions wrapped by
66 // PY_CPythonAPI.h.
67 #include <PY/PY_CPythonAPI.h>
68 
69 // This file is needed for all HDK plugins:
70 #include <UT/UT_DSOVersion.h>
71 
72 // We include this file only to get the forward declaration for
73 // HOMextendLibrary(). See below for details.
74 #include <HOM/HOM_Module.h>
75 
76 // This file contains functions that will run arbitrary Python code:
77 #include <PY/PY_Python.h>
78 
79 // The class defined in this file acquires the GIL within Houdini's Python
80 // runtime environment:
82 
83 // This file defines a convenience class to automatically decrement Python
84 // reference counts:
85 #include <PY/PY_AutoObject.h>
86 
87 // This class in this file is Houdini's internal representation of an object
88 // node:
89 #include <OBJ/OBJ_Node.h>
90 
91 // This file defines OPgetDirector(), which returns the root ("/") node.
92 #include <OP/OP_Director.h>
93 
94 // This file defines a function to demangle a C++ class name from the result
95 // returned by type_info::name():
96 #include <UT/UT_SysSpecific.h>
97 
98 // This file lets us start a undo block.
99 #include <UT/UT_UndoManager.h>
100 
101 static void
102 ObjNode_setSelectable(const char *node_path, bool selectable)
103 {
104  // This is the function that does the actual work. It takes a path
105  // to the object node and a flag to say if it should be selectable.
106  //
107  // Look up the OBJ_Node for the path.
108  OP_Node *op_node = OPgetDirector()->findNode(node_path);
109  if (!op_node)
110  throw HOM_OperationFailed("Internal error (could not find node)");
111 
112  OBJ_Node *obj_node = op_node->castToOBJNode();
113  if (!obj_node)
114  throw HOM_OperationFailed("Internal error (node is not an object)");
115 
116  // Now that we have the OBJ_Node object, we can access anything exposed
117  // by the HDK. For this example, we simply need to call setPickable().
118  // First, though, we create an undo block, so that any operations called
119  // by setPickable will be put into this block.
120  UT_AutoUndoBlock undo_block("Setting selectable flag", ANYLEVEL);
121 
122  // If this node is inside a locked asset, we want to raise a Python
123  // hou.PermissionError exception. To do that, we simply throw an instance
124  // of the C++ HOM_PermissionError class.
125  if (!obj_node->canAccess(PRM_WRITE_OK))
126  throw HOM_PermissionError();
127 
128  obj_node->setPickable(selectable);
129 }
130 
131 static PY_PyObject *
132 createHouException(
133  const char *exception_class_name, const char *instance_message,
134  PY_PyObject *&exception_class)
135 {
136  // Create an instance of the given exception class from the hou
137  // module, passing the instance message into the exeption class's
138  // __init__ method. This function returns a new exception instance, or
139  // NULL if an error occurred. The class is returned in exception_class
140  // and is a borrowed reference.
141  exception_class = NULL;
142 
143  // Note that a PY_AutoObject class is just a wrapper around a
144  // PY_PyObject pointer that will call PY_Py_XDECREF when the it's destroyed.
145  // We use it for Python API functions that return new object instances.
146  // Because this HDK extension is installed after the hou module is
147  // imported, we can be sure that we can be sure hou_module won't be null.
148  PY_AutoObject hou_module(PY_PyImport_ImportModule("hou"));
149 
150  // Look up the exception by name in the module's dictionary. Note that
151  // PY_PyModule_GetDict returns a borrowed reference and that it never
152  // returns NULL. PY_PyDict_GetItemString also returns a borrowed
153  // reference.
154  PY_PyObject *hou_module_dict = PY_PyModule_GetDict(hou_module);
155  exception_class = PY_PyDict_GetItemString(
156  hou_module_dict, exception_class_name);
157 
158  // PY_PyDict_GetItemString doesn't set a Python exception, so we are careful
159  // to set it ourselves if the class name isn't valid.
160  if (!exception_class)
161  {
162  PY_PyErr_SetString(
163  PY_PyExc_RuntimeError(),
164  "Could not find exception class in hou module");
165  return NULL;
166  }
167 
168  // Create an instance of the exception. First create a tuple containing
169  // the arguments to __init__.
170  PY_AutoObject args(PY_Py_BuildValue("(s)", instance_message));
171  if (!args)
172  return NULL;
173 
174  return PY_PyObject_Call(exception_class, args, /*kwargs=*/NULL);
175 }
176 
177 static PY_PyObject *
178 ObjNode_setSelectable_Wrapper(PY_PyObject *self, PY_PyObject *args)
179 {
180  // This is a wrapper that is called from the Python runtime engine. It
181  // translates the Python arguments to C/C++ ones, calls a function to do
182  // the actual work, and converts exceptions raised by that function into
183  // Python exceptions.
184  //
185  // Note that you could also use swig to automatically generate wrapper
186  // functions like this.
187  //
188  // Since this function is called from the Python runtime engine, we
189  // don't need to manually acquire the Python global interpreter lock (GIL).
190 
191  // First extract the arguments: a string and an integer (bool).
192  const char *node_path;
193  int selectable;
194  if (!PY_PyArg_ParseTuple(args, "si", &node_path, &selectable))
195  return NULL;
196 
197  // Now call ObjNode_setSelectable to do the actual work, taking care
198  // of the locking and exception handling here.
199  try
200  {
201  // If this code is called from a thread other than the main thread,
202  // creating a HOM_AutoLock instance will lock, waiting until Houdini
203  // is sitting idle in its event loop. This way, we can be sure that
204  // any code that accesses Houdini's internal state is threadsafe.
205  HOM_AutoLock hom_lock;
206 
207  // Call the wrapped function to do the actual work.
208  ObjNode_setSelectable(node_path, (bool)selectable);
209 
210  // Return PY_Py_None to indicate that no error occurred. If your
211  // wrapped function returns a value, you'll need to convert it into
212  // a Python object here.
213  return PY_Py_None();
214  }
215  catch (HOM_Error &error)
216  {
217  // The exceptions used by the hou module are subclasses of HOM_Error
218  // (and can be found in HOM_Errors.h). We use RTTI to get the class
219  // name, remove the "HOM_" prefix, and look up the corresponding
220  // exception class in the hou Python module.
221  std::string exception_class_name = UTunmangleClassNameFromTypeIdName(
222  typeid(error).name());
223  if (exception_class_name.find("HOM_") == 0)
224  exception_class_name = exception_class_name.substr(4);
225 
226  // Note that a PY_AutoObject class is just a wrapper around a
227  // PY_PyObject pointer that will call PY_Py_XDECREF when the it's
228  // destroyed.
229  PY_PyObject *exception_class;
230  PY_AutoObject exception_instance(createHouException(
231  exception_class_name.c_str(), error.instanceMessage().c_str(),
232  exception_class));
233  if (!exception_instance)
234  return NULL;
235 
236  // Set the exception and return NULL so Python knows an exception was
237  // raised.
238  PY_PyErr_SetObject(exception_class, exception_instance);
239  return NULL;
240  }
241 }
242 
243 void
245 {
246  // This function installs the C++ HOM extension. When the hou module is
247  // first imported, Houdini will call functions named HOMextendLibrary in
248  // HDK dso's. This function is declared in an extern "C" in HOM_Module.h.
249 
250  {
251  // A PY_InterpreterAutoLock will grab the Python global interpreter
252  // lock (GIL). It's important that we have the GIL before making
253  // any calls into the Python API.
254  PY_InterpreterAutoLock interpreter_auto_lock;
255 
256  // We'll create a new module named "_hom_extensions", and add functions
257  // to it. We don't give a docstring here because it's given in the
258  // Python implementation below.
259  static PY_PyMethodDef hom_extension_methods[] = {
260  {"ObjNode_setSelectable", ObjNode_setSelectable_Wrapper,
261  PY_METH_VARARGS(), ""},
262  { NULL, NULL, 0, NULL }
263  };
264 
265  PY_Py_InitModule("_hom_extensions", hom_extension_methods);
266  }
267 
268  // Run some Python code to add a method to the hou.ObjectNode class that
269  // will call our custom function. We create a Python function
270  // that takes in a hou.ObjNode instance. That function calls path()
271  // on the instance to get the full path to the object node, and passes
272  // that path and other arguments to the function in the _hom_extensions
273  // module. Then that function is assigned to a method in hou.ObjNode
274  // and the function's name is removed from the global dictionary.
276  "def _setSelectable(self, selectable):\n"
277  " '''Make this node selectable/unselectable in the viewport\n"
278  " and network editor.'''\n"
279  " import _hom_extensions\n"
280  " _hom_extensions.ObjNode_setSelectable(self.path(), selectable)\n"
281  "__import__('hou').ObjNode.setSelectable = _setSelectable\n"
282  "del _setSelectable\n");
283 }
284 
PY_API bool PYrunPythonStatementsAndExpectNoErrors(const char *python_code, const char *heading=NULL, PY_EvaluationContext *context=NULL)
OP_Node * findNode(const char *path) const
Uses the path (eg. "/obj/geo1") to find a node in our heirarchy.
PY_API int PY_PyArg_ParseTuple(PY_PyObject *args, const char *format,...)
GLsizei const GLchar *const * string
Definition: glcorearb.h:813
PY_API PY_PyObject * PY_Py_BuildValue(const char *format,...)
#define PY_Py_None()
UT_API std::string UTunmangleClassNameFromTypeIdName(const std::string &name)
PY_PyObject
virtual std::string instanceMessage()
Definition: HOM_Errors.h:62
GLuint const GLchar * name
Definition: glcorearb.h:785
int canAccess(unsigned mask) const
virtual int setPickable(int onOff) override
OP_API OP_Director * OPgetDirector()
PY_API PY_PyObject * PY_Py_InitModule(const char *name, PY_PyMethodDef *methods)
OBJ_Node * castToOBJNode() const
Definition: OP_Node.h:587
void HOMextendLibrary()