示例#1
0
    def on_load_base_layer(self):
        # look for base layers in the config
        layer_config = get_layer_config()

        dlg = QDialog()
        vbox = QVBoxLayout()
        list_widget = QListWidget()
        button_box = QDialogButtonBox(QDialogButtonBox.Ok
                                      | QDialogButtonBox.Cancel)
        vbox.addWidget(list_widget)
        vbox.addWidget(button_box)
        dlg.setWindowTitle("Select a base layer to load")
        dlg.setLayout(vbox)

        # populate the list widget
        for (uri, provider), cfg in layer_config.items():
            layer_name = cfg.get(
                "layer_name", "Unnamed layer ({}, {})".format(uri, provider))
            list_item = QListWidgetItem()
            list_item.setText(layer_name)
            list_item.setData(Qt.UserRole, (uri, provider))
            list_widget.addItem(list_item)

        button_box.rejected.connect(dlg.reject)
        button_box.accepted.connect(dlg.accept)

        if dlg.exec_():
            item = list_widget.currentItem()
            if item:
                uri, provider = item.data(Qt.UserRole)
                self.iface.addVectorLayer(uri, item.text(), provider)
示例#2
0
    def _setupUI(self):
        self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)

        self.setMinimumHeight(180)

        self.main_horizontal_layout = QHBoxLayout(self)

        italic_font = QFont()
        italic_font.setItalic(True)

        # deselected widget
        self.deselected_widget = QListWidget(self)
        self._set_list_widget_defaults(self.deselected_widget)
        deselected_label = QLabel()
        deselected_label.setText('Deselected')
        deselected_label.setAlignment(Qt.AlignCenter)
        deselected_label.setFont(italic_font)
        deselected_v_layout = QVBoxLayout()
        deselected_v_layout.addWidget(deselected_label)
        deselected_v_layout.addWidget(self.deselected_widget)

        # selected widget
        self.selected_widget = QListWidget(self)
        self._set_list_widget_defaults(self.selected_widget)
        selected_label = QLabel()
        selected_label.setText('Selected')
        selected_label.setAlignment(Qt.AlignCenter)
        selected_label.setFont(italic_font)
        selected_v_layout = QVBoxLayout()
        selected_v_layout.addWidget(selected_label)
        selected_v_layout.addWidget(self.selected_widget)

        # buttons
        self.buttons_vertical_layout = QVBoxLayout()
        self.buttons_vertical_layout.setContentsMargins(0, -1, 0, -1)

        self.select_all_btn = SmallQPushButton('>>')
        self.deselect_all_btn = SmallQPushButton('<<')
        self.select_btn = SmallQPushButton('>')
        self.deselect_btn = SmallQPushButton('<')
        self.select_btn.setToolTip('Add the selected items')
        self.deselect_btn.setToolTip('Remove the selected items')
        self.select_all_btn.setToolTip('Add all')
        self.deselect_all_btn.setToolTip('Remove all')

        # add buttons
        spacer_label = QLabel()  # pragmatic way to create a spacer with
        # the same height of the labels on top
        # of the lists, in order to align the
        # buttons with the lists.
        self.buttons_vertical_layout.addWidget(spacer_label)
        self.buttons_vertical_layout.addWidget(self.select_btn)
        self.buttons_vertical_layout.addWidget(self.deselect_btn)
        self.buttons_vertical_layout.addWidget(self.select_all_btn)
        self.buttons_vertical_layout.addWidget(self.deselect_all_btn)

        # add sub widgets
        self.main_horizontal_layout.addLayout(deselected_v_layout)
        self.main_horizontal_layout.addLayout(self.buttons_vertical_layout)
        self.main_horizontal_layout.addLayout(selected_v_layout)
    def initGui(self):
        layout = QVBoxLayout()
        buttonBox = QDialogButtonBox(QDialogButtonBox.Ok
                                     | QDialogButtonBox.Close)
        self.filterBox = QLineEdit()
        self.filterBox.setPlaceholderText(
            "[enter text or date in dd/mm/yyyy format to filter history]")
        self.filterBox.textChanged.connect(self.filterCommits)
        self.list = QListWidget()
        self.list.setAlternatingRowColors(True)
        self.list.setSelectionMode(QAbstractItemView.SingleSelection)
        self.list.setSelectionBehavior(QAbstractItemView.SelectRows)
        log = self.repo.log(until=self.until, path=self.path, limit=100)
        for commit in log:
            item = CommitListItem(commit)
            self.list.addItem(item)
        layout.addWidget(self.filterBox)
        layout.addWidget(self.list)
        layout.addWidget(buttonBox)
        self.setLayout(layout)

        buttonBox.accepted.connect(self.okPressed)
        buttonBox.rejected.connect(self.cancelPressed)

        self.resize(500, 400)
        self.setWindowTitle("Select commit")
示例#4
0
 def createCellWidget(self, qmlDict, attr, count):
     """
     Creates specific widgets for each attribute, which can be a QCombobox, a QLineEdit or a QListWidget.
     """
     if attr in qmlDict:
         enableIgnoreOption = False
         #case the type is dict the cell widget must be a combobox
         if isinstance(qmlDict[attr],dict):
             comboItem = DsgCustomComboBox()
             comboItem.addItems(sorted(qmlDict[attr].keys()))
             self.attributeTableWidget.setCellWidget(count, 1, comboItem)
         #case the type is tuple the cell widget must be a listwidget
         if isinstance(qmlDict[attr],tuple):
             (table, filterKeys) = qmlDict[attr]
             #getting the value relation dictionary used to make the listwidget
             valueRelation = self.makeValueRelationDict(table, filterKeys)
             list = QListWidget()
             for key in list(valueRelation.keys()):
                 listItem = QListWidgetItem(key)
                 listItem.setCheckState(Qt.Unchecked)
                 list.addItem(listItem)
             self.attributeTableWidget.setCellWidget(count, 1, list)
     #this is the normal case, a simple lineedit
     else:
         textItem = QLineEdit()
         self.attributeTableWidget.setCellWidget(count, 1, textItem)
         enableIgnoreOption = True
     #insert here aditional parameters
     self.createAditionalParameters(count, enableIgnoreOption)
示例#5
0
    def __init__(self, parent, layout_object):
        super().__init__(parent, layout_object)
        self.plot_item = layout_object
        self.message_bar = None

        vl = QVBoxLayout()
        vl.setContentsMargins(0, 0, 0, 0)

        plot_tools_layout = QHBoxLayout()

        plot_add_button = QPushButton()
        plot_add_button.setIcon(GuiUtils.get_icon('symbologyAdd.svg'))
        plot_add_button.setToolTip('Add a new plot')
        plot_tools_layout.addWidget(plot_add_button)
        plot_add_button.clicked.connect(self.add_plot)

        plot_remove_button = QPushButton()
        plot_remove_button.setIcon(GuiUtils.get_icon('symbologyRemove.svg'))
        plot_remove_button.setToolTip('Remove selected plot')
        plot_tools_layout.addWidget(plot_remove_button)
        plot_remove_button.clicked.connect(self.remove_plot)

        plot_duplicate_button = QPushButton()
        plot_duplicate_button.setIcon(
            GuiUtils.get_icon('mActionDuplicateLayer.svg'))
        plot_duplicate_button.setToolTip('Duplicates the selected plot')
        plot_tools_layout.addWidget(plot_duplicate_button)
        plot_duplicate_button.clicked.connect(self.duplicate_plot)

        plot_move_up_button = QPushButton()
        plot_move_up_button.setIcon(GuiUtils.get_icon('mActionArrowUp.svg'))
        plot_move_up_button.setToolTip('Move selected plot up')
        plot_tools_layout.addWidget(plot_move_up_button)
        plot_move_up_button.clicked.connect(self.move_up_plot)

        plot_move_down_button = QPushButton()
        plot_move_down_button.setIcon(
            GuiUtils.get_icon('mActionArrowDown.svg'))
        plot_move_down_button.setToolTip('Move selected plot down')
        plot_tools_layout.addWidget(plot_move_down_button)
        plot_move_down_button.clicked.connect(self.move_down_plot)

        vl.addLayout(plot_tools_layout)

        self.plot_list = QListWidget()
        self.plot_list.setSelectionMode(QListWidget.SingleSelection)
        self.plot_list.doubleClicked.connect(self.show_properties)
        vl.addWidget(self.plot_list)
        self.populate_plot_list()

        plot_properties_button = QPushButton(self.tr('Setup Selected Plot'))
        vl.addWidget(plot_properties_button)
        plot_properties_button.clicked.connect(self.show_properties)

        self.panel = None
        self.setPanelTitle(self.tr('Plot Properties'))
        self.item_properties_widget = QgsLayoutItemPropertiesWidget(
            self, layout_object)
        vl.addWidget(self.item_properties_widget)
        self.setLayout(vl)
示例#6
0
 def createEditor(self, parent, option, index):
     """
     Creates a custom editor to edit value relation data
     """
     # special combobox for field type
     if index.column() == self.column:
         list = QListWidget(parent)
         for item in self.itemsDict:
             listItem = QListWidgetItem(item)
             listItem.setCheckState(Qt.Unchecked)
             list.addItem(listItem)
         return list
     return QItemDelegate.createEditor(self, parent, option, index)
示例#7
0
            def initWindow(self):
                self.addButton = QPushButton("Hinzufügen", self)
                self.closeButton = QPushButton("Schließen", self)
                self.listWidget = QListWidget(self)
                self.listWidget.move(50, 50)
                self.listWidget.resize(250, 350)
                self.addButton.move(100, 410)
                self.addButton.clicked.connect(self.add)
                self.closeButton.move(220, 410)
                self.closeButton.clicked.connect(self.closing)

                self.setWindowTitle(self.title)
                self.setGeometry(self.top, self.left, self.width, self.height)
示例#8
0
    def __init__(self, viewer, feature_id, feature_name, config_list, config):
        QDialog.__init__(self)

        self.__viewer = viewer
        self.__feature_id = feature_id
        self.__feature_name = feature_name
        self.__config_list = config_list
        self.__config = config

        vbox = QVBoxLayout()

        btn = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        btn.accepted.connect(self.accept)
        btn.rejected.connect(self.reject)

        self.__list = QListWidget()
        self.__list.setSelectionMode(QAbstractItemView.ExtendedSelection)

        hbox = QHBoxLayout()
        lbl = QLabel("Sub selection")
        self.__sub_selection_combo = QComboBox()
        self.__sub_selection_combo.setEnabled(False)
        hbox.addWidget(lbl)
        hbox.addWidget(self.__sub_selection_combo)

        from_elsewhere_btn = QPushButton("Select another station")

        self.__title_label = QLabel()

        hbox2 = QHBoxLayout()
        hbox2.addWidget(self.__title_label)
        hbox2.addWidget(from_elsewhere_btn)

        vbox.addLayout(hbox2)
        vbox.addWidget(self.__list)
        vbox.addLayout(hbox)
        vbox.addWidget(btn)

        self.__list.itemSelectionChanged.connect(self.on_selection_changed)
        self.__sub_selection_combo.currentIndexChanged[str].connect(
            self.on_combo_changed)
        from_elsewhere_btn.clicked.connect(self.on_from_elsewhere_clicked)

        self.setLayout(vbox)
        self.setWindowTitle("Choose the data to add")
        self.resize(400, 200)

        self._populate_list()
        self.set_title(feature_name)
示例#9
0
    def __init__(self, parent, tree, name, value, action=None):
        QTreeWidgetItem.__init__(self, parent)
        self.parent = parent
        self.tree = tree
        self.name = name
        self._value = value
        self.combo = None
        self.list = None
        self.setText(0, name)
        widget = None
        if isinstance(value, QgsColorButton):
            widget = value
        elif isinstance(value, bool):
            if value:
                self.setCheckState(1, Qt.Checked)
            else:
                self.setCheckState(1, Qt.Unchecked)
        elif isinstance(value, tuple):
            self.combo = QComboBox()
            self.combo.setSizeAdjustPolicy(0)
            for option in value:
                self.combo.addItem(option)
            widget = self.combo
        elif isinstance(value, list):
            self.list = QListWidget()
            self.list.setSizeAdjustPolicy(0)
            self.list.setSelectionMode(QListWidget.MultiSelection)
            for option in value:
                self.list.addItem(option)
            widget = self.list
        else:
            self.setText(1, unicode(value))

        if action:
            layout = QHBoxLayout()
            layout.setMargin(0)
            if widget:
                layout.addWidget(widget)
            button = QToolButton()
            button.setDefaultAction(action)
            button.setText(action.text())
            layout.addWidget(button)
            layout.addStretch(1)
            widget = QWidget()
            widget.setLayout(layout)

        if widget:
            self.tree.setItemWidget(self, 1, widget)
    def __init__(self,
                 electorate_registry: LinzElectoralDistrictRegistry,
                 parent=None):
        super().__init__(parent)

        self.electorate_registry = electorate_registry

        self.setWindowTitle(self.tr('Deprecate Electorate'))

        layout = QVBoxLayout()

        self.search = QgsFilterLineEdit()
        self.search.setShowSearchIcon(True)
        self.search.setPlaceholderText(
            self.tr('Search for {}').format(
                electorate_registry.type_string_sentence()))
        self.search.textChanged.connect(self.filter_changed)
        layout.addWidget(self.search)

        request = QgsFeatureRequest()
        request.setFlags(QgsFeatureRequest.NoGeometry)
        request.setSubsetOfAttributes([
            electorate_registry.source_field_index,
            electorate_registry.title_field_index,
            electorate_registry.deprecated_field_index
        ])
        self.list = QListWidget()
        for f in electorate_registry.source_layer.getFeatures(request):
            title = f[electorate_registry.title_field_index]
            code = f[electorate_registry.source_field_index]
            deprecated = f[electorate_registry.deprecated_field_index]

            if deprecated:
                title = '*' + title
            item = QListWidgetItem(title)
            item.setData(Qt.UserRole, code)
            self.list.addItem(item)

        layout.addWidget(self.list, 10)

        button_box = QDialogButtonBox(QDialogButtonBox.Ok
                                      | QDialogButtonBox.Cancel)
        layout.addWidget(button_box)
        button_box.rejected.connect(self.reject)
        button_box.accepted.connect(self.accept)

        self.setLayout(layout)
        self.list.itemDoubleClicked.connect(self.accept)
示例#11
0
    def __init__(self, viewer, features, config_list, config):
        QDialog.__init__(self)

        self.__viewer = viewer
        self.__features = features
        self.__config_list = config_list
        self.__config = config
        vbox = QVBoxLayout()

        btn = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        btn.accepted.connect(self.accept)
        btn.rejected.connect(self.reject)

        self.__list = QListWidget()
        self.__list.setSelectionMode(QAbstractItemView.ExtendedSelection)

        hbox = QHBoxLayout()
        lbl = QLabel("Sub selection")
        self.__sub_selection_combo = QComboBox()
        self.__sub_selection_combo.setEnabled(False)
        hbox.addWidget(lbl)
        hbox.addWidget(self.__sub_selection_combo)

        self.__title_label = QLabel()

        hbox2 = QHBoxLayout()
        hbox2.addWidget(self.__title_label)

        vbox.addLayout(hbox2)
        vbox.addWidget(self.__list)
        vbox.addLayout(hbox)
        vbox.addWidget(btn)

        self.__list.itemSelectionChanged.connect(self.on_selection_changed)
        self.__sub_selection_combo.currentIndexChanged[str].connect(
            self.on_combo_changed)

        self.setLayout(vbox)
        self.setWindowTitle("Choose the data to add")
        self.resize(400, 200)

        self._populate_list()
        self.set_title(",".join([
            feature[self.__config["name_column"]]
            for feature in self.__features
        ]))
示例#12
0
        class listview(QDialog):
            name = "class"

            def __init__(self, table):
                super().__init__()
                self.table = table
                self.title = "Hinzufügen"
                self.top = 600
                self.left = 200
                self.width = 350
                self.height = 450

                self.initWindow()

            def initWindow(self):
                self.addButton = QPushButton("Hinzufügen", self)
                self.closeButton = QPushButton("Schließen", self)
                self.listWidget = QListWidget(self)
                self.listWidget.move(50, 50)
                self.listWidget.resize(250, 350)
                self.addButton.move(100, 410)
                self.addButton.clicked.connect(self.add)
                self.closeButton.move(220, 410)
                self.closeButton.clicked.connect(self.closing)

                self.setWindowTitle(self.title)
                self.setGeometry(self.top, self.left, self.width, self.height)

            def fill(self):
                self.listWidget.clear()
                if self.table.missingattr != []:
                    self.listWidget.addItems(self.table.missingattr)
                self.show()

            def add(self):
                print([li.text() for li in self.listWidget.selectedItems()])
                self.table.add(
                    [li.text() for li in self.listWidget.selectedItems()])
                self.fill()
                print("ok")

            def closing(self):
                self.close()
    def __init__(self, scenario_registry: ScenarioRegistry, parent=None):
        """
        Constructor for ScenarioSelectionDialog
        :param scenario_registry: linked scenario registry
        :param parent: parent widget
        """
        super().__init__(parent)

        self.scenario_registry = scenario_registry

        self.setWindowTitle(self.tr('Select Current Scenario'))

        layout = QVBoxLayout()

        self.search = QgsFilterLineEdit()
        self.search.setShowSearchIcon(True)
        self.search.setPlaceholderText(self.tr('Search for scenario'))
        self.search.textChanged.connect(self.filter_changed)
        layout.addWidget(self.search)

        self.list = QListWidget()
        for title, scenario_id in scenario_registry.scenario_titles().items():
            item = QListWidgetItem(title)
            item.setData(Qt.UserRole, scenario_id)
            self.list.addItem(item)

        layout.addWidget(self.list, 10)

        button_box = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        layout.addWidget(button_box)
        button_box.rejected.connect(self.reject)
        button_box.accepted.connect(self.accept)

        self.setLayout(layout)

        self.list.itemDoubleClicked.connect(
            self.accept)

        # select last scenario by default
        if self.list.count() > 0:
            self.list.item(self.list.count() - 1).setSelected(True)
示例#14
0
    def __init__(self, dockwidget, parent, params):

        QDialog.__init__(self, parent)
        main_lay = QVBoxLayout(self)
        self.dockwidget = dockwidget
        self.params = params

        self.setWindowTitle('Tags editor')

        # Top frame
        self.fra_top = QFrame()
        fra_top_lay = QVBoxLayout(self.fra_top)

        self.lst_main = QListWidget(self)
        self.btn_add = QPushButton('Add tag')
        self.btn_add.clicked.connect(self.add_tag)
        self.btn_remove = QPushButton('Remove tag')
        self.btn_remove.clicked.connect(self.remove_tag)

        fra_top_lay.addWidget(self.lst_main)
        fra_top_lay.addWidget(self.btn_add)
        fra_top_lay.addWidget(self.btn_remove)

        # Bottom frame
        self.fra_bottom = QFrame()
        fra_bottom_lay = QHBoxLayout(self.fra_bottom)

        btb_main = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        btb_main.accepted.connect(self.ok)
        btb_main.rejected.connect(self.reject)

        fra_bottom_lay.addWidget(btb_main)

        # Main
        main_lay.addWidget(self.fra_top)
        main_lay.addWidget(self.fra_bottom)

        self.initialize()
示例#15
0
    def __init__(self, parent, mono=False):

        super().__init__(parent)
        self.mono = mono
        if self.mono:
            return

        self.mlist = QListWidget(self)
        self.line_edit = ComplexLineEdit(self)

        self.clear()

        self.line_edit.setReadOnly(True)
        self.line_edit.installEventFilter(self)

        self.setModel(self.mlist.model())
        self.setView(self.mlist)
        self.view().setVerticalScrollBarPolicy(Qt.ScrollBarAsNeeded)
        self.setLineEdit(self.line_edit)

        # NOTE: this is necessary to handle the case in which an item in the
        # list is clicked to its right part, outside the text
        self.activated.connect(self.itemClicked)
    def setup_value_mapping_panels(self, classification):
        """Setup value mapping panel in the right panel.

        :param classification: Classification definition.
        :type classification: dict
        """
        # Set text in the label
        layer_purpose = self.parent.step_kw_purpose.selected_purpose()
        layer_subcategory = self.parent.step_kw_subcategory. \
            selected_subcategory()

        if is_raster_layer(self.parent.layer):
            description_text = classify_raster_question % (
                layer_subcategory['name'],
                layer_purpose['name'],
                classification['name'])

            dataset = gdal.Open(self.parent.layer.source(), GA_ReadOnly)
            active_band = self.parent.step_kw_band_selector.selected_band()
            unique_values = numpy.unique(numpy.array(
                dataset.GetRasterBand(active_band).ReadAsArray()))
            field_type = 0
            # Convert datatype to a json serializable type
            if numpy.issubdtype(unique_values.dtype, float):
                unique_values = [float(i) for i in unique_values]
            else:
                unique_values = [int(i) for i in unique_values]
        else:
            field = self.parent.step_kw_field.selected_fields()
            field_index = self.parent.layer.fields().indexFromName(field)
            field_type = self.parent.layer.fields()[field_index].type()
            description_text = classify_vector_question % (
                layer_subcategory['name'],
                layer_purpose['name'],
                classification['name'],
                field.upper())
            unique_values = list(self.parent.layer.uniqueValues(field_index))

        # Set description
        description_label = QLabel(description_text)
        description_label.setWordWrap(True)
        self.right_layout.addWidget(description_label)

        self.list_unique_values = QListWidget()
        self.list_unique_values.setDragDropMode(QAbstractItemView.DragDrop)
        self.list_unique_values.setDefaultDropAction(Qt.MoveAction)

        self.tree_mapping_widget = QTreeWidget()
        self.tree_mapping_widget.setDragDropMode(QAbstractItemView.DragDrop)
        self.tree_mapping_widget.setDefaultDropAction(Qt.MoveAction)
        self.tree_mapping_widget.header().hide()

        self.tree_mapping_widget.itemChanged.connect(
            self.update_dragged_item_flags)

        value_mapping_layout = QHBoxLayout()
        value_mapping_layout.addWidget(self.list_unique_values)
        value_mapping_layout.addWidget(self.tree_mapping_widget)

        self.right_layout.addLayout(value_mapping_layout)

        default_classes = classification['classes']

        # Assign unique values to classes (according to default)
        unassigned_values = list()
        assigned_values = dict()
        for default_class in default_classes:
            assigned_values[default_class['key']] = list()
        for unique_value in unique_values:
            if (unique_value is None
                    or (hasattr(unique_value, 'isNull')
                        and unique_value.isNull())):
                # Don't classify features with NULL value
                continue
            # Capitalization of the value and removing '_' (raw OSM data).
            value_as_string = str(unique_value).upper().replace('_', ' ')
            assigned = False
            for default_class in default_classes:
                if 'string_defaults' in default_class:
                    # To make it case insensitive
                    upper_string_defaults = [
                        c.upper() for c in default_class['string_defaults']]
                    in_string_default = (
                        value_as_string in upper_string_defaults)
                    condition_1 = field_type > 9 and in_string_default
                else:
                    condition_1 = False
                condition_2 = (
                    field_type < 10
                    and 'numeric_default_min' in default_class
                    and 'numeric_default_max' in default_class
                    and (default_class['numeric_default_min']
                         <= unique_value
                         < default_class['numeric_default_max']))
                if condition_1 or condition_2:
                    assigned_values[default_class['key']] += [unique_value]
                    assigned = True
                    break
            if not assigned:
                # add to unassigned values list otherwise
                unassigned_values += [unique_value]
        self.populate_classified_values(
            unassigned_values,
            assigned_values,
            default_classes,
            self.list_unique_values,
            self.tree_mapping_widget
        )

        # Current value map for exposure and classification
        available_classifications = self.value_maps.get(
            self.active_exposure['key'])
        if not available_classifications:
            return
        # Get active one
        current_classification = available_classifications.get(
            classification['key'])
        if not current_classification:
            return
        # Should come from metadata
        current_value_map = current_classification.get('classes')
        if not current_value_map:
            return

        unassigned_values = list()
        assigned_values = dict()
        for default_class in default_classes:
            assigned_values[default_class['key']] = list()
        for unique_value in unique_values:
            if (unique_value is None
                    or (hasattr(unique_value, 'isNull')
                        and unique_value.isNull())):
                # Don't classify features with NULL value
                continue
            # check in value map
            assigned = False
            for key, value_list in list(current_value_map.items()):
                if unique_value in value_list and key in assigned_values:
                    assigned_values[key] += [unique_value]
                    assigned = True
            if not assigned:
                unassigned_values += [unique_value]
        self.populate_classified_values(
            unassigned_values,
            assigned_values,
            default_classes,
            self.list_unique_values,
            self.tree_mapping_widget
        )
