diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..915a258 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,26 @@ +name: Semantic Release + +on: + push: + branches: + - main + +jobs: + release: + runs-on: ubuntu-24.04 + concurrency: release + permissions: + id-token: write + contents: write + + steps: + - uses: actions/checkout@v3 + with: + fetch-depth: 0 + + - name: Python Semantic Release + uses: python-semantic-release/python-semantic-release@v9.8.2 + with: + # Note: This token expires July 2025 + # See: https://python-semantic-release.readthedocs.io/en/latest/index.html#index-creating-vcs-releases + github_token: ${{ secrets.GH_TOKEN_FOR_SEMANTIC_CI_RELEASE }} \ No newline at end of file diff --git a/.tool-versions b/.tool-versions new file mode 100644 index 0000000..17b2499 --- /dev/null +++ b/.tool-versions @@ -0,0 +1,2 @@ +# Need to use system python to access QGIS libraries +python system diff --git a/__init__.py b/__init__.py index 57a203b..4c650fa 100644 --- a/__init__.py +++ b/__init__.py @@ -1,3 +1,5 @@ +__version__ = "0.0.0" + import logging from .plugin import OFDSDedupPlugin diff --git a/dev_requirements.in b/dev_requirements.in index ac46706..99c9a53 100644 --- a/dev_requirements.in +++ b/dev_requirements.in @@ -10,3 +10,6 @@ mypy>=1.8,<2 PyQt5-stubs qgis-stubs pip-tools +python-semantic-release +pyclean +jinja2-cli diff --git a/dev_requirements.txt b/dev_requirements.txt index 0760bdb..d7f06d4 100644 --- a/dev_requirements.txt +++ b/dev_requirements.txt @@ -1,9 +1,11 @@ # -# This file is autogenerated by pip-compile with Python 3.10 +# This file is autogenerated by pip-compile with Python 3.12 # by the following command: # # pip-compile dev_requirements.in # +annotated-types==0.7.0 + # via pydantic astor==0.8.1 # via flake8-qgis attrs==23.2.0 @@ -12,14 +14,22 @@ black==24.3.0 # via -r dev_requirements.in build==1.2.1 # via pip-tools +certifi==2024.6.2 + # via requests +charset-normalizer==3.3.2 + # via requests click==8.1.7 # via # black + # click-option-group # pip-tools + # python-semantic-release +click-option-group==0.5.6 + # via python-semantic-release coverage[toml]==7.4.4 # via pytest-cov -exceptiongroup==1.2.0 - # via pytest +dotty-dict==1.3.1 + # via python-semantic-release flake8==7.0.0 # via # -r dev_requirements.in @@ -29,12 +39,32 @@ flake8-bugbear==24.2.6 # via -r dev_requirements.in flake8-qgis==1.0.0 # via -r dev_requirements.in +gitdb==4.0.11 + # via gitpython +gitpython==3.1.43 + # via python-semantic-release +idna==3.7 + # via requests +importlib-resources==6.4.0 + # via python-semantic-release iniconfig==2.0.0 # via pytest isort==5.13.2 # via -r dev_requirements.in +jinja2==3.1.4 + # via + # jinja2-cli + # python-semantic-release +jinja2-cli==0.8.2 + # via -r dev_requirements.in +markdown-it-py==3.0.0 + # via rich +markupsafe==2.1.5 + # via jinja2 mccabe==0.7.0 # via flake8 +mdurl==0.1.2 + # via markdown-it-py mypy==1.9.0 # via -r dev_requirements.in mypy-extensions==1.0.0 @@ -54,10 +84,18 @@ platformdirs==4.2.0 # via black pluggy==1.4.0 # via pytest +pyclean==3.0.0 + # via -r dev_requirements.in pycodestyle==2.11.1 # via flake8 +pydantic==2.7.4 + # via python-semantic-release +pydantic-core==2.18.4 + # via pydantic pyflakes==3.2.0 # via flake8 +pygments==2.18.0 + # via rich pyproject-hooks==1.0.0 # via # build @@ -75,21 +113,34 @@ pytest-cov==5.0.0 # via -r dev_requirements.in pytest-qgis==2.0.0 # via -r dev_requirements.in +python-gitlab==4.6.0 + # via python-semantic-release +python-semantic-release==9.8.2 + # via -r dev_requirements.in qgis-stubs==0.2.0.post1 # via -r dev_requirements.in -tomli==2.0.1 +requests==2.32.3 # via - # black - # build - # coverage - # mypy - # pip-tools - # pyproject-hooks - # pytest + # python-gitlab + # python-semantic-release + # requests-toolbelt +requests-toolbelt==1.0.0 + # via python-gitlab +rich==13.7.1 + # via python-semantic-release +shellingham==1.5.4 + # via python-semantic-release +smmap==5.0.1 + # via gitdb +tomlkit==0.12.5 + # via python-semantic-release typing-extensions==4.11.0 # via - # black # mypy + # pydantic + # pydantic-core +urllib3==2.2.1 + # via requests wheel==0.43.0 # via pip-tools diff --git a/gui_style.py b/gui_style.py new file mode 100644 index 0000000..e6fad9b --- /dev/null +++ b/gui_style.py @@ -0,0 +1,73 @@ +# -*- coding: utf-8 -*- + +# Form implementation generated from reading ui file 'gui_style.ui' +# +# Created by: PyQt5 UI code generator 5.15.10 +# +# WARNING: Any manual changes made to this file will be lost when pyuic5 is +# run again. Do not edit this file unless you know what you are doing. + + +from PyQt5 import QtCore, QtGui, QtWidgets + + +class Ui_StyleOFDSDialog(object): + def setupUi(self, StyleOFDSDialog): + StyleOFDSDialog.setObjectName("StyleOFDSDialog") + StyleOFDSDialog.resize(457, 161) + self.gridLayout_2 = QtWidgets.QGridLayout(StyleOFDSDialog) + self.gridLayout_2.setObjectName("gridLayout_2") + self.gridLayout = QtWidgets.QGridLayout() + self.gridLayout.setObjectName("gridLayout") + self.spansComboBox = QtWidgets.QComboBox(StyleOFDSDialog) + self.spansComboBox.setObjectName("spansComboBox") + self.gridLayout.addWidget(self.spansComboBox, 3, 1, 1, 1) + self.line = QtWidgets.QFrame(StyleOFDSDialog) + self.line.setFrameShape(QtWidgets.QFrame.HLine) + self.line.setFrameShadow(QtWidgets.QFrame.Sunken) + self.line.setObjectName("line") + self.gridLayout.addWidget(self.line, 6, 2, 1, 1) + self.nodesLabel = QtWidgets.QLabel(StyleOFDSDialog) + self.nodesLabel.setMaximumSize(QtCore.QSize(100, 16777215)) + self.nodesLabel.setObjectName("nodesLabel") + self.gridLayout.addWidget(self.nodesLabel, 2, 0, 1, 1) + self.nodesComboBox = QtWidgets.QComboBox(StyleOFDSDialog) + self.nodesComboBox.setObjectName("nodesComboBox") + self.gridLayout.addWidget(self.nodesComboBox, 2, 1, 1, 1) + self.closeButton = QtWidgets.QPushButton(StyleOFDSDialog) + self.closeButton.setObjectName("closeButton") + self.gridLayout.addWidget(self.closeButton, 7, 2, 1, 1) + self.spansApplyButton = QtWidgets.QPushButton(StyleOFDSDialog) + self.spansApplyButton.setMaximumSize(QtCore.QSize(100, 16777215)) + self.spansApplyButton.setObjectName("spansApplyButton") + self.gridLayout.addWidget(self.spansApplyButton, 3, 2, 1, 1) + self.infoLabel = QtWidgets.QLabel(StyleOFDSDialog) + self.infoLabel.setObjectName("infoLabel") + self.gridLayout.addWidget(self.infoLabel, 0, 0, 1, 3) + self.nodesApplyButton = QtWidgets.QPushButton(StyleOFDSDialog) + self.nodesApplyButton.setMaximumSize(QtCore.QSize(100, 16777215)) + self.nodesApplyButton.setObjectName("nodesApplyButton") + self.gridLayout.addWidget(self.nodesApplyButton, 2, 2, 1, 1) + self.spansLabel = QtWidgets.QLabel(StyleOFDSDialog) + self.spansLabel.setMaximumSize(QtCore.QSize(100, 16777215)) + self.spansLabel.setObjectName("spansLabel") + self.gridLayout.addWidget(self.spansLabel, 3, 0, 1, 1) + self.refreshButton = QtWidgets.QPushButton(StyleOFDSDialog) + self.refreshButton.setObjectName("refreshButton") + self.gridLayout.addWidget(self.refreshButton, 7, 1, 1, 1) + self.gridLayout_2.addLayout(self.gridLayout, 1, 0, 1, 1) + + self.retranslateUi(StyleOFDSDialog) + self.closeButton.pressed.connect(StyleOFDSDialog.close) # type: ignore + QtCore.QMetaObject.connectSlotsByName(StyleOFDSDialog) + + def retranslateUi(self, StyleOFDSDialog): + _translate = QtCore.QCoreApplication.translate + StyleOFDSDialog.setWindowTitle(_translate("StyleOFDSDialog", "Style OFDS")) + self.nodesLabel.setText(_translate("StyleOFDSDialog", "Nodes Layer")) + self.closeButton.setText(_translate("StyleOFDSDialog", "Close")) + self.spansApplyButton.setText(_translate("StyleOFDSDialog", "Apply")) + self.infoLabel.setText(_translate("StyleOFDSDialog", "Apply styles to OFDS layers")) + self.nodesApplyButton.setText(_translate("StyleOFDSDialog", "Apply")) + self.spansLabel.setText(_translate("StyleOFDSDialog", "Spans Layer")) + self.refreshButton.setText(_translate("StyleOFDSDialog", "Refresh Layers")) diff --git a/gui_style.ui b/gui_style.ui new file mode 100644 index 0000000..b5bc594 --- /dev/null +++ b/gui_style.ui @@ -0,0 +1,128 @@ + + + StyleOFDSDialog + + + + 0 + 0 + 457 + 161 + + + + Style OFDS + + + + + + + + + + + Qt::Horizontal + + + + + + + + 100 + 16777215 + + + + Nodes Layer + + + + + + + + + + Close + + + + + + + + 100 + 16777215 + + + + Apply + + + + + + + Apply styles to OFDS layers + + + + + + + + 100 + 16777215 + + + + Apply + + + + + + + + 100 + 16777215 + + + + Spans Layer + + + + + + + Refresh Layers + + + + + + + + + + + closeButton + pressed() + StyleOFDSDialog + close() + + + 399 + 136 + + + 228 + 80 + + + + + diff --git a/metadata.txt b/metadata.txt index 7034962..5555c11 100644 --- a/metadata.txt +++ b/metadata.txt @@ -1,9 +1,9 @@ -[general] -name=OFDS Consolidation Tool -description=A tool to consolidate multiple data sets formatted using the Open Fibre Data Standard -about=A tool to consolidate multiple data sets formatted using the Open Fibre Data Standard -version=0.1 -qgisMinimumVersion=3.28 -author=ODSC -email=code@opendataservices.coop +[general] +name=OFDS Consolidation Tool +description=A tool to consolidate multiple data sets formatted using the Open Fibre Data Standard +about=A tool to consolidate multiple data sets formatted using the Open Fibre Data Standard +version=0.0.0 +qgisMinimumVersion=3.28 +author=ODSC +email=code@opendataservices.coop repository=https://github.com/Open-Telecoms-Data/ofds_consolidation_tool \ No newline at end of file diff --git a/plugin.py b/plugin.py index 1e9d849..7337f80 100644 --- a/plugin.py +++ b/plugin.py @@ -4,28 +4,54 @@ from PyQt5.QtCore import PYQT_VERSION_STR, QT_VERSION_STR from PyQt5.QtWidgets import QAction from qgis.core import QgsProject -from qgis.utils import iface # type: ignore +from qgis.gui import QgisInterface +from qgis.utils import iface + +iface: QgisInterface from .tool.tool import OFDSDedupToolDialog +from .tool.style import OFDSStyleToolDialog +from . import __version__ logger = logging.getLogger(__name__) class OFDSDedupPlugin: def __init__(self): - self.tool_dialog = OFDSDedupToolDialog() + self.consolidate_tool_dialog = OFDSDedupToolDialog() + self.style_tool_dialog = OFDSStyleToolDialog() def initGui(self): - self.action = QAction("Consolidate OFDS", iface.mainWindow()) - self.action.triggered.connect(self.run) - iface.addToolBarIcon(self.action) + self.action_consolidate = QAction("Consolidate OFDS", iface.mainWindow()) + self.action_consolidate.setObjectName("consolidateOfdsAction") + self.action_consolidate.triggered.connect(self.run_consolidate) + iface.addToolBarIcon(self.action_consolidate) + iface.addPluginToMenu("&OFDS", self.action_consolidate) + + self.action_style = QAction("Style OFDS", iface.mainWindow()) + self.action_style.setObjectName("styleOfdsAction") + self.action_style.triggered.connect(self.run_style) + iface.addToolBarIcon(self.action_style) + iface.addPluginToMenu("&OFDS", self.action_style) def unload(self): - iface.removeToolBarIcon(self.action) - del self.action + iface.removePluginMenu("&OFDS", self.action_consolidate) + iface.removeToolBarIcon(self.action_consolidate) + del self.action_consolidate + + iface.removePluginMenu("&OFDS", self.action_style) + iface.removeToolBarIcon(self.action_style) + del self.action_style + + self.consolidate_tool_dialog.close() + del self.consolidate_tool_dialog + + self.style_tool_dialog.close() + del self.style_tool_dialog - def run(self): - logger.debug("Running OFDS Consolidation plugin") + def run_consolidate(self): + logger.debug("Opening OFDS Consolidate Tool Dialog") + logger.debug(f"OFDS Consolidation Tool Plugin v{__version__}") logger.debug( f"Qt: v{QT_VERSION_STR} PyQt: v{PYQT_VERSION_STR} Python: {sys.version}" ) @@ -34,5 +60,15 @@ def run(self): if not project: raise Exception - self.tool_dialog.reset(project=project) - self.tool_dialog.show() + self.consolidate_tool_dialog.reset(project=project) + self.consolidate_tool_dialog.show() + + def run_style(self): + logger.debug("Opening OFDS Style Tool Dialog") + logger.debug(f"OFDS Consolidation Tool Plugin v{__version__}") + project = QgsProject.instance() + if not project: + raise Exception + + self.style_tool_dialog.refresh_layers() + self.style_tool_dialog.show() diff --git a/pyproject.toml b/pyproject.toml index 64ce2f9..ac71535 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,29 +4,25 @@ version = "0.1.0" requires-python = ">=3.8" authors = [ - {name = "ODSC", email = "code@opendataservices.coop"} + { name = "ODSC", email = "code@opendataservices.coop" } ] description = "A tool to consolidate multiple fibre network datasets formatted using the Open Fibre Data Standard" readme = "README.md" -license = {file = "LICENSE"} +license = { file = "LICENSE" } keywords = ["open", "fibre", "data", "standard", "ofds", "consolidation", "tool", "gis", "qgis"] # See: https://pypi.org/classifiers/ classifiers = [ # Development Status - "Development Status :: 2 - Pre-Alpha", - + "Development Status :: 3 - Alpha", # Environment "Environment :: Plugins", - # Indicate who your project is intended for "Intended Audience :: Telecommunications Industry", "Topic :: Desktop Environment", "Topic :: Scientific/Engineering :: GIS", - # License "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", - # Specify the Python versions you support here. "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.8", @@ -46,7 +42,7 @@ testpaths = [ [tool.black] line-length = 88 -force-exclude = "gui.py" # gui.py is generated by pyuic5, so don't format it +force-exclude = "gui(_style)?.py" # gui.py is generated by pyuic5, so don't format it [tool.isort] # https://black.readthedocs.io/en/stable/guides/using_black_with_other_tools.html#isort @@ -60,5 +56,63 @@ warn_unused_configs = true check_untyped_defs = true exclude = [ "gui.py", + "gui_style.py", "_lib/jellyfish/_jellyfish.py", -] \ No newline at end of file +] + +[tool.semantic_release] +assets = [] +build_command = "/bin/bash ./scripts/build-package.sh" +build_command_env = [] +commit_message = "{version}\n\nAutomatically generated by python-semantic-release" +commit_parser = "angular" +logging_use_named_masks = false +major_on_zero = true +allow_zero_version = true +no_git_verify = false +tag_format = "v{version}" + +[tool.semantic_release.branches.main] +match = '(main|master|rg/release)' +prerelease_token = "rc" +prerelease = false + +[tool.semantic_release.changelog] +template_dir = "templates" +changelog_file = "CHANGELOG.md" +exclude_commit_patterns = [] + +[tool.semantic_release.changelog.environment] +block_start_string = "{%" +block_end_string = "%}" +variable_start_string = "{{" +variable_end_string = "}}" +comment_start_string = "{#" +comment_end_string = "#}" +trim_blocks = false +lstrip_blocks = false +newline_sequence = "\n" +keep_trailing_newline = false +extensions = [] +autoescape = true + +[tool.semantic_release.commit_author] +env = "GIT_COMMIT_AUTHOR" +default = "semantic-release " + +[tool.semantic_release.commit_parser_options] +allowed_tags = ["build", "chore", "ci", "docs", "feat", "fix", "perf", "style", "refactor", "test"] +minor_tags = ["feat"] +patch_tags = ["fix", "perf"] +default_bump_level = 0 + +[tool.semantic_release.remote] +name = "origin" +type = "github" +ignore_token_for_push = false +insecure = false + +[tool.semantic_release.publish] +dist_glob_patterns = ["dist/*"] +upload_to_vcs_release = true + diff --git a/resources.py b/resources.py new file mode 100644 index 0000000..7764417 --- /dev/null +++ b/resources.py @@ -0,0 +1,10 @@ +from pathlib import Path + +# Paths to resources + +PLUGIN_DIR = Path(__file__).resolve().parent + +STYLESHEETS_DIR = PLUGIN_DIR / "tool" / "stylesheets" + +STYLESHEET_NODES = STYLESHEETS_DIR / "networkProviderNodesMultiplePlainOutput.qml" +STYLESHEET_SPANS = STYLESHEETS_DIR / "networkProviderSpansMultiplePlainOutput.qml" diff --git a/scripts/build-package.sh b/scripts/build-package.sh new file mode 100755 index 0000000..239586b --- /dev/null +++ b/scripts/build-package.sh @@ -0,0 +1,53 @@ +#!/bin/bash +set -euo pipefail + +NEW_VERSION="${NEW_VERSION:?Error: NEW_VERSION not set}" + +# Update metadata.txt with the new version +jinja2 --strict -D new_version="${NEW_VERSION}" templates/.metadata.txt.j2 > metadata.txt + +# Create a QGIS-compatible Plugin and +# Copy our files into the plugin dir. +# Note the directory name must not conflict with other plugins. + +ZIPDIR="dist/ofds_consolidation_tool/" + +# Check we won't accidentally overwrite stuff +if [ -d "$ZIPDIR" ]; then + echo "Error: temporary directory already exists. Are you sure you're running this in project root?" + exit 1 +fi + +# Make our dist dir if it doesn't exist +mkdir -p "$ZIPDIR" + +# Clean up __pycache__ and .pyc files +pyclean . || true + + +# Copy in relevant files +cp -r \ + "metadata.txt" \ + "README.md" \ + "LICENSE" \ + "docs/" \ + "pyproject.toml" \ + "tests/" \ + "dev_requirements.in" \ + "dev_requirements.txt" \ + "tool/" \ + "gui.py" \ + "gui_style.py" \ + "helpers.py" \ + "ofds.py" \ + "plugin.py" \ + "resources.py" \ + "$ZIPDIR" + +pushd dist/ +# Create the Zip +zip -r "OFDS_Consolidation_Tool_QGIS_Plugin_v${NEW_VERSION}.zip" ofds_consolidation_tool/ + +# Clean up temporary dir +rm -r ofds_consolidation_tool/ +popd diff --git a/scripts/compile-gui.sh b/scripts/compile-gui.sh index e8b9ec1..d65eaf1 100755 --- a/scripts/compile-gui.sh +++ b/scripts/compile-gui.sh @@ -2,3 +2,4 @@ set -euo pipefail pyuic5 gui.ui -o gui.py +pyuic5 gui_style.ui -o gui_style.py diff --git a/templates/.metadata.txt.j2 b/templates/.metadata.txt.j2 new file mode 100644 index 0000000..bb32a3f --- /dev/null +++ b/templates/.metadata.txt.j2 @@ -0,0 +1,9 @@ +[general] +name=OFDS Consolidation Tool +description=A tool to consolidate multiple data sets formatted using the Open Fibre Data Standard +about=A tool to consolidate multiple data sets formatted using the Open Fibre Data Standard +version={{ new_version }} +qgisMinimumVersion=3.28 +author=ODSC +email=code@opendataservices.coop +repository=https://github.com/Open-Telecoms-Data/ofds_consolidation_tool \ No newline at end of file diff --git a/tool/style.py b/tool/style.py new file mode 100644 index 0000000..54912fe --- /dev/null +++ b/tool/style.py @@ -0,0 +1,86 @@ +import logging +from typing import List, cast + +from PyQt5.QtWidgets import QDialog +from qgis.core import ( + QgsProject, + QgsMapLayer, + QgsWkbTypes, + QgsVectorLayer, + QgsMapLayerType, +) + +from ..resources import STYLESHEET_NODES, STYLESHEET_SPANS +from ..gui_style import Ui_StyleOFDSDialog + +logger = logging.getLogger(__name__) + + +class OFDSStyleToolDialog(QDialog): + """ + The top-level GUI element, the Dialog box that holds all our other sub-elements. + + Note that there is a singleton instance of this class, it isn't created/destroyed + each time we open the tool, but it remains constantly "open" in the backgroud even + when closed or hidden, and all state is still the same next time the tool is opened + from the toolbar. This means it's important to properly tidy up the GUI and any + created/opened resources when we're done. + """ + + ui: Ui_StyleOFDSDialog + + def __init__(self): + super().__init__() + + self.ui = Ui_StyleOFDSDialog() + self.ui.setupUi(self) + + # Connect UI signals to slots on this class + self.ui.nodesApplyButton.pressed.connect(self.apply_nodes_style) + self.ui.spansApplyButton.pressed.connect(self.apply_spans_style) + self.ui.refreshButton.pressed.connect(self.refresh_layers) + + def refresh_layers(self): + """Refresh and populate the lists of layers from the current Project""" + + project = QgsProject.instance() + + # Find all the layers the user has loaded + all_layers: List[QgsMapLayer] = project.mapLayers(validOnly=True).values() + + # Filter the layers that the tool can accept + selectable_layers: List[QgsVectorLayer] = list() + + for layer in all_layers: + # only vector layers can be valid OFDS layers + if layer.type() == QgsMapLayerType.VectorLayer: + selectable_layers.append(cast(QgsVectorLayer, layer)) + + ncb = self.ui.nodesComboBox + scb = self.ui.spansComboBox + + ncb.clear() + for layer in selectable_layers: + if layer.geometryType() == QgsWkbTypes.GeometryType.PointGeometry: + ncb.addItem(layer.name(), layer) + + scb.clear() + for layer in selectable_layers: + if layer.geometryType() == QgsWkbTypes.GeometryType.LineGeometry: + scb.addItem(layer.name(), layer) + + def apply_nodes_style(self): + """Apply the Nodes style to the currently selected Nodes layer""" + nodes_layer = self.ui.nodesComboBox.currentData() + if isinstance(nodes_layer, QgsVectorLayer): + if nodes_layer.geometryType() == QgsWkbTypes.GeometryType.PointGeometry: + nodes_layer.loadNamedStyle(STYLESHEET_NODES.as_posix(), False) + nodes_layer.triggerRepaint() + + def apply_spans_style(self): + """Apply the Spans style to the currently selected Spans layer""" + spans_layer = self.ui.spansComboBox.currentData() + if isinstance(spans_layer, QgsVectorLayer): + if spans_layer.geometryType() == QgsWkbTypes.GeometryType.LineGeometry: + spans_layer.loadNamedStyle(STYLESHEET_SPANS.as_posix(), False) + spans_layer.triggerRepaint() diff --git a/tool/view.py b/tool/view.py index 3bdc49b..3804614 100644 --- a/tool/view.py +++ b/tool/view.py @@ -1,6 +1,7 @@ import logging -from typing import List, Optional, Tuple, Union from dataclasses import dataclass +from typing import List, Optional, Tuple, Union + from PyQt5.QtWidgets import ( QTextEdit, QPushButton, @@ -24,9 +25,6 @@ NodeComparison, SpanComparison, ) -from ..gui import Ui_OFDSDedupToolDialog - -from ..helpers import EPSG3857, getOpenStreetMapLayer from .model.network import FeatureType from .viewmodel.state import ( ToolLayerSelectState, @@ -36,6 +34,9 @@ ToolState, ToolStateEnum, ) +from ..gui import Ui_OFDSDedupToolDialog +from ..helpers import EPSG3857, getOpenStreetMapLayer +from ..resources import STYLESHEET_NODES, STYLESHEET_SPANS logger = logging.getLogger(__name__) @@ -554,6 +555,9 @@ def __init__(self, project: QgsProject, mapCanvas: QgsMapCanvas): def update(self, state: Optional[ToolState]): if isinstance(state, ToolOutputState): + state.output_network.nodesLayer.loadNamedStyle(STYLESHEET_NODES.as_posix()) + state.output_network.spansLayer.loadNamedStyle(STYLESHEET_SPANS.as_posix()) + self.minimapview.update( MiniMapView.State( nodesLayer=state.output_network.nodesLayer,