示例#1
0
class ItemWidgetBase(QFrame):

    checkedStateChanged = pyqtSignal()
    thumbnailChanged = pyqtSignal()

    def __init__(self, item):
        QFrame.__init__(self)
        self.item = item
        self.is_updating_checkbox = False
        self.setMouseTracking(True)
        self.setStyleSheet("ItemWidgetBase{border: 2px solid transparent;}")

    def _setup_ui(self, text, thumbnailurl):

        self.lockLabel = QLabel()
        iconSize = QSize(16, 16)
        self.lockLabel.setPixmap(LOCK_ICON.pixmap(iconSize))
        self.checkBox = QCheckBox("")
        self.checkBox.stateChanged.connect(self.check_box_state_changed)
        self.nameLabel = QLabel(text)
        self.iconLabel = QLabel()
        self.labelZoomTo = QLabel()
        self.labelZoomTo.setPixmap(ZOOMTO_ICON.pixmap(QSize(18, 18)))
        self.labelZoomTo.setToolTip("Zoom to extent")
        self.labelZoomTo.mousePressEvent = self.zoom_to_extent
        self.labelAddPreview = QLabel()
        self.labelAddPreview.setPixmap(ADD_PREVIEW_ICON.pixmap(QSize(18, 18)))
        self.labelAddPreview.setToolTip("Add preview layer to map")
        self.labelAddPreview.mousePressEvent = self._add_preview_clicked

        layout = QHBoxLayout()
        layout.setMargin(0)
        layout.addWidget(self.checkBox)
        layout.addWidget(self.lockLabel)
        pixmap = QPixmap(PLACEHOLDER_THUMB, 'SVG')
        self.thumbnail = None
        thumb = pixmap.scaled(48, 48, Qt.KeepAspectRatio,
                              Qt.SmoothTransformation)
        self.iconLabel.setPixmap(thumb)
        self.iconLabel.setFixedSize(48, 48)
        layout.addWidget(self.iconLabel)
        if thumbnailurl is not None:
            download_thumbnail(thumbnailurl, self)
        layout.addWidget(self.nameLabel)
        layout.addStretch()
        layout.addWidget(self.labelZoomTo)
        layout.addWidget(self.labelAddPreview)
        layout.addSpacing(10)
        self.setLayout(layout)

        self.footprint = QgsRubberBand(iface.mapCanvas(),
                                       QgsWkbTypes.PolygonGeometry)
        self.footprint.setStrokeColor(PLANET_COLOR)
        self.footprint.setWidth(2)

    def set_thumbnail(self, img):
        self.thumbnail = QPixmap(img)
        thumb = self.thumbnail.scaled(48, 48, Qt.KeepAspectRatio,
                                      Qt.SmoothTransformation)
        self.iconLabel.setPixmap(thumb)
        self.thumbnailChanged.emit()

    def is_selected(self):
        return self.checkBox.isChecked()

    def _geom_bbox_in_project_crs(self):
        transform = QgsCoordinateTransform(
            QgsCoordinateReferenceSystem("EPSG:4326"),
            QgsProject.instance().crs(), QgsProject.instance())
        return transform.transformBoundingBox(self.geom.boundingBox())

    def _geom_in_project_crs(self):
        transform = QgsCoordinateTransform(
            QgsCoordinateReferenceSystem("EPSG:4326"),
            QgsProject.instance().crs(), QgsProject.instance())
        geom = QgsGeometry(self.geom)
        geom.transform(transform)
        return geom

    def show_footprint(self):
        self.footprint.setToGeometry(self._geom_in_project_crs())

    def hide_footprint(self):
        self.footprint.reset(QgsWkbTypes.PolygonGeometry)

    def enterEvent(self, event):
        self.setStyleSheet(
            "ItemWidgetBase{border: 2px solid rgb(0, 157, 165);}")
        self.show_footprint()

    def leaveEvent(self, event):
        self.setStyleSheet("ItemWidgetBase{border: 2px solid transparent;}")
        self.hide_footprint()

    def zoom_to_extent(self, evt):
        rect = QgsRectangle(self._geom_bbox_in_project_crs())
        rect.scale(1.05)
        iface.mapCanvas().setExtent(rect)
        iface.mapCanvas().refresh()

    def _add_preview_clicked(self, evt):
        self.add_preview()

    @waitcursor
    def add_preview(self):
        send_analytics_for_preview(self.item.images())
        create_preview_group(self.name(), self.item.images())

    def check_box_state_changed(self):
        self.checkedStateChanged.emit()
        self.is_updating_checkbox = True
        total = self.item.childCount()
        if self.checkBox.isTristate():
            self.checkBox.setTristate(False)
            self.checkBox.setChecked(False)
        else:
            for i in range(total):
                w = self.item.treeWidget().itemWidget(self.item.child(i), 0)
                w.set_checked(self.checkBox.isChecked())
        self.is_updating_checkbox = False

    def update_checkbox(self):
        if self.is_updating_checkbox:
            return
        selected = 0
        total = self.item.childCount()
        for i in range(total):
            w = self.item.treeWidget().itemWidget(self.item.child(i), 0)
            if w.is_selected():
                selected += 1
        self.checkBox.blockSignals(True)
        if selected == total:
            self.checkBox.setTristate(False)
            self.checkBox.setCheckState(Qt.Checked)
        elif selected == 0:
            self.checkBox.setTristate(False)
            self.checkBox.setCheckState(Qt.Unchecked)
        else:
            self.checkBox.setTristate(True)
            self.checkBox.setCheckState(Qt.PartiallyChecked)
        self.checkBox.blockSignals(False)
        self.checkedStateChanged.emit()

    def set_checked(self, checked):
        self.checkBox.setChecked(checked)

    def update_thumbnail(self):
        thumbnails = self.scene_thumbnails()
        if thumbnails and None not in thumbnails:
            bboxes = [img[GEOMETRY] for img in self.item.images()]
            pixmap = createCompoundThumbnail(bboxes, thumbnails)
            thumb = pixmap.scaled(48, 48, Qt.KeepAspectRatio,
                                  Qt.SmoothTransformation)
            self.iconLabel.setPixmap(thumb)
            self.thumbnailChanged.emit()

    def scene_thumbnails(self):
        thumbnails = []
        try:
            for i in range(self.item.childCount()):
                w = self.item.treeWidget().itemWidget(self.item.child(i), 0)
                thumbnails.extend(w.scene_thumbnails())
        except RuntimeError:
            # item might not exist anymore. In this case, we just return
            # an empty list
            pass
        return thumbnails
