Salome HOME
fix boundary condition dialog
[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
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         #print"on_preset_changed"
258         combo = self.sender()
259
260         preset = str(combo.currentText())
261
262         if preset and self.presets.has_key(preset):
263             values = self.presets[preset]
264             row_nb = combo.property(ROW_PROPERTY_NAME)
265
266             if row_nb >= 0 and row_nb < self.boundaryConditionsTable.rowCount():
267                 lihbor = values[0]
268                 liubor = values[1]
269                 livbor = values[2]
270                 litbor = values[3]
271                 #print "on_preset_changed ", preset, lihbor, liubor, livbor, litbor
272
273                 #if lihbor is not None:
274                 self.boundaryConditionsTable.item(row_nb, 1).setText(str(lihbor))
275
276                 #if liubor is not None:
277                 self.boundaryConditionsTable.item(row_nb, 2).setText(str(liubor))
278
279                 #if livbor is not None:
280                 self.boundaryConditionsTable.item(row_nb, 3).setText(str(livbor))
281
282                 #if litbor is not None:
283                 self.boundaryConditionsTable.item(row_nb, 4).setText(str(litbor))
284                 
285         if isinstance(combo, QComboBox):
286             ind = combo.findText(preset)
287             if ind >= 0:
288                 combo.setCurrentIndex(ind)
289
290     """Define result file path"""
291     def on_result_file_browse(self):
292         file_path, filt = QFileDialog.getSaveFileName(self, self.tr("Select output file path"))
293         #print file_path
294         if file_path:
295             self.resultBndConditionsFileEdit.setText(file_path)
296
297     """Set groups list"""
298     def set_groups(self, groups):
299         #print "set_groups"
300         self.boundaryConditionsTable.setRowCount(0)
301         for group in groups:
302             # Add row
303             row_nb = self.boundaryConditionsTable.rowCount()
304             self.boundaryConditionsTable.insertRow(row_nb)
305
306             # 'Preset' column
307             combo = QComboBox(self)
308             #combo.addItem('')
309             if len(self.presets) > 0:
310                 items = self.presets.keys()
311                 items.sort()
312                 combo.addItems(items)
313
314             combo.setProperty(ROW_PROPERTY_NAME, row_nb)
315             combo.currentIndexChanged.connect(self.on_preset_changed)
316
317             self.boundaryConditionsTable.setCellWidget(row_nb, 0, combo)
318
319             # 'LIHBOR' column
320             self.boundaryConditionsTable.setItem(row_nb, 1, QTableWidgetItem(''))
321
322             # 'LIUBOR' column
323             self.boundaryConditionsTable.setItem(row_nb, 2, QTableWidgetItem(''))
324
325             # 'LIVBOR' column
326             self.boundaryConditionsTable.setItem(row_nb, 3, QTableWidgetItem(''))
327
328             # 'LITBOR' column
329             self.boundaryConditionsTable.setItem(row_nb, 4, QTableWidgetItem(''))
330
331             # 'Group' column
332             item = QTableWidgetItem(group)
333             font = item.font()
334             font.setBold(True)
335             item.setFont(font)
336             item.setFlags(Qt.ItemIsEnabled)
337             self.boundaryConditionsTable.setItem(row_nb, 5, item)
338
339         self.update_table()
340
341     """Update conditions data in the table from the conditions input file"""
342     def update_table(self):
343         #print "update_table"
344         is_updated = False
345
346         nb_rows = self.boundaryConditionsTable.rowCount()
347         for row_nb in xrange(0, nb_rows):
348             group_name = str(self.boundaryConditionsTable.item(row_nb, 5).text())
349             if self.input_conditions.has_key(group_name):
350                 
351                 values = self.input_conditions[group_name]
352                 #print values
353
354                 lihbor = str(values[0])
355                 liubor = str(values[1])
356                 livbor = str(values[2])
357                 litbor = str(values[3])
358                 #print lihbor, liubor, livbor, litbor
359
360                 self.boundaryConditionsTable.item(row_nb, 1).setText(lihbor)
361                 self.boundaryConditionsTable.item(row_nb, 2).setText(liubor)
362                 self.boundaryConditionsTable.item(row_nb, 3).setText(livbor)
363                 self.boundaryConditionsTable.item(row_nb, 4).setText(litbor)
364
365 #                 combo = self.boundaryConditionsTable.cellWidget(row_nb, 0)
366 #                 if isinstance(combo, QComboBox):
367 #                     preset_name = get_preset_name(self.presets, lihbor, liubor, livbor, litbor)
368 #                     ind = combo.findText(preset_name)
369 #                     if ind >= 0:
370 #                         combo.setCurrentIndex(ind)
371
372                 is_updated = True
373
374         if not is_updated and nb_rows > 0 and len(self.input_conditions) > 0:
375             QMessageBox.information(self, self.tr("Information"), self.tr("No one group name from the MED file is presented in the input list of conditions."))
376
377     """Get output file path"""
378     def get_output_path(self):
379         path = self.bndConditionsFileEdit.text()
380
381         if not self.sameAsInputCB.isChecked():
382             path = self.resultBndConditionsFileEdit.text()
383
384         return path
385
386     """Check if the input data is valid"""
387     def is_valid(self):
388         is_ok = False
389
390         if self.boundaryConditionsTable.rowCount() < 1:
391             QMessageBox.critical(self, self.tr("Insufficient input data"), self.tr("Boundary conditions list is empty."))
392         elif len(self.get_output_path())==0:
393             QMessageBox.critical(self, self.tr("Insufficient input data"), self.tr("Output file path is empty."))
394         else:
395             has_empty_cells = True
396             for row_nb in xrange(0, self.boundaryConditionsTable.rowCount()):
397                 lihbor = str(self.boundaryConditionsTable.item(row_nb, 1).text())
398                 liubor = str(self.boundaryConditionsTable.item(row_nb, 2).text())
399                 livbor = str(self.boundaryConditionsTable.item(row_nb, 3).text())
400                 litbor = str(self.boundaryConditionsTable.item(row_nb, 4).text())
401                 #print "valid: ", lihbor, liubor, livbor, litbor
402                 if lihbor and liubor and livbor and litbor:
403                     has_empty_cells = False # Full lines are OK
404                     #print "valid: full line"
405                 if (not lihbor) and (not liubor) and (not livbor) and (not litbor):
406                     has_empty_cells = False # Empty lines are OK
407                     #print "valid: empty line"
408
409             if has_empty_cells:
410                 QMessageBox.critical(self, self.tr("Insufficient input data"), self.tr("Table has empty cell(s)."))
411             else:
412                 is_ok = True
413
414         return is_ok
415
416     """Shows help page"""
417     def on_help(self):
418         msg = """
419         <h2>Boundary conditions dialog</h2>
420
421         This dialog is used to read and write boundary conditions files.
422         Below is the description of the dialog controls.
423
424         <h3>MED file</h3>
425         This field allows selection of a med file (via the standard file open dialog).
426         The file must contain groups of edges, if this is not the case a warning message appears.
427         The filling of this field is mandatory.
428
429         <h3>Boundary conditions file</h3>
430         This field allows selecting the file of boundary conditions (via the standard file open dialog).
431         The data from this file is displayed in the table "Boundary conditions".
432         This field is optional; if it is not selected the table will not be prefilled.
433
434         <h3>Result boundary conditions file</h3>
435         This field allows selecting the file in which to save the data (via the standard file save dialog).
436         This field is mandatory if the "Same as the input" check box is unchecked.
437
438         <h3>Boundary conditions table</h3>
439         Contains data representing the list of boundary conditions.
440         The first column contains a list of presets.
441         The last columns is read-only, it contains names of group of edges from the selected MED file.
442         Other columns is for LIHBOR, LIUBOR, LIVBOR and LITBOR parameters, which can take a value ranging between 0 and 6.
443         <br>
444         <br>
445         When the table is filled and the output file is defined, the user clicks on "Apply" or "Apply and Close" button
446         to perform the data export to the file.
447         Click on "Close" button does not lead to saving the data and just closes the dialog.
448         """
449         QMessageBox.about(self, self.tr("About boundary conditions dialog"), msg);
450         pass