Salome HOME
Merge branch 'OCCT780'
[modules/gui.git] / src / SALOME_PY / SalomePy.cxx
1 // Copyright (C) 2007-2024  CEA, EDF, OPEN CASCADE
2 //
3 // Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
4 // CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
5 //
6 // This library is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU Lesser General Public
8 // License as published by the Free Software Foundation; either
9 // version 2.1 of the License, or (at your option) any later version.
10 //
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 // Lesser General Public License for more details.
15 //
16 // You should have received a copy of the GNU Lesser General Public
17 // License along with this library; if not, write to the Free Software
18 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
19 //
20 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
21 //
22
23 //  SALOME SALOME_PY : binding of VTK graphics and Python
24 //  File   : SalomePy.cxx
25 //  Author : Paul RASCLE, EDF
26 //
27 #ifdef WIN32
28 // E.A. : On windows with python 2.6, there is a conflict
29 // E.A. : between pymath.h and Standard_math.h which define
30 // E.A. : some same symbols : acosh, asinh, ...
31 #include <Standard_Real.hxx>
32 #include <Python.h>
33 #include <pymath.h>
34 #endif
35
36 #include <vtkPythonUtil.h>
37
38 #include <vtkVersion.h>
39 #include <vtkRenderer.h>
40 #include <vtkRenderWindow.h>
41 #include <vtkRenderWindowInteractor.h>
42
43 #include <SALOME_Event.h>
44
45 #include <SUIT_Session.h>
46 #include <LightApp_Application.h>
47 #include <LightApp_Study.h>
48
49 #include <SVTK_ViewManager.h>
50 #include <SVTK_ViewWindow.h>
51
52 #define VTK_XVERSION (VTK_MAJOR_VERSION*10000+VTK_MINOR_VERSION*100+VTK_BUILD_VERSION)
53
54 /*!
55   \brief Python wrappings for VTK viewer of the SALOME desktop.
56
57   All methods are implemented using Event mechanism. The module
58   provides the following functions:
59   - getRenderer()
60   - getRenderWindow()
61   - getRenderWindowInteractor()
62   - showTrihedron()
63   - fitAll()
64   - setView()
65   - resetView()
66
67   Usage in Python:
68   \code
69   import SalomePy
70   renderer = SalomePy.getRenderer()     # get VTK renderer
71   window   = SalomePy.getRenderWindow() # get render window
72   \endcode
73
74   The methods getRenderer(), getRenderWindow() and getRenderWindowInteractor()
75   open new VTK viewer if there is no one opened.
76   In case of any error these methods return None object to the Python.
77 */
78
79 #ifdef WIN32
80  #if defined SALOMEPY_EXPORTS || defined SalomePy_EXPORTS
81   #define SALOMEPY_EXPORT __declspec( dllexport )
82  #else
83   #define SALOMEPY_EXPORT __declspec( dllimport )
84  #endif
85 #else
86  #define SALOMEPY_EXPORT
87 #endif
88
89
90 #define PUBLISH_ENUM(i)                              \
91 {                                                    \
92   PyObject *w;                                       \
93   int rc;                                            \
94   if ( ( w = PyLong_FromLong( i ) ) == NULL ) return NULL; \
95   rc = PyDict_SetItemString( aModuleDict, #i, w );   \
96   Py_DECREF( w );                                    \
97   if ( rc < 0 ) return NULL;                              \
98 }
99
100 //! View operation type
101 enum {
102   ViewFront,     //!< front view
103   ViewBack,      //!< back view
104   ViewTop,       //!< top view
105   ViewBottom,    //!< bottom view
106   ViewRight,     //!< right view
107   ViewLeft       //!< left view
108 };
109
110 /*!
111   \brief Get Python class object by name
112   \internal
113   \param theClassName Python class name
114   \return Python class object or None object if class is not found
115 */
116 static PyTypeObject* GetPyClass( const char* theClassName )
117 {
118   static PyObject* aVTKModule = 0;
119   PyObject* aPyClass = 0;
120   if( !aVTKModule ) {
121     aVTKModule = PyImport_ImportModule( "vtk.vtkRenderingCore" ); 
122     if( PyErr_Occurred() ) {
123       PyErr_Print();
124     }
125   }
126   if ( aVTKModule ) {
127     PyObject* aVTKDict = PyModule_GetDict( aVTKModule );
128     aPyClass = PyDict_GetItemString(aVTKDict, const_cast<char*>( theClassName ) );
129     if (!PyType_Check(aPyClass))
130       return 0;
131   }
132   return (PyTypeObject *)aPyClass;
133 }
134
135 /*!
136   \brief VTK window find/create mode
137   \internal
138 */
139 enum { 
140   __Find,          // try to find VTK window; if not found, do nothing
141   __FindOrCreate,  // try to find VTK window; if not found, create new one
142   __Create };      // create new VTK window
143
144 /*!
145   \brief Find or create VTK window.
146   \internal
147   \param toCreate window find/create mode
148   \return VTK window pointer or 0 if it could not be found/created
149 */
150 static SVTK_ViewWindow* GetVTKViewWindow( int toCreate = __FindOrCreate, int toKeepDetached = 0 ) {
151   SVTK_ViewWindow* aVW = 0;
152   if ( SUIT_Session::session() ) {
153     // get application
154     LightApp_Application* anApp = dynamic_cast<LightApp_Application*>( SUIT_Session::session()->activeApplication() );
155     if ( anApp ) {
156       // get active study
157       LightApp_Study* aStudy = dynamic_cast<LightApp_Study*>( anApp->activeStudy() );
158       if ( aStudy ) {
159         // find or create VTK view manager
160         if ( toCreate == __Create ) {
161           SVTK_ViewManager* aVM = dynamic_cast<SVTK_ViewManager*>( anApp->createViewManager( "VTKViewer" ) );
162           if ( aVM ) {
163             aVW = dynamic_cast<SVTK_ViewWindow*>( aVM->getActiveView() );
164             if ( !aVW )
165               aVW = dynamic_cast<SVTK_ViewWindow*>( aVM->createViewWindow() );
166             // VSR : When new view window is created it can be not active yet at this moment,
167             // so the following is a some workaround
168             if ( !aVW && !aVM->getViews().isEmpty() )
169               aVW = dynamic_cast<SVTK_ViewWindow*>( aVM->getViews()[0] );
170           }
171         }
172         else {
173           anApp->setProperty("keep_detached", toKeepDetached != 0 );
174           SVTK_ViewManager* aVM = dynamic_cast<SVTK_ViewManager*>( anApp->getViewManager( "VTKViewer", toCreate == __FindOrCreate ) );
175           anApp->setProperty("keep_detached", QVariant());
176           if ( aVM ) {
177             aVW = dynamic_cast<SVTK_ViewWindow*>( aVM->getActiveView() );
178             // VSR : When new view window is created it can be not active yet at this moment,
179             // so the following is a some workaround
180             if ( !aVW && !aVM->getViews().isEmpty() )
181               aVW = dynamic_cast<SVTK_ViewWindow*>( aVM->getViews()[0] );
182           }
183         }
184       }
185     }
186   }
187   return aVW;
188 }
189
190 /*!
191   \fn PyObject* getRenderer( int toCreate = 0 );
192   \brief Get VTK renderer (vtkRenderer).
193   
194   If \a toCreate parameter is 0 (by default) the function tries to find
195   and reuse existing VTK window; if it is not found, the new VTK window
196   is opened.
197
198   If \a toCreate parameter is non-zero, the function always creates
199   new VTK window.
200
201   If VTK window could not be found and or created, the None Python object
202   is returned.
203
204   \param toCreate window creation mode
205   \return VTK window renderer object
206 */
207
208 class SALOMEPY_EXPORT TGetRendererEvent: public SALOME_Event
209 {
210 public:
211   typedef PyObject* TResult;
212   TResult myResult;
213   int     myCreate;
214   int     myKeepDetached;
215   TGetRendererEvent( bool toCreate, bool toKeepDetached )
216     : myResult( Py_None ), myCreate( toCreate ), myKeepDetached( toKeepDetached ) {}
217   virtual void Execute()
218   {
219     PyTypeObject* aPyClass = ::GetPyClass( "vtkRenderer" );
220     SVTK_ViewWindow* aVTKViewWindow = 
221       ::GetVTKViewWindow( myCreate ? __Create : __FindOrCreate, myKeepDetached );
222     if( aVTKViewWindow && aPyClass ) {
223       vtkRenderer* aVTKObject = aVTKViewWindow->getRenderer();
224       myResult = PyVTKObject_FromPointer( aPyClass, NULL, aVTKObject );
225     }
226   }
227 };
228
229 extern "C" SALOMEPY_EXPORT PyObject* libSalomePy_getRenderer( PyObject* /*self*/, PyObject* args )
230 {
231   PyObject* aResult = Py_None;
232   int toCreate = 0;
233   int toKeepDetached = 0;
234   if ( !PyArg_ParseTuple( args, "|ii:getRenderer", &toCreate, &toKeepDetached ) )
235     PyErr_Print();
236   else
237     aResult = ProcessEvent( new TGetRendererEvent( toCreate, toKeepDetached ) );
238   return aResult;
239 }
240
241 /*!
242   \fn PyObject* getRenderWindow( int toCreate = 0 );
243   \brief Get VTK render window (vtkRenderWindow).
244   
245   If \a toCreate parameter is 0 (by default) the function tries to find 
246   and reuse existing VTK window; if it is not found, the new VTK window
247   is opened.
248
249   If \a toCreate parameter is non-zero, the function always creates
250   new VTK window.
251
252   If VTK window could not be found and or created, the None Python object
253   is returned.
254
255   \param toCreate window creation mode
256   \return VTK window render window object
257 */
258
259 class SALOMEPY_EXPORT TGetRenderWindowEvent: public SALOME_Event
260 {
261 public:
262   typedef PyObject* TResult;
263   TResult myResult;
264   int     myCreate;
265   int     myKeepDetached;
266   TGetRenderWindowEvent( bool toCreate, bool toKeepDetached )
267     : myResult( Py_None ), myCreate( toCreate ), myKeepDetached( toKeepDetached ) {}
268   virtual void Execute()
269   {
270     PyTypeObject* aPyClass = ::GetPyClass( "vtkRenderWindow" );
271     SVTK_ViewWindow* aVTKViewWindow = 
272       ::GetVTKViewWindow( myCreate ? __Create : __FindOrCreate, myKeepDetached );
273     if( aVTKViewWindow && aPyClass ) {
274       vtkRenderWindow* aVTKObject = aVTKViewWindow->getRenderWindow();
275       myResult = PyVTKObject_FromPointer( aPyClass, NULL, aVTKObject );
276     }
277   }
278 };
279
280 extern "C" SALOMEPY_EXPORT PyObject* libSalomePy_getRenderWindow( PyObject* /*self*/, PyObject* args )
281 {
282   PyObject* aResult = Py_None;
283   int toCreate = 0;
284   int toKeepDetached = 0;
285   if ( !PyArg_ParseTuple( args, "|ii:getRenderWindow", &toCreate, &toKeepDetached ) )
286     PyErr_Print();
287   else
288     aResult = ProcessEvent( new TGetRenderWindowEvent( toCreate, toKeepDetached ) );
289   return aResult;
290 }
291
292 /*!
293   \fn PyObject* getRenderWindowInteractor( int toCreate = 0 );
294   \brief Get VTK render window interactor (getRenderWindowInteractor).
295   
296   If \a toCreate parameter is 0 (by default) the function tries to find 
297   and reuse existing VTK window; if it is not found, the new VTK window
298   is opened.
299
300   If \a toCreate parameter is non-zero, the function always creates
301   new VTK window.
302
303   If VTK window could not be found and or created, the None Python object
304   is returned.
305
306   \param toCreate window creation mode
307   \return VTK window render window interactor object
308 */
309
310 class SALOMEPY_EXPORT TGetRenderWindowInteractorEvent: public SALOME_Event
311 {
312 public:
313   typedef PyObject* TResult;
314   TResult myResult;
315   int     myCreate;
316   int     myKeepDetached;
317   TGetRenderWindowInteractorEvent( bool toCreate, bool toKeepDetached )
318     : myResult( Py_None ), myCreate( toCreate ), myKeepDetached( toKeepDetached ) {}
319   virtual void Execute()
320   {
321     PyTypeObject* aPyClass = ::GetPyClass( "vtkRenderWindowInteractor" );
322     SVTK_ViewWindow* aVTKViewWindow = 
323       ::GetVTKViewWindow( myCreate ? __Create : __FindOrCreate, myKeepDetached );
324     if( aVTKViewWindow && aPyClass ) {
325       vtkRenderWindowInteractor* aVTKObject = aVTKViewWindow->getInteractor();
326       myResult = PyVTKObject_FromPointer( aPyClass, NULL, aVTKObject );
327     }
328   }
329 };
330
331 extern "C" SALOMEPY_EXPORT PyObject* libSalomePy_getRenderWindowInteractor( PyObject* /*self*/, PyObject* args )
332 {
333   PyObject* aResult = Py_None;
334   int toCreate = 0;
335   int toKeepDetached = 0;
336   if ( !PyArg_ParseTuple( args, "|ii:getRenderWindowInteractor", &toCreate, &toKeepDetached ) )
337     PyErr_Print();
338   else
339     aResult = ProcessEvent( new TGetRenderWindowInteractorEvent( toCreate, toKeepDetached ) );
340   return aResult;
341 }
342
343 /*!
344   \fn PyObject* showTrihedron( int show );
345   \brief Show/hide trihedron in the current VTK viewer.
346
347   If there is no active VTK viewer, nothing happens.
348   
349   \param show new trihedron visibility state
350   \return nothing (Py_None)
351 */
352
353 extern "C" SALOMEPY_EXPORT PyObject* libSalomePy_showTrihedron( PyObject* /*self*/, PyObject* args )
354 {
355   class TEvent: public SALOME_Event
356   {
357   public:
358     int myShow;
359     TEvent( int bShow )
360       : myShow( bShow ) {}
361     virtual void Execute()
362     {
363       if( SVTK_ViewWindow* aVTKViewWindow = GetVTKViewWindow( __Find ) ) {
364         if ( aVTKViewWindow->isTrihedronDisplayed() != (bool)myShow )
365           aVTKViewWindow->onViewTrihedron(myShow);
366       }
367     }
368   };
369   
370   PyObject* aResult = Py_None;
371   int bShow = 0;
372   if ( !PyArg_ParseTuple( args, "i:showTrihedron", &bShow ) )
373     PyErr_Print();
374   else
375     ProcessVoidEvent( new TEvent( bShow ) );
376   return aResult;
377 }
378
379 /*!
380   \fn PyObject* fitAll();
381   \brief Fit all the contents in the current VTK viewer.
382
383   If there is no active VTK viewer, nothing happens.
384
385   \return nothing (Py_None)
386 */
387
388 extern "C" SALOMEPY_EXPORT PyObject* libSalomePy_fitAll( PyObject* /*self*/, PyObject* /*args*/ )
389 {
390   class TEvent: public SALOME_Event
391   {
392   public:
393     TEvent() {}
394     virtual void Execute()
395     {
396       if( SVTK_ViewWindow* aVTKViewWindow = GetVTKViewWindow( __Find ) ) {
397         aVTKViewWindow->onFitAll();
398       }
399     }
400   };
401   
402   ProcessVoidEvent( new TEvent() );
403   return Py_None;
404 }
405
406 /*!
407   \fn PyObject* setView( int type );
408   \brief Set view type for the current VTK viewer.
409
410   If there is no active VTK viewer, nothing happens.
411   
412   \param type view type
413   \return nothing (Py_None)
414 */
415
416 extern "C" SALOMEPY_EXPORT PyObject* libSalomePy_setView( PyObject* /*self*/, PyObject* args )
417 {
418   class TEvent: public SALOME_Event
419   {
420   public:
421     long myType;
422     TEvent( long type ) : myType( type) {}
423     virtual void Execute()
424     {
425       if( SVTK_ViewWindow* aVTKViewWindow = GetVTKViewWindow( __Find ) ) {
426         switch( myType ) {
427         case ViewFront:
428           aVTKViewWindow->onFrontView();  break;
429         case ViewBack:
430           aVTKViewWindow->onBackView();   break;
431         case ViewTop:
432           aVTKViewWindow->onTopView();    break;
433         case ViewBottom:
434           aVTKViewWindow->onBottomView(); break;
435         case ViewRight:
436           aVTKViewWindow->onRightView();  break;
437         case ViewLeft:
438           aVTKViewWindow->onLeftView();   break;
439         default:
440           PyErr_Format(PyExc_ValueError,"setView: wrong parameter value; must be between %d and %d", ViewFront, ViewLeft );
441           break;
442         }
443       }
444     }
445   };
446   
447   long type = -1;
448   if ( !PyArg_ParseTuple( args, "l:setView", &type ) )
449     PyErr_Print();
450   else {
451     ProcessVoidEvent( new TEvent( type ) );
452     if( PyErr_Occurred() )
453       PyErr_Print();
454   }
455   return Py_None;
456 }
457
458 /*!
459   \fn PyObject* resetView();
460   \brief Reset contents of the current VTK viewer.
461
462   If there is no active VTK viewer, nothing happens.
463   
464   \return nothing (Py_None)
465 */
466
467 extern "C" SALOMEPY_EXPORT PyObject* libSalomePy_resetView( PyObject* /*self*/, PyObject* /*args*/ )
468 {
469   class TEvent: public SALOME_Event
470   {
471   public:
472     TEvent() {}
473     virtual void Execute()
474     {
475       if( SVTK_ViewWindow* aVTKViewWindow = GetVTKViewWindow( __Find ) ) {
476         aVTKViewWindow->onResetView();
477       }
478     }
479   };
480   
481   ProcessVoidEvent( new TEvent() );
482   return Py_None;
483 }
484
485 static PyMethodDef libSalomePy_methods[] = 
486 {
487   { "getRenderer",               libSalomePy_getRenderer,               METH_VARARGS, PyDoc_STR("Get renderer from current vtk view") },
488   { "getRenderWindow",           libSalomePy_getRenderWindow,           METH_VARARGS, PyDoc_STR("Get render window from current vtk view") },
489   { "getRenderWindowInteractor", libSalomePy_getRenderWindowInteractor, METH_VARARGS, PyDoc_STR("Get interactor from current vtk view") },
490   { "showTrihedron",             libSalomePy_showTrihedron,             METH_VARARGS, PyDoc_STR("Show/hide trihedron in current vtk view") },
491   { "fitAll",                    libSalomePy_fitAll,                    METH_NOARGS,  PyDoc_STR("Fit current vtk view to show all contents")  },
492   { "setView",                   libSalomePy_setView,                   METH_VARARGS, PyDoc_STR("Set side view for the current VTK viewer") },
493   { "resetView",                 libSalomePy_resetView,                 METH_NOARGS,  PyDoc_STR("Reset camera for current vtk view")  },
494   { 0, 0, 0, 0 }
495 };
496
497 struct module_state {
498     PyObject *error;
499 };
500
501 #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
502
503 static int libSalomePy_traverse(PyObject *m, visitproc visit, void *arg) {
504     Py_VISIT(GETSTATE(m)->error);
505     return 0;
506 }
507
508 static int libSalomePy_clear(PyObject *m) {
509     Py_CLEAR(GETSTATE(m)->error);
510     return 0;
511 }
512
513 static struct PyModuleDef moduledef = {
514         PyModuleDef_HEAD_INIT,
515         "libSalomePy",
516         NULL,
517         sizeof(struct module_state),
518         libSalomePy_methods,
519         NULL,
520         libSalomePy_traverse,
521         libSalomePy_clear,
522         NULL
523 };
524
525 /*!
526   \brief Python module initialization.
527   \internal
528 */
529 extern "C" SALOMEPY_EXPORT PyMODINIT_FUNC PyInit_libSalomePy(void)
530 {
531   // init module
532   PyObject *aModule = PyModule_Create(&moduledef);
533   if( PyErr_Occurred() ) {
534     PyErr_Print();
535     return NULL;
536   }
537
538   // get module's dictionary
539   PyObject *aModuleDict = PyModule_GetDict( aModule );
540   if ( aModuleDict == NULL )
541     return NULL;
542
543   // export View type enumeration
544   PUBLISH_ENUM( ViewFront );
545   PUBLISH_ENUM( ViewBack );
546   PUBLISH_ENUM( ViewTop );
547   PUBLISH_ENUM( ViewBottom );
548   PUBLISH_ENUM( ViewRight );
549   PUBLISH_ENUM( ViewLeft );
550
551   return aModule;
552 }