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

Alister Hood alister.hood at gmail.com
Mon May 15 19:09:38 PDT 2023


And including this in loadModels() is a fairly safe way to remove the
copied model files from previous versions:

            modelsFiles = glob.glob(os.path.join(self.modelsPath,
'*.model3'))

            for modelFileName in modelsFiles:
                destFilename =
os.path.join(ModelerUtils.defaultModelsFolder(),
os.path.basename(modelFileName))
                try:
                    if os.path.exists(destFilename):
                        move(destFilename, destFilename + '.bak')
                except Exception as ex:
                    QgsMessageLog.logMessage(self.tr('Failed to rename
existing model: {} - {}'.format(destFilename, str(ex))), self.messageTag,
Qgis.Warning)
                    continue

It does mean that if someone happens to have a model with the same name as
one provided by the plugin, it will disappear, but at least it will still
be there as a *.bak file, rather than just being overwritten.

On Tue, 16 May 2023 at 13:19, Alister Hood <alister.hood at gmail.com> wrote:

> 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/c3934f68/attachment-0001.htm>


More information about the QGIS-Developer mailing list