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