Skip to content

Commit

Permalink
Refactor Python code to be testable, add first basic test
Browse files Browse the repository at this point in the history
  • Loading branch information
Gustry committed Jun 20, 2024
1 parent c7ba841 commit 3907771
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 77 deletions.
13 changes: 13 additions & 0 deletions dynamic_layers/definitions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
__copyright__ = 'Copyright 2024, 3Liz'
__license__ = 'GPL version 3'
__email__ = 'info@3liz.org'

from qgis.PyQt.QtGui import QColor

GREEN = QColor(175, 208, 126)


class CustomProperty:
DynamicDatasourceActive = 'dynamicDatasourceActive'
DynamicDatasourceContent = 'dynamicDatasourceContent'

83 changes: 37 additions & 46 deletions dynamic_layers/dynamic_layers.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,16 +26,15 @@
from functools import partial

from qgis.PyQt.QtCore import Qt, QSettings, QTranslator, QCoreApplication
from qgis.PyQt.QtGui import QAction, QIcon, QTextCursor, QColor
from qgis.PyQt.QtGui import QAction, QIcon, QTextCursor
from qgis.PyQt.QtWidgets import qApp, QMessageBox, QTableWidgetItem
from qgis.core import Qgis, QgsMapLayer, QgsIconUtils, QgsProject
from qgis.utils import OverrideCursor

from dynamic_layers.dynamic_layers_dialog import DynamicLayersDialog
from dynamic_layers.dynamic_layers_engine import DynamicLayersEngine
from dynamic_layers.tools import resources_path

GREEN = QColor(175, 208, 126)
from dynamic_layers.definitions import GREEN, CustomProperty


class DynamicLayers:
Expand Down Expand Up @@ -69,6 +68,8 @@ def __init__(self, iface):

QCoreApplication.installTranslator(self.translator)

self.project = QgsProject.instance()

# Create the dialog (after translation) and keep reference
self.dlg = DynamicLayersDialog()

Expand Down Expand Up @@ -342,7 +343,7 @@ def populate_layer_table(self):
self.dlg.twLayers.setHorizontalHeaderLabels(tuple(columns))

# load content from project layers
for layer in QgsProject.instance().mapLayers().values():
for layer in self.project.mapLayers().values():

line_data = []

Expand All @@ -353,7 +354,7 @@ def populate_layer_table(self):
self.dlg.twLayers.setColumnCount(col_count)
i = 0

if layer.customProperty('dynamicDatasourceActive') == str(True):
if layer.customProperty(CustomProperty.DynamicDatasourceActive) == str(True):
bg = GREEN
else:
bg = Qt.transparent
Expand Down Expand Up @@ -402,11 +403,11 @@ def get_layer_property(layer: QgsMapLayer, prop: str) -> str | None:
elif prop == 'uri':
return layer.dataProvider().dataSourceUri().split('|')[0]

elif prop == 'dynamicDatasourceActive':
return layer.customProperty('dynamicDatasourceActive')
elif prop == CustomProperty.DynamicDatasourceActive:
return layer.customProperty(CustomProperty.DynamicDatasourceActive)

elif prop == 'dynamicDatasourceContent':
return layer.customProperty('dynamicDatasourceContent')
elif prop == CustomProperty.DynamicDatasourceContent:
return layer.customProperty(CustomProperty.DynamicDatasourceContent)

else:
return None
Expand Down Expand Up @@ -435,8 +436,7 @@ def on_row_selection_changed(self):

# Get layer
layer_id = self.dlg.twLayers.item(row, 0).data(Qt.EditRole)
lr = QgsProject.instance()
layer = lr.mapLayer(layer_id)
layer = self.project.mapLayer(layer_id)
if not layer:
show_layer_properties = False
else:
Expand Down Expand Up @@ -470,7 +470,7 @@ def on_row_selection_changed(self):
widget.setCurrentIndex(list_dic[val])

