コード例 #1
0
    def processAlgorithm(
            self,  # pylint:disable=too-many-locals,too-many-statements,too-many-branches
            parameters,
            context,
            feedback):
        input_file = self.parameterAsString(parameters, self.INPUT, context)
        output_file = self.parameterAsFileOutput(parameters, self.OUTPUT,
                                                 context)

        fields = QgsFields()
        fields.append(QgsField('name', QVariant.String, '', 60))
        fields.append(QgsField('warning', QVariant.String, '', 250))

        sink, dest = self.parameterAsSink(parameters, self.REPORT, context,
                                          fields)

        style = QgsStyle()
        style.createMemoryDatabase()

        results = {}

        symbol_names = set()

        def make_name_unique(original_name):
            """
            Ensures that the symbol name is unique (in a case-insensitive way)
            """
            counter = 0
            candidate = original_name
            while candidate.lower() in symbol_names:
                # make name unique
                if counter == 0:
                    candidate += '_1'
                else:
                    candidate = candidate[:candidate.rfind('_') +
                                          1] + str(counter)
                counter += 1
            symbol_names.add(candidate.lower())
            return candidate

        symbols_to_extract = [
            Extractor.FILL_SYMBOLS, Extractor.LINE_SYMBOLS,
            Extractor.MARKER_SYMBOLS, Extractor.COLOR_RAMPS,
            Extractor.LINE_PATCHES, Extractor.AREA_PATCHES
        ]
        if Qgis.QGIS_VERSION_INT >= 30900:
            symbols_to_extract.extend(
                (Extractor.TEXT_SYMBOLS, Extractor.LABELS,
                 Extractor.MAPLEX_LABELS))

        type_percent = 100.0 / len(symbols_to_extract)

        results[self.LABEL_SETTINGS_COUNT] = 0
        results[self.UNREADABLE_LABEL_SETTINGS] = 0

        for type_index, symbol_type in enumerate(symbols_to_extract):
            feedback.pushInfo('Importing {} from {}'.format(
                symbol_type, input_file))

            try:
                raw_symbols = Extractor.extract_styles(input_file, symbol_type)
            except MissingBinaryException:
                raise QgsProcessingException(  # pylint: disable=raise-missing-from
                    'The MDB tools "mdb-export" utility is required to convert .style databases. Please setup a path to the MDB tools utility in the SLYR options panel.'
                )

            feedback.pushInfo('Found {} symbols of type "{}"\n\n'.format(
                len(raw_symbols), symbol_type))

            if feedback.isCanceled():
                break

            unreadable = 0
            for index, raw_symbol in enumerate(raw_symbols):
                feedback.setProgress(index / len(raw_symbols) * type_percent +
                                     type_percent * type_index)
                if feedback.isCanceled():
                    break
                name = raw_symbol[Extractor.NAME]
                tags = raw_symbol[Extractor.TAGS].split(';')
                feedback.pushInfo('{}/{}: {}'.format(index + 1,
                                                     len(raw_symbols), name))

                unique_name = make_name_unique(name)
                if name != unique_name:
                    feedback.pushInfo(
                        'Corrected to unique name of {}'.format(unique_name))

                handle = BytesIO(raw_symbol[Extractor.BLOB])
                stream = Stream(handle)

                f = QgsFeature()
                try:
                    symbol = stream.read_object()
                except UnreadableSymbolException as e:
                    feedback.reportError(
                        'Error reading symbol {}: {}'.format(name, e), False)
                    unreadable += 1
                    if sink:
                        f.setAttributes(
                            [name, 'Error reading symbol: {}'.format(e)])
                        sink.addFeature(f)
                    continue
                except NotImplementedException as e:
                    feedback.reportError(
                        'Parsing {} is not supported: {}'.format(name, e),
                        False)
                    unreadable += 1
                    if sink:
                        f.setAttributes(
                            [name, 'Parsing not supported: {}'.format(e)])
                        sink.addFeature(f)
                    continue
                except UnsupportedVersionException as e:
                    feedback.reportError(
                        'Cannot read {} version: {}'.format(name, e), False)
                    unreadable += 1
                    if sink:
                        f.setAttributes(
                            [name, 'Version not supported: {}'.format(e)])
                        sink.addFeature(f)
                    continue
                except UnknownClsidException as e:
                    feedback.reportError(str(e), False)
                    unreadable += 1
                    if sink:
                        f.setAttributes([name, 'Unknown object: {}'.format(e)])
                        sink.addFeature(f)
                    continue
                except UnreadablePictureException as e:
                    feedback.reportError(str(e), False)
                    unreadable += 1
                    if sink:
                        f.setAttributes(
                            [name, 'Unreadable picture: {}'.format(e)])
                        sink.addFeature(f)
                    continue

                def unsupported_object_callback(msg, level=Context.WARNING):
                    if level == Context.WARNING:
                        feedback.reportError('Warning: {}'.format(msg), False)
                    elif level == Context.CRITICAL:
                        feedback.reportError(msg, False)

                    if sink:
                        feat = QgsFeature()
                        feat.setAttributes([name, msg])  # pylint: disable=cell-var-from-loop
                        sink.addFeature(feat)

                context = Context()
                context.symbol_name = unique_name
                context.style_folder, _ = os.path.split(output_file)
                context.unsupported_object_callback = unsupported_object_callback

                if symbol_type in (Extractor.AREA_PATCHES,
                                   Extractor.LINE_PATCHES):
                    feedback.reportError(
                        '{}: Legend patch conversion requires the licensed version of SLYR'
                        .format(name), False)
                    unreadable += 1
                    if sink:
                        f.setAttributes(
                            [name, 'Unreadable legend patch: {}'.format(name)])
                        sink.addFeature(f)
                    continue

                try:
                    qgis_symbol = SymbolConverter.Symbol_to_QgsSymbol(
                        symbol, context)

                except NotImplementedException as e:
                    feedback.reportError(str(e), False)
                    unreadable += 1
                    if sink:
                        f.setAttributes([name, str(e)])
                        sink.addFeature(f)
                    continue
                except UnreadablePictureException as e:
                    feedback.reportError(str(e), False)
                    unreadable += 1
                    if sink:
                        f.setAttributes(
                            [name, 'Unreadable picture: {}'.format(e)])
                        sink.addFeature(f)
                    continue

                if isinstance(qgis_symbol, QgsSymbol):
                    style.addSymbol(unique_name, qgis_symbol, True)
                elif isinstance(qgis_symbol, QgsColorRamp):
                    style.addColorRamp(unique_name, qgis_symbol, True)
                elif isinstance(qgis_symbol, QgsTextFormat):
                    if Qgis.QGIS_VERSION_INT >= 30900:
                        style.addTextFormat(unique_name, qgis_symbol, True)
                elif isinstance(qgis_symbol, QgsPalLayerSettings):
                    if Qgis.QGIS_VERSION_INT >= 30900:
                        style.addLabelSettings(unique_name, qgis_symbol, True)
                elif Qgis.QGIS_VERSION_INT >= 31300:
                    if isinstance(qgis_symbol, QgsLegendPatchShape):
                        style.addLegendPatchShape(unique_name, qgis_symbol,
                                                  True)

                if tags:
                    if isinstance(qgis_symbol, QgsSymbol):
                        assert style.tagSymbol(QgsStyle.SymbolEntity,
                                               unique_name, tags)
                    elif isinstance(qgis_symbol, QgsColorRamp):
                        assert style.tagSymbol(QgsStyle.ColorrampEntity,
                                               unique_name, tags)
                    elif isinstance(qgis_symbol, QgsTextFormat) and hasattr(
                            QgsStyle, 'TextFormatEntity'):
                        assert style.tagSymbol(QgsStyle.TextFormatEntity,
                                               unique_name, tags)
                    elif isinstance(qgis_symbol,
                                    QgsPalLayerSettings) and hasattr(
                                        QgsStyle, 'LabelSettingsEntity'):
                        assert style.tagSymbol(QgsStyle.LabelSettingsEntity,
                                               unique_name, tags)
                    elif Qgis.QGIS_VERSION_INT >= 31300:
                        if isinstance(qgis_symbol, QgsLegendPatchShape):
                            assert style.tagSymbol(
                                QgsStyle.LegendPatchShapeEntity, unique_name,
                                tags)

            if symbol_type == Extractor.FILL_SYMBOLS:
                results[self.FILL_SYMBOL_COUNT] = len(raw_symbols)
                results[self.UNREADABLE_FILL_SYMBOLS] = unreadable
            elif symbol_type == Extractor.LINE_SYMBOLS:
                results[self.LINE_SYMBOL_COUNT] = len(raw_symbols)
                results[self.UNREADABLE_LINE_SYMBOLS] = unreadable
            elif symbol_type == Extractor.MARKER_SYMBOLS:
                results[self.MARKER_SYMBOL_COUNT] = len(raw_symbols)
                results[self.UNREADABLE_MARKER_SYMBOLS] = unreadable
            elif symbol_type == Extractor.COLOR_RAMPS:
                results[self.COLOR_RAMP_COUNT] = len(raw_symbols)
                results[self.UNREADABLE_COLOR_RAMPS] = unreadable
            elif symbol_type == Extractor.TEXT_SYMBOLS:
                results[self.TEXT_FORMAT_COUNT] = len(raw_symbols)
                results[self.UNREADABLE_TEXT_FORMATS] = unreadable
            elif symbol_type in (Extractor.MAPLEX_LABELS, Extractor.LABELS):
                results[self.LABEL_SETTINGS_COUNT] += len(raw_symbols)
                results[self.UNREADABLE_LABEL_SETTINGS] += unreadable
            elif symbol_type == Extractor.LINE_PATCHES:
                results[self.LINE_PATCH_COUNT] = len(raw_symbols)
                results[self.UNREADABLE_LINE_PATCHES] = unreadable
            elif symbol_type == Extractor.AREA_PATCHES:
                results[self.AREA_PATCH_COUNT] = len(raw_symbols)
                results[self.UNREADABLE_AREA_PATCHES] = unreadable

        style.exportXml(output_file)
        results[self.OUTPUT] = output_file
        results[self.REPORT] = dest
        return results
