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