1 // Copyright (C) 2007-2015 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 "XmedDataModel.hxx"
26 #include "DlgAlias.hxx"
28 #include <SALOMEconfig.h>
29 #include CORBA_CLIENT_HEADER(MEDEventListener)
31 #include <SalomeApp_Application.h>
32 #include <SALOME_LifeCycleCORBA.hxx>
33 #include <SUIT_FileDlg.h>
34 #include <SUIT_Desktop.h>
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.
41 WorkspaceController::WorkspaceController(StandardApp_Module * salomeModule)
42 : TreeGuiManager(salomeModule->getApp(), "Workspace")
44 _salomeModule = salomeModule;
45 getDockWidgets()->getDockWidget()->setObjectName("medWorkspaceDock");
47 this->tabifyDockWidgets(false);
49 // -------------------------------------------------------------
50 // Setup the MEDEventListener to manage notification from the
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
60 _medEventListener = MEDEventListener_i::getInstance();
61 MEDCALC::MEDEventListener_ptr medEventListenerServant = _medEventListener->_this();
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);
72 // Connect the signals emitted from the MEDEventListener to slot of
74 connect(_medEventListener, SIGNAL(medEventSignal(const MEDCALC::MedEvent*)),
75 this, SLOT(processMedEvent(const MEDCALC::MedEvent*)));
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.
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"));
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
95 _consoleDriver = new XmedConsoleDriver(salomeApp);
96 _consoleDriver->setup();
99 WorkspaceController::~WorkspaceController() {
100 MEDEventListener_i::release();
104 * This creates the GUI actions for driving the Workspace. The
105 * WorkspaceController creates itself this actions and implements the
108 void WorkspaceController::createActions() {
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);
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);
124 * Implementation of the slot processItemList inherited from TreeGuiManager
126 void WorkspaceController::processItemList(QStringList itemNameIdList, int actionId) {
127 if ( actionId == _actionIds.display ) {
128 STDLOG("WorkspaceController::processItemList: display");
129 this->_viewItemList(itemNameIdList);
131 else if ( actionId == _actionIds.useInTui ) {
132 STDLOG("WorkspaceController::processItemList: use");
133 this->_importItemList(itemNameIdList);
135 else if ( actionId == _actionIds.exportToPv ) {
136 STDLOG("WorkspaceController::processItemList: export");
137 this->_exportItemList(itemNameIdList);
139 else if ( actionId == _actionIds.save ) {
140 STDLOG("WorkspaceController::processItemList: save");
141 this->_saveItemList(itemNameIdList);
143 else if ( actionId == _actionIds.remove ) {
144 STDLOG("WorkspaceController::processItemList: remove");
145 this->_removeItemList(itemNameIdList);
148 STDLOG("WorkspaceController::processItemList: ERR : action unknown ");
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".
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);
168 * This function is the unit function used to import field in the
169 * console (see _importItemList).
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");
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));
183 if ( dataObject == NULL ) {
184 LOG("WorkspaceController: WARN! No data object associated to the item "<<itemNameId);
188 // Then, we can request this data object to obtain the associated
190 MEDCALC::FieldHandler * fieldHandler = dataObject->getFieldHandler();
191 STDLOG("Field: mesh="<<fieldHandler->meshname<<" name="<<fieldHandler->fieldname);
193 // Finally, we can import the field
194 bool askForOptions = true;
195 _importFieldIntoConsole(fieldHandler, askForOptions);
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
205 void WorkspaceController::_importFieldIntoConsole(MEDCALC::FieldHandler * fieldHandler,
209 STDLOG("alias="<<alias);
211 // By default, the alias is the name of the field
212 QString *effectiveAlias;
213 if ( alias == NULL ) {
214 effectiveAlias = new QString(fieldHandler->fieldname);
217 effectiveAlias = new QString(alias);
220 // We can propose to the user to specify some additionnal
221 // informations concerning what must be imported.
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 ) {
228 dialog.setAlias(*effectiveAlias);
229 int choice = dialog.exec();
230 if ( choice == QDialog::Rejected ) {
231 // The user decides to cancel the operation
234 *effectiveAlias = dialog.getAlias();
238 // Then, the list of python commands can be written and executed to
239 // define the field in the console
241 QStringList commands;
242 commands+=QString("%1=medcalc.newFieldProxy(fieldHandlerId=%2)")
243 .arg(*effectiveAlias)
244 .arg(fieldHandler->id);
246 _consoleDriver->exec(commands);
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.
254 void WorkspaceController::processMedEvent(const MEDCALC::MedEvent * event) {
255 STDLOG("WorkspaceController::processMedEvent");
256 STDLOG("fieldid :"<<event->fieldid);
258 XmedDataModel * dataModel = (XmedDataModel *)this->getDataModel();
259 if ( dataModel == NULL ) {
260 STDLOG("No data model associated to this tree view");
264 if ( event->type == MEDCALC::EVENT_UPDATE_FIELD ) {
265 std::cout << "WorkspaceController::processMedEvent[MEDCALC::EVENT_UPDATE_FIELD]: Not implemented yet";
267 else if ( event->type == MEDCALC::EVENT_PUT_IN_WORKSPACE ) {
268 STDLOG("add new field");
269 MEDCALC::FieldHandler * fieldHandler =
270 MEDFactoryClient::getDataManager()->getFieldHandler(event->fieldid);
272 XmedDataObject * dataObject = (XmedDataObject *)dataModel->newDataObject();
273 dataObject->setFieldHandler(*fieldHandler);
274 this->getDataTreeModel()->addData(dataObject);
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->fieldid) {
282 std::string itemNameId = obj->getNameId();
283 this->getDataTreeModel()->removeData(obj);
284 dataModel->removeDataObject(itemNameId);
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);
299 else if ( event->type == MEDCALC::EVENT_ADD_DATASOURCE ) {
300 emit workspaceSignal(event); // forward to DatasourceController
306 * This function save a list of fields in a med file. The med file
307 * name is requested to the user using a file chooser dialog box
309 void WorkspaceController::_saveItemList(QStringList itemNameIdList) {
310 XmedDataProcessor * dataProcessor = new XmedDataProcessor(this->getDataModel());
311 dataProcessor->process(itemNameIdList);
312 MEDCALC::FieldIdList_var fieldIdList = dataProcessor->getResultingFieldIdList();
313 delete dataProcessor;
316 filter.append(tr("FILE_FILTER_MED"));
317 QString filename = SUIT_FileDlg::getFileName(_salomeModule->getApp()->desktop(),
320 tr("SAVE_SELECTED_FIELDS"),
323 if ( filename.isEmpty() ) return;
325 MEDFactoryClient::getDataManager()->saveFields(QCHARSTAR(filename), fieldIdList);
329 * This function remove the selected item from workspace.
331 void WorkspaceController::_removeItemList(QStringList itemNameIdList) {
332 XmedDataModel * dataModel = (XmedDataModel *)this->getDataModel();
333 if ( dataModel == NULL ) {
334 LOG("No data model associated to this tree view");
338 // __GBO__: In this version, we consider only the first field in the selection
339 QString itemNameId = itemNameIdList[0];
341 // We can request the dataModel to obtain the dataObject associated
342 // to this item (iteNameId is a TreeView id, Qt stuff only).
343 XmedDataObject * dataObject =
344 (XmedDataObject *)dataModel->getDataObject(QS2S(itemNameId));
346 if ( dataObject == NULL ) {
347 LOG("WorkspaceController: WARN! No data object associated to the item "<<itemNameId);
351 // Then, we can request this data object to obtain the associated
353 MEDCALC::FieldHandler * fieldHandler = dataObject->getFieldHandler();
354 STDLOG("Field: mesh="<<fieldHandler->meshname<<" name="<<fieldHandler->fieldname);
356 // Remove the field variable from console
357 QStringList commands;
358 commands+=QString("removeFromWorkspace(accessField(%1))").arg(fieldHandler->id);
359 _consoleDriver->exec(commands);
361 // Finally, we can remove the field from tree data model and tree view
362 this->getDataTreeModel()->removeData(dataObject);
363 dataModel->removeDataObject(QS2S(itemNameId));
367 * This function export the list of specified field item to PARAVIS
368 * module. This consists in create a med file gathering the selected
369 * items, then to import this file in PARAVIS, and finally to create a
370 * scalar map of the first item to start the job.
372 void WorkspaceController::_exportItemList(QStringList itemNameIdList) {
373 XmedDataProcessor * dataProcessor = new XmedDataProcessor(this->getDataModel());
374 dataProcessor->process(itemNameIdList);
375 MEDCALC::FieldIdList_var fieldIdList = dataProcessor->getResultingFieldIdList();
376 delete dataProcessor;
378 // _GBO_ We use a temporary file to proceed with this export to
379 // paravis. I'm sure it could be better in a futur version or when I
380 // will get a better understanding of paravis API.
381 const char * tmpfilename = "/tmp/medcalc_export2paravis.med";
382 MEDFactoryClient::getDataManager()->saveFields(tmpfilename, fieldIdList);
384 // We import the whole file but create a scalar map for the first
385 // selected field only (it's just an export to continue the job in
387 XmedDataModel * dataModel = (XmedDataModel *)this->getDataModel();
388 if ( dataModel == NULL ) {
389 STDLOG("No data model associated to this tree view");
392 QString itemNameId = itemNameIdList[0];
393 XmedDataObject * dataObject = (XmedDataObject *)dataModel->getDataObject(QS2S(itemNameId));
394 if ( dataObject == NULL ) {
395 LOG("WorkspaceController: WARN! No data object associated to the item "<<itemNameId);
398 MEDCALC::FieldHandler * fieldHandler = dataObject->getFieldHandler();
399 QStringList commands;
401 commands+=QString("from xmed.driver_pvis import pvis_scalarmap");
402 commands+=QString("pvis_scalarmap('%1','%2','%3',%4,%5)")
404 .arg(QString(fieldHandler->meshname))
405 .arg(QString(fieldHandler->fieldname))
406 .arg(fieldHandler->type)
407 .arg(fieldHandler->iteration);
409 commands += "print 'Not implemented yet'";
410 _consoleDriver->exec(commands);
415 * This function sends a request to the SALOME data visualisation
416 * (module VISU or PARAVIS) for displaying a scalar map of the fields
417 * associated to the model items in the specified list.
420 void WorkspaceController::_viewItemList(QStringList itemNameIdList) {
422 // __GBO__: In this version, we consider only the first field in the selection
423 QString itemNameId = itemNameIdList[0];
425 XmedDataModel * dataModel = (XmedDataModel *)this->getDataModel();
426 if ( dataModel == NULL ) {
427 LOG("No data model associated to this tree view");
431 // We can request the dataModel to obtain the dataObject associated
432 // to this item (iteNameId is a TreeView id, Qt stuff only).
433 XmedDataObject * dataObject =
434 (XmedDataObject *)dataModel->getDataObject(QS2S(itemNameId));
435 if ( dataObject == NULL ) {
436 LOG("WorkspaceController: WARN! No data object associated to the item "<<itemNameId);
440 // Then, we can request this data object to obtain the associated
442 MEDCALC::FieldHandler * fieldHandler = dataObject->getFieldHandler();
444 // And finally, we can create the set of medcalc instructions to
445 // generate the scalar map on this field.
446 QStringList commands;
447 //commands+=QString("view(accessField(%1))").arg(fieldHandler->id);
448 commands += "print 'Not implemented yet'";
449 _consoleDriver->exec(commands);
453 * This slot can process the event coming from the
454 * DatasourceController. The connection between the datasource signal
455 * and this slot is realized by the main class MEDModule.
457 void WorkspaceController::processDatasourceEvent(const DatasourceEvent * event) {
458 XmedDataModel * dataModel = (XmedDataModel *)this->getDataModel();
459 if ( dataModel == NULL ) {
460 STDLOG("No data model associated to this tree view");
465 // __GBO__ To know what to do we should test the type, because the
466 // object could be a mesh, a timeseries or a single field. We test
467 // here the case of a single field. Moreover, there could have
468 // options such that "change the underlying mesh".
471 XmedDataObject * dataObject = event->objectdata;
473 if ( event->eventtype == DatasourceEvent::EVENT_IMPORT_OBJECT ) {
474 STDLOG("IMPORT object in workspace:\n"<<dataObject->toString());
475 // _GBO_ QUESTION: tag automatically the object as a peristant object ??
476 // We first add the data object to the internal data model
477 dataModel->addDataObject(dataObject);
478 // Then we request the tree view to consider this new object
479 this->getDataTreeModel()->addData(dataObject);
481 else if ( event->eventtype == DatasourceEvent::EVENT_USE_OBJECT ) {
482 STDLOG("USE object in workspace:\n"<<dataObject->toString());
483 // We first add the data object to the internal data model
484 dataModel->addDataObject(dataObject);
485 // Then we request the tree view to consider this new object
486 this->getDataTreeModel()->addData(dataObject);
488 // We define a proxy for this object in the tui console.
489 STDLOG("Define a proxy variable in the console with name : "<<QCHARSTAR(event->objectalias));
490 bool askForOptions = false;
491 _importFieldIntoConsole(dataObject->getFieldHandler(),
493 QCHARSTAR(event->objectalias));
495 else if ( event->eventtype == DatasourceEvent::EVENT_VIEW_OBJECT_SCALAR_MAP ) {
496 QStringList commands;
498 #define stringify( name ) # name
499 QString viewMode = stringify(MEDCALC::VIEW_MODE_NEW_LAYOUT); // :TODO: change this (get value from dedicated dialog)
500 viewMode.replace("::", ".");
502 MEDCALC::FieldHandler* fieldHandler = dataObject->getFieldHandler();
503 commands += QString("medcalc.MakeScalarMap(accessField(%1), %2)").arg(fieldHandler->id).arg(viewMode);
504 _consoleDriver->exec(commands);
506 else if ( event->eventtype == DatasourceEvent::EVENT_ADD_DATASOURCE ) {
507 QStringList commands;
508 commands += QString("medcalc.LoadDataSource('%1')").arg(event->objectalias);
509 _consoleDriver->exec(commands);
511 else if ( event->eventtype == DatasourceEvent::EVENT_ADD_IMAGE_AS_DATASOURCE ) {
512 QStringList commands;
513 commands += QString("medcalc.LoadImageAsDataSource('%1')").arg(event->objectalias);
514 _consoleDriver->exec(commands);
517 STDLOG("The event "<<event->eventtype<<" is not implemented yet");
522 void WorkspaceController::OnSaveWorkspace() {
524 // Dialog to get the filename where the workspace must be saved into
526 filter.append(tr("FILE_FILTER_MED"));
528 QString filename = SUIT_FileDlg::getFileName(_salomeModule->getApp()->desktop(),
531 tr("SAVE_WORKSPACE_DATA"),
534 if ( filename.isEmpty() ) return;
536 STDLOG("OnWorkspaceSave: save the workspace in the file " << QCHARSTAR(filename));
537 QStringList commands;
538 commands+=QString("saveWorkspace('%1')").arg(filename);
539 _consoleDriver->exec(commands);
542 #include <QMessageBox>
543 void WorkspaceController::OnCleanWorkspace() {
544 // Remove field from console
545 QStringList commands;
546 commands += QString("cleanWorkspace()");
547 _consoleDriver->exec(commands);