示例#17
0
class DataSelector(QDialog):
    def __init__(self, viewer, features, config_list, config):
        QDialog.__init__(self)

        self.__viewer = viewer
        self.__features = features
        self.__config_list = config_list
        self.__config = config
        vbox = QVBoxLayout()

        btn = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        btn.accepted.connect(self.accept)
        btn.rejected.connect(self.reject)

        self.__list = QListWidget()
        self.__list.setSelectionMode(QAbstractItemView.ExtendedSelection)

        hbox = QHBoxLayout()
        lbl = QLabel("Sub selection")
        self.__sub_selection_combo = QComboBox()
        self.__sub_selection_combo.setEnabled(False)
        hbox.addWidget(lbl)
        hbox.addWidget(self.__sub_selection_combo)

        self.__title_label = QLabel()

        hbox2 = QHBoxLayout()
        hbox2.addWidget(self.__title_label)

        vbox.addLayout(hbox2)
        vbox.addWidget(self.__list)
        vbox.addLayout(hbox)
        vbox.addWidget(btn)

        self.__list.itemSelectionChanged.connect(self.on_selection_changed)
        self.__sub_selection_combo.currentIndexChanged[str].connect(
            self.on_combo_changed)

        self.setLayout(vbox)
        self.setWindowTitle("Choose the data to add")
        self.resize(400, 200)

        self._populate_list()
        self.set_title(",".join([
            feature[self.__config["name_column"]]
            for feature in self.__features
        ]))

    def set_title(self, title):
        self.__title_label.setText("Station(s): {}".format(title))

    def _populate_list(self):
        self.__list.clear()
        for cfg in self.__config_list:
            if cfg["type"] in ("cumulative", "instantaneous"):
                # check number of features for this station

                if cfg.get("feature_filter_type") == "unique_data_from_values":
                    # get unique filter values

                    layerid = cfg["source"]
                    data_l = QgsProject.instance().mapLayers()[layerid]
                    values = set()
                    for feature in self.__features:
                        feature_id = feature[self.__config["id_column"]]
                        req = QgsFeatureRequest()
                        req.setFilterExpression("{}={}".format(
                            cfg["feature_ref_column"], feature_id))
                        values.update([
                            f[cfg["feature_filter_column"]]
                            for f in data_l.getFeatures(req)
                        ])

                        cfg.set_filter_unique_values(sorted(list(values)))

            item = QListWidgetItem(cfg["name"])
            item.setData(Qt.UserRole, cfg)
            self.__list.addItem(item)

    def accept(self):

        for feature in self.__features:
            self.__load_feature(feature)

    def __load_feature(self, feature):
        # FIXME this code too similar to main_dialog.load_plots
        # FIXME find a way to factor

        feature_id = feature[self.__config["id_column"]]
        feature_name = feature[self.__config["name_column"]]

        for item in self.__list.selectedItems():
            # now add the selected configuration
            cfg = item.data(Qt.UserRole)
            if cfg["type"] in ("cumulative", "instantaneous", "continuous"):
                layerid = cfg["source"]
                data_l = QgsProject.instance().mapLayers()[layerid]
                req = QgsFeatureRequest()
                filter_expr = "{}='{}'".format(cfg["feature_ref_column"],
                                               feature_id)
                req.setFilterExpression(filter_expr)

                title = cfg["name"]

                if cfg.get_filter_value():
                    filter_expr += " and {}='{}'".format(
                        cfg["feature_filter_column"], cfg.get_filter_value())

                    title = cfg.get_filter_value()
                else:
                    title = cfg["name"]

                f = None
                # test if the layer actually contains data
                for f in data_l.getFeatures(req):
                    break
                if f is None:
                    return
                if cfg["type"] == "instantaneous":
                    uom = cfg.get_uom()
                    data = LayerData(data_l,
                                     cfg["event_column"],
                                     cfg["value_column"],
                                     filter_expression=filter_expr,
                                     uom=uom)
                    uom = data.uom()
                    self.__viewer.add_data_cell(data,
                                                title,
                                                uom,
                                                station_name=feature_name,
                                                config=cfg)
                    self.__viewer.add_scale()
                if cfg["type"] == "continuous":
                    uom = cfg.get_uom()
                    fids = [f.id() for f in data_l.getFeatures(req)]
                    data = FeatureData(
                        data_l,
                        cfg["values_column"],
                        feature_ids=fids,
                        x_start_fieldname=cfg["start_measure_column"],
                        x_delta_fieldname=cfg["interval_column"])
                    self.__viewer.add_data_cell(data,
                                                title,
                                                uom,
                                                station_name=feature_name,
                                                config=cfg)
                elif cfg["type"] == "cumulative":
                    self.__viewer.add_histogram(
                        data_l,
                        filter_expression=filter_expr,
                        column_mapping={
                            f: cfg[f]
                            for f in ("min_event_column", "max_event_column",
                                      "value_column")
                        },
                        title=title,
                        config=cfg,
                        station_name=feature_name)

            elif cfg["type"] == "image":
                self.__viewer.add_imagery_from_db(cfg, feature_id)

        QDialog.accept(self)

    def on_selection_changed(self):
        self.__sub_selection_combo.clear()
        self.__sub_selection_combo.setEnabled(False)
        for item in self.__list.selectedItems():
            cfg = item.data(Qt.UserRole)
            for v in cfg.get_filter_unique_values():
                self.__sub_selection_combo.addItem(v)
            if cfg.get_filter_value():
                self.__sub_selection_combo.setCurrentIndex(
                    self.__sub_selection_combo.findText(
                        cfg.get_filter_value()))
            self.__sub_selection_combo.setEnabled(True)
            return

    def on_combo_changed(self, text):
        for item in self.__list.selectedItems():
            cfg = item.data(Qt.UserRole)
            cfg.set_filter_value(text)
            item.setData(Qt.UserRole, cfg)
            return
示例#18
0
 def __init__(self):
     QListWidget.__init__(self)
示例#19
0
    def __init__(self, parent, scope=None, xml_doc=None, md=None):
        super(MetadadoSNIMar, self).__init__(parent)
        if scope is None:
            self.scope = SCOPES.get_code_representation(md.hierarchy)
        else:
            self.scope = scope

        self.current_index = 0
        self.widgetStalker = {}
        if platform.system() != "Linux":
            font = QFont()
            font.setFamily(u"Segoe UI Symbol")
            self.setFont(font)

        self.sidelist = QListWidget(self)
        self.sidelist.setMinimumWidth(150)
        self.sidelist.setMaximumWidth(150)
        self.sidelist.setWordWrap(True)
        self.sidelist.setTextElideMode(Qt.ElideNone)
        self.sidelist.setIconSize(QSize(25, 25))
        self.sidelist.clicked.connect(self.list_clicked)
        index = 0
        if self.scope == SCOPES.SERVICES:
            tabs = cons.TABLIST_SERVICES
        else:
            tabs = cons.TABLIST_CDG_SERIES

        for tab_element in tabs:
            bufWidget = QListWidgetItem(
                qgui.QIcon(':/resourcesFolder/icons/' + tab_element[1]),
                tab_element[0])
            self.widgetStalker[tab_element[2]] = {
                "widget": bufWidget,
                "missingFields": set(),
                "incompleteEntries": set()
            }
            bufWidget.setSizeHint(QSize(150, 50))
            if platform.system() != "Linux":
                font = QFont()
                font.setFamily(u"Segoe UI Symbol")
                bufWidget.setFont(font)

            self.sidelist.insertItem(index, bufWidget)
            index += 1
        self.widgetstack = QStackedWidget(self)

        # Setup metadata stuff
        self.xml_doc = xml_doc
        self.is_new_file = True if xml_doc is None else False
        self.md = md
        self.codelist = self.parent().codelists
        self.helps = self.parent().helps
        self.orgs = self.parent().orgs
        f = open(
            os.path.join(pluginDirectory('EditorMetadadosMarswInforbiomares'),
                         'resourcesFolder/stylesheet.qtcss'))
        self.sytlesheet = f.read()
        for btn in self.findChildren(QPushButton):
            btn.setStyleSheet(self.sytlesheet)
            btn.setFocusPolicy(Qt.NoFocus)
        self.reference_systems_list = self.parent().reference_systems
        tab_list = []

        # Setup snimarEditorController
        self.identification = snimarEditorController.IdentificationWidget(
            self, self.scope)
        tab_list.append(self.identification)
        if self.scope == SCOPES.SERVICES:
            self.operations = snimarEditorController.ServiceOperationsWidget(
                self)
            tab_list.append(self.operations)
        self.keywords = snimarEditorController.KeywordsWidget(self, self.scope)
        tab_list.append(self.keywords)
        self.geographicinfo = snimarEditorController.GeographicInfoWidget(
            self, self.scope)
        tab_list.append(self.geographicinfo)
        self.temporalinfo = snimarEditorController.TemporalInfoWidget(
            self, self.scope)
        tab_list.append(self.temporalinfo)
        self.quality = snimarEditorController.QualityWidget(self, self.scope)
        tab_list.append(self.quality)
        self.restrictions = snimarEditorController.RestrictionsWidget(
            self, self.scope)
        tab_list.append(self.restrictions)
        self.distribution = snimarEditorController.DistributionWidget(
            self, self.scope)
        tab_list.append(self.distribution)
        self.metadata = snimarEditorController.MetadataWidget(self)
        tab_list.append(self.metadata)

        self.setupUi()
        if not self.is_new_file:
            # Setup data
            self.identification.set_data(self.md)
            if self.scope == SCOPES.SERVICES:
                self.operations.set_data(md)
            self.temporalinfo.set_data(self.md)
            self.keywords.set_data(self.md)
            self.metadata.set_data(self.md)
            self.distribution.set_data(self.md)
            self.restrictions.set_data(self.md)
            self.quality.set_data(self.md)
            self.geographicinfo.set_data(self.md)
示例#20
0
class MetadadoSNIMar(QWidget):
    def __init__(self, parent, scope=None, xml_doc=None, md=None):
        super(MetadadoSNIMar, self).__init__(parent)
        if scope is None:
            self.scope = SCOPES.get_code_representation(md.hierarchy)
        else:
            self.scope = scope

        self.current_index = 0
        self.widgetStalker = {}
        if platform.system() != "Linux":
            font = QFont()
            font.setFamily(u"Segoe UI Symbol")
            self.setFont(font)

        self.sidelist = QListWidget(self)
        self.sidelist.setMinimumWidth(150)
        self.sidelist.setMaximumWidth(150)
        self.sidelist.setWordWrap(True)
        self.sidelist.setTextElideMode(Qt.ElideNone)
        self.sidelist.setIconSize(QSize(25, 25))
        self.sidelist.clicked.connect(self.list_clicked)
        index = 0
        if self.scope == SCOPES.SERVICES:
            tabs = cons.TABLIST_SERVICES
        else:
            tabs = cons.TABLIST_CDG_SERIES

        for tab_element in tabs:
            bufWidget = QListWidgetItem(
                qgui.QIcon(':/resourcesFolder/icons/' + tab_element[1]),
                tab_element[0])
            self.widgetStalker[tab_element[2]] = {
                "widget": bufWidget,
                "missingFields": set(),
                "incompleteEntries": set()
            }
            bufWidget.setSizeHint(QSize(150, 50))
            if platform.system() != "Linux":
                font = QFont()
                font.setFamily(u"Segoe UI Symbol")
                bufWidget.setFont(font)

            self.sidelist.insertItem(index, bufWidget)
            index += 1
        self.widgetstack = QStackedWidget(self)

        # Setup metadata stuff
        self.xml_doc = xml_doc
        self.is_new_file = True if xml_doc is None else False
        self.md = md
        self.codelist = self.parent().codelists
        self.helps = self.parent().helps
        self.orgs = self.parent().orgs
        f = open(
            os.path.join(pluginDirectory('EditorMetadadosMarswInforbiomares'),
                         'resourcesFolder/stylesheet.qtcss'))
        self.sytlesheet = f.read()
        for btn in self.findChildren(QPushButton):
            btn.setStyleSheet(self.sytlesheet)
            btn.setFocusPolicy(Qt.NoFocus)
        self.reference_systems_list = self.parent().reference_systems
        tab_list = []

        # Setup snimarEditorController
        self.identification = snimarEditorController.IdentificationWidget(
            self, self.scope)
        tab_list.append(self.identification)
        if self.scope == SCOPES.SERVICES:
            self.operations = snimarEditorController.ServiceOperationsWidget(
                self)
            tab_list.append(self.operations)
        self.keywords = snimarEditorController.KeywordsWidget(self, self.scope)
        tab_list.append(self.keywords)
        self.geographicinfo = snimarEditorController.GeographicInfoWidget(
            self, self.scope)
        tab_list.append(self.geographicinfo)
        self.temporalinfo = snimarEditorController.TemporalInfoWidget(
            self, self.scope)
        tab_list.append(self.temporalinfo)
        self.quality = snimarEditorController.QualityWidget(self, self.scope)
        tab_list.append(self.quality)
        self.restrictions = snimarEditorController.RestrictionsWidget(
            self, self.scope)
        tab_list.append(self.restrictions)
        self.distribution = snimarEditorController.DistributionWidget(
            self, self.scope)
        tab_list.append(self.distribution)
        self.metadata = snimarEditorController.MetadataWidget(self)
        tab_list.append(self.metadata)

        self.setupUi()
        if not self.is_new_file:
            # Setup data
            self.identification.set_data(self.md)
            if self.scope == SCOPES.SERVICES:
                self.operations.set_data(md)
            self.temporalinfo.set_data(self.md)
            self.keywords.set_data(self.md)
            self.metadata.set_data(self.md)
            self.distribution.set_data(self.md)
            self.restrictions.set_data(self.md)
            self.quality.set_data(self.md)
            self.geographicinfo.set_data(self.md)

    def setupUi(self):
        self.widgetstack.addWidget(self.identification)
        if self.scope == SCOPES.SERVICES:
            self.widgetstack.addWidget(self.operations)
        self.widgetstack.addWidget(self.keywords)
        self.widgetstack.addWidget(self.geographicinfo)
        self.widgetstack.addWidget(self.temporalinfo)
        self.widgetstack.addWidget(self.quality)
        self.widgetstack.addWidget(self.restrictions)
        self.widgetstack.addWidget(self.distribution)
        self.widgetstack.addWidget(self.metadata)

        self.grid = QGridLayout(self)
        self.grid.addWidget(self.sidelist, 0, 0, 1, 1)
        self.grid.addWidget(self.widgetstack, 0, 1, 1, 1)
        self.setLayout(self.grid)
        self.widgetstack.setCurrentIndex(0)

    @qcore.pyqtSlot()
    def list_clicked(self):
        index = self.sidelist.currentRow()
        if index != self.current_index:
            self.widgetstack.setCurrentIndex(index)
            self.current_index = index

    def get_tab_data(self, md):
        if self.scope != SCOPES.SERVICES:
            md.identification = snimarProfileModel.MD_DataIdentification()
        else:
            md.serviceidentification = snimarProfileModel.SV_ServiceIdentification(
            )

        self.identification.get_data(md)
        if self.scope == SCOPES.SERVICES:
            self.operations.get_data(md)
        self.keywords.get_data(md)
        self.geographicinfo.get_data(md)
        self.temporalinfo.get_data(md)
        self.quality.get_data(md)
        self.restrictions.get_data(md)
        self.distribution.get_data(md)
        self.metadata.get_data(md)

    # ------------------------------------------------------------------------
    #                    Validation STUFF
    # ------------------------------------------------------------------------

    def is_doc_Snimar_Valid(self):
        for x in list(self.widgetStalker.values()):
            if len(x["missingFields"]) > 0 or len(x["incompleteEntries"]) > 0:
                return False

        return True

    def register_mandatory_missingfield(self, widgetName, fieldName):
        self.widgetStalker[widgetName]["missingFields"].add(
            fieldName.replace(u'\u26a0', '').strip())
        self.widgetStalker[widgetName]["widget"].setToolTip(
            self.genToolTip(widgetName))
        self.widgetStalker[widgetName]["widget"].setForeground(
            QColor(cons.ERROR_COLOR))

        self.widgetStalker[widgetName]["widget"].setText(
            self.widgetStalker[widgetName]["widget"].text().replace(
                u'\u26a0', '') + u'\u26a0')

    def unregister_mandatory_missingfield(self, widgetName, fieldName):
        self.widgetStalker[widgetName]["missingFields"].discard(fieldName)
        if len(self.widgetStalker[widgetName]
               ["incompleteEntries"]) != 0 or len(
                   self.widgetStalker[widgetName]["missingFields"]) != 0:
            self.widgetStalker[widgetName]["widget"].setText(
                self.widgetStalker[widgetName]["widget"].text().replace(
                    u'\u26a0', '') + u'\u26a0')
        else:
            self.widgetStalker[widgetName]["widget"].setForeground(
                QColor("black"))
            self.widgetStalker[widgetName]["widget"].setText(
                self.widgetStalker[widgetName]["widget"].text().replace(
                    u'\u26a0', ''))

        self.widgetStalker[widgetName]["widget"].setToolTip(
            self.genToolTip(widgetName))

    def register_incomplete_entries(self, widgetName, fieldName):
        self.widgetStalker[widgetName]["incompleteEntries"].add(
            fieldName.replace(u'\u26a0', '').strip())
        self.widgetStalker[widgetName]["widget"].setToolTip(
            self.genToolTip(widgetName))
        self.widgetStalker[widgetName]["widget"].setForeground(
            QColor(cons.ERROR_COLOR))
        self.widgetStalker[widgetName]["widget"].setText(
            self.widgetStalker[widgetName]["widget"].text().replace(
                u'\u26a0', '') + u'\u26a0')

    def unregister_incomplete_entries(self, widgetName, fieldName):
        self.widgetStalker[widgetName]["incompleteEntries"].discard(fieldName)
        if len(self.widgetStalker[widgetName]
               ["incompleteEntries"]) != 0 or len(
                   self.widgetStalker[widgetName]["missingFields"]) != 0:
            self.widgetStalker[widgetName]["widget"].setText(
                self.widgetStalker[widgetName]["widget"].text().replace(
                    u'\u26a0', '') + u'\u26a0')
        else:
            self.widgetStalker[widgetName]["widget"].setForeground(
                QColor("black"))
            self.widgetStalker[widgetName]["widget"].setText(
                self.widgetStalker[widgetName]["widget"].text().replace(
                    u'\u26a0', ''))
        self.widgetStalker[widgetName]["widget"].setToolTip(
            self.genToolTip(widgetName))

    def genToolTip(self, widgetName):
        tooltip = ""
        if len(self.widgetStalker[widgetName]["missingFields"]) > 0:
            tooltip += u"Campos Obrigatórios\nem falta:\n-" + "\n-".join(
                self.widgetStalker[widgetName]["missingFields"])
        if len(self.widgetStalker[widgetName]["missingFields"]) > 0 and len(
                self.widgetStalker[widgetName]["incompleteEntries"]) > 0:
            tooltip += u"\n---------------------\n"
        if len(self.widgetStalker[widgetName]["incompleteEntries"]) > 0:
            tooltip += u"Campos Incompletos\Incorrectos:\n-" + "\n-".join(
                self.widgetStalker[widgetName]["incompleteEntries"])
        tooltip += u"\n---------------------\n"
        return tooltip + u"\nA Conformidade diz respeito\n" \
                         u"as obrigações de formato " \
                         u"\ne completude do documento.\n" \
                         u"A validade do conteúdo é da\n" \
                         u"inteira responsabilidade\n" \
                         u"do utilizador. "
class GroupSelectParameterWidget(GenericParameterWidget):

    """Widget class for Group Select Parameter."""

    def __init__(self, parameter, parent=None):
        """Constructor.

        :param parameter: A GroupSelectParameter object.
        :type parameter: GroupSelectParameter
        """
        QWidget.__init__(self, parent)
        self._parameter = parameter

        # Store spin box
        self.spin_boxes = {}

        # Create elements
        # Label (name)
        self.label = QLabel(self._parameter.name)

        # Layouts
        self.main_layout = QVBoxLayout()
        self.input_layout = QVBoxLayout()

        # _inner_input_layout must be filled with widget in the child class
        self.inner_input_layout = QVBoxLayout()

        self.radio_button_layout = QGridLayout()

        # Create radio button group
        self.input_button_group = QButtonGroup()

        # List widget
        self.list_widget = QListWidget()
        self.list_widget.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.list_widget.setDragDropMode(QAbstractItemView.DragDrop)
        self.list_widget.setDefaultDropAction(Qt.MoveAction)
        self.list_widget.setEnabled(False)
        self.list_widget.setSizePolicy(
            QSizePolicy.Maximum, QSizePolicy.Expanding)

        for i, key in enumerate(self._parameter.options):
            value = self._parameter.options[key]
            radio_button = QRadioButton(value.get('label'))
            self.radio_button_layout.addWidget(radio_button, i, 0)
            if value.get('type') == SINGLE_DYNAMIC:
                percentage_spin_box = PercentageSpinBox(self)
                self.radio_button_layout.addWidget(percentage_spin_box, i, 1)
                percentage_spin_box.setValue(value.get('value', 0))
                step = percentage_spin_box.singleStep()
                if step > 1:
                    precision = 0
                else:
                    precision = len(str(step).split('.')[1])
                    if precision > 3:
                        precision = 3
                percentage_spin_box.setDecimals(precision)
                self.spin_boxes[key] = percentage_spin_box

                # Enable spin box depends on the selected option
                if self._parameter.selected == key:
                    percentage_spin_box.setEnabled(True)
                else:
                    percentage_spin_box.setEnabled(False)

            elif value.get('type') == STATIC:
                static_value = value.get('value', 0)
                if static_value is not None:
                    self.radio_button_layout.addWidget(
                        QLabel(str(static_value * 100) + ' %'), i, 1)
            elif value.get('type') == MULTIPLE_DYNAMIC:
                if self._parameter.selected == key:
                    self.list_widget.setEnabled(True)
                else:
                    self.list_widget.setEnabled(False)

            self.input_button_group.addButton(radio_button, i)
            if self._parameter.selected == key:
                radio_button.setChecked(True)

        # Help text
        self.help_label = QLabel(self._parameter.help_text)
        self.help_label.setSizePolicy(
            QSizePolicy.Maximum, QSizePolicy.Expanding)
        self.help_label.setWordWrap(True)
        self.help_label.setAlignment(Qt.AlignTop)

        self.inner_input_layout.addLayout(self.radio_button_layout)
        self.inner_input_layout.addWidget(self.list_widget)

        # Put elements into layouts
        self.input_layout.addWidget(self.label)
        self.input_layout.addLayout(self.inner_input_layout)

        self.help_layout = QVBoxLayout()
        self.help_layout.addWidget(self.help_label)

        self.main_layout.addLayout(self.input_layout)
        self.main_layout.addLayout(self.help_layout)

        self.setLayout(self.main_layout)

        self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Expanding)

        # Update list widget
        self.update_list_widget()

        # Connect signal
        # noinspection PyUnresolvedReferences
        self.input_button_group.buttonClicked.connect(
            self.radio_buttons_clicked)

    def get_parameter(self):
        """Obtain list parameter object from the current widget state.

        :returns: A DefaultValueParameter from the current state of widget
        :rtype: DefaultValueParameter
        """
        # Set value for each key
        for key, value in list(self._parameter.options.items()):
            if value.get('type') == STATIC:
                continue
            elif value.get('type') == SINGLE_DYNAMIC:
                new_value = self.spin_boxes.get(key).value()
                self._parameter.set_value_for_key(key, new_value)
            elif value.get('type') == MULTIPLE_DYNAMIC:
                # Need to iterate through all items
                items = []
                for index in range(self.list_widget.count()):
                    items.append(self.list_widget.item(index))
                new_value = [i.text() for i in items]
                self._parameter.set_value_for_key(key, new_value)

        # Get selected radio button
        radio_button_checked_id = self.input_button_group.checkedId()
        # No radio button checked, then default value = None
        if radio_button_checked_id == -1:
            self._parameter.selected = None
        else:
            self._parameter.selected = list(self._parameter.options.keys())[
                radio_button_checked_id]

        return self._parameter

    def update_list_widget(self):
        """Update list widget when radio button is clicked."""
        # Get selected radio button
        radio_button_checked_id = self.input_button_group.checkedId()
        # No radio button checked, then default value = None
        if radio_button_checked_id > -1:
            selected_dict = list(self._parameter.options.values())[
                radio_button_checked_id]
            if selected_dict.get('type') == MULTIPLE_DYNAMIC:
                for field in selected_dict.get('value'):
                    # Update list widget
                    field_item = QListWidgetItem(self.list_widget)
                    field_item.setFlags(
                        Qt.ItemIsEnabled
                        | Qt.ItemIsSelectable
                        | Qt.ItemIsDragEnabled)
                    field_item.setData(Qt.UserRole, field)
                    field_item.setText(field)
                    self.list_widget.addItem(field_item)

    def radio_buttons_clicked(self):
        """Handler when selected radio button changed."""
        # Disable all spin boxes
        for spin_box in list(self.spin_boxes.values()):
            spin_box.setEnabled(False)
        # Disable list widget
        self.list_widget.setEnabled(False)

        # Get selected radio button
        radio_button_checked_id = self.input_button_group.checkedId()

        if radio_button_checked_id > -1:
            selected_value = list(self._parameter.options.values())[
                radio_button_checked_id]
            if selected_value.get('type') == MULTIPLE_DYNAMIC:
                # Enable list widget
                self.list_widget.setEnabled(True)
            elif selected_value.get('type') == SINGLE_DYNAMIC:
                selected_key = list(self._parameter.options.keys())[
                    radio_button_checked_id]
                self.spin_boxes[selected_key].setEnabled(True)

    def select_radio_button(self, key):
        """Helper to select a radio button with key.

        :param key: The key of the radio button.
        :type key: str
        """
        key_index = list(self._parameter.options.keys()).index(key)
        radio_button = self.input_button_group.button(key_index)
        radio_button.click()
    def setup_value_mapping_panels(self, classification):
        """Setup value mapping panel in the right panel.

        :param classification: Classification definition.
        :type classification: dict
        """
        # Set text in the label
        layer_purpose = self.parent.step_kw_purpose.selected_purpose()
        layer_subcategory = self.parent.step_kw_subcategory. \
            selected_subcategory()

        if is_raster_layer(self.parent.layer):
            description_text = classify_raster_question % (
                layer_subcategory['name'],
                layer_purpose['name'],
                classification['name'])

            dataset = gdal.Open(self.parent.layer.source(), GA_ReadOnly)
            active_band = self.parent.step_kw_band_selector.selected_band()
            unique_values = numpy.unique(numpy.array(
                dataset.GetRasterBand(active_band).ReadAsArray()))
            field_type = 0
            # Convert datatype to a json serializable type
            if numpy.issubdtype(unique_values.dtype, float):
                unique_values = [float(i) for i in unique_values]
            else:
                unique_values = [int(i) for i in unique_values]
        else:
            field = self.parent.step_kw_field.selected_fields()
            field_index = self.parent.layer.fields().indexFromName(field)
            field_type = self.parent.layer.fields()[field_index].type()
            description_text = classify_vector_question % (
                layer_subcategory['name'],
                layer_purpose['name'],
                classification['name'],
                field.upper())
            unique_values = list(self.parent.layer.uniqueValues(field_index))

        # Set description
        description_label = QLabel(description_text)
        description_label.setWordWrap(True)
        self.right_layout.addWidget(description_label)

        self.list_unique_values = QListWidget()
        self.list_unique_values.setDragDropMode(QAbstractItemView.DragDrop)
        self.list_unique_values.setDefaultDropAction(Qt.MoveAction)

        self.tree_mapping_widget = QTreeWidget()
        self.tree_mapping_widget.setDragDropMode(QAbstractItemView.DragDrop)
        self.tree_mapping_widget.setDefaultDropAction(Qt.MoveAction)
        self.tree_mapping_widget.header().hide()

        self.tree_mapping_widget.itemChanged.connect(
            self.update_dragged_item_flags)

        value_mapping_layout = QHBoxLayout()
        value_mapping_layout.addWidget(self.list_unique_values)
        value_mapping_layout.addWidget(self.tree_mapping_widget)

        self.right_layout.addLayout(value_mapping_layout)

        default_classes = classification['classes']

        # Assign unique values to classes (according to default)
        unassigned_values = list()
        assigned_values = dict()
        for default_class in default_classes:
            assigned_values[default_class['key']] = list()
        for unique_value in unique_values:
            if (unique_value is None
                    or (hasattr(unique_value, 'isNull')
                        and unique_value.isNull())):
                # Don't classify features with NULL value
                continue
            # Capitalization of the value and removing '_' (raw OSM data).
            value_as_string = str(unique_value).upper().replace('_', ' ')
            assigned = False
            for default_class in default_classes:
                if 'string_defaults' in default_class:
                    # To make it case insensitive
                    upper_string_defaults = [
                        c.upper() for c in default_class['string_defaults']]
                    in_string_default = (
                        value_as_string in upper_string_defaults)
                    condition_1 = field_type > 9 and in_string_default
                else:
                    condition_1 = False
                condition_2 = (
                    field_type < 10
                    and 'numeric_default_min' in default_class
                    and 'numeric_default_max' in default_class
                    and (default_class['numeric_default_min']
                         <= unique_value
                         < default_class['numeric_default_max']))
                if condition_1 or condition_2:
                    assigned_values[default_class['key']] += [unique_value]
                    assigned = True
                    break
            if not assigned:
                # add to unassigned values list otherwise
                unassigned_values += [unique_value]
        self.populate_classified_values(
            unassigned_values,
            assigned_values,
            default_classes,
            self.list_unique_values,
            self.tree_mapping_widget
        )

        # Current value map for exposure and classification
        available_classifications = self.value_maps.get(
            self.active_exposure['key'])
        if not available_classifications:
            return
        # Get active one
        current_classification = available_classifications.get(
            classification['key'])
        if not current_classification:
            return
        # Should come from metadata
        current_value_map = current_classification.get('classes')
        if not current_value_map:
            return

        unassigned_values = list()
        assigned_values = dict()
        for default_class in default_classes:
            assigned_values[default_class['key']] = list()
        for unique_value in unique_values:
            if (unique_value is None
                    or (hasattr(unique_value, 'isNull')
                        and unique_value.isNull())):
                # Don't classify features with NULL value
                continue
            # check in value map
            assigned = False
            for key, value_list in list(current_value_map.items()):
                if unique_value in value_list and key in assigned_values:
                    assigned_values[key] += [unique_value]
                    assigned = True
            if not assigned:
                unassigned_values += [unique_value]
        self.populate_classified_values(
            unassigned_values,
            assigned_values,
            default_classes,
            self.list_unique_values,
            self.tree_mapping_widget
        )
