Salome HOME
GUI callback: add MEDPresentation
[modules/med.git] / src / MEDCalc / gui / WorkspaceController.cxx
1 // Copyright (C) 2007-2015  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
20 // Author : Guillaume Boulant (EDF)
21
22 #include "WorkspaceController.hxx"
23 #include "QtHelper.hxx"
24 #include "MEDFactoryClient.hxx"
25 #include "XmedDataModel.hxx"
26 #include "DlgAlias.hxx"
27
28 #include <SALOMEconfig.h>
29 #include CORBA_CLIENT_HEADER(MEDEventListener)
30
31 #include <SalomeApp_Application.h>
32 #include <SALOME_LifeCycleCORBA.hxx>
33 #include <SUIT_FileDlg.h>
34 #include <SUIT_Desktop.h>
35
36 /*!
37  * This class defines a DockWidget plugged in the SALOME application,
38  * and containing a tree view for rendering a hierarchical data
39  * model. This datamodel contains the objects used in the workspace.
40  */
41 WorkspaceController::WorkspaceController(StandardApp_Module * salomeModule)
42   : TreeGuiManager(salomeModule->getApp(), "Workspace")
43 {
44   _salomeModule = salomeModule;
45   getDockWidgets()->getDockWidget()->setObjectName("medWorkspaceDock");
46
47   this->tabifyDockWidgets(false);
48
49   // -------------------------------------------------------------
50   // Setup the MEDEventListener to manage notification from the
51   // python console.
52
53   // We create a MEDEventListener CORBA object inside this GUI class
54   // with the role of listening events coming from the python console
55   // (or even the components if needed). The events arising in the
56   // python console are send as CORBA request to this CORBA
57   // servant. Then this object can process the event by notifying the
58   // GUI of something to update for example (using signals and slots
59   // of course).
60   _medEventListener = MEDEventListener_i::getInstance();
61   MEDCALC::MEDEventListener_ptr medEventListenerServant = _medEventListener->_this();
62
63   // We store the IOR inside the MEDDataManager to share this data
64   // with other parts of the application, in particular the python
65   // console that could retrieve this IOR using the
66   // getEventListenerIOR() function of the MEDDataManager.
67   SalomeApp_Application * salomeApp = salomeModule->getApp();
68   const char * medEventListenerIOR =
69     salomeApp->orb()->object_to_string(medEventListenerServant);
70   MEDFactoryClient::getDataManager()->setEventListenerIOR(medEventListenerIOR);
71
72   // Connect the signals emitted from the MEDEventListener to slot of
73   // this class.
74   connect(_medEventListener, SIGNAL(medEventSignal(const MEDCALC::MedEvent*)),
75     this, SLOT(processMedEvent(const MEDCALC::MedEvent*)));
76   // >>> WARN:
77   // Note that this class must be mocked (Q_OBJECT + moc file
78   // generated from header file) so that to be able to connect a
79   // signal to a slot of this class.
80
81   // -------------------------------------------------------------
82   // Customize the treeview rendering the datamodel with specific
83   // action for the popup menu
84   this->getDataTreeView()->clearActions();
85   _actionIds.display    = this->getDataTreeView()->addAction(tr("VISUALIZE_SCALAR_MAP"));
86   _actionIds.useInTui   = this->getDataTreeView()->addAction(tr("USE_IN_CONSOLE"));
87   _actionIds.exportToPv = this->getDataTreeView()->addAction(tr("EXPORT_TO_PARAVIS"));
88   _actionIds.save       = this->getDataTreeView()->addAction(tr("SAVE_AS_MED"));
89   _actionIds.remove     = this->getDataTreeView()->addAction(tr("REMOVE_FROM_WORKSPACE"));
90
91   // -------------------------------------------------------------
92   // Initialize the python console. Note that this must be done at
93   // last because the setup will try to initiate a connection to the
94   // event listener.
95   _consoleDriver = new XmedConsoleDriver(salomeApp);
96   _consoleDriver->setup();
97 }
98
99 WorkspaceController::~WorkspaceController() {
100   MEDEventListener_i::release();
101 }
102
103 /**
104  * This creates the GUI actions for driving the Workspace. The
105  * WorkspaceController creates itself this actions and implements the
106  * connected slots.
107  */
108 void WorkspaceController::createActions() {
109
110   QString label   = tr("LAB_SAVE_WORKSPACE");
111   QString tooltip = tr("TIP_SAVE_WORKSPACE");
112   QString icon    = tr("ICO_WORKSPACE_SAVE");
113   int actionId = _salomeModule->createStandardAction(label,this,SLOT(OnSaveWorkspace()),icon,tooltip);
114   _salomeModule->addActionInToolbar(actionId);
115
116   label   = tr("LAB_CLEAN_WORKSPACE");
117   tooltip = tr("TIP_CLEAN_WORKSPACE");
118   icon    = tr("ICO_WORKSPACE_CLEAN");
119   actionId = _salomeModule->createStandardAction(label,this,SLOT(OnCleanWorkspace()),icon,tooltip);
120   _salomeModule->addActionInToolbar(actionId);
121 }
122
123 /*!
124  * Implementation of the slot processItemList inherited from TreeGuiManager
125  */
126 void WorkspaceController::processItemList(QStringList itemNameIdList, int actionId) {
127   if ( actionId == _actionIds.display ) {
128     STDLOG("WorkspaceController::processItemList: display");
129     this->_viewItemList(itemNameIdList);
130   }
131   else if ( actionId == _actionIds.useInTui ) {
132     STDLOG("WorkspaceController::processItemList: use");
133     this->_importItemList(itemNameIdList);
134   }
135   else if ( actionId == _actionIds.exportToPv ) {
136     STDLOG("WorkspaceController::processItemList: export");
137     this->_exportItemList(itemNameIdList);
138   }
139   else if ( actionId == _actionIds.save ) {
140     STDLOG("WorkspaceController::processItemList: save");
141     this->_saveItemList(itemNameIdList);
142   }
143   else if ( actionId == _actionIds.remove ) {
144     STDLOG("WorkspaceController::processItemList: remove");
145     this->_removeItemList(itemNameIdList);
146   }
147   else {
148     STDLOG("WorkspaceController::processItemList: ERR : action unknown ");
149   }
150 }
151
152 /*!
153  * This function import in the console all the fields associated to
154  * the model items of the specified list. "Import a fields" means
155  * "define a field proxy variable in the python context to manipulate
156  * the real field in the database".
157  */
158 void WorkspaceController::_importItemList(QStringList itemNameIdList) {
159   LOG("WorkspaceController: signal received : display item list "<<itemNameIdList);
160   QStringList::const_iterator it;
161   for (it = itemNameIdList.constBegin(); it != itemNameIdList.constEnd(); ++it) {
162     QString itemNameId = *it;
163     this->_importItem(itemNameId);
164   }
165 }
166
167 /*!
168  * This function is the unit function used to import field in the
169  * console (see _importItemList).
170  */
171 void WorkspaceController::_importItem(QString itemNameId) {
172   XmedDataModel * dataModel = (XmedDataModel *)this->getDataModel();
173   if ( dataModel == NULL ) {
174     LOG("No data model associated to this tree view");
175     return;
176   }
177
178   // We can request the dataModel to obtain the dataObject associated
179   // to this item (iteNameId is a TreeView id, Qt stuff only).
180   XmedDataObject * dataObject =
181     (XmedDataObject *)dataModel->getDataObject(QS2S(itemNameId));
182
183   if ( dataObject == NULL ) {
184     LOG("WorkspaceController: WARN! No data object associated to the item "<<itemNameId);
185     return;
186   }
187
188   // Then, we can request this data object to obtain the associated
189   // FieldHandler.
190   MEDCALC::FieldHandler * fieldHandler = dataObject->getFieldHandler();
191   STDLOG("Field: mesh="<<fieldHandler->meshname<<" name="<<fieldHandler->fieldname);
192
193   // Finally, we can import the field
194   bool askForOptions = true;
195   _importFieldIntoConsole(fieldHandler, askForOptions);
196 }
197
198 /**
199  * This function import the specified field into the tui console. This
200  * means to define a field proxy variable in the python context to
201  * manipulate the field. We can raise a gui to specify some import
202  * options or simply specify the alias (i.e. the name of the python
203  * variable).
204  */
205 void WorkspaceController::_importFieldIntoConsole(MEDCALC::FieldHandler * fieldHandler,
206               bool askForOptions,
207               const char * alias)
208 {
209   STDLOG("alias="<<alias);
210
211   // By default, the alias is the name of the field
212   QString *effectiveAlias;
213   if ( alias == NULL ) {
214     effectiveAlias = new QString(fieldHandler->fieldname);
215   }
216   else {
217     effectiveAlias = new QString(alias);
218   }
219
220   // We can propose to the user to specify some additionnal
221   // informations concerning what must be imported.
222   //
223   // In this version, we just ask the alias the field will be
224   // manipulated with. The default alias is the field name. This alias
225   // should be asked to the user to get a short name to manipulate.
226   if ( askForOptions ) {
227     DlgAlias dialog;
228     dialog.setAlias(*effectiveAlias);
229     int choice = dialog.exec();
230     if ( choice == QDialog::Rejected ) {
231       // The user decides to cancel the operation
232       return;
233     }
234     *effectiveAlias = dialog.getAlias();
235   }
236
237   //
238   // Then, the list of python commands can be written and executed to
239   // define the field in the console
240   //
241   QStringList commands;
242   commands+=QString("%1=medcalc.newFieldProxy(fieldHandlerId=%2)")
243     .arg(*effectiveAlias)
244     .arg(fieldHandler->id);
245
246   _consoleDriver->exec(commands);
247 }
248
249 /*!
250  * This function is a Qt slot connected to the signal medEventSignal
251  * emitted from the MEDEventListener. It processes events coming from
252  * the python console.
253  */
254 void WorkspaceController::processMedEvent(const MEDCALC::MedEvent * event) {
255   STDLOG("WorkspaceController::processMedEvent");
256   STDLOG("dataId  :"<<event->dataId);
257
258   XmedDataModel * dataModel = (XmedDataModel *)this->getDataModel();
259   if ( dataModel == NULL ) {
260     STDLOG("No data model associated to this tree view");
261     return;
262   }
263
264   if ( event->type == MEDCALC::EVENT_UPDATE_FIELD ) {
265     std::cout << "WorkspaceController::processMedEvent[MEDCALC::EVENT_UPDATE_FIELD]: Not implemented yet";
266   }
267   else if ( event->type == MEDCALC::EVENT_PUT_IN_WORKSPACE ) {
268     STDLOG("add new field");
269     MEDCALC::FieldHandler * fieldHandler =
270       MEDFactoryClient::getDataManager()->getFieldHandler(event->dataId);
271
272     XmedDataObject * dataObject = (XmedDataObject *)dataModel->newDataObject();
273     dataObject->setFieldHandler(*fieldHandler);
274     this->getDataTreeModel()->addData(dataObject);
275   }
276   else if ( event->type == MEDCALC::EVENT_REMOVE_FROM_WORKSPACE ) {
277     STDLOG("remove field");
278     std::map<string, DataObject *>::iterator itr = dataModel->begin();
279     for ( ; itr != dataModel->end(); ++itr) {
280       XmedDataObject* obj = dynamic_cast<XmedDataObject*>(itr->second);
281       if (obj->getFieldHandler()->id == event->dataId) {
282         std::string itemNameId = obj->getNameId();
283         this->getDataTreeModel()->removeData(obj);
284         dataModel->removeDataObject(itemNameId);
285         return;
286       }
287     }
288   }
289   else if ( event->type == MEDCALC::EVENT_CLEAN_WORKSPACE ) {
290     STDLOG("clean workspace");
291     std::map<string, DataObject *>::iterator itr = dataModel->begin();
292     for ( ; itr != dataModel->end(); ++itr) {
293       XmedDataObject* obj = dynamic_cast<XmedDataObject*>(itr->second);
294       std::string itemNameId = obj->getNameId();
295       this->getDataTreeModel()->removeData(obj);
296       dataModel->removeDataObject(itemNameId);
297     }
298   }
299   else if ( event->type == MEDCALC::EVENT_ADD_DATASOURCE ) {
300     emit workspaceSignal(event); // forward to DatasourceController
301   }
302   else if ( event->type == MEDCALC::EVENT_ADD_PRESENTATION ) {
303     emit workspaceSignal(event); // forward to DatasourceController
304   }
305
306 }
307
308 /*!
309  * This function save a list of fields in a med file. The med file
310  * name is requested to the user using a file chooser dialog box
311  */
312 void WorkspaceController::_saveItemList(QStringList itemNameIdList) {
313   XmedDataProcessor * dataProcessor = new XmedDataProcessor(this->getDataModel());
314   dataProcessor->process(itemNameIdList);
315   MEDCALC::FieldIdList_var fieldIdList = dataProcessor->getResultingFieldIdList();
316   delete dataProcessor;
317
318   QStringList filter;
319   filter.append(tr("FILE_FILTER_MED"));
320   QString filename = SUIT_FileDlg::getFileName(_salomeModule->getApp()->desktop(),
321                                                "",
322                                                filter,
323                                                tr("SAVE_SELECTED_FIELDS"),
324                                                false);
325
326   if ( filename.isEmpty() ) return;
327
328   MEDFactoryClient::getDataManager()->saveFields(QCHARSTAR(filename), fieldIdList);
329 }
330
331 /*!
332  * This function remove the selected item from workspace.
333  */
334 void WorkspaceController::_removeItemList(QStringList itemNameIdList) {
335   XmedDataModel * dataModel = (XmedDataModel *)this->getDataModel();
336   if ( dataModel == NULL ) {
337     LOG("No data model associated to this tree view");
338     return;
339   }
340
341   // __GBO__: In this version, we consider only the first field in the selection
342   QString itemNameId = itemNameIdList[0];
343
344   // We can request the dataModel to obtain the dataObject associated
345   // to this item (iteNameId is a TreeView id, Qt stuff only).
346   XmedDataObject * dataObject =
347     (XmedDataObject *)dataModel->getDataObject(QS2S(itemNameId));
348
349   if ( dataObject == NULL ) {
350     LOG("WorkspaceController: WARN! No data object associated to the item "<<itemNameId);
351     return;
352   }
353
354   // Then, we can request this data object to obtain the associated
355   // FieldHandler.
356   MEDCALC::FieldHandler * fieldHandler = dataObject->getFieldHandler();
357   STDLOG("Field: mesh="<<fieldHandler->meshname<<" name="<<fieldHandler->fieldname);
358
359   // Remove the field variable from console
360   QStringList commands;
361   commands+=QString("removeFromWorkspace(accessField(%1))").arg(fieldHandler->id);
362   _consoleDriver->exec(commands);
363
364   // Finally, we can remove the field from tree data model and tree view
365   this->getDataTreeModel()->removeData(dataObject);
366   dataModel->removeDataObject(QS2S(itemNameId));
367 }
368
369 /**
370  * This function export the list of specified field item to PARAVIS
371  * module. This consists in create a med file gathering the selected
372  * items, then to import this file in PARAVIS, and finally to create a
373  * scalar map of the first item to start the job.
374  */
375 void WorkspaceController::_exportItemList(QStringList itemNameIdList) {
376   XmedDataProcessor * dataProcessor = new XmedDataProcessor(this->getDataModel());
377   dataProcessor->process(itemNameIdList);
378   MEDCALC::FieldIdList_var fieldIdList = dataProcessor->getResultingFieldIdList();
379   delete dataProcessor;
380
381   // _GBO_ We use a temporary file to proceed with this export to
382   // paravis. I'm sure it could be better in a futur version or when I
383   // will get a better understanding of paravis API.
384   const char * tmpfilename = "/tmp/medcalc_export2paravis.med";
385   MEDFactoryClient::getDataManager()->saveFields(tmpfilename, fieldIdList);
386
387   // We import the whole file but create a scalar map for the first
388   // selected field only (it's just an export to continue the job in
389   // paravis)
390   XmedDataModel * dataModel = (XmedDataModel *)this->getDataModel();
391   if ( dataModel == NULL ) {
392     STDLOG("No data model associated to this tree view");
393     return;
394   }
395   QString itemNameId = itemNameIdList[0];
396   XmedDataObject * dataObject = (XmedDataObject *)dataModel->getDataObject(QS2S(itemNameId));
397   if ( dataObject == NULL ) {
398     LOG("WorkspaceController: WARN! No data object associated to the item "<<itemNameId);
399     return;
400   }
401   MEDCALC::FieldHandler * fieldHandler = dataObject->getFieldHandler();
402   QStringList commands;
403   /*
404   commands+=QString("from xmed.driver_pvis import pvis_scalarmap");
405   commands+=QString("pvis_scalarmap('%1','%2','%3',%4,%5)")
406     .arg(tmpfilename)
407     .arg(QString(fieldHandler->meshname))
408     .arg(QString(fieldHandler->fieldname))
409     .arg(fieldHandler->type)
410     .arg(fieldHandler->iteration);
411   */
412   commands += "print 'Not implemented yet'";
413   _consoleDriver->exec(commands);
414
415 }
416
417 /*!
418  * This function sends a request to the SALOME data visualisation
419  * (module VISU or PARAVIS) for displaying a scalar map of the fields
420  * associated to the model items in the specified list.
421  *
422  */
423 void WorkspaceController::_viewItemList(QStringList itemNameIdList) {
424
425   // __GBO__: In this version, we consider only the first field in the selection
426   QString itemNameId = itemNameIdList[0];
427
428   XmedDataModel * dataModel = (XmedDataModel *)this->getDataModel();
429   if ( dataModel == NULL ) {
430     LOG("No data model associated to this tree view");
431     return;
432   }
433
434   // We can request the dataModel to obtain the dataObject associated
435   // to this item (iteNameId is a TreeView id, Qt stuff only).
436   XmedDataObject * dataObject =
437     (XmedDataObject *)dataModel->getDataObject(QS2S(itemNameId));
438   if ( dataObject == NULL ) {
439     LOG("WorkspaceController: WARN! No data object associated to the item "<<itemNameId);
440     return;
441   }
442
443   // Then, we can request this data object to obtain the associated
444   // FieldHandler.
445   MEDCALC::FieldHandler * fieldHandler = dataObject->getFieldHandler();
446
447   // And finally, we can create the set of medcalc instructions to
448   // generate the scalar map on this field.
449   QStringList commands;
450   //commands+=QString("view(accessField(%1))").arg(fieldHandler->id);
451   commands += "print 'Not implemented yet'";
452   _consoleDriver->exec(commands);
453 }
454
455 /**
456  * This slot can process the event coming from the
457  * DatasourceController. The connection between the datasource signal
458  * and this slot is realized by the main class MEDModule.
459  */
460 void WorkspaceController::processDatasourceEvent(const DatasourceEvent * event) {
461   XmedDataModel * dataModel = (XmedDataModel *)this->getDataModel();
462   if ( dataModel == NULL ) {
463     STDLOG("No data model associated to this tree view");
464     return;
465   }
466
467   // >>>
468   // __GBO__ To know what to do we should test the type, because the
469   // object could be a mesh, a timeseries or a single field. We test
470   // here the case of a single field. Moreover, there could have
471   // options such that "change the underlying mesh".
472   // <<<
473
474   XmedDataObject * dataObject = event->objectdata;
475
476   if ( event->eventtype == DatasourceEvent::EVENT_IMPORT_OBJECT ) {
477     STDLOG("IMPORT object in workspace:\n"<<dataObject->toString());
478     // _GBO_ QUESTION: tag automatically the object as a peristant object ??
479     // We first add the data object to the internal data model
480     dataModel->addDataObject(dataObject);
481     // Then we request the tree view to consider this new object
482     this->getDataTreeModel()->addData(dataObject);
483   }
484   else if ( event->eventtype == DatasourceEvent::EVENT_USE_OBJECT ) {
485     STDLOG("USE object in workspace:\n"<<dataObject->toString());
486     // We first add the data object to the internal data model
487     dataModel->addDataObject(dataObject);
488     // Then we request the tree view to consider this new object
489     this->getDataTreeModel()->addData(dataObject);
490
491     // We define a proxy for this object in the tui console.
492     STDLOG("Define a proxy variable in the console with name : "<<QCHARSTAR(event->objectalias));
493     bool askForOptions = false;
494     _importFieldIntoConsole(dataObject->getFieldHandler(),
495           askForOptions,
496           QCHARSTAR(event->objectalias));
497   }
498   else if ( event->eventtype == DatasourceEvent::EVENT_VIEW_OBJECT_SCALAR_MAP ) {
499     QStringList commands;
500
501 #define stringify( name ) # name
502     QString viewMode = stringify(MEDCALC::VIEW_MODE_NEW_LAYOUT); // :TODO: change this (get value from dedicated dialog)
503     viewMode.replace("::", ".");
504
505     MEDCALC::FieldHandler* fieldHandler = dataObject->getFieldHandler();
506     commands += QString("medcalc.MakeScalarMap(accessField(%1), %2)").arg(fieldHandler->id).arg(viewMode);
507     _consoleDriver->exec(commands);
508   }
509   else if ( event->eventtype == DatasourceEvent::EVENT_ADD_DATASOURCE ) {
510     QStringList commands;
511     commands += QString("medcalc.LoadDataSource('%1')").arg(event->objectalias);
512     _consoleDriver->exec(commands);
513   }
514   else if ( event->eventtype == DatasourceEvent::EVENT_ADD_IMAGE_AS_DATASOURCE ) {
515     QStringList commands;
516     commands += QString("medcalc.LoadImageAsDataSource('%1')").arg(event->objectalias);
517     _consoleDriver->exec(commands);
518   }
519   else {
520     STDLOG("The event "<<event->eventtype<<" is not implemented yet");
521   }
522
523 }
524
525 void WorkspaceController::OnSaveWorkspace() {
526
527   // Dialog to get the filename where the workspace must be saved into
528   QStringList filter;
529   filter.append(tr("FILE_FILTER_MED"));
530
531   QString filename = SUIT_FileDlg::getFileName(_salomeModule->getApp()->desktop(),
532                                                "",
533                                                filter,
534                                                tr("SAVE_WORKSPACE_DATA"),
535                                                false);
536
537   if ( filename.isEmpty() ) return;
538
539   STDLOG("OnWorkspaceSave: save the workspace in the file " << QCHARSTAR(filename));
540   QStringList commands;
541   commands+=QString("saveWorkspace('%1')").arg(filename);
542   _consoleDriver->exec(commands);
543 }
544
545 #include <QMessageBox>
546 void WorkspaceController::OnCleanWorkspace() {
547   // Remove field from console
548   QStringList commands;
549   commands += QString("cleanWorkspace()");
550   _consoleDriver->exec(commands);
551 }