def __init__(self, parent=None): super().__init__(parent) self.setupUi(self) self.symbol_units.addItem('Points', QgsUnitTypes.RenderPoints) self.symbol_units.addItem('Millimeters', QgsUnitTypes.RenderMillimeters) self.setObjectName('slyrOptions') self.picture_store.setStorageMode(QgsFileWidget.GetDirectory) self.inkscape_path_widget.setStorageMode(QgsFileWidget.GetFile) self.mdbtools_path_widget.setStorageMode(QgsFileWidget.GetDirectory) if not Extractor.is_windows(): self.label_mdb_tools_win.hide() s = QSettings() self.enable_annotations.setChecked( int(s.value('/plugins/slyr/enable_annotations', 0))) self.enable_layouts.setChecked( int(s.value('/plugins/slyr/convert_layouts', 1))) self.convert_font_to_svg.setChecked( int(s.value('/plugins/slyr/convert_font_to_svg', 0))) self.store_relative.setChecked( int(s.value('/plugins/slyr/store_relative', 0))) self.embed_pictures.setChecked( int( s.value('/plugins/slyr/embed_pictures', Qgis.QGIS_VERSION_INT >= 30600))) self.apply_tweaks.setChecked( int(s.value('/plugins/slyr/apply_tweaks', 1))) prev_units = int( s.value('/plugins/slyr/symbol_units', int(QgsUnitTypes.RenderPoints))) self.symbol_units.setCurrentIndex( self.symbol_units.findData(prev_units)) self.picture_store.setFilePath( s.value('/plugins/slyr/picture_store_folder', '')) self.inkscape_path_widget.setFilePath( s.value('/plugins/slyr/inkscape_path', 'inkscape')) self.mdbtools_path_widget.setFilePath( s.value('/plugins/slyr/mdbtools_path', ''))
def open_style(input_file): # pylint: disable=too-many-locals,too-many-branches,too-many-statements """ Opens a .style file """ if not Extractor.is_mdb_tools_binary_available(): bar = iface.messageBar() widget = bar.createMessage('SLYR', "MDB Tools utility not found") settings_button = QPushButton("Configure…", pressed=partial(open_settings, widget)) widget.layout().addWidget(settings_button) bar.pushWidget(widget, Qgis.Critical) return True style = QgsStyle() style.createMemoryDatabase() symbol_names = set() def make_name_unique(name): """ Ensures that the symbol name is unique (in a case insensitive way) """ counter = 0 candidate = 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): """ Handles feedback to progress dialog bridge """ progress_dialog.setValue(progress) iters = 0 while QCoreApplication.hasPendingEvents() and iters < 100: QCoreApplication.processEvents() iters += 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: 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(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(';') if symbol_type in ( Extractor.AREA_PATCHES, Extractor.LINE_PATCHES, Extractor.TEXT_SYMBOLS, Extractor.MAPLEX_LABELS, Extractor.LABELS): if symbol_type == Extractor.AREA_PATCHES: type_string = 'area patches' elif symbol_type == Extractor.LINE_PATCHES: type_string = 'line patches' elif symbol_type == Extractor.TEXT_SYMBOLS: type_string = 'text symbols' elif symbol_type == Extractor.MAPLEX_LABELS: type_string = 'maplex labels' elif symbol_type == Extractor.LABELS: type_string = 'labels' else: type_string = '' unreadable.append('<b>{}</b>: {} conversion requires a licensed version of the SLYR plugin'.format( html.escape(name), type_string)) continue 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), html.escape(msg))) elif level == Context.CRITICAL: errors.add('<b>{}</b>: {}'.format(html.escape(unique_name), html.escape(msg))) context.unsupported_object_callback = unsupported_object_callback # context.style_folder, _ = os.path.split(output_file) 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) 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) 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>' 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.exec_() return True
from slyr_community.parser.initalize_registry import initialize_registry initialize_registry() parser = argparse.ArgumentParser() parser.add_argument("file", help="style file to extract") args = parser.parse_args() total = 0 unreadable = [] for symbol_type in (Extractor.FILL_SYMBOLS, Extractor.LINE_SYMBOLS, Extractor.MARKER_SYMBOLS, Extractor.COLORS): print('{}:{}'.format(args.file, symbol_type)) raw_symbols = Extractor.extract_styles(args.file, symbol_type) print('Found {} symbols of type "{}"\n\n'.format(len(raw_symbols), symbol_type)) for index, symbol in enumerate(raw_symbols): print('{}.\t{}\n\tCategory: {}\n\tTags: {}'.format( index + 1, symbol[Extractor.NAME], symbol[Extractor.CATEGORY], symbol[Extractor.TAGS])) handle = BytesIO(symbol[Extractor.BLOB]) if symbol_type == Extractor.COLORS: color_model, color = read_color_and_model(handle) print(color_model, color) else: try: symbol_properties = read_symbol(file_handle=handle)
def processAlgorithm( self, # pylint: disable=missing-docstring,too-many-locals,too-many-statements parameters, context, feedback): input_file = self.parameterAsString(parameters, self.INPUT, context) output_file = self.parameterAsFileOutput(parameters, self.OUTPUT, context) results = {} colors = [] _, file_name = os.path.split(input_file) file_name, _ = os.path.splitext(file_name) feedback.pushInfo('Importing colors from {}'.format(input_file)) try: raw_colors = Extractor.extract_styles(input_file, Extractor.COLORS) 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 {} colors'.format(len(raw_colors))) unreadable = 0 for index, raw_color in enumerate(raw_colors): feedback.setProgress(index / len(raw_colors) * 100) if feedback.isCanceled(): break name = raw_color[Extractor.NAME] feedback.pushInfo('{}/{}: {}'.format(index + 1, len(raw_colors), name)) handle = BytesIO(raw_color[Extractor.BLOB]) stream = Stream(handle) try: color = stream.read_object() except InvalidColorException: feedback.reportError('Error reading color {}'.format(name), False) unreadable += 1 continue qcolor = ColorConverter.color_to_qcolor(color) colors.append((name, qcolor)) results[self.COLOR_COUNT] = len(raw_colors) results[self.UNREADABLE_COLOR_COUNT] = unreadable with open(output_file, 'wt') as f: f.write('GIMP Palette\n') f.write('Name: {}\n'.format(file_name)) f.write('Columns: 4\n') f.write('#\n') for c in colors: f.write('{} {} {} {}\n'.format(c[1].red(), c[1].green(), c[1].blue(), c[0])) results[self.OUTPUT] = output_file return results
def canExecute(self): if not Extractor.is_mdb_tools_binary_available(): return False, 'The MDB tools "mdb-export" utility is required to convert .style databases. Please setup a path to the MDB tools utility in the Settings - Options dialog, under the SLYR tab.' return True, None
def processAlgorithm( self, # pylint:disable=missing-docstring,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(name): """ Ensures that the symbol name is unique (in a case insensitive way) """ counter = 0 candidate = 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)) if symbol_type in (Extractor.AREA_PATCHES, Extractor.LINE_PATCHES, Extractor.TEXT_SYMBOLS, Extractor.MAPLEX_LABELS, Extractor.LABELS): if symbol_type == Extractor.AREA_PATCHES: type_string = 'area patches' elif symbol_type == Extractor.LINE_PATCHES: type_string = 'line patches' elif symbol_type == Extractor.TEXT_SYMBOLS: type_string = 'text symbols' elif symbol_type == Extractor.MAPLEX_LABELS: type_string = 'maplex labels' elif symbol_type == Extractor.LABELS: type_string = 'labels' else: type_string = '' feedback.reportError( 'Converting {} is available in the licensed version of SLYR only - please see https://north-road.com/slyr/ for details' .format(type_string)) unreadable += 1 continue 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: f = QgsFeature() f.setAttributes([name, msg]) # pylint: disable=cell-var-from-loop sink.addFeature(f) context = Context() context.symbol_name = unique_name context.style_folder, _ = os.path.split(output_file) context.unsupported_object_callback = unsupported_object_callback 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) 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) 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
args = parser.parse_args() if not args.file: args.file = '/home/nyall/Styles/GEO_Surface___Solid_Shades.style' if not args.destination: args.destination = '/home/nyall/Styles/GEO_Surface___Solid_Shades.xml' styles = [(args.file, Extractor.FILL_SYMBOLS)] style = QgsStyle() for (fill_style_db, symbol_type) in styles: print('{}:{}'.format(fill_style_db, symbol_type)) raw_symbols = Extractor.extract_styles(fill_style_db, symbol_type) print('Found {} symbols of type "{}"\n\n'.format(len(raw_symbols), symbol_type)) for index, raw_symbol in enumerate(raw_symbols): name = raw_symbol[Extractor.NAME] # print('{}/{}: {}'.format(index + 1, len(raw_symbols),name)) handle = BytesIO(raw_symbol[Extractor.BLOB]) try: symbol = read_symbol(file_handle=handle) except UnreadableSymbolException: print('Error reading symbol {}'.format(name)) continue qgis_symbol = FillSymbol_to_QgsFillSymbol(symbol) style.addSymbol(name, qgis_symbol)