def read(self, stream: Stream, version): self.texture = stream.read_object('texture') self.color = stream.read_object('color') self.transparency_color = stream.read_object('transparency color') self.outline = stream.read_object('outline') self.angle = stream.read_double('angle') self.size = stream.read_double('size') stream.read_int('raster op', expected=13) self.symbol_level = stream.read_int('level')
def read(self, stream: Stream, version): self.ramp = stream.read_object('Color ramp') self.fill_color = stream.read_object('fill color') # either an entire LineSymbol or just a LineSymbolLayer self.outline = stream.read_object('outline') self.percent = stream.read_double('percent') self.intervals = stream.read_uint('intervals') self.angle = stream.read_double('angle') self.type = stream.read_uint('Gradient type') self.symbol_level = SymbolLayer.read_symbol_level(stream)
def read(self, stream: Stream, version): _ = stream.read_double('unused double') _ = stream.read_double('unused double') self.line = stream.read_object('pattern line') # either an entire LineSymbol or just a LineSymbolLayer self.outline = stream.read_object('outline') self.angle = stream.read_double('angle') self.offset = stream.read_double('offset') self.separation = stream.read_double('separation') self.symbol_level = SymbolLayer.read_symbol_level(stream)
def read(self, stream: Stream, version): self.flip = stream.read_uchar('flip') != 0 self.offset = stream.read_double('offset') self.pattern_marker = stream.read_object('pattern marker') self.template = stream.read_object('template') self.decoration = stream.read_object('decoration') self.symbol_level = SymbolLayer.read_symbol_level(stream) self.decoration_on_top = stream.read_uchar('decoration on top') != 0 self.line_start_offset = stream.read_double('line start offset') self.cap = self.read_cap(stream) self.join = self.read_join(stream) self.miter_limit = stream.read_double('miter limit')
def read(self, stream: Stream, version): """ Reads the decoration information """ self.fixed_angle = not bool(stream.read_uchar()) stream.log('detected {}'.format( 'fixed angle' if self.fixed_angle else 'not fixed angle')) self.flip_first = bool(stream.read_uchar()) stream.log('detected {}'.format( 'flip first' if self.flip_first else 'no flip first')) self.flip_all = bool(stream.read_uchar()) stream.log('detected {}'.format( 'flip all' if self.flip_all else 'no flip all')) self.position_as_ratio = stream.read_ushort('position as ratio') != 0 self.marker = stream.read_object('marker') # next bit is the number of doubles coming next marker_number_positions = stream.read_uint('marker positions') # next bit is the positions themselves -- maybe we can infer this from the number of positions # alone. E.g. 2 positions = 0, 1. 3 positions = 0, 0.5, 1 for _ in range(marker_number_positions): self.marker_positions.append(stream.read_double()) stream.log('marker positions are {}'.format(self.marker_positions))
def read(self, stream: Stream, version): self.color = stream.read_object('color') self.width = stream.read_double('width') self.line_type = self.read_line_type(stream) stream.log('read line type of {}'.format(self.line_type)) self.symbol_level = SymbolLayer.read_symbol_level(stream)
def read(self, stream: Stream, version): has_picture = stream.read_int('has picture') != 0 stream.read_int('unknown') stream.read_int('unknown') stream.read_int('unknown') stream.read_int('unknown') stream.read_int('unknown') stream.read_int('unknown') stream.read_int('unknown') stream.read_int('unknown') self.transparency = stream.read_double('transparency') self.color = stream.read_object('color') self.transparent_texture_color = stream.read_object('transparent texture color') if has_picture: self.picture = BmpPicture() self.picture.read(stream, 1)
def read(self, stream: Stream, version): self.color = stream.read_object('color') self.size_z = stream.read_double('size z') self.type = stream.read_int('type', expected=(0, 1, 2, 3, 4, 5, 6)) self.quality = stream.read_double('quality') # 0->1 self.symbol_level = SymbolLayer.read_symbol_level(stream) self.z_rotation = stream.read_double('z rotation') self.x_offset = stream.read_double('x offset') self.y_offset = stream.read_double('y offset') self.z_offset = stream.read_double('z offset') stream.read_ushort('unknown', expected=65535) self.dx = stream.read_double('dx') self.dy = stream.read_double('dy') self.dz = stream.read_double('dz') self.x_rotation = stream.read_double('x rotation') self.y_rotation = stream.read_double('y rotation') self.width = stream.read_double('width') self.depth_y = stream.read_double('depth y') self.keep_aspect_ratio = stream.read_ushort('keep aspect ratio') != 0 self.billboard_display = stream.read_ushort('display front face') != 0
def read(self, stream: Stream, version): self.color = stream.read_object('color') self.width = stream.read_double('width') self.type = stream.read_int('type', expected=(0, 1, 2)) self.quality = stream.read_double('quality') self.symbol_level = SymbolLayer.read_symbol_level(stream)
def run_symbol_checks(self, path): """ Checks all bin symbols against expectations """ blobs = [] for fn in os.listdir(path): file = os.path.join(path, fn) if os.path.isfile(file): blobs.append(file) for file in blobs: print(file) group, symbol_name = os.path.split(file) path, group = os.path.split(group) with open(file, 'rb') as f: expected_symbol = expected[group][symbol_name] if 'skip' in expected_symbol: continue stream = Stream(f, debug=False) color = stream.read_object() self.assertEqual(expected_symbol['color'], color.to_dict())
def read(self, stream: Stream, version): self.color = stream.read_object('color') self.size_z = stream.read_double('size z') self.geometry = stream.read_object('multipatch') stream.read_double('unknown', expected=0) stream.read_double('unknown', expected=0) stream.read_int('unknown', expected=9) self.origin_x = stream.read_double('origin x') self.origin_y = stream.read_double('origin y') self.origin_z = stream.read_double('origin z') self.material_draping = stream.read_ushort('no material draping') == 0 stream.read_int('unknown', expected=13) stream.read_int('unknown', expected=0) self.rotation_z = stream.read_double('rotation z') self.offset_x = stream.read_double('offset x') self.offset_y = stream.read_double('offset y') self.offset_z = stream.read_double('offset z') stream.read_ushort('unknown', expected=65535) self.picture = stream.read_object('picture') self.rotation_x = stream.read_double('rotation x') self.rotation_y = stream.read_double('rotation y') self.size_x = stream.read_double('size x') self.size_y = stream.read_double('size y') self.keep_aspect = stream.read_ushort('keep aspect') != 0 stream.read_ushort('unknown', expected=0) if version == 7: return self.display_face_front = stream.read_ushort('display face front') != 0 if version < 7: stream.read_int('unknown', expected=0) stream.read_double('unknown') stream.read_ushort('unknown', expected=0) stream.read_ushort('unknown', expected=65535) if version > 8: stream.read_ushort('unknown', expected=0) stream.read_int('unknown', expected=0)
def read(self, stream: Stream, version): self.random = bool(stream.read_int('random')) self.offset_x = stream.read_double('offset x') self.offset_y = stream.read_double('offset y') self.separation_x = stream.read_double('separation x') self.separation_y = stream.read_double('separation y') _ = stream.read_double('unused double') _ = stream.read_double('unused double') self.marker = stream.read_object('fill marker') # either an entire LineSymbol or just a LineSymbolLayer self.outline = stream.read_object('outline') self.symbol_level = SymbolLayer.read_symbol_level(stream) self.grid_angle = stream.read_double('grid angle')
def read_symbol(_io_stream): """ Reads a symbol from the specified file """ stream = Stream(_io_stream, False, tolerant=False) res = stream.read_object('symbol') assert stream.tell() == stream.end, (stream.tell, stream.end) return res
def read(self, stream: Stream, version): self.symbol_level = SymbolLayer.read_symbol_level(stream) _ = stream.read_object('unused color') number_layers = stream.read_int('layers') for i in range(number_layers): layer = stream.read_object('symbol layer {}/{}'.format(i + 1, number_layers)) self.layers.extend([layer]) for l in self.layers: l.read_enabled(stream) for l in self.layers: l.read_locked(stream) if version >= 2: for l in self.layers: l.read_tags(stream)
def read(self, stream: Stream, version): """ Reads the decoration information """ # next bit is probably number of decorations? count = stream.read_uint('count of decorations') for i in range(count): decoration = stream.read_object('decoration element {}/{}'.format(i, count)) self.decorations.append(decoration)
def check_handle(self, file_handle): try: stream = Stream(file_handle) o = stream.read_object() if o is not None: return PersistentMatch(file_handle.tell() - 1, 1, o) else: return None except: # nopep8, pylint: disable=bare-except pass return None
def check_handle(self, file_handle): try: start = file_handle.tell() stream = Stream(file_handle) color = stream.read_object() if issubclass(color.__class__, Color): return ColorMatch(start, file_handle.tell() - start, color.color_model, color) else: return None except: # nopep8, pylint: disable=bare-except pass return None
def read(self, stream: Stream, version): self.color = stream.read_object('color') self.size = stream.read_double('size') self.width = stream.read_double('width') self.angle = stream.read_double('angle') self.style = stream.read_uint('style') self.symbol_level = SymbolLayer.read_symbol_level(stream) self.x_offset = stream.read_double('x offset') self.y_offset = stream.read_double('y offset') self.rotate_with_transform = stream.read_ushort('rotate with transform') != 0
def read(self, stream: Stream, version): self.color = stream.read_object('color') stream.read_int('raster op', expected=13) stream.read_int('unknown', expected=0) stream.read_int( 'unknown' ) # expected=(0,194149600, 239970880, 324278056, 6757008) ) self.color_ramp = stream.read_object('color ramp') self.invert = stream.read_ushort('invert') != 0 self.legend_class_index = stream.read_int('legend class index') if 2 < version < 5: stream.read_int('unknown', expected=0) stream.read_ushort('unknown', expected=0) elif version >= 5: stream.read_ushort('unknown', expected=0) stream.read_int('unknown', expected=0) self.number_of_label = stream.read_int('number of labels') self.horizontal = stream.read_ushort('horizontal') != 0 self.show_ticks = stream.read_ushort('show ticks') != 0
def read(self, stream: Stream, version): if version <= 4: self.picture = stream.read_object('picture') elif version == 7: _ = stream.read_ushort('pic version?') _ = stream.read_uint('picture type?') self.picture = stream.read_object('picture') elif version == 8: self.picture = Picture.create_from_stream(stream) self.color_background = stream.read_object('color bg') self.color_foreground = stream.read_object('color fg') self.color_transparent = stream.read_object('color trans') # either an entire LineSymbol or just a LineSymbolLayer self.outline = stream.read_object('outline') self.angle = stream.read_double('angle') self.scale_x = stream.read_double('scale_x') self.scale_y = stream.read_double('scale_y') self.offset_x = stream.read_double('offset x') self.offset_y = stream.read_double('offset y') self.separation_x = stream.read_double('separation x') self.separation_y = stream.read_double('separation y') stream.read(16) self.symbol_level = SymbolLayer.read_symbol_level(stream) self.swap_fb_gb = bool(stream.read_uchar('swap fgbg')) if version < 4: return stream.read(6) if 4 < version < 8: stream.read(4)
def read(self, stream: Stream, version): self.symbol_level = SymbolLayer.read_symbol_level(stream) # consume unused properties - MultiLayerMarkerSymbol implements IMarkerSymbol # so that the size/offsets/angle are required properties. But they aren't used # or exposed anywhere for MultiLayerMarkerSymbol _ = stream.read_double('unused marker size') _ = stream.read_double('unused marker x/y/offset or angle') _ = stream.read_double('unused marker x/y/offset or angle') _ = stream.read_double('unused marker x/y/offset or angle') _ = stream.read_object('unused color') self.halo = stream.read_int() == 1 self.halo_size = stream.read_double('halo size') self.halo_symbol = stream.read_object('halo') # useful stuff number_layers = stream.read_int('layers') for i in range(number_layers): layer = stream.read_object('symbol layer {}/{}'.format( i + 1, number_layers)) self.layers.extend([layer]) for layer in self.layers: layer.read_enabled(stream) for layer in self.layers: layer.read_locked(stream) if version > 1: _ = stream.read_double('unknown size') _ = stream.read_double('unknown size') if version >= 3: for layer in self.layers: layer.read_tags(stream)
def read(self, stream: Stream, version): if version in (3, 4, 5): self.picture = stream.read_object('picture') elif version in (7, 8): _ = stream.read_ushort('pic version?') _ = stream.read_uint('picture type?') self.picture = stream.read_object('picture') elif version == 9: self.picture = Picture.create_from_stream(stream) if version < 4: _ = stream.read_object() if version <= 8: _ = stream.read_object() self.color_foreground = stream.read_object('color 1') self.color_background = stream.read_object('color 2') if version >= 9: self.color_transparent = stream.read_object('color 3') self.angle = stream.read_double('angle') self.size = stream.read_double('size') self.x_offset = stream.read_double('x offset') self.y_offset = stream.read_double('y offset') self.x_scale = stream.read_double('x scale') self.y_scale = stream.read_double('y scale') self.symbol_level = SymbolLayer.read_symbol_level(stream) self.swap_fb_gb = bool(stream.read_uchar('swap fgbg')) self.rotate_with_transform = stream.read_ushort( 'rotate with transform') != 0 if version < 5: return stream.read_int('unknown', expected=0) stream.read_ushort('unknown', expected=0) if version == 7: return if 5 < version <= 8: size = stream.read_int('unknown size') stream.read(size)
def read(self, stream: Stream, version): size = stream.read_int('size') # TODO - reverse engineer stream.read(size) self.crs = stream.read_object('crs')
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 read(self, stream: Stream, version): stream.read_uchar('unknown', expected=0) self.offset = stream.read_double('offset') self.width = stream.read_double('width') self.fill_symbol = stream.read_object('fill symbol')
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
def read(self, stream: Stream, version): # first bit is either an entire LineSymbol or just a LineSymbolLayer self.outline = stream.read_object('outline') self.color = stream.read_object('color') self.symbol_level = SymbolLayer.read_symbol_level(stream) self.fill_style = stream.read_int('fill style')
def read(self, stream: Stream, version): self.color = stream.read_object('color') self.symbol_level = SymbolLayer.read_symbol_level(stream) stream.read_int('unknown int')
def read(self, stream: Stream, version): self.width = stream.read_double('width') self.texture_fill_symbol = stream.read_object('texture fill symbol') self.vertical_orientation = stream.read_uchar( 'vertical orientation') != 0
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