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