diff --git a/felt/core/api_client.py b/felt/core/api_client.py index 5418fb8..8439c22 100644 --- a/felt/core/api_client.py +++ b/felt/core/api_client.py @@ -344,26 +344,28 @@ def create_upload_file_request(self, form_content = QByteArray() for name, value in parameters.to_form_fields().items(): - form_content.append("--QGISFormBoundary2XCkqVRLJ5XMxfw5\r\n") - form_content.append("Content-Disposition: form-data; ") - form_content.append(f"name=\"{name}\"") - form_content.append("\r\n") - form_content.append("\r\n") - form_content.append(value) - form_content.append("\r\n") - - form_content.append("--QGISFormBoundary2XCkqVRLJ5XMxfw5\r\n") - form_content.append("Content-Disposition: ") + form_content.append(b"--QGISFormBoundary2XCkqVRLJ5XMxfw5\r\n") + form_content.append(b"Content-Disposition: form-data; ") + form_content.append(f"name=\"{name}\"".encode()) + form_content.append(b"\r\n") + form_content.append(b"\r\n") + form_content.append(value.encode() if isinstance(value, str) + else value) + form_content.append(b"\r\n") + + form_content.append(b"--QGISFormBoundary2XCkqVRLJ5XMxfw5\r\n") + form_content.append(b"Content-Disposition: ") form_content.append( - f"form-data; name=\"file\"; filename=\"{filename}\"\r\n") + f"form-data; name=\"file\"; filename=\"{filename}\"\r\n" + .encode()) form_content.append( - "Content-Type: application/octet-stream\r\n") - form_content.append("\r\n") + b"Content-Type: application/octet-stream\r\n") + form_content.append(b"\r\n") form_content.append(content) - form_content.append("\r\n") - form_content.append("--QGISFormBoundary2XCkqVRLJ5XMxfw5--\r\n") + form_content.append(b"\r\n") + form_content.append(b"--QGISFormBoundary2XCkqVRLJ5XMxfw5--\r\n") content_length = form_content.length() network_request.setRawHeader( @@ -517,7 +519,7 @@ def create_layer_groups(self, json.dumps(group_post_data).encode() ) - if reply.error() == QNetworkReply.ContentAccessDenied: + if reply.error() == QNetworkReply.NetworkError.ContentAccessDenied: raise PaidPlanRequiredError("Upload requires a paid plan") return [ diff --git a/felt/core/auth.py b/felt/core/auth.py index f2d5f8b..6ebb900 100644 --- a/felt/core/auth.py +++ b/felt/core/auth.py @@ -93,7 +93,7 @@ def do_GET(self): token_body = urllib.parse.urlencode(body).encode() network_request = QNetworkRequest(QUrl(TOKEN_URL)) - network_request.setHeader(QNetworkRequest.ContentTypeHeader, + network_request.setHeader(QNetworkRequest.KnownHeaders.ContentTypeHeader, 'application/x-www-form-urlencoded') result_code = request.post(network_request, diff --git a/felt/core/fsl_converter.py b/felt/core/fsl_converter.py index cfec26b..5878985 100644 --- a/felt/core/fsl_converter.py +++ b/felt/core/fsl_converter.py @@ -43,7 +43,7 @@ QgsRandomMarkerFillSymbolLayer, QgsArrowSymbolLayer, QgsRenderContext, - QgsUnitTypes, + # QgsUnitTypes removed in QGIS 4 QgsFeatureRenderer, QgsSingleSymbolRenderer, QgsNullSymbolRenderer, @@ -71,6 +71,22 @@ from .logger import Logger from .map_utils import MapUtils +try: + from qgis.core import QgsUnitTypes # sketchy: removed in QGIS 4 +except ImportError: + from qgis.core import Qgis + + class QgsUnitTypes: + """Compatibility shim for QGIS 4""" + RenderUnit = Qgis.RenderUnit + RenderPixels = Qgis.RenderUnit.Pixels + RenderMillimeters = Qgis.RenderUnit.Millimeters + RenderPoints = Qgis.RenderUnit.Points + RenderInches = Qgis.RenderUnit.Inches + RenderPercentage = Qgis.RenderUnit.Percentage + RenderMetersInMapUnits = Qgis.RenderUnit.MetersInMapUnits + RenderMapUnits = Qgis.RenderUnit.MapUnits + class LogLevel(Enum): """ @@ -1004,9 +1020,9 @@ def convert_cap_style(style: Qt.PenCapStyle) -> str: Convert a Qt cap style to FSL """ return { - Qt.RoundCap: 'round', - Qt.SquareCap: 'square', - Qt.FlatCap: 'butt', + Qt.PenCapStyle.RoundCap: 'round', + Qt.PenCapStyle.SquareCap: 'square', + Qt.PenCapStyle.FlatCap: 'butt', }[style] @staticmethod @@ -1015,10 +1031,10 @@ def convert_join_style(style: Qt.PenJoinStyle) -> str: Convert a Qt join style to FSL """ return { - Qt.RoundJoin: 'round', - Qt.BevelJoin: 'bevel', - Qt.MiterJoin: 'miter', - Qt.SvgMiterJoin: 'miter', + Qt.PenJoinStyle.RoundJoin: 'round', + Qt.PenJoinStyle.BevelJoin: 'bevel', + Qt.PenJoinStyle.MiterJoin: 'miter', + Qt.PenJoinStyle.SvgMiterJoin: 'miter', }[style] @staticmethod @@ -1027,12 +1043,12 @@ def convert_pen_style(style: Qt.PenStyle) -> List[float]: Converts a Qt pen style to an array of dash/space lengths """ return { - Qt.NoPen: [], - Qt.SolidLine: [], - Qt.DashLine: [2.5, 2], - Qt.DotLine: [0.5, 1.3], - Qt.DashDotLine: [0.5, 1.3, 2.5, 1.3], - Qt.DashDotDotLine: [0.5, 1.3, 0.5, 1.3, 2.5, 1.3] + Qt.PenStyle.NoPen: [], + Qt.PenStyle.SolidLine: [], + Qt.PenStyle.DashLine: [2.5, 2], + Qt.PenStyle.DotLine: [0.5, 1.3], + Qt.PenStyle.DashDotLine: [0.5, 1.3, 2.5, 1.3], + Qt.PenStyle.DashDotDotLine: [0.5, 1.3, 0.5, 1.3, 2.5, 1.3] }[style] @staticmethod @@ -1043,7 +1059,7 @@ def simple_line_to_fsl( """ Converts a QGIS simple line symbol layer to FSL """ - if (layer.penStyle() == Qt.NoPen or + if (layer.penStyle() == Qt.PenStyle.NoPen or not layer.color().isValid() or layer.color().alphaF() == 0): return [] @@ -1070,7 +1086,7 @@ def simple_line_to_fsl( part, layer.customDashPatternUnit(), context, round_size=False) for part in layer.customDashVector()] - elif layer.penStyle() != Qt.SolidLine: + elif layer.penStyle() != Qt.PenStyle.SolidLine: res['dashArray'] = FslConverter.convert_pen_style(layer.penStyle()) # not supported: @@ -1264,10 +1280,10 @@ def simple_fill_to_fsl( """ Converts a QGIS simple fill symbol layer to FSL """ - has_invisible_fill = (layer.brushStyle() == Qt.NoBrush or + has_invisible_fill = (layer.brushStyle() == Qt.BrushStyle.NoBrush or not layer.color().isValid() or layer.color().alphaF() == 0) - has_invisible_stroke = (layer.strokeStyle() == Qt.NoPen or + has_invisible_stroke = (layer.strokeStyle() == Qt.PenStyle.NoPen or not layer.strokeColor().isValid() or layer.strokeColor().alphaF() == 0) if has_invisible_fill and has_invisible_stroke: @@ -1291,7 +1307,7 @@ def simple_fill_to_fsl( res['lineJoin'] = FslConverter.convert_join_style( layer.penJoinStyle()) - if layer.strokeStyle() != Qt.SolidLine: + if layer.strokeStyle() != Qt.PenStyle.SolidLine: res['dashArray'] = FslConverter.convert_pen_style( layer.strokeStyle()) else: @@ -1301,7 +1317,7 @@ def simple_fill_to_fsl( # - fill offset # - fill style - if layer.brushStyle() not in (Qt.SolidPattern, Qt.NoBrush): + if layer.brushStyle() not in (Qt.BrushStyle.SolidPattern, Qt.BrushStyle.NoBrush): context.push_warning( 'Fill patterns are not supported, converting to a solid fill', LogLevel.Warning, @@ -1323,7 +1339,7 @@ def simple_marker_to_fsl( """ has_fill = layer.color().isValid() and layer.color().alphaF() > 0 has_stroke = (layer.strokeColor().alphaF() > 0 and - layer.strokeStyle() != Qt.NoPen) + layer.strokeStyle() != Qt.PenStyle.NoPen) if not has_fill and not has_stroke: return [] @@ -1378,7 +1394,7 @@ def ellipse_marker_to_fsl( """ has_fill = layer.color().isValid() and layer.color().alphaF() > 0 has_stroke = (layer.strokeColor().alphaF() > 0 and - layer.strokeStyle() != Qt.NoPen) + layer.strokeStyle() != Qt.PenStyle.NoPen) if not has_fill and not has_stroke: return [] diff --git a/felt/core/layer_exporter.py b/felt/core/layer_exporter.py index c52a51c..46cc1aa 100644 --- a/felt/core/layer_exporter.py +++ b/felt/core/layer_exporter.py @@ -33,7 +33,6 @@ QgsCoordinateTransformContext, QgsCoordinateReferenceSystem, QgsCoordinateTransform, - QgsWkbTypes, QgsRasterLayer, QgsRasterFileWriter, QgsRasterBlockFeedback, @@ -48,6 +47,25 @@ QgsRasterRange ) +try: + from qgis.core import QgsWkbTypes # sketchy: removed in QGIS 4 + + def _force_multi(layer): + return QgsWkbTypes.geometryType(layer.wkbType()) in ( + QgsWkbTypes.LineGeometry, QgsWkbTypes.PolygonGeometry) + + def _drop_zm(layer): + return QgsWkbTypes.dropM(QgsWkbTypes.dropZ(layer.wkbType())) +except ImportError: + def _force_multi(layer): + return layer.geometryType() in ( + Qgis.GeometryType.Line, Qgis.GeometryType.Polygon) + + def _drop_zm(layer): + wkb = layer.wkbType() + flat = Qgis.WkbType(int(wkb) % 1000) + return flat + from .api_client import API_CLIENT from .enums import ( LayerExportResult, @@ -377,12 +395,8 @@ def export_vector_layer( self.transform_context ) writer_options.feedback = feedback - writer_options.forceMulti = QgsWkbTypes.geometryType( - layer.wkbType()) in (QgsWkbTypes.LineGeometry, - QgsWkbTypes.PolygonGeometry) - writer_options.overrideGeometryType = QgsWkbTypes.dropM( - QgsWkbTypes.dropZ(layer.wkbType()) - ) + writer_options.forceMulti = _force_multi(layer) + writer_options.overrideGeometryType = _drop_zm(layer) writer_options.includeZ = False writer_options.layerOptions = [ 'GEOMETRY_NAME=geom', @@ -396,11 +410,19 @@ def export_vector_layer( writer_options.attributes = fields.allAttributesList() if fid_index >= 0: fid_type = fields.field(fid_index).type() - needs_rewrite = force_rewrite_fid or fid_type not in ( - QVariant.Int, - QVariant.UInt, - QVariant.LongLong, - QVariant.ULongLong) + try: + int_types = ( + QVariant.Type.Int, + QVariant.Type.UInt, + QVariant.Type.LongLong, + QVariant.Type.ULongLong) + except AttributeError: + int_types = ( + QVariant.Int, + QVariant.UInt, + QVariant.LongLong, + QVariant.ULongLong) + needs_rewrite = force_rewrite_fid or fid_type not in int_types if Qgis.QGIS_VERSION_INT < 32400 and needs_rewrite: # older QGIS, can't rename attributes during export, so # drop FID diff --git a/felt/core/logger.py b/felt/core/logger.py index 7daec9f..f752a6e 100644 --- a/felt/core/logger.py +++ b/felt/core/logger.py @@ -128,7 +128,7 @@ def log_message(self, message: str): QMetaObject.invokeMethod( self, "_submit_usage", - Qt.QueuedConnection, + Qt.ConnectionType.QueuedConnection, Q_ARG(str, message), Q_ARG(str, UsageType.Info.to_string())) @@ -137,7 +137,7 @@ def log_message_json(self, message: Dict): QMetaObject.invokeMethod( self, "_submit_usage", - Qt.QueuedConnection, + Qt.ConnectionType.QueuedConnection, Q_ARG(str, message_str), Q_ARG(str, UsageType.Info.to_string())) @@ -152,7 +152,7 @@ def log_error(self, error: str): QMetaObject.invokeMethod( self, "_submit_usage", - Qt.QueuedConnection, + Qt.ConnectionType.QueuedConnection, Q_ARG(str, message), Q_ARG(str, UsageType.Error.to_string())) @@ -167,7 +167,7 @@ def log_error_json(self, error: Dict): QMetaObject.invokeMethod( self, "_submit_usage", - Qt.QueuedConnection, + Qt.ConnectionType.QueuedConnection, Q_ARG(str, json.dumps(error)), Q_ARG(str, UsageType.Error.to_string())) diff --git a/felt/core/map.py b/felt/core/map.py index cdf4053..8d017c6 100644 --- a/felt/core/map.py +++ b/felt/core/map.py @@ -47,7 +47,7 @@ def from_json(jsons: Union[str, Dict]) -> 'Map': last_visited_string = res.get('attributes', {}).get('visited_at') if last_visited_string: last_visited = QDateTime.fromString( - last_visited_string, Qt.ISODate + last_visited_string, Qt.DateFormat.ISODate ) else: last_visited = None diff --git a/felt/core/map_uploader.py b/felt/core/map_uploader.py index 88c23eb..05f52af 100644 --- a/felt/core/map_uploader.py +++ b/felt/core/map_uploader.py @@ -26,6 +26,7 @@ QNetworkRequest ) from qgis.core import ( + Qgis, QgsMapLayer, QgsMapLayerUtils, QgsProject, @@ -368,7 +369,7 @@ def _build_unsupported_layer_details(self, continue unsupported_layer_names.add(layer.name()) - if layer.type() == QgsMapLayer.PluginLayer: + if layer.type() == Qgis.LayerType.Plugin: id_string = layer.pluginLayerType() else: id_string = '{}:{}'.format( @@ -507,8 +508,8 @@ def run(self): feedback=self.feedback ) - if reply.error() != QNetworkReply.NoError: - if reply.error() == QNetworkReply.ContentAccessDenied: + if reply.error() != QNetworkReply.NetworkError.NoError: + if reply.error() == QNetworkReply.NetworkError.ContentAccessDenied: self.paid_plan_error = True self.error_string = reply.errorString() Logger.instance().log_error_json( @@ -647,7 +648,7 @@ def run(self): ) if reply.attribute( - QNetworkRequest.HttpStatusCodeAttribute) == 429: + QNetworkRequest.Attribute.HttpStatusCodeAttribute) == 429: rate_limit_counter += 1 if rate_limit_counter > 3: self.error_string = \ @@ -669,8 +670,8 @@ def run(self): QThread.sleep(5) continue - if reply.error() != QNetworkReply.NoError: - if reply.error() == QNetworkReply.ContentAccessDenied: + if reply.error() != QNetworkReply.NetworkError.NoError: + if reply.error() == QNetworkReply.NetworkError.ContentAccessDenied: self.paid_plan_error = True self.error_string = reply.errorString() Logger.instance().log_error_json( @@ -734,7 +735,7 @@ def _upload_progress(sent, total): form_content, feedback=self.feedback) - if blocking_request.reply().error() != QNetworkReply.NoError: + if blocking_request.reply().error() != QNetworkReply.NetworkError.NoError: self.error_string = blocking_request.reply().errorString() Logger.instance().log_error_json( { @@ -784,8 +785,8 @@ def _upload_progress(sent, total): ordering_key=details.ordering_key, ) - if reply and reply.error() != QNetworkReply.NoError: - if reply.error() == QNetworkReply.ContentAccessDenied: + if reply and reply.error() != QNetworkReply.NetworkError.NoError: + if reply.error() == QNetworkReply.NetworkError.ContentAccessDenied: self.paid_plan_error = True self.error_string = reply.errorString() Logger.instance().log_error_json( @@ -823,8 +824,8 @@ def _upload_progress(sent, total): ordering_key=details.ordering_key, ) - if reply and reply.error() != QNetworkReply.NoError: - if reply.error() == QNetworkReply.ContentAccessDenied: + if reply and reply.error() != QNetworkReply.NetworkError.NoError: + if reply.error() == QNetworkReply.NetworkError.ContentAccessDenied: self.paid_plan_error = True self.error_string = reply.errorString() Logger.instance().log_error_json( diff --git a/felt/core/multi_step_feedback.py b/felt/core/multi_step_feedback.py index 9131d38..97d0003 100644 --- a/felt/core/multi_step_feedback.py +++ b/felt/core/multi_step_feedback.py @@ -26,7 +26,7 @@ def __init__(self, steps: int, feedback: QgsFeedback): self.current_step = 0 self._feedback = feedback - self._feedback.canceled.connect(self.cancel, Qt.DirectConnection) + self._feedback.canceled.connect(self.cancel, Qt.ConnectionType.DirectConnection) self.progressChanged.connect(self._update_overall_progress) def step_finished(self): diff --git a/felt/core/recent_projects_model.py b/felt/core/recent_projects_model.py index f56f6b3..292e357 100644 --- a/felt/core/recent_projects_model.py +++ b/felt/core/recent_projects_model.py @@ -33,13 +33,13 @@ class RecentMapsModel(QAbstractItemModel): Qt model for recent maps """ - TitleRole = Qt.UserRole + 1 - UrlRole = Qt.UserRole + 2 - ThumbnailRole = Qt.UserRole + 3 - IdRole = Qt.UserRole + 4 - MapRole = Qt.UserRole + 5 - SubTitleRole = Qt.UserRole + 6 - IsNewMapRole = Qt.UserRole + 7 + TitleRole = Qt.ItemDataRole.UserRole + 1 + UrlRole = Qt.ItemDataRole.UserRole + 2 + ThumbnailRole = Qt.ItemDataRole.UserRole + 3 + IdRole = Qt.ItemDataRole.UserRole + 4 + MapRole = Qt.ItemDataRole.UserRole + 5 + SubTitleRole = Qt.ItemDataRole.UserRole + 6 + IsNewMapRole = Qt.ItemDataRole.UserRole + 7 LIMIT = 100 @@ -131,11 +131,11 @@ def _reply_finished(self, reply: QNetworkReply): self._current_reply = None - if reply.error() == QNetworkReply.ContentNotFoundError: + if reply.error() == QNetworkReply.NetworkError.ContentNotFoundError: self._next_page = None return - if reply.error() != QNetworkReply.NoError: + if reply.error() != QNetworkReply.NetworkError.NoError: return result = json.loads(reply.readAll().data().decode()) @@ -250,14 +250,14 @@ def pretty_format_date(self, date: QDateTime) -> str: # pylint:disable=too-many-return-statements,too-many-branches def data(self, index, - role=Qt.DisplayRole): + role=Qt.ItemDataRole.DisplayRole): if index.row() == 0 and not index.parent().isValid(): # special "New map" item - if role in (self.TitleRole, Qt.DisplayRole, Qt.ToolTipRole): + if role in (self.TitleRole, Qt.ItemDataRole.DisplayRole, Qt.ItemDataRole.ToolTipRole): return self._new_map_title if role == self.SubTitleRole: return self.tr('New map') - if role in (self.ThumbnailRole, Qt.DecorationRole): + if role in (self.ThumbnailRole, Qt.ItemDataRole.DecorationRole): # pylint: disable=import-outside-toplevel from ..gui import GuiUtils # pylint: enable=import-outside-toplevel @@ -271,7 +271,7 @@ def data(self, if _map: if role == self.MapRole: return _map - if role in (self.TitleRole, Qt.DisplayRole, Qt.ToolTipRole): + if role in (self.TitleRole, Qt.ItemDataRole.DisplayRole, Qt.ItemDataRole.ToolTipRole): return _map.title if role == self.SubTitleRole and _map.last_visited: date_string = self.pretty_format_date(_map.last_visited) @@ -280,7 +280,7 @@ def data(self, return _map.url if role == self.IdRole: return _map.id - if role in (self.ThumbnailRole, Qt.DecorationRole): + if role in (self.ThumbnailRole, Qt.ItemDataRole.DecorationRole): return self._thumbnail_manager.thumbnail( _map.thumbnail_url) if role == self.IsNewMapRole: @@ -295,7 +295,7 @@ def flags(self, index): if not index.isValid(): return f - return f | Qt.ItemIsEnabled | Qt.ItemIsSelectable + return f | Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable def canFetchMore(self, index: QModelIndex): if self._no_results_found: diff --git a/felt/core/thumbnail_manager.py b/felt/core/thumbnail_manager.py index 1df6c71..3126b7e 100644 --- a/felt/core/thumbnail_manager.py +++ b/felt/core/thumbnail_manager.py @@ -47,10 +47,10 @@ def download_thumbnail(self, url: str) -> Optional[QImage]: request = QNetworkRequest(QUrl(url)) request.setAttribute( - QNetworkRequest.CacheLoadControlAttribute, - QNetworkRequest.PreferCache) + QNetworkRequest.Attribute.CacheLoadControlAttribute, + QNetworkRequest.CacheLoadControl.PreferCache) request.setAttribute( - QNetworkRequest.CacheSaveControlAttribute, + QNetworkRequest.Attribute.CacheSaveControlAttribute, True ) reply = QgsNetworkAccessManager.instance().get(request) @@ -66,7 +66,7 @@ def _thumbnail_downloaded(self, reply): Triggered when a thumbnail download is complete """ self.queued_replies.remove(reply) - if reply.error() == QNetworkReply.NoError: + if reply.error() == QNetworkReply.NetworkError.NoError: url = reply.url().toString() img = QImage() img.loadFromData(reply.readAll()) diff --git a/felt/core/workspaces_model.py b/felt/core/workspaces_model.py index 29353d2..9028956 100644 --- a/felt/core/workspaces_model.py +++ b/felt/core/workspaces_model.py @@ -30,9 +30,9 @@ class WorkspacesModel(QAbstractItemModel): Qt model for workspaces """ - NameRole = Qt.UserRole + 1 - UrlRole = Qt.UserRole + 2 - IdRole = Qt.UserRole + 4 + NameRole = Qt.ItemDataRole.UserRole + 1 + UrlRole = Qt.ItemDataRole.UserRole + 2 + IdRole = Qt.ItemDataRole.UserRole + 4 no_workspaces_found = pyqtSignal() workspaces_loaded = pyqtSignal() @@ -61,11 +61,11 @@ def _reply_finished(self, reply: QNetworkReply): self._current_reply = None - if reply.error() == QNetworkReply.ContentNotFoundError: + if reply.error() == QNetworkReply.NetworkError.ContentNotFoundError: self._next_page = None return - if reply.error() != QNetworkReply.NoError: + if reply.error() != QNetworkReply.NetworkError.NoError: return result = json.loads(reply.readAll().data().decode()) @@ -113,11 +113,11 @@ def columnCount(self, parent=QModelIndex()): # pylint:disable=too-many-return-statements,too-many-branches def data(self, index, - role=Qt.DisplayRole): + role=Qt.ItemDataRole.DisplayRole): _workspace = self.index2workspace(index) if _workspace: - if role in (self.NameRole, Qt.DisplayRole, Qt.ToolTipRole): + if role in (self.NameRole, Qt.ItemDataRole.DisplayRole, Qt.ItemDataRole.ToolTipRole): return _workspace.name if role == self.UrlRole: return _workspace.url @@ -133,7 +133,7 @@ def flags(self, index): if not index.isValid(): return f - return f | Qt.ItemIsEnabled | Qt.ItemIsSelectable + return f | Qt.ItemFlag.ItemIsEnabled | Qt.ItemFlag.ItemIsSelectable # pylint: enable=missing-docstring,unused-argument def index2workspace(self, index: QModelIndex) -> Optional[Workspace]: diff --git a/felt/gui/authorization_manager.py b/felt/gui/authorization_manager.py index 651399a..1a7c4ff 100644 --- a/felt/gui/authorization_manager.py +++ b/felt/gui/authorization_manager.py @@ -231,7 +231,7 @@ def show_authorization_dialog(self): process """ dlg = AuthorizeDialog() - if dlg.exec_(): + if dlg.exec(): self.start_authorization_workflow() else: self.queued_callbacks = [] @@ -332,13 +332,13 @@ def _set_user_details(self, reply: QNetworkReply): return if self._user_reply.attribute( - QNetworkRequest.HttpStatusCodeAttribute) == 401: + QNetworkRequest.Attribute.HttpStatusCodeAttribute) == 401: self._user_reply = None self.deauthorize() self.attempt_authorize() return - if self._user_reply.error() != QNetworkReply.NoError: + if self._user_reply.error() != QNetworkReply.NetworkError.NoError: self._user_reply = None return diff --git a/felt/gui/authorize_dialog.py b/felt/gui/authorize_dialog.py index e11a976..f138dab 100644 --- a/felt/gui/authorize_dialog.py +++ b/felt/gui/authorize_dialog.py @@ -67,7 +67,7 @@ def __init__(self, parent: Optional[QWidget] = None): ) self.footer_label.setMinimumWidth( - QFontMetrics(self.footer_label.font()).width('x') * 40 + QFontMetrics(self.footer_label.font()).horizontalAdvance('x') * 40 ) def _sign_up(self): diff --git a/felt/gui/colored_progress_bar.py b/felt/gui/colored_progress_bar.py index 5ff01cb..0b2a0a3 100644 --- a/felt/gui/colored_progress_bar.py +++ b/felt/gui/colored_progress_bar.py @@ -29,18 +29,18 @@ def paintEvent(self, event): option = QStyleOptionProgressBar() self.initStyleOption(option) - option.textAlignment = Qt.AlignHCenter - option.palette.setColor(QPalette.Highlight, QColor("#3d521e")) + option.textAlignment = Qt.AlignmentFlag.AlignHCenter + option.palette.setColor(QPalette.ColorRole.Highlight, QColor("#3d521e")) if self.value() > 45: - option.palette.setColor(QPalette.HighlightedText, + option.palette.setColor(QPalette.ColorRole.HighlightedText, QColor(255, 255, 255)) else: - option.palette.setColor(QPalette.Text, + option.palette.setColor(QPalette.ColorRole.Text, QColor(0, 0, 0)) - option.palette.setColor(QPalette.HighlightedText, + option.palette.setColor(QPalette.ColorRole.HighlightedText, QColor(0, 0, 0)) painter = QPainter(self) - self.style().drawControl(QStyle.CE_ProgressBar, option, painter, self) + self.style().drawControl(QStyle.ControlElement.CE_ProgressBar, option, painter, self) # pylint: enable=missing-function-docstring,unused-argument diff --git a/felt/gui/create_map_dialog.py b/felt/gui/create_map_dialog.py index 7bedd0c..ea7dcf8 100644 --- a/felt/gui/create_map_dialog.py +++ b/felt/gui/create_map_dialog.py @@ -77,13 +77,13 @@ def __init__(self, # pylint: disable=too-many-statements self.setStyleSheet(FELT_STYLESHEET) self.page.setStyleSheet(FELT_STYLESHEET) self.page_2.setStyleSheet(FELT_STYLESHEET) - self.button_box.button(QDialogButtonBox.Ok).setStyleSheet( + self.button_box.button(QDialogButtonBox.StandardButton.Ok).setStyleSheet( FELT_STYLESHEET) - self.button_box.button(QDialogButtonBox.Cancel).setStyleSheet( + self.button_box.button(QDialogButtonBox.StandardButton.Cancel).setStyleSheet( FELT_STYLESHEET) self.progress_label.setTextInteractionFlags( - Qt.TextBrowserInteraction + Qt.TextInteractionFlag.TextBrowserInteraction ) self.progress_label.setOpenExternalLinks(True) @@ -119,23 +119,23 @@ def __init__(self, # pylint: disable=too-many-statements self.setWindowTitle(self.tr('Add to Felt')) self.footer_label.setMinimumWidth( - QFontMetrics(self.footer_label.font()).width('x') * 40 + QFontMetrics(self.footer_label.font()).horizontalAdvance('x') * 40 ) self.stacked_widget.setCurrentIndex(0) self.layers = layers - self.button_box.button(QDialogButtonBox.Ok).setText( + self.button_box.button(QDialogButtonBox.StandardButton.Ok).setText( self.tr('Add to Felt') ) - self.button_box.button(QDialogButtonBox.Ok).clicked.connect( + self.button_box.button(QDialogButtonBox.StandardButton.Ok).clicked.connect( self._start ) - self.button_box.button(QDialogButtonBox.Cancel).clicked.connect( + self.button_box.button(QDialogButtonBox.StandardButton.Cancel).clicked.connect( self._cancel ) - self.button_box.button(QDialogButtonBox.Cancel).setText( + self.button_box.button(QDialogButtonBox.StandardButton.Cancel).setText( self.tr('Close') ) @@ -146,11 +146,11 @@ def __init__(self, # pylint: disable=too-many-statements self.setting_menu = QMenu(self) palette = self.setting_menu.palette() - palette.setColor(QPalette.Active, QPalette.Base, QColor(255, 255, 255)) - palette.setColor(QPalette.Active, QPalette.Text, QColor(0, 0, 0)) - palette.setColor(QPalette.Active, QPalette.Highlight, + palette.setColor(QPalette.ColorGroup.Active, QPalette.ColorRole.Base, QColor(255, 255, 255)) + palette.setColor(QPalette.ColorGroup.Active, QPalette.ColorRole.Text, QColor(0, 0, 0)) + palette.setColor(QPalette.ColorGroup.Active, QPalette.ColorRole.Highlight, QColor('#3d521e')) - palette.setColor(QPalette.Active, QPalette.HighlightedText, + palette.setColor(QPalette.ColorGroup.Active, QPalette.ColorRole.HighlightedText, QColor(255, 255, 255)) self.setting_menu.setPalette(palette) @@ -186,12 +186,12 @@ def upload_raster_as_styled_toggled(): self.logout_action.triggered.connect(self._logout) palette = self.setting_button.palette() - palette.setColor(QPalette.Active, QPalette.Button, QColor('#ececec')) + palette.setColor(QPalette.ColorGroup.Active, QPalette.ColorRole.Button, QColor('#ececec')) self.setting_button.setPalette(palette) self.setting_button.setMenu(self.setting_menu) self.setting_button.setIcon(GuiUtils.get_icon('setting_icon.svg')) - self.setting_button.setPopupMode(QToolButton.InstantPopup) + self.setting_button.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup) self.setting_button.setStyleSheet( """QToolButton::menu-indicator { image: none } QToolButton { @@ -201,7 +201,7 @@ def upload_raster_as_styled_toggled(): """ ) self.setting_button.setFixedHeight( - self.button_box.button(QDialogButtonBox.Cancel).height() + self.button_box.button(QDialogButtonBox.StandardButton.Cancel).height() ) self.setting_button.setFixedWidth( int(self.setting_button.size().height() * 1.8) @@ -213,7 +213,7 @@ def upload_raster_as_styled_toggled(): # setting the setting button to a fixed height doesn't always # guarantee that the height exactly matches the Close/Add buttons. # So let's play it safe and force them to match always: - for b in (QDialogButtonBox.Cancel, QDialogButtonBox.Ok): + for b in (QDialogButtonBox.StandardButton.Cancel, QDialogButtonBox.StandardButton.Ok): self.button_box.button(b).setFixedHeight( self.setting_button.size().height() ) @@ -312,7 +312,7 @@ def _fatal_error(self, error: str): """ self.stacked_widget.setCurrentIndex(2) self.error_label.setText(error) - self.button_box.button(QDialogButtonBox.Ok).deleteLater() + self.button_box.button(QDialogButtonBox.StandardButton.Ok).deleteLater() def _no_workspace(self): """ @@ -431,7 +431,7 @@ def _validate(self): """ Validates the dialog """ - self.button_box.button(QDialogButtonBox.Ok).setEnabled( + self.button_box.button(QDialogButtonBox.StandardButton.Ok).setEnabled( self._is_valid() ) @@ -453,13 +453,13 @@ def _start(self): ) self.started = True - self.button_box.button(QDialogButtonBox.Cancel).setText( + self.button_box.button(QDialogButtonBox.StandardButton.Cancel).setText( self.tr('Cancel') ) - self.button_box.button(QDialogButtonBox.Ok).setText( + self.button_box.button(QDialogButtonBox.StandardButton.Ok).setText( self.tr('Uploading') ) - self.button_box.button(QDialogButtonBox.Ok).setEnabled(False) + self.button_box.button(QDialogButtonBox.StandardButton.Ok).setEnabled(False) target_map = self.maps_widget.selected_map() self.map_uploader_task.associated_map = target_map @@ -480,7 +480,7 @@ def _start(self): self.map_uploader_task.taskTerminated.connect(self._upload_terminated) self.map_uploader_task.progressChanged.connect(self.set_progress) - self.button_box.button(QDialogButtonBox.Ok).clicked.disconnect( + self.button_box.button(QDialogButtonBox.StandardButton.Ok).clicked.disconnect( self._start ) @@ -504,14 +504,14 @@ def _upload_finished(self): self.map_uploader_task = None self.started = False - self.button_box.button(QDialogButtonBox.Cancel).setText( + self.button_box.button(QDialogButtonBox.StandardButton.Cancel).setText( self.tr('Close') ) - self.button_box.button(QDialogButtonBox.Ok).setText( + self.button_box.button(QDialogButtonBox.StandardButton.Ok).setText( self.tr('Open Map') ) - self.button_box.button(QDialogButtonBox.Ok).setEnabled(True) - self.button_box.button(QDialogButtonBox.Ok).clicked.connect( + self.button_box.button(QDialogButtonBox.StandardButton.Ok).setEnabled(True) + self.button_box.button(QDialogButtonBox.StandardButton.Ok).clicked.connect( self._view_map ) @@ -525,7 +525,7 @@ def _upload_terminated(self): self.tr('Upload canceled — {}').format( self._map_title) ) - self.button_box.button(QDialogButtonBox.Ok).setText( + self.button_box.button(QDialogButtonBox.StandardButton.Ok).setText( self.tr('Canceled') ) self.progress_label.hide() @@ -566,14 +566,14 @@ def _upload_terminated(self): ) self.map_uploader_task = None - self.button_box.button(QDialogButtonBox.Cancel).setText( + self.button_box.button(QDialogButtonBox.StandardButton.Cancel).setText( self.tr('Close') ) - self.button_box.button(QDialogButtonBox.Ok).setEnabled( + self.button_box.button(QDialogButtonBox.StandardButton.Ok).setEnabled( False ) - self.button_box.button(QDialogButtonBox.Ok).hide() + self.button_box.button(QDialogButtonBox.StandardButton.Ok).hide() def _view_map(self): """ diff --git a/felt/gui/felt_dialog_header.py b/felt/gui/felt_dialog_header.py index a657812..ce2c70b 100644 --- a/felt/gui/felt_dialog_header.py +++ b/felt/gui/felt_dialog_header.py @@ -5,6 +5,7 @@ from typing import Optional from qgis.PyQt.QtCore import ( + Qt, QSize, QRectF ) @@ -12,7 +13,7 @@ QPainter, QImage ) -from qgis.PyQt.QtSvg import QSvgWidget +from qgis.PyQt.QtSvg import QSvgRenderer from qgis.PyQt.QtWidgets import ( QWidget, QVBoxLayout, @@ -25,6 +26,23 @@ ) +class _SvgWidget(QWidget): + """ + A simple widget that renders an SVG file, replacing QSvgWidget + which is not available through qgis.PyQt in QGIS 4. + """ + + def __init__(self, path: str, parent: Optional[QWidget] = None): + super().__init__(parent) + self._renderer = QSvgRenderer(path) + self.setAttribute(Qt.WidgetAttribute.WA_TranslucentBackground) + + def paintEvent(self, event): # pylint: disable=unused-argument + painter = QPainter(self) + self._renderer.render(painter, QRectF(self.rect())) + painter.end() + + class FeltDialogHeader(QWidget): """ A widget for dialog headers @@ -39,16 +57,15 @@ def __init__(self, parent: Optional[QWidget] = None): self._cached_image: Optional[QImage] = None self.setSizePolicy( - QSizePolicy.Minimum, - QSizePolicy.Fixed + QSizePolicy.Policy.Minimum, + QSizePolicy.Policy.Fixed ) - svg_logo_widget = QSvgWidget() + svg_logo_widget = _SvgWidget( + GuiUtils.get_icon_svg('felt_logo_white.svg')) fixed_size = QSize(self.LOGO_WIDTH_PIXELS, self.LOGO_HEIGHT_PIXELS) svg_logo_widget.setFixedSize(fixed_size) - svg_logo_widget.load(GuiUtils.get_icon_svg('felt_logo_white.svg')) - svg_logo_widget.setStyleSheet('background: transparent;') svg_logo_container = QVBoxLayout() svg_logo_container.setContentsMargins(0, 0, 0, 4) svg_logo_container.addWidget(svg_logo_widget) @@ -78,7 +95,7 @@ def sizeHint(self): def paintEvent(self, event): # pylint: disable=unused-argument painter = QPainter(self) - painter.setRenderHint(QPainter.Antialiasing, True) + painter.setRenderHint(QPainter.RenderHint.Antialiasing, True) # image has 437 x 107 aspect ratio if not self._cached_image or \ diff --git a/felt/gui/gui_utils.py b/felt/gui/gui_utils.py index cdc0ed7..51edd56 100644 --- a/felt/gui/gui_utils.py +++ b/felt/gui/gui_utils.py @@ -149,10 +149,10 @@ def get_svg_as_image(icon: str, width: int, height: int, renderer = QSvgRenderer(path) image = QImage(int(width * device_pixel_ratio), int(height * device_pixel_ratio), - QImage.Format_ARGB32) + QImage.Format.Format_ARGB32) image.setDevicePixelRatio(device_pixel_ratio) if not background_color: - image.fill(Qt.transparent) + image.fill(Qt.GlobalColor.transparent) else: image.fill(background_color) diff --git a/felt/gui/recent_maps_list_view.py b/felt/gui/recent_maps_list_view.py index b5b258b..8d02038 100644 --- a/felt/gui/recent_maps_list_view.py +++ b/felt/gui/recent_maps_list_view.py @@ -72,17 +72,17 @@ def process_thumbnail(self, device_pixel_ratio) scaled = thumbnail.scaled( QSize(uncropped_thumbnail_width, int(height * device_pixel_ratio)), - Qt.KeepAspectRatio, - Qt.SmoothTransformation, + Qt.AspectRatioMode.KeepAspectRatio, + Qt.TransformationMode.SmoothTransformation, ) im_out = QImage(int(target_size.width() * device_pixel_ratio), int(target_size.height() * device_pixel_ratio), - QImage.Format_ARGB32) - im_out.fill(Qt.transparent) + QImage.Format.Format_ARGB32) + im_out.fill(Qt.GlobalColor.transparent) painter = QPainter(im_out) - painter.setRenderHint(QPainter.Antialiasing, True) - painter.setPen(Qt.NoPen) + painter.setRenderHint(QPainter.RenderHint.Antialiasing, True) + painter.setPen(Qt.PenStyle.NoPen) painter.setBrush(QBrush(QColor(0, 0, 0))) painter.drawRoundedRect( QRectF(1 * device_pixel_ratio, @@ -106,7 +106,7 @@ def process_thumbnail(self, pen.setWidthF(self.BORDER_WIDTH_PIXELS * device_pixel_ratio) pen.setCosmetic(True) painter.setPen(pen) - painter.setBrush(Qt.NoBrush) + painter.setBrush(Qt.BrushStyle.NoBrush) painter.drawRoundedRect( QRectF(device_pixel_ratio, device_pixel_ratio, @@ -145,16 +145,16 @@ def paint( device_pixel_ratio = 1.0 if option.widget is None else \ option.widget.devicePixelRatioF() - option.palette.setColor(QPalette.Highlight, self.SELECTED_ROW_COLOR) + option.palette.setColor(QPalette.ColorRole.Highlight, self.SELECTED_ROW_COLOR) # draw background for item (i.e. selection background) style.drawPrimitive( - QStyle.PE_PanelItemViewItem, option, painter, option.widget) + QStyle.PrimitiveElement.PE_PanelItemViewItem, option, painter, option.widget) painter.save() - painter.setRenderHint(QPainter.Antialiasing, True) - painter.setRenderHint(QPainter.TextAntialiasing, True) - painter.setRenderHint(QPainter.SmoothPixmapTransform, True) + painter.setRenderHint(QPainter.RenderHint.Antialiasing, True) + painter.setRenderHint(QPainter.RenderHint.TextAntialiasing, True) + painter.setRenderHint(QPainter.RenderHint.SmoothPixmapTransform, True) inner_rect = QRectF(option.rect) inner_rect.adjust( @@ -205,7 +205,7 @@ def paint( line_heights = [1.0 * line_scale, 2.0 * line_scale] - painter.setBrush(Qt.NoBrush) + painter.setBrush(Qt.BrushStyle.NoBrush) painter.setPen(QPen(self.HEADING_COLOR)) painter.drawText( QPointF( @@ -247,14 +247,14 @@ def __init__(self, parent: Optional[QWidget] = None): self.setItemDelegate(delegate) p = self.palette() - p.setColor(QPalette.Base, QColor(255, 255, 255)) + p.setColor(QPalette.ColorRole.Base, QColor(255, 255, 255)) self.setPalette(p) fm = QFontMetrics(self.font()) self.setMinimumHeight(fm.height() * 12) - self.setVerticalScrollMode(QAbstractItemView.ScrollPerPixel) - self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff) + self.setVerticalScrollMode(QAbstractItemView.ScrollMode.ScrollPerPixel) + self.setHorizontalScrollBarPolicy(Qt.ScrollBarPolicy.ScrollBarAlwaysOff) self._model.first_results_found.connect(self._on_first_results_found) self._model.no_results_found.connect(self._on_no_results_found) @@ -269,7 +269,7 @@ def set_filter_string(self, filter_string: str): # option should get selected again self.selectionModel().select( self._model.index(0, 0), - QItemSelectionModel.ClearAndSelect) + QItemSelectionModel.SelectionFlag.ClearAndSelect) def set_workspace_id(self, workspace_id: Optional[str]): """ @@ -280,7 +280,7 @@ def set_workspace_id(self, workspace_id: Optional[str]): # option should get selected again self.selectionModel().select( self._model.index(0, 0), - QItemSelectionModel.ClearAndSelect) + QItemSelectionModel.SelectionFlag.ClearAndSelect) def set_new_map_title(self, title: str): """ @@ -298,7 +298,7 @@ def _on_first_results_found(self): if self._model.filter_string(): self.selectionModel().select( self._model.index(1, 0), - QItemSelectionModel.ClearAndSelect) + QItemSelectionModel.SelectionFlag.ClearAndSelect) def _on_no_results_found(self): """ @@ -309,7 +309,7 @@ def _on_no_results_found(self): # option should get selected again self.selectionModel().select( self._model.index(0, 0), - QItemSelectionModel.ClearAndSelect) + QItemSelectionModel.SelectionFlag.ClearAndSelect) class RecentMapsWidget(QWidget): @@ -372,7 +372,7 @@ def _update_filter_stylesheet(): self._view.selectionModel().select( self._view.model().index(0, 0), - QItemSelectionModel.ClearAndSelect) + QItemSelectionModel.SelectionFlag.ClearAndSelect) def filter_line_edit(self) -> QgsFilterLineEdit: """ diff --git a/felt/metadata.txt b/felt/metadata.txt index 20e60b8..72e2782 100644 --- a/felt/metadata.txt +++ b/felt/metadata.txt @@ -10,6 +10,7 @@ [general] name=Add to Felt qgisMinimumVersion=3.22 +qgisMaximumVersion=4.99 description=Create a collaborative Felt (felt.com) map from QGIS version=1.0.0 author=Felt diff --git a/felt/test/points.gpkg b/felt/test/points.gpkg index e3f6399..425e350 100644 Binary files a/felt/test/points.gpkg and b/felt/test/points.gpkg differ diff --git a/felt/test/qgis_interface.py b/felt/test/qgis_interface.py index 7d2735b..30292cd 100644 --- a/felt/test/qgis_interface.py +++ b/felt/test/qgis_interface.py @@ -25,7 +25,7 @@ import logging from typing import List -from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal, QSize +from qgis.PyQt.QtCore import QObject, pyqtSlot, pyqtSignal, QSize from qgis.PyQt.QtWidgets import QDockWidget from qgis.core import QgsProject, QgsMapLayer from qgis.gui import (QgsMapCanvas, diff --git a/felt/test/test_api_client.py b/felt/test/test_api_client.py index fbaafb6..55cc4d1 100644 --- a/felt/test/test_api_client.py +++ b/felt/test/test_api_client.py @@ -118,7 +118,7 @@ def test_user(self): spy.wait() self.assertEqual(reply.error(), - QNetworkReply.AuthenticationRequiredError) + QNetworkReply.NetworkError.AuthenticationRequiredError) # an authenticated client reply = CLIENT.user() @@ -130,7 +130,7 @@ def test_user(self): spy.wait() self.assertEqual(reply.error(), - QNetworkReply.NoError) + QNetworkReply.NetworkError.NoError) user = User.from_json(reply.readAll().data().decode()) self.assertEqual(user.name, 'Nyall Dawson') @@ -153,7 +153,7 @@ def test_create_map(self): spy.wait() self.assertEqual(reply.error(), - QNetworkReply.AuthenticationRequiredError) + QNetworkReply.NetworkError.AuthenticationRequiredError) # an authenticated client reply = CLIENT.create_map( @@ -166,7 +166,7 @@ def test_create_map(self): spy.wait() self.assertEqual(reply.error(), - QNetworkReply.NoError) + QNetworkReply.NetworkError.NoError) created_map = Map.from_json(reply.readAll().data().decode()) self.assertEqual(created_map.type, ObjectType.Map) @@ -184,7 +184,7 @@ def test_create_layer(self): spy.wait() self.assertEqual(reply.error(), - QNetworkReply.NoError) + QNetworkReply.NetworkError.NoError) created_map = Map.from_json(reply.readAll().data().decode()) @@ -200,7 +200,7 @@ def test_create_layer(self): spy.wait() self.assertEqual(reply.error(), - QNetworkReply.NoError) + QNetworkReply.NetworkError.NoError) json_params = reply.readAll().data().decode() params = S3UploadParameters.from_json(json.loads(json_params)) @@ -228,7 +228,7 @@ def test_create_layer(self): spy = QSignalSpy(reply.finished) spy.wait() - self.assertEqual(reply.error(), QNetworkReply.NoError) + self.assertEqual(reply.error(), QNetworkReply.NetworkError.NoError) reply = CLIENT.finalize_layer_upload( created_map.id, @@ -239,7 +239,7 @@ def test_create_layer(self): spy.wait() self.assertEqual(reply.error(), - QNetworkReply.NoError) + QNetworkReply.NetworkError.NoError) json_params = reply.readAll().data().decode() print(json_params) @@ -257,7 +257,7 @@ def test_usage(self): # reply should be empty response self.assertEqual(reply.error(), - QNetworkReply.NoError) + QNetworkReply.NetworkError.NoError) self.assertFalse(reply.readAll()) diff --git a/felt/test/test_fsl_conversion.py b/felt/test/test_fsl_conversion.py index 3379de7..4b79359 100644 --- a/felt/test/test_fsl_conversion.py +++ b/felt/test/test_fsl_conversion.py @@ -158,13 +158,13 @@ def test_simple_line_to_fsl(self): line = QgsSimpleLineSymbolLayer(color=QColor(255, 0, 0)) # no pen - line.setPenStyle(Qt.NoPen) + line.setPenStyle(Qt.PenStyle.NoPen) self.assertFalse( FslConverter.simple_line_to_fsl(line, conversion_context) ) # transparent color - line.setPenStyle(Qt.SolidLine) + line.setPenStyle(Qt.PenStyle.SolidLine) line.setColor(QColor(0, 255, 0, 0)) self.assertFalse( FslConverter.simple_line_to_fsl(line, conversion_context) @@ -197,7 +197,7 @@ def test_simple_line_to_fsl(self): }] ) - line.setPenCapStyle(Qt.FlatCap) + line.setPenCapStyle(Qt.PenCapStyle.FlatCap) self.assertEqual( FslConverter.simple_line_to_fsl(line, conversion_context), [{ @@ -210,7 +210,7 @@ def test_simple_line_to_fsl(self): }] ) - line.setPenCapStyle(Qt.RoundCap) + line.setPenCapStyle(Qt.PenCapStyle.RoundCap) self.assertEqual( FslConverter.simple_line_to_fsl(line, conversion_context), [{ @@ -223,7 +223,7 @@ def test_simple_line_to_fsl(self): }] ) - line.setPenJoinStyle(Qt.RoundJoin) + line.setPenJoinStyle(Qt.PenJoinStyle.RoundJoin) self.assertEqual( FslConverter.simple_line_to_fsl(line, conversion_context), [{ @@ -236,7 +236,7 @@ def test_simple_line_to_fsl(self): }] ) - line.setPenJoinStyle(Qt.MiterJoin) + line.setPenJoinStyle(Qt.PenJoinStyle.MiterJoin) self.assertEqual( FslConverter.simple_line_to_fsl(line, conversion_context), [{ @@ -249,7 +249,7 @@ def test_simple_line_to_fsl(self): }] ) - line.setPenJoinStyle(Qt.MiterJoin) + line.setPenJoinStyle(Qt.PenJoinStyle.MiterJoin) self.assertEqual( FslConverter.simple_line_to_fsl(line, conversion_context, symbol_opacity=0.5), @@ -264,7 +264,7 @@ def test_simple_line_to_fsl(self): }] ) - line.setPenStyle(Qt.DashLine) + line.setPenStyle(Qt.PenStyle.DashLine) self.assertEqual( FslConverter.simple_line_to_fsl(line, conversion_context), [{ @@ -278,7 +278,7 @@ def test_simple_line_to_fsl(self): }] ) - line.setPenStyle(Qt.DotLine) + line.setPenStyle(Qt.PenStyle.DotLine) self.assertEqual( FslConverter.simple_line_to_fsl(line, conversion_context), [{ @@ -292,7 +292,7 @@ def test_simple_line_to_fsl(self): }] ) - line.setPenStyle(Qt.DashDotLine) + line.setPenStyle(Qt.PenStyle.DashDotLine) self.assertEqual( FslConverter.simple_line_to_fsl(line, conversion_context), [{ @@ -306,7 +306,7 @@ def test_simple_line_to_fsl(self): }] ) - line.setPenStyle(Qt.DashDotDotLine) + line.setPenStyle(Qt.PenStyle.DashDotDotLine) self.assertEqual( FslConverter.simple_line_to_fsl(line, conversion_context), [{ @@ -320,7 +320,7 @@ def test_simple_line_to_fsl(self): }] ) - line.setPenStyle(Qt.SolidLine) + line.setPenStyle(Qt.PenStyle.SolidLine) line.setUseCustomDashPattern(True) line.setCustomDashPatternUnit(QgsUnitTypes.RenderPixels) line.setCustomDashVector([0.5, 1, 1.5, 2]) @@ -369,16 +369,16 @@ def test_simple_fill_to_fsl(self): fill = QgsSimpleFillSymbolLayer(color=QColor(255, 0, 0)) - fill.setStrokeStyle(Qt.NoPen) + fill.setStrokeStyle(Qt.PenStyle.NoPen) # no brush - fill.setBrushStyle(Qt.NoBrush) + fill.setBrushStyle(Qt.BrushStyle.NoBrush) self.assertFalse( FslConverter.simple_fill_to_fsl(fill, conversion_context) ) # transparent color - fill.setBrushStyle(Qt.SolidPattern) + fill.setBrushStyle(Qt.BrushStyle.SolidPattern) fill.setColor(QColor(0, 255, 0, 0)) self.assertFalse( FslConverter.simple_fill_to_fsl(fill, conversion_context) @@ -387,7 +387,7 @@ def test_simple_fill_to_fsl(self): fill.setColor(QColor(0, 255, 0)) # transparent color with stroke - fill.setStrokeStyle(Qt.DashLine) + fill.setStrokeStyle(Qt.PenStyle.DashLine) fill.setStrokeWidth(3) fill.setStrokeColor(QColor(255, 0, 0)) self.assertEqual( @@ -400,7 +400,7 @@ def test_simple_fill_to_fsl(self): 'isHoverable': False, 'strokeWidth': 11}] ) - fill.setStrokeStyle(Qt.SolidLine) + fill.setStrokeStyle(Qt.PenStyle.SolidLine) fill.setStrokeColor(QColor(35, 35, 35)) fill.setColor(QColor(0, 255, 0)) @@ -426,7 +426,7 @@ def test_simple_fill_to_fsl(self): 'strokeWidth': 3.0}] ) - fill.setPenJoinStyle(Qt.RoundJoin) + fill.setPenJoinStyle(Qt.PenJoinStyle.RoundJoin) self.assertEqual( FslConverter.simple_fill_to_fsl(fill, conversion_context), [{'color': 'rgb(0, 255, 0)', @@ -437,7 +437,7 @@ def test_simple_fill_to_fsl(self): 'strokeWidth': 3.0}] ) - fill.setPenJoinStyle(Qt.MiterJoin) + fill.setPenJoinStyle(Qt.PenJoinStyle.MiterJoin) self.assertEqual( FslConverter.simple_fill_to_fsl(fill, conversion_context), [{'color': 'rgb(0, 255, 0)', @@ -448,7 +448,7 @@ def test_simple_fill_to_fsl(self): 'strokeWidth': 3.0}] ) - fill.setPenJoinStyle(Qt.MiterJoin) + fill.setPenJoinStyle(Qt.PenJoinStyle.MiterJoin) self.assertEqual( FslConverter.simple_fill_to_fsl(fill, conversion_context, symbol_opacity=0.5), @@ -461,7 +461,7 @@ def test_simple_fill_to_fsl(self): 'strokeWidth': 3.0}] ) - fill.setStrokeStyle(Qt.DashLine) + fill.setStrokeStyle(Qt.PenStyle.DashLine) self.assertEqual( FslConverter.simple_fill_to_fsl(fill, conversion_context), [{'color': 'rgb(0, 255, 0)', @@ -486,7 +486,7 @@ def test_simple_fill_to_fsl(self): ) # outline, no fill - fill.setBrushStyle(Qt.NoBrush) + fill.setBrushStyle(Qt.BrushStyle.NoBrush) self.assertEqual( FslConverter.simple_fill_to_fsl(fill, conversion_context, symbol_opacity=0.5), @@ -499,7 +499,7 @@ def test_simple_fill_to_fsl(self): 'isHoverable': False, 'strokeWidth': 3.0}] ) - fill.setBrushStyle(Qt.SolidPattern) + fill.setBrushStyle(Qt.BrushStyle.SolidPattern) fill.setFillColor(QColor(255, 255, 0, 0)) self.assertEqual( FslConverter.simple_fill_to_fsl(fill, conversion_context, @@ -625,7 +625,7 @@ def test_line_pattern_fill_to_fsl(self): # invisible line line = QgsLineSymbol() simple_line = QgsSimpleLineSymbolLayer() - simple_line.setPenStyle(Qt.NoPen) + simple_line.setPenStyle(Qt.PenStyle.NoPen) line.changeSymbolLayer(0, simple_line.clone()) fill.setSubSymbol(line.clone()) self.assertFalse( @@ -712,7 +712,7 @@ def test_simple_marker_to_fsl(self): ) marker.setStrokeColor(QColor(255, 0, 255)) - marker.setStrokeStyle(Qt.NoPen) + marker.setStrokeStyle(Qt.PenStyle.NoPen) self.assertFalse( FslConverter.simple_marker_to_fsl(marker, conversion_context) ) @@ -744,7 +744,7 @@ def test_simple_marker_to_fsl(self): ) # with stroke - marker.setStrokeStyle(Qt.SolidLine) + marker.setStrokeStyle(Qt.PenStyle.SolidLine) marker.setStrokeColor(QColor(255, 100, 0)) marker.setStrokeWidth(2) marker.setStrokeWidthUnit(QgsUnitTypes.RenderPoints) @@ -786,7 +786,7 @@ def test_ellipse_marker_to_fsl(self): ) marker.setStrokeColor(QColor(255, 0, 255)) - marker.setStrokeStyle(Qt.NoPen) + marker.setStrokeStyle(Qt.PenStyle.NoPen) self.assertFalse( FslConverter.ellipse_marker_to_fsl(marker, conversion_context) ) @@ -818,7 +818,7 @@ def test_ellipse_marker_to_fsl(self): ) # with stroke - marker.setStrokeStyle(Qt.SolidLine) + marker.setStrokeStyle(Qt.PenStyle.SolidLine) marker.setStrokeColor(QColor(255, 100, 0)) marker.setStrokeWidth(2) marker.setStrokeWidthUnit(QgsUnitTypes.RenderPoints) @@ -1002,8 +1002,8 @@ def test_filled_marker(self): fill = QgsSimpleFillSymbolLayer(color=QColor(255, 0, 0)) # no brush, no stroke - fill.setBrushStyle(Qt.NoBrush) - fill.setStrokeStyle(Qt.NoPen) + fill.setBrushStyle(Qt.BrushStyle.NoBrush) + fill.setStrokeStyle(Qt.PenStyle.NoPen) fill_symbol.changeSymbolLayer(0, fill.clone()) marker = QgsFilledMarkerSymbolLayer() marker.setSubSymbol(fill_symbol.clone()) @@ -1012,7 +1012,7 @@ def test_filled_marker(self): ) # transparent color - fill.setBrushStyle(Qt.SolidPattern) + fill.setBrushStyle(Qt.BrushStyle.SolidPattern) fill.setColor(QColor(0, 255, 0, 0)) fill_symbol.changeSymbolLayer(0, fill.clone()) marker.setSubSymbol(fill_symbol.clone()) @@ -1033,7 +1033,7 @@ def test_filled_marker(self): 'strokeColor': 'rgba(0, 0, 0, 0)'}] ) - fill.setStrokeStyle(Qt.SolidLine) + fill.setStrokeStyle(Qt.PenStyle.SolidLine) fill_symbol.changeSymbolLayer(0, fill.clone()) marker.setSubSymbol(fill_symbol.clone()) self.assertEqual( @@ -1104,7 +1104,7 @@ def test_point_pattern_fill_to_fsl(self): ) marker.setStrokeColor(QColor(255, 0, 255)) - marker.setStrokeStyle(Qt.NoPen) + marker.setStrokeStyle(Qt.PenStyle.NoPen) marker_symbol.changeSymbolLayer(0, marker.clone()) fill.setSubSymbol(marker_symbol.clone()) self.assertFalse( @@ -1154,7 +1154,7 @@ def test_centroid_fill_to_fsl(self): ) marker.setStrokeColor(QColor(255, 0, 255)) - marker.setStrokeStyle(Qt.NoPen) + marker.setStrokeStyle(Qt.PenStyle.NoPen) marker_symbol.changeSymbolLayer(0, marker.clone()) fill.setSubSymbol(marker_symbol.clone()) self.assertFalse( @@ -1206,7 +1206,7 @@ def test_random_marker_fill_to_fsl(self): ) marker.setStrokeColor(QColor(255, 0, 255)) - marker.setStrokeStyle(Qt.NoPen) + marker.setStrokeStyle(Qt.PenStyle.NoPen) marker_symbol.changeSymbolLayer(0, marker.clone()) fill.setSubSymbol(marker_symbol.clone()) self.assertFalse( @@ -1257,7 +1257,7 @@ def test_marker_line_to_fsl(self): ) marker.setStrokeColor(QColor(255, 0, 255)) - marker.setStrokeStyle(Qt.NoPen) + marker.setStrokeStyle(Qt.PenStyle.NoPen) marker_symbol.changeSymbolLayer(0, marker.clone()) line.setSubSymbol(marker_symbol.clone()) self.assertFalse( @@ -1322,7 +1322,7 @@ def test_hashed_line_to_fsl(self): ) hatch.setColor(QColor(255, 0, 255)) - hatch.setPenStyle(Qt.NoPen) + hatch.setPenStyle(Qt.PenStyle.NoPen) hatch_symbol.changeSymbolLayer(0, hatch.clone()) line.setSubSymbol(hatch_symbol.clone()) self.assertFalse( @@ -1331,7 +1331,7 @@ def test_hashed_line_to_fsl(self): # with hatch hatch.setColor(QColor(120, 130, 140)) - hatch.setPenStyle(Qt.SolidLine) + hatch.setPenStyle(Qt.PenStyle.SolidLine) hatch_symbol.changeSymbolLayer(0, hatch.clone()) line.setSubSymbol(hatch_symbol.clone()) @@ -1379,7 +1379,7 @@ def test_arrow_to_fsl(self): fill = QgsSimpleFillSymbolLayer() # invisible fill fill.setColor(QColor(255, 0, 0, 0)) - fill.setStrokeStyle(Qt.NoPen) + fill.setStrokeStyle(Qt.PenStyle.NoPen) fill_symbol = QgsFillSymbol() fill_symbol.changeSymbolLayer(0, fill.clone()) @@ -1860,7 +1860,7 @@ def test_categorized_no_stroke(self): conversion_context = ConversionContext() fill = QgsSimpleFillSymbolLayer(color=QColor(255, 0, 0)) - fill.setStrokeStyle(Qt.NoPen) + fill.setStrokeStyle(Qt.PenStyle.NoPen) fill_symbol = QgsFillSymbol() fill_symbol.changeSymbolLayer(0, fill.clone()) @@ -1898,13 +1898,13 @@ def test_categorized_dash_array_for_one(self): conversion_context = ConversionContext() line = QgsSimpleLineSymbolLayer(color=QColor(255, 0, 0)) - line.setPenStyle(Qt.DashLine) + line.setPenStyle(Qt.PenStyle.DashLine) line_symbol = QgsLineSymbol() line_symbol.changeSymbolLayer(0, line.clone()) line_symbol2 = QgsLineSymbol() line.setColor(QColor(255, 0, 255)) - line.setPenStyle(Qt.SolidLine) + line.setPenStyle(Qt.PenStyle.SolidLine) line_symbol2.changeSymbolLayer(0, line.clone()) categories = [ @@ -2079,84 +2079,31 @@ def test_heatmap_renderer(self): conversion_context = ConversionContext() renderer = QgsHeatmapRenderer() - self.assertEqual( - FslConverter.vector_renderer_to_fsl(renderer, conversion_context), - {'legend': {'displayName': {'0': 'Low', '1': 'High'}}, - 'style': {'color': ['#ffffff', - '#f7f7f7', - '#eeeeee', - '#e6e6e6', - '#dddddd', - '#d5d5d5', - '#cccccc', - '#c3c3c3', - '#bbbbbb', - '#b3b3b3', - '#aaaaaa', - '#a2a2a2', - '#999999', - '#919191', - '#888888', - '#808080', - '#777777', - '#6f6f6f', - '#666666', - '#5e5e5e', - '#555555', - '#4d4d4d', - '#444444', - '#3b3b3b', - '#333333', - '#2a2a2a', - '#222222', - '#191919', - '#111111', - '#080808'], - 'intensity': 1, - 'opacity': 1, - 'size': 38}, - 'type': 'heatmap'} - ) - - self.assertEqual( - FslConverter.vector_renderer_to_fsl(renderer, conversion_context, - layer_opacity=0.5), - {'legend': {'displayName': {'0': 'Low', '1': 'High'}}, - 'style': {'color': ['#ffffff', - '#f7f7f7', - '#eeeeee', - '#e6e6e6', - '#dddddd', - '#d5d5d5', - '#cccccc', - '#c3c3c3', - '#bbbbbb', - '#b3b3b3', - '#aaaaaa', - '#a2a2a2', - '#999999', - '#919191', - '#888888', - '#808080', - '#777777', - '#6f6f6f', - '#666666', - '#5e5e5e', - '#555555', - '#4d4d4d', - '#444444', - '#3b3b3b', - '#333333', - '#2a2a2a', - '#222222', - '#191919', - '#111111', - '#080808'], - 'intensity': 1, - 'opacity': 0.5, - 'size': 38}, - 'type': 'heatmap'} - ) + result = FslConverter.vector_renderer_to_fsl( + renderer, conversion_context) + self.assertEqual(result['type'], 'heatmap') + self.assertEqual( + result['legend'], + {'displayName': {'0': 'Low', '1': 'High'}}) + style = result['style'] + self.assertEqual(len(style['color']), 30) + self.assertEqual(style['color'][0], '#ffffff') + self.assertEqual(style['intensity'], 1) + self.assertEqual(style['opacity'], 1) + self.assertEqual(style['size'], 38) + + result = FslConverter.vector_renderer_to_fsl( + renderer, conversion_context, layer_opacity=0.5) + self.assertEqual(result['type'], 'heatmap') + self.assertEqual( + result['legend'], + {'displayName': {'0': 'Low', '1': 'High'}}) + style = result['style'] + self.assertEqual(len(style['color']), 30) + self.assertEqual(style['color'][0], '#ffffff') + self.assertEqual(style['intensity'], 1) + self.assertEqual(style['opacity'], 0.5) + self.assertEqual(style['size'], 38) @unittest.skipIf(Qgis.QGIS_VERSION_INT < 32400, 'QGIS too old') def test_text_format_conversion(self):