Salome HOME
[MEDCalc] Add dock widget for presentation parameters
[modules/med.git] / src / MEDCalc / gui / DatasourceController.cxx
1 // Copyright (C) 2007-2016  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 "DatasourceController.hxx"
23 #include "DatasourceConstants.hxx"
24
25 #include <SalomeApp_Application.h>
26 #include <SalomeApp_Study.h>
27 #include <SalomeApp_DataObject.h>
28
29 #include <SALOME_ListIO.hxx>
30 #include <LightApp_SelectionMgr.h>
31
32 #include <SALOME_LifeCycleCORBA.hxx>
33 #include <SALOMEDS_SObject.hxx>
34 #include <SALOMEDS_Study.hxx>
35
36 #include "MEDFactoryClient.hxx"
37 #include "MEDModule.hxx"
38 #include "QtHelper.hxx"
39
40 #include CORBA_CLIENT_HEADER(SALOMEDS)
41 #include CORBA_CLIENT_HEADER(SALOMEDS_Attributes)
42 #include <SUIT_FileDlg.h>
43 #include <SUIT_Desktop.h>
44
45 #include <QStringList>
46 #include <QString>
47 #include <QMessageBox>
48
49 #include "DlgAlias.hxx"
50
51 //
52 // ==============================================================
53 // Datasource controller
54 // ==============================================================
55 //
56 //DatasourceController::DatasourceController(StandardApp_Module* salomeModule)
57 DatasourceController::DatasourceController(MEDModule* salomeModule)
58 {
59   STDLOG("Creating a DatasourceController");
60   _salomeModule = salomeModule;
61   _studyEditor = _salomeModule->getStudyEditor();
62
63   _dlgChangeUnderlyingMesh = new DlgChangeUnderlyingMesh(_studyEditor);
64   connect(_dlgChangeUnderlyingMesh,SIGNAL(inputValidated()),
65           this, SLOT(OnChangeUnderlyingMeshInputValidated()));
66
67   _dlgInterpolateField = new DlgInterpolateField(_studyEditor);
68   connect(_dlgInterpolateField,SIGNAL(inputValidated()),
69           this, SLOT(OnInterpolateFieldInputValidated()));
70 }
71
72 DatasourceController::~DatasourceController() {
73   STDLOG("Deleting the DatasourceController");
74 }
75
76 void DatasourceController::createActions() {
77   //QWidget* dsk = _salomeModule->getApp()->desktop();
78   //SUIT_ResourceMgr* resMgr = _salomeModule->getApp()->resourceMgr();
79   int toolbarId = _salomeModule->createTool("Datasource", "DatasourceToolbar");
80
81   //
82   // Main actions (toolbar and menubar)
83   //
84   QString label   = tr("LAB_ADD_DATA_SOURCE");
85   QString tooltip = tr("TIP_ADD_DATA_SOURCE");
86   QString icon    = tr("ICO_DATASOURCE_ADD");
87   int actionId;
88   actionId = _salomeModule->createStandardAction(label,this, SLOT(OnAddDatasource()),icon,tooltip);
89   //_salomeModule->addActionInToolbar(actionId);
90   _salomeModule->createTool(actionId, toolbarId);
91
92   // This action has to be placed in the general file menu with the label "Import MED file"
93   int menuId = _salomeModule->createMenu( tr( "MEN_FILE" ), -1,  1 );
94   //_salomeModule->addActionInMenubar(actionId, menuId);
95   _salomeModule->action(actionId)->setIconVisibleInMenu(true);
96   _salomeModule->createMenu(actionId, menuId, 10);
97
98   label   = tr("LAB_ADD_IMAGE_SOURCE");
99   tooltip = tr("TIP_ADD_IMAGE_SOURCE");
100   icon    = tr("ICO_IMAGE_ADD");
101   actionId = _salomeModule->createStandardAction(label,this, SLOT(OnAddImagesource()),icon,tooltip);
102   // _salomeModule->addActionInToolbar(actionId);
103   _salomeModule->createTool(actionId, toolbarId);
104   _salomeModule->action(actionId)->setIconVisibleInMenu(true);
105   _salomeModule->createMenu(actionId, menuId, 20);
106
107   //
108   // Actions for popup menu only
109   //
110   // Expand field timeseries
111   label = tr("LAB_EXPAND_FIELD");
112   icon  = tr("ICO_DATASOURCE_EXPAND_FIELD");
113   actionId = _salomeModule->createStandardAction(label,this,SLOT(OnExpandField()),icon);
114   _salomeModule->addActionInPopupMenu(actionId);
115
116   // Use in workspace
117   label = tr("LAB_USE_IN_WORKSPACE");
118   icon  = tr("ICO_DATASOURCE_USE");
119   actionId = _salomeModule->createStandardAction(label,this,SLOT(OnUseInWorkspace()),icon);
120   _salomeModule->addActionInPopupMenu(actionId);
121
122   // Change underlying mesh (note that this action creates a new field in
123   // the workspace that corresponds to a copy of the selected field
124   // modified by the change of the underlying mesh.
125   label = tr("LAB_CHANGE_MESH");
126   icon  = tr("ICO_DATASOURCE_CHANGE_MESH");
127   actionId = _salomeModule->createStandardAction(label,this,SLOT(OnChangeUnderlyingMesh()),icon);
128   _salomeModule->addActionInPopupMenu(actionId);
129
130   label = tr("LAB_INTERPOLATE_FIELD");
131   icon  = tr("ICO_DATASOURCE_INTERPOLATE_FIELD");
132   actionId = _salomeModule->createStandardAction(label,this,SLOT(OnInterpolateField()),icon);
133   _salomeModule->addActionInPopupMenu(actionId);
134 }
135
136 /**
137  * This function adds the specified MED file as a datasource in the
138  * dataspace. Technically speaking, the engine loads the
139  * meta-information concerning med data from the file, gives this
140  * informations to the GUI, and the GUI creates a tree view of these
141  * data in the study object browser.
142  */
143 // This function emits a signal that will be caught by workspace to delegate command (datasource creation) to python console.
144 void
145 DatasourceController::addDatasource(const char* filename)
146 {
147   DatasourceEvent* event = new DatasourceEvent();
148   event->eventtype = DatasourceEvent::EVENT_ADD_DATASOURCE;
149   event->objectalias = filename;
150   emit datasourceSignal(event);
151 }
152 // After above data source creation, python console emits a signal, forwarded by workspace, to update the GUI
153 void
154 DatasourceController::updateTreeViewWithNewDatasource(const MEDCALC::DatasourceHandler* datasourceHandler)
155 {
156   if (!datasourceHandler) {
157     return;
158   }
159
160   SalomeApp_Study* study = dynamic_cast<SalomeApp_Study*>(_salomeModule->application()->activeStudy());
161   _PTR(Study) studyDS = study->studyDS();
162
163   _salomeModule->engine()->addDatasourceToStudy(_CAST(Study, studyDS)->GetStudy(), *datasourceHandler);
164
165   // update Object browser
166   _salomeModule->getApp()->updateObjectBrowser(true);
167 }
168
169 void DatasourceController::OnAddDatasource()
170 {
171   // Dialog to get the filename where the input data are read from
172   QStringList filter;
173   filter.append(tr("FILE_FILTER_MED"));
174
175   QString anInitialPath = "";
176   if ( SUIT_FileDlg::getLastVisitedPath().isEmpty() )
177     anInitialPath = QDir::currentPath();
178
179   QStringList filenames = SUIT_FileDlg::getOpenFileNames( _salomeModule->getApp()->desktop(),
180                                                           anInitialPath,
181                                                           filter,
182                                                           tr("IMPORT_MED_FIELDS") );
183
184   if ( filenames.count() <= 0 ) return;
185   for ( QStringList::ConstIterator itFile = filenames.begin();
186         itFile != filenames.end(); ++itFile ) {
187     QString filename = *itFile;
188     this->addDatasource(QCHARSTAR(filename));
189     _salomeModule->updateObjBrowser(true);
190   }
191 }
192
193 #include "DlgImageToMed.hxx"
194 //#include <stdio.h>
195 //#include <stdlib.h>
196 void DatasourceController::OnAddImagesource()
197 {
198
199   DlgImageToMed dialog;
200   dialog.setAutoLoaded(true);
201   int choice = dialog.exec();
202   if ( choice == QDialog::Rejected ) {
203     // The user decides to cancel the operation
204     return;
205   }
206
207   QString imageFilename = dialog.getImageFilepath();
208   /*
209   QString medFilename   = dialog.getMedFilepath();
210   bool autoLoad         = dialog.isAutoLoaded();
211
212   std::string ROOT_DIR(getenv("MED_ROOT_DIR"));
213   std::string command(ROOT_DIR+"/bin/salome/med/image2med.py");
214   command += " -i "+QS2S(imageFilename);
215   command += " -m "+QS2S(medFilename);
216   int error = system(command.c_str());
217   if ( error != 0 ) {
218     QMessageBox::critical(_salomeModule->getApp()->desktop(),
219            tr("Operation failed"),
220            tr("The creation of med data from the image file failed"));
221     return;
222   }
223
224   if ( autoLoad ) {
225     this->addDatasource(QCHARSTAR(medFilename));
226     _salomeModule->updateObjBrowser(true);
227   }
228   */
229
230   DatasourceEvent* event = new DatasourceEvent();
231   event->eventtype = DatasourceEvent::EVENT_ADD_IMAGE_AS_DATASOURCE;
232   event->objectalias = imageFilename;
233   emit datasourceSignal(event);
234 }
235
236 void DatasourceController::OnExpandField()
237 {
238   // We need a studyEditor updated on the active study
239   _studyEditor->updateActiveStudy();
240
241   // Get the selected objects in the study (SObject)
242   SALOME_StudyEditor::SObjectList* listOfSObject = _studyEditor->getSelectedObjects();
243   for (int i=0; i<listOfSObject->size(); i++) {
244     SALOMEDS::SObject_var soFieldseries = listOfSObject->at(i);
245
246     // First retrieve the fieldseries id associated to this study object
247     long fieldseriesId = _studyEditor->getParameterInt(soFieldseries,OBJECT_ID);
248     STDLOG("Expand the field timeseries "<<fieldseriesId);
249
250     // If fieldseriesId equals -1, then it means that it is not a
251     // fieldseries managed by the MED module, and we stop this
252     // function process.
253     if ( fieldseriesId < 0 )
254       continue;
255     // _GBO_ A better correction should be to no display the
256     // contextual menu if the selected object is not conform
257
258     // Then retrieve the list of fields in this timeseries
259     MEDCALC::FieldHandlerList* fieldHandlerList =
260       MEDFactoryClient::getDataManager()->getFieldListInFieldseries(fieldseriesId);
261
262     // Finally, create an entry for each of the field
263     for(CORBA::ULong iField=0; iField<fieldHandlerList->length(); iField++) {
264       MEDCALC::FieldHandler fieldHandler = (*fieldHandlerList)[iField];
265       SALOMEDS::SObject_var soField = _studyEditor->newObject(soFieldseries);
266       std::string label("it="); label += ToString(fieldHandler.iteration);
267       _studyEditor->setName(soField,label.c_str());
268       _studyEditor->setParameterInt(soField, OBJECT_ID, fieldHandler.id);
269       _studyEditor->setParameterBool(soField,OBJECT_IS_IN_WORKSPACE,false);
270     }
271   }
272   _salomeModule->updateObjBrowser(true);
273 }
274
275 void DatasourceController::OnUseInWorkspace() {
276   // We need a studyEditor updated on the active study
277   _studyEditor->updateActiveStudy();
278
279   // Get the selected objects in the study (SObject)
280   SALOME_StudyEditor::SObjectList* listOfSObject = _studyEditor->getSelectedObjects();
281   if ( listOfSObject->size() == 1 ) {
282     // In this case we ask the name of the variable for the python
283     // console
284
285     // >>>
286     // _GBO_ Note that it works only for a single field but the
287     // XmedDataObject will be improved to deal with mesh, timeseries
288     // and single field in a futur version. We suppose here that a
289     // single field has been selected.
290     // <<<
291
292     SALOMEDS::SObject_var soField = listOfSObject->at(0);
293
294     bool isInWorkspace = _studyEditor->getParameterBool(soField,OBJECT_IS_IN_WORKSPACE);
295     if ( isInWorkspace ) {
296       QMessageBox::warning(_salomeModule->getApp()->desktop(),
297          tr("Operation not allowed"),
298          tr("This field is already defined in the workspace"));
299       return;
300     }
301
302     int fieldId = _studyEditor->getParameterInt(soField,OBJECT_ID);
303
304     // If fieldId equals -1, then it means that it is not a field
305     // managed by the MED module, and we stop this function process.
306     if ( fieldId < 0 ) {
307       QMessageBox::warning(_salomeModule->getApp()->desktop(),
308          tr("Operation not allowed"),
309          tr("This element is not a field object"));
310       return;
311     }
312
313     MEDCALC::FieldHandler* fieldHandler =
314       MEDFactoryClient::getDataManager()->getFieldHandler(fieldId);
315
316     if (! fieldHandler) {
317       QMessageBox::warning(_salomeModule->getApp()->desktop(),
318          tr("Operation not allowed"),
319          tr("No field is defined"));
320       return;
321     }
322
323     QString alias(fieldHandler->fieldname);
324     DlgAlias dialog;
325     dialog.setAlias(alias);
326     int choice = dialog.exec();
327     if ( choice == QDialog::Rejected ) {
328       // The user decides to cancel the operation
329       return;
330     }
331     alias = dialog.getAlias();
332
333     DatasourceEvent* event = new DatasourceEvent();
334     event->eventtype = DatasourceEvent::EVENT_USE_OBJECT;
335     XmedDataObject* dataObject = new XmedDataObject();
336     dataObject->setFieldHandler(*fieldHandler);
337     event->objectdata  = dataObject;
338     event->objectalias = alias;
339     emit datasourceSignal(event);
340     // Tag the item to prevent double import
341     //    _studyEditor->setParameterBool(soField,OBJECT_IS_IN_WORKSPACE,true);
342     // Tag the field as persistent on the server. It means that a
343     // saving of the workspace will save at least this field (maybe it
344     // should be an option?)
345     MEDFactoryClient::getDataManager()->markAsPersistent(fieldId, true);
346   }
347   else {
348     // In this case, we don't ask the user to specify an alias for
349     // each item, we just import the whole set of items.
350     for (int i=0; i<listOfSObject->size(); i++) {
351       SALOMEDS::SObject_var soField = listOfSObject->at(i);
352
353       bool isInWorkspace = _studyEditor->getParameterBool(soField,OBJECT_IS_IN_WORKSPACE);
354       if ( !isInWorkspace ) {
355         int fieldId = _studyEditor->getParameterInt(soField,OBJECT_ID);
356         MEDCALC::FieldHandler* fieldHandler =
357           MEDFactoryClient::getDataManager()->getFieldHandler(fieldId);
358         DatasourceEvent* event = new DatasourceEvent();
359         event->eventtype = DatasourceEvent::EVENT_IMPORT_OBJECT;
360         XmedDataObject* dataObject = new XmedDataObject();
361         dataObject->setFieldHandler(*fieldHandler);
362         event->objectdata  = dataObject;
363         emit datasourceSignal(event);
364         // Note that this signal is processed by the WorkspaceController
365
366         // Tag the item to prevent double import
367         //        _studyEditor->setParameterBool(soField,OBJECT_IS_IN_WORKSPACE,true);
368         // Tag the field as persistent on the server. It means that a
369         // saving of the workspace will save at least this field (maybe it
370         // should be an option?)
371         MEDFactoryClient::getDataManager()->markAsPersistent(fieldId, true);
372       }
373       else {
374         STDLOG("The field "<<_studyEditor->getName(soField)<<
375                " is already defined in the workspace");
376       }
377     }
378   }
379 }
380
381 void DatasourceController::OnChangeUnderlyingMesh() {
382   // We need a studyEditor updated on the active study
383   _studyEditor->updateActiveStudy();
384
385   // Get the selected objects in the study (SObject). In cas of a
386   // multiple selection, we consider only the first item. At least one
387   // item must be selected.
388   SALOME_StudyEditor::SObjectList* listOfSObject = _studyEditor->getSelectedObjects();
389   if ( listOfSObject->size() > 0 ) {
390     SALOMEDS::SObject_var soField = listOfSObject->at(0);
391     int fieldId = _studyEditor->getParameterInt(soField,OBJECT_ID);
392     // _GBO_ : the dialog should not be modal, so that we can choose a
393     // mesh in the browser. Then we have to emit a signal from the
394     // dialog.accept, connected to a slot of the DatasourceControler
395     _dlgChangeUnderlyingMesh->setFieldId(fieldId);
396     Qt::WindowFlags flags = _dlgChangeUnderlyingMesh->windowFlags();
397     _dlgChangeUnderlyingMesh->setWindowFlags(flags | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint);
398     _dlgChangeUnderlyingMesh->open();
399   }
400 }
401
402 void DatasourceController::OnChangeUnderlyingMeshInputValidated() {
403   int meshId = _dlgChangeUnderlyingMesh->getMeshId();
404   STDLOG("meshId = " << ToString(meshId));
405   int fieldId = _dlgChangeUnderlyingMesh->getFieldId();
406   MEDCALC::FieldHandler* fieldHandler =
407     MEDFactoryClient::getDataManager()->getFieldHandler(fieldId);
408
409   // We don't modify the original field but create first a duplicate
410   MEDCALC::FieldHandler* duplicate = MEDFactoryClient::getCalculator()->dup(*fieldHandler);
411   MEDFactoryClient::getDataManager()->changeUnderlyingMesh(duplicate->id, meshId);
412
413   // Request once more the duplicate to update the meta-data on this
414   // client side
415   duplicate = MEDFactoryClient::getDataManager()->getFieldHandler(duplicate->id);
416
417   // >>>
418   // WARN: the following is a temporary code for test purpose
419   // Automatically add in ws
420   DatasourceEvent* event = new DatasourceEvent();
421   event->eventtype = DatasourceEvent::EVENT_IMPORT_OBJECT;
422   XmedDataObject* dataObject = new XmedDataObject();
423   dataObject->setFieldHandler(*duplicate);
424   event->objectdata = dataObject;
425   emit datasourceSignal(event);
426   // Note that this signal is processed by the WorkspaceController
427
428   // Tag the item to prevent double import
429   //_studyEditor->setParameterBool(soField,OBJECT_IS_IN_WORKSPACE,true);
430 }
431
432 void DatasourceController::OnInterpolateField() {
433   // We need a studyEditor updated on the active study
434   _studyEditor->updateActiveStudy();
435
436   // Get the selected objects in the study (SObject). In case of a
437   // multiple selection, we consider only the first item. At least one
438   // item must be selected.
439   SALOME_StudyEditor::SObjectList* listOfSObject = _studyEditor->getSelectedObjects();
440   if ( listOfSObject->size() > 0 ) {
441     SALOMEDS::SObject_var soField = listOfSObject->at(0);
442     int fieldId = _studyEditor->getParameterInt(soField,OBJECT_ID);
443     // _GBO_ : the dialog should not be modal, so that we can choose a
444     // mesh in the browser. Then we have to emit a signal from the
445     // dialog.accept, connected to a slot of the DatasourceControler
446     _dlgInterpolateField->setFieldId(fieldId);
447     Qt::WindowFlags flags = _dlgInterpolateField->windowFlags();
448     _dlgInterpolateField->setWindowFlags(flags | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint);
449     _dlgInterpolateField->open();
450   }
451 }
452
453 void DatasourceController::OnInterpolateFieldInputValidated() {
454   MEDCALC::InterpolationParameters params;
455   params.precision = _dlgInterpolateField->getPrecision();
456   STDLOG("precision = " << params.precision);
457   params.defaultValue = _dlgInterpolateField->getDefaultValue();
458   STDLOG("defaultValue = " << params.defaultValue);
459   params.reverse = _dlgInterpolateField->getReverse();
460   STDLOG("reverse = " << params.reverse);
461   params.intersectionType = _dlgInterpolateField->getIntersectionType().c_str();
462   STDLOG("intersectionType = " << params.intersectionType);
463   params.method = _dlgInterpolateField->getMethod().c_str();
464   STDLOG("method = " << params.method);
465   params.nature = _dlgInterpolateField->getFieldNature().c_str();
466   STDLOG("nature = " << params.nature);
467
468   int meshId = _dlgInterpolateField->getMeshId();
469   STDLOG("meshId = " << ToString(meshId));
470   int fieldId = _dlgInterpolateField->getFieldId();
471   MEDCALC::FieldHandler* fieldHandler = MEDFactoryClient::getDataManager()->getFieldHandler(fieldId);
472
473   // We don't modify the original field but create first a duplicate
474   // MEDCALC::FieldHandler* duplicate = MEDFactoryClient::getCalculator()->dup(*fieldHandler);
475   //MEDFactoryClient::getDataManager()->changeUnderlyingMesh(duplicate->id, meshId);
476   MEDCALC::FieldHandler* result = NULL;
477   try {
478     result = MEDFactoryClient::getDataManager()->interpolateField(fieldId, meshId, params);
479   }
480   catch(...) {
481     STDLOG("Unable to process field interpolation; please check interpolation parameters");
482     QMessageBox::critical(_salomeModule->getApp()->desktop(),
483                           tr("Operation failed"),
484                           tr("Unable to process field interpolation; please check interpolation parameters"));
485     return;
486   }
487
488   // Request once more the duplicate to update the meta-data on this
489   // client side
490   // duplicate = MEDFactoryClient::getDataManager()->getFieldHandler(duplicate->id);
491
492   // >>>
493   // WARN: the following is a temporary code for test purpose
494   // Automatically add in ws
495   DatasourceEvent* event = new DatasourceEvent();
496   event->eventtype = DatasourceEvent::EVENT_IMPORT_OBJECT;
497   XmedDataObject* dataObject = new XmedDataObject();
498   dataObject->setFieldHandler(*result);
499   event->objectdata = dataObject;
500   emit datasourceSignal(event);
501   // Note that this signal is processed by the WorkspaceController
502
503   // // Tag the item to prevent double import
504   // //_studyEditor->setParameterBool(soField,OBJECT_IS_IN_WORKSPACE,true);
505 }
506
507 void
508 DatasourceController::processWorkspaceEvent(const MEDCALC::MedEvent* event)
509 {
510   if ( event->type == MEDCALC::EVENT_ADD_DATASOURCE ) {
511     MEDCALC::DatasourceHandler* datasourceHandler = MEDFactoryClient::getDataManager()->getDatasourceHandler(event->filename);
512     this->updateTreeViewWithNewDatasource(datasourceHandler);
513   }
514 }