Salome HOME
menu et icones. en cours
[modules/hydrosolver.git] / src / HYDROGUI / HYDROSOLVERGUI.py
1 #  Copyright (C) 2012-2013 EDF
2 #
3 #  This file is part of SALOME HYDRO module.
4 #
5 #  SALOME HYDRO module is free software: you can redistribute it and/or modify
6 #  it under the terms of the GNU General Public License as published by
7 #  the Free Software Foundation, either version 3 of the License, or
8 #  (at your option) any later version.
9 #
10 #  SALOME HYDRO module is distributed in the hope that it will be useful,
11 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 #  GNU General Public License for more details.
14 #
15 #  You should have received a copy of the GNU General Public License
16 #  along with SALOME HYDRO module.  If not, see <http://www.gnu.org/licenses/>.
17
18 import sys
19 import os
20 import traceback
21 import logging
22
23 from PyQt5.QtWidgets import QMessageBox, QApplication, QDialog
24
25 import salome
26 import SALOME
27 import SalomePyQt
28 sgPyQt = SalomePyQt.SalomePyQt()
29 from salome.kernel.studyedit import getStudyEditor
30 from salome.kernel.logger import Logger
31 from salome.kernel import termcolor
32 logger = Logger("HYDROGUI", color = termcolor.BLUE)
33 #logger.setLevel(logging.ERROR)
34
35 import HYDROSOLVER_ORB
36 from salome.hydro.interpolz_gui import InterpolzDlg
37 from salome.hydro.gui_utils import HSGUIException, wait_cursor, get_and_check_selected_file_path
38 import salome.hydro.study as hydro_study
39 from salome.hydro.mascaret.eficas.appli import EficasForMascaretAppli
40 from salome.hydro.telemac2d.eficas.appli import EficasForTelemac2DAppli
41 from salome.hydro.coupling1d2d.eficas.appli import EficasForCoupling1D2DAppli
42 from salome.hydro.pytel.gui import create_case_pytel,  run_selected_case_pytel, edit_selected_case_pytel,  generate_job_for_selected_case_pytel
43 #from salome.hydro.boundary_conditions.eficas.appli import EficasForBoundaryConditionsAppli
44 from eficasSalome import runEficas
45
46 import BndConditionsDialog
47
48 ################################################
49 # GUI context class
50 # Used to store actions, menus, toolbars, etc...
51 ################################################
52
53 class GUIcontext:
54     # menus/toolbars/actions IDs
55     HYDRO_MENU_ID = 90
56     CREATE_MASCARET_CASE_ID = 941
57     RUN_MASCARET_ID = 942
58     EDIT_MASCARET_CASE_ID = 943
59     SHOW_LOG_ID = 944
60     CREATE_TELEMAC2D_CASE_ID = 945
61     RUN_TELEMAC2D_ID = 946
62     EDIT_TELEMAC2D_CASE_ID = 947
63     CREATE_COUPLING1D2D_CASE_ID = 948
64     EDIT_COUPLING1D2D_CASE_ID = 949
65     OPEN_SCHEMA_IN_YACS_ID = 950
66     CREATE_PYTEL_CASE_ID = 951
67     RUN_PYTEL_ID = 952
68     EDIT_PYTEL_CASE_ID = 953
69     GENERATE_JOB = 954
70     #DEFINE_BOUNDARY_CONDITIONS_ID = 955
71     DEFINE_CAS_FILE = 955
72     EDIT_BOUNDARY_CONDITIONS_FILE_ID = 956
73     GENERATE_INTERPOLZ_PY_ID = 957
74
75     def __init__( self ):
76         # create top-level menu
77         mid = sgPyQt.createMenu( "Hydro", -1, GUIcontext.HYDRO_MENU_ID, sgPyQt.defaultMenuGroup() )
78         # create toolbar
79         tid = sgPyQt.createTool( "Hydro" )
80         # create actions and fill menu and toolbar with actions
81         a = sgPyQt.createAction( GUIcontext.EDIT_BOUNDARY_CONDITIONS_FILE_ID,
82                                  "Edit boundary conditions file", "Edit boundary conditions file",
83                                  "Create/edit the boundary conditions file for Telemac",
84                                  "edit_boundary_conditions_file.png" )
85         sgPyQt.createMenu( a, mid )
86         sgPyQt.createTool( a, tid )
87
88
89         a = sgPyQt.createAction( GUIcontext.CREATE_MASCARET_CASE_ID,
90                                  "Create Mascaret case", "Create Mascaret case",
91                                  "Create a new Mascaret case", "create_case1d.png" )
92         sgPyQt.createMenu( a, mid )
93         sgPyQt.createTool( a, tid )
94
95         a = sgPyQt.createAction( GUIcontext.CREATE_TELEMAC2D_CASE_ID,
96                                  "Create Telemac2D case", "Create Telemac2D case",
97                                  "Create a new Telemac2D case", "create_case2d.png" )
98         sgPyQt.createMenu( a, mid )
99         sgPyQt.createTool( a, tid )
100
101         a = sgPyQt.createAction( GUIcontext.CREATE_COUPLING1D2D_CASE_ID,
102                                  "Create 1D / 2D coupling", "Create 1D / 2D coupling",
103                                  "Create a new 1D / 2D coupling", "create_case_couplage.png" )
104         sgPyQt.createMenu( a, mid )
105         sgPyQt.createTool( a, tid )
106
107         a = sgPyQt.createSeparator()
108
109         a = sgPyQt.createAction( GUIcontext.CREATE_PYTEL_CASE_ID,
110                                 "Create case for Pytel execution", "Create case for Pytel execution",
111                                 "Create a new case for Pytel execution", "create_case_pytel.png" )
112
113         sgPyQt.createMenu( a, mid )
114         sgPyQt.createTool( a, tid )
115
116         a = sgPyQt.createAction( GUIcontext.EDIT_PYTEL_CASE_ID,
117                                 "Edit case for Pytel execution", "Edit case for Pytel execution",
118                                 "Edit a new case for Pytel execution", "edit_case_pytel.png" )
119
120         sgPyQt.createMenu( a, mid )
121         sgPyQt.createTool( a, tid )
122
123         a = sgPyQt.createSeparator()
124
125         a = sgPyQt.createAction( GUIcontext.GENERATE_INTERPOLZ_PY_ID,
126                                  "Generate interpolz.py", "Generate interpolz.py",
127                                  "Generate interpolation script from the template", "generate_interpolz_py.png" )
128
129         sgPyQt.createMenu( a, mid )
130         sgPyQt.createTool( a, tid )
131
132         a = sgPyQt.createSeparator()
133         a = sgPyQt.createAction( GUIcontext.DEFINE_CAS_FILE,
134                                  "Edit cas file", "Edit cas file",
135                                  "Create/edit a .cas file for Telemac",
136                                  "define_cas_file.png" )
137         sgPyQt.createMenu( a, mid )
138         sgPyQt.createTool( a, tid )
139
140
141         # the following action are used in context popup
142         sgPyQt.createAction( GUIcontext.RUN_MASCARET_ID, "Compute case", "Compute case",
143                              "Run Mascaret solver to compute the case" )
144         sgPyQt.createAction( GUIcontext.EDIT_MASCARET_CASE_ID, "Edit case", "Edit case",
145                              "Edit the selected Mascaret case" )
146         sgPyQt.createAction( GUIcontext.SHOW_LOG_ID, "Show log", "Show log",
147                              "Show the log for the selected variable" )
148
149         sgPyQt.createAction( GUIcontext.RUN_TELEMAC2D_ID, "Compute case", "Compute case",
150                              "Run Telemac2D solver to compute the case" )
151         sgPyQt.createAction( GUIcontext.EDIT_TELEMAC2D_CASE_ID, "Edit case", "Edit case",
152                              "Edit the selected Telemac2D case" )
153
154         sgPyQt.createAction( GUIcontext.EDIT_COUPLING1D2D_CASE_ID, "Edit coupling", "Edit coupling",
155                              "Edit the selected 1D / 2D coupling" )
156         sgPyQt.createAction( GUIcontext.OPEN_SCHEMA_IN_YACS_ID, "Open schema in YACS", "Open schema in YACS",
157                              "Open the selected 1D / 2D coupling schema in YACS" )
158
159         sgPyQt.createAction( GUIcontext.RUN_PYTEL_ID, "Compute case", "Compute case",
160                             "Run Pytel launcher to compute the case" )
161         sgPyQt.createAction( GUIcontext.EDIT_PYTEL_CASE_ID, "Edit case", "Edit case",
162                             "Edit the selected Pytel case" )
163         sgPyQt.createAction( GUIcontext.GENERATE_JOB, "Generate batch job", "Generate batch job",
164                              "Generate a batch job to run the selected case")
165
166         sgPyQt.createAction( GUIcontext.DEFINE_CAS_FILE, "Edit .cas file", "Edit .cas file",
167                              "Edit .cas file")
168
169
170 ################################################
171 # Global variables
172 ################################################
173
174 # study-to-context map
175 __study2context__   = {}
176 # current context
177 __current_context__ = None
178 # object counter
179 __objectid__ = 0
180
181 ################################################
182 # Internal methods
183 ################################################
184
185 ###
186 # get active study ID
187 ###
188 def _getStudyId():
189     return sgPyQt.getStudyId()
190
191 ###
192 # get active study
193 ###
194 def _getStudy():
195     studyId = _getStudyId()
196     study = getStudyManager().GetStudyByID( studyId )
197     return study
198
199 ###
200 # returns True if object has children
201 ###
202 def _hasChildren( sobj ):
203     if sobj:
204         study = _getStudy()
205         iter  = study.NewChildIterator( sobj )
206         while iter.More():
207             name = iter.Value().GetName()
208             if name:
209                 return True
210             iter.Next()
211             pass
212         pass
213     return False
214
215 ###
216 # get current GUI context
217 ###
218 def _getContext():
219     global __current_context__
220     return __current_context__
221
222 ###
223 # set and return current GUI context
224 # study ID is passed as parameter
225 ###
226 def _setContext( studyID ):
227     global __study2context__, __current_context__
228     if not __study2context__.has_key(studyID):
229         __study2context__[studyID] = GUIcontext()
230         pass
231     __current_context__ = __study2context__[studyID]
232     return __current_context__
233
234 ###
235 # increment object counter in the map
236 ###
237 def _incObjToMap( m, id ):
238     if id not in m: m[id] = 0
239     m[id] += 1
240     pass
241
242 ################################################
243 # Callback functions
244 ################################################
245
246 # called when module is activated
247 # returns True if activating is successfull and False otherwise
248 def activate():
249     ctx = _setContext( _getStudyId() )
250     return True
251
252 # called when module is deactivated
253 def deactivate():
254     pass
255
256 # called when active study is changed
257 # active study ID is passed as parameter
258 def activeStudyChanged( studyID ):
259     ctx = _setContext( _getStudyId() )
260     pass
261
262 # called when popup menu is invoked
263 # popup menu and menu context are passed as parameters
264 def createPopupMenu(popup, context):
265     ed = getStudyEditor()
266     _setContext(ed.studyId)
267     if salome.sg.SelectedCount() == 1:
268         # one object is selected
269         sobj = ed.study.FindObjectID(salome.sg.getSelected(0))
270         selectedType = ed.getTypeId(sobj)
271         if selectedType == hydro_study.MASCARET_CASE_TYPE_ID:
272             popup.addAction(sgPyQt.action(GUIcontext.EDIT_MASCARET_CASE_ID))
273             popup.addAction(sgPyQt.action(GUIcontext.RUN_MASCARET_ID))
274         elif selectedType == hydro_study.TELEMAC2D_CASE_TYPE_ID:
275             popup.addAction(sgPyQt.action(GUIcontext.EDIT_TELEMAC2D_CASE_ID))
276             popup.addAction(sgPyQt.action(GUIcontext.RUN_TELEMAC2D_ID))
277         elif selectedType == hydro_study.COUPLING1D2D_CASE_TYPE_ID:
278             popup.addAction(sgPyQt.action(GUIcontext.EDIT_COUPLING1D2D_CASE_ID))
279             popup.addAction(sgPyQt.action(GUIcontext.OPEN_SCHEMA_IN_YACS_ID))
280         elif selectedType == hydro_study.LOG_TYPE_ID:
281             popup.addAction(sgPyQt.action(GUIcontext.SHOW_LOG_ID))
282         elif selectedType == hydro_study.PYTEL_CASE_TYPE_ID:
283            popup.addAction(sgPyQt.action(GUIcontext.EDIT_PYTEL_CASE_ID))
284            popup.addAction(sgPyQt.action(GUIcontext.RUN_PYTEL_ID))
285            popup.addAction(sgPyQt.action(GUIcontext.GENERATE_JOB))
286
287 # called when GUI action is activated
288 # action ID is passed as parameter
289 def OnGUIEvent( commandID ):
290     try:
291         dict_command[commandID]()
292     except HSGUIException, exc:
293         QMessageBox.critical(sgPyQt.getDesktop(),
294                                    QApplication.translate("OnGUIEvent", "Error"),
295                                    unicode(exc))
296     except:
297         logger.exception("Unhandled exception caught in HYDROSOLVER GUI")
298         msg = QApplication.translate("OnGUIEvent",
299             "Unhandled error happened in HYDROSOLVER module. Please report a bug on "
300             '<a href="https://forge-pleiade.der.edf.fr/projects/salome-rex/issues">SALOME bugtracker</a>, '
301             'category "HYDROSOLVER", describing how you got there and including the following traceback:')
302         msgBox = QMessageBox(QMessageBox.Critical,
303                                    QApplication.translate("OnGUIEvent", "Error"),
304                                    msg,
305                                    parent = sgPyQt.getDesktop())
306         msgBox.setDetailedText(traceback.format_exc())
307         msgBox.exec_()
308
309 ################################################
310 # GUI actions implementation
311 ################################################
312
313 # --------------------------------------------------------------------------------------- #
314 # Dialog box for text display (deprecated, it was only used in the calcium coupling test) #
315 # --------------------------------------------------------------------------------------- #
316 """
317 from TextDisplayDialog import Ui_TextDisplayDialog
318
319 class MyTextDisplayDialog(Ui_TextDisplayDialog, QDialog):
320
321     def __init__(self, parent = None, modal = 0):
322         QDialog.__init__(self, parent)
323         Ui_TextDisplayDialog.__init__(self)
324         self.setupUi(self)
325         (self.closeButton.clicked.connect(self.close)
326
327     def set_log(self, log):
328         self.contentTextEdit.setPlainText(log)
329         self.setWindowTitle("Coupling log")
330 """
331
332 ###
333 # Open Eficas for Mascaret to create a new case
334 ###
335 def create_mascaret_case():
336     EficasForMascaretAppli()
337
338 ###
339 # Open Eficas for Mascaret to edit the selected case
340 ###
341 def edit_mascaret_case():
342     EficasForMascaretAppli(get_and_check_selected_file_path())
343
344 # Run Mascaret on selected case
345 def run_mascaret():
346     try:
347         with wait_cursor:
348             ed = hydro_study.HydroStudyEditor()
349             sobj = ed.editor.study.FindObjectID(salome.sg.getSelected(0))
350             (file_list, lig_file, input_vars, output_vars) = ed.get_mascaret_params_from_case(sobj)
351             var_names = [var["NOM"].strip() for var in output_vars]
352             mascaret_vars = [var["VARIABLE_MASCARET"].strip() for var in output_vars]
353             engine = salome.lcc.FindOrLoadComponent("FactoryServer", "MASCARET")
354             logger.debug("Calling MASCARET.Compute(%s, %s, %s)" %
355                          (file_list, lig_file, mascaret_vars))
356             output_values = engine.Compute(file_list, lig_file, mascaret_vars)
357             ed.add_results_to_mascaret_case(sobj, var_names, output_values)
358             salome.sg.updateObjBrowser( 0 )
359     except SALOME.SALOME_Exception, exc:
360         salome.sg.updateObjBrowser( 0 )
361         msg = unicode(QApplication.translate("run_mascaret",
362                       "An error happened while trying to run Mascaret:"))
363         msg += "\n" + exc.details.text
364         raise HSGUIException(msg)
365
366 # Display selected log (deprecated, it was only used in the calcium coupling test)
367 def show_log():
368     ed = hydro_study.HydroStudyEditor()
369     sobj = ed.editor.study.FindObjectID(salome.sg.getSelected(0))
370     if sobj is not None:
371         (found, attr) = getStudyEditor().builder.FindAttribute(
372                                                 sobj, "AttributeParameter")
373         log = attr.GetString("log")
374         dialog = MyTextDisplayDialog(sgPyQt.getDesktop())
375         dialog.set_log(log)
376         dialog.show()
377
378 ###
379 # Open Eficas for Telemac2D to create a new case
380 ###
381 def create_telemac2d_case():
382     EficasForTelemac2DAppli()
383
384 ###
385 # Open Eficas for Telemac2D to edit the selected case
386 ###
387 def edit_telemac2d_case():
388     EficasForTelemac2DAppli(get_and_check_selected_file_path())
389
390 # Run Telemac2D on selected case
391 def run_telemac2d():
392     engine = None
393     try:
394         with wait_cursor:
395             ed = hydro_study.HydroStudyEditor()
396             entry = salome.sg.getSelected(0)
397             engine = salome.lcc.FindOrLoadComponent("Telemac2DContainer", "TELEMAC2D")
398             engine.Compute(ed.editor.studyId, entry)
399             # Stop container after execution so that we can use another fortran user file in the next run
400             engine.GetContainerRef().Shutdown()
401     except SALOME.SALOME_Exception, exc:
402         if engine is not None:
403             engine.GetContainerRef().Shutdown()
404         msg = unicode(QApplication.translate("run_telemac2d",
405                       "An error happened while trying to run Telemac2D:"))
406         msg += "\n" + exc.details.text
407         raise HSGUIException(msg)
408     except Exception, exc:
409         logger.exception("An error happened in the computation (Telemac2D probably crashed).")
410         try:
411             engine.GetContainerRef().Shutdown()
412         except:
413             pass
414         msg = unicode(QApplication.translate("run_telemac2d",
415                       "An error happened in the computation (Telemac2D probably crashed, "
416                       "see logs and listing files for more details):"))
417         msg += "\n" + unicode(exc)
418         raise HSGUIException(msg)
419
420 ###
421 # Open Eficas for 1D / 2D Coupling to create a new coupling case
422 ###
423 def create_coupling1d2d_case():
424     EficasForCoupling1D2DAppli()
425
426 ###
427 # Open Eficas for 1D / 2D Coupling to edit the selected coupling case
428 ###
429 def edit_coupling1d2d_case():
430     EficasForCoupling1D2DAppli(get_and_check_selected_file_path())
431
432 def open_schema_in_yacs():
433     ed = getStudyEditor()
434     sobj = ed.study.FindObjectID(salome.sg.getSelected(0))
435     filename = sobj.GetComment()
436     if filename.endswith(".comm"):
437         filename = filename[:-5] + ".xml"
438     if not os.path.isfile(filename):
439         raise HSGUIException(QApplication.translate("open_schema_in_yacs",
440                                   "Schema file %1 does not exist").arg(filename))
441     yg = salome.ImportComponentGUI("YACS")
442     yg.loadSchema(filename)
443
444 ###
445 # Open Eficas for boundary conditions definition
446 ###
447 #def define_boundary_conditions():
448 #    EficasForBoundaryConditionsAppli()
449
450 ###
451 # Open dialog for boundary conditions edition
452 ###
453 def edit_boundary_conditions_file():
454     desktop = sgPyQt.getDesktop()
455     dlg = BndConditionsDialog.BoundaryConditionsDialog(desktop)
456     dlg.exec_()
457
458 ###
459 # Open dialog for interpolz.py script generation
460 ###
461 def generate_interpolz_py():
462     desktop = sgPyQt.getDesktop()
463     dlg = InterpolzDlg(desktop)
464     dlg.show()
465
466 ###
467 # Open dialog for boundary conditions edition
468 ###
469 def eficas_for_cas_Telemac2D():
470    runEficas(code='TELEMAC')
471 ###
472 # Commands dictionary
473 ###
474 dict_command = {
475     GUIcontext.CREATE_MASCARET_CASE_ID: create_mascaret_case,
476     GUIcontext.RUN_MASCARET_ID: run_mascaret,
477     GUIcontext.EDIT_MASCARET_CASE_ID: edit_mascaret_case,
478     GUIcontext.SHOW_LOG_ID: show_log,
479     GUIcontext.CREATE_TELEMAC2D_CASE_ID: create_telemac2d_case,
480     GUIcontext.RUN_TELEMAC2D_ID: run_telemac2d,
481     GUIcontext.EDIT_TELEMAC2D_CASE_ID: edit_telemac2d_case,
482     GUIcontext.CREATE_COUPLING1D2D_CASE_ID: create_coupling1d2d_case,
483     GUIcontext.EDIT_COUPLING1D2D_CASE_ID: edit_coupling1d2d_case,
484     GUIcontext.OPEN_SCHEMA_IN_YACS_ID: open_schema_in_yacs,
485     GUIcontext.CREATE_PYTEL_CASE_ID: create_case_pytel,
486     GUIcontext.RUN_PYTEL_ID: run_selected_case_pytel,
487     GUIcontext.EDIT_PYTEL_CASE_ID: edit_selected_case_pytel,
488     GUIcontext.GENERATE_JOB: generate_job_for_selected_case_pytel,
489     #GUIcontext.DEFINE_BOUNDARY_CONDITIONS_ID: define_boundary_conditions,
490     GUIcontext.EDIT_BOUNDARY_CONDITIONS_FILE_ID: edit_boundary_conditions_file,
491     GUIcontext.GENERATE_INTERPOLZ_PY_ID: generate_interpolz_py,
492     GUIcontext.DEFINE_CAS_FILE: eficas_for_cas_Telemac2D,
493     }