# "active" checkbox
is_active = layer.customProperty('dynamicDatasourceActive') == str(True)
is_active = layer.customProperty(CustomProperty.DynamicDatasourceActive) == str(True)
self.dlg.cbDatasourceActive.setChecked(is_active)

def on_cb_datasource_active_change(self):
Expand Down Expand Up @@ -507,8 +507,8 @@ def on_cb_datasource_active_change(self):
self.dlg.twLayers.item(row, 2).setData(Qt.EditRole, input_value)

# Record the new value in the project
self.selectedLayer.setCustomProperty('dynamicDatasourceActive', input_value)
QgsProject.instance().setDirty(True)
self.selectedLayer.setCustomProperty(CustomProperty.DynamicDatasourceActive, input_value)
self.project.setDirty(True)

def on_layer_property_change(self, key: str):
"""
Expand All @@ -533,7 +533,7 @@ def on_layer_property_change(self, key: str):

# Record the new value in the project
self.selectedLayer.setCustomProperty(item['xml'], input_value)
QgsProject.instance().setDirty(True)
self.project.setDirty(True)

def on_copy_from_layer(self):
"""
Expand Down Expand Up @@ -590,7 +590,7 @@ def populate_variable_table(self):
Fill the variable table
"""
# Get the list of variable from the project
variable_list = QgsProject.instance().readListEntry('PluginDynamicLayers', 'VariableList')
variable_list = self.project.readListEntry('PluginDynamicLayers', 'VariableList')
if not variable_list:
return

Expand Down Expand Up @@ -672,9 +672,8 @@ def on_add_variable_clicked(self):
self.variableList.append(v_name)

# Add variable to the project
project = QgsProject.instance()
project.writeEntry('PluginDynamicLayers', 'VariableList', self.variableList)
project.setDirty(True)
self.project.writeEntry('PluginDynamicLayers', 'VariableList', self.variableList)
self.project.setDirty(True)

def on_remove_variable_clicked(self):
"""
Expand All @@ -698,9 +697,8 @@ def on_remove_variable_clicked(self):
self.variableList.remove(v_name)

# Update project
p = QgsProject.instance()
p.writeEntry('PluginDynamicLayers', 'VariableList', self.variableList)
p.setDirty(True)
self.project.writeEntry('PluginDynamicLayers', 'VariableList', self.variableList)
self.project.setDirty(True)

# Remove selected lines
self.dlg.twVariableList.removeRow(self.dlg.twVariableList.currentRow())
Expand Down Expand Up @@ -740,21 +738,20 @@ def on_copy_from_project_clicked(self):
return

# Check if project has got some WMS capabilities
project = QgsProject.instance()

# Title
p_title = ''
if project.readEntry('ProjectTitle', '/PluginDynamicLayers'):
p_title = project.readEntry('ProjectTitle', '/PluginDynamicLayers')[0]
if not p_title and project.readEntry('WMSServiceTitle', "/"):
p_title = project.readEntry('WMSServiceTitle', "/")[0]
if self.project.readEntry('ProjectTitle', '/PluginDynamicLayers'):
p_title = self.project.readEntry('ProjectTitle', '/PluginDynamicLayers')[0]
if not p_title and self.project.readEntry('WMSServiceTitle', "/"):
p_title = self.project.readEntry('WMSServiceTitle', "/")[0]

# Abstract
p_abstract = ''
if project.readEntry('ProjectAbstract', '/PluginDynamicLayers'):
p_abstract = project.readEntry('ProjectAbstract', '/PluginDynamicLayers')[0]
if not p_abstract and project.readEntry('WMSServiceAbstract', "/"):
p_abstract = project.readEntry('WMSServiceAbstract', "/")[0]
if self.project.readEntry('ProjectAbstract', '/PluginDynamicLayers'):
p_abstract = self.project.readEntry('ProjectAbstract', '/PluginDynamicLayers')[0]
if not p_abstract and self.project.readEntry('WMSServiceAbstract', "/"):
p_abstract = self.project.readEntry('WMSServiceAbstract', "/")[0]