示例#2
0
class PoleRow(object):
    """
    Creates all input fields necessary to change the properties of a pole in
    the cable layout. The layout is identified by the position (index) it has
    in the vertical layout.
    """
    ICON_ADD_ROW = ":/plugins/SeilaplanPlugin/gui/icons/icon_addrow.png"
    ICON_DEL_ROW = ":/plugins/SeilaplanPlugin/gui/icons/icon_bin.png"

    def __init__(self,
                 parent,
                 widget,
                 layout,
                 idx,
                 nr,
                 name,
                 rowType,
                 dist,
                 distRange,
                 height=False,
                 angle=False,
                 delBtn=False,
                 addBtn=False):
        self.parent = parent
        self.widget = widget
        self.layout = layout
        self.index = idx
        self.rowType = rowType
        self.parent.poleCount += 1

        self.row = QHBoxLayout()
        self.row.setAlignment(Qt.AlignLeft)

        self.labelNr = None
        self.statusSwitcher = None
        self.fieldName = None
        self.fieldDist = None
        self.fieldHeight = None
        self.fieldAngle = None
        self.addBtn = None
        self.delBtn = None

        self.addRowToLayout()
        self.addBtnPlus(addBtn)
        if self.rowType == 'anchor':
            self.addSwitcher()
        else:
            self.addLabelNr(nr)
        self.addFieldName(name)
        self.addFieldDist(dist, distRange)
        if self.rowType not in ['anchor']:
            self.addFieldHeight(height)
            self.addFieldAngle(angle)
        self.addBtnDel(delBtn)

    def addRowToLayout(self):
        if self.index == self.parent.poleCount:
            # Add layout at the end
            self.layout.addLayout(self.row)
        else:
            # Insert new row between existing ones
            self.layout.insertLayout(self.index + 1, self.row)

    def addSwitcher(self):
        self.statusSwitcher = QCheckBox(self.widget)
        self.statusSwitcher.setText('')
        self.statusSwitcher.setFixedWidth(20)
        self.statusSwitcher.setChecked(True)
        self.row.addWidget(self.statusSwitcher)

        # Connect events
        self.statusSwitcher.stateChanged.connect(
            lambda newVal: self.parent.onRowChange(newVal == 2, self.index,
                                                   'active'))

    def addLabelNr(self, nr):
        self.labelNr = QLabel(self.widget)
        self.labelNr.setFixedWidth(20)
        self.labelNr.setAlignment(Qt.AlignVCenter | Qt.AlignRight)
        self.row.addWidget(self.labelNr)
        if nr:
            self.labelNr.setText(f"{nr}:")

    def updateIndex(self, idx):
        self.index = idx

    def updateLabelNr(self, label):
        if self.labelNr:
            if label:
                self.labelNr.setText(f"{label}:")
            else:
                self.labelNr.setText("")

    def addFieldName(self, value):
        self.fieldName = QLineEditWithFocus(self.widget)
        self.fieldName.setFocusPolicy(Qt.ClickFocus)
        self.fieldName.setFixedWidth(200)
        self.fieldName.setText(value)
        self.row.addWidget(self.fieldName)

        # Connect events
        self.fieldName.inFocus.connect(
            lambda x: self.parent.zoomIn(self.index))
        self.fieldName.outFocus.connect(self.parent.zoomOut)
        self.fieldName.textChanged.connect(
            lambda newVal: self.parent.onRowChange(newVal, self.index, 'name'))

    def addFieldDist(self, value, distRange):
        self.fieldDist = QDoubleSpinBoxWithFocus(self.widget)
        self.fieldDist.setFocusPolicy(Qt.ClickFocus)
        self.fieldDist.setDecimals(0)
        self.fieldDist.setSingleStep(self.parent.pole_dist_step)
        self.fieldDist.setSuffix(" m")
        self.fieldDist.setFixedWidth(95)
        self.fieldDist.setRange(float(distRange[0]), float(distRange[1]))
        self.fieldDist.setValue(float(value))
        self.row.addWidget(self.fieldDist)

        # Connect events
        self.fieldDist.inFocus.connect(
            lambda x: self.parent.zoomIn(self.index))
        self.fieldDist.outFocus.connect(self.parent.zoomOut)
        self.fieldDist.valueChanged.connect(
            lambda newVal: self.parent.onRowChange(newVal, self.index, 'd'))

    def addFieldHeight(self, value):
        if value is False:
            return
        self.fieldHeight = QDoubleSpinBoxWithFocus(self.widget)
        self.fieldHeight.setFocusPolicy(Qt.ClickFocus)
        self.fieldHeight.setDecimals(1)
        self.fieldHeight.setSingleStep(self.parent.pole_height_step)
        # Pole rows with type fixed are only used in profile window, so before
        #  optimization. That's why they only have 1 meter resolution.
        if self.rowType == 'fixed':
            self.fieldHeight.setDecimals(0)
            self.fieldHeight.setSingleStep(1)
        self.fieldHeight.setSuffix(" m")
        self.fieldHeight.setFixedWidth(95)
        self.fieldHeight.setRange(0.0, 50.0)
        if value is not None:
            self.fieldHeight.setValue(float(value))
        self.row.addWidget(self.fieldHeight)

        # Connect events
        self.fieldHeight.inFocus.connect(
            lambda x: self.parent.zoomIn(self.index))
        self.fieldHeight.outFocus.connect(self.parent.zoomOut)
        self.fieldHeight.valueChanged.connect(
            lambda newVal: self.parent.onRowChange(newVal, self.index, 'h'))

    def addFieldAngle(self, value):
        if value is False:
            return
        self.fieldAngle = QSpinBoxWithFocus(self.widget)
        self.fieldAngle.setFocusPolicy(Qt.ClickFocus)
        self.fieldAngle.setSuffix(" °")
        self.fieldAngle.setFixedWidth(60)
        self.fieldAngle.setRange(-180, 180)
        if value is not None:
            self.fieldAngle.setValue(int(value))
        self.row.addWidget(self.fieldAngle)

        # Connect events
        self.fieldAngle.inFocus.connect(
            lambda x: self.parent.zoomIn(self.index))
        self.fieldAngle.outFocus.connect(self.parent.zoomOut)
        self.fieldAngle.valueChanged.connect(
            lambda newVal: self.parent.onRowChange(newVal, self.index, 'angle'
                                                   ))

    def addBtnPlus(self, createButton):
        if createButton is False:
            self.row.addSpacing(33)
            return
        self.addBtn = QPushButton(self.widget)
        self.addBtn.setMaximumSize(QSize(27, 27))
        icon = QIcon()
        icon.addPixmap(QPixmap(PoleRow.ICON_ADD_ROW), QIcon.Normal, QIcon.Off)
        self.addBtn.setIcon(icon)
        self.addBtn.setIconSize(QSize(16, 16))
        self.addBtn.setToolTip(
            self.tr('Fuegt eine neue Stuetze nach dieser hinzu'))
        self.addBtn.setAutoDefault(False)
        self.row.addWidget(self.addBtn)

        self.addBtn.clicked.connect(lambda x: self.parent.onRowAdd(self.index))

    def addBtnDel(self, createButton):
        if createButton is False:
            self.row.addSpacing(33)
            return
        self.delBtn = QPushButton(self.widget)
        self.delBtn.setMaximumSize(QSize(27, 27))
        icon = QIcon()
        icon.addPixmap(QPixmap(PoleRow.ICON_DEL_ROW), QIcon.Normal, QIcon.Off)
        self.delBtn.setIcon(icon)
        self.delBtn.setIconSize(QSize(16, 16))
        self.delBtn.setToolTip(self.tr('Loescht die Stuetze'))
        self.delBtn.setAutoDefault(False)
        self.row.addWidget(self.delBtn)

        self.delBtn.clicked.connect(lambda x: self.parent.onRowDel(self.index))

    def updateLowerDistRange(self, minimum):
        self.fieldDist.setMinimum(minimum)

    def updateUpperDistRange(self, maximum):
        self.fieldDist.setMaximum(maximum)

    def activate(self):
        self.statusSwitcher.blockSignals(True)
        self.statusSwitcher.setChecked(True)
        self.statusSwitcher.blockSignals(False)
        self.fieldName.setEnabled(True)
        self.fieldDist.setEnabled(True)

    def deactivate(self):
        self.statusSwitcher.blockSignals(True)
        self.statusSwitcher.setChecked(False)
        self.statusSwitcher.blockSignals(False)
        self.fieldName.setEnabled(False)
        self.fieldDist.setEnabled(False)

    def remove(self):
        # Disconnect all widgets
        self.fieldName.disconnect()
        self.fieldDist.disconnect()
        if self.fieldHeight: self.fieldHeight.disconnect()
        if self.fieldAngle: self.fieldAngle.disconnect()
        if self.addBtn: self.addBtn.disconnect()
        if self.delBtn: self.delBtn.disconnect()

        for i in reversed(range(self.row.count())):
            item = self.row.takeAt(i)
            widget = item.widget()
            if widget is not None:
                widget.deleteLater()
            else:
                # For spacers
                self.row.removeItem(item)

        self.layout.removeItem(self.row)
        self.parent.poleCount -= 1

    # noinspection PyMethodMayBeStatic
    def tr(self, message, **kwargs):
        """Get the translation for a string using Qt translation API.
        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString

        Parameters
        ----------
        **kwargs
        """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate(type(self).__name__, message)
