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