ask = False
previous_title = self.dlg.inProjectTitle.text()
Expand All @@ -778,8 +775,8 @@ def on_copy_from_project_clicked(self):
if result == QMessageBox.No:
return

if not project.readEntry('WMSServiceCapabilities', "/")[1]:
project.writeEntry('WMSServiceCapabilities', "/", str(True))
if not self.project.readEntry('WMSServiceCapabilities', "/")[1]:
self.project.writeEntry('WMSServiceCapabilities', "/", str(True))

self.dlg.inProjectTitle.setText(p_title)
self.dlg.inProjectAbstract.setText(p_abstract)
Expand Down Expand Up @@ -810,26 +807,21 @@ def on_project_property_changed(self, prop: str) -> str | None:
else:
return

p = QgsProject.instance()

# Store value into the project
xml = self.projectPropertiesInputs[prop]['xml']
p.writeEntry('PluginDynamicLayers', xml, val)
p.setDirty(True)
self.project.writeEntry('PluginDynamicLayers', xml, val)
self.project.setDirty(True)

def populate_project_properties(self):
"""
Fill in the project properties item
from XML
"""

p = QgsProject.instance()
# lr = QgsProject.instance()
# Fill the property from the PluginDynamicLayers XML
for prop, item in self.projectPropertiesInputs.items():
widget = item['widget']
xml = self.projectPropertiesInputs[prop]['xml']
val = p.readEntry('PluginDynamicLayers', xml)
val = self.project.readEntry('PluginDynamicLayers', xml)
if val:
val = val[0]
if not val:
Expand Down Expand Up @@ -862,7 +854,7 @@ def on_apply_variables_clicked(self, source: str = 'table'):
dle = DynamicLayersEngine()

# Set the dynamic layers list
dle.set_dynamic_layers_list()
dle.set_dynamic_layers_list(self.project)

# Set search and replace dictionary
# Collect variables names and values
Expand All @@ -882,7 +874,7 @@ def on_apply_variables_clicked(self, source: str = 'table'):
dle.set_dynamic_layers_datasource_from_dic()

# Set project properties
dle.set_dynamic_project_properties()
dle.set_dynamic_project_properties(self.project)

# Set extent layer
extent_layer = self.dlg.inExtentLayer.currentLayer()
Expand All @@ -895,11 +887,10 @@ def on_apply_variables_clicked(self, source: str = 'table'):
dle.set_extent_margin(extent_margin)

# Set new extent
dle.set_project_extent()
dle.set_project_extent(self.project)

# Set project as dirty
p = QgsProject.instance()
p.setDirty(True)
self.project.setDirty(True)

def run(self):
"""Run method that performs all the real work"""
Expand Down
60 changes: 29 additions & 31 deletions dynamic_layers/dynamic_layers_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@
QgsRectangle,
)

from dynamic_layers.definitions import CustomProperty