示例#3
0
class ParamBox(QDialog):
    """
    Param box of the plugin
    """
    def __init__(self, parent=None, tree_dock=None):
        QWidget.__init__(self, parent)
        self.tree_dock = tree_dock

        # Init GUI
        self.init_gui()

        # Evaluate flags and tupdate the state of the Apply button
        self.evaluate_flags()

    def init_gui(self):
        """
        """
        dlg_layout = QVBoxLayout()
        params_layout = QVBoxLayout()
        params_layout.setAlignment(Qt.AlignTop)

        # Config files groupbox
        self.config_files_groupbox = QgsCollapsibleGroupBox(
            u"Fichier de configuration de l'arbre des ressources")
        config_file_groupbox_layout = QFormLayout(self.config_files_groupbox)

        # URL of the file
        self.config_file_url_label = QLabel(u"URL du fichier", self)
        self.config_file_url_edit = QLineEdit(self)
        self.config_file_url_edit.editingFinished.connect(
            self.config_file_url_changed)
        config_file_groupbox_layout.addRow(self.config_file_url_label,
                                           self.config_file_url_edit)

        # Download the file at startup
        self.download_cb = QCheckBox(
            u"Télécharger le fichier à chaque lancement de QGIS", self)
        self.download_cb.stateChanged.connect(self.download_cb_changed)
        config_file_groupbox_layout.addRow(self.download_cb)

        params_layout.addWidget(self.config_files_groupbox)

        # Download the file now
        self.download_now_label = QLabel(u"Télécharger le fichier maintenant",
                                         self)
        self.download_now_btnbox = QDialogButtonBox()
        self.download_now_btnbox.setOrientation(Qt.Horizontal)
        self.download_now_btnbox.setStandardButtons(QDialogButtonBox.Yes)
        self.download_now_btnbox.button(QDialogButtonBox.Yes).clicked.connect(
            self.download_file_now)
        config_file_groupbox_layout.addRow(self.download_now_label,
                                           self.download_now_btnbox)

        # Content of the resource tree groupbox
        self.resource_tree_groupbox = QgsCollapsibleGroupBox(
            u"Contenu de l'arbre des ressources")
        resource_tree_groupbox_layout = QFormLayout(
            self.resource_tree_groupbox)

        # Hide resources with a warn flag
        self.hide_resources_with_warn_status_cb = QCheckBox(
            u"Masquer les ressources en cours d'intégration", self)
        self.hide_resources_with_warn_status_cb.stateChanged.connect(
            self.hide_resources_with_warn_cb_changed)
        resource_tree_groupbox_layout.addRow(
            self.hide_resources_with_warn_status_cb)

        # Hide empty groups in the resources tree
        self.hide_empty_groups_cb = QCheckBox(
            u"Masquer les groupes de ressources vides", self)
        self.hide_empty_groups_cb.stateChanged.connect(
            self.hide_empty_groups_cb_changed)
        resource_tree_groupbox_layout.addRow(self.hide_empty_groups_cb)

        params_layout.addWidget(self.resource_tree_groupbox)
        dlg_layout.addLayout(params_layout)

        # Set values
        self.set_values_from_qsettings()

        # Bottom dialog buttons
        self.button_box = QDialogButtonBox()
        self.button_box.setOrientation(Qt.Horizontal)
        self.button_box.setStandardButtons(QDialogButtonBox.RestoreDefaults
                                           | QDialogButtonBox.Apply
                                           | QDialogButtonBox.Close)
        self.button_box.button(
            QDialogButtonBox.RestoreDefaults).clicked.connect(
                self.restore_defaults_button_clicked)
        self.button_box.button(QDialogButtonBox.Close).clicked.connect(
            self.close_button_clicked)
        self.button_box.button(QDialogButtonBox.Apply).clicked.connect(
            self.apply_button_clicked)

        # Dialog box title, layout, size and display
        title = u"Paramétrage de l'extension GéoSAS…"
        self.setWindowTitle(title)
        dlg_layout.addWidget(self.button_box)
        self.setLayout(dlg_layout)
        self.setMinimumWidth(500)
        self.resize(self.sizeHint())
        self.setSizeGripEnabled(False)
        self.setFixedSize(self.size())
        self.show()
        self.setSizeGripEnabled(False)

    def set_values_from_qsettings(self):
        """
        """
        # URL of the file
        self.config_file_url_edit.blockSignals(True)
        self.config_file_url_edit.setText(
            PluginGlobals.instance().CONFIG_FILE_URLS[0])
        self.config_file_url_edit.setCursorPosition(0)
        self.config_file_url_edit.blockSignals(False)

        # Download the file at startup
        self.download_cb.blockSignals(True)
        self.download_cb.setChecked(
            PluginGlobals.instance().CONFIG_FILES_DOWNLOAD_AT_STARTUP)
        self.download_cb.blockSignals(True)

        # Hide resources with a warn flag
        self.hide_resources_with_warn_status_cb.blockSignals(True)
        self.hide_resources_with_warn_status_cb.setChecked(
            PluginGlobals.instance().HIDE_RESOURCES_WITH_WARN_STATUS)
        self.hide_resources_with_warn_status_cb.blockSignals(False)

        # Hide empty groups in the resources tree
        self.hide_empty_groups_cb.blockSignals(True)
        self.hide_empty_groups_cb.setChecked(
            PluginGlobals.instance().HIDE_EMPTY_GROUPS)
        self.hide_empty_groups_cb.blockSignals(False)

    def evaluate_flags(self):
        """
        """
        # Detect modifications
        file_url_changed = (self.config_file_url_edit.text() !=
                            PluginGlobals.instance().CONFIG_FILE_URLS[0])

        download_at_startup_changed = \
            (self.download_cb.isChecked() != PluginGlobals.instance().CONFIG_FILES_DOWNLOAD_AT_STARTUP)

        hide_resources_with_warn_status_changed = \
            (self.hide_resources_with_warn_status_cb.isChecked() !=
             PluginGlobals.instance().HIDE_RESOURCES_WITH_WARN_STATUS)

        hide_empty_groups_changed = \
            (self.hide_empty_groups_cb.isChecked() != PluginGlobals.instance().HIDE_EMPTY_GROUPS)

        # Init flags
        self.need_update_visibility_of_tree_items = hide_empty_groups_changed or hide_resources_with_warn_status_changed
        self.need_update_of_tree_content = file_url_changed
        self.need_save = file_url_changed or download_at_startup_changed or \
                         hide_resources_with_warn_status_changed or \
                         hide_empty_groups_changed

        # Update state of the Apply Button
        self.button_box.button(QDialogButtonBox.Apply).setEnabled(
            self.need_save)

    def download_cb_changed(self, state):
        """
        Event sent when the state of the checkbox change
        """
        self.evaluate_flags()

    def config_file_url_changed(self):
        """
        Event sent when the text of the line edit has been edited
        """
        self.evaluate_flags()

    def hide_resources_with_warn_cb_changed(self, state):
        """
        Event sent when the state of the checkbox change
        """
        self.evaluate_flags()

    def hide_empty_groups_cb_changed(self, state):
        """
        Event sent when the state of the checkbox change
        """
        self.evaluate_flags()

    def download_file_now(self):
        """
        """
        # Download, read the resources tree file and update the GUI
        download_tree_config_file(self.config_file_url_edit.text())
        self.ressources_tree = TreeNodeFactory(
            PluginGlobals.instance().config_file_path).root_node
        if self.tree_dock is not None:
            self.tree_dock.set_tree_content(self.ressources_tree)

    def save_settings(self):
        """
        """
        # URL of the file
        new_value = [self.config_file_url_edit.text()]
        PluginGlobals.instance().set_qgis_settings_value(
            "config_file_urls", new_value)

        # Download the file at startup
        new_value = self.download_cb.isChecked()
        PluginGlobals.instance().set_qgis_settings_value(
            "config_files_download_at_startup", new_value)

        # Hide resources with a warn flag
        new_value = self.hide_resources_with_warn_status_cb.isChecked()
        PluginGlobals.instance().set_qgis_settings_value(
            "hide_resources_with_warn_status", new_value)

        # Hide empty groups in the resources tree
        new_value = self.hide_empty_groups_cb.isChecked()
        PluginGlobals.instance().set_qgis_settings_value(
            "hide_empty_groups", new_value)

        # Download, read the resources tree file and update the GUI
        if self.need_update_of_tree_content:
            download_tree_config_file(
                PluginGlobals.instance().CONFIG_FILE_URLS[0])
            self.ressources_tree = TreeNodeFactory(
                PluginGlobals.instance().config_file_path).root_node
            self.tree_dock.set_tree_content(self.ressources_tree)

        # Update the visibility of tree items
        elif self.need_update_visibility_of_tree_items and self.tree_dock is not None:
            self.tree_dock.update_visibility_of_tree_items()

    def apply_button_clicked(self):
        """
        """
        self.save_settings()
        self.evaluate_flags()

    def close_button_clicked(self):
        """
        """
        self.close()

    def restore_defaults_button_clicked(self):
        """
        """
        # URL of the file
        self.config_file_url_edit.blockSignals(True)
        self.config_file_url_edit.setText(
            PluginGlobals.instance().get_qgis_setting_default_value(
                "CONFIG_FILE_URLS")[0])
        self.config_file_url_edit.setCursorPosition(0)
        self.config_file_url_edit.blockSignals(False)

        # Download the file at startup
        self.download_cb.blockSignals(True)
        self.download_cb.setChecked(
            PluginGlobals.instance().get_qgis_setting_default_value(
                "CONFIG_FILES_DOWNLOAD_AT_STARTUP"))
        self.download_cb.blockSignals(False)

        # Hide resources with a warn flag
        self.hide_resources_with_warn_status_cb.blockSignals(True)
        self.hide_resources_with_warn_status_cb.setChecked(
            PluginGlobals.instance().get_qgis_setting_default_value(
                "HIDE_RESOURCES_WITH_WARN_STATUS"))
        self.hide_resources_with_warn_status_cb.blockSignals(False)

        # Hide empty groups in the resources tree
        self.hide_empty_groups_cb.blockSignals(True)
        self.hide_empty_groups_cb.setChecked(
            PluginGlobals.instance().get_qgis_setting_default_value(
                "HIDE_EMPTY_GROUPS"))
        self.hide_empty_groups_cb.blockSignals(False)

        self.evaluate_flags()

    def closeEvent(self, evnt):
        """
        """

        if self.need_save:
            reply = QMessageBox.question(
                self, u"Sauvegarder ?",
                u"Voulez-vous appliquer les changements avant de fermer la fenêtre de paramétrage de l'extension ?",
                QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel,
                QMessageBox.Yes)

            if reply == QMessageBox.No:
                evnt.accept()
                super(ParamBox, self).closeEvent(evnt)
            elif reply == QMessageBox.Yes:
                self.save_settings()
                evnt.accept()
                super(ParamBox, self).closeEvent(evnt)

        else:
            evnt.accept()
            super(ParamBox, self).closeEvent(evnt)

        evnt.ignore()