コード例 #2
0
ファイル: style_items.py プロジェクト: nyalldawson/slyr
    def open_style(input_file):  # pylint: disable=too-many-locals,too-many-branches,too-many-statements
        """
        Opens a .style file
        """
        if input_file.lower().endswith(
                '.style') and not Extractor.is_mdb_tools_binary_available():
            message_bar = iface.messageBar()
            widget = message_bar.createMessage('SLYR',
                                               "MDB Tools utility not found")
            settings_button = QPushButton("Configure…",
                                          pressed=partial(
                                              BrowserUtils.open_settings,
                                              widget))
            widget.layout().addWidget(settings_button)
            message_bar.pushWidget(widget, Qgis.Critical)
            return True

        style = QgsStyle()
        style.createMemoryDatabase()

        symbol_names = set()

        def make_name_unique(original_name):
            """
            Ensures that the symbol name is unique (in a case-insensitive way)
            """
            counter = 0
            candidate = original_name
            while candidate.lower() in symbol_names:
                # make name unique
                if counter == 0:
                    candidate += '_1'
                else:
                    candidate = candidate[:candidate.rfind('_') +
                                          1] + str(counter)
                counter += 1
            symbol_names.add(candidate.lower())
            return candidate

        feedback = QgsFeedback()

        progress_dialog = QProgressDialog("Loading style database…", "Abort",
                                          0, 100, None)
        progress_dialog.setWindowTitle("Loading Style")

        def progress_changed(progress: float):
            """
            Handles feedback to progress dialog bridge
            """
            progress_dialog.setValue(int(progress))
            iterations = 0
            while QCoreApplication.hasPendingEvents() and iterations < 100:
                QCoreApplication.processEvents()
                iterations += 1

        feedback.progressChanged.connect(progress_changed)

        def cancel():
            """
            Slot to cancel the import
            """
            feedback.cancel()

        progress_dialog.canceled.connect(cancel)
        unreadable = []
        warnings = set()
        errors = set()

        types_to_extract = [
            Extractor.FILL_SYMBOLS, Extractor.LINE_SYMBOLS,
            Extractor.MARKER_SYMBOLS, Extractor.COLOR_RAMPS,
            Extractor.TEXT_SYMBOLS, Extractor.LABELS, Extractor.MAPLEX_LABELS,
            Extractor.AREA_PATCHES, Extractor.LINE_PATCHES
        ]

        type_percent = 100 / len(types_to_extract)

        for type_index, symbol_type in enumerate(types_to_extract):

            try:
                raw_symbols = Extractor.extract_styles(input_file, symbol_type)
            except MissingBinaryException:
                BrowserUtils.show_warning(
                    'MDB Tools utility not found',
                    'Convert style',
                    'The MDB tools "mdb-export" utility is required to convert .style databases. Please setup a path to the MDB tools utility in the SLYR options panel.',
                    level=Qgis.Critical)
                progress_dialog.deleteLater()
                return True

            if feedback.isCanceled():
                break

            for index, raw_symbol in enumerate(raw_symbols):
                feedback.setProgress(
                    int(index / len(raw_symbols) * type_percent +
                        type_percent * type_index))
                if feedback.isCanceled():
                    break
                name = raw_symbol[Extractor.NAME]
                tags = raw_symbol[Extractor.TAGS].split(';')

                unique_name = make_name_unique(name)

                handle = BytesIO(raw_symbol[Extractor.BLOB])
                stream = Stream(handle)
                stream.allow_shortcuts = False

                try:
                    symbol = stream.read_object()
                except UnreadableSymbolException as e:
                    e = 'Unreadable object: {}'.format(e)
                    unreadable.append('<b>{}</b>: {}'.format(
                        html.escape(name), html.escape(str(e))))
                    continue
                except NotImplementedException as e:
                    unreadable.append('<b>{}</b>: {}'.format(
                        html.escape(name), html.escape(str(e))))
                    continue
                except UnsupportedVersionException as e:
                    e = 'Unsupported version: {}'.format(e)
                    unreadable.append('<b>{}</b>: {}'.format(
                        html.escape(name), html.escape(str(e))))
                    continue
                except UnknownClsidException as e:
                    unreadable.append('<b>{}</b>: {}'.format(
                        html.escape(name), html.escape(str(e))))
                    continue
                except UnreadablePictureException as e:
                    unreadable.append('<b>{}</b>: {}'.format(
                        html.escape(name), html.escape(str(e))))
                    continue

                context = Context()
                context.symbol_name = unique_name

                def unsupported_object_callback(msg, level=Context.WARNING):
                    if level == Context.WARNING:
                        warnings.add('<b>{}</b>: {}'.format(
                            html.escape(unique_name),  # pylint: disable=cell-var-from-loop
                            html.escape(msg)))
                    elif level == Context.CRITICAL:
                        errors.add('<b>{}</b>: {}'.format(
                            html.escape(unique_name),  # pylint: disable=cell-var-from-loop
                            html.escape(msg)))

                context.unsupported_object_callback = unsupported_object_callback
                # context.style_folder, _ = os.path.split(output_file)

                if symbol_type in (Extractor.AREA_PATCHES,
                                   Extractor.LINE_PATCHES):
                    unreadable.append(
                        '<b>{}</b>: Legend patch conversion requires the licensed version of SLYR'
                        .format(html.escape(name)))
                    continue

                try:
                    qgis_symbol = SymbolConverter.Symbol_to_QgsSymbol(
                        symbol, context)
                except NotImplementedException as e:
                    unreadable.append('<b>{}</b>: {}'.format(
                        html.escape(name), html.escape(str(e))))
                    continue
                except UnreadablePictureException as e:
                    unreadable.append('<b>{}</b>: {}'.format(
                        html.escape(name), html.escape(str(e))))
                    continue

                if isinstance(qgis_symbol, QgsSymbol):
                    # self.check_for_missing_fonts(qgis_symbol, feedback)
                    style.addSymbol(unique_name, qgis_symbol, True)
                elif isinstance(qgis_symbol, QgsColorRamp):
                    style.addColorRamp(unique_name, qgis_symbol, True)
                elif isinstance(qgis_symbol, QgsTextFormat):
                    if Qgis.QGIS_VERSION_INT >= 30900:
                        style.addTextFormat(unique_name, qgis_symbol, True)
                elif isinstance(qgis_symbol, QgsPalLayerSettings):
                    if Qgis.QGIS_VERSION_INT >= 30900:
                        style.addLabelSettings(unique_name, qgis_symbol, True)
                elif Qgis.QGIS_VERSION_INT >= 31300:
                    if isinstance(qgis_symbol, QgsLegendPatchShape):
                        style.addLegendPatchShape(unique_name, qgis_symbol,
                                                  True)

                if tags:
                    if isinstance(qgis_symbol, QgsSymbol):
                        assert style.tagSymbol(QgsStyle.SymbolEntity,
                                               unique_name, tags)
                    elif isinstance(qgis_symbol, QgsColorRamp):
                        assert style.tagSymbol(QgsStyle.ColorrampEntity,
                                               unique_name, tags)
                    elif isinstance(qgis_symbol, QgsTextFormat) and hasattr(
                            QgsStyle, 'TextFormatEntity'):
                        assert style.tagSymbol(QgsStyle.TextFormatEntity,
                                               unique_name, tags)
                    elif isinstance(qgis_symbol,
                                    QgsPalLayerSettings) and hasattr(
                                        QgsStyle, 'LabelSettingsEntity'):
                        assert style.tagSymbol(QgsStyle.LabelSettingsEntity,
                                               unique_name, tags)
                    elif Qgis.QGIS_VERSION_INT >= 31300:
                        if isinstance(qgis_symbol, QgsLegendPatchShape):
                            assert style.tagSymbol(
                                QgsStyle.LegendPatchShapeEntity, unique_name,
                                tags)
        progress_dialog.deleteLater()
        if feedback.isCanceled():
            return True

        if errors or unreadable or warnings:
            message = ''
            if unreadable:
                message = '<p>The following symbols could not be converted:</p>'
                message += '<ul>'
                for w in unreadable:
                    message += '<li>{}</li>'.format(w.replace('\n', '<br>'))
                message += '</ul>'

            if errors:
                message += '<p>The following errors were generated while converting symbols:</p>'
                message += '<ul>'
                for w in errors:
                    message += '<li>{}</li>'.format(w.replace('\n', '<br>'))
                message += '</ul>'

            if warnings:
                message += '<p>The following warnings were generated while converting symbols:</p>'
                message += '<ul>'
                for w in warnings:
                    message += '<li>{}</li>'.format(w.replace('\n', '<br>'))
                message += '</ul>'

            BrowserUtils.show_warning(
                'style could not be completely converted',
                'Convert style',
                message,
                level=Qgis.Critical if
                (unreadable or errors) else Qgis.Warning)

        if Qgis.QGIS_VERSION_INT >= 30800:
            dlg = QgsStyleManagerDialog(style, readOnly=True)
            dlg.setFavoritesGroupVisible(False)
            dlg.setSmartGroupsVisible(False)
            fi = QFileInfo(input_file)
            dlg.setBaseStyleName(fi.baseName())
        else:
            dlg = QgsStyleManagerDialog(style)
        dlg.setWindowTitle(fi.baseName())
        dlg.exec_()
        return True