示例#23
0
class PlotLayoutItemWidget(QgsLayoutItemBaseWidget):
    """
    Configuration widget for layout plot items
    """
    def __init__(self, parent, layout_object):
        super().__init__(parent, layout_object)
        self.plot_item = layout_object
        self.message_bar = None

        vl = QVBoxLayout()
        vl.setContentsMargins(0, 0, 0, 0)

        plot_tools_layout = QHBoxLayout()

        plot_add_button = QPushButton()
        plot_add_button.setIcon(GuiUtils.get_icon('symbologyAdd.svg'))
        plot_add_button.setToolTip('Add a new plot')
        plot_tools_layout.addWidget(plot_add_button)
        plot_add_button.clicked.connect(self.add_plot)

        plot_remove_button = QPushButton()
        plot_remove_button.setIcon(GuiUtils.get_icon('symbologyRemove.svg'))
        plot_remove_button.setToolTip('Remove selected plot')
        plot_tools_layout.addWidget(plot_remove_button)
        plot_remove_button.clicked.connect(self.remove_plot)

        plot_duplicate_button = QPushButton()
        plot_duplicate_button.setIcon(
            GuiUtils.get_icon('mActionDuplicateLayer.svg'))
        plot_duplicate_button.setToolTip('Duplicates the selected plot')
        plot_tools_layout.addWidget(plot_duplicate_button)
        plot_duplicate_button.clicked.connect(self.duplicate_plot)

        plot_move_up_button = QPushButton()
        plot_move_up_button.setIcon(GuiUtils.get_icon('mActionArrowUp.svg'))
        plot_move_up_button.setToolTip('Move selected plot up')
        plot_tools_layout.addWidget(plot_move_up_button)
        plot_move_up_button.clicked.connect(self.move_up_plot)

        plot_move_down_button = QPushButton()
        plot_move_down_button.setIcon(
            GuiUtils.get_icon('mActionArrowDown.svg'))
        plot_move_down_button.setToolTip('Move selected plot down')
        plot_tools_layout.addWidget(plot_move_down_button)
        plot_move_down_button.clicked.connect(self.move_down_plot)

        vl.addLayout(plot_tools_layout)

        self.plot_list = QListWidget()
        self.plot_list.setSelectionMode(QListWidget.SingleSelection)
        self.plot_list.doubleClicked.connect(self.show_properties)
        vl.addWidget(self.plot_list)
        self.populate_plot_list()

        plot_properties_button = QPushButton(self.tr('Setup Selected Plot'))
        vl.addWidget(plot_properties_button)
        plot_properties_button.clicked.connect(self.show_properties)

        self.panel = None
        self.setPanelTitle(self.tr('Plot Properties'))
        self.item_properties_widget = QgsLayoutItemPropertiesWidget(
            self, layout_object)
        vl.addWidget(self.item_properties_widget)
        self.setLayout(vl)

    def populate_plot_list(self):
        """
        Clears and re-populates the plot list widget. The currently selection is retained
        """
        selected_index = self.plot_list.currentRow()
        self.plot_list.clear()
        for setting in self.plot_item.plot_settings:
            plot_type = setting.plot_type if setting.plot_type is not None else '(not set)'
            legend_title = ('[' + setting.properties.get('name') + ']') \
                if setting.properties.get('name', '') != '' else ''
            self.plot_list.addItem(plot_type + ' ' + legend_title)

        # select index within range [0, len(plot_settings)-1]
        selected_index = max(
            0, min(len(self.plot_item.plot_settings) - 1, selected_index))
        self.plot_list.setCurrentRow(selected_index,
                                     QItemSelectionModel.SelectCurrent)

    def add_plot(self):
        """
         Adds a new plot and updates the plot list and the plot item
        """
        self.plot_item.add_plot()
        self.populate_plot_list()
        self.plot_item.refresh()

    def duplicate_plot(self):
        """
         Duplicates an existing plot and updates the plot list and the plot item
        """

        selected_plot_index = self.plot_list.currentRow()
        if selected_plot_index < 0:
            return

        self.plot_item.duplicate_plot(selected_plot_index)
        self.populate_plot_list()
        self.plot_item.refresh()

    def remove_plot(self):
        """
        Removes the selected plot and updates the plot list and the plot item
        """
        selected_index = self.plot_list.currentRow()
        if selected_index < 0:
            return

        self.plot_item.remove_plot(selected_index)
        self.populate_plot_list()
        self.plot_item.refresh()

    def move_up_plot(self):
        """
        Moves the selected plot up and updates the plot list and the plot item
        """
        selected_index = self.plot_list.currentRow()
        if selected_index <= 0:
            return

        item = self.plot_item.plot_settings.pop(selected_index)
        self.plot_item.plot_settings.insert(selected_index - 1, item)
        self.plot_list.setCurrentRow(selected_index - 1,
                                     QItemSelectionModel.SelectCurrent)
        self.populate_plot_list()
        self.plot_item.refresh()

    def move_down_plot(self):
        """
        Moves the selected plot down and updates the plot list and the plot item
        """
        selected_index = self.plot_list.currentRow()
        if selected_index >= len(self.plot_item.plot_settings) - 1:
            return

        item = self.plot_item.plot_settings.pop(selected_index)
        self.plot_item.plot_settings.insert(selected_index + 1, item)
        self.plot_list.setCurrentRow(selected_index + 1,
                                     QItemSelectionModel.SelectCurrent)
        self.populate_plot_list()
        self.plot_item.refresh()

    def show_properties(self):
        """
        Shows the plot properties panel
        """
        selected_plot_index = self.plot_list.currentRow()
        if selected_plot_index < 0:
            return

        self.panel = DataPlotlyPanelWidget(
            mode=DataPlotlyPanelWidget.MODE_LAYOUT,
            message_bar=self.message_bar)

        # not quite right -- we ideally want to also add the source layer scope into the context given by plot item,
        # but that causes a hard lock in the Python GIL (because PyQt doesn't release the GIL when creating the menu
        # for the property override buttons). Nothing much we can do about that here (or in QGIS,
        # it's a Python/PyQt limitation)
        self.panel.registerExpressionContextGenerator(self.plot_item)
        self.panel.set_print_layout(self.plot_item.layout())

        self.panel.linked_map_combo.blockSignals(True)
        self.panel.linked_map_combo.setItem(self.plot_item.linked_map)
        self.panel.linked_map_combo.blockSignals(False)

        self.panel.linked_map_combo.itemChanged.connect(
            self.linked_map_changed)

        self.panel.set_settings(
            self.plot_item.plot_settings[selected_plot_index])
        # self.panel.set_settings(self.layoutItem().plot_settings)
        self.openPanel(self.panel)
        self.panel.widgetChanged.connect(self.update_item_settings)
        self.panel.panelAccepted.connect(self.set_item_settings)

    def update_item_settings(self):
        """
        Updates the plot item without dismissing the properties panel
        """
        if not self.panel:
            return

        self.plot_item.set_plot_settings(self.plot_list.currentRow(),
                                         self.panel.get_settings())
        self.populate_plot_list()
        self.plot_item.update()

    def set_item_settings(self):
        """
        Updates the plot item based on the settings from the properties panel
        """
        if not self.panel:
            return

        self.plot_item.set_plot_settings(self.plot_list.currentRow(),
                                         self.panel.get_settings())
        self.populate_plot_list()
        self.panel = None
        self.plot_item.update()

    def linked_map_changed(self, linked_map):
        """
        Triggered when the linked map is changed
        """
        self.plot_item.set_linked_map(linked_map)
        self.plot_item.update()

    def setNewItem(self, item):  # pylint: disable=missing-docstring
        if item.type() != ITEM_TYPE:
            return False

        self.plot_item = item
        self.item_properties_widget.setItem(item)
        self.populate_plot_list()

        if self.panel is not None:
            self.panel.set_settings(self.plot_item.plot_settings[0])

            self.panel.linked_map_combo.blockSignals(True)
            self.panel.linked_map_combo.setItem(self.plot_item.linked_map)
            self.panel.linked_map_combo.blockSignals(False)

        return True

    def setDesignerInterface(self, iface):  # pylint: disable=missing-docstring
        super().setDesignerInterface(iface)
        self.message_bar = iface.messageBar()
        if self.panel:
            self.panel.message_bar = self.message_bar
    def __init__(self, parameter, parent=None):
        """Constructor.

        :param parameter: A GroupSelectParameter object.
        :type parameter: GroupSelectParameter
        """
        QWidget.__init__(self, parent)
        self._parameter = parameter

        # Store spin box
        self.spin_boxes = {}

        # Create elements
        # Label (name)
        self.label = QLabel(self._parameter.name)

        # Layouts
        self.main_layout = QVBoxLayout()
        self.input_layout = QVBoxLayout()

        # _inner_input_layout must be filled with widget in the child class
        self.inner_input_layout = QVBoxLayout()

        self.radio_button_layout = QGridLayout()

        # Create radio button group
        self.input_button_group = QButtonGroup()

        # List widget
        self.list_widget = QListWidget()
        self.list_widget.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.list_widget.setDragDropMode(QAbstractItemView.DragDrop)
        self.list_widget.setDefaultDropAction(Qt.MoveAction)
        self.list_widget.setEnabled(False)
        self.list_widget.setSizePolicy(
            QSizePolicy.Maximum, QSizePolicy.Expanding)

        for i, key in enumerate(self._parameter.options):
            value = self._parameter.options[key]
            radio_button = QRadioButton(value.get('label'))
            self.radio_button_layout.addWidget(radio_button, i, 0)
            if value.get('type') == SINGLE_DYNAMIC:
                percentage_spin_box = PercentageSpinBox(self)
                self.radio_button_layout.addWidget(percentage_spin_box, i, 1)
                percentage_spin_box.setValue(value.get('value', 0))
                step = percentage_spin_box.singleStep()
                if step > 1:
                    precision = 0
                else:
                    precision = len(str(step).split('.')[1])
                    if precision > 3:
                        precision = 3
                percentage_spin_box.setDecimals(precision)
                self.spin_boxes[key] = percentage_spin_box

                # Enable spin box depends on the selected option
                if self._parameter.selected == key:
                    percentage_spin_box.setEnabled(True)
                else:
                    percentage_spin_box.setEnabled(False)

            elif value.get('type') == STATIC:
                static_value = value.get('value', 0)
                if static_value is not None:
                    self.radio_button_layout.addWidget(
                        QLabel(str(static_value * 100) + ' %'), i, 1)
            elif value.get('type') == MULTIPLE_DYNAMIC:
                if self._parameter.selected == key:
                    self.list_widget.setEnabled(True)
                else:
                    self.list_widget.setEnabled(False)

            self.input_button_group.addButton(radio_button, i)
            if self._parameter.selected == key:
                radio_button.setChecked(True)

        # Help text
        self.help_label = QLabel(self._parameter.help_text)
        self.help_label.setSizePolicy(
            QSizePolicy.Maximum, QSizePolicy.Expanding)
        self.help_label.setWordWrap(True)
        self.help_label.setAlignment(Qt.AlignTop)

        self.inner_input_layout.addLayout(self.radio_button_layout)
        self.inner_input_layout.addWidget(self.list_widget)

        # Put elements into layouts
        self.input_layout.addWidget(self.label)
        self.input_layout.addLayout(self.inner_input_layout)

        self.help_layout = QVBoxLayout()
        self.help_layout.addWidget(self.help_label)

        self.main_layout.addLayout(self.input_layout)
        self.main_layout.addLayout(self.help_layout)

        self.setLayout(self.main_layout)

        self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Expanding)

        # Update list widget
        self.update_list_widget()

        # Connect signal
        # noinspection PyUnresolvedReferences
        self.input_button_group.buttonClicked.connect(
            self.radio_buttons_clicked)
示例#25
0
    def __init__(self, field_group=None, parent=None, iface=None):
        """Constructor."""
        # Init from parent class
        QWidget.__init__(self, parent)

        # Attributes
        self.layer = None
        self.metadata = {}
        self.parent = parent
        self.iface = iface
        self.field_group = field_group
        self.setting = QSettings()  # TODO(IS): Make dynamic

        # Main container
        self.main_layout = QVBoxLayout()

        # Inner layout
        self.header_layout = QHBoxLayout()
        self.content_layout = QHBoxLayout()
        self.footer_layout = QHBoxLayout()

        # Header
        self.header_label = QLabel()
        self.header_label.setWordWrap(True)

        # Content
        self.field_layout = QVBoxLayout()
        self.parameter_layout = QHBoxLayout()

        self.field_description = QLabel(tr('List of fields'))

        self.field_list = QListWidget()
        self.field_list.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.field_list.setDragDropMode(QAbstractItemView.DragDrop)
        self.field_list.setDefaultDropAction(Qt.MoveAction)

        self.field_list.setSizePolicy(
            QSizePolicy.Maximum, QSizePolicy.Expanding)
        # noinspection PyUnresolvedReferences
        self.field_list.itemSelectionChanged.connect(self.update_footer)

        # Footer
        self.footer_label = QLabel()
        self.footer_label.setWordWrap(True)

        # Parameters
        self.extra_parameters = [
            (GroupSelectParameter, GroupSelectParameterWidget)
        ]

        self.parameters = []
        self.parameter_container = None

        # Adding to layout
        self.header_layout.addWidget(self.header_label)

        self.field_layout.addWidget(self.field_description)
        self.field_layout.addWidget(self.field_list)
        self.field_layout.setSizeConstraint(QLayout.SetMaximumSize)

        self.content_layout.addLayout(self.field_layout)
        self.content_layout.addLayout(self.parameter_layout)

        self.footer_layout.addWidget(self.footer_label)

        self.main_layout.addLayout(self.header_layout)
        self.main_layout.addLayout(self.content_layout)
        self.main_layout.addLayout(self.footer_layout)

        self.setLayout(self.main_layout)
示例#26
0
class FieldMappingTab(QWidget, object):

    """Widget class for field mapping."""

    def __init__(self, field_group=None, parent=None, iface=None):
        """Constructor."""
        # Init from parent class
        QWidget.__init__(self, parent)

        # Attributes
        self.layer = None
        self.metadata = {}
        self.parent = parent
        self.iface = iface
        self.field_group = field_group
        self.setting = QSettings()  # TODO(IS): Make dynamic

        # Main container
        self.main_layout = QVBoxLayout()

        # Inner layout
        self.header_layout = QHBoxLayout()
        self.content_layout = QHBoxLayout()
        self.footer_layout = QHBoxLayout()

        # Header
        self.header_label = QLabel()
        self.header_label.setWordWrap(True)

        # Content
        self.field_layout = QVBoxLayout()
        self.parameter_layout = QHBoxLayout()

        self.field_description = QLabel(tr('List of fields'))

        self.field_list = QListWidget()
        self.field_list.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.field_list.setDragDropMode(QAbstractItemView.DragDrop)
        self.field_list.setDefaultDropAction(Qt.MoveAction)

        self.field_list.setSizePolicy(
            QSizePolicy.Maximum, QSizePolicy.Expanding)
        # noinspection PyUnresolvedReferences
        self.field_list.itemSelectionChanged.connect(self.update_footer)

        # Footer
        self.footer_label = QLabel()
        self.footer_label.setWordWrap(True)

        # Parameters
        self.extra_parameters = [
            (GroupSelectParameter, GroupSelectParameterWidget)
        ]

        self.parameters = []
        self.parameter_container = None

        # Adding to layout
        self.header_layout.addWidget(self.header_label)

        self.field_layout.addWidget(self.field_description)
        self.field_layout.addWidget(self.field_list)
        self.field_layout.setSizeConstraint(QLayout.SetMaximumSize)

        self.content_layout.addLayout(self.field_layout)
        self.content_layout.addLayout(self.parameter_layout)

        self.footer_layout.addWidget(self.footer_label)

        self.main_layout.addLayout(self.header_layout)
        self.main_layout.addLayout(self.content_layout)
        self.main_layout.addLayout(self.footer_layout)

        self.setLayout(self.main_layout)

    def set_layer(self, layer, keywords=None):
        """Set layer and update UI accordingly.

        :param layer: A vector layer that has been already patched with
            metadata.
        :type layer: QgsVectorLayer

        :param keywords: Custom keyword for the layer.
        :type keywords: dict, None
        """
        self.layer = layer
        if keywords is not None:
            self.metadata = keywords
        else:
            # Check if it has keywords
            if not hasattr(layer, 'keywords'):
                message = 'Layer {layer_name} does not have keywords.'.format(
                    layer_name=layer.name())
                raise KeywordNotFoundError(message)
            self.metadata = layer.keywords
        self.populate_parameter()

    def populate_field_list(self, excluded_fields=None):
        """Helper to add field of the layer to the list.

        :param excluded_fields: List of field that want to be excluded.
        :type excluded_fields: list
        """
        # Populate fields list
        if excluded_fields is None:
            excluded_fields = []
        self.field_list.clear()
        for field in self.layer.fields():
            # Skip if it's excluded
            if field.name() in excluded_fields:
                continue
            # Skip if it's not number (float, int, etc)
            if field.type() not in qvariant_numbers:
                continue
            field_item = QListWidgetItem(self.field_list)
            field_item.setFlags(
                Qt.ItemIsEnabled
                | Qt.ItemIsSelectable
                | Qt.ItemIsDragEnabled)
            field_item.setData(Qt.UserRole, field.name())
            field_item.setText(field.name())
            self.field_list.addItem(field_item)

    def populate_parameter(self):
        """Helper to setup the parameter widget."""
        used_fields = []
        self.parameters = []
        for field in self.field_group.get('fields', []):
            selected_option = DO_NOT_REPORT
            options = OrderedDict([
                (DO_NOT_REPORT,
                 {
                     'label': tr('Do not report'),
                     'value': None,
                     'type': STATIC,
                     'constraint': {}
                 }),
            ])

            # Example: count
            if field['absolute']:
                # Used in field options
                field_label = tr('Count fields')
            else:  # Example: ratio
                # Used in field options
                field_label = tr('Ratio fields')
                global_default_value = get_inasafe_default_value_qsetting(
                    self.setting, GLOBAL, field['key'])
                options[GLOBAL_DEFAULT] = {
                    'label': tr('Global default'),
                    'value': global_default_value,
                    'type': STATIC,
                    'constraint': {}
                }
                default_custom_value = get_inasafe_default_value_qsetting(
                    self.setting, RECENT, field['key'])
                custom_value = self.metadata.get(
                    'inasafe_default_values', {}).get(
                    field['key'], default_custom_value)
                if field['key'] in self.metadata.get(
                        'inasafe_default_values', {}):
                    if custom_value == global_default_value:
                        selected_option = GLOBAL_DEFAULT
                    else:
                        selected_option = CUSTOM_VALUE
                min_value = field['default_value'].get('min_value', 0)
                max_value = field['default_value'].get('max_value', 100)
                default_step = (max_value - min_value) / 100.0
                step = field['default_value'].get('increment', default_step)
                options[CUSTOM_VALUE] = {
                    'label': tr('Custom'),
                    'value': custom_value,
                    'type': SINGLE_DYNAMIC,
                    'constraint': {
                        'min': min_value,
                        'max': max_value,
                        'step': step
                    }
                }

            custom_fields = self.metadata.get('inasafe_fields', {}).get(
                field['key'], [])
            if field['key'] in self.metadata.get('inasafe_fields', {}):
                selected_option = FIELDS
            if isinstance(custom_fields, str):
                custom_fields = [custom_fields]
            options[FIELDS] = {
                'label': field_label,
                'value': custom_fields,
                'type': MULTIPLE_DYNAMIC,
                'constraint': {}
            }
            used_fields.extend(custom_fields)

            parameter = GroupSelectParameter()
            parameter.guid = field['key']
            parameter.name = field['name']
            parameter.options = options
            parameter.selected = selected_option
            parameter.help_text = field['help_text']
            parameter.description = field['description']

            self.parameters.append(parameter)

        self.parameter_container = ParameterContainer(
            parameters=self.parameters,
            extra_parameters=self.extra_parameters,
            vertical=False
        )
        self.parameter_container.setup_ui()

        constraints = self.field_group.get('constraints', {})

        for key, value in list(constraints.items()):
            self.parameter_container.add_validator(
                validators[key],
                kwargs=value['kwargs'],
                validation_message=value['message'])

        self.parameter_layout.addWidget(self.parameter_container)

        default_ratio_help_text = tr(
            'By default, InaSAFE will calculate the default ratio '
            'however users have the option to include this in the '
            'analysis report. If you do not want to see the default '
            'results in the report choose "do not report".')
        # Set move or copy
        if self.field_group.get('exclusive', False):
            # If exclusive, do not add used field.
            self.populate_field_list(excluded_fields=used_fields)
            # Use move action since it's exclusive
            self.field_list.setDefaultDropAction(Qt.MoveAction)
            # Just make sure that the signal is disconnected
            try:
                # noinspection PyUnresolvedReferences
                self.field_list.itemChanged.disconnect(self.drop_remove)
            except TypeError:
                pass
            # Set header
            header_text = self.field_group['description']
            header_text += '\n\n' + default_ratio_help_text
            header_text += '\n\n' + tr(
                'You can only map one field to one concept.')
        else:
            # If not exclusive, add all field.
            self.populate_field_list()
            # Use copy action since it's not exclusive
            self.field_list.setDefaultDropAction(Qt.CopyAction)
            # noinspection PyUnresolvedReferences
            self.field_list.itemChanged.connect(
                partial(self.drop_remove, field_list=self.field_list))
            self.connect_drop_remove_parameter()
            # Set header
            header_text = self.field_group['description']
            header_text += '\n\n' + default_ratio_help_text
            header_text += '\n\n' + tr(
                'You can map one field to more than one concepts.')

        self.header_label.setText(header_text)

    def get_parameter_value(self):
        """Get parameter of the tab.

        :returns: Dictionary of parameters by type in this format:
            {'fields': {}, 'values': {}}.
        :rtype: dict
        """
        parameters = self.parameter_container.get_parameters(True)
        field_parameters = {}
        value_parameters = {}
        for parameter in parameters:
            if parameter.selected_option_type() in [SINGLE_DYNAMIC, STATIC]:
                value_parameters[parameter.guid] = parameter.value
            elif parameter.selected_option_type() == MULTIPLE_DYNAMIC:
                field_parameters[parameter.guid] = parameter.value
        return {
            'fields': field_parameters,
            'values': value_parameters
        }

    def update_footer(self):
        """Update footer when the field list change."""
        field_item = self.field_list.currentItem()

        if not field_item:
            self.footer_label.setText('')
            return

        field_name = field_item.data(Qt.UserRole)
        field = self.layer.fields().field(field_name)

        index = self.layer.fields().lookupField(field_name)
        unique_values = list(self.layer.uniqueValues(index))
        pretty_unique_values = ', '.join([str(v) for v in unique_values[:10]])

        footer_text = tr('Field type: {0}\n').format(field.typeName())
        footer_text += tr('Unique values: {0}').format(pretty_unique_values)
        self.footer_label.setText(footer_text)

    def connect_drop_remove_parameter(self):
        parameter_widgets = self.parameter_container.get_parameter_widgets()
        for parameter_widget in parameter_widgets:
            field_list = parameter_widget.widget().list_widget
            field_list.itemChanged.connect(
                partial(self.drop_remove, field_list=field_list))

    @staticmethod
    def drop_remove(*args, **kwargs):
        """Action when we need to remove dropped item.

        :param *args: Position arguments.
        :type *args: list

        :param kwargs: Keywords arguments.
        :type kwargs: dict
        """
        dropped_item = args[0]
        field_list = kwargs['field_list']
        num_duplicate = 0
        for i in range(field_list.count()):
            if dropped_item.text() == field_list.item(i).text():
                num_duplicate += 1
        if num_duplicate > 1:
            # Notes(IS): For some reason, removeItemWidget is not working.
            field_list.takeItem(field_list.row(dropped_item))
