1# -*- coding: utf-8 -*-
2
3# -------------------------------------------------------------------------------
4
5# This file is part of Code_Saturne, a general-purpose CFD tool.
6#
7# Copyright (C) 1998-2021 EDF S.A.
8#
9# This program is free software; you can redistribute it and/or modify it under
10# the terms of the GNU General Public License as published by the Free Software
11# Foundation; either version 2 of the License, or (at your option) any later
12# version.
13#
14# This program is distributed in the hope that it will be useful, but WITHOUT
15# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16# FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
17# details.
18#
19# You should have received a copy of the GNU General Public License along with
20# this program; if not, write to the Free Software Foundation, Inc., 51 Franklin
21# Street, Fifth Floor, Boston, MA 02110-1301, USA.
22
23# -------------------------------------------------------------------------------
24
25"""
26This module contains the following classes:
27- LabelDelegate
28- LocalizationSelectorDelegate
29- StandardItemModelLocalization
30- LocalizationView
31"""
32
33# -------------------------------------------------------------------------------
34# Standard modules
35# -------------------------------------------------------------------------------
36
37import logging
38
39# -------------------------------------------------------------------------------
40# Third-party modules
41# -------------------------------------------------------------------------------
42
43from code_saturne.Base.QtCore import *
44from code_saturne.Base.QtGui import *
45from code_saturne.Base.QtWidgets import *
46
47# -------------------------------------------------------------------------------
48# Application modules import
49# -------------------------------------------------------------------------------
50
51from code_saturne.model.Common import LABEL_LENGTH_MAX, GuiParam
52from code_saturne.Base.QtPage import IntValidator, RegExpValidator
53from code_saturne.Base.QtPage import from_qvariant, to_text_string
54from code_saturne.Pages.BoundaryNatureForm import Ui_BoundaryNatureForm
55from code_saturne.model.LocalizationModel import LocalizationModel, Zone
56
57# -------------------------------------------------------------------------------
58# log config
59# -------------------------------------------------------------------------------
60
61logging.basicConfig()
62log = logging.getLogger("BoundaryNatureView")
63log.setLevel(GuiParam.DEBUG)
64
65
66# -------------------------------------------------------------------------------
67# QComboBox delegate for the boundary nature
68# -------------------------------------------------------------------------------
69
70class BoundaryNatureDelegate(QItemDelegate):
71    """
72    Use of a combo box in the table.
73    """
74
75    def __init__(self, parent, dicoM2V):
76        super(BoundaryNatureDelegate, self).__init__(parent)
77        self.parent = parent
78        self.dicoM2V = dicoM2V
79
80        self.dicoV2M = {}
81        for k, v in list(self.dicoM2V.items()):
82            self.dicoV2M[v] = k
83
84    def createEditor(self, parent, option, index):
85        editor = QComboBox(parent)
86        for k in list(self.dicoV2M.keys()):
87            editor.addItem(k)
88        editor.installEventFilter(self)
89        editor.setMinimumWidth(120)
90        return editor
91
92    def setEditorData(self, comboBox, index):
93        row = index.row()
94        col = index.column()
95        str_model = index.model().getData(row, col)
96        idx = list(self.dicoM2V.keys()).index(str_model)
97        comboBox.setCurrentIndex(idx)
98
99    def setModelData(self, comboBox, model, index):
100        txt = str(comboBox.currentText())
101        value = self.dicoV2M[txt]
102        selectionModel = self.parent.selectionModel()
103        for idx in selectionModel.selectedIndexes():
104            if idx.column() == index.column():
105                model.setData(idx, value, Qt.DisplayRole)
106
107
108# -------------------------------------------------------------------------------
109# StandarItemModel class
110# -------------------------------------------------------------------------------
111
112class StandardItemModelLocalization(QStandardItemModel):
113    def __init__(self, mdl, zoneType, dicoM2V, tree=None, case=None):
114        """
115        """
116        QStandardItemModel.__init__(self)
117        self.headers = [self.tr("Label"),
118                        self.tr("Nature")]
119        self.setColumnCount(len(self.headers))
120
121        self.mdl = mdl
122        self.zoneType = zoneType
123        self.dicoM2V = dicoM2V
124        self.browser = tree
125        self.case = case
126
127        self._data = []
128        self._disable = []
129
130    def data(self, index, role):
131        if not index.isValid():
132            return None
133
134        if role == Qt.DisplayRole:
135            row = index.row()
136            col = index.column()
137
138            if col == 0:
139                return self._data[row][col]
140
141            elif col == 1:
142                key = self._data[row][col]
143                return self.dicoM2V[key]
144
145        return None
146
147    def flags(self, index):
148        if not index.isValid():
149            return Qt.ItemIsEnabled
150        if index.column() == 0:
151            return Qt.ItemIsEnabled
152        return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable
153
154    def headerData(self, section, orientation, role):
155        if orientation == Qt.Horizontal and role == Qt.DisplayRole:
156            return self.headers[section]
157        return None
158
159    def setData(self, index, value, role):
160        col = index.column()
161
162        if col == 1:
163            row = index.row()
164
165            nature = str(from_qvariant(value, to_text_string))
166            self._data[row][1] = nature
167
168            self.mdl.setNature(self._data[row][0], nature)
169
170        self.dataChanged.emit(index, index)
171        self.browser.configureTree(self.case)
172        return True
173
174    def addItem(self, zone):
175        """
176        Add an element in the table view.
177        """
178        line = [zone.getLabel(),
179                zone.getNature()]
180        self._data.append(line)
181        row = self.rowCount()
182        self.setRowCount(row + 1)
183
184        # If row is disabled, it appears in light gray
185        # self._disable.append((row, 0))
186        self.browser.configureTree(self.case)
187        return zone
188
189    def getItem(self, row):
190        return self._data[row]
191
192    def getData(self, row, column):
193        return self._data[row][column]
194
195
196# -------------------------------------------------------------------------------
197# Main class
198# -------------------------------------------------------------------------------
199
200class BoundaryNatureView(QWidget, Ui_BoundaryNatureForm):
201    """
202    Main class
203    """
204
205    def __init__(self, parent, case, tree=None):
206        """
207        Constructor
208        """
209        QWidget.__init__(self, parent)
210
211        Ui_BoundaryNatureForm.__init__(self)
212        self.setupUi(self)
213
214        self.case = case
215        self.case.undoStopGlobal()
216
217        dicoM2V = Zone("BoundaryZone", case=self.case).getModel2ViewDictionary()
218        zoneType = "BoundaryZone"
219        self.zoneType = "BoundaryZone"
220
221        self.mdl = LocalizationModel("BoundaryZone", case)
222        self.case['dump_python'].append([self.mdl.__module__, "BoundaryZone", ()])
223
224        self.browser = tree
225
226        # Delegates
227        delegateNature = BoundaryNatureDelegate(self.tableView, dicoM2V)
228
229        # Model for table View
230        self.modelLocalization = StandardItemModelLocalization(self.mdl,
231                                                               zoneType,
232                                                               dicoM2V,
233                                                               tree,
234                                                               case)
235        self.tableView.setModel(self.modelLocalization)
236        self.tableView.setItemDelegateForColumn(1, delegateNature)
237        last_section = 1
238
239        # Populate QTableView model
240        for zone in self.mdl.getZones():
241            self.modelLocalization.addItem(zone)
242
243        if QT_API == "PYQT4":
244            self.tableView.verticalHeader().setResizeMode(QHeaderView.ResizeToContents)
245            self.tableView.horizontalHeader().setResizeMode(QHeaderView.ResizeToContents)
246            self.tableView.horizontalHeader().setResizeMode(last_section, QHeaderView.Stretch)
247        elif QT_API == "PYQT5":
248            self.tableView.verticalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
249            self.tableView.horizontalHeader().setSectionResizeMode(QHeaderView.ResizeToContents)
250            self.tableView.horizontalHeader().setSectionResizeMode(last_section, QHeaderView.Stretch)
251
252        # Connections
253        self.modelLocalization.dataChanged.connect(self.dataChanged)
254        self.tableView.clicked.connect(self.slotChangeSelection)
255
256        # Context menu
257        self.tableView.setContextMenuPolicy(Qt.CustomContextMenu)
258        self.tableView.customContextMenuRequested[QPoint].connect(self.slotContextMenu)
259
260        self.case.undoStartGlobal()
261
262    def slotChangeSelection(self):
263        """
264        """
265        current = self.tableView.currentIndex()
266
267    @pyqtSlot()
268    def slotContextMenu(self):
269        """
270        Public slot
271
272        Create the popup menu of the Browser
273        """
274        fileMenu = QMenu(self.tableView)
275
276        self.actionInlet = QAction(self.tr("Select all inlets"), self.tableView)
277        self.actionInlet.triggered.connect(self.slotSelectBoundaries)
278        fileMenu.addAction(self.actionInlet)
279
280        self.actionOutlet = QAction(self.tr("Select all outlets"), self.tableView)
281        self.actionOutlet.triggered.connect(self.slotSelectBoundaries)
282        fileMenu.addAction(self.actionOutlet)
283
284        self.actionWall = QAction(self.tr("Select all walls"), self.tableView)
285        self.actionWall.triggered.connect(self.slotSelectBoundaries)
286        fileMenu.addAction(self.actionWall)
287
288        self.actionSymmetry = QAction(self.tr("Select all symmetries"), self.tableView)
289        self.actionSymmetry.triggered.connect(self.slotSelectBoundaries)
290        fileMenu.addAction(self.actionSymmetry)
291
292        fileMenu.popup(QCursor().pos())
293        fileMenu.show()
294
295    def dataChanged(self, topLeft, bottomRight):
296        for row in range(topLeft.row(), bottomRight.row() + 1):
297            self.tableView.resizeRowToContents(row)
298        for col in range(topLeft.column(), bottomRight.column() + 1):
299            self.tableView.resizeColumnToContents(col)
300
301    @pyqtSlot()
302    def slotSelectBoundaries(self):
303        """
304        Public slot.
305
306        Warning: works only if the selection mode of the view is set to MultiSelection.
307        """
308        previous_selecion_mode = self.tableView.selectionMode()
309        self.tableView.setSelectionMode(QAbstractItemView.MultiSelection)
310        self.tableView.clearSelection()
311
312        if self.sender() == self.actionInlet:
313            select = "inlet"
314        elif self.sender() == self.actionOutlet:
315            select = "outlet"
316        elif self.sender() == self.actionWall:
317            select = "wall"
318        elif self.sender() == self.actionSymmetry:
319            select = "symmetry"
320
321        for row in range(self.modelLocalization.rowCount()):
322            [label, nature] = self.modelLocalization.getItem(row)
323            if nature == select:
324                self.tableView.selectRow(row)
325
326        self.tableView.setSelectionMode(previous_selecion_mode)
327
328
329# -------------------------------------------------------------------------------
330# End
331# -------------------------------------------------------------------------------
332