Salome HOME
b2783648a90e70e4ba09bb422bc12e13a64c7a27
[modules/hydrosolver.git] / src / HYDROGUI / BndConditionsDialog.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 os
19 import sys
20
21 from PyQt5.QtCore import *
22 from PyQt5.QtGui import *
23 from PyQt5.QtWidgets import *
24 from PyQt5 import uic
25
26 from MEDLoader import MEDFileMesh
27
28 # TODO: get rid of sys.path.append() ?
29 hydro_solver_root = os.path.join(os.environ['HYDROSOLVER_ROOT_DIR'], 'lib', 'python2.7', 'site-packages', 'salome')
30 #sys.path.append(os.path.join(hydro_solver_root, 'salome', 'hydrotools'))
31
32 from salome.hydro import boundaryConditions
33
34 ROW_PROPERTY_NAME = "row"
35
36 """Get names of groups on edges from the given MED file"""
37 def get_med_groups_on_edges(file_path):
38     try:
39         med_file_mesh = MEDFileMesh.New(file_path)
40         groups = list(med_file_mesh.getGroupsOnSpecifiedLev(-1))
41     except Exception as e:
42         print e.what()
43         return []
44
45     return groups
46
47 """Get preset name corresponding to the given values of LIHBOR, LIUBOR, LIVBOR and LITBOR"""
48 def get_preset_name(presets, lihbor, liubor, livbor, litbor):
49     name = 'Custom'
50     res = (lihbor, liubor, livbor, litbor)
51     for key, val in presets.iteritems():
52       if val == res:
53         name = key
54         break
55     return name
56
57 """Convert string to integer, return None if conversion is not possible"""
58 def get_int(str):
59     value = ''
60
61     if str.isdigit():
62         value = int(str)
63
64     return value
65
66 """Item delegate for LIHBOR, LIUBOR, LIVBOR and LITBOR columns"""
67 class ValueDelegate(QStyledItemDelegate):
68
69     def __init__(self, parent = None):
70         QStyledItemDelegate.__init__(self, parent)
71
72     def createEditor(self, parent, option, index):
73         line_edit = QLineEdit(parent)
74
75         validator = QIntValidator(parent)
76         validator.setRange(0, 6)
77
78         line_edit.setValidator(validator)
79
80         return line_edit
81
82     def setEditorData(self, editor, index):
83         value = index.model().data(index, Qt.EditRole)
84         if value.isdigit():
85             editor.setText(value)
86         else:
87             editor.setText('')
88
89     def setModelData(self, editor, model, index):
90         model.setData(index, editor.text(), Qt.EditRole)
91
92     def updateEditorGeometry(self, editor, option, index):
93         editor.setGeometry(option.rect)
94
95 """Boundary conditions definition dialog"""
96 class BoundaryConditionsDialog(QDialog):
97
98     def __init__(self, parent = None, modal = 0):
99         QDialog.__init__(self, parent)
100         uic.loadUi(os.path.join(hydro_solver_root, 'BndConditionsDialog.ui'), self )
101
102         # Connections
103         self.medFileButton.clicked.connect(self.on_med_file_browse)
104         self.bndConditionsFileButton.clicked.connect(self.on_bnd_file_browse)
105         self.resultBndConditionsFileButton.clicked.connect(self.on_result_file_browse)
106
107         self.sameAsInputCB.toggled.connect(self.resultBndConditionsFileEdit.setDisabled)
108         self.sameAsInputCB.toggled.connect(self.resultBndConditionsFileButton.setDisabled)
109
110         self.boundaryConditionsTable.cellChanged.connect(self.on_cell_changed)
111
112         self.applyAndCloseButton.clicked.connect(self.on_apply_and_close)
113         self.applyButton.clicked.connect(self.on_apply)
114         self.closeButton.clicked.connect(self.reject)
115         self.helpButton.clicked.connect(self.on_help)
116
117         # Set widgets properties
118         self.init_widgets()
119
120         # Input conditions
121         self.input_conditions = {}
122
123         # Init presets
124         self.presets = {}
125         self.init_presets()
126
127     """Initialize presets"""
128     def init_presets(self):
129         # TODO: determine another presets path
130         presets_data_root = os.path.join(os.environ['HYDROSOLVER_ROOT_DIR'], 'lib', 'python2.7', 'site-packages', 'salome', 'tests', 'data')
131         file_path = os.path.join(presets_data_root, 'bnd_conditions_presets.txt')
132         reader = boundaryConditions.PresetReader(file_path)
133         self.presets = reader.read()
134
135     """Initialize widget properties"""
136     def init_widgets(self):
137         self.medFileEdit.setReadOnly(True)
138         self.bndConditionsFileEdit.setReadOnly(True)
139         self.resultBndConditionsFileEdit.setReadOnly(True)
140
141         delegate = ValueDelegate(self.boundaryConditionsTable)
142         self.boundaryConditionsTable.setItemDelegateForColumn(1, delegate)
143         self.boundaryConditionsTable.setItemDelegateForColumn(2, delegate)
144         self.boundaryConditionsTable.setItemDelegateForColumn(3, delegate)
145         self.boundaryConditionsTable.setItemDelegateForColumn(4, delegate)
146
147     """Process cell data changes"""
148     def on_cell_changed(self, row, column):
149         #print "on_cell_changed"
150         lihbor = liubor = livbor = litbor = None
151
152         item = self.boundaryConditionsTable.item(row, 1)
153         if item:
154             lihbor = get_int(str(item.text()))
155
156         item = self.boundaryConditionsTable.item(row, 2)
157         if item:
158             liubor = get_int(str(item.text()))
159
160         item = self.boundaryConditionsTable.item(row, 3)
161         if item:
162             livbor = get_int(str(item.text()))
163
164         item = self.boundaryConditionsTable.item(row, 4)
165         if item:
166             litbor = get_int(str(item.text()))
167
168         preset_name = get_preset_name(self.presets, lihbor, liubor, livbor, litbor)
169         #print "on_cell_changed ", preset_name, lihbor, liubor, livbor, litbor
170
171         combo = self.boundaryConditionsTable.cellWidget(row, 0)
172         if isinstance(combo, QComboBox):
173             ind = combo.findText(preset_name)
174             if ind >= 0:
175                 combo.currentIndexChanged.disconnect(self.on_preset_changed)
176                 combo.setCurrentIndex(ind)
177                 combo.currentIndexChanged.connect(self.on_preset_changed)
178
179     """Save the user data to boundary conditions file"""
180     def on_apply(self):
181         # Save boundary conditions file
182         if not self.is_valid():
183             print 'Not valid'
184             return False
185
186         if self.sameAsInputCB.isChecked():
187           file_path = self.bndConditionsFileEdit.text()
188         else:
189           file_path = self.resultBndConditionsFileEdit.text()
190         print 'File path:', file_path
191         writer = boundaryConditions.BoundaryConditionWriter(file_path)
192
193         conditions = []
194         for row_nb in xrange(0, self.boundaryConditionsTable.rowCount()):
195             lihbor = str(self.boundaryConditionsTable.item(row_nb, 1).text())
196             liubor = str(self.boundaryConditionsTable.item(row_nb, 2).text())
197             livbor = str(self.boundaryConditionsTable.item(row_nb, 3).text())
198             litbor = str(self.boundaryConditionsTable.item(row_nb, 4).text())
199             group_name = str(self.boundaryConditionsTable.item(row_nb, 5).text())
200             if lihbor != "":
201               conditions.append(boundaryConditions.BoundaryCondition(lihbor, liubor, livbor, litbor, group_name))
202
203         writer.write(conditions)
204
205         return True
206
207     """Save the user data to boundary conditions file and close the dialog"""
208     def on_apply_and_close(self):
209         if self.on_apply():
210             self.accept()
211
212     """Select MED file"""
213     def on_med_file_browse(self):
214         # Get file path
215         file_path, filt = QFileDialog.getOpenFileName(self, self.tr("Open MED file"), "", self.tr("MED files (*.med)"))
216         if not file_path:
217             return
218
219         # Get names of groups on edges
220         groups = get_med_groups_on_edges(str(file_path))
221
222         if len(groups) > 0:
223             # Display the path
224             self.medFileEdit.setText(file_path)
225
226             # Update table
227             self.set_groups(groups)
228         else:
229             QMessageBox.warning(self, self.tr("Warning"), self.tr("Can't get group names from the selected MED file."))
230         self.boundaryConditionsTable.resizeColumnsToContents()
231
232     """Select boundary conditions file"""
233     def on_bnd_file_browse(self):
234         file_path, filt = QFileDialog.getOpenFileName(self, self.tr("Open boundary conditions file"))
235
236         if file_path:
237             self.bndConditionsFileEdit.setText(file_path)
238             reader = boundaryConditions.BoundaryConditionReader(file_path)
239             conditions = reader.read()
240
241             self.input_conditions.clear()
242             for cnd in conditions:
243                 self.input_conditions[cnd.group] = (cnd.lihbor, cnd.liubor, cnd.livbor, cnd.litbor)
244
245             # Check read errors
246             read_errors = reader.errors
247             if len( read_errors ) > 0:
248                 msg = "\n".join(read_errors)
249                 QMessageBox.warning(self, self.tr("Warning"), msg)
250
251             if len(self.input_conditions) > 0:
252                 self.update_table()
253             else:
254                 QMessageBox.warning(self, self.tr("Warning"), self.tr("No conditions have been read from the file."))
255
256     """Called on preset selection in the first column of the table"""
257     def on_preset_changed(self):
258         #print"on_preset_changed"
259         combo = self.sender()
260
261         preset = str(combo.currentText())
262
263         if preset and self.presets.has_key(preset):
264             values = self.presets[preset]
265             row_nb = combo.property(ROW_PROPERTY_NAME)
266
267             if row_nb >= 0 and row_nb < self.boundaryConditionsTable.rowCount():
268                 lihbor = values[0]
269                 liubor = values[1]
270                 livbor = values[2]
271                 litbor = values[3]
272                 #print "on_preset_changed ", preset, lihbor, liubor, livbor, litbor
273
274                 #if lihbor is not None:
275                 self.boundaryConditionsTable.item(row_nb, 1).setText(str(lihbor))
276
277                 #if liubor is not None:
278                 self.boundaryConditionsTable.item(row_nb, 2).setText(str(liubor))
279
280                 #if livbor is not None:
281                 self.boundaryConditionsTable.item(row_nb, 3).setText(str(livbor))
282
283                 #if litbor is not None:
284                 self.boundaryConditionsTable.item(row_nb, 4).setText(str(litbor))
285                 
286         if isinstance(combo, QComboBox):
287             ind = combo.findText(preset)
288             if ind >= 0:
289                 combo.setCurrentIndex(ind)
290
291     """Define result file path"""
292     def on_result_file_browse(self):
293         file_path, filt = QFileDialog.getSaveFileName(self, self.tr("Select output file path"))
294         #print file_path
295         if file_path:
296             self.resultBndConditionsFileEdit.setText(file_path)
297
298     """Set groups list"""
299     def set_groups(self, groups):
300         #print "set_groups"
301         self.boundaryConditionsTable.setRowCount(0)
302         for group in groups:
303             # Add row
304             row_nb = self.boundaryConditionsTable.rowCount()
305             self.boundaryConditionsTable.insertRow(row_nb)
306
307             # 'Preset' column
308             combo = QComboBox(self)
309             #combo.addItem('')
310             if len(self.presets) > 0:
311                 items = self.presets.keys()
312                 items.sort()
313                 combo.addItems(items)
314
315             combo.setProperty(ROW_PROPERTY_NAME, row_nb)
316             combo.currentIndexChanged.connect(self.on_preset_changed)
317
318             self.boundaryConditionsTable.setCellWidget(row_nb, 0, combo)
319
320             # 'LIHBOR' column
321             self.boundaryConditionsTable.setItem(row_nb, 1, QTableWidgetItem(''))
322
323             # 'LIUBOR' column
324             self.boundaryConditionsTable.setItem(row_nb, 2, QTableWidgetItem(''))
325
326             # 'LIVBOR' column
327             self.boundaryConditionsTable.setItem(row_nb, 3, QTableWidgetItem(''))
328
329             # 'LITBOR' column
330             self.boundaryConditionsTable.setItem(row_nb, 4, QTableWidgetItem(''))
331
332             # 'Group' column
333             item = QTableWidgetItem(group)
334             font = item.font()
335             font.setBold(True)
336             item.setFont(font)
337             item.setFlags(Qt.ItemIsEnabled)
338             self.boundaryConditionsTable.setItem(row_nb, 5, item)
339
340         self.update_table()
341
342     """Update conditions data in the table from the conditions input file"""
343     def update_table(self):
344         #print "update_table"
345         is_updated = False
346
347         nb_rows = self.boundaryConditionsTable.rowCount()
348         for row_nb in xrange(0, nb_rows):
349             group_name = str(self.boundaryConditionsTable.item(row_nb, 5).text())
350             if self.input_conditions.has_key(group_name):
351                 
352                 values = self.input_conditions[group_name]
353                 #print values
354
355                 lihbor = str(values[0])
356                 liubor = str(values[1])
357                 livbor = str(values[2])
358                 litbor = str(values[3])
359                 #print lihbor, liubor, livbor, litbor
360
361                 self.boundaryConditionsTable.item(row_nb, 1).setText(lihbor)
362                 self.boundaryConditionsTable.item(row_nb, 2).setText(liubor)
363                 self.boundaryConditionsTable.item(row_nb, 3).setText(livbor)
364                 self.boundaryConditionsTable.item(row_nb, 4).setText(litbor)
365
366 #                 combo = self.boundaryConditionsTable.cellWidget(row_nb, 0)
367 #                 if isinstance(combo, QComboBox):
368 #                     preset_name = get_preset_name(self.presets, lihbor, liubor, livbor, litbor)
369 #                     ind = combo.findText(preset_name)
370 #                     if ind >= 0:
371 #                         combo.setCurrentIndex(ind)
372
373                 is_updated = True
374
375         if not is_updated and nb_rows > 0 and len(self.input_conditions) > 0:
376             QMessageBox.information(self, self.tr("Information"), self.tr("No one group name from the MED file is presented in the input list of conditions."))
377
378     """Get output file path"""
379     def get_output_path(self):
380         path = self.bndConditionsFileEdit.text()
381
382         if not self.sameAsInputCB.isChecked():
383             path = self.resultBndConditionsFileEdit.text()
384
385         return path
386
387     """Check if the input data is valid"""
388     def is_valid(self):
389         is_ok = True
390
391         if self.boundaryConditionsTable.rowCount() < 1:
392             QMessageBox.critical(self, self.tr("Insufficient input data"), self.tr("Boundary conditions list is empty."))
393         elif len(self.get_output_path())==0:
394             QMessageBox.critical(self, self.tr("Insufficient input data"), self.tr("Output file path is empty."))
395         else:
396             for row_nb in xrange(0, self.boundaryConditionsTable.rowCount()):
397                 has_empty_cells = True
398                 lihbor = str(self.boundaryConditionsTable.item(row_nb, 1).text())
399                 liubor = str(self.boundaryConditionsTable.item(row_nb, 2).text())
400                 livbor = str(self.boundaryConditionsTable.item(row_nb, 3).text())
401                 litbor = str(self.boundaryConditionsTable.item(row_nb, 4).text())
402                 #print "test valid: ", lihbor, liubor, livbor, litbor
403                 if (lihbor != '') and (liubor != '')  and (livbor != '')  and (litbor != '') :
404                     has_empty_cells = False # Full lines are OK
405                     #print "valid: full line"
406                 if (lihbor == '') and (liubor == '') and (livbor == '') and (litbor == ''):
407                     has_empty_cells = False # Empty lines are OK
408                     #print "valid: empty line"
409
410                 if has_empty_cells:
411                     #print "not valid!"
412                     QMessageBox.critical(self, self.tr("Insufficient input data"), self.tr("Table has empty cell(s)."))
413                     is_ok = False
414                     break
415
416         return is_ok
417
418     """Shows help page"""
419     def on_help(self):
420         msg = """
421         <h2>Boundary conditions dialog</h2>
422
423         This dialog is used to read and write boundary conditions files.
424         Below is the description of the dialog controls.
425
426         <h3>MED file</h3>
427         This field allows selection of a med file (via the standard file open dialog).
428         The file must contain groups of edges, if this is not the case a warning message appears.
429         The filling of this field is mandatory.
430
431         <h3>Boundary conditions file</h3>
432         This field allows selecting the file of boundary conditions (via the standard file open dialog).
433         The data from this file is displayed in the table "Boundary conditions".
434         This field is optional; if it is not selected the table will not be prefilled.
435
436         <h3>Result boundary conditions file</h3>
437         This field allows selecting the file in which to save the data (via the standard file save dialog).
438         This field is mandatory if the "Same as the input" check box is unchecked.
439
440         <h3>Boundary conditions table</h3>
441         Contains data representing the list of boundary conditions.
442         The first column contains a list of presets.
443         The last columns is read-only, it contains names of group of edges from the selected MED file.
444         Other columns is for LIHBOR, LIUBOR, LIVBOR and LITBOR parameters, which can take a value ranging between 0 and 6.
445         <br>
446         <br>
447         When the table is filled and the output file is defined, the user clicks on "Apply" or "Apply and Close" button
448         to perform the data export to the file.
449         Click on "Close" button does not lead to saving the data and just closes the dialog.
450         """
451         QMessageBox.about(self, self.tr("About boundary conditions dialog"), msg);
452         pass