示例#27
0
class GraphDialog(QDialog):

    edit_patterns = 0
    edit_curves = 1

    titles = {edit_patterns: 'Pattern editor', edit_curves: 'Curve editor'}

    labels = {edit_patterns: 'Patterns', edit_curves: 'Curves'}

    def __init__(self, dockwidget, parent, params, edit_type):

        QDialog.__init__(self, parent)
        main_lay = QVBoxLayout(self)
        self.dockwidget = dockwidget
        self.params = params
        self.edit_type = edit_type

        self.x_label = ''
        self.y_label = ''

        self.setMinimumWidth(600)
        self.setMinimumHeight(400)

        self.setWindowTitle(self.titles[edit_type])  # TODO: softcode
        self.setWindowModality(QtCore.Qt.ApplicationModal)

        self.current = None

        self.current_saved = False

        # File
        self.lbl_file = QLabel('File:')
        self.fra_file = QFrame()
        self.fra_file.setContentsMargins(0, 0, 0, 0)
        fra_file_lay = QHBoxLayout(self.fra_file)

        if edit_type == self.edit_patterns:
            self.txt_file = QLineEdit(self.params.patterns_file)
        elif edit_type == self.edit_curves:
            self.txt_file = QLineEdit(self.params.curves_file)

        self.txt_file.setReadOnly(True)
        self.txt_file.setAlignment(QtCore.Qt.AlignLeft
                                   | QtCore.Qt.AlignVCenter)
        self.txt_file.setSizePolicy(QSizePolicy.MinimumExpanding,
                                    QSizePolicy.Minimum)
        fra_file_lay.addWidget(self.txt_file)
        self.btn_file = QPushButton('Change')  # TODO: softcode
        self.btn_file.clicked.connect(self.import_file)
        fra_file_lay.addWidget(self.btn_file)
        fra_file_lay.setContentsMargins(0, 0, 0, 0)

        self.lbl_list = QLabel(self.labels[edit_type])
        self.lst_list = QListWidget()
        self.lst_list.currentItemChanged.connect(self.list_item_changed)

        # Form
        self.fra_form = QFrame()
        fra_form1_lay = QFormLayout(self.fra_form)
        fra_form1_lay.setContentsMargins(0, 0, 0, 0)
        fra_form1_lay.addRow(self.lbl_list, self.lst_list)

        # Buttons
        self.fra_buttons = QFrame()
        fra_buttons_lay = QHBoxLayout(self.fra_buttons)
        fra_buttons_lay.setContentsMargins(0, 0, 0, 0)

        if self.edit_type == self.edit_patterns:
            ele_name = 'pattern'
        elif self.edit_type == self.edit_curves:
            ele_name = 'curve'

        self.btn_new = QPushButton('New ' + ele_name)  # TODO: softcode
        self.btn_new.clicked.connect(self.new_element)
        fra_buttons_lay.addWidget(self.btn_new)

        self.btn_import = QPushButton('Import ' + ele_name +
                                      's')  # TODO: softcode
        self.btn_import.clicked.connect(self.import_file)
        fra_buttons_lay.addWidget(self.btn_import)

        self.btn_save = QPushButton('Save current ' +
                                    ele_name)  # TODO: softcode
        self.btn_save.clicked.connect(self.save)
        fra_buttons_lay.addWidget(self.btn_save)

        self.btn_del = QPushButton('Delete current ' +
                                   ele_name)  # TODO: softcode
        self.btn_del.clicked.connect(self.del_item)
        fra_buttons_lay.addWidget(self.btn_del)

        # ID
        self.lbl_id = QLabel('ID:')
        self.txt_id = QLineEdit()
        self.txt_id.setSizePolicy(QSizePolicy.Maximum,
                                  QSizePolicy.MinimumExpanding)
        self.lbl_desc = QLabel('Desc.:')
        self.txt_desc = QLineEdit()

        self.fra_id = QFrame()
        fra_id_lay = QHBoxLayout(self.fra_id)
        fra_id_lay.addWidget(self.lbl_id)
        fra_id_lay.addWidget(self.txt_id)
        fra_id_lay.addWidget(self.lbl_desc)
        fra_id_lay.addWidget(self.txt_desc)

        # Table form
        self.table = QTableWidget(self)
        self.rows_nr = 24
        self.cols_nr = 2
        self.table.setRowCount(self.rows_nr)
        self.table.setColumnCount(self.cols_nr)
        self.table.verticalHeader().setVisible(False)

        # Initialize empty table
        self.clear_table()

        self.table.itemChanged.connect(self.data_changed)

        self.fra_table = QFrame()
        fra_table_lay = QVBoxLayout(self.fra_table)
        fra_table_lay.setContentsMargins(0, 0, 0, 0)

        if edit_type == self.edit_curves:
            self.fra_pump_type = QFrame()
            fra_pump_type_lay = QFormLayout(self.fra_pump_type)

            self.lbl_pump_type = QLabel('Curve type:')  # TODO: softcode
            self.cbo_pump_type = QComboBox()

            for key, name in Curve.type_names.items():
                self.cbo_pump_type.addItem(name, key)

            fra_pump_type_lay.addRow(self.lbl_pump_type, self.cbo_pump_type)

            fra_table_lay.addWidget(self.fra_pump_type)

            self.cbo_pump_type.activated.connect(self.cbo_pump_type_activated)

        fra_table_lay.addWidget(self.table)
        self.btn_add_row = QPushButton('Add row')
        self.btn_add_row.clicked.connect(self.add_row)
        fra_table_lay.addWidget(self.btn_add_row)

        # Graph canvas
        self.fra_graph = QFrame()
        self.static_canvas = StaticMplCanvas(self.fra_graph,
                                             width=5,
                                             height=4,
                                             dpi=100)
        fra_graph_lay = QVBoxLayout(self.fra_graph)
        fra_graph_lay.addWidget(self.static_canvas)

        # Top frame
        self.fra_top = QFrame()
        fra_top_lay = QVBoxLayout(self.fra_top)
        fra_top_lay.addWidget(self.fra_form)
        fra_top_lay.addWidget(self.fra_id)
        fra_top_lay.addWidget(self.fra_buttons)

        # Bottom frame
        self.fra_bottom = QFrame()
        fra_bottom_lay = QHBoxLayout(self.fra_bottom)
        fra_bottom_lay.addWidget(self.fra_table)
        fra_bottom_lay.addWidget(self.fra_graph)

        # Main
        main_lay.addWidget(self.fra_top)
        main_lay.addWidget(self.fra_bottom)

        # Get existing patterns/curves
        self.need_to_update_graph = False
        if self.edit_type == self.edit_patterns:
            for pattern_id, pattern in self.params.patterns.items():
                self.lst_list.addItem(pattern.id)

        elif self.edit_type == self.edit_curves:
            for curve_id, curve in self.params.curves.items():
                self.lst_list.addItem(curve.id)

        if self.lst_list.count() > 0:
            self.lst_list.setCurrentRow(0)
            self.txt_id.setEnabled(True)
            self.txt_desc.setEnabled(True)
            self.btn_save.setEnabled(True)
            self.btn_del.setEnabled(True)
            self.table.setEnabled(True)
            self.table.setEditTriggers(QAbstractItemView.AllEditTriggers)
        else:
            self.txt_id.setEnabled(False)
            self.txt_desc.setEnabled(False)
            self.btn_save.setEnabled(False)
            self.btn_del.setEnabled(False)
            self.table.setEnabled(False)
            self.table.setEditTriggers(QAbstractItemView.NoEditTriggers)

        self.new_dialog = None
        self.need_to_update_graph = True

    def cbo_pump_type_activated(self):
        self.update_table_headers()
        self.update_graph()

    def add_row(self):
        row_pos = self.table.rowCount()
        self.table.insertRow(row_pos)
        col = 0
        item = QTableWidgetItem(str(row_pos))

        if self.edit_type == self.edit_patterns:
            self.table.setItem(row_pos, col, item)
            item.setFlags(QtCore.Qt.ItemIsSelectable)

    def setVisible(self, bool):
        QDialog.setVisible(self, bool)

        self.update_table_headers()
        self.update_graph()

    def list_item_changed(self):

        p_index = self.lst_list.currentRow()

        flags = Qt.ItemFlags()
        flags != Qt.ItemIsEnabled

        # Clear table
        self.clear_table()

        self.need_to_update_graph = False
        if p_index >= 0:

            self.table.setRowCount(0)

            if self.edit_type == self.edit_patterns:
                self.current = self.params.patterns[
                    self.lst_list.currentItem().text()]
                for v in range(len(self.current.values)):

                    row_pos = self.table.rowCount()
                    self.table.insertRow(row_pos)

                    item = QTableWidgetItem(str(v))
                    item.setFlags(flags)
                    self.table.setItem(v, 0, item)
                    self.table.setItem(
                        v, 1, QTableWidgetItem(str(self.current.values[v])))

            elif self.edit_type == self.edit_curves:
                self.current = self.params.curves[
                    self.lst_list.currentItem().text()]
                for v in range(len(self.current.xs)):

                    row_pos = self.table.rowCount()
                    self.table.insertRow(row_pos)

                    self.table.setItem(
                        v, 0, QTableWidgetItem(str(self.current.xs[v])))
                    self.table.setItem(
                        v, 1, QTableWidgetItem(str(self.current.ys[v])))

                curve_type = self.current.type
                self.cbo_pump_type.setCurrentIndex(curve_type)

            # Update GUI
            self.txt_id.setText(self.current.id)
            self.txt_desc.setText(self.current.desc)

            self.update_table_headers()

            # Update graph
            self.need_to_update_graph = True
            self.update_graph()

        else:

            # No curves
            self.txt_id.setText('')
            self.txt_desc.setText('')

            # Update table and chart
            self.need_to_update_graph = False
            for v in range(self.table.columnCount()):
                self.table.setItem(v, 1, QTableWidgetItem(''))

            self.need_to_update_graph = True
            self.update_graph()

    def import_file(self):

        config_file = ConfigFile(Parameters.config_file_path)

        directory = None
        if self.edit_type == GraphDialog.edit_curves:
            directory = self.params.last_curves_dir
        elif self.edit_type == GraphDialog.edit_patterns:
            directory = self.params.last_patterns_dir

        if directory is None:
            directory = self.params.last_project_dir

        file_path, __ = QFileDialog.getOpenFileName(self, 'Select file',
                                                    directory,
                                                    'Files (*.txt *.inp)')

        if file_path is None or file_path == '':
            return
        else:
            if self.edit_type == GraphDialog.edit_patterns:
                # Save patterns file path in configuration file
                config_file.set_patterns_file_path(file_path)
                Parameters.patterns_file = file_path
            elif self.edit_type == GraphDialog.edit_curves:
                # Save curve file path in configuration file
                config_file.set_curves_file_path(file_path)
                Parameters.curves_file = file_path

        self.read(file_path)

    def read(self, file_path):

        self.lst_list.clear()

        if self.edit_type == self.edit_patterns:
            InpFile.read_patterns(self.params, file_path)
            for pattern_id, pattern in self.params.patterns.items():
                # desc = ' (' + pattern.desc + ')' if pattern.desc is not None else ''
                self.lst_list.addItem(pattern.id)
                self.params.patterns[pattern.id] = pattern

        elif self.edit_type == self.edit_curves:
            InpFile.read_curves(self.params, file_path)
            for curve_id, curve in self.params.curves.items():
                # desc = ' (' + curve.desc + ')' if curve.desc is not None else ''
                self.lst_list.addItem(curve.id)
                self.params.curves[curve.id] = curve

        if self.lst_list.count() > 0:
            self.lst_list.setCurrentRow(0)

    def new_element(self):

        old_ids = []
        if self.edit_type == self.edit_patterns:
            for pattern in self.params.patterns.values():
                old_ids.append(pattern.id)
        elif self.edit_type == self.edit_curves:
            for curve in self.params.curves.values():
                old_ids.append(curve.id)
        self.new_dialog = NewIdDialog(self, old_ids)
        self.new_dialog.exec_()

        new_id = self.new_dialog.get_newid()
        description = self.new_dialog.get_description()
        if new_id is None or description is None:
            return

        if self.edit_type == self.edit_patterns:
            new_pattern = Pattern(new_id, description)
            self.params.patterns[new_pattern.id] = new_pattern
            self.lst_list.addItem(new_pattern.id)
        elif self.edit_type == self.edit_curves:
            curve_type = self.cbo_pump_type.itemData(
                self.cbo_pump_type.currentIndex())
            new_curve = Curve(new_id, curve_type, desc=description)
            self.params.curves[new_curve.id] = new_curve
            self.lst_list.addItem(new_curve.id)

        self.lst_list.setCurrentRow(self.lst_list.count() - 1)

        self.txt_id.setText(new_id)
        self.txt_desc.setText(description)

        # Clear table
        self.clear_table()
        self.static_canvas.axes.clear()

        self.txt_id.setEnabled(True)
        self.txt_desc.setEnabled(True)
        self.btn_save.setEnabled(True)
        self.btn_del.setEnabled(True)
        self.table.setEnabled(True)
        self.table.setEditTriggers(QAbstractItemView.AllEditTriggers)

    def save(self):

        self.need_to_update_graph = False

        # Check for ID
        if not self.txt_id.text():
            QMessageBox.warning(
                self,
                Parameters.plug_in_name,
                u'Please specify the ID.',  # TODO: softcode
                QMessageBox.Ok)
            return

        if self.edit_type == GraphDialog.edit_patterns:
            values = []
            for row in range(self.table.rowCount()):
                item = self.table.item(row, 1)
                if item is not None and item.text() != '':
                    values.append(self.from_item_to_val(item))
                else:
                    values.append('0')

            pattern = Pattern(self.txt_id.text(), self.txt_desc.text(), values)

            old_patterns = self.params.patterns
            old_patterns[pattern.id] = pattern
            self.params.patterns = old_patterns

            self.lst_list.currentItem().setText(pattern.id)

        elif self.edit_type == GraphDialog.edit_curves:

            # Check for ID unique
            xs = []
            ys = []
            for row in range(self.table.rowCount()):
                item_x = self.table.item(row, 0)
                item_y = self.table.item(row, 1)

                if item_x.text().strip() != '' and item_y.text().strip() != '':
                    xs.append(self.from_item_to_val(item_x))
                    ys.append(self.from_item_to_val(item_y))

            curve_type = self.cbo_pump_type.itemData(
                self.cbo_pump_type.currentIndex())
            curve = Curve(self.txt_id.text(), curve_type, self.txt_desc.text())
            for v in range(len(xs)):
                curve.append_xy(xs[v], ys[v])

            old_curves = self.params.curves
            old_curves[curve.id] = curve
            self.params.curves = old_curves

            self.lst_list.currentItem().setText(curve.id)

            # Update GUI
            self.dockwidget.update_curves_combo()

        # self.read()
        self.need_to_update_graph = True

    def clear_table(self):

        self.need_to_update_graph = False
        for r in range(self.table.rowCount()):
            self.table.setItem(r, 0, QTableWidgetItem(None))
            self.table.setItem(r, 1, QTableWidgetItem(None))

        for row in range(self.rows_nr):
            for col in range(self.cols_nr):
                if self.edit_type == self.edit_patterns:
                    if col == 0:
                        item = QTableWidgetItem(str(row))
                        self.table.setItem(row, col, item)
                        item.setFlags(QtCore.Qt.ItemIsSelectable)
                    # elif col == 1 and row == 0:
                    #     item = QTableWidgetItem(str(1))
                    #     self.table.setItem(row, col, item)
                    #     item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)

                # elif self.edit_type == self.edit_curves:
                # if row == 0:
                # item = QTableWidgetItem(str(0))
                # self.table.setItem(row, 0, item)
                # item = QTableWidgetItem(str(1))
                # self.table.setItem(row, 1, item)
                # item.setFlags(QtCore.Qt.ItemIsSelectable | QtCore.Qt.ItemIsEnabled)
        self.need_to_update_graph = True

    def del_item(self):
        selected_row = self.lst_list.currentRow()
        name = self.lst_list.currentItem().text()
        if selected_row < 0:
            return

        self.lst_list.takeItem(selected_row)
        if self.lst_list.count() == 0:
            self.txt_id.setEnabled(False)
            self.txt_desc.setEnabled(False)
            self.btn_save.setEnabled(False)
            self.btn_del.setEnabled(False)
            self.table.setEnabled(False)
            self.table.setEditTriggers(QAbstractItemView.NoEditTriggers)

        if self.edit_type == GraphDialog.edit_curves:
            del self.params.curves[name]
            # Update GUI
            self.dockwidget.update_curves_combo()
        elif self.edit_type == GraphDialog.edit_patterns:
            del self.params.patterns[name]
            # Update GUI
            self.dockwidget.update_patterns_combo()

    def data_changed(self):

        if self.need_to_update_graph:
            self.update_graph()

    def update_table_headers(self):
        if self.edit_type == self.edit_patterns:
            self.x_label = 'Time period'
            self.y_label = 'Multiplier'
        elif self.edit_type == self.edit_curves:
            cbo_data = self.cbo_pump_type.itemData(
                self.cbo_pump_type.currentIndex())
            if cbo_data == Curve.type_efficiency:
                self.x_label = 'Flow ' + '[' + self.params.options.flow_units + ']'
                self.y_label = 'Efficiency ' + '[' + self.params.options.units_deltaz[
                    self.params.options.units] + ']'
            if cbo_data == Curve.type_headloss:
                self.x_label = 'Flow ' + '[' + self.params.options.flow_units + ']'
                self.y_label = 'Headloss ' + '[' + self.params.options.units_deltaz[
                    self.params.options.units] + ']'
            if cbo_data == Curve.type_pump:
                self.x_label = 'Flow ' + '[' + self.params.options.flow_units + ']'
                self.y_label = 'Head ' + '[' + self.params.options.units_deltaz[
                    self.params.options.units] + ']'
            if cbo_data == Curve.type_volume:
                self.x_label = 'Height ' + '[' + self.params.options.flow_units + ']'
                self.y_label = 'Volume ' + '[' + self.params.options.units_deltaz[
                    self.params.options.units] + ']'

        self.table.setHorizontalHeaderLabels([self.x_label,
                                              self.y_label])  # TODO: softcode

    def update_graph(self):

        if not self.need_to_update_graph:
            return

        xs = []
        ys = []

        for row in range(self.table.rowCount()):
            item = self.table.item(row, 0)
            x = self.from_item_to_val(item)
            item = self.table.item(row, 1)
            y = self.from_item_to_val(item)

            if x is not None:
                xs.append(float(x))
            if y is not None:
                ys.append(float(y))

        if len(xs) == 0 or len(ys) == 0:
            self.static_canvas.clear()
            return

        xys_t = list(zip(xs, ys))
        xys_t.sort()

        xys = list(zip(*xys_t))
        xs = xys[0]
        ys = xys[1]

        if self.edit_type == self.edit_patterns:
            y_axis_label = 'Mult. avg.: ' + '{0:.2f}'.format(
                (numpy.average(ys)))
            self.static_canvas.draw_bars_graph(
                ys,
                time_period=self.params.times.pattern_timestep,
                y_axes_label=y_axis_label)

        elif self.edit_type == self.edit_curves:

            # Account for different types of curves
            cbo_data = self.cbo_pump_type.itemData(
                self.cbo_pump_type.currentIndex())

            series_length = min(len(xs), len(ys))

            # Need to account for different types of curves
            if cbo_data == Curve.type_efficiency or cbo_data == Curve.type_headloss or cbo_data == Curve.type_volume:
                self.static_canvas.draw_line_graph(xs[:series_length],
                                                   ys[:series_length],
                                                   self.x_label, self.y_label)

            elif cbo_data == Curve.type_pump:
                if series_length == 1 or series_length == 3:
                    if series_length == 1:
                        # 3 curve points
                        curve_xs = [0, xs[0], xs[0] * 2]
                        curve_ys = [ys[0] * 1.33, ys[0], 0]
                        # y = a * x^2 + b * x + c

                    elif series_length == 3:
                        # 3 curve points
                        curve_xs = [xs[0], xs[1], xs[2]]
                        curve_ys = [ys[0], ys[1], ys[2]]

                    (a, b, c) = numpy.polyfit(curve_xs, curve_ys, 2)

                    # Create a few interpolated values
                    interp_xs = []
                    interp_ys = []
                    n_vals = 30
                    for v in range(n_vals + 1):
                        x = (curve_xs[2] - curve_xs[0]) / n_vals * v
                        interp_xs.append(x)
                        y = a * x**2 + b * x + c
                        interp_ys.append(y)

                    self.static_canvas.draw_line_graph(interp_xs, interp_ys,
                                                       self.x_label,
                                                       self.y_label)

                else:
                    self.static_canvas.draw_line_graph(xs[:series_length],
                                                       ys[:series_length],
                                                       self.x_label,
                                                       self.y_label)

    def from_item_to_val(self, item):

        if item is None:
            value = None
        else:
            value = item.text()
        try:
            value = float(value)
            value = max(value, 0)

        except:
            value = None

        return value
