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