Salome HOME
cd5e751ed25b59aad12f5ba14fd35f34fc4e3117
[modules/gui.git] / tools / CurvePlot / src / cpp / CurvePlot.cxx
1 // Copyright (C) 2010-2022  CEA/DEN, EDF R&D
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Lesser General Public
5 // License as published by the Free Software Foundation; either
6 // version 2.1 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Lesser General Public License for more details.
12 //
13 // You should have received a copy of the GNU Lesser General Public
14 // License along with this library; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 //
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 //
19 // Author : Adrien BRUNETON
20 //
21
22 #include <Python.h>
23
24 // see  https://docs.scipy.org/doc/numpy/reference/c-api.array.html?highlight=import_array
25 // and  https://docs.scipy.org/doc/numpy-1.15.0/reference/c-api.deprecations.html
26 #define PY_ARRAY_UNIQUE_SYMBOL CURVEPLOT_ARRAY_API
27 #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
28 #include <numpy/ndarraytypes.h>
29 #include <numpy/ndarrayobject.h>
30 #include <PyInterp_Utils.h>  // GUI
31
32 #include "CurvePlot.hxx"
33 #include "CurvePlot_Exception.hxx"
34
35 namespace
36 {
37   PyObject * strToPyUnicode(std::string s)
38   {
39     return PyUnicode_DecodeUTF8(s.c_str(), s.size(), (char*)"strict");
40   }
41
42   void HandleAndPrintPyError(std::string msg)
43   {
44     if(PyErr_Occurred())
45       {
46         PyErr_Print();
47         throw CURVEPLOT::Exception(msg);
48       }
49   }
50 }
51
52 namespace CURVEPLOT
53 {
54   /**
55    * To be called before doing anything
56    */
57   void* InitializeCurvePlot()
58   {
59     PyLockWrapper lock;
60     // TODO: discuss where the below should really happen:
61     // doc: http://docs.scipy.org/doc/numpy/reference/c-api.array.html#importing-the-api
62     import_array(); // a macro really which contains a return
63     return NULL;
64   }
65
66   class ColumnVector::Internal
67   {
68   public:
69     Internal() : _npArray(0) {}
70     ~Internal() {}
71
72     PyArrayObject * _npArray;
73   };
74
75   ColumnVector::ColumnVector()
76   {
77     _impl = new Internal();
78   }
79
80   ColumnVector::~ColumnVector()
81   {
82     delete _impl;
83   }
84
85   int ColumnVector::size() const
86   {
87     if (!_impl->_npArray)
88       return 0;
89     {
90         PyLockWrapper lock;
91         int ndim = PyArray_NDIM(_impl->_npArray);
92         if (ndim != 1)
93           throw Exception("ColumnVector::size() : wrong number of dimensions for internal array!!");
94         npy_intp * dims = PyArray_DIMS(_impl->_npArray);
95         return dims[0];
96     }
97   }
98
99   ColumnVector ColumnVector::BuildFromCMemory(double * data, int size)
100   {
101     ColumnVector ret;
102     if (size <= 0)
103       return ret;
104     npy_intp dims[1] = {size};
105
106     {
107       PyLockWrapper lock;
108       PyObject * obj = PyArray_SimpleNewFromData(1,dims,NPY_DOUBLE, data);
109       PyArrayObject * aobj = (PyArrayObject * )obj;
110
111       // Make Numpy responsible of the memory of the array (the memory will be freed
112       // as soon as the array is released in NumPy)
113       PyArray_ENABLEFLAGS(aobj, NPY_ARRAY_OWNDATA);
114
115       ret._impl->_npArray = aobj;
116       return ret;
117     }
118   }
119
120   ColumnVector ColumnVector::BuildFromStdVector(const std::vector<double> & vec)
121   {
122     ColumnVector ret;
123     if (vec.size() == 0)
124       return ret;
125
126     double * c_mem = (double *)malloc(sizeof(double) * vec.size());
127     if (!c_mem)
128       throw Exception("ColumnVector::BuildFromStdVector() : memory allocation error!");
129     const double * data = &vec.front();
130     std::copy(data, data+vec.size(), c_mem);
131     npy_intp dims[1] = {(intptr_t) vec.size()};
132
133     {
134       PyLockWrapper lock;
135       PyObject * obj = PyArray_SimpleNewFromData(1,dims,NPY_DOUBLE, c_mem);
136       PyArrayObject * aobj = (PyArrayObject * )obj;
137
138       // Make Numpy responsible of the memory of the array (the memory will be freed
139       // as soon as the array is released in NumPy)
140       PyArray_ENABLEFLAGS(aobj, NPY_ARRAY_OWNDATA);
141
142       ret._impl->_npArray = aobj;
143       return ret;
144     }
145   }
146
147   std::string ColumnVector::toStdString() const
148   {
149     std::string ret_str = "(None)";
150     if (!_impl->_npArray)
151       return ret_str;
152
153     {
154        PyLockWrapper lock;
155        PyObjWrapper ret_py(
156                  PyObject_CallMethod((PyObject *)_impl->_npArray, (char *)"__str__", NULL)
157                  );
158        // Now extract the returned string
159        if(!PyUnicode_Check(ret_py))
160          throw Exception("CurvePlot::toStdString(): Unexpected returned type!");
161        Py_ssize_t size;
162        const char *ptr = PyUnicode_AsUTF8AndSize(ret_py, &size);
163        ret_str = std::string(ptr);
164     }
165     return ret_str;
166   }
167
168   void ColumnVector::createPythonVar(std::string varName) const
169   {
170     PyObject* main_module = PyImport_AddModule((char*)"__main__");
171     PyObject* global_dict = PyModule_GetDict(main_module);
172     PyDict_SetItemString(global_dict, varName.c_str(), (PyObject *)_impl->_npArray);
173   }
174
175   void ColumnVector::cleanPythonVar(std::string varName) const
176   {
177     // Could be a static method really ...
178
179     std::string s = std::string("del ") + varName;
180     const char * cmd = s.c_str();
181     PyRun_SimpleString(cmd);
182   }
183
184   CurvePlot * CurvePlot::_instance = NULL;
185
186   class CurvePlot::Internal
187   {
188   public:
189     Internal() : _controller(0) {}
190     ///! Plot2d controller from Python:
191     PyObject * _controller;
192   };
193
194   CurvePlot::CurvePlot(bool test_mode)
195   {
196     // TODO: do not use an intermediate variable '__cont', but use directly Py***CallMethod()
197     _impl = new Internal();
198     {
199     PyLockWrapper lock;
200     std::string code;
201     if (test_mode)
202        code = std::string("import curveplot; from curveplot.SalomePyQt_MockUp import SalomePyQt;") +
203            std::string("__cont=curveplot.PlotController.GetInstance(sgPyQt=SalomePyQt())");
204     else
205        code = std::string("import curveplot;")+
206            std::string("__cont=curveplot.PlotController.GetInstance()");
207
208     int ret = PyRun_SimpleString(const_cast<char*>(code.c_str()));
209     if (ret == -1)
210       throw Exception("CurvePlot::CurvePlot(): Unable to load curveplot Python module!");
211
212     // Now get the reference to __engine and save the pointer.
213     // All the calls below returns *borrowed* references
214     PyObject* main_module = PyImport_AddModule((char*)"__main__");
215     PyObject* global_dict = PyModule_GetDict(main_module);
216     PyObject* tmp = PyDict_GetItemString(global_dict, "__cont");
217
218     _impl->_controller = tmp;
219     Py_INCREF(_impl->_controller);
220     PyRun_SimpleString(const_cast<char*>("del __cont"));
221     }
222   }
223
224   CurvePlot::~CurvePlot()
225   {
226     if(_impl->_controller != NULL)
227       {
228         PyLockWrapper lock;
229         Py_XDECREF(_impl->_controller);
230       }
231     delete _impl;
232   }
233
234   CurvePlot * CurvePlot::GetInstance(bool test_mode)
235   {
236     if(!_instance)
237       _instance = new CurvePlot(test_mode);
238     return _instance;
239   }
240
241   void CurvePlot::ToggleCurveBrowser(bool with_curve_browser)
242   {
243     if(_instance)
244       throw Exception("CurvePlot::ToggleCurveBrowser() must be invoked before anything else!");
245
246     PyLockWrapper lock;
247     std::string bool_s = with_curve_browser ? "True" : "False";
248     std::string cod = std::string("import curveplot; curveplot.PlotController.WITH_CURVE_BROWSER=") + bool_s;
249     PyRun_SimpleString(const_cast<char *>(cod.c_str()));
250     HandleAndPrintPyError("CurvePlot::ToggleCurveBrowser(): Unable to toggle Curve Browser!");
251   }
252
253   PlotID CurvePlot::AddCurve(const ColumnVector & x, const ColumnVector & y,
254                              PlotID & plot_set_id,
255                              std::string curve_label/*=""*/, std::string x_label/*=""*/, std::string y_label/*=""*/,
256                              bool append/*=true*/)
257   {
258     PyLockWrapper lock;
259     PyObject * cont = GetInstance()->_impl->_controller;
260
261     PyObject * xx = (PyObject *)x._impl->_npArray;
262     PyObject * yy = (PyObject *)y._impl->_npArray;
263
264     PyObjWrapper ret(
265           PyObject_CallMethod(cont, (char *)"AddCurve", (char *)"OOOOOi", xx, yy,
266               strToPyUnicode(curve_label), strToPyUnicode(x_label), strToPyUnicode(y_label),
267               append ? 1 : 0)
268           );
269     HandleAndPrintPyError("CurvePlot::AddCurve(): unexpected error!");
270     // Now extract curve_id and plot_set_id from the returned tuple:
271     if(!PyTuple_Check(ret))
272         throw Exception("CurvePlot::AddCurve(): Unexpected returned type!");
273     PyObject * o1 = PyTuple_GetItem(ret, 0);
274     if (!PyLong_Check(o1))
275       throw Exception("CurvePlot::AddCurve(): Unexpected returned type!");
276     PlotID curveId = PyLong_AsLong(o1);
277     PyObject * o2 = PyTuple_GetItem(ret, 1);
278     if (!PyLong_Check(o2))
279       throw Exception("CurvePlot::AddCurve(): Unexpected returned type!");
280     plot_set_id = PyLong_AsLong(o2);
281     return curveId;
282   }
283
284   PlotID CurvePlot::AddPlotSet(std::string title/*=""*/)
285   {
286     PyLockWrapper lock;
287     PyObject * cont = GetInstance()->_impl->_controller;
288
289     PyObjWrapper ret(
290           PyObject_CallMethod(cont, (char *)"AddPlotSet", (char *)"O", strToPyUnicode(title))
291           );
292     HandleAndPrintPyError("CurvePlot::AddPlotSet(): unexpected error!");
293     return PyLong_AsLong(ret);
294   }
295
296   PlotID CurvePlot::DeleteCurve(PlotID curve_id/*=-1*/)
297   {
298     PyLockWrapper lock;
299     PyObject * cont = GetInstance()->_impl->_controller;
300
301     PyObjWrapper ret(
302           PyObject_CallMethod(cont, (char *)"DeleteCurve", (char *)"i", curve_id)
303           );
304     HandleAndPrintPyError("CurvePlot::DeleteCurve(): unexpected error!");
305     return PyLong_AsLong(ret);
306   }
307
308   PlotID CurvePlot::DeletePlotSet(PlotID plot_set_id/*=-1*/)
309   {
310     PyLockWrapper lock;
311     PyObject * cont = GetInstance()->_impl->_controller;
312
313     PyObjWrapper ret(
314           PyObject_CallMethod(cont, (char *)"DeletePlotSet", (char *)"i", plot_set_id)
315           );
316     HandleAndPrintPyError("CurvePlot::DeletePlotSet(): unexpected error!");
317     return PyLong_AsLong(ret);
318   }
319
320   PlotID CurvePlot::ClearPlotSet(PlotID plot_set_id/*=-1*/)
321   {
322     PyLockWrapper lock;
323     PyObject * cont = GetInstance()->_impl->_controller;
324
325     PyObjWrapper ret(
326           PyObject_CallMethod(cont, (char *)"ClearPlotSet", (char *)"i", plot_set_id)
327           );
328     HandleAndPrintPyError("CurvePlot::ClearPlotSet(): unexpected error!");
329     return PyLong_AsLong(ret);
330   }
331
332   bool CurvePlot::SetXLabel(std::string x_label, PlotID plot_set_id/*=-1*/)
333   {
334     PyLockWrapper lock;
335     PyObject * cont = GetInstance()->_impl->_controller;
336
337     PyObjWrapper ret(
338           PyObject_CallMethod(cont, (char *)"SetXLabel", (char *)"Oi", strToPyUnicode(x_label), plot_set_id)
339           );
340     HandleAndPrintPyError("CurvePlot::SetXLabel(): unexpected error!");
341     return ((PyObject *)ret == Py_True);
342   }
343
344   bool CurvePlot::SetYLabel(std::string y_label, PlotID plot_set_id/*=-1*/)
345   {
346     PyLockWrapper lock;
347     PyObject * cont = GetInstance()->_impl->_controller;
348
349     PyObjWrapper ret(
350           PyObject_CallMethod(cont, (char *)"SetYLabel", (char *)"Oi", strToPyUnicode(y_label), plot_set_id)
351           );
352     HandleAndPrintPyError("CurvePlot::SetYLabel(): unexpected error!");
353     return ((PyObject *)ret == Py_True);
354   }
355   
356   bool CurvePlot::SetPlotSetTitle(std::string title, PlotID plot_set_id/*=-1*/)
357   {
358     PyLockWrapper lock;
359     PyObject * cont = GetInstance()->_impl->_controller;
360
361     PyObjWrapper ret(
362           PyObject_CallMethod(cont, (char *)"SetPlotSetTitle", (char *)"Oi", strToPyUnicode(title), plot_set_id));
363     HandleAndPrintPyError("CurvePlot::SetPlotSetTitle(): unexpected error!");
364     return ((PyObject *)ret == Py_True);
365   }
366
367   PlotID CurvePlot::GetPlotSetID(PlotID curve_id)
368   {
369     PyLockWrapper lock;
370     PyObject * cont = GetInstance()->_impl->_controller;
371
372     PyObjWrapper ret(
373           PyObject_CallMethod(cont, (char *)"GetPlotSetID", (char *)"i", curve_id)
374           );
375     HandleAndPrintPyError("CurvePlot::GetPlotSetID(): unexpected error!");
376     return PyLong_AsLong(ret);
377   }
378
379   PlotID CurvePlot::GetPlotSetIDByName(std::string name)
380   {
381     PyLockWrapper lock;
382     PyObject * cont = GetInstance()->_impl->_controller;
383
384     PyObjWrapper ret(
385           PyObject_CallMethod(cont, (char *)"GetPlotSetIDByName", (char *)"O", strToPyUnicode(name))
386           );
387     HandleAndPrintPyError("CurvePlot::GetPlotSetIDByName(): unexpected error!");
388     return PyLong_AsLong(ret);
389   }
390
391   PlotID CurvePlot::GetCurrentCurveID()
392   {
393     PyLockWrapper lock;
394     PyObject * cont = GetInstance()->_impl->_controller;
395
396     PyObjWrapper ret(
397           PyObject_CallMethod(cont, (char *)"GetCurrentCurveID", (char *)"")
398           );
399     HandleAndPrintPyError("CurvePlot::GetCurrentCurveID(): unexpected error!");
400     return PyLong_AsLong(ret);
401   }
402
403   PlotID CurvePlot::GetCurrentPlotSetID()
404   {
405     PyLockWrapper lock;
406     PyObject * cont = GetInstance()->_impl->_controller;
407
408     PyObjWrapper ret(
409           PyObject_CallMethod(cont, (char *)"GetCurrentPlotSetID", (char *)"")
410           );
411     HandleAndPrintPyError("CurvePlot::GetCurrentPlotSetID(): unexpected error!");
412     return PyLong_AsLong(ret);
413   }
414
415   bool CurvePlot::IsValidPlotSetID(PlotID plot_set_id)
416   {
417     PyLockWrapper lock;
418     PyObject * cont = GetInstance()->_impl->_controller;
419
420     PyObjWrapper ret(
421           PyObject_CallMethod(cont, (char *)"IsValidPlotSetID", (char *)"i", plot_set_id));
422     HandleAndPrintPyError("CurvePlot::IsValidPlotSetID(): unexpected error!");
423     return ((PyObject *)ret == Py_True);
424   }
425
426   int CurvePlot::GetSalomeViewID(PlotID plot_set_id)
427   {
428     PyLockWrapper lock;
429     PyObject * cont = GetInstance()->_impl->_controller;
430
431     PyObjWrapper ret(
432           PyObject_CallMethod(cont, (char *)"GetSalomeViewID", (char *)"i", plot_set_id));
433     HandleAndPrintPyError("CurvePlot::GetSalomeViewID(): unexpected error!");
434     return PyLong_AsLong(ret);
435   }
436
437   void CurvePlot::OnSalomeViewTryClose(int salome_view_id)
438   {
439     PyLockWrapper lock;
440     PyObject * cont = GetInstance()->_impl->_controller;
441
442     PyObjWrapper ret(
443           PyObject_CallMethod(cont, (char *)"OnSalomeViewTryClose", (char *)"i", salome_view_id));
444     HandleAndPrintPyError("CurvePlot::OnSalomeViewTryClose(): unexpected error!");
445   }
446
447 }