class StepKwMultiClassifications(WizardStep, FORM_CLASS):

    """InaSAFE Wizard Step Multi Classifications."""

    def __init__(self, parent=None):
        """Constructor for the tab.

        :param parent: widget to use as parent (Wizard Dialog).
        :type parent: QWidget
        """
        WizardStep.__init__(self, parent)
        self.exposures = []
        self.exposure_labels = []
        self.exposure_combo_boxes = []
        self.exposure_edit_buttons = []
        self.mode = CHOOSE_MODE

        self.layer_purpose = None
        self.layer_mode = None

        # Store the current representative state of the UI.
        # self.classifications = {}
        self.value_maps = {}
        self.thresholds = {}

        # Temporary attributes
        self.threshold_classes = OrderedDict()
        self.active_exposure = None

        self.list_unique_values = None
        self.tree_mapping_widget = None

        # GUI, good for testing
        self.save_button = None
        self.restore_default_button = None

        # Has default threshold
        # Trick for EQ raster for population #3853
        self.use_default_thresholds = False
        # Index of the special case exposure classification
        self.special_case_index = None

    def is_ready_to_next_step(self):
        """Check if the step is complete.

        :returns: True if new step may be enabled.
        :rtype: bool
        """
        # Still editing
        if self.mode == EDIT_MODE:
            return False
        for combo_box in self.exposure_combo_boxes:
            # Enable if there is one that has classification
            if combo_box.currentIndex() > 0:
                return True
        # Trick for EQ raster for population #3853
        if self.use_default_thresholds:
            return True
        return False

    def get_next_step(self):
        """Find the proper step when user clicks the Next button.

        :returns: The step to be switched to.
        :rtype: WizardStep instance or None
        """
        if self.layer_purpose != layer_purpose_aggregation:
            subcategory = self.parent.step_kw_subcategory.\
                selected_subcategory()
        else:
            subcategory = {'key': None}

        if is_raster_layer(self.parent.layer):
            return self.parent.step_kw_source

        # Check if it can go to inasafe field step
        inasafe_fields = get_non_compulsory_fields(
            self.layer_purpose['key'], subcategory['key'])

        if not skip_inasafe_field(self.parent.layer, inasafe_fields):
            return self.parent.step_kw_inasafe_fields

        # Check if it can go to inasafe default field step
        default_inasafe_fields = get_fields(
            self.layer_purpose['key'],
            subcategory['key'],
            replace_null=True,
            in_group=False
        )
        if default_inasafe_fields:
            return self.parent.step_kw_default_inasafe_fields

        # Any other case
        return self.parent.step_kw_source

    def set_wizard_step_description(self):
        """Set the text for description."""
        subcategory = self.parent.step_kw_subcategory.selected_subcategory()
        field = self.parent.step_kw_field.selected_fields()
        is_raster = is_raster_layer(self.parent.layer)

        if is_raster:
            if self.layer_mode == layer_mode_continuous:
                text_label = multiple_continuous_hazard_classifications_raster
            else:
                text_label = multiple_classified_hazard_classifications_raster
            # noinspection PyAugmentAssignment
            text_label = text_label % (
                subcategory['name'], self.layer_purpose['name'])
        else:
            if self.layer_mode == layer_mode_continuous:
                text_label = multiple_continuous_hazard_classifications_vector
            else:
                text_label = multiple_classified_hazard_classifications_vector
            # noinspection PyAugmentAssignment
            text_label = text_label % (
                subcategory['name'], self.layer_purpose['name'], field)

        self.multi_classifications_label.setText(text_label)

    def setup_left_panel(self):
        """Setup the UI for left panel.

        Generate all exposure, combobox, and edit button.
        """
        hazard = self.parent.step_kw_subcategory.selected_subcategory()
        left_panel_heading = QLabel(tr('Classifications'))
        left_panel_heading.setFont(big_font)
        self.left_layout.addWidget(left_panel_heading)

        inner_left_layout = QGridLayout()

        row = 0
        for exposure in exposure_all:
            special_case = False
            if not setting('developer_mode'):
                # Filter out unsupported exposure for the hazard
                if exposure in hazard['disabled_exposures']:
                    # Remove from the storage if the exposure is disabled
                    if self.layer_mode == layer_mode_continuous:
                        if exposure['key'] in self.thresholds:
                            self.thresholds.pop(exposure['key'])
                    else:
                        if exposure['key'] in self.value_maps:
                            self.value_maps.pop(exposure['key'])
                    continue
            # Trick for EQ raster for population #3853
            if exposure == exposure_population and hazard == hazard_earthquake:
                if is_raster_layer(self.parent.layer):
                    if self.layer_mode == layer_mode_continuous:
                        self.use_default_thresholds = True
                        special_case = True
                        # Set classification for EQ Raster for Population
                        self.thresholds[exposure_population['key']] = {
                            earthquake_mmi_scale['key']: {
                                'classes': default_classification_thresholds(
                                    earthquake_mmi_scale),
                                'active': True
                            }
                        }

            # Add label
            # Hazard on Exposure Classifications
            label = tr(
                '{hazard_name} on {exposure_name} Classifications').format(
                hazard_name=hazard['name'],
                exposure_name=exposure['name']
            )
            exposure_label = QLabel(label)

            # Add combo box
            exposure_combo_box = QComboBox()
            hazard_classifications = hazard.get('classifications')
            exposure_combo_box.addItem(tr('No classifications'))
            exposure_combo_box.setItemData(
                0, None, Qt.UserRole)

            current_index = 0
            i = 0
            # Iterate through all available hazard classifications
            for hazard_classification in hazard_classifications:
                # Skip if the classification is not for the exposure
                if 'exposures' in hazard_classification:
                    if exposure not in hazard_classification['exposures']:
                        continue
                exposure_combo_box.addItem(hazard_classification['name'])
                exposure_combo_box.setItemData(
                    i + 1, hazard_classification, Qt.UserRole)
                if self.layer_mode == layer_mode_continuous:
                    current_hazard_classifications = self.thresholds.get(
                        exposure['key'])
                else:
                    current_hazard_classifications = self.value_maps.get(
                        exposure['key'])
                if current_hazard_classifications:
                    current_hazard_classification = \
                        current_hazard_classifications.get(
                            hazard_classification['key'])
                    if current_hazard_classification:
                        is_active = current_hazard_classification.get('active')
                        if is_active:
                            current_index = i + 1
                i += 1
            # Set current classification
            exposure_combo_box.setCurrentIndex(current_index)

            # Add edit button
            exposure_edit_button = QPushButton(tr('Edit'))

            # For special case. Raster EQ on Population.
            if special_case:
                mmi_index = exposure_combo_box.findText(
                    earthquake_mmi_scale['name'])
                exposure_combo_box.setCurrentIndex(mmi_index)
                exposure_combo_box.setEnabled(False)
                exposure_edit_button.setEnabled(False)
                tool_tip_message = tr(
                    'InaSAFE use default classification for Raster Earthquake '
                    'hazard on population.')
                exposure_label.setToolTip(tool_tip_message)
                exposure_combo_box.setToolTip(tool_tip_message)
                exposure_edit_button.setToolTip(tool_tip_message)

            else:
                if current_index == 0:
                    # Disable if there is no classification chosen.
                    exposure_edit_button.setEnabled(False)
                exposure_edit_button.clicked.connect(
                    partial(
                        self.edit_button_clicked,
                        edit_button=exposure_edit_button,
                        exposure_combo_box=exposure_combo_box,
                        exposure=exposure))
                exposure_combo_box.currentIndexChanged.connect(
                    partial(
                        self.classifications_combo_box_changed,
                        exposure=exposure,
                        exposure_combo_box=exposure_combo_box,
                        edit_button=exposure_edit_button))

            # Arrange in layout
            inner_left_layout.addWidget(exposure_label, row, 0)
            inner_left_layout.addWidget(exposure_combo_box, row, 1)
            inner_left_layout.addWidget(exposure_edit_button, row, 2)

            # Adding to step's attribute
            self.exposures.append(exposure)
            self.exposure_combo_boxes.append(exposure_combo_box)
            self.exposure_edit_buttons.append(exposure_edit_button)
            self.exposure_labels.append(label)
            if special_case:
                self.special_case_index = len(self.exposures) - 1

            row += 1

        self.left_layout.addLayout(inner_left_layout)
        # To push the inner_left_layout up
        self.left_layout.addStretch(1)

    # noinspection PyUnusedLocal
    def edit_button_clicked(self, edit_button, exposure_combo_box, exposure):
        """Method to handle when an edit button is clicked.

        :param edit_button: The edit button.
        :type edit_button: QPushButton

        :param exposure_combo_box: The combo box of the exposure, contains
            list of classifications.
        :type exposure_combo_box: QComboBox

        :param exposure: Exposure definition.
        :type exposure: dict
        """
        # Note(IS): Do not change the text of edit button for now until we
        # have better behaviour.
        classification = self.get_classification(exposure_combo_box)

        if self.mode == CHOOSE_MODE:
            # Change mode
            self.mode = EDIT_MODE
            # Set active exposure
            self.active_exposure = exposure
            # Disable all edit button
            for exposure_edit_button in self.exposure_edit_buttons:
                exposure_edit_button.setEnabled(False)
            # Except one that was clicked
            # edit_button.setEnabled(True)
            # Disable all combo box
            for exposure_combo_box in self.exposure_combo_boxes:
                exposure_combo_box.setEnabled(False)
            # Change the edit button to cancel
            # edit_button.setText(tr('Cancel'))

            # Clear right panel
            clear_layout(self.right_layout)
            # Show edit threshold or value mapping
            if self.layer_mode == layer_mode_continuous:
                self.setup_thresholds_panel(classification)
            else:
                self.setup_value_mapping_panels(classification)
            self.add_buttons(classification)

        elif self.mode == EDIT_MODE:
            # Behave the same as cancel button clicked.
            self.cancel_button_clicked()

        self.parent.pbnNext.setEnabled(self.is_ready_to_next_step())

    def show_current_state(self):
        """Setup the UI for QTextEdit to show the current state."""
        right_panel_heading = QLabel(tr('Status'))
        right_panel_heading.setFont(big_font)
        right_panel_heading.setSizePolicy(
            QSizePolicy.Maximum, QSizePolicy.Maximum)
        self.right_layout.addWidget(right_panel_heading)

        message = m.Message()
        if self.layer_mode == layer_mode_continuous:
            title = tr('Thresholds')
        else:
            title = tr('Value maps')

        message.add(m.Heading(title, **INFO_STYLE))

        for i in range(len(self.exposures)):
            message.add(m.Text(self.exposure_labels[i]))

            classification = self.get_classification(
                self.exposure_combo_boxes[i])
            if self.layer_mode == layer_mode_continuous:
                thresholds = self.thresholds.get(self.exposures[i]['key'])
                if not thresholds or not classification:
                    message.add(m.Paragraph(tr('No classifications set.')))
                    continue
                table = m.Table(
                    style_class='table table-condensed table-striped')
                header = m.Row()
                header.add(m.Cell(tr('Class name')))
                header.add(m.Cell(tr('Minimum')))
                header.add(m.Cell(tr('Maximum')))
                table.add(header)
                classes = classification.get('classes')
                # Sort by value, put the lowest first
                classes = sorted(classes, key=lambda k: k['value'])
                for the_class in classes:
                    threshold = thresholds[classification['key']]['classes'][
                        the_class['key']]
                    row = m.Row()
                    row.add(m.Cell(the_class['name']))
                    row.add(m.Cell(threshold[0]))
                    row.add(m.Cell(threshold[1]))
                    table.add(row)
            else:
                value_maps = self.value_maps.get(self.exposures[i]['key'])
                if not value_maps or not classification:
                    message.add(m.Paragraph(tr('No classifications set.')))
                    continue
                table = m.Table(
                    style_class='table table-condensed table-striped')
                header = m.Row()
                header.add(m.Cell(tr('Class name')))
                header.add(m.Cell(tr('Value')))
                table.add(header)
                classes = classification.get('classes')
                # Sort by value, put the lowest first
                classes = sorted(classes, key=lambda k: k['value'])
                for the_class in classes:
                    value_map = value_maps[classification['key']][
                        'classes'].get(the_class['key'], [])
                    row = m.Row()
                    row.add(m.Cell(the_class['name']))
                    row.add(m.Cell(', '.join([str(v) for v in value_map])))
                    table.add(row)
            message.add(table)

        # status_text_edit = QTextBrowser(None)
        status_text_edit = QWebView(None)
        status_text_edit.setSizePolicy(
            QSizePolicy.Ignored,
            QSizePolicy.Ignored)

        status_text_edit.page().mainFrame().setScrollBarPolicy(
            Qt.Horizontal,
            Qt.ScrollBarAlwaysOff)
        html_string = html_header() + message.to_html() + html_footer()
        status_text_edit.setHtml(html_string)
        self.right_layout.addWidget(status_text_edit)

    def set_widgets(self):
        """Set widgets on the Multi classification step."""
        self.clear()
        self.layer_mode = self.parent.step_kw_layermode.selected_layermode()
        self.layer_purpose = self.parent.step_kw_purpose.selected_purpose()
        self.set_current_state()

        # Set the step description
        self.set_wizard_step_description()

        # Set the left panel
        self.setup_left_panel()

        # Set the right panel, for the beginning show the viewer
        self.show_current_state()

    def clear(self):
        """Clear current state."""
        self.exposures = []
        self.exposure_labels = []
        self.exposure_combo_boxes = []
        self.exposure_edit_buttons = []
        self.mode = CHOOSE_MODE

        self.layer_purpose = None
        self.layer_mode = None
        self.special_case_index = None

        self.value_maps = {}
        self.thresholds = {}

        # Temporary attributes
        self.threshold_classes = OrderedDict()
        self.active_exposure = None

        self.list_unique_values = None
        self.tree_mapping_widget = None

        clear_layout(self.left_layout)
        clear_layout(self.right_layout)

    def get_current_state(self):
        """Obtain current classification and value map / threshold."""
        def clean_state(dictionary):
            """Clean dictionary from bad value.

            :param dictionary: Dictionary of value maps or thresholds.
            :type dictionary: dict

            :returns: Clean state.
            :rtype: dict
            """
            clean_dictionary = {
                k: v for k, v in list(dictionary.items())
                if isinstance(v, dict)}

            return clean_dictionary

        if self.layer_mode == layer_mode_continuous:
            output = {'thresholds': clean_state(self.thresholds)}
            key = 'thresholds'
        else:
            output = {'value_maps': clean_state(self.value_maps)}
            key = 'value_maps'

        # Clean non existing hazard class key
        empty_exposure_classifications = []
        for the_exposure, the_hazard_classifications in list(
                output[key].items()):
            for the_hazard_classification in list(the_hazard_classifications.
                                                  keys()):
                invalid_classifications = []
                if not definition(the_hazard_classification):
                    invalid_classifications.append(
                        the_hazard_classification)
                for invalid_classification in invalid_classifications:
                    the_hazard_classifications.pop(invalid_classification)
            if not the_hazard_classifications:
                empty_exposure_classifications.append(the_exposure)

        for empty_exposure_classification in empty_exposure_classifications:
            output[key].pop(empty_exposure_classification)

        return output

    @staticmethod
    def get_classification(combo_box):
        """Helper to obtain the classification from a combo box.

        :param combo_box: A classification combo box.
        :type combo_box: QComboBox.

        :returns: Classification definitions.
        :rtype: dict
        """
        return combo_box.itemData(combo_box.currentIndex(), Qt.UserRole)

    def setup_thresholds_panel(self, classification):
        """Setup threshold panel in the right panel.

        :param classification: Classification definition.
        :type classification: dict
        """
        # Set text in the label
        layer_purpose = self.parent.step_kw_purpose.selected_purpose()
        layer_subcategory = self.parent.step_kw_subcategory.\
            selected_subcategory()

        if is_raster_layer(self.parent.layer):
            active_band = self.parent.step_kw_band_selector.selected_band()
            layer_extent = self.parent.layer.extent()
            statistics = self.parent.layer.dataProvider().bandStatistics(
                active_band, QgsRasterBandStats.All, layer_extent, 0)
            description_text = continuous_raster_question % (
                layer_purpose['name'],
                layer_subcategory['name'],
                classification['name'],
                statistics.minimumValue,
                statistics.maximumValue)
        else:
            field_name = self.parent.step_kw_field.selected_fields()
            field_index = self.parent.layer.fields().lookupField(field_name)
            min_value_layer = self.parent.layer.minimumValue(field_index)
            max_value_layer = self.parent.layer.maximumValue(field_index)
            description_text = continuous_vector_question % (
                layer_purpose['name'],
                layer_subcategory['name'],
                field_name,
                classification['name'],
                min_value_layer,
                max_value_layer)

        # Set description
        description_label = QLabel(description_text)
        description_label.setWordWrap(True)
        self.right_layout.addWidget(description_label)

        if self.thresholds:
            thresholds = self.thresholds
        else:
            thresholds = self.parent.get_existing_keyword('thresholds')
        selected_unit = self.parent.step_kw_unit.selected_unit()['key']

        self.threshold_classes = OrderedDict()
        classes = classification.get('classes')
        # Sort by value, put the lowest first
        classes = sorted(classes, key=lambda the_key: the_key['value'])

        grid_layout_thresholds = QGridLayout()

        for i, the_class in enumerate(classes):
            class_layout = QHBoxLayout()

            # Class label
            class_label = QLabel(the_class['name'])

            # Min label
            min_label = QLabel(tr('Min >'))

            # Min value as double spin
            min_value_input = QDoubleSpinBox()
            # TODO(IS) We can set the min and max depends on the unit, later
            min_value_input.setMinimum(0)
            min_value_input.setMaximum(999999)

            if thresholds.get(self.active_exposure['key']):
                exposure_thresholds = thresholds.get(
                    self.active_exposure['key'])
                if exposure_thresholds.get(classification['key']):
                    exposure_thresholds_classifications = exposure_thresholds\
                        .get(classification['key'])
                    min_value_input.setValue(
                        exposure_thresholds_classifications['classes'][
                            the_class['key']][0])
                else:
                    default_min = the_class['numeric_default_min']
                    if isinstance(default_min, dict):
                        default_min = the_class[
                            'numeric_default_min'][selected_unit]
                    min_value_input.setValue(default_min)
            else:
                default_min = the_class['numeric_default_min']
                if isinstance(default_min, dict):
                    default_min = the_class[
                        'numeric_default_min'][selected_unit]
                min_value_input.setValue(default_min)
            min_value_input.setSingleStep(0.1)

            # Max label
            max_label = QLabel(tr('Max <='))

            # Max value as double spin
            max_value_input = QDoubleSpinBox()
            # TODO(IS) We can set the min and max depends on the unit, later
            max_value_input.setMinimum(0)
            max_value_input.setMaximum(999999)
            if thresholds.get(self.active_exposure['key']):
                exposure_thresholds = thresholds.get(
                    self.active_exposure['key'])
                if exposure_thresholds.get(classification['key']):
                    exposure_thresholds_classifications = exposure_thresholds \
                        .get(classification['key'])
                    max_value_input.setValue(
                        exposure_thresholds_classifications['classes'][
                            the_class['key']][1])
                else:
                    default_max = the_class['numeric_default_max']
                    if isinstance(default_max, dict):
                        default_max = the_class[
                            'numeric_default_max'][selected_unit]
                    max_value_input.setValue(default_max)
            else:
                default_max = the_class['numeric_default_max']
                if isinstance(default_max, dict):
                    default_max = the_class[
                        'numeric_default_max'][selected_unit]
                max_value_input.setValue(default_max)
            max_value_input.setSingleStep(0.1)

            # Add to class_layout
            class_layout.addWidget(min_label)
            class_layout.addWidget(min_value_input)
            class_layout.addWidget(max_label)
            class_layout.addWidget(max_value_input)

            class_layout.setStretch(0, 1)
            class_layout.setStretch(1, 2)
            class_layout.setStretch(2, 1)
            class_layout.setStretch(3, 2)

            # Add to grid_layout
            grid_layout_thresholds.addWidget(class_label, i, 0)
            grid_layout_thresholds.addLayout(class_layout, i, 1)

            self.threshold_classes[the_class['key']] = [
                min_value_input, max_value_input]

        grid_layout_thresholds.setColumnStretch(0, 1)
        grid_layout_thresholds.setColumnStretch(0, 2)

        def min_max_changed(double_spin_index, mode):
            """Slot when min or max value change.

            :param double_spin_index: The index of the double spin.
            :type double_spin_index: int

            :param mode: The flag to indicate the min or max value.
            :type mode: int
            """
            if mode == MAX_VALUE_MODE:
                current_max_value = list(self.threshold_classes.values())[
                    double_spin_index][1]
                target_min_value = list(self.threshold_classes.values())[
                    double_spin_index + 1][0]
                if current_max_value.value() != target_min_value.value():
                    target_min_value.setValue(current_max_value.value())
            elif mode == MIN_VALUE_MODE:
                current_min_value = list(self.threshold_classes.values())[
                    double_spin_index][0]
                target_max_value = list(self.threshold_classes.values())[
                    double_spin_index - 1][1]
                if current_min_value.value() != target_max_value.value():
                    target_max_value.setValue(current_min_value.value())

        # Set behaviour
        for k, v in list(self.threshold_classes.items()):
            index = list(self.threshold_classes.keys()).index(k)
            if index < len(self.threshold_classes) - 1:
                # Max value changed
                v[1].valueChanged.connect(partial(
                    min_max_changed,
                    double_spin_index=index,
                    mode=MAX_VALUE_MODE))
            if index > 0:
                # Min value
                v[0].valueChanged.connect(partial(
                    min_max_changed,
                    double_spin_index=index,
                    mode=MIN_VALUE_MODE))

        grid_layout_thresholds.setSpacing(0)

        self.right_layout.addLayout(grid_layout_thresholds)

    def add_buttons(self, classification):
        """Helper to setup 3 buttons.

        :param classification: The current classification.
        :type classification: dict
        """
        # Note(IS): Until we have good behaviour, we will disable cancel
        # button.
        # Add 3 buttons: Restore default, Cancel, Save

        # Restore default button, only for continuous layer (with threshold)
        if self.layer_mode == layer_mode_continuous['key']:
            self.restore_default_button = QPushButton(tr('Restore Default'))
            self.restore_default_button.clicked.connect(partial(
                self.restore_default_button_clicked,
                classification=classification))

        # Cancel button
        # cancel_button = QPushButton(tr('Cancel'))
        # cancel_button.clicked.connect(self.cancel_button_clicked)

        # Save button
        self.save_button = QPushButton(tr('Save'))
        self.save_button.clicked.connect(
            partial(self.save_button_clicked, classification=classification))

        button_layout = QHBoxLayout()
        button_layout.addStretch(1)
        button_layout.addWidget(self.restore_default_button)
        button_layout.addWidget(self.save_button)

        button_layout.setStretch(0, 3)
        button_layout.setStretch(1, 1)
        button_layout.setStretch(2, 1)
        # button_layout.setStretch(3, 1)

        self.right_layout.addLayout(button_layout)

    def setup_value_mapping_panels(self, classification):
        """Setup value mapping panel in the right panel.

        :param classification: Classification definition.
        :type classification: dict
        """
        # Set text in the label
        layer_purpose = self.parent.step_kw_purpose.selected_purpose()
        layer_subcategory = self.parent.step_kw_subcategory. \
            selected_subcategory()

        if is_raster_layer(self.parent.layer):
            description_text = classify_raster_question % (
                layer_subcategory['name'],
                layer_purpose['name'],
                classification['name'])

            dataset = gdal.Open(self.parent.layer.source(), GA_ReadOnly)
            active_band = self.parent.step_kw_band_selector.selected_band()
            unique_values = numpy.unique(numpy.array(
                dataset.GetRasterBand(active_band).ReadAsArray()))
            field_type = 0
            # Convert datatype to a json serializable type
            if numpy.issubdtype(unique_values.dtype, float):
                unique_values = [float(i) for i in unique_values]
            else:
                unique_values = [int(i) for i in unique_values]
        else:
            field = self.parent.step_kw_field.selected_fields()
            field_index = self.parent.layer.fields().indexFromName(field)
            field_type = self.parent.layer.fields()[field_index].type()
            description_text = classify_vector_question % (
                layer_subcategory['name'],
                layer_purpose['name'],
                classification['name'],
                field.upper())
            unique_values = list(self.parent.layer.uniqueValues(field_index))

        # Set description
        description_label = QLabel(description_text)
        description_label.setWordWrap(True)
        self.right_layout.addWidget(description_label)

        self.list_unique_values = QListWidget()
        self.list_unique_values.setDragDropMode(QAbstractItemView.DragDrop)
        self.list_unique_values.setDefaultDropAction(Qt.MoveAction)

        self.tree_mapping_widget = QTreeWidget()
        self.tree_mapping_widget.setDragDropMode(QAbstractItemView.DragDrop)
        self.tree_mapping_widget.setDefaultDropAction(Qt.MoveAction)
        self.tree_mapping_widget.header().hide()

        self.tree_mapping_widget.itemChanged.connect(
            self.update_dragged_item_flags)

        value_mapping_layout = QHBoxLayout()
        value_mapping_layout.addWidget(self.list_unique_values)
        value_mapping_layout.addWidget(self.tree_mapping_widget)

        self.right_layout.addLayout(value_mapping_layout)

        default_classes = classification['classes']

        # Assign unique values to classes (according to default)
        unassigned_values = list()
        assigned_values = dict()
        for default_class in default_classes:
            assigned_values[default_class['key']] = list()
        for unique_value in unique_values:
            if (unique_value is None
                    or (hasattr(unique_value, 'isNull')
                        and unique_value.isNull())):
                # Don't classify features with NULL value
                continue
            # Capitalization of the value and removing '_' (raw OSM data).
            value_as_string = str(unique_value).upper().replace('_', ' ')
            assigned = False
            for default_class in default_classes:
                if 'string_defaults' in default_class:
                    # To make it case insensitive
                    upper_string_defaults = [
                        c.upper() for c in default_class['string_defaults']]
                    in_string_default = (
                        value_as_string in upper_string_defaults)
                    condition_1 = field_type > 9 and in_string_default
                else:
                    condition_1 = False
                condition_2 = (
                    field_type < 10
                    and 'numeric_default_min' in default_class
                    and 'numeric_default_max' in default_class
                    and (default_class['numeric_default_min']
                         <= unique_value
                         < default_class['numeric_default_max']))
                if condition_1 or condition_2:
                    assigned_values[default_class['key']] += [unique_value]
                    assigned = True
                    break
            if not assigned:
                # add to unassigned values list otherwise
                unassigned_values += [unique_value]
        self.populate_classified_values(
            unassigned_values,
            assigned_values,
            default_classes,
            self.list_unique_values,
            self.tree_mapping_widget
        )

        # Current value map for exposure and classification
        available_classifications = self.value_maps.get(
            self.active_exposure['key'])
        if not available_classifications:
            return
        # Get active one
        current_classification = available_classifications.get(
            classification['key'])
        if not current_classification:
            return
        # Should come from metadata
        current_value_map = current_classification.get('classes')
        if not current_value_map:
            return

        unassigned_values = list()
        assigned_values = dict()
        for default_class in default_classes:
            assigned_values[default_class['key']] = list()
        for unique_value in unique_values:
            if (unique_value is None
                    or (hasattr(unique_value, 'isNull')
                        and unique_value.isNull())):
                # Don't classify features with NULL value
                continue
            # check in value map
            assigned = False
            for key, value_list in list(current_value_map.items()):
                if unique_value in value_list and key in assigned_values:
                    assigned_values[key] += [unique_value]
                    assigned = True
            if not assigned:
                unassigned_values += [unique_value]
        self.populate_classified_values(
            unassigned_values,
            assigned_values,
            default_classes,
            self.list_unique_values,
            self.tree_mapping_widget
        )

    # noinspection PyMethodMayBeStatic
    def update_dragged_item_flags(self, item):
        """Fix the drop flag after the item is dropped.

        Check if it looks like an item dragged from QListWidget
        to QTreeWidget and disable the drop flag.
        For some reasons the flag is set when dragging.

        :param item: Item which is dragged.
        :type item: QTreeWidgetItem

        .. note:: This is a slot executed when the item change.
        """
        if int(item.flags() & Qt.ItemIsDropEnabled) \
                and int(item.flags() & Qt.ItemIsDragEnabled):
            item.setFlags(item.flags() & ~Qt.ItemIsDropEnabled)

    @staticmethod
    def populate_classified_values(
            unassigned_values, assigned_values, default_classes,
            list_unique_values, tree_mapping_widget):
        """Populate lstUniqueValues and treeClasses.from the parameters.

        :param unassigned_values: List of values that haven't been assigned
            to a class. It will be put in list_unique_values.
        :type unassigned_values: list

        :param assigned_values: Dictionary with class as the key and list of
            value as the value of the dictionary. It will be put in
            tree_mapping_widget.
        :type assigned_values: dict

        :param default_classes: Default classes from unit.
        :type default_classes: list

        :param list_unique_values: List Widget for unique values
        :type list_unique_values: QListWidget

        :param tree_mapping_widget: Tree Widget for classifying.
        :type tree_mapping_widget: QTreeWidget
        """
        # Populate the unique values list
        list_unique_values.clear()
        list_unique_values.setSelectionMode(
            QAbstractItemView.ExtendedSelection)
        for value in unassigned_values:
            value_as_string = value is not None and str(value) or 'NULL'
            list_item = QListWidgetItem(list_unique_values)
            list_item.setFlags(
                Qt.ItemIsEnabled
                | Qt.ItemIsSelectable
                | Qt.ItemIsDragEnabled)
            list_item.setData(Qt.UserRole, value)
            list_item.setText(value_as_string)
            list_unique_values.addItem(list_item)
        # Populate assigned values tree
        tree_mapping_widget.clear()
        bold_font = QFont()
        bold_font.setItalic(True)
        bold_font.setBold(True)
        bold_font.setWeight(75)
        tree_mapping_widget.invisibleRootItem().setFlags(
            Qt.ItemIsEnabled)
        for default_class in default_classes:
            # Create branch for class
            tree_branch = QTreeWidgetItem(tree_mapping_widget)
            tree_branch.setFlags(
                Qt.ItemIsDropEnabled | Qt.ItemIsEnabled)
            tree_branch.setExpanded(True)
            tree_branch.setFont(0, bold_font)
            if 'name' in default_class:
                default_class_name = default_class['name']
            else:
                default_class_name = default_class['key']
            tree_branch.setText(0, default_class_name)
            tree_branch.setData(0, Qt.UserRole, default_class['key'])
            if 'description' in default_class:
                tree_branch.setToolTip(0, default_class['description'])
            # Assign known values
            for value in assigned_values[default_class['key']]:
                string_value = value is not None and str(value) or 'NULL'
                tree_leaf = QTreeWidgetItem(tree_branch)
                tree_leaf.setFlags(
                    Qt.ItemIsEnabled
                    | Qt.ItemIsSelectable
                    | Qt.ItemIsDragEnabled)
                tree_leaf.setData(0, Qt.UserRole, value)
                tree_leaf.setText(0, string_value)

    def cancel_button_clicked(self):
        """Action for cancel button clicked."""
        # Change mode
        self.mode = CHOOSE_MODE
        # Enable all edit buttons and combo boxes
        for i in range(len(self.exposures)):
            if i == self.special_case_index:
                self.exposure_edit_buttons[i].setEnabled(False)
                self.exposure_combo_boxes[i].setEnabled(False)
                continue
            if self.get_classification(self.exposure_combo_boxes[i]):
                self.exposure_edit_buttons[i].setEnabled(True)
            else:
                self.exposure_edit_buttons[i].setEnabled(False)
            # self.exposure_edit_buttons[i].setText(tr('Edit'))
            self.exposure_combo_boxes[i].setEnabled(True)

        # Clear right panel
        clear_layout(self.right_layout)
        # Show current state
        self.show_current_state()
        # Unset active exposure
        self.active_exposure = None

        self.parent.pbnNext.setEnabled(self.is_ready_to_next_step())

    def save_button_clicked(self, classification):
        """Action for save button clicked.

        :param classification: The classification that being edited.
        :type classification: dict
        """
        # Save current edit
        if self.layer_mode == layer_mode_continuous:
            thresholds = self.get_threshold()
            classification_class = {
                'classes': thresholds,
                'active': True
            }
            if self.thresholds.get(self.active_exposure['key']):
                # Set other class to not active
                for current_classification in list(self.thresholds.get(
                        self.active_exposure['key']).values()):
                    current_classification['active'] = False
            else:
                self.thresholds[self.active_exposure['key']] = {}

            self.thresholds[self.active_exposure['key']][
                classification['key']] = classification_class
        else:
            value_maps = self.get_value_map()
            classification_class = {
                'classes': value_maps,
                'active': True
            }
            if self.value_maps.get(self.active_exposure['key']):
                # Set other class to not active
                for current_classification in list(self.value_maps.get(
                        self.active_exposure['key']).values()):
                    current_classification['active'] = False
            else:
                self.value_maps[self.active_exposure['key']] = {}

            self.value_maps[self.active_exposure['key']][
                classification['key']] = classification_class
        # Back to choose mode
        self.cancel_button_clicked()

    def restore_default_button_clicked(self, classification):
        """Action for restore default button clicked.

        It will set the threshold with default value.

        :param classification: The classification that being edited.
        :type classification: dict
        """
        # Obtain default value
        class_dict = {}
        for the_class in classification.get('classes'):
            class_dict[the_class['key']] = {
                'numeric_default_min': the_class['numeric_default_min'],
                'numeric_default_max': the_class['numeric_default_max'],
            }
        # Set for all threshold
        for key, value in list(self.threshold_classes.items()):
            value[0].setValue(class_dict[key]['numeric_default_min'])
            value[1].setValue(class_dict[key]['numeric_default_max'])

    def get_threshold(self):
        """Return threshold based on current state."""
        value_map = dict()
        for key, value in list(self.threshold_classes.items()):
            value_map[key] = [
                value[0].value(),
                value[1].value(),
            ]
        return value_map

    def get_value_map(self):
        """Obtain the value-to-class mapping set by user.

        :returns: The complete mapping as a dict of lists.
        :rtype: dict
        """
        value_map = {}
        tree_clone = self.tree_mapping_widget.invisibleRootItem().clone()
        for tree_branch in tree_clone.takeChildren():
            value_list = []
            for tree_leaf in tree_branch.takeChildren():
                value_list += [tree_leaf.data(0, Qt.UserRole)]
            if value_list:
                value_map[tree_branch.data(0, Qt.UserRole)] = value_list
        return value_map

    def set_current_state(self):
        """"Helper to set the state of the step from current keywords."""
        if not self.thresholds:
            self.thresholds = self.parent.get_existing_keyword('thresholds')
        if not self.value_maps:
            self.value_maps = self.parent.get_existing_keyword('value_maps')

    def classifications_combo_box_changed(
            self, index, exposure, exposure_combo_box, edit_button):
        """Action when classification combo box changed.

        :param index: The index of the combo box.
        :type index: int

        :param exposure: The exposure associated with the combo box.
        :type exposure: dict

        :param exposure_combo_box: Combo box for the classification.
        :type exposure_combo_box: QComboBox

        :param edit_button: The edit button associate with combo box.
        :type edit_button: QPushButton
        """
        # Disable button if it's no classification
        edit_button.setEnabled(bool(index))

        classification = self.get_classification(exposure_combo_box)
        self.activate_classification(exposure, classification)
        clear_layout(self.right_layout)
        self.show_current_state()

        self.parent.pbnNext.setEnabled(self.is_ready_to_next_step())

        # Open edit panel directly
        edit_button.click()

    def activate_classification(self, exposure, classification=None):
        """Set active to True for classification for the exposure.

        If classification = None, all classification set active = False.

        :param exposure: Exposure definition.
        :type exposure: dict

        :param classification: Classification definition.
        :type classification: dict
        """
        if self.layer_mode == layer_mode_continuous:
            selected_unit = self.parent.step_kw_unit.selected_unit()['key']
            target = self.thresholds.get(exposure['key'])
            if target is None:
                self.thresholds[exposure['key']] = {}
            target = self.thresholds.get(exposure['key'])
        else:
            selected_unit = None
            target = self.value_maps.get(exposure['key'])
            if target is None:
                self.value_maps[exposure['key']] = {}
            target = self.value_maps.get(exposure['key'])

        if classification is not None:
            if classification['key'] not in target:
                if self.layer_mode == layer_mode_continuous:
                    default_classes = default_classification_thresholds(
                        classification, selected_unit)
                    target[classification['key']] = {
                        'classes': default_classes,
                        'active': True
                    }
                else:
                    # Set classes to empty, since we haven't mapped anything
                    target[classification['key']] = {
                        'classes': {},
                        'active': True
                    }
                return

        for classification_key, value in list(target.items()):
            if classification is None:
                value['active'] = False
                continue

            if classification_key == classification['key']:
                value['active'] = True
            else:
                value['active'] = False

    @property
    def step_name(self):
        """Get the human friendly name for the wizard step.

        :returns: The name of the wizard step.
        :rtype: str
        """
        return tr('Multi Classification Step')

    def help_content(self):
        """Return the content of help for this step wizard.

            We only needs to re-implement this method in each wizard step.

        :returns: A message object contains help.
        :rtype: m.Message
        """
        message = m.Message()
        message.add(m.Paragraph(tr(
            'In this wizard step: {step_name}, you will be able to set the '
            'classification that you will use per exposure type. You can also '
            'set the threshold or value map for each classification.'
        ).format(step_name=self.step_name)))
        return message
