1 // Copyright (C) 2007-2016 CEA/DEN, EDF R&D
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.
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.
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
17 // See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 // Author : Guillaume Boulant (EDF)
22 #include "WorkspaceController.hxx"
23 #include "QtHelper.hxx"
24 #include "MEDFactoryClient.hxx"
25 #include "MEDModule.hxx"
26 #include "XmedDataModel.hxx"
27 #include "DlgAlias.hxx"
29 #include <SALOMEconfig.h>
30 #include CORBA_CLIENT_HEADER(MEDEventListener)
32 #include <SalomeApp_Application.h>
33 #include <SALOME_LifeCycleCORBA.hxx>
34 #include <SUIT_FileDlg.h>
35 #include <SUIT_Desktop.h>
36 #include <SUIT_ResourceMgr.h>
39 #include <QMessageBox>
42 * This class defines a DockWidget plugged in the SALOME application,
43 * and containing a tree view for rendering a hierarchical data
44 * model. This datamodel contains the objects used in the workspace.
46 WorkspaceController::WorkspaceController(MEDModule* salomeModule)
47 : TreeGuiManager(salomeModule->getApp(), "Workspace")
49 _salomeModule = salomeModule;
50 getDockWidgets()->getDockWidget()->setObjectName("medWorkspaceDock");
52 this->tabifyDockWidgets(false);
54 // -------------------------------------------------------------
55 // Setup the MEDEventListener to manage notification from the
58 // We create a MEDEventListener CORBA object inside this GUI class
59 // with the role of listening events coming from the python console
60 // (or even the components if needed). The events arising in the
61 // python console are send as CORBA request to this CORBA
62 // servant. Then this object can process the event by notifying the
63 // GUI of something to update for example (using signals and slots
65 _medEventListener = MEDEventListener_i::getInstance();
66 MEDCALC::MEDEventListener_ptr medEventListenerServant = _medEventListener->_this();
68 // We store the IOR inside the MEDDataManager to share this data
69 // with other parts of the application, in particular the python
70 // console that could retrieve this IOR using the
71 // getEventListenerIOR() function of the MEDDataManager.
72 SalomeApp_Application* salomeApp = salomeModule->getApp();
73 const char* medEventListenerIOR =
74 salomeApp->orb()->object_to_string(medEventListenerServant);
75 MEDFactoryClient::getDataManager()->setEventListenerIOR(medEventListenerIOR);
77 // Connect the signals emitted from the MEDEventListener to slot of
79 connect(_medEventListener, SIGNAL(medEventSignal(const MEDCALC::MedEvent*)),
80 this, SLOT(processMedEvent(const MEDCALC::MedEvent*)));
82 // Note that this class must be mocked (Q_OBJECT + moc file
83 // generated from header file) so that to be able to connect a
84 // signal to a slot of this class.
86 // -------------------------------------------------------------
87 // Customize the treeview rendering the datamodel with specific
88 // action for the popup menu
89 this->getDataTreeView()->clearActions();
90 _actionIds.display = this->getDataTreeView()->addAction(tr("VISUALIZE_SCALAR_MAP"));
91 _actionIds.useInTui = this->getDataTreeView()->addAction(tr("USE_IN_CONSOLE"));
92 _actionIds.exportToPv = this->getDataTreeView()->addAction(tr("EXPORT_TO_PARAVIS"));
93 _actionIds.save = this->getDataTreeView()->addAction(tr("SAVE_AS_MED"));
94 _actionIds.remove = this->getDataTreeView()->addAction(tr("REMOVE_FROM_WORKSPACE"));
96 // -------------------------------------------------------------
97 // Initialize the python console. Note that this must be done at
98 // last because the setup will try to initiate a connection to the
100 _consoleDriver = new XmedConsoleDriver(salomeModule);
101 _consoleDriver->setup();
104 WorkspaceController::~WorkspaceController() {
105 STDLOG("WorkspaceController::~WorkspaceController()");
106 MEDEventListener_i::release();
110 * This creates the GUI actions for driving the Workspace. The
111 * WorkspaceController creates itself this actions and implements the
114 void WorkspaceController::createActions() {
115 QWidget* dsk = _salomeModule->getApp()->desktop();
116 SUIT_ResourceMgr* resMgr = _salomeModule->getApp()->resourceMgr();
117 int toolbarId = _salomeModule->createTool("Workspace", "WorkspaceToolbar");
119 QString label = tr("LAB_SAVE_WORKSPACE");
120 QString tooltip = tr("TIP_SAVE_WORKSPACE");
121 QString icon = tr("ICO_WORKSPACE_SAVE");
122 int actionId = _salomeModule->createStandardAction(label,this,SLOT(OnSaveWorkspace()),icon,tooltip);
123 _salomeModule->createTool(actionId, toolbarId);
125 label = tr("LAB_CLEAN_WORKSPACE");
126 tooltip = tr("TIP_CLEAN_WORKSPACE");
127 icon = tr("ICO_WORKSPACE_CLEAN");
128 actionId = _salomeModule->createStandardAction(label,this,SLOT(OnCleanWorkspace()),icon,tooltip);
129 _salomeModule->createTool(actionId, toolbarId);
133 * Implementation of the slot processItemList inherited from TreeGuiManager
135 void WorkspaceController::processItemList(QStringList itemNameIdList, int actionId) {
136 if ( actionId == _actionIds.display ) {
137 STDLOG("WorkspaceController::processItemList: display");
138 this->_viewItemList(itemNameIdList);
140 else if ( actionId == _actionIds.useInTui ) {
141 STDLOG("WorkspaceController::processItemList: use");
142 this->_importItemList(itemNameIdList);
144 else if ( actionId == _actionIds.exportToPv ) {
145 STDLOG("WorkspaceController::processItemList: export");
146 this->_exportItemList(itemNameIdList);
148 else if ( actionId == _actionIds.save ) {
149 STDLOG("WorkspaceController::processItemList: save");
150 this->_saveItemList(itemNameIdList);
152 else if ( actionId == _actionIds.remove ) {
153 STDLOG("WorkspaceController::processItemList: remove");
154 this->_removeItemList(itemNameIdList);
157 STDLOG("WorkspaceController::processItemList: ERR : action unknown ");
162 * This function import in the console all the fields associated to
163 * the model items of the specified list. "Import a fields" means
164 * "define a field proxy variable in the python context to manipulate
165 * the real field in the database".
167 void WorkspaceController::_importItemList(QStringList itemNameIdList) {
168 LOG("WorkspaceController: signal received : display item list "<<itemNameIdList);
169 QStringList::const_iterator it;
170 for (it = itemNameIdList.constBegin(); it != itemNameIdList.constEnd(); ++it) {
171 QString itemNameId = *it;
172 this->_importItem(itemNameId);
177 * This function is the unit function used to import field in the
178 * console (see _importItemList).
180 void WorkspaceController::_importItem(QString itemNameId) {
181 XmedDataModel* dataModel = (XmedDataModel*)this->getDataModel();
182 if ( dataModel == NULL ) {
183 LOG("No data model associated to this tree view");
187 // We can request the dataModel to obtain the dataObject associated
188 // to this item (iteNameId is a TreeView id, Qt stuff only).
189 XmedDataObject* dataObject =
190 (XmedDataObject*)dataModel->getDataObject(QS2S(itemNameId));
192 if ( dataObject == NULL ) {
193 LOG("WorkspaceController: WARN! No data object associated to the item "<<itemNameId);
197 // Then, we can request this data object to obtain the associated
199 MEDCALC::FieldHandler* fieldHandler = dataObject->getFieldHandler();
200 STDLOG("Field: mesh="<<fieldHandler->meshname<<" name="<<fieldHandler->fieldname);
202 // Finally, we can import the field
203 bool askForOptions = true;
204 _importFieldIntoConsole(fieldHandler, askForOptions);
208 * This function import the specified field into the tui console. This
209 * means to define a field proxy variable in the python context to
210 * manipulate the field. We can raise a gui to specify some import
211 * options or simply specify the alias (i.e. the name of the python
214 void WorkspaceController::_importFieldIntoConsole(MEDCALC::FieldHandler* fieldHandler,
218 STDLOG("alias="<<alias);
220 // By default, the alias is the name of the field
221 QString*effectiveAlias;
222 if ( alias == NULL ) {
223 effectiveAlias = new QString(fieldHandler->fieldname);
226 effectiveAlias = new QString(alias);
229 // We can propose to the user to specify some additionnal
230 // informations concerning what must be imported.
232 // In this version, we just ask the alias the field will be
233 // manipulated with. The default alias is the field name. This alias
234 // should be asked to the user to get a short name to manipulate.
235 if ( askForOptions ) {
237 dialog.setAlias(*effectiveAlias);
238 int choice = dialog.exec();
239 if ( choice == QDialog::Rejected ) {
240 // The user decides to cancel the operation
243 *effectiveAlias = dialog.getAlias();
247 // Then, the list of python commands can be written and executed to
248 // define the field in the console
250 QStringList commands;
251 commands+=QString("%1=medcalc.newFieldProxy(fieldHandlerId=%2)")
252 .arg(*effectiveAlias)
253 .arg(fieldHandler->id);
255 _consoleDriver->exec(commands);
259 * This function is a Qt slot connected to the signal medEventSignal
260 * emitted from the MEDEventListener. It processes events coming from
261 * the python console.
263 void WorkspaceController::processMedEvent(const MEDCALC::MedEvent* event) {
264 STDLOG("WorkspaceController::processMedEvent");
265 STDLOG("dataId :"<<event->dataId);
267 XmedDataModel* dataModel = (XmedDataModel*)this->getDataModel();
268 if ( dataModel == NULL ) {
269 STDLOG("No data model associated to this tree view");
273 if ( event->type == MEDCALC::EVENT_UPDATE_FIELD ) {
274 std::cout << "WorkspaceController::processMedEvent[MEDCALC::EVENT_UPDATE_FIELD]: Not implemented yet";
276 else if ( event->type == MEDCALC::EVENT_PUT_IN_WORKSPACE ) {
277 STDLOG("add new field");
278 MEDCALC::FieldHandler* fieldHandler =
279 MEDFactoryClient::getDataManager()->getFieldHandler(event->dataId);
281 XmedDataObject* dataObject = (XmedDataObject*)dataModel->newDataObject();
282 dataObject->setFieldHandler(*fieldHandler);
283 this->getDataTreeModel()->addData(dataObject);
285 else if ( event->type == MEDCALC::EVENT_REMOVE_FROM_WORKSPACE ) {
286 STDLOG("remove field");
287 std::map<string, DataObject*>::iterator itr = dataModel->begin();
288 for ( ; itr != dataModel->end(); ++itr) {
289 XmedDataObject* obj = dynamic_cast<XmedDataObject*>(itr->second);
290 if (obj->getFieldHandler()->id == event->dataId) {
291 std::string itemNameId = obj->getNameId();
292 this->getDataTreeModel()->removeData(obj);
293 dataModel->removeDataObject(itemNameId);
298 else if ( event->type == MEDCALC::EVENT_CLEAN_WORKSPACE ) {
299 STDLOG("clean workspace");
300 std::map<string, DataObject*>::iterator itr = dataModel->begin();
301 for ( ; itr != dataModel->end(); ++itr) {
302 XmedDataObject* obj = dynamic_cast<XmedDataObject*>(itr->second);
303 std::string itemNameId = obj->getNameId();
304 this->getDataTreeModel()->removeData(obj);
305 dataModel->removeDataObject(itemNameId);
308 else if ( event->type == MEDCALC::EVENT_ADD_DATASOURCE ) {
309 emit workspaceSignal(event); // forward to DatasourceController
311 else if ( event->type == MEDCALC::EVENT_ADD_PRESENTATION ) {
312 emit workspaceSignal(event); // forward to PresentationController
314 else if ( event->type == MEDCALC::EVENT_REMOVE_PRESENTATION ) {
315 emit workspaceSignal(event); // forward to PresentationController
317 else if ( event->type == MEDCALC::EVENT_MODIFY_PRESENTATION ) {
318 emit workspaceSignal(event); // forward to PresentationController
320 else if ( event->type == MEDCALC::EVENT_PLAY_TEST ) {
321 emit workspaceSignal(event); // forward to TestController
323 else if ( event->type == MEDCALC::EVENT_QUIT_SALOME ) {
324 emit workspaceSignal(event); // forward to TestController
326 else if ( event->type == MEDCALC::EVENT_ERROR ) {
327 std::string msg(event->msg);
328 QMessageBox::warning(_salomeModule->getApp()->desktop(), "Error", QString::fromStdString(msg));
331 STDLOG("WorkspaceController::processMedEvent(): Unhandled event!!!");
335 * This function save a list of fields in a med file. The med file
336 * name is requested to the user using a file chooser dialog box
338 void WorkspaceController::_saveItemList(QStringList itemNameIdList) {
339 XmedDataProcessor* dataProcessor = new XmedDataProcessor(this->getDataModel());
340 dataProcessor->process(itemNameIdList);
341 MEDCALC::FieldIdList_var fieldIdList = dataProcessor->getResultingFieldIdList();
342 delete dataProcessor;
345 filter.append(tr("FILE_FILTER_MED"));
346 QString filename = SUIT_FileDlg::getFileName(_salomeModule->getApp()->desktop(),
349 tr("SAVE_SELECTED_FIELDS"),
352 if ( filename.isEmpty() ) return;
354 MEDFactoryClient::getDataManager()->saveFields(QCHARSTAR(filename), fieldIdList);
358 * This function remove the selected item from workspace.
360 void WorkspaceController::_removeItemList(QStringList itemNameIdList) {
361 XmedDataModel* dataModel = (XmedDataModel*)this->getDataModel();
362 if ( dataModel == NULL ) {
363 LOG("No data model associated to this tree view");
367 // __GBO__: In this version, we consider only the first field in the selection
368 QString itemNameId = itemNameIdList[0];
370 // We can request the dataModel to obtain the dataObject associated
371 // to this item (iteNameId is a TreeView id, Qt stuff only).
372 XmedDataObject* dataObject =
373 (XmedDataObject*)dataModel->getDataObject(QS2S(itemNameId));
375 if ( dataObject == NULL ) {
376 LOG("WorkspaceController: WARN! No data object associated to the item "<<itemNameId);
380 // Then, we can request this data object to obtain the associated
382 MEDCALC::FieldHandler* fieldHandler = dataObject->getFieldHandler();
383 STDLOG("Field: mesh="<<fieldHandler->meshname<<" name="<<fieldHandler->fieldname);
385 // Remove the field variable from console
386 QStringList commands;
387 commands+=QString("removeFromWorkspace(accessField(%1))").arg(fieldHandler->id);
388 _consoleDriver->exec(commands);
390 // Finally, we can remove the field from tree data model and tree view
391 this->getDataTreeModel()->removeData(dataObject);
392 dataModel->removeDataObject(QS2S(itemNameId));
396 * This function export the list of specified field item to PARAVIS
397 * module. This consists in create a med file gathering the selected
398 * items, then to import this file in PARAVIS, and finally to create a
399 * scalar map of the first item to start the job.
401 void WorkspaceController::_exportItemList(QStringList itemNameIdList) {
402 XmedDataProcessor* dataProcessor = new XmedDataProcessor(this->getDataModel());
403 dataProcessor->process(itemNameIdList);
404 MEDCALC::FieldIdList_var fieldIdList = dataProcessor->getResultingFieldIdList();
405 delete dataProcessor;
407 // _GBO_ We use a temporary file to proceed with this export to
408 // paravis. I'm sure it could be better in a futur version or when I
409 // will get a better understanding of paravis API.
410 const char* tmpfilename = "/tmp/medcalc_export2paravis.med";
411 MEDFactoryClient::getDataManager()->saveFields(tmpfilename, fieldIdList);
413 // We import the whole file but create a scalar map for the first
414 // selected field only (it's just an export to continue the job in
416 XmedDataModel* dataModel = (XmedDataModel*)this->getDataModel();
417 if ( dataModel == NULL ) {
418 STDLOG("No data model associated to this tree view");
421 QString itemNameId = itemNameIdList[0];
422 XmedDataObject* dataObject = (XmedDataObject*)dataModel->getDataObject(QS2S(itemNameId));
423 if ( dataObject == NULL ) {
424 LOG("WorkspaceController: WARN! No data object associated to the item "<<itemNameId);
427 MEDCALC::FieldHandler* fieldHandler = dataObject->getFieldHandler();
428 QStringList commands;
430 commands+=QString("from xmed.driver_pvis import pvis_scalarmap");
431 commands+=QString("pvis_scalarmap('%1','%2','%3',%4,%5)")
433 .arg(QString(fieldHandler->meshname))
434 .arg(QString(fieldHandler->fieldname))
435 .arg(fieldHandler->type)
436 .arg(fieldHandler->iteration);
438 commands += "print 'Not implemented yet'";
439 _consoleDriver->exec(commands);
444 * This function sends a request to the SALOME data visualisation
445 * (module VISU or PARAVIS) for displaying a scalar map of the fields
446 * associated to the model items in the specified list.
449 void WorkspaceController::_viewItemList(QStringList itemNameIdList) {
451 // __GBO__: In this version, we consider only the first field in the selection
452 QString itemNameId = itemNameIdList[0];
454 XmedDataModel* dataModel = (XmedDataModel*)this->getDataModel();
455 if ( dataModel == NULL ) {
456 LOG("No data model associated to this tree view");
460 // We can request the dataModel to obtain the dataObject associated
461 // to this item (iteNameId is a TreeView id, Qt stuff only).
462 XmedDataObject* dataObject =
463 (XmedDataObject*)dataModel->getDataObject(QS2S(itemNameId));
464 if ( dataObject == NULL ) {
465 LOG("WorkspaceController: WARN! No data object associated to the item "<<itemNameId);
469 // Then, we can request this data object to obtain the associated
471 MEDCALC::FieldHandler* fieldHandler = dataObject->getFieldHandler();
473 // And finally, we can create the set of medcalc instructions to
474 // generate the scalar map on this field.
475 QStringList commands;
476 //commands+=QString("view(accessField(%1))").arg(fieldHandler->id);
477 commands += "print 'Not implemented yet'";
478 _consoleDriver->exec(commands);
482 * This slot can process the event coming from the
483 * DatasourceController. The connection between the datasource signal
484 * and this slot is realized by the main class MEDModule.
486 void WorkspaceController::processDatasourceEvent(const DatasourceEvent* event) {
487 XmedDataModel* dataModel = (XmedDataModel*)this->getDataModel();
488 if ( dataModel == NULL ) {
489 STDLOG("No data model associated to this tree view");
494 // __GBO__ To know what to do we should test the type, because the
495 // object could be a mesh, a timeseries or a single field. We test
496 // here the case of a single field. Moreover, there could have
497 // options such that "change the underlying mesh".
500 XmedDataObject* dataObject = event->objectdata;
502 if ( event->eventtype == DatasourceEvent::EVENT_IMPORT_OBJECT ) {
503 std::cout << "IMPORT object in workspace: " << dataObject->toString() << std::endl;
504 STDLOG("IMPORT object in workspace:\n"<<dataObject->toString());
505 // _GBO_ QUESTION: tag automatically the object as a peristant object ??
506 // We first add the data object to the internal data model
507 dataModel->addDataObject(dataObject);
508 // Then we request the tree view to consider this new object
509 this->getDataTreeModel()->addData(dataObject);
511 else if ( event->eventtype == DatasourceEvent::EVENT_USE_OBJECT ) {
512 STDLOG("USE object in workspace:\n"<<dataObject->toString());
513 // We first add the data object to the internal data model
514 dataModel->addDataObject(dataObject);
515 // Then we request the tree view to consider this new object
516 this->getDataTreeModel()->addData(dataObject);
518 // We define a proxy for this object in the tui console.
519 STDLOG("Define a proxy variable in the console with name : "<<QCHARSTAR(event->objectalias));
520 bool askForOptions = false;
521 _importFieldIntoConsole(dataObject->getFieldHandler(),
523 QCHARSTAR(event->objectalias));
525 else if ( event->eventtype == DatasourceEvent::EVENT_ADD_DATASOURCE ) {
526 QStringList commands;
527 commands += QString("source_id = medcalc.LoadDataSource('%1')").arg(event->objectalias);
528 commands += QString("source_id");
529 _consoleDriver->exec(commands);
531 else if ( event->eventtype == DatasourceEvent::EVENT_ADD_IMAGE_AS_DATASOURCE ) {
532 QStringList commands;
533 commands += QString("source_id = medcalc.LoadImageAsDataSource('%1')").arg(event->objectalias);
534 commands += QString("source_id");
535 _consoleDriver->exec(commands);
538 STDLOG("The event "<<event->eventtype<<" is not implemented yet");
543 WorkspaceController::processProcessingEvent(const ProcessingEvent* event)
545 XmedDataModel* dataModel = (XmedDataModel*)this->getDataModel();
546 if ( dataModel == NULL ) {
547 STDLOG("No data model associated to this tree view");
552 // __GBO__ To know what to do we should test the type, because the
553 // object could be a mesh, a timeseries or a single field. We test
554 // here the case of a single field. Moreover, there could have
555 // options such that "change the underlying mesh".
558 XmedDataObject* dataObject = event->objectdata;
560 if ( event->eventtype == ProcessingEvent::EVENT_IMPORT_OBJECT ) {
561 std::cout << "IMPORT object in workspace: " << dataObject->toString() << std::endl;
562 STDLOG("IMPORT object in workspace:\n"<<dataObject->toString());
563 // _GBO_ QUESTION: tag automatically the object as a peristant object ??
564 // We first add the data object to the internal data model
565 dataModel->addDataObject(dataObject);
566 // Then we request the tree view to consider this new object
567 this->getDataTreeModel()->addData(dataObject);
571 void WorkspaceController::OnSaveWorkspace() {
573 // Dialog to get the filename where the workspace must be saved into
575 filter.append(tr("FILE_FILTER_MED"));
577 QString filename = SUIT_FileDlg::getFileName(_salomeModule->getApp()->desktop(),
580 tr("SAVE_WORKSPACE_DATA"),
583 if ( filename.isEmpty() ) return;
585 STDLOG("OnWorkspaceSave: save the workspace in the file " << QCHARSTAR(filename));
586 QStringList commands;
587 commands+=QString("saveWorkspace('%1')").arg(filename);
588 _consoleDriver->exec(commands);
591 #include <QMessageBox>
592 void WorkspaceController::OnCleanWorkspace() {
593 // Remove field from console
594 QStringList commands;
595 commands += QString("cleanWorkspace()");
596 _consoleDriver->exec(commands);