[QGIS-Developer] Best practice for plugins including processing models

Alister Hood alister.hood at gmail.com
Mon May 15 18:19:50 PDT 2023


Further to this, I am now using the following code to load the models
directly from the plugin folder, rather than copying them into
%AppData%\Roaming\QGIS\QGIS3\profiles\default\processing\models


    def loadModels(self):
            modelsFolders=ModelerUtils.modelsFolders()
            if self.modelsPath not in modelsFolders:
                modelsFolders.append(self.modelsPath)

ProcessingConfig.setSettingValue(ModelerUtils.MODELS_FOLDER,
';'.join(modelsFolders))

QgsApplication.processingRegistry().providerById('model').refreshAlgorithms()

    def unloadModels(self):
            modelsFolders=ModelerUtils.modelsFolders()
            if self.modelsPath in modelsFolders:
                modelsFolders.remove(self.modelsPath)

ProcessingConfig.setSettingValue(ModelerUtils.MODELS_FOLDER,
';'.join(modelsFolders))

QgsApplication.processingRegistry().providerById('model').refreshAlgorithms()


On Fri, 28 Apr 2023 at 15:07, Alister Hood <alister.hood at gmail.com> wrote:

> Hi, I maintain an in-house plugin for our team of civil engineers, who
> apart from myself are not very advanced GIS users.
> It does things like set useful defaults and install a set of useful
> plugins.  It also includes some processing models, which are loaded with
> the code below, which seems to be the conventional way models are loaded in
> publicly available plugins which ship processing models.  This code copies
> the model files included in the plugin to the default folder for processing
> models - somewhere like
> %AppData%\Roaming\QGIS\QGIS3\profiles\default\processing\models
>
> One of my colleagues did some QGIS training and created a processing
> model, which coincidentally had the same filename as a model I created on
> the same day, so his model got overwritten when my plugin was updated.
> This is not a problem for us as we are a small team and can be careful what
> we do going forward.  But it highlights that the way plugin authors are
> loading model files is what seems to me to be pretty poor practice, for two
> reasons.
>
> Firstly models are available in qgis even when the plugin is unloaded or
> uninstalled, which is probably not desirable.
> It is trivial to implement an unloadModels function to rectify this, and I
> have done so in my in-house plugin.  I'm wondering - should plugin authors
> be encouraged to do the same?
>
> Secondly, loading the plugin will silently overwrite any existing models
> with the same filename, as occurred here.  Plugins generally contain models
> with fairly special filenames, so hopefully this is unlikely to occur,
> unless:
> - someone produces a plugin that is a fork of another one, or
> - someone ships a plugin with a model that is previously publicly
> available somewhere else, or that was created by following an online
> tutorial, or
> - a user edits a model provided by a plugin.
> In any case, would it be a good idea to encourage plugin authors to load
> models another way, without copying them into
> %AppData%\Roaming\QGIS\QGIS3\profiles\default\processing\models?
> I realise that to change an existing plugin there is the added
> complication of what to do when updating from an old version - the only
> safeish way I can think of to automatically clean up obsolete model files
> would be to check for them and then use a checksum to verify whether they
> should be deleted.
> For what it's worth, I note that when a user tries to manually "add model
> to toolbox", QGIS refuses if there is an existing model with the same
> filename.
>
> Thanks.
>
>
>     def loadModels(self):
>             '''Register models present in models folder of the plugin.'''
>             try:
>                 iface.initializationCompleted.disconnect(self.loadModels)
>             except:
>                 pass
>
>             #QgsMessageLog.logMessage(self.tr('testing'),
> self.messageTag, Qgis.Warning)
>             modelsFiles = glob.glob(os.path.join(self.modelsPath,
> '*.model3'))
>
>             for modelFileName in modelsFiles:
>                 alg = QgsProcessingModelAlgorithm()
>                 if not alg.fromFile(modelFileName):
>                     QgsMessageLog.logMessage(self.tr('Not well formed
> model: {}'.format(modelFileName)), self.messageTag, Qgis.Warning)
>                     continue
>
>                 destFilename =
> os.path.join(ModelerUtils.modelsFolders()[0],
> os.path.basename(modelFileName))
>                 try:
>                     if os.path.exists(destFilename):
>                         os.remove(destFilename)
>
>                     if isWindows():
>                         copyfile(modelFileName, destFilename)
>                     else:
>                         os.symlink(modelFileName, destFilename)
>                 except Exception as ex:
>                     QgsMessageLog.logMessage(self.tr('Failed to install
> model: {} - {}'.format(modelFileName, str(ex))), self.messageTag,
> Qgis.Warning)
>                     continue
>
>
> QgsApplication.processingRegistry().providerById('model').refreshAlgorithms()
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.osgeo.org/pipermail/qgis-developer/attachments/20230516/54cd78ae/attachment.htm>


More information about the QGIS-Developer mailing list