class GroupSelectParameterWidget(GenericParameterWidget):
    """Widget class for Group Select Parameter."""
    def __init__(self, parameter, parent=None):
        """Constructor.

        :param parameter: A GroupSelectParameter object.
        :type parameter: GroupSelectParameter
        """
        QWidget.__init__(self, parent)
        self._parameter = parameter

        # Store spin box
        self.spin_boxes = {}

        # Create elements
        # Label (name)
        self.label = QLabel(self._parameter.name)

        # Layouts
        self.main_layout = QVBoxLayout()
        self.input_layout = QVBoxLayout()

        # _inner_input_layout must be filled with widget in the child class
        self.inner_input_layout = QVBoxLayout()

        self.radio_button_layout = QGridLayout()

        # Create radio button group
        self.input_button_group = QButtonGroup()

        # List widget
        self.list_widget = QListWidget()
        self.list_widget.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.list_widget.setDragDropMode(QAbstractItemView.DragDrop)
        self.list_widget.setDefaultDropAction(Qt.MoveAction)
        self.list_widget.setEnabled(False)
        self.list_widget.setSizePolicy(QSizePolicy.Maximum,
                                       QSizePolicy.Expanding)

        for i, key in enumerate(self._parameter.options):
            value = self._parameter.options[key]
            radio_button = QRadioButton(value.get('label'))
            self.radio_button_layout.addWidget(radio_button, i, 0)
            if value.get('type') == SINGLE_DYNAMIC:
                percentage_spin_box = PercentageSpinBox(self)
                self.radio_button_layout.addWidget(percentage_spin_box, i, 1)
                percentage_spin_box.setValue(value.get('value', 0))
                step = percentage_spin_box.singleStep()
                if step > 1:
                    precision = 0
                else:
                    precision = len(str(step).split('.')[1])
                    if precision > 3:
                        precision = 3
                percentage_spin_box.setDecimals(precision)
                self.spin_boxes[key] = percentage_spin_box

                # Enable spin box depends on the selected option
                if self._parameter.selected == key:
                    percentage_spin_box.setEnabled(True)
                else:
                    percentage_spin_box.setEnabled(False)

            elif value.get('type') == STATIC:
                static_value = value.get('value', 0)
                if static_value is not None:
                    self.radio_button_layout.addWidget(
                        QLabel(str(static_value * 100) + ' %'), i, 1)
            elif value.get('type') == MULTIPLE_DYNAMIC:
                if self._parameter.selected == key:
                    self.list_widget.setEnabled(True)
                else:
                    self.list_widget.setEnabled(False)

            self.input_button_group.addButton(radio_button, i)
            if self._parameter.selected == key:
                radio_button.setChecked(True)

        # Help text
        self.help_label = QLabel(self._parameter.help_text)
        self.help_label.setSizePolicy(QSizePolicy.Maximum,
                                      QSizePolicy.Expanding)
        self.help_label.setWordWrap(True)
        self.help_label.setAlignment(Qt.AlignTop)

        self.inner_input_layout.addLayout(self.radio_button_layout)
        self.inner_input_layout.addWidget(self.list_widget)

        # Put elements into layouts
        self.input_layout.addWidget(self.label)
        self.input_layout.addLayout(self.inner_input_layout)

        self.help_layout = QVBoxLayout()
        self.help_layout.addWidget(self.help_label)

        self.main_layout.addLayout(self.input_layout)
        self.main_layout.addLayout(self.help_layout)

        self.setLayout(self.main_layout)

        self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Expanding)

        # Update list widget
        self.update_list_widget()

        # Connect signal
        # noinspection PyUnresolvedReferences
        self.input_button_group.buttonClicked.connect(
            self.radio_buttons_clicked)

    def get_parameter(self):
        """Obtain list parameter object from the current widget state.

        :returns: A DefaultValueParameter from the current state of widget
        :rtype: DefaultValueParameter
        """
        # Set value for each key
        for key, value in list(self._parameter.options.items()):
            if value.get('type') == STATIC:
                continue
            elif value.get('type') == SINGLE_DYNAMIC:
                new_value = self.spin_boxes.get(key).value()
                self._parameter.set_value_for_key(key, new_value)
            elif value.get('type') == MULTIPLE_DYNAMIC:
                # Need to iterate through all items
                items = []
                for index in range(self.list_widget.count()):
                    items.append(self.list_widget.item(index))
                new_value = [i.text() for i in items]
                self._parameter.set_value_for_key(key, new_value)

        # Get selected radio button
        radio_button_checked_id = self.input_button_group.checkedId()
        # No radio button checked, then default value = None
        if radio_button_checked_id == -1:
            self._parameter.selected = None
        else:
            self._parameter.selected = list(
                self._parameter.options.keys())[radio_button_checked_id]

        return self._parameter

    def update_list_widget(self):
        """Update list widget when radio button is clicked."""
        # Get selected radio button
        radio_button_checked_id = self.input_button_group.checkedId()
        # No radio button checked, then default value = None
        if radio_button_checked_id > -1:
            selected_dict = list(
                self._parameter.options.values())[radio_button_checked_id]
            if selected_dict.get('type') == MULTIPLE_DYNAMIC:
                for field in selected_dict.get('value'):
                    # Update list widget
                    field_item = QListWidgetItem(self.list_widget)
                    field_item.setFlags(Qt.ItemIsEnabled
                                        | Qt.ItemIsSelectable
                                        | Qt.ItemIsDragEnabled)
                    field_item.setData(Qt.UserRole, field)
                    field_item.setText(field)
                    self.list_widget.addItem(field_item)

    def radio_buttons_clicked(self):
        """Handler when selected radio button changed."""
        # Disable all spin boxes
        for spin_box in list(self.spin_boxes.values()):
            spin_box.setEnabled(False)
        # Disable list widget
        self.list_widget.setEnabled(False)

        # Get selected radio button
        radio_button_checked_id = self.input_button_group.checkedId()

        if radio_button_checked_id > -1:
            selected_value = list(
                self._parameter.options.values())[radio_button_checked_id]
            if selected_value.get('type') == MULTIPLE_DYNAMIC:
                # Enable list widget
                self.list_widget.setEnabled(True)
            elif selected_value.get('type') == SINGLE_DYNAMIC:
                selected_key = list(
                    self._parameter.options.keys())[radio_button_checked_id]
                self.spin_boxes[selected_key].setEnabled(True)

    def select_radio_button(self, key):
        """Helper to select a radio button with key.

        :param key: The key of the radio button.
        :type key: str
        """
        key_index = list(self._parameter.options.keys()).index(key)
        radio_button = self.input_button_group.button(key_index)
        radio_button.click()