try:
from qgis.utils import iface
except Exception:
Expand Down Expand Up @@ -87,8 +89,8 @@ def __init__(
return

self.layer = layer
self.dynamic_datasource_active = layer.customProperty('dynamicDatasourceActive') == str(True)
self.dynamic_datasource_content = layer.customProperty('dynamicDatasourceContent')
self.dynamic_datasource_active = layer.customProperty(CustomProperty.DynamicDatasourceActive) == str(True)
self.dynamic_datasource_content = layer.customProperty(CustomProperty.DynamicDatasourceContent)

def set_new_source_uri_from_dict(self, search_and_replace_dictionary: dict = None):
"""
Expand Down Expand Up @@ -296,16 +298,15 @@ def set_search_and_replace_dictionary_from_layer(self, layer: QgsVectorLayer, ex

self.search_and_replace_dictionary = search_and_replace_dictionary

def set_dynamic_layers_list(self):
def set_dynamic_layers_list(self, project: QgsProject):
"""
Add the passed layers to the dynamic layers dictionary
"""
# Get the layers with dynamicDatasourceActive enable
lr = QgsProject.instance()
self.dynamic_layers = {
lid: layer for lid, layer in lr.mapLayers().items() if
layer.customProperty('dynamicDatasourceActive') == str(True) and layer.customProperty(
'dynamicDatasourceContent')
lid: layer for lid, layer in project.mapLayers().items() if
layer.customProperty(CustomProperty.DynamicDatasourceActive) == str(True) and layer.customProperty(
CustomProperty.DynamicDatasourceContent)
}

def set_dynamic_layers_datasource_from_dic(self):
Expand All @@ -323,69 +324,66 @@ def set_dynamic_layers_datasource_from_dic(self):
a = LayerDataSourceModifier(layer)
a.set_new_source_uri_from_dict(self.search_and_replace_dictionary)

if self.iface and layer.renderer() and layer.renderer().type() == 'graduatedSymbol':
if not self.iface:
continue

if layer.renderer() and layer.renderer().type() == 'graduatedSymbol':
layer.triggerRepaint()

if self.iface:
self.iface.actionDraw().trigger()
self.iface.mapCanvas().refresh()
if not self.iface:
return

self.iface.actionDraw().trigger()
self.iface.mapCanvas().refresh()

def set_dynamic_project_properties(self, title: str = None, abstract: str = None):
def set_dynamic_project_properties(self, project: QgsProject, title: str = None, abstract: str = None):
"""
Set some project properties : title, abstract
based on the templates stored in the project file in <PluginDynamicLayers>
and by using the search and replace dictionary
"""
# Get project instance
p = QgsProject.instance()

# Make sure WMS Service is active
if not p.readEntry('WMSServiceCapabilities', "/")[1]:
p.writeEntry('WMSServiceCapabilities', "/", "True")
if not project.readEntry('WMSServiceCapabilities', "/")[1]:
project.writeEntry('WMSServiceCapabilities', "/", "True")

# title
if not title:
xml = 'ProjectTitle'
val = p.readEntry('PluginDynamicLayers', xml)
val = project.readEntry('PluginDynamicLayers', xml)
if val:
title = val[0]
self.set_project_property('title', title)
self.set_project_property(project, 'title', title)

# abstract
if not abstract:
xml = 'ProjectAbstract'
val = p.readEntry('PluginDynamicLayers', xml)
val = project.readEntry('PluginDynamicLayers', xml)
if val:
abstract = val[0]
self.set_project_property('abstract', abstract)
self.set_project_property(project, 'abstract', abstract)

def set_project_property(self, prop: str, val: str):
def set_project_property(self, project: QgsProject, prop: str, val: str):
"""
Set a project property
And replace variable if found in the properties
"""
# Get project instance
p = QgsProject.instance()

# Replace variable in given val via dictionary
t = DynamicLayersTools()
val = t.search_and_replace_string_by_dictionary(val, self.search_and_replace_dictionary)

# Title
if prop == 'title':
p.writeEntry('WMSServiceTitle', '', val)
project.writeEntry('WMSServiceTitle', '', val)

# Abstract
elif prop == 'abstract':
p.writeEntry('WMSServiceAbstract', '',val)
project.writeEntry('WMSServiceAbstract', '', val)

def set_project_extent(self) -> QgsRectangle:
def set_project_extent(self, project: QgsProject) -> QgsRectangle:
"""
Sets the project extent
and corresponding XML property
"""
p = QgsProject.instance()

# Get extent from extent layer (if given)
p_extent = None
if self.extent_layer:
Expand Down Expand Up @@ -413,7 +411,7 @@ def set_project_extent(self) -> QgsRectangle:
p_extent.yMaximum(),
]
p_wms_extent = [str(i) for i in p_wms_extent]
p.writeEntry('WMSExtent', '', p_wms_extent)
project.writeEntry('WMSExtent', '', p_wms_extent)

# Zoom canvas to extent
if self.iface:
Expand Down
Loading

0 comments on commit 3907771

Please sign in to comment.