class LinearBarcodeLayoutItemWidget(QgsLayoutItemBaseWidget):  # pylint: disable=too-few-public-methods
    """Widget for configuring a LinearBarcodeLayoutItem."""
    def __init__(self, parent, layout_object):
        super().__init__(parent, layout_object)
        self._barcode_item = layout_object
        self.message_bar = None

        self.setPanelTitle(self.tr('Linear Barcode Properties'))
        self._init_widgets(layout_object)
        self._current_meta = self._barcode_cbo.current_metadata()
        self._update_gui_values()

    def _init_widgets(self, layout_object):
        """Initialize widgets"""
        lbl_title = QLabel()
        lbl_title.setStyleSheet(
            'padding: 2px; font-weight: bold; background-color: '
            'rgb(200, 200, 200);')
        lbl_title.setText(self.tr('Linear Barcode'))
        self._cd_value_widget = CodeValueWidget(self)
        self._cd_value_widget.value_changed.connect(
            self._on_code_value_changed)
        value_groupbox = QgsCollapsibleGroupBoxBasic(self.tr('Data'))
        gp_layout = QVBoxLayout()
        gp_layout.setContentsMargins(0, 0, 0, 0)
        gp_layout.addWidget(self._cd_value_widget)
        value_groupbox.setLayout(gp_layout)

        # Barcode properties
        barcode_props_groupbox = QgsCollapsibleGroupBoxBasic(
            self.tr('Properties'))
        barcode_props_layout = QGridLayout()

        lbl_barcode_type = QLabel(self.tr('Linear barcode type'))
        self._barcode_cbo = LinearMetadataCombobox()
        self._barcode_cbo.metadata_changed.connect(
            self._on_linear_type_changed)
        barcode_props_layout.addWidget(lbl_barcode_type, 0, 0)
        barcode_props_layout.addWidget(self._barcode_cbo, 0, 1)
        self._chk_checksum = QCheckBox(self.tr('Add checksum'))
        self._chk_checksum.stateChanged.connect(self._on_add_checksum)
        barcode_props_layout.addWidget(self._chk_checksum, 1, 0, 1, 2)
        self._chk_render_txt = QCheckBox(self.tr('Render barcode text'))
        self._chk_render_txt.stateChanged.connect(self._on_render_text_changed)
        barcode_props_layout.addWidget(self._chk_render_txt, 2, 0, 1, 2)
        barcode_props_layout.setColumnStretch(1, 1)

        barcode_props_groupbox.setLayout(barcode_props_layout)

        # Properties widget
        self._prop_widget = QgsLayoutItemPropertiesWidget(self, layout_object)
        self._prop_widget.showBackgroundGroup(False)

        # Add widgets to layout
        layout = QVBoxLayout()
        layout.setContentsMargins(0, 0, 0, 0)
        layout.addWidget(lbl_title)
        layout.addWidget(barcode_props_groupbox)
        layout.addWidget(value_groupbox)
        layout.addWidget(self._prop_widget)

        # Set layout
        self.setLayout(layout)

    def setDesignerInterface(self, iface):
        """
        Use layout iface to set the message_bar.
        """
        super().setDesignerInterface(iface)
        self.message_bar = iface.messageBar()

    def metadata(self, metadata_id):
        """
        Gets the metadata object that corresponds to the given id.
        :param metadata_id: Metadata id.
        :type metadata_id: str
        :return: Returns the metadata object corresponding to the
        given id.
        :rtype: AbstractLinearBarcodeMetadata
        """
        reg = LinearBarcodeMetadataRegistry.instance()

        return reg.metadata_by_typeid(metadata_id)

    def setNewItem(self, item):
        """
        Set widget properties to sync with item properties.
        """
        if item.type() != LINEAR_BARCODE_TYPE:
            return False

        self._barcode_item = item
        self._prop_widget.setItem(self._barcode_item)
        self._update_gui_values()

        return True

    def _on_linear_type_changed(self, metadata_id):
        """
        Slot raised when the linear barcode type has been changed.
        """
        meta = self.metadata(metadata_id)
        if meta is None:
            return

        self._current_meta = meta
        self._adapt_ui_item_checksum_properties()
        self.set_barcode_data()

    def _adapt_ui_item_checksum_properties(self):
        """
        Update UI and barcode item properties based on the properties of the
        current metadata object.
        """
        if self._current_meta is None:
            return

        if self._current_meta.supports_manual_checksum():
            self._barcode_item.supports_manual_checksum = True
            self._chk_checksum.setEnabled(True)
            # Set check status based on barcode properties
            if self._barcode_item.add_checksum:
                self._chk_checksum.setCheckState(Qt.Checked)
            else:
                self._chk_checksum.setCheckState(Qt.Unchecked)
        else:
            self._barcode_item.supports_manual_checksum = False
            self._chk_checksum.setEnabled(False)
            self._barcode_item.add_checksum = False
            if self._current_meta.is_checksum_automatic():
                self._chk_checksum.setCheckState(Qt.Checked)
            else:
                self._chk_checksum.setCheckState(Qt.Unchecked)

    def _on_add_checksum(self, state):
        """
        Slot raised to add checksum to the barcode data.
        """
        add_checksum = False
        if state == Qt.Checked:
            add_checksum = True

        self._barcode_item.beginCommand(self.tr('Change add checksum'),
                                        QgsLayoutItem.UndoCustomCommand)
        self._barcode_item.blockSignals(True)
        self._barcode_item.add_checksum = add_checksum
        self._barcode_item.blockSignals(False)
        self._barcode_item.endCommand()

    def _on_code_value_changed(self, value):
        """
        Slot raised when the barcode value changes.
        """
        self.set_barcode_data()

    def set_barcode_data(self):
        """
        Assert if characters (computed from the expression) are valid. If
        not, remove invalid characters then check if the maximum number of
        characters are within the maximum set for the given linear barcode
        type.
        """
        # Flag for the validity of barcode data
        is_invalid = False
        if self._current_meta is None:
            return

        user_value = self._cd_value_widget.code_value
        cd_val = self._barcode_item.evaluate_expression(user_value)

        if not cd_val:
            return

        # Check valid characters
        valid_chars = []
        for ch in cd_val:
            if self._current_meta.is_character_allowed(ch):
                valid_chars.append(ch)

        sanitized_txt = ''.join(valid_chars)

        # Notify user if there were invalid chars
        if len(sanitized_txt) != len(cd_val):
            self.add_warning_message(
                self.tr('Barcode data contains invalid characters.'))
            is_invalid = True

        cd_val = sanitized_txt
        # Check max length
        max_length = self._current_meta.max_input_length()
        if max_length != -1 and len(cd_val) > max_length:
            self.add_warning_message(
                self.tr('Barcode data cannot exceed the maximum length of '
                        '{0} characters for {1} linear barcode type.'.format(
                            max_length, self._current_meta.display_name())))
            is_invalid = True

        # Highlight barcode data based on validity of the input
        self._cd_value_widget.highlight_invalid_data(is_invalid)

        if is_invalid:  # pylint: disable=no-else-return
            return
        else:
            # Remove any warning items if still visible
            # Check if message_bar has already been initialized
            if self.message_bar is not None:
                self.message_bar.clearWidgets()

        # Set barcode value
        self._barcode_item.beginCommand(self.tr('Change code value'),
                                        QgsLayoutItem.UndoLabelText)
        self._barcode_item.blockSignals(True)
        try:
            self._barcode_item.barcode_type = self._current_meta.type_id()
            self._barcode_item.code_value = user_value
        except BarcodeException as bc_ex:
            self.add_warning_message(str(bc_ex))
            is_invalid = True
        self._barcode_item.blockSignals(False)
        self._barcode_item.endCommand()

    def _on_render_text_changed(self, state):
        """
        Slot raised when render_text has been checked/unchecked.
        """
        render_text = False
        if state == Qt.Checked:
            render_text = True

        self._barcode_item.beginCommand(self.tr('Change render text'),
                                        QgsLayoutItem.UndoCustomCommand)
        self._barcode_item.blockSignals(True)
        self._barcode_item.render_text = render_text
        self._barcode_item.blockSignals(False)
        self._barcode_item.endCommand()

    def _update_gui_values(self):
        """
        Update gui items based on the item properties.
        """
        self._current_meta = self.metadata(self._barcode_item.barcode_type)

        # Barcode type in combobox
        self._barcode_cbo.blockSignals(True)
        self._barcode_cbo.set_current_metadata(self._barcode_item.barcode_type)
        self._barcode_cbo.blockSignals(False)

        # Checksum property
        self._adapt_ui_item_checksum_properties()

        # Render text property
        self._chk_render_txt.blockSignals(True)
        if self._barcode_item.render_text:
            self._chk_render_txt.setCheckState(Qt.Checked)
        else:
            self._chk_render_txt.setCheckState(Qt.Unchecked)
        self._chk_render_txt.blockSignals(False)

        # Barcode value (which could also be an expression)
        self._cd_value_widget.block_value_widget_signals(True)
        self._cd_value_widget.code_value = self._barcode_item.code_value
        self._cd_value_widget.value_text_edit.moveCursor(
            QTextCursor.End, QTextCursor.MoveAnchor)
        self._cd_value_widget.block_value_widget_signals(False)

        # Now set barcode data
        self.set_barcode_data()

    def add_warning_message(self, msg):
        """
        Add warning message to the layout interface.
        :param msg: Warning message.
        :type msg: str
        """
        self.message_bar.pushWarning(self.tr('Linear Barcode'), msg)