Salome HOME
Introduce new syntax to openFiles() and saveFiles() Python callback functions, to...
[modules/gui.git] / src / SALOME_PYQT / SALOME_PYQT_GUILight / SALOME_PYQT_ModuleLight.cxx
1 // Copyright (C) 2007-2016  CEA/DEN, EDF R&D, OPEN CASCADE
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
20 // File   : SALOME_PYQT_ModuleLight.cxx
21 // Author : Vadim SANDLER, Open CASCADE S.A.S. (vadim.sandler@opencascade.com)
22
23 #include "SALOME_PYQT_DataModelLight.h"
24 #include "SALOME_PYQT_ModuleLight.h"
25 #include "SALOME_PYQT_PyModule.h"
26
27 #include "CAM_Application.h"
28 #include "SUITApp_init_python.hxx"
29 #include "SUIT_DataObjectIterator.h"
30 #include "LightApp_Application.h"
31 #include "SUIT_DataBrowser.h"
32 #include "sipAPISalomePyQtGUILight.h"
33
34 #ifndef GUI_DISABLE_CORBA
35 #include <Container_init_python.hxx>
36 #endif
37
38 #include <QCoreApplication>
39
40 // Py_ssize_t for old Pythons
41 // This code is as recommended by"
42 // http://www.python.org/dev/peps/pep-0353/#conversion-guidelines
43 //#if PY_VERSION_HEX < 0x02050000 && !defined(PY_SSIZE_T_MIN)
44 //typedef int Py_ssize_t;
45 //# define PY_SSIZE_T_MAX INT_MAX
46 //# define PY_SSIZE_T_MIN INT_MIN
47 //#endif
48
49 //
50 // NB: Python requests.
51 // General rule for Python requests created by Python-based GUI modules
52 // (SALOME_PYQT_ModuleLight and other ones):
53 // all requests should be executed SYNCHRONOUSLY within the main GUI thread.
54 // However, it is obligatory that ANY Python call is wrapped with a request object,
55 // so that ALL Python API calls are serialized with PyInterp_Dispatcher.
56 //
57 // NB: Library initialization
58 // Since the SalomePyQtGUILight library is not imported in Python it's initialization function
59 // should be called manually (and only once) in order to initialize global sip data
60 // and to get C API from sip : sipBuildResult for example
61 //
62
63 #define INIT_FUNCTION initSalomePyQtGUILight
64 #if defined(SIP_STATIC_MODULE)
65 extern "C" void INIT_FUNCTION();
66 #else
67 PyMODINIT_FUNC INIT_FUNCTION();
68 #endif
69
70 /*!
71   \fn CAM_Module* createModule()
72   \brief Module factory function.
73   \internal
74   
75   Creates an instance of SALOME_PYQT_Module object by request
76   of an application when the module is loaded and initialized.
77
78   \return new module object
79 */
80
81 extern "C" 
82 {
83   SALOME_PYQT_LIGHT_EXPORT CAM_Module* createModule() 
84   {
85     QCoreApplication* app = QCoreApplication::instance();
86     bool alreadyInitialized = app && app->property( "salome_pyqt_gui_light_initialized" ).toBool();
87
88     // make initialization only once (see comment above) !
89     if ( !alreadyInitialized ) {
90       PyLockWrapper lck; // GIL acquisition
91       INIT_FUNCTION();
92       if ( app ) app->setProperty( "salome_pyqt_gui_light_initialized", true );
93     }
94
95     return new SALOME_PYQT_ModuleLight();
96   }
97 }
98
99 /*!
100   \class SALOME_PYQT_ModuleLight
101   \brief This class implements GUI module for "light-weight" (no-CORBA-engine)
102   Python-based SALOME modules.
103 */
104
105 /*!
106   \brief Constructor
107 */
108 SALOME_PYQT_ModuleLight::SALOME_PYQT_ModuleLight()
109   : LightApp_Module( "noname" ) // name is set explicitly at the module initialization
110 {
111   // initialize helper
112   myHelper = new PyModuleHelper( this );
113 }
114
115 /*!
116   \brief Destructor
117 */
118 SALOME_PYQT_ModuleLight::~SALOME_PYQT_ModuleLight()
119 {
120   // as myHelper is a QObject, it should be deleted automatically
121 }
122
123 /*!
124   \brief Initialization of the module.
125   \param app parent application object
126   \sa PyModuleHelper::initialize()
127 */
128 void SALOME_PYQT_ModuleLight::initialize( CAM_Application* app )
129 {
130   // call base implementation
131   LightApp_Module::initialize( app );
132
133   // ... then call helper
134   myHelper->initialize( app );
135   SUIT_DataBrowser* ob = getApp()->objectBrowser();
136   if (ob && ob->model()) {
137     connect( ob->model(), SIGNAL( clicked( SUIT_DataObject*, int ) ),
138              myHelper, SLOT( onObjectBrowserClicked( SUIT_DataObject*, int ) ), Qt::UniqueConnection );
139   }
140 }
141
142 /*!
143   \brief Activation of the module.
144   \param study parent study
145   \return \c true if activation is successful and \c false otherwise
146   \sa PyModuleHelper::activate()
147 */
148 bool SALOME_PYQT_ModuleLight::activateModule( SUIT_Study* study )
149 {
150   // call base implementation and then helper
151   return LightApp_Module::activateModule( study ) && myHelper->activate( study );
152 }
153
154 /*!
155   \brief Deactivation of the module.
156   \param study parent study
157   \return \c true if deactivation is successful and \c false otherwise
158   \sa PyModuleHelper::deactivate()
159 */
160 bool SALOME_PYQT_ModuleLight::deactivateModule( SUIT_Study* study )
161 {  
162   // call helper
163   bool res = myHelper->deactivate( study );
164     
165   // ... then call base implementation
166   return LightApp_Module::deactivateModule( study ) && res;
167 }
168
169 /*!
170   \brief Close of the module.
171
172   This function is usually used in order to close the module's 
173   specific menus and toolbars and perform other such actions
174   required when the module is closed.
175 */
176 void SALOME_PYQT_ModuleLight::onModelClosed()
177 {
178   // call helper
179   myHelper->modelClosed(application()->activeStudy());
180   LightApp_Module::onModelClosed();
181 }
182
183
184 /*!
185   \brief Get the dockable windows associated with the module.
186   \param winMap output map of dockable windows in form { <window_type> : <dock_area> }
187   \sa PyModuleHelper::windows()
188 */
189 void SALOME_PYQT_ModuleLight::windows( QMap<int, int>& winMap ) const
190 {
191   // get list of dockable windows from helper
192   winMap = myHelper->windows();
193 }
194
195 /*!
196   \brief Define the compatible view windows associated with the module.
197   \param viewList output list of view windows types
198   \sa PyModuleHelper::viewManagers()
199 */
200 void SALOME_PYQT_ModuleLight::viewManagers( QStringList& viewList ) const
201 {
202   // get list of view types from helper
203   viewList = myHelper->viewManagers();
204 }
205
206 /*!
207   \brief Process study activation.
208   \sa PyModuleHelper::studyActivated()
209 */
210 void SALOME_PYQT_ModuleLight::studyActivated()
211 {
212   // call helper
213   myHelper->studyActivated( application()->activeStudy() );
214 }
215
216 /*!
217   \brief Process context popup menu request.
218   \param context popup menu context (e.g. "ObjectBrowser")
219   \param menu popup menu
220   \param title popup menu title (not used)
221   \sa PyModuleHelper::contextMenu()
222 */
223 void SALOME_PYQT_ModuleLight::contextMenuPopup( const QString& context, 
224                                                 QMenu*         menu, 
225                                                 QString&       /*title*/ )
226 {
227   // call helper
228   myHelper->contextMenu( context, menu );
229 }
230
231 /*!
232   \brief Export preferences for the Python module.
233   \sa PyModuleHelper::createPreferences()
234 */
235 void SALOME_PYQT_ModuleLight::createPreferences()
236 {
237   // call helper
238   myHelper->createPreferences();
239 }
240
241 /*!
242   \brief Process module's preferences changing.
243   \param section preference resources section
244   \param parameter preference resources parameter name
245   \sa PyModuleHelper::preferencesChanged()
246 */
247 void SALOME_PYQT_ModuleLight::preferencesChanged( const QString& section, const QString& parameter )
248 {
249   // call helper
250   myHelper->preferencesChanged( section, parameter );
251 }
252
253 /*!
254   \brief Save module data. Called when user saves study.
255   \param files output list of files where module stores data
256   \param url study URL
257   \sa PyModuleHelper::save()
258 */
259 void SALOME_PYQT_ModuleLight::save( QStringList& files, const QString& url )
260 {
261   // call helper
262   myHelper->save( files, url );
263 }
264
265 /*
266   \brief Load module data. Called when user opens study 
267   and activates module.
268   \param files list of files where module data is stored
269   \param url study URL
270   \return \c true if loading has been finished successfully or \c false otherwise
271   \sa PyModuleHelper::load()
272 */
273 bool SALOME_PYQT_ModuleLight::load( const QStringList& files, const QString& url )
274 {
275   // call helper
276   return myHelper->load( files, url );
277 }
278
279 /*!
280   \brief Dump module data to the Python script. 
281   Called when user activates dump study operation.
282   \param files output list of files where module stores python script
283   \sa PyModuleHelper::dumpPython()
284 */
285 void SALOME_PYQT_ModuleLight::dumpPython( QStringList& files )
286 {
287   // call helper
288   myHelper->dumpPython( files );
289 }
290
291 /*!
292   \brief Test if object \a what can be dragged by the user.
293   \param what data object being tested
294   \return \c true if object can be dragged or \c false otherwise
295   \sa PyModuleHelper::isDraggable()
296 */
297 bool SALOME_PYQT_ModuleLight::isDraggable( const SUIT_DataObject* what ) const
298 {
299   // call helper
300   return myHelper->isDraggable( what );
301 }
302
303 /*!
304   \brief Test if drop operation can be done on the \a where object.
305   \param where data object being tested
306   \return \c true if if drop operation is supported by object or \c false otherwise
307   \sa PyModuleHelper::isDropAccepted()
308 */
309 bool SALOME_PYQT_ModuleLight::isDropAccepted( const SUIT_DataObject* where ) const
310 {
311   // call helper
312   return myHelper->isDropAccepted( where );
313 }
314
315 /*!
316   \brief Perform drop operation
317   \param what list of data objects being dropped
318   \param where target data object for drop operation
319   \param row line (child item index) where drop operation is performed to
320   \param action current drop action (copy or move)
321   \sa PyModuleHelper::dropObjects()
322 */
323 void SALOME_PYQT_ModuleLight::dropObjects( const DataObjectList& what, SUIT_DataObject* where,
324                                            const int row, Qt::DropAction action )
325 {
326   // call helper
327   myHelper->dropObjects( what, where, row, action );
328 }
329
330 /*!
331   \brief Create new empty data object
332   \param parent entry of parent data object
333   \return entry of created data object
334 */
335 QString SALOME_PYQT_ModuleLight::createObject( const QString& parent )
336 {
337   QString entry;
338   SALOME_PYQT_DataObjectLight* obj = 0;
339
340   if ( !parent.isEmpty() ) {
341     SALOME_PYQT_DataObjectLight* parentObj = findObject( parent );
342     if ( parentObj )
343       obj = new SALOME_PYQT_DataObjectLight( parentObj );
344   }
345   else {
346     SALOME_PYQT_DataModelLight* dm =
347       dynamic_cast<SALOME_PYQT_DataModelLight*>( dataModel() );
348     if ( dm )
349       obj = new SALOME_PYQT_DataObjectLight( dm->getRoot() );
350   }  
351   if ( obj )
352     entry = obj->entry();
353   return entry;
354 }
355
356 /*!
357  \brief Create new data object with specified name, icon and tooltip
358  \param name data object name
359  \param icon data object icon
360  \param toolTip data object tooltip
361  \param parent entry of parent data object
362  \return entry of created data object
363 */
364 QString SALOME_PYQT_ModuleLight::createObject( const QString& name,
365                                                const QString& icon,
366                                                const QString& toolTip,
367                                                const QString& parent )
368 {
369   QString entry = createObject( parent );
370   SALOME_PYQT_DataObjectLight* obj = findObject( entry );
371   if ( obj ) {
372     obj->setName( name);
373     obj->setToolTip( toolTip );
374     obj->setIcon( icon );
375   }
376   return entry;
377 }
378
379 /*!
380   \brief Set data object name
381   \param entry data object entry
382   \param name data object name
383 */
384 void SALOME_PYQT_ModuleLight::setName( const QString& entry, const QString& name )
385 {
386   SALOME_PYQT_DataObjectLight* dataObj = findObject( entry );
387   if ( dataObj )
388     dataObj->setName( name );
389 }
390
391 /*!
392   \brief Get data object name
393   \param entry data object entry
394   \return data object name
395 */
396 QString SALOME_PYQT_ModuleLight::getName( const QString& entry ) const
397 {
398   QString name;
399   SALOME_PYQT_DataObjectLight* dataObj = findObject( entry );
400   if ( dataObj )
401     name = dataObj->name();
402   return name;
403 }
404
405 /*!
406   \brief Set data object icon
407   \param entry data object entry
408   \param icon data object icon file name (icon is loaded from module resources)
409 */
410 void SALOME_PYQT_ModuleLight::setIcon( const QString& entry, const QString& icon )
411 {
412   SALOME_PYQT_DataObjectLight* dataObj = findObject( entry );
413   if ( dataObj )
414     dataObj->setIcon( icon );
415 }
416
417 /*!
418   \brief Set data object tooltip
419   \param entry data object entry
420   \param toolTip data object tooltip
421 */
422 void SALOME_PYQT_ModuleLight::setToolTip( const QString& entry, const QString& toolTip )
423 {
424   SALOME_PYQT_DataObjectLight* dataObj = findObject( entry );
425   if ( dataObj )
426     dataObj->setToolTip( toolTip );
427 }
428
429 /*!
430   \brief Get data object tooltip
431   \param entry data object entry
432   \return data object tooltip
433 */
434 QString SALOME_PYQT_ModuleLight::getToolTip( const QString& entry ) const
435 {
436   QString toolTip;
437   SALOME_PYQT_DataObjectLight* dataObj = findObject( entry );
438   if ( dataObj )
439     toolTip = dataObj->toolTip();
440   return toolTip;
441 }
442
443 /*!
444   \brief Set data object color
445   \param entry data object entry
446   \param color data object color
447  */
448 void SALOME_PYQT_ModuleLight::setColor( const QString& entry, const QColor& color )
449 {
450   SALOME_PYQT_DataObjectLight* dataObj = findObject( entry );
451   if ( dataObj )
452     dataObj->setColor( color );
453 }
454
455 /*!
456   \brief Get data object color
457   \param entry data object entry
458   \return data object color
459 */
460 QColor SALOME_PYQT_ModuleLight::getColor( const QString& entry ) const
461 {
462   QColor color;
463   SALOME_PYQT_DataObjectLight* dataObj = findObject( entry );
464   if ( dataObj )
465     color = dataObj->color( SUIT_DataObject::Foreground );
466   return color;
467 }
468
469 void SALOME_PYQT_ModuleLight::setObjectPosition( const QString& theEntry, int thePos )
470 {
471   SALOME_PYQT_DataObjectLight* dataObj = findObject( theEntry );
472   if ( dataObj )
473     dataObj->setPosition(thePos);
474 }
475
476 int SALOME_PYQT_ModuleLight::getObjectPosition( const QString& theEntry )
477 {
478   SALOME_PYQT_DataObjectLight* dataObj = findObject( theEntry );
479   if ( dataObj )
480     return dataObj->position();
481   return -1;
482 }
483
484
485 /*!
486   \brief Set reference to another data object
487   \param entry data object entry
488   \param refEntry referenced data object entry
489 */
490 void SALOME_PYQT_ModuleLight::setReference( const QString& entry, const QString& refEntry )
491 {
492   SALOME_PYQT_DataObjectLight* dataObj = findObject( entry );
493   if ( dataObj )
494     dataObj->setRefEntry( refEntry );
495 }
496
497 /*
498   \brief Get entry of the referenced object (if there's any)
499   \param entry data object entry
500   \return referenced data object entry
501 */
502 QString SALOME_PYQT_ModuleLight::getReference( const QString& entry ) const
503 {
504   QString refEntry;
505   SALOME_PYQT_DataObjectLight* dataObj = findObject( entry );
506   if ( dataObj )
507     refEntry = dataObj->refEntry();
508   return refEntry;
509 }
510
511 /*!
512   \brief Remove object by entry
513   \param entry data object entry
514 */
515 void SALOME_PYQT_ModuleLight::removeObject( const QString& entry )
516 {
517   SALOME_PYQT_DataObjectLight* dataObj = findObject( entry );
518   if ( dataObj && dataObj->parent() )
519     dataObj->parent()->removeChild( dataObj );
520 }
521
522 /*!
523   \brief Remove all child data objects from specified data object
524   \param entry data object entry
525 */
526 void SALOME_PYQT_ModuleLight::removeChildren( const QString& entry )
527 {
528   SUIT_DataObject* dataObj = 0;
529   if ( !entry.isEmpty() ) {
530     dataObj = findObject( entry );
531   }
532   else {
533     SALOME_PYQT_DataModelLight* dm =
534       dynamic_cast<SALOME_PYQT_DataModelLight*>( dataModel() );
535     if ( dm ) dataObj = dm->getRoot();
536   }
537   if ( dataObj ) {
538     DataObjectList children;
539     dataObj->children( children );
540     QListIterator<SUIT_DataObject*> it( children );
541     while ( it.hasNext() ) {
542       dataObj->removeChild( it.next() );
543     }
544   }
545 }
546
547 /*!
548   \brief Get entries of all child data objects of specified data object
549   \param entry data object entry
550   \param recursive \c true for recursive processing
551 */
552 QStringList SALOME_PYQT_ModuleLight::getChildren( const QString& entry, const bool recursive ) const
553 {
554   QStringList entryList;
555   SUIT_DataObject* dataObj = 0;
556   if ( !entry.isEmpty() ) {
557     dataObj = findObject( entry );
558   }
559   else {
560     SALOME_PYQT_DataModelLight* dm =
561       dynamic_cast<SALOME_PYQT_DataModelLight*>( dataModel() );
562     if ( dm ) dataObj = dm->getRoot();
563   }
564   if ( dataObj ) {
565     DataObjectList lst;
566     dataObj->children( lst, recursive );
567     QListIterator<SUIT_DataObject*> it( lst );
568     while ( it.hasNext() ) {
569       SALOME_PYQT_DataObjectLight* sobj = dynamic_cast<SALOME_PYQT_DataObjectLight*>( it.next() );
570       entryList.append( sobj->entry() );
571     }
572   }
573   return entryList;
574 }
575
576 /*!
577   \brief Create new instance of data model and return it.
578 */
579 CAM_DataModel* SALOME_PYQT_ModuleLight::createDataModel()
580 {
581   return new SALOME_PYQT_DataModelLight( this );
582 }
583
584 /*!
585   \brief Find data object by its entry
586   \param entry data object entry
587   \return data object with given entry or 0 if object isn't found
588 */
589 SALOME_PYQT_DataObjectLight* SALOME_PYQT_ModuleLight::findObject( const QString& entry ) const
590 {
591   SALOME_PYQT_DataObjectLight* obj = 0;
592   SALOME_PYQT_DataModelLight* dm =
593     dynamic_cast<SALOME_PYQT_DataModelLight*>( dataModel() );
594   if ( !entry.isEmpty() && dm ) {
595     for ( SUIT_DataObjectIterator it( dm->getRoot(), SUIT_DataObjectIterator::DepthLeft ); it.current() && !obj; ++it ) { 
596       SALOME_PYQT_DataObjectLight* curentobj =
597         dynamic_cast<SALOME_PYQT_DataObjectLight*>( it.current() );
598       if ( curentobj && curentobj->entry() == entry )
599         obj = curentobj;
600     }
601   }
602   return obj;
603 }