class ScenarioSelectionDialog(QDialog):
    """
    A dialog used for selecting from available scenarios
    """

    def __init__(self, scenario_registry: ScenarioRegistry, parent=None):
        """
        Constructor for ScenarioSelectionDialog
        :param scenario_registry: linked scenario registry
        :param parent: parent widget
        """
        super().__init__(parent)

        self.scenario_registry = scenario_registry

        self.setWindowTitle(self.tr('Select Current Scenario'))

        layout = QVBoxLayout()

        self.search = QgsFilterLineEdit()
        self.search.setShowSearchIcon(True)
        self.search.setPlaceholderText(self.tr('Search for scenario'))
        self.search.textChanged.connect(self.filter_changed)
        layout.addWidget(self.search)

        self.list = QListWidget()
        for title, scenario_id in scenario_registry.scenario_titles().items():
            item = QListWidgetItem(title)
            item.setData(Qt.UserRole, scenario_id)
            self.list.addItem(item)

        layout.addWidget(self.list, 10)

        button_box = QDialogButtonBox(
            QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        layout.addWidget(button_box)
        button_box.rejected.connect(self.reject)
        button_box.accepted.connect(self.accept)

        self.setLayout(layout)

        self.list.itemDoubleClicked.connect(
            self.accept)

        # select last scenario by default
        if self.list.count() > 0:
            self.list.item(self.list.count() - 1).setSelected(True)

    def set_selected_scenario(self, scenario):
        """
        Sets the scenario selected in the dialog
        :param scenario: scenario to select
        """
        for i in range(self.list.count()):
            if self.list.item(i).data(Qt.UserRole) == scenario:
                self.list.item(i).setSelected(True)
                return

    def selected_scenario(self):
        """
        Returns the scenario selected in the dialog
        """
        if self.list.selectedItems():
            return self.list.selectedItems()[0].data(Qt.UserRole)

        return None

    def filter_changed(self, filter_text):
        """
        Handles search filter changes
        """
        for i in range(self.list.count()):
            item = self.list.item(i)
            item.setHidden(filter_text.upper() not in item.text().upper())
    def __init__(self, parameter, parent=None):
        """Constructor.

        :param parameter: A GroupSelectParameter object.
        :type parameter: GroupSelectParameter
        """
        QWidget.__init__(self, parent)
        self._parameter = parameter

        # Store spin box
        self.spin_boxes = {}

        # Create elements
        # Label (name)
        self.label = QLabel(self._parameter.name)

        # Layouts
        self.main_layout = QVBoxLayout()
        self.input_layout = QVBoxLayout()

        # _inner_input_layout must be filled with widget in the child class
        self.inner_input_layout = QVBoxLayout()

        self.radio_button_layout = QGridLayout()

        # Create radio button group
        self.input_button_group = QButtonGroup()

        # List widget
        self.list_widget = QListWidget()
        self.list_widget.setSelectionMode(QAbstractItemView.ExtendedSelection)
        self.list_widget.setDragDropMode(QAbstractItemView.DragDrop)
        self.list_widget.setDefaultDropAction(Qt.MoveAction)
        self.list_widget.setEnabled(False)
        self.list_widget.setSizePolicy(QSizePolicy.Maximum,
                                       QSizePolicy.Expanding)

        for i, key in enumerate(self._parameter.options):
            value = self._parameter.options[key]
            radio_button = QRadioButton(value.get('label'))
            self.radio_button_layout.addWidget(radio_button, i, 0)
            if value.get('type') == SINGLE_DYNAMIC:
                percentage_spin_box = PercentageSpinBox(self)
                self.radio_button_layout.addWidget(percentage_spin_box, i, 1)
                percentage_spin_box.setValue(value.get('value', 0))
                step = percentage_spin_box.singleStep()
                if step > 1:
                    precision = 0
                else:
                    precision = len(str(step).split('.')[1])
                    if precision > 3:
                        precision = 3
                percentage_spin_box.setDecimals(precision)
                self.spin_boxes[key] = percentage_spin_box

                # Enable spin box depends on the selected option
                if self._parameter.selected == key:
                    percentage_spin_box.setEnabled(True)
                else:
                    percentage_spin_box.setEnabled(False)

            elif value.get('type') == STATIC:
                static_value = value.get('value', 0)
                if static_value is not None:
                    self.radio_button_layout.addWidget(
                        QLabel(str(static_value * 100) + ' %'), i, 1)
            elif value.get('type') == MULTIPLE_DYNAMIC:
                if self._parameter.selected == key:
                    self.list_widget.setEnabled(True)
                else:
                    self.list_widget.setEnabled(False)

            self.input_button_group.addButton(radio_button, i)
            if self._parameter.selected == key:
                radio_button.setChecked(True)

        # Help text
        self.help_label = QLabel(self._parameter.help_text)
        self.help_label.setSizePolicy(QSizePolicy.Maximum,
                                      QSizePolicy.Expanding)
        self.help_label.setWordWrap(True)
        self.help_label.setAlignment(Qt.AlignTop)

        self.inner_input_layout.addLayout(self.radio_button_layout)
        self.inner_input_layout.addWidget(self.list_widget)

        # Put elements into layouts
        self.input_layout.addWidget(self.label)
        self.input_layout.addLayout(self.inner_input_layout)

        self.help_layout = QVBoxLayout()
        self.help_layout.addWidget(self.help_label)

        self.main_layout.addLayout(self.input_layout)
        self.main_layout.addLayout(self.help_layout)

        self.setLayout(self.main_layout)

        self.setSizePolicy(QSizePolicy.Maximum, QSizePolicy.Expanding)

        # Update list widget
        self.update_list_widget()

        # Connect signal
        # noinspection PyUnresolvedReferences
        self.input_button_group.buttonClicked.connect(
            self.radio_buttons_clicked)
示例#32
0
    def __init__(self, dockwidget, parent, params, edit_type):

        QDialog.__init__(self, parent)
        main_lay = QVBoxLayout(self)
        self.dockwidget = dockwidget
        self.params = params
        self.edit_type = edit_type

        self.x_label = ''
        self.y_label = ''

        self.setMinimumWidth(600)
        self.setMinimumHeight(400)

        self.setWindowTitle(self.titles[edit_type])  # TODO: softcode
        self.setWindowModality(QtCore.Qt.ApplicationModal)

        self.current = None

        self.current_saved = False

        # File
        self.lbl_file = QLabel('File:')
        self.fra_file = QFrame()
        self.fra_file.setContentsMargins(0, 0, 0, 0)
        fra_file_lay = QHBoxLayout(self.fra_file)

        if edit_type == self.edit_patterns:
            self.txt_file = QLineEdit(self.params.patterns_file)
        elif edit_type == self.edit_curves:
            self.txt_file = QLineEdit(self.params.curves_file)

        self.txt_file.setReadOnly(True)
        self.txt_file.setAlignment(QtCore.Qt.AlignLeft
                                   | QtCore.Qt.AlignVCenter)
        self.txt_file.setSizePolicy(QSizePolicy.MinimumExpanding,
                                    QSizePolicy.Minimum)
        fra_file_lay.addWidget(self.txt_file)
        self.btn_file = QPushButton('Change')  # TODO: softcode
        self.btn_file.clicked.connect(self.import_file)
        fra_file_lay.addWidget(self.btn_file)
        fra_file_lay.setContentsMargins(0, 0, 0, 0)

        self.lbl_list = QLabel(self.labels[edit_type])
        self.lst_list = QListWidget()
        self.lst_list.currentItemChanged.connect(self.list_item_changed)

        # Form
        self.fra_form = QFrame()
        fra_form1_lay = QFormLayout(self.fra_form)
        fra_form1_lay.setContentsMargins(0, 0, 0, 0)
        fra_form1_lay.addRow(self.lbl_list, self.lst_list)

        # Buttons
        self.fra_buttons = QFrame()
        fra_buttons_lay = QHBoxLayout(self.fra_buttons)
        fra_buttons_lay.setContentsMargins(0, 0, 0, 0)

        if self.edit_type == self.edit_patterns:
            ele_name = 'pattern'
        elif self.edit_type == self.edit_curves:
            ele_name = 'curve'

        self.btn_new = QPushButton('New ' + ele_name)  # TODO: softcode
        self.btn_new.clicked.connect(self.new_element)
        fra_buttons_lay.addWidget(self.btn_new)

        self.btn_import = QPushButton('Import ' + ele_name +
                                      's')  # TODO: softcode
        self.btn_import.clicked.connect(self.import_file)
        fra_buttons_lay.addWidget(self.btn_import)

        self.btn_save = QPushButton('Save current ' +
                                    ele_name)  # TODO: softcode
        self.btn_save.clicked.connect(self.save)
        fra_buttons_lay.addWidget(self.btn_save)

        self.btn_del = QPushButton('Delete current ' +
                                   ele_name)  # TODO: softcode
        self.btn_del.clicked.connect(self.del_item)
        fra_buttons_lay.addWidget(self.btn_del)

        # ID
        self.lbl_id = QLabel('ID:')
        self.txt_id = QLineEdit()
        self.txt_id.setSizePolicy(QSizePolicy.Maximum,
                                  QSizePolicy.MinimumExpanding)
        self.lbl_desc = QLabel('Desc.:')
        self.txt_desc = QLineEdit()

        self.fra_id = QFrame()
        fra_id_lay = QHBoxLayout(self.fra_id)
        fra_id_lay.addWidget(self.lbl_id)
        fra_id_lay.addWidget(self.txt_id)
        fra_id_lay.addWidget(self.lbl_desc)
        fra_id_lay.addWidget(self.txt_desc)

        # Table form
        self.table = QTableWidget(self)
        self.rows_nr = 24
        self.cols_nr = 2
        self.table.setRowCount(self.rows_nr)
        self.table.setColumnCount(self.cols_nr)
        self.table.verticalHeader().setVisible(False)

        # Initialize empty table
        self.clear_table()

        self.table.itemChanged.connect(self.data_changed)

        self.fra_table = QFrame()
        fra_table_lay = QVBoxLayout(self.fra_table)
        fra_table_lay.setContentsMargins(0, 0, 0, 0)

        if edit_type == self.edit_curves:
            self.fra_pump_type = QFrame()
            fra_pump_type_lay = QFormLayout(self.fra_pump_type)

            self.lbl_pump_type = QLabel('Curve type:')  # TODO: softcode
            self.cbo_pump_type = QComboBox()

            for key, name in Curve.type_names.items():
                self.cbo_pump_type.addItem(name, key)

            fra_pump_type_lay.addRow(self.lbl_pump_type, self.cbo_pump_type)

            fra_table_lay.addWidget(self.fra_pump_type)

            self.cbo_pump_type.activated.connect(self.cbo_pump_type_activated)

        fra_table_lay.addWidget(self.table)
        self.btn_add_row = QPushButton('Add row')
        self.btn_add_row.clicked.connect(self.add_row)
        fra_table_lay.addWidget(self.btn_add_row)

        # Graph canvas
        self.fra_graph = QFrame()
        self.static_canvas = StaticMplCanvas(self.fra_graph,
                                             width=5,
                                             height=4,
                                             dpi=100)
        fra_graph_lay = QVBoxLayout(self.fra_graph)
        fra_graph_lay.addWidget(self.static_canvas)

        # Top frame
        self.fra_top = QFrame()
        fra_top_lay = QVBoxLayout(self.fra_top)
        fra_top_lay.addWidget(self.fra_form)
        fra_top_lay.addWidget(self.fra_id)
        fra_top_lay.addWidget(self.fra_buttons)

        # Bottom frame
        self.fra_bottom = QFrame()
        fra_bottom_lay = QHBoxLayout(self.fra_bottom)
        fra_bottom_lay.addWidget(self.fra_table)
        fra_bottom_lay.addWidget(self.fra_graph)

        # Main
        main_lay.addWidget(self.fra_top)
        main_lay.addWidget(self.fra_bottom)

        # Get existing patterns/curves
        self.need_to_update_graph = False
        if self.edit_type == self.edit_patterns:
            for pattern_id, pattern in self.params.patterns.items():
                self.lst_list.addItem(pattern.id)

        elif self.edit_type == self.edit_curves:
            for curve_id, curve in self.params.curves.items():
                self.lst_list.addItem(curve.id)

        if self.lst_list.count() > 0:
            self.lst_list.setCurrentRow(0)
            self.txt_id.setEnabled(True)
            self.txt_desc.setEnabled(True)
            self.btn_save.setEnabled(True)
            self.btn_del.setEnabled(True)
            self.table.setEnabled(True)
            self.table.setEditTriggers(QAbstractItemView.AllEditTriggers)
        else:
            self.txt_id.setEnabled(False)
            self.txt_desc.setEnabled(False)
            self.btn_save.setEnabled(False)
            self.btn_del.setEnabled(False)
            self.table.setEnabled(False)
            self.table.setEditTriggers(QAbstractItemView.NoEditTriggers)

        self.new_dialog = None
        self.need_to_update_graph = True
示例#33
0
class TreeSettingItem(QTreeWidgetItem):
    def __init__(self, parent, tree, name, value, action=None):
        QTreeWidgetItem.__init__(self, parent)
        self.parent = parent
        self.tree = tree
        self.name = name
        self._value = value
        self.combo = None
        self.list = None
        self.setText(0, name)
        widget = None
        if isinstance(value, QgsColorButton):
            widget = value
        elif isinstance(value, bool):
            if value:
                self.setCheckState(1, Qt.Checked)
            else:
                self.setCheckState(1, Qt.Unchecked)
        elif isinstance(value, tuple):
            self.combo = QComboBox()
            self.combo.setSizeAdjustPolicy(0)
            for option in value:
                self.combo.addItem(option)
            widget = self.combo
        elif isinstance(value, list):
            self.list = QListWidget()
            self.list.setSizeAdjustPolicy(0)
            self.list.setSelectionMode(QListWidget.MultiSelection)
            for option in value:
                self.list.addItem(option)
            widget = self.list
        else:
            self.setText(1, unicode(value))

        if action:
            layout = QHBoxLayout()
            layout.setMargin(0)
            if widget:
                layout.addWidget(widget)
            button = QToolButton()
            button.setDefaultAction(action)
            button.setText(action.text())
            layout.addWidget(button)
            layout.addStretch(1)
            widget = QWidget()
            widget.setLayout(layout)

        if widget:
            self.tree.setItemWidget(self, 1, widget)

    def setValue(self, value):
        if isinstance(value, bool):
            if value:
                self.setCheckState(1, Qt.Checked)
            else:
                self.setCheckState(1, Qt.Unchecked)
        elif self.combo:
            index = self.combo.findText(value)
            if index != -1:
                self.combo.setCurrentIndex(index)
        else:
            self.setText(1, str(value))

    def value(self):
        if isinstance(self._value, bool):
            return self.checkState(1) == Qt.Checked
        elif isinstance(self._value, (int, float)):
            return float(self.text(1))
        elif isinstance(self._value, tuple):
            return self.combo.currentText()
        else:
            return self.text(1)
class DeprecateElectorateDialog(QDialog):
    """
    A dialog used for selecting electorates to deprecate (or un-deprecate)
    :param district_registry: associated registry of available
    districts to show
    """
    def __init__(self,
                 electorate_registry: LinzElectoralDistrictRegistry,
                 parent=None):
        super().__init__(parent)

        self.electorate_registry = electorate_registry

        self.setWindowTitle(self.tr('Deprecate Electorate'))

        layout = QVBoxLayout()

        self.search = QgsFilterLineEdit()
        self.search.setShowSearchIcon(True)
        self.search.setPlaceholderText(
            self.tr('Search for {}').format(
                electorate_registry.type_string_sentence()))
        self.search.textChanged.connect(self.filter_changed)
        layout.addWidget(self.search)

        request = QgsFeatureRequest()
        request.setFlags(QgsFeatureRequest.NoGeometry)
        request.setSubsetOfAttributes([
            electorate_registry.source_field_index,
            electorate_registry.title_field_index,
            electorate_registry.deprecated_field_index
        ])
        self.list = QListWidget()
        for f in electorate_registry.source_layer.getFeatures(request):
            title = f[electorate_registry.title_field_index]
            code = f[electorate_registry.source_field_index]
            deprecated = f[electorate_registry.deprecated_field_index]

            if deprecated:
                title = '*' + title
            item = QListWidgetItem(title)
            item.setData(Qt.UserRole, code)
            self.list.addItem(item)

        layout.addWidget(self.list, 10)

        button_box = QDialogButtonBox(QDialogButtonBox.Ok
                                      | QDialogButtonBox.Cancel)
        layout.addWidget(button_box)
        button_box.rejected.connect(self.reject)
        button_box.accepted.connect(self.accept)

        self.setLayout(layout)
        self.list.itemDoubleClicked.connect(self.accept)

    def selected_district(self):
        """
        Returns the electorate selected in the dialog
        """
        if self.list.selectedItems():
            return self.list.selectedItems()[0].data(Qt.UserRole)

        return None

    def filter_changed(self, filter_text):
        """
        Handles search filter changes
        """
        for i in range(self.list.count()):
            item = self.list.item(i)
            item.setHidden(filter_text.upper() not in item.text().upper())
示例#35
0
class TagsDialog(QDialog):

    def __init__(self, dockwidget, parent, params):

        QDialog.__init__(self, parent)
        main_lay = QVBoxLayout(self)
        self.dockwidget = dockwidget
        self.params = params

        self.setWindowTitle('Tags editor')

        # Top frame
        self.fra_top = QFrame()
        fra_top_lay = QVBoxLayout(self.fra_top)

        self.lst_main = QListWidget(self)
        self.btn_add = QPushButton('Add tag')
        self.btn_add.clicked.connect(self.add_tag)
        self.btn_remove = QPushButton('Remove tag')
        self.btn_remove.clicked.connect(self.remove_tag)

        fra_top_lay.addWidget(self.lst_main)
        fra_top_lay.addWidget(self.btn_add)
        fra_top_lay.addWidget(self.btn_remove)

        # Bottom frame
        self.fra_bottom = QFrame()
        fra_bottom_lay = QHBoxLayout(self.fra_bottom)

        btb_main = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel)
        btb_main.accepted.connect(self.ok)
        btb_main.rejected.connect(self.reject)

        fra_bottom_lay.addWidget(btb_main)

        # Main
        main_lay.addWidget(self.fra_top)
        main_lay.addWidget(self.fra_bottom)

        self.initialize()

    def initialize(self):
        for tag_name in self.params.tag_names:
            self.lst_main.insertItem(self.lst_main.count(), QListWidgetItem(tag_name, self.lst_main))

    def ok(self):
        tag_names = []
        for r in range(self.lst_main.count()):
            tag_names.append(self.lst_main.item(r).text())

        self.params.tag_names = tag_names

        self.setVisible(False)

    def reject(self):
        self.setVisible(False)

    def add_tag(self):

        tag_name_dialog = TagNameDialog(self.dockwidget, self)
        tag_name_dialog.exec_()
        tag_name = tag_name_dialog.get_tag_name()

        if tag_name is not None:
            current_row = self.lst_main.currentRow()
            if current_row is None:
                current_row = self.lst_main.count()
            self.lst_main.insertItem(current_row, QListWidgetItem(tag_name, self.lst_main))

    def remove_tag(self):
        sel_items = self.lst_main.selectedItems()
        for sel_item in sel_items:
            self.lst_main.takeItem(self.lst_main.row(sel_item))
