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