diff --git a/Rendering/PythonContext2D/CMakeLists.txt b/Rendering/PythonContext2D/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..2c6b320a3607c75e381cf7b7c2d08f1dfdd12e51 --- /dev/null +++ b/Rendering/PythonContext2D/CMakeLists.txt @@ -0,0 +1,5 @@ +set(module_SRCS + vtkPythonItem.cxx + ) + +vtk_module_library(vtkPythonContext2D ${module_SRCS}) diff --git a/Rendering/PythonContext2D/Testing/Data/Baseline/testPythonItem.png.sha512 b/Rendering/PythonContext2D/Testing/Data/Baseline/testPythonItem.png.sha512 new file mode 100644 index 0000000000000000000000000000000000000000..5c8501f419568c8e7a05c287559e8d1c12a0c855 --- /dev/null +++ b/Rendering/PythonContext2D/Testing/Data/Baseline/testPythonItem.png.sha512 @@ -0,0 +1 @@ +69efe0b7307942844762db383e8752beca8041a28f408c57778bd06569b3febb8973f1dc41d6ceb02555465f417405516f4406a1221d3d906eaf15962ee293cc diff --git a/Rendering/PythonContext2D/Testing/Python/CMakeLists.txt b/Rendering/PythonContext2D/Testing/Python/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..dfe0423236c379081ec2857aaebb36b1cadec350 --- /dev/null +++ b/Rendering/PythonContext2D/Testing/Python/CMakeLists.txt @@ -0,0 +1,3 @@ +vtk_add_test_python( + testPythonItem.py + ) diff --git a/Rendering/PythonContext2D/Testing/Python/testPythonItem.py b/Rendering/PythonContext2D/Testing/Python/testPythonItem.py new file mode 100644 index 0000000000000000000000000000000000000000..7ae57fd3ea93e48f4d59d0b2ef9286d1e2c32b91 --- /dev/null +++ b/Rendering/PythonContext2D/Testing/Python/testPythonItem.py @@ -0,0 +1,151 @@ +import sys +import vtk +from vtk.test import Testing + + +class CustomPythonItem(object): + def __init__(self, polydata): + self.polydata = polydata + + def Initialize(self, vtkSelf): + return True + + def Paint(self, vtkSelf, context2D): + context2D.DrawPolyData(0.0, + 0.0, + self.polydata, + self.polydata.GetCellData().GetScalars(), + vtk.VTK_SCALAR_MODE_USE_CELL_DATA) + + pen = context2D.GetPen() + + penColor = [0, 0, 0] + pen.GetColor(penColor) + penWidth = pen.GetWidth() + + brush = context2D.GetBrush() + brushColor = [0, 0, 0, 0] + brush.GetColor(brushColor) + + pen.SetColor([0, 0, 255]) + brush.SetColor([0, 0, 255]) + context2D.DrawWedge(0.75, 0.25, 0.125, 0.005, 30.0, 60.0) + + pen.SetWidth(20.0) + pen.SetColor([0, 0, 0]) + brush.SetColor([0, 0, 0]) + context2D.DrawMarkers(vtk.vtkMarkerUtilities.CIRCLE, False, [0.1, 0.1, 0.5, 0.5, 0.9, 0.9], 3) + pen.SetWidth(1.0) + + textProp = vtk.vtkTextProperty() + textProp.BoldOn() + textProp.ItalicOn() + textProp.SetFontSize(22) + textProp.SetColor(0.5, 0.0, 1.0) + textProp.SetOrientation(45) + context2D.ApplyTextProp(textProp) + context2D.DrawString(0.35, 0.4, b"Context2D!") + + pen.SetColor([200, 200, 30]) + brush.SetColor([200, 200, 30]) + brush.SetOpacity(128) + context2D.DrawPolygon([0.5, 0.5, 0.75, 0.0, 1.0, 0.5], 3) + + pen.SetColor([133, 70, 70]) + brush.SetColor([133, 70, 70]) + brush.SetOpacity(255) + context2D.DrawArc(0.25, 0.75, 0.125, 0.0, 360.0) + + pen.SetWidth(penWidth) + pen.SetColor(penColor) + brush.SetColor(brushColor[:3]) + brush.SetOpacity(brushColor[3]) + + return True + + +def buildPolyData(): + pd = vtk.vtkPolyData() + + pts = vtk.vtkPoints() + pts.InsertNextPoint([0.1, 0.1, 0.0]) + pts.InsertNextPoint([0.9, 0.9, 0.0]) + + pd.SetPoints(pts) + + lines = vtk.vtkCellArray() + lines.InsertNextCell(2) + lines.InsertCellPoint(0) + lines.InsertCellPoint(1) + + pd.SetLines(lines) + + colors = vtk.vtkUnsignedCharArray() + colors.SetNumberOfComponents(4) + colors.InsertNextTypedTuple([27, 128, 89, 255]) + + pd.GetCellData().SetScalars(colors) + + return pd + + +class TestPythonItem(Testing.vtkTest): + def testPythonItem(self): + width = 400 + height = 400 + + view = vtk.vtkContextView() + renWin = view.GetRenderWindow() + renWin.SetSize(width, height) + + area = vtk.vtkInteractiveArea() + view.GetScene().AddItem(area) + + drawAreaBounds = vtk.vtkRectd(0.0, 0.0, 1.0, 1.0) + + vp = [0.05, 0.95, 0.05, 0.95] + screenGeometry = vtk.vtkRecti(int(vp[0] * width), + int(vp[2] * height), + int((vp[1] - vp[0]) * width), + int((vp[3] - vp[2]) * height)) + + item = vtk.vtkPythonItem() + item.SetPythonObject(CustomPythonItem(buildPolyData())) + item.SetVisible(True) + area.GetDrawAreaItem().AddItem(item) + + area.SetDrawAreaBounds(drawAreaBounds) + area.SetGeometry(screenGeometry) + area.SetFillViewport(False) + area.SetShowGrid(False) + + axisLeft = area.GetAxis(vtk.vtkAxis.LEFT) + axisRight = area.GetAxis(vtk.vtkAxis.RIGHT) + axisBottom = area.GetAxis(vtk.vtkAxis.BOTTOM) + axisTop = area.GetAxis(vtk.vtkAxis.TOP) + axisTop.SetVisible(False) + axisRight.SetVisible(False) + axisLeft.SetVisible(False) + axisBottom.SetVisible(False) + axisTop.SetMargins(0, 0) + axisRight.SetMargins(0, 0) + axisLeft.SetMargins(0, 0) + axisBottom.SetMargins(0, 0) + + renWin.Render() + + # Create a vtkTesting object + rtTester = vtk.vtkTesting() + rtTester.SetRenderWindow(renWin) + + for arg in sys.argv[1:]: + rtTester.AddArgument(arg) + + # Perform the image comparison test and print out the result. + result = rtTester.RegressionTest(0.0) + + if result == 0: + raise Exception("TestPythonItem failed.") + +if __name__ == "__main__": + Testing.main([(TestPythonItem, 'test')]) diff --git a/Rendering/PythonContext2D/module.cmake b/Rendering/PythonContext2D/module.cmake new file mode 100644 index 0000000000000000000000000000000000000000..2ddd15eaac58adbbbe7698e21feeac6c97b554ab --- /dev/null +++ b/Rendering/PythonContext2D/module.cmake @@ -0,0 +1,19 @@ +if (VTK_WRAP_PYTHON) + vtk_module(vtkPythonContext2D + GROUPS + StandAlone + COMPILE_DEPENDS + vtkPython + OPTIONAL_PYTHON_LINK + EXCLUDE_FROM_JAVA_WRAPPING + TEST_DEPENDS + vtkTestingCore + KIT + vtkWrapping + DEPENDS + vtkRenderingContext2D + PRIVATE_DEPENDS + vtkCommonCore + vtkWrappingPythonCore + ) +endif () diff --git a/Rendering/PythonContext2D/vtkPythonItem.cxx b/Rendering/PythonContext2D/vtkPythonItem.cxx new file mode 100644 index 0000000000000000000000000000000000000000..df655160eb4f3d05cfe7cf7d764bbe2d337ca6ee --- /dev/null +++ b/Rendering/PythonContext2D/vtkPythonItem.cxx @@ -0,0 +1,186 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkPythonItem.cxx + + Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen + All rights reserved. + See Copyright.txt or http://www.kitware.com/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ +#include "vtkPythonItem.h" +#include "vtkObjectFactory.h" + +#include "vtkContext2D.h" +#include "vtkInformation.h" +#include "vtkInformationVector.h" +#include "vtkPythonUtil.h" +#include "vtkSmartPyObject.h" + +vtkStandardNewMacro(vtkPythonItem); + +//------------------------------------------------------------------------------ +void vtkPythonItem::PrintSelf(ostream& os, vtkIndent indent) +{ + this->Superclass::PrintSelf(os,indent); + + vtkPythonScopeGilEnsurer gilEnsurer; + vtkSmartPyObject str; + if (this->Object) + { + str.TakeReference(PyObject_Str(this->Object)); + } + + os << indent << "Object: " << Object << std::endl; + if (str) + { + os << indent << "Object (string): "; +#ifndef VTK_PY3K + os << PyString_AsString(str); +#else + PyObject *bytes = PyUnicode_EncodeLocale(str, VTK_PYUNICODE_ENC); + if (bytes) + { + os << PyBytes_AsString(bytes); + Py_DECREF(bytes); + } +#endif + os << std::endl; + } +} + +//------------------------------------------------------------------------------ +vtkPythonItem::vtkPythonItem() +{ + this->Object = nullptr; +} + +//------------------------------------------------------------------------------ +vtkPythonItem::~vtkPythonItem() +{ + // we check if Python is still initialized since the Python interpreter may + // have been finalized before the VTK object is released. + if (Py_IsInitialized()) + { + vtkPythonScopeGilEnsurer gilEnsurer; + Py_XDECREF(this->Object); + } +} + +// This macro gets the method passed in as the parameter method +// from the PyObject passed in as the parameter obj and creates a +// vtkSmartPyObject variable with the name passed in as the parameter +// var containing that method's PyObject. If obj is nullptr, obj.method +// does not exist or obj.method is not a callable method, this macro +// causes the function using it to return with the return value +// passed in as the parameter failValue +// var - the name of the resulting vtkSmartPyObject with the +// method object in it. Can be used in the code following +// the macro's use as the variable name +// obj - the PyObject to get the method from +// method - the name of the method to look for. Should be a +// C string. +// failValue - the value to return if the lookup fails and the +// function using the macro should return. Pass in a +// block comment /**/ for void functions using this macro +#define VTK_GET_METHOD(var, obj, method, failValue) \ + if (!(obj)) \ + { \ + return failValue; \ + } \ + vtkSmartPyObject var(PyObject_GetAttrString(obj, method)); \ + if (!(var)) \ + { \ + return failValue; \ + } \ + if (!PyCallable_Check(var)) \ + { \ + return failValue; \ + } + +//------------------------------------------------------------------------------ +static PyObject* VTKToPython(vtkObjectBase* obj) +{ + // Return value: New reference. + return vtkPythonUtil::GetObjectFromPointer(obj); +} + +//------------------------------------------------------------------------------ +bool vtkPythonItem::CheckResult(const char* method, const vtkSmartPyObject &res) +{ + vtkPythonScopeGilEnsurer gilEnsurer; + if (!res) + { + vtkErrorMacro("Failure when calling method: \"" << method << "\":"); + if (PyErr_Occurred() != nullptr) + { + PyErr_Print(); + PyErr_Clear(); + } + return false; + } + if (!PyBool_Check(res)) + { + vtkWarningMacro("The method \"" << method << "\" should have returned boolean but did not") + return false; + } + + if (res == Py_False) + { + return false; + } + + return true; +} + +//------------------------------------------------------------------------------ +void vtkPythonItem::SetPythonObject(PyObject* obj) +{ + vtkPythonScopeGilEnsurer gilEnsurer; + + if (!obj) + { + return; + } + + Py_XDECREF(this->Object); + + this->Object = obj; + Py_INCREF(this->Object); + + char mname[] = "Initialize"; + VTK_GET_METHOD(method, this->Object, mname, /* no return */) + + vtkSmartPyObject args(PyTuple_New(1)); + + PyObject* vtkself = VTKToPython(this); + PyTuple_SET_ITEM(args.GetPointer(), 0, vtkself); + + vtkSmartPyObject result(PyObject_Call(method, args, nullptr)); + + CheckResult(mname, result); +} + +//------------------------------------------------------------------------------ +bool vtkPythonItem::Paint(vtkContext2D *painter) +{ + vtkPythonScopeGilEnsurer gilEnsurer; + char mname[] = "Paint"; + VTK_GET_METHOD(method, this->Object, mname, 0) + + vtkSmartPyObject args(PyTuple_New(2)); + + PyObject* vtkself = VTKToPython(this); + PyTuple_SET_ITEM(args.GetPointer(), 0, vtkself); + + PyObject* pypainter = VTKToPython(painter); + PyTuple_SET_ITEM(args.GetPointer(), 1, pypainter); + + vtkSmartPyObject result(PyObject_Call(method, args, nullptr)); + + return CheckResult(mname, result); +} diff --git a/Rendering/PythonContext2D/vtkPythonItem.h b/Rendering/PythonContext2D/vtkPythonItem.h new file mode 100644 index 0000000000000000000000000000000000000000..1f8229a77c0336447b123af03c8538124dcb32d1 --- /dev/null +++ b/Rendering/PythonContext2D/vtkPythonItem.h @@ -0,0 +1,70 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: vtkPythonItem.h + + Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen + All rights reserved. + See Copyright.txt or http://www.kitware.com/Copyright.htm for details. + + This software is distributed WITHOUT ANY WARRANTY; without even + the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + PURPOSE. See the above copyright notice for more information. + +=========================================================================*/ + +/** + * @class vtkPythonItem + * @brief A vtkContextItem that can be implemented in Python + * + * + * This class allows implementation of arbitrary context items in Python. + * + * @sa + * vtkAbstractContextItem +*/ + +#ifndef vtkPythonItem_h +#define vtkPythonItem_h +#if !defined(__VTK_WRAP__) || defined(__VTK_WRAP_HIERARCHY__) || defined(__VTK_WRAP_PYTHON__) + +#include "vtkPython.h" // Must be first + +#include "vtkPythonContext2DModule.h" // For export macro +#include "vtkContextItem.h" + +class vtkSmartPyObject; + +class VTKPYTHONCONTEXT2D_EXPORT vtkPythonItem : public vtkContextItem +{ +public: + vtkTypeMacro(vtkPythonItem, vtkContextItem); + void PrintSelf(ostream &os, vtkIndent indent) override; + + static vtkPythonItem * New(); + + /** + * Specify the Python object to use to operate on the data. A reference will + * be taken on the object. This will also invoke Initialize() on the Python + * object, providing an opportunity to perform tasks commonly done in the + * constructor of C++ vtkContextItem subclasses. + */ + void SetPythonObject(PyObject* obj); + + bool Paint(vtkContext2D *painter) override; + +protected: + vtkPythonItem(); + ~vtkPythonItem() override; + +private: + vtkPythonItem(const vtkPythonItem &) = delete; + void operator=(const vtkPythonItem &) = delete; + + bool CheckResult(const char* method, const vtkSmartPyObject& res); + + PyObject* Object; +}; + +#endif // #ifndef vtkPythonItem_h +#endif