1# -*- coding: utf-8 -*-
2
3"""
4***************************************************************************
5    ProjectProvider.py
6    ------------------------
7    Date                 : July 2018
8    Copyright            : (C) 2018 by Nyall Dawson
9    Email                : nyall dot dawson at gmail dot com
10***************************************************************************
11*                                                                         *
12*   This program is free software; you can redistribute it and/or modify  *
13*   it under the terms of the GNU General Public License as published by  *
14*   the Free Software Foundation; either version 2 of the License, or     *
15*   (at your option) any later version.                                   *
16*                                                                         *
17***************************************************************************
18"""
19
20__author__ = 'Nyall Dawson'
21__date__ = 'July 2018'
22__copyright__ = '(C) 2018, Nyall Dawson'
23
24from qgis.core import (Qgis,
25                       QgsApplication,
26                       QgsProcessingProvider,
27                       QgsMessageLog,
28                       QgsProcessingModelAlgorithm,
29                       QgsProject,
30                       QgsXmlUtils,
31                       QgsRuntimeProfiler)
32
33PROJECT_PROVIDER_ID = 'project'
34
35
36class ProjectProvider(QgsProcessingProvider):
37
38    def __init__(self, project=None):
39        super().__init__()
40        if project is None:
41            self.project = QgsProject.instance()
42        else:
43            self.project = project
44
45        self.model_definitions = {}  # dict of models in project
46        self.is_loading = False
47
48        # must reload models if providers list is changed - previously unavailable algorithms
49        # which models depend on may now be available
50        QgsApplication.processingRegistry().providerAdded.connect(self.on_provider_added)
51
52        self.project.readProject.connect(self.read_project)
53        self.project.writeProject.connect(self.write_project)
54        self.project.cleared.connect(self.clear)
55
56    def on_provider_added(self, _):
57        self.refreshAlgorithms()
58
59    def load(self):
60        with QgsRuntimeProfiler.profile('Project Provider'):
61            self.refreshAlgorithms()
62
63        return True
64
65    def clear(self):
66        """
67        Remove all algorithms from the provider
68        """
69        self.model_definitions = {}
70        self.refreshAlgorithms()
71
72    def add_model(self, model):
73        """
74        Adds a model to the provider
75        :type model: QgsProcessingModelAlgorithm
76        :param model: model to add
77        """
78        definition = model.toVariant()
79        self.model_definitions[model.name()] = definition
80        self.refreshAlgorithms()
81
82    def remove_model(self, model):
83        """
84        Removes a model from the project
85        :type model: QgsProcessingModelAlgorithm
86        :param model: model to remove
87        """
88        if model is None:
89            return
90
91        if model.name() in self.model_definitions:
92            del self.model_definitions[model.name()]
93
94        self.refreshAlgorithms()
95
96    def read_project(self, doc):
97        """
98        Reads the project model definitions from the project DOM document
99        :param doc: DOM document
100        """
101        self.model_definitions = {}
102        project_models_nodes = doc.elementsByTagName('projectModels')
103        if project_models_nodes:
104            project_models_node = project_models_nodes.at(0)
105            model_nodes = project_models_node.childNodes()
106            for n in range(model_nodes.count()):
107                model_element = model_nodes.at(n).toElement()
108                definition = QgsXmlUtils.readVariant(model_element)
109                algorithm = QgsProcessingModelAlgorithm()
110                if algorithm.loadVariant(definition):
111                    self.model_definitions[algorithm.name()] = definition
112
113        self.refreshAlgorithms()
114
115    def write_project(self, doc):
116        """
117        Writes out the project model definitions into the project DOM document
118        :param doc: DOM document
119        """
120        qgis_nodes = doc.elementsByTagName('qgis')
121        if not qgis_nodes:
122            return
123
124        qgis_node = qgis_nodes.at(0)
125        project_models_node = doc.createElement('projectModels')
126
127        for a in self.algorithms():
128            definition = a.toVariant()
129            element = QgsXmlUtils.writeVariant(definition, doc)
130            project_models_node.appendChild(element)
131        qgis_node.appendChild(project_models_node)
132
133    def name(self):
134        return self.tr('Project models', 'ProjectProvider')
135
136    def longName(self):
137        return self.tr('Models embedded in the current project', 'ProjectProvider')
138
139    def id(self):
140        return PROJECT_PROVIDER_ID
141
142    def icon(self):
143        return QgsApplication.getThemeIcon("/mIconQgsProjectFile.svg")
144
145    def svgIconPath(self):
146        return QgsApplication.iconPath("mIconQgsProjectFile.svg")
147
148    def supportsNonFileBasedOutput(self):
149        return True
150
151    def loadAlgorithms(self):
152        if self.is_loading:
153            return
154        self.is_loading = True
155
156        for definition in self.model_definitions.values():
157            algorithm = QgsProcessingModelAlgorithm()
158            if algorithm.loadVariant(definition):
159                self.addAlgorithm(algorithm)
160            else:
161                QgsMessageLog.logMessage(
162                    self.tr('Could not load model from project', 'ProjectProvider'),
163                    self.tr('Processing'), Qgis.Critical)
164
165        self.is_loading = False
166