Skip to content

Commit

Permalink
Add new dialog to generate all projects
Browse files Browse the repository at this point in the history
  • Loading branch information
Gustry committed Nov 22, 2024
1 parent a4b7557 commit 1576e66
Show file tree
Hide file tree
Showing 5 changed files with 372 additions and 26 deletions.
13 changes: 13 additions & 0 deletions dynamic_layers/core/generate_projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
QgsProject,
QgsVectorLayer,
)
from qgis.PyQt.QtWidgets import QApplication

from dynamic_layers.core.dynamic_layers_engine import DynamicLayersEngine
from dynamic_layers.tools import (
Expand All @@ -35,6 +36,7 @@ def __init__(
destination: Path,
copy_side_car_files: bool,
feedback: QgsProcessingFeedback = None,
limit: int = None,
):
""" Constructor. """
self.project = project
Expand All @@ -44,6 +46,7 @@ def __init__(
self.expression_destination = expression_destination
self.copy_side_car_files = copy_side_car_files
self.feedback = feedback
self.limit = limit

def process(self) -> bool:
""" Generate all projects needed according to the coverage layer. """
Expand All @@ -64,12 +67,22 @@ def process(self) -> bool:
request = QgsFeatureRequest()
# noinspection PyUnresolvedReferences
request.setFlags(QgsFeatureRequest.NoGeometry)
if self.limit >= 0:
# For debug only
request.setLimit(self.limit)
if total >= self.limit:
total = self.limit

for i, feature in enumerate(self.coverage.getFeatures(request)):
if self.feedback:
if self.feedback.isCanceled():
break
self.feedback.pushDebugInfo(tr('Feature : {}').format(feature.id()))

if hasattr(self.feedback, 'widget'):
# It's the own Feedback object
QApplication.processEvents()

engine.set_layer_and_feature(self.coverage, feature)
engine.update_dynamic_layers_datasource()
if self.feedback:
Expand Down
46 changes: 22 additions & 24 deletions dynamic_layers/dynamic_layers.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,15 @@
__license__ = 'GPL version 3'
__email__ = '[email protected]'

from qgis import processing
from qgis.core import Qgis, QgsApplication, QgsMessageLog, QgsProject
from qgis.core import Qgis, QgsApplication, QgsMessageLog
from qgis.gui import QgisInterface
from qgis.PyQt.QtCore import QCoreApplication, QSettings, QTranslator
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import QAction, QMenu

from dynamic_layers.definitions import PLUGIN_MESSAGE
from dynamic_layers.dynamic_layers_dialog import DynamicLayersDialog
from dynamic_layers.processing_provider.provider import Provider
from dynamic_layers.generate_projects import GenerateProjectsDialog
from dynamic_layers.tools import open_help, plugin_path, resources_path, tr


Expand All @@ -21,7 +20,7 @@ class DynamicLayers:
def __init__(self, iface: QgisInterface):
"""Constructor."""
self.iface = iface
self.provider = None
# self.provider = None

self.help_action_about_menu = None
self.menu = None
Expand All @@ -40,22 +39,23 @@ def __init__(self, iface: QgisInterface):
QCoreApplication.installTranslator(self.translator)

# noinspection PyPep8Naming
def initProcessing(self):
""" Init processing provider. """
self.provider = Provider()
# noinspection PyArgumentList
QgsApplication.processingRegistry().addProvider(self.provider)
# def initProcessing(self):
# """ Init processing provider. """
# self.provider = Provider()
# # noinspection PyArgumentList
# QgsApplication.processingRegistry().addProvider(self.provider)

# noinspection PyPep8Naming
def initGui(self):
"""Create the menu entries and toolbar icons inside the QGIS GUI."""
# noinspection PyArgumentList
main_icon = QIcon(str(resources_path('icons', 'icon.png')))
self.menu = QMenu("Dynamic Layers")
self.menu.setIcon(main_icon)

self.main_dialog_action = QAction(main_icon, tr("Setup the project"), self.iface.mainWindow())
# noinspection PyUnresolvedReferences
self.main_dialog_action.triggered.connect(self.run)
self.main_dialog_action.triggered.connect(self.open_single_project_dialog)
self.menu.addAction(self.main_dialog_action)

# noinspection PyArgumentList
Expand All @@ -65,12 +65,12 @@ def initGui(self):
self.iface.mainWindow()
)
# noinspection PyUnresolvedReferences
self.generate_projects_action.triggered.connect(self.generate_projects_clicked)
self.generate_projects_action.triggered.connect(self.open_generate_projects_dialog)
self.menu.addAction(self.generate_projects_action)

self.iface.pluginMenu().addMenu(self.menu)

self.initProcessing()
# self.initProcessing()

# Open the online help
self.help_action_about_menu = QAction(main_icon, tr('Project generator'), self.iface.mainWindow())
Expand All @@ -80,9 +80,9 @@ def initGui(self):

def unload(self):
"""Removes the plugin menu item and icon from QGIS GUI."""
if self.provider:
# noinspection PyArgumentList
QgsApplication.processingRegistry().removeProvider(self.provider)
# if self.provider:
# # noinspection PyArgumentList
# QgsApplication.processingRegistry().removeProvider(self.provider)

if self.generate_projects_action:
self.iface.removePluginMenu("Dynamic Layers", self.generate_projects_action)
Expand All @@ -97,17 +97,15 @@ def unload(self):
del self.help_action_about_menu

@staticmethod
def generate_projects_clicked():
""" Open the Processing algorithm dialog. """
# noinspection PyUnresolvedReferences
processing.execAlgorithmDialog(
"dynamic_layers:generate_projects",
{}
)
def open_generate_projects_dialog():
""" Open the generate projects dialog. """
dialog = GenerateProjectsDialog()
dialog.exec()
del dialog

@staticmethod
def run():
""" Open the plugin dialog. """
def open_single_project_dialog():
""" Open the single project dialog. """
dialog = DynamicLayersDialog()
dialog.populate_layer_table()
dialog.populate_variable_table()
Expand Down
162 changes: 162 additions & 0 deletions dynamic_layers/generate_projects.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
__copyright__ = 'Copyright 2024, 3Liz'
__license__ = 'GPL version 3'
__email__ = '[email protected]'


from pathlib import Path

from qgis.core import (
QgsApplication,
QgsExpressionContext,
QgsExpressionContextUtils,
QgsMapLayerProxyModel,
QgsProcessingException,
QgsProcessingFeedback,
QgsProject,
)
from qgis.gui import QgsExpressionBuilderDialog, QgsFileWidget
from qgis.PyQt import uic
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import (
QDialog,
QDialogButtonBox,
QPlainTextEdit,
QProgressBar,
)
from qgis.utils import OverrideCursor

from dynamic_layers.core.generate_projects import GenerateProjects
from dynamic_layers.definitions import QtVar
from dynamic_layers.tools import open_help, tr

folder = Path(__file__).resolve().parent
ui_file = folder / 'resources' / 'ui' / 'generate_projects.ui'
FORM_CLASS, _ = uic.loadUiType(ui_file)


class GenerateProjectsDialog(QDialog, FORM_CLASS):
# noinspection PyArgumentList
def __init__(self, parent: QDialog = None):
"""Constructor."""
# noinspection PyArgumentList
super().__init__(parent)
self.setupUi(self)
self.setWindowTitle(tr("Generate many QGIS projects"))
self.project = QgsProject.instance()

self.button_box.button(QDialogButtonBox.Apply).clicked.connect(self.generate_projects)
self.button_box.button(QDialogButtonBox.Help).clicked.connect(open_help)
self.button_box.button(QDialogButtonBox.Cancel).clicked.connect(self.close)

self.expression.setText("")
self.expression.setToolTip(tr("Open the expression builder"))
self.expression.setIcon(QIcon(QgsApplication.iconPath('mIconExpression.svg')))
self.expression.clicked.connect(self.open_expression_builder)

self.coverage.setFilters(QgsMapLayerProxyModel.Filter.VectorLayer)
self.coverage.layerChanged.connect(self.layer_changed)

self.destination.setStorageMode(QgsFileWidget.StorageMode.GetDirectory)
self.field.setAllowEmptyFieldName(False)
self.layer_changed()
self.debug_limit.setValue(0)

# DEBUG
self.file_name.setText('"schema" || \'/test_\' || "schema" || \'.qgs\'')
self.destination.setFilePath('/tmp/demo_cartophyl')
self.debug_limit.setValue(5)

def layer_changed(self):
self.field.setLayer(self.coverage.currentLayer())

def open_expression_builder(self):
""" Open the expression builder. """
layer = self.coverage.currentLayer()
if not layer:
return
context = QgsExpressionContext()
context.appendScope(QgsExpressionContextUtils.globalScope())
context.appendScope(QgsExpressionContextUtils.projectScope(QgsProject.instance()))
context.appendScope(QgsExpressionContextUtils.layerScope(layer))

dialog = QgsExpressionBuilderDialog(layer, context=context)
dialog.setExpressionText(self.file_name.text())
result = dialog.exec()

if result != QDialog.Accepted:
return

content = dialog.expressionText()
self.file_name.setText(content)

def generate_projects(self):
"""The OK button to generate all projects. """
layer = self.coverage.currentLayer()
if not layer:
return

feedback = TextFeedBack(self.logs, self.progress)

if self.project.isDirty():
feedback.reportError(tr("You must save your project first."))

self.button_box.button(QDialogButtonBox.Apply).setEnabled(False)
result = False
with OverrideCursor(QtVar.WaitCursor):
self.logs.clear()

generator = GenerateProjects(
self.project,
layer,
self.field.currentField(),
self.file_name.text(),
Path(self.destination.filePath()),
self.copy_side_care_files.isChecked(),
feedback,
limit=self.debug_limit.value(),
)

try:
result = generator.process()
except QgsProcessingException as e:
feedback.reportError(str(e))
except Exception as e:
feedback.reportError(str(e))

if result:
feedback.pushInfo(tr("End") + " 👍")
feedback.pushInfo(tr("Dialog can be closed"))
# In case of success, the button is not enabled again
else:
feedback.pushWarning(tr("End, but there was an error"))
self.button_box.button(QDialogButtonBox.Apply).setEnabled(True)


class TextFeedBack(QgsProcessingFeedback):

def __init__(self, widget: QPlainTextEdit, progress: QProgressBar):
super().__init__()
self.widget = widget
self.progress = progress

def setProgressText(self, text):
pass

def setProgress(self, i: int):
self.progress.setValue(i)

def pushInfo(self, text):
self.widget.appendHtml(f"<p>{text}</p>")

def pushCommandInfo(self, text):
self.widget.appendHtml(f"<p style=\"color:grey\">{text}</p>")

def pushDebugInfo(self, text):
self.widget.appendHtml(f"<p style=\"color:grey\">{text}</p>")

def pushConsoleInfo(self, text):
self.widget.appendHtml(f"<p style=\"color:grey\">{text}</p>")

def reportError(self, text, fatal_error=False):
_ = fatal_error
self.widget.appendHtml(f"<p style=\"color:red\">{text}</p>")
3 changes: 1 addition & 2 deletions dynamic_layers/processing_provider/generate_projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,11 @@
from pathlib import Path
from typing import Tuple

from qgis.core import (
from qgis.core import ( # QgsFeatureRequest,
QgsExpression,
QgsProcessing,
QgsProcessingAlgorithm,
QgsProcessingException,
# QgsFeatureRequest,
QgsProcessingParameterBoolean,
QgsProcessingParameterExpression,
QgsProcessingParameterFeatureSource,
Expand Down
Loading

0 comments on commit 1576e66

Please sign in to comment.