class StepKwMultiClassifications(WizardStep, FORM_CLASS):

    """InaSAFE Wizard Step Multi Classifications."""

    def __init__(self, parent=None):
        """Constructor for the tab.

        :param parent: widget to use as parent (Wizard Dialog).
        :type parent: QWidget
        """
        WizardStep.__init__(self, parent)
        self.exposures = []
        self.exposure_labels = []
        self.exposure_combo_boxes = []
        self.exposure_edit_buttons = []
        self.mode = CHOOSE_MODE

        self.layer_purpose = None
        self.layer_mode = None

        # Store the current representative state of the UI.
        # self.classifications = {}
        self.value_maps = {}
        self.thresholds = {}

        # Temporary attributes
        self.threshold_classes = OrderedDict()
        self.active_exposure = None

        self.list_unique_values = None
        self.tree_mapping_widget = None

        # GUI, good for testing
        self.save_button = None
        self.restore_default_button = None

        # Has default threshold
        # Trick for EQ raster for population #3853
        self.use_default_thresholds = False
        # Index of the special case exposure classification
        self.special_case_index = None

    def is_ready_to_next_step(self):
        """Check if the step is complete.

        :returns: True if new step may be enabled.
        :rtype: bool
        """
        # Still editing
        if self.mode == EDIT_MODE:
            return False
        for combo_box in self.exposure_combo_boxes:
            # Enable if there is one that has classification
            if combo_box.currentIndex() > 0:
                return True
        # Trick for EQ raster for population #3853
        if self.use_default_thresholds:
            return True
        return False

    def get_next_step(self):
        """Find the proper step when user clicks the Next button.

        :returns: The step to be switched to.
        :rtype: WizardStep instance or None
        """
        if self.layer_purpose != layer_purpose_aggregation:
            subcategory = self.parent.step_kw_subcategory.\
                selected_subcategory()
        else:
            subcategory = {'key': None}

        if is_raster_layer(self.parent.layer):
            return self.parent.step_kw_source

        # Check if it can go to inasafe field step
        inasafe_fields = get_non_compulsory_fields(
            self.layer_purpose['key'], subcategory['key'])

        if not skip_inasafe_field(self.parent.layer, inasafe_fields):
            return self.parent.step_kw_inasafe_fields

        # Check if it can go to inasafe default field step
        default_inasafe_fields = get_fields(
            self.layer_purpose['key'],
            subcategory['key'],
            replace_null=True,
            in_group=False
        )
        if default_inasafe_fields:
            return self.parent.step_kw_default_inasafe_fields

        # Any other case
        return self.parent.step_kw_source

    def set_wizard_step_description(self):
        """Set the text for description."""
        subcategory = self.parent.step_kw_subcategory.selected_subcategory()
        field = self.parent.step_kw_field.selected_fields()
        is_raster = is_raster_layer(self.parent.layer)

        if is_raster:
            if self.layer_mode == layer_mode_continuous:
                text_label = multiple_continuous_hazard_classifications_raster
            else:
                text_label = multiple_classified_hazard_classifications_raster
            # noinspection PyAugmentAssignment
            text_label = text_label % (
                subcategory['name'], self.layer_purpose['name'])
        else:
            if self.layer_mode == layer_mode_continuous:
                text_label = multiple_continuous_hazard_classifications_vector
            else:
                text_label = multiple_classified_hazard_classifications_vector
            # noinspection PyAugmentAssignment
            text_label = text_label % (
                subcategory['name'], self.layer_purpose['name'], field)

        self.multi_classifications_label.setText(text_label)

    def setup_left_panel(self):
        """Setup the UI for left panel.

        Generate all exposure, combobox, and edit button.
        """
        hazard = self.parent.step_kw_subcategory.selected_subcategory()
        left_panel_heading = QLabel(tr('Classifications'))
        left_panel_heading.setFont(big_font)
        self.left_layout.addWidget(left_panel_heading)

        inner_left_layout = QGridLayout()

        row = 0
        for exposure in exposure_all:
            special_case = False
            if not setting('developer_mode'):
                # Filter out unsupported exposure for the hazard
                if exposure in hazard['disabled_exposures']:
                    # Remove from the storage if the exposure is disabled
                    if self.layer_mode == layer_mode_continuous:
                        if exposure['key'] in self.thresholds:
                            self.thresholds.pop(exposure['key'])
                    else:
                        if exposure['key'] in self.value_maps:
                            self.value_maps.pop(exposure['key'])
                    continue
            # Trick for EQ raster for population #3853
            if exposure == exposure_population and hazard == hazard_earthquake:
                if is_raster_layer(self.parent.layer):
                    if self.layer_mode == layer_mode_continuous:
                        self.use_default_thresholds = True
                        special_case = True
                        # Set classification for EQ Raster for Population
                        self.thresholds[exposure_population['key']] = {
                            earthquake_mmi_scale['key']: {
                                'classes': default_classification_thresholds(
                                    earthquake_mmi_scale),
                                'active': True
                            }
                        }

            # Add label
            # Hazard on Exposure Classifications
            label = tr(
                '{hazard_name} on {exposure_name} Classifications').format(
                hazard_name=hazard['name'],
                exposure_name=exposure['name']
            )
            exposure_label = QLabel(label)

            # Add combo box
            exposure_combo_box = QComboBox()
            hazard_classifications = hazard.get('classifications')
            exposure_combo_box.addItem(tr('No classifications'))
            exposure_combo_box.setItemData(
                0, None, Qt.UserRole)

            current_index = 0
            i = 0
            # Iterate through all available hazard classifications
            for hazard_classification in hazard_classifications:
                # Skip if the classification is not for the exposure
                if 'exposures' in hazard_classification:
                    if exposure not in hazard_classification['exposures']:
                        continue
                exposure_combo_box.addItem(hazard_classification['name'])
                exposure_combo_box.setItemData(
                    i + 1, hazard_classification, Qt.UserRole)
                if self.layer_mode == layer_mode_continuous:
                    current_hazard_classifications = self.thresholds.get(
                        exposure['key'])
                else:
                    current_hazard_classifications = self.value_maps.get(
                        exposure['key'])
                if current_hazard_classifications:
                    current_hazard_classification = \
                        current_hazard_classifications.get(
                            hazard_classification['key'])
                    if current_hazard_classification:
                        is_active = current_hazard_classification.get('active')
                        if is_active:
                            current_index = i + 1
                i += 1
            # Set current classification
            exposure_combo_box.setCurrentIndex(current_index)

            # Add edit button
            exposure_edit_button = QPushButton(tr('Edit'))

            # For special case. Raster EQ on Population.
            if special_case:
                mmi_index = exposure_combo_box.findText(
                    earthquake_mmi_scale['name'])
                exposure_combo_box.setCurrentIndex(mmi_index)
                exposure_combo_box.setEnabled(False)
                exposure_edit_button.setEnabled(False)
                tool_tip_message = tr(
                    'InaSAFE use default classification for Raster Earthquake '
                    'hazard on population.')
                exposure_label.setToolTip(tool_tip_message)
                exposure_combo_box.setToolTip(tool_tip_message)
                exposure_edit_button.setToolTip(tool_tip_message)

            else:
                if current_index == 0:
                    # Disable if there is no classification chosen.
                    exposure_edit_button.setEnabled(False)
                exposure_edit_button.clicked.connect(
                    partial(
                        self.edit_button_clicked,
                        edit_button=exposure_edit_button,
                        exposure_combo_box=exposure_combo_box,
                        exposure=exposure))
                exposure_combo_box.currentIndexChanged.connect(
                    partial(
                        self.classifications_combo_box_changed,
                        exposure=exposure,
                        exposure_combo_box=exposure_combo_box,
                        edit_button=exposure_edit_button))

            # Arrange in layout
            inner_left_layout.addWidget(exposure_label, row, 0)
            inner_left_layout.addWidget(exposure_combo_box, row, 1)
            inner_left_layout.addWidget(exposure_edit_button, row, 2)

            # Adding to step's attribute
            self.exposures.append(exposure)
            self.exposure_combo_boxes.append(exposure_combo_box)
            self.exposure_edit_buttons.append(exposure_edit_button)
            self.exposure_labels.append(label)
            if special_case:
                self.special_case_index = len(self.exposures) - 1

            row += 1

        self.left_layout.addLayout(inner_left_layout)
        # To push the inner_left_layout up
        self.left_layout.addStretch(1)

    # noinspection PyUnusedLocal
    def edit_button_clicked(self, edit_button, exposure_combo_box, exposure):
        """Method to handle when an edit button is clicked.

        :param edit_button: The edit button.
        :type edit_button: QPushButton

        :param exposure_combo_box: The combo box of the exposure, contains
            list of classifications.
        :type exposure_combo_box: QComboBox

        :param exposure: Exposure definition.
        :type exposure: dict
        """
        # Note(IS): Do not change the text of edit button for now until we
        # have better behaviour.
        classification = self.get_classification(exposure_combo_box)

        if self.mode == CHOOSE_MODE:
            # Change mode
            self.mode = EDIT_MODE
            # Set active exposure
            self.active_exposure = exposure
            # Disable all edit button
            for exposure_edit_button in self.exposure_edit_buttons:
                exposure_edit_button.setEnabled(False)
            # Except one that was clicked
            # edit_button.setEnabled(True)
            # Disable all combo box
            for exposure_combo_box in self.exposure_combo_boxes:
                exposure_combo_box.setEnabled(False)
            # Change the edit button to cancel
            # edit_button.setText(tr('Cancel'))

            # Clear right panel
            clear_layout(self.right_layout)
            # Show edit threshold or value mapping
            if self.layer_mode == layer_mode_continuous:
                self.setup_thresholds_panel(classification)
            else:
                self.setup_value_mapping_panels(classification)
            self.add_buttons(classification)

        elif self.mode == EDIT_MODE:
            # Behave the same as cancel button clicked.
            self.cancel_button_clicked()

        self.parent.pbnNext.setEnabled(self.is_ready_to_next_step())

    def show_current_state(self):
        """Setup the UI for QTextEdit to show the current state."""
        right_panel_heading = QLabel(tr('Status'))
        right_panel_heading.setFont(big_font)
        right_panel_heading.setSizePolicy(
            QSizePolicy.Maximum, QSizePolicy.Maximum)
        self.right_layout.addWidget(right_panel_heading)

        message = m.Message()
        if self.layer_mode == layer_mode_continuous:
            title = tr('Thresholds')
        else:
            title = tr('Value maps')

        message.add(m.Heading(title, **INFO_STYLE))

        for i in range(len(self.exposures)):
            message.add(m.Text(self.exposure_labels[i]))

            classification = self.get_classification(
                self.exposure_combo_boxes[i])
            if self.layer_mode == layer_mode_continuous:
                thresholds = self.thresholds.get(self.exposures[i]['key'])
                if not thresholds or not classification:
                    message.add(m.Paragraph(tr('No classifications set.')))
                    continue
                table = m.Table(
                    style_class='table table-condensed table-striped')
                header = m.Row()
                header.add(m.Cell(tr('Class name')))
                header.add(m.Cell(tr('Minimum')))
                header.add(m.Cell(tr('Maximum')))
                table.add(header)
                classes = classification.get('classes')
                # Sort by value, put the lowest first
                classes = sorted(classes, key=lambda k: k['value'])
                for the_class in classes:
                    threshold = thresholds[classification['key']]['classes'][
                        the_class['key']]
                    row = m.Row()
                    row.add(m.Cell(the_class['name']))
                    row.add(m.Cell(threshold[0]))
                    row.add(m.Cell(threshold[1]))
                    table.add(row)
            else:
                value_maps = self.value_maps.get(self.exposures[i]['key'])
                if not value_maps or not classification:
                    message.add(m.Paragraph(tr('No classifications set.')))
                    continue
                table = m.Table(
                    style_class='table table-condensed table-striped')
                header = m.Row()
                header.add(m.Cell(tr('Class name')))
                header.add(m.Cell(tr('Value')))
                table.add(header)
                classes = classification.get('classes')
                # Sort by value, put the lowest first
                classes = sorted(classes, key=lambda k: k['value'])
                for the_class in classes:
                    value_map = value_maps[classification['key']][
                        'classes'].get(the_class['key'], [])
                    row = m.Row()
                    row.add(m.Cell(the_class['name']))
                    row.add(m.Cell(', '.join([str(v) for v in value_map])))
                    table.add(row)
            message.add(table)

        # status_text_edit = QTextBrowser(None)
        status_text_edit = QWebView(None)
        status_text_edit.setSizePolicy(
            QSizePolicy.Ignored,
            QSizePolicy.Ignored)

        status_text_edit.page().mainFrame().setScrollBarPolicy(
            Qt.Horizontal,
            Qt.ScrollBarAlwaysOff)
        html_string = html_header() + message.to_html() + html_footer()
        status_text_edit.setHtml(html_string)
        self.right_layout.addWidget(status_text_edit)

    def set_widgets(self):
        """Set widgets on the Multi classification step."""
        self.clear()
        self.layer_mode = self.parent.step_kw_layermode.selected_layermode()
        self.layer_purpose = self.parent.step_kw_purpose.selected_purpose()
        self.set_current_state()

        # Set the step description
        self.set_wizard_step_description()

        # Set the left panel
        self.setup_left_panel()

        # Set the right panel, for the beginning show the viewer
        self.show_current_state()

    def clear(self):
        """Clear current state."""
        self.exposures = []
        self.exposure_labels = []
        self.exposure_combo_boxes = []
        self.exposure_edit_buttons = []
        self.mode = CHOOSE_MODE

        self.layer_purpose = None
        self.layer_mode = None
        self.special_case_index = None

        self.value_maps = {}
        self.thresholds = {}

        # Temporary attributes
        self.threshold_classes = OrderedDict()
        self.active_exposure = None

        self.list_unique_values = None
        self.tree_mapping_widget = None

        clear_layout(self.left_layout)
        clear_layout(self.right_layout)

    def get_current_state(self):
        """Obtain current classification and value map / threshold."""
        def clean_state(dictionary):
            """Clean dictionary from bad value.

            :param dictionary: Dictionary of value maps or thresholds.
            :type dictionary: dict

            :returns: Clean state.
            :rtype: dict
            """
            clean_dictionary = {
                k: v for k, v in list(dictionary.items())
                if isinstance(v, dict)}

            return clean_dictionary

        if self.layer_mode == layer_mode_continuous:
            output = {'thresholds': clean_state(self.thresholds)}
            key = 'thresholds'
        else:
            output = {'value_maps': clean_state(self.value_maps)}
            key = 'value_maps'

        # Clean non existing hazard class key
        empty_exposure_classifications = []
        for the_exposure, the_hazard_classifications in list(
                output[key].items()):
            for the_hazard_classification in list(the_hazard_classifications.
                                                  keys()):
                invalid_classifications = []
                if not definition(the_hazard_classification):
                    invalid_classifications.append(
                        the_hazard_classification)
                for invalid_classification in invalid_classifications:
                    the_hazard_classifications.pop(invalid_classification)
            if not the_hazard_classifications:
                empty_exposure_classifications.append(the_exposure)

        for empty_exposure_classification in empty_exposure_classifications:
            output[key].pop(empty_exposure_classification)

        return output

    @staticmethod
    def get_classification(combo_box):
        """Helper to obtain the classification from a combo box.

        :param combo_box: A classification combo box.
        :type combo_box: QComboBox.

        :returns: Classification definitions.
        :rtype: dict
        """
        return combo_box.itemData(combo_box.currentIndex(), Qt.UserRole)

    def setup_thresholds_panel(self, classification):
        """Setup threshold panel in the right panel.

        :param classification: Classification definition.
        :type classification: dict
        """
        # Set text in the label
        layer_purpose = self.parent.step_kw_purpose.selected_purpose()
        layer_subcategory = self.parent.step_kw_subcategory.\
            selected_subcategory()

        if is_raster_layer(self.parent.layer):
            active_band = self.parent.step_kw_band_selector.selected_band()
            layer_extent = self.parent.layer.extent()
            statistics = self.parent.layer.dataProvider().bandStatistics(
                active_band, QgsRasterBandStats.All, layer_extent, 0)
            description_text = continuous_raster_question % (
                layer_purpose['name'],
                layer_subcategory['name'],
                classification['name'],
                statistics.minimumValue,
                statistics.maximumValue)
        else:
            field_name = self.parent.step_kw_field.selected_fields()
            field_index = self.parent.layer.fields().lookupField(field_name)
            min_value_layer = self.parent.layer.minimumValue(field_index)
            max_value_layer = self.parent.layer.maximumValue(field_index)
            description_text = continuous_vector_question % (
                layer_purpose['name'],
                layer_subcategory['name'],
                field_name,
                classification['name'],
                min_value_layer,
                max_value_layer)

        # Set description
        description_label = QLabel(description_text)
        description_label.setWordWrap(True)
        self.right_layout.addWidget(description_label)

        if self.thresholds:
            thresholds = self.thresholds
        else:
            thresholds = self.parent.get_existing_keyword('thresholds')
        selected_unit = self.parent.step_kw_unit.selected_unit()['key']

        self.threshold_classes = OrderedDict()
        classes = classification.get('classes')
        # Sort by value, put the lowest first
        classes = sorted(classes, key=lambda the_key: the_key['value'])

        grid_layout_thresholds = QGridLayout()

        for i, the_class in enumerate(classes):
            class_layout = QHBoxLayout()

            # Class label
            class_label = QLabel(the_class['name'])

            # Min label
            min_label = QLabel(tr('Min >'))

            # Min value as double spin
            min_value_input = QDoubleSpinBox()
            # TODO(IS) We can set the min and max depends on the unit, later
            min_value_input.setMinimum(0)
            min_value_input.setMaximum(999999)

            if thresholds.get(self.active_exposure['key']):
                exposure_thresholds = thresholds.get(
                    self.active_exposure['key'])
                if exposure_thresholds.get(classification['key']):
                    exposure_thresholds_classifications = exposure_thresholds\
                        .get(classification['key'])
                    min_value_input.setValue(
                        exposure_thresholds_classifications['classes'][
                            the_class['key']][0])
                else:
                    default_min = the_class['numeric_default_min']
                    if isinstance(default_min, dict):
                        default_min = the_class[
                            'numeric_default_min'][selected_unit]
                    min_value_input.setValue(default_min)
            else:
                default_min = the_class['numeric_default_min']
                if isinstance(default_min, dict):
                    default_min = the_class[
                        'numeric_default_min'][selected_unit]
                min_value_input.setValue(default_min)
            min_value_input.setSingleStep(0.1)

            # Max label
            max_label = QLabel(tr('Max <='))

            # Max value as double spin
            max_value_input = QDoubleSpinBox()
            # TODO(IS) We can set the min and max depends on the unit, later
            max_value_input.setMinimum(0)
            max_value_input.setMaximum(999999)
            if thresholds.get(self.active_exposure['key']):
                exposure_thresholds = thresholds.get(
                    self.active_exposure['key'])
                if exposure_thresholds.get(classification['key']):
                    exposure_thresholds_classifications = exposure_thresholds \
                        .get(classification['key'])
                    max_value_input.setValue(
                        exposure_thresholds_classifications['classes'][
                            the_class['key']][1])
                else:
                    default_max = the_class['numeric_default_max']
                    if isinstance(default_max, dict):
                        default_max = the_class[
                            'numeric_default_max'][selected_unit]
                    max_value_input.setValue(default_max)
            else:
                default_max = the_class['numeric_default_max']
                if isinstance(default_max, dict):
                    default_max = the_class[
                        'numeric_default_max'][selected_unit]
                max_value_input.setValue(default_max)
            max_value_input.setSingleStep(0.1)

            # Add to class_layout
            class_layout.addWidget(min_label)
            class_layout.addWidget(min_value_input)
            class_layout.addWidget(max_label)
            class_layout.addWidget(max_value_input)

            class_layout.setStretch(0, 1)
            class_layout.setStretch(1, 2)
            class_layout.setStretch(2, 1)
            class_layout.setStretch(3, 2)

            # Add to grid_layout
            grid_layout_thresholds.addWidget(class_label, i, 0)
            grid_layout_thresholds.addLayout(class_layout, i, 1)

            self.threshold_classes[the_class['key']] = [
                min_value_input, max_value_input]

        grid_layout_thresholds.setColumnStretch(0, 1)
        grid_layout_thresholds.setColumnStretch(0, 2)

        def min_max_changed(double_spin_index, mode):
            """Slot when min or max value change.

            :param double_spin_index: The index of the double spin.
            :type double_spin_index: int

            :param mode: The flag to indicate the min or max value.
            :type mode: int
            """
            if mode == MAX_VALUE_MODE:
                current_max_value = list(self.threshold_classes.values())[
                    double_spin_index][1]
                target_min_value = list(self.threshold_classes.values())[
                    double_spin_index + 1][0]
                if current_max_value.value() != target_min_value.value():
                    target_min_value.setValue(current_max_value.value())
            elif mode == MIN_VALUE_MODE:
                current_min_value = list(self.threshold_classes.values())[
                    double_spin_index][0]
                target_max_value = list(self.threshold_classes.values())[
                    double_spin_index - 1][1]
                if current_min_value.value() != target_max_value.value():
                    target_max_value.setValue(current_min_value.value())

        # Set behaviour
        for k, v in list(self.threshold_classes.items()):
            index = list(self.threshold_classes.keys()).index(k)
            if index < len(self.threshold_classes) - 1:
                # Max value changed
                v[1].valueChanged.connect(partial(
                    min_max_changed,
                    double_spin_index=index,
                    mode=MAX_VALUE_MODE))
            if index > 0:
                # Min value
                v[0].valueChanged.connect(partial(
                    min_max_changed,
                    double_spin_index=index,
                    mode=MIN_VALUE_MODE))

        grid_layout_thresholds.setSpacing(0)

        self.right_layout.addLayout(grid_layout_thresholds)

    def add_buttons(self, classification):
        """Helper to setup 3 buttons.

        :param classification: The current classification.
        :type classification: dict
        """
        # Note(IS): Until we have good behaviour, we will disable cancel
        # button.
        # Add 3 buttons: Restore default, Cancel, Save

        # Restore default button, only for continuous layer (with threshold)
        if self.layer_mode == layer_mode_continuous['key']:
            self.restore_default_button = QPushButton(tr('Restore Default'))
            self.restore_default_button.clicked.connect(partial(
                self.restore_default_button_clicked,
                classification=classification))

        # Cancel button
        # cancel_button = QPushButton(tr('Cancel'))
        # cancel_button.clicked.connect(self.cancel_button_clicked)

        # Save button
        self.save_button = QPushButton(tr('Save'))
        self.save_button.clicked.connect(
            partial(self.save_button_clicked, classification=classification))

        button_layout = QHBoxLayout()
        button_layout.addStretch(1)
        button_layout.addWidget(self.restore_default_button)
        button_layout.addWidget(self.save_button)

        button_layout.setStretch(0, 3)
        button_layout.setStretch(1, 1)
        button_layout.setStretch(2, 1)
        # button_layout.setStretch(3, 1)

        self.right_layout.addLayout(button_layout)

    def setup_value_mapping_panels(self, classification):
        """Setup value mapping panel in the right panel.

        :param classification: Classification definition.
        :type classification: dict
        """
        # Set text in the label
        layer_purpose = self.parent.step_kw_purpose.selected_purpose()
        layer_subcategory = self.parent.step_kw_subcategory. \
            selected_subcategory()

        if is_raster_layer(self.parent.layer):
            description_text = classify_raster_question % (
                layer_subcategory['name'],
                layer_purpose['name'],
                classification['name'])

            dataset = gdal.Open(self.parent.layer.source(), GA_ReadOnly)
            active_band = self.parent.step_kw_band_selector.selected_band()
            unique_values = numpy.unique(numpy.array(
                dataset.GetRasterBand(active_band).ReadAsArray()))
            field_type = 0
            # Convert datatype to a json serializable type
            if numpy.issubdtype(unique_values.dtype, float):
                unique_values = [float(i) for i in unique_values]
            else:
                unique_values = [int(i) for i in unique_values]
        else:
            field = self.parent.step_kw_field.selected_fields()
            field_index = self.parent.layer.fields().indexFromName(field)
            field_type = self.parent.layer.fields()[field_index].type()
            description_text = classify_vector_question % (
                layer_subcategory['name'],
                layer_purpose['name'],
                classification['name'],
                field.upper())
            unique_values = list(self.parent.layer.uniqueValues(field_index))

        # Set description
        description_label = QLabel(description_text)
        description_label.setWordWrap(True)
        self.right_layout.addWidget(description_label)

        self.list_unique_values = QListWidget()
        self.list_unique_values.setDragDropMode(QAbstractItemView.DragDrop)
        self.list_unique_values.setDefaultDropAction(Qt.MoveAction)

        self.tree_mapping_widget = QTreeWidget()
        self.tree_mapping_widget.setDragDropMode(QAbstractItemView.DragDrop)
        self.tree_mapping_widget.setDefaultDropAction(Qt.MoveAction)
        self.tree_mapping_widget.header().hide()

        self.tree_mapping_widget.itemChanged.connect(
            self.update_dragged_item_flags)

        value_mapping_layout = QHBoxLayout()
        value_mapping_layout.addWidget(self.list_unique_values)
        value_mapping_layout.addWidget(self.tree_mapping_widget)

        self.right_layout.addLayout(value_mapping_layout)

        default_classes = classification['classes']

        # Assign unique values to classes (according to default)
        unassigned_values = list()
        assigned_values = dict()
        for default_class in default_classes:
            assigned_values[default_class['key']] = list()
        for unique_value in unique_values:
            if (unique_value is None
                    or (hasattr(unique_value, 'isNull')
                        and unique_value.isNull())):
                # Don't classify features with NULL value
                continue
            # Capitalization of the value and removing '_' (raw OSM data).
            value_as_string = str(unique_value).upper().replace('_', ' ')
            assigned = False
            for default_class in default_classes:
                if 'string_defaults' in default_class:
                    # To make it case insensitive
                    upper_string_defaults = [
                        c.upper() for c in default_class['string_defaults']]
                    in_string_default = (
                        value_as_string in upper_string_defaults)
                    condition_1 = field_type > 9 and in_string_default
                else:
                    condition_1 = False
                condition_2 = (
                    field_type < 10
                    and 'numeric_default_min' in default_class
                    and 'numeric_default_max' in default_class
                    and (default_class['numeric_default_min']
                         <= unique_value
                         < default_class['numeric_default_max']))
                if condition_1 or condition_2:
                    assigned_values[default_class['key']] += [unique_value]
                    assigned = True
                    break
            if not assigned:
                # add to unassigned values list otherwise
                unassigned_values += [unique_value]
        self.populate_classified_values(
            unassigned_values,
            assigned_values,
            default_classes,
            self.list_unique_values,
            self.tree_mapping_widget
        )

        # Current value map for exposure and classification
        available_classifications = self.value_maps.get(
            self.active_exposure['key'])
        if not available_classifications:
            return
        # Get active one
        current_classification = available_classifications.get(
            classification['key'])
        if not current_classification:
            return
        # Should come from metadata
        current_value_map = current_classification.get('classes')
        if not current_value_map:
            return

        unassigned_values = list()
        assigned_values = dict()
        for default_class in default_classes:
            assigned_values[default_class['key']] = list()
        for unique_value in unique_values:
            if (unique_value is None
                    or (hasattr(unique_value, 'isNull')
                        and unique_value.isNull())):
                # Don't classify features with NULL value
                continue
            # check in value map
            assigned = False
            for key, value_list in list(current_value_map.items()):
                if unique_value in value_list and key in assigned_values:
                    assigned_values[key] += [unique_value]
                    assigned = True
            if not assigned:
                unassigned_values += [unique_value]
        self.populate_classified_values(
            unassigned_values,
            assigned_values,
            default_classes,
            self.list_unique_values,
            self.tree_mapping_widget
        )

    # noinspection PyMethodMayBeStatic
    def update_dragged_item_flags(self, item):
        """Fix the drop flag after the item is dropped.

        Check if it looks like an item dragged from QListWidget
        to QTreeWidget and disable the drop flag.
        For some reasons the flag is set when dragging.

        :param item: Item which is dragged.
        :type item: QTreeWidgetItem

        .. note:: This is a slot executed when the item change.
        """
        if int(item.flags() & Qt.ItemIsDropEnabled) \
                and int(item.flags() & Qt.ItemIsDragEnabled):
            item.setFlags(item.flags() & ~Qt.ItemIsDropEnabled)

    @staticmethod
    def populate_classified_values(
            unassigned_values, assigned_values, default_classes,
            list_unique_values, tree_mapping_widget):
        """Populate lstUniqueValues and treeClasses.from the parameters.

        :param unassigned_values: List of values that haven't been assigned
            to a class. It will be put in list_unique_values.
        :type unassigned_values: list

        :param assigned_values: Dictionary with class as the key and list of
            value as the value of the dictionary. It will be put in
            tree_mapping_widget.
        :type assigned_values: dict

        :param default_classes: Default classes from unit.
        :type default_classes: list

        :param list_unique_values: List Widget for unique values
        :type list_unique_values: QListWidget

        :param tree_mapping_widget: Tree Widget for classifying.
        :type tree_mapping_widget: QTreeWidget
        """
        # Populate the unique values list
        list_unique_values.clear()
        list_unique_values.setSelectionMode(
            QAbstractItemView.ExtendedSelection)
        for value in unassigned_values:
            value_as_string = value is not None and str(value) or 'NULL'
            list_item = QListWidgetItem(list_unique_values)
            list_item.setFlags(
                Qt.ItemIsEnabled
                | Qt.ItemIsSelectable
                | Qt.ItemIsDragEnabled)
            list_item.setData(Qt.UserRole, value)
            list_item.setText(value_as_string)
            list_unique_values.addItem(list_item)
        # Populate assigned values tree
        tree_mapping_widget.clear()
        bold_font = QFont()
        bold_font.setItalic(True)
        bold_font.setBold(True)
        bold_font.setWeight(75)
        tree_mapping_widget.invisibleRootItem().setFlags(
            Qt.ItemIsEnabled)
        for default_class in default_classes:
            # Create branch for class
            tree_branch = QTreeWidgetItem(tree_mapping_widget)
            tree_branch.setFlags(
                Qt.ItemIsDropEnabled | Qt.ItemIsEnabled)
            tree_branch.setExpanded(True)
            tree_branch.setFont(0, bold_font)
            if 'name' in default_class:
                default_class_name = default_class['name']
            else:
                default_class_name = default_class['key']
            tree_branch.setText(0, default_class_name)
            tree_branch.setData(0, Qt.UserRole, default_class['key'])
            if 'description' in default_class:
                tree_branch.setToolTip(0, default_class['description'])
            # Assign known values
            for value in assigned_values[default_class['key']]:
                string_value = value is not None and str(value) or 'NULL'
                tree_leaf = QTreeWidgetItem(tree_branch)
                tree_leaf.setFlags(
                    Qt.ItemIsEnabled
                    | Qt.ItemIsSelectable
                    | Qt.ItemIsDragEnabled)
                tree_leaf.setData(0, Qt.UserRole, value)
                tree_leaf.setText(0, string_value)

    def cancel_button_clicked(self):
        """Action for cancel button clicked."""
        # Change mode
        self.mode = CHOOSE_MODE
        # Enable all edit buttons and combo boxes
        for i in range(len(self.exposures)):
            if i == self.special_case_index:
                self.exposure_edit_buttons[i].setEnabled(False)
                self.exposure_combo_boxes[i].setEnabled(False)
                continue
            if self.get_classification(self.exposure_combo_boxes[i]):
                self.exposure_edit_buttons[i].setEnabled(True)
            else:
                self.exposure_edit_buttons[i].setEnabled(False)
            # self.exposure_edit_buttons[i].setText(tr('Edit'))
            self.exposure_combo_boxes[i].setEnabled(True)

        # Clear right panel
        clear_layout(self.right_layout)
        # Show current state
        self.show_current_state()
        # Unset active exposure
        self.active_exposure = None

        self.parent.pbnNext.setEnabled(self.is_ready_to_next_step())

    def save_button_clicked(self, classification):
        """Action for save button clicked.

        :param classification: The classification that being edited.
        :type classification: dict
        """
        # Save current edit
        if self.layer_mode == layer_mode_continuous:
            thresholds = self.get_threshold()
            classification_class = {
                'classes': thresholds,
                'active': True
            }
            if self.thresholds.get(self.active_exposure['key']):
                # Set other class to not active
                for current_classification in list(self.thresholds.get(
                        self.active_exposure['key']).values()):
                    current_classification['active'] = False
            else:
                self.thresholds[self.active_exposure['key']] = {}

            self.thresholds[self.active_exposure['key']][
                classification['key']] = classification_class
        else:
            value_maps = self.get_value_map()
            classification_class = {
                'classes': value_maps,
                'active': True
            }
            if self.value_maps.get(self.active_exposure['key']):
                # Set other class to not active
                for current_classification in list(self.value_maps.get(
                        self.active_exposure['key']).values()):
                    current_classification['active'] = False
            else:
                self.value_maps[self.active_exposure['key']] = {}

            self.value_maps[self.active_exposure['key']][
                classification['key']] = classification_class
        # Back to choose mode
        self.cancel_button_clicked()

    def restore_default_button_clicked(self, classification):
        """Action for restore default button clicked.

        It will set the threshold with default value.

        :param classification: The classification that being edited.
        :type classification: dict
        """
        # Obtain default value
        class_dict = {}
        for the_class in classification.get('classes'):
            class_dict[the_class['key']] = {
                'numeric_default_min': the_class['numeric_default_min'],
                'numeric_default_max': the_class['numeric_default_max'],
            }
        # Set for all threshold
        for key, value in list(self.threshold_classes.items()):
            value[0].setValue(class_dict[key]['numeric_default_min'])
            value[1].setValue(class_dict[key]['numeric_default_max'])

    def get_threshold(self):
        """Return threshold based on current state."""
        value_map = dict()
        for key, value in list(self.threshold_classes.items()):
            value_map[key] = [
                value[0].value(),
                value[1].value(),
            ]
        return value_map

    def get_value_map(self):
        """Obtain the value-to-class mapping set by user.

        :returns: The complete mapping as a dict of lists.
        :rtype: dict
        """
        value_map = {}
        tree_clone = self.tree_mapping_widget.invisibleRootItem().clone()
        for tree_branch in tree_clone.takeChildren():
            value_list = []
            for tree_leaf in tree_branch.takeChildren():
                value_list += [tree_leaf.data(0, Qt.UserRole)]
            if value_list:
                value_map[tree_branch.data(0, Qt.UserRole)] = value_list
        return value_map

    def set_current_state(self):
        """"Helper to set the state of the step from current keywords."""
        if not self.thresholds:
            self.thresholds = self.parent.get_existing_keyword('thresholds')
        if not self.value_maps:
            self.value_maps = self.parent.get_existing_keyword('value_maps')

    def classifications_combo_box_changed(
            self, index, exposure, exposure_combo_box, edit_button):
        """Action when classification combo box changed.

        :param index: The index of the combo box.
        :type index: int

        :param exposure: The exposure associated with the combo box.
        :type exposure: dict

        :param exposure_combo_box: Combo box for the classification.
        :type exposure_combo_box: QComboBox

        :param edit_button: The edit button associate with combo box.
        :type edit_button: QPushButton
        """
        # Disable button if it's no classification
        edit_button.setEnabled(bool(index))

        classification = self.get_classification(exposure_combo_box)
        self.activate_classification(exposure, classification)
        clear_layout(self.right_layout)
        self.show_current_state()

        self.parent.pbnNext.setEnabled(self.is_ready_to_next_step())

        # Open edit panel directly
        edit_button.click()

    def activate_classification(self, exposure, classification=None):
        """Set active to True for classification for the exposure.

        If classification = None, all classification set active = False.

        :param exposure: Exposure definition.
        :type exposure: dict

        :param classification: Classification definition.
        :type classification: dict
        """
        if self.layer_mode == layer_mode_continuous:
            selected_unit = self.parent.step_kw_unit.selected_unit()['key']
            target = self.thresholds.get(exposure['key'])
            if target is None:
                self.thresholds[exposure['key']] = {}
            target = self.thresholds.get(exposure['key'])
        else:
            selected_unit = None
            target = self.value_maps.get(exposure['key'])
            if target is None:
                self.value_maps[exposure['key']] = {}
            target = self.value_maps.get(exposure['key'])

        if classification is not None:
            if classification['key'] not in target:
                if self.layer_mode == layer_mode_continuous:
                    default_classes = default_classification_thresholds(
                        classification, selected_unit)
                    target[classification['key']] = {
                        'classes': default_classes,
                        'active': True
                    }
                else:
                    # Set classes to empty, since we haven't mapped anything
                    target[classification['key']] = {
                        'classes': {},
                        'active': True
                    }
                return

        for classification_key, value in list(target.items()):
            if classification is None:
                value['active'] = False
                continue

            if classification_key == classification['key']:
                value['active'] = True
            else:
                value['active'] = False

    @property
    def step_name(self):
        """Get the human friendly name for the wizard step.

        :returns: The name of the wizard step.
        :rtype: str
        """
        return tr('Multi Classification Step')

    def help_content(self):
        """Return the content of help for this step wizard.

            We only needs to re-implement this method in each wizard step.

        :returns: A message object contains help.
        :rtype: m.Message
        """
        message = m.Message()
        message.add(m.Paragraph(tr(
            'In this wizard step: {step_name}, you will be able to set the '
            'classification that you will use per exposure type. You can also '
            'set the threshold or value map for each classification.'
        ).format(step_name=self.step_name)))
        return message