Register a Processing Provider in a Plugin
An algorithm that lives behind a plugin's own dialog can be run by a person. The same algorithm registered as a Processing provider can be run by a person, chained into a model, executed in batch across two hundred files, called from another script, and run from the command line by qgis_process — with no extra work from you. That is the whole argument for the provider: one class, perhaps thirty lines, and your tool inherits the entire framework.
This recipe belongs to Processing Provider Plugins. It covers writing the provider class, registering and unregistering it from the plugin lifecycle, choosing identifiers that will not change, and keeping the whole thing reloadable while you develop.
Prerequisites
- QGIS 3.34 LTR (bundled Python 3.12) or newer.
- A plugin skeleton with
initGui()andunload()— see Plugin Boilerplate and Structure. - At least one algorithm class; writing one is covered in Write a Custom Processing Algorithm in PyQGIS.
Write the provider
from qgis.core import QgsProcessingProvider
from qgis.PyQt.QtGui import QIcon
from .algorithms.summarise_parcels import SummariseParcelsAlgorithm
from .algorithms.split_by_ward import SplitByWardAlgorithm
class ParcelToolsProvider(QgsProcessingProvider):
def loadAlgorithms(self):
for algorithm in (SummariseParcelsAlgorithm(), SplitByWardAlgorithm()):
self.addAlgorithm(algorithm)
def id(self):
return "parceltools"
def name(self):
return self.tr("Parcel Tools")
def longName(self):
return self.tr("Parcel Tools for local authority GIS")
def icon(self):
return QIcon(":/plugins/parcel_tools/icon.png")
Breakdown: loadAlgorithms() is called by the framework when the provider is registered and after every refresh, and must construct fresh algorithm instances each time — reusing one is a subtle source of state leaking between runs. id() is the identifier that appears in every algorithm's full name as parceltools:summariseparcels, and it becomes part of the public interface the moment somebody saves a model or writes a script that uses it: choose it once, in lower case with no spaces, and never change it. name() is the display name shown as the group in the toolbox and should be translated. The icon is optional but makes the group recognisable in a toolbox with a dozen providers.
Register and unregister it
from qgis.core import QgsApplication
from .provider import ParcelToolsProvider
class ParcelTools:
def __init__(self, iface):
self.iface = iface
self.provider = None
def initProcessing(self):
self.provider = ParcelToolsProvider()
QgsApplication.processingRegistry().addProvider(self.provider)
def initGui(self):
self.initProcessing()
def unload(self):
if self.provider is not None:
QgsApplication.processingRegistry().removeProvider(self.provider)
self.provider = None
Breakdown: Keeping the provider as an attribute is essential — the registry does not take ownership in a way Python's garbage collector respects, and a provider created as a local variable disappears, taking the algorithms with it and sometimes crashing on the next toolbox refresh. Separating initProcessing() from initGui() is a small courtesy that lets a headless context register the algorithms without building any interface. The unregister in unload() is what makes plugin reloading work: without it, each reload adds another copy of the provider and the toolbox fills with duplicates that reference dead code.
Confirm it registered
from qgis.core import QgsApplication
registry = QgsApplication.processingRegistry()
print([p.id() for p in registry.providers()])
algorithm = registry.algorithmById("parceltools:summariseparcels")
print(algorithm.displayName() if algorithm else "not found")
Breakdown: The provider list is the first thing to check when a toolbox group does not appear — if the id is missing, the registration never happened; if it is present but empty, loadAlgorithms() raised and the framework swallowed it. Looking up by the full id confirms the algorithm's identifier is what you think it is, which matters because it is the string users will put in scripts. A None here with the provider present almost always means the algorithm's own name() differs from what you expected — the framework lower-cases it and strips nothing else.
Refresh during development
Editing an algorithm and re-running it from the toolbox uses the code loaded at registration, so nothing appears to change. Two mechanisms fix that.
QgsApplication.processingRegistry().providerById("parceltools").refreshAlgorithms()
Breakdown: refreshAlgorithms() calls loadAlgorithms() again, which picks up new or removed algorithms but not edits inside a module Python has already imported — module caching applies here exactly as it does everywhere else. For code changes, reload the plugin, which re-imports everything and re-runs initGui(); the Plugin Reloader workflow in Reload a QGIS Plugin Without Restarting is the practical answer, and it works correctly only if unload() removes the provider. This is the single most common reason a Processing plugin becomes annoying to develop.
Group algorithms sensibly
Once a provider carries more than a handful of algorithms, grouping matters. Groups come from the algorithms rather than the provider:
class SummariseParcelsAlgorithm(QgsProcessingAlgorithm):
def group(self):
return self.tr("Reporting")
def groupId(self):
return "reporting"
def name(self):
return "summariseparcels"
def displayName(self):
return self.tr("Summarise parcels by ward")
Breakdown: group() and groupId() create a subfolder inside your provider's section of the toolbox, so twenty algorithms become four groups of five. As with the provider, the id is machine-facing and permanent while the display name is human-facing and translatable — the pair appears throughout the Processing API and mixing them up produces a toolbox showing raw identifiers. name() combines with the provider id to form the full algorithm id, and it is the string every saved model and script will contain, so treat a rename as a breaking change and provide the old name as an alias if you must.
QGIS version compatibility
| QGIS version | Python | Notes |
|---|---|---|
| 3.22 LTR | 3.9 | QgsProcessingProvider, registry add and remove as described. |
| 3.28 LTR | 3.9 | Identical. |
| 3.34 LTR | 3.12 | Baseline for this page. |
| 3.40 / 3.44 | 3.12 | Identical registration; the toolbox gained better search and provider-level settings. |
Providers can also declare supportsNonFileBasedOutput() and their own settings entries in the Processing options page — both are worth adding once the provider is stable, and neither changes how registration works.
Troubleshooting
- The group never appears in the toolbox. The provider was not kept as an attribute, or
addProvider()was never reached becauseinitGui()raised earlier. - The group appears empty.
loadAlgorithms()raised — usually an import error in an algorithm module. Check the Python error log. - Duplicate groups after reloading.
removeProvider()is missing fromunload(). - Edits to an algorithm have no effect. Module caching. Reload the plugin rather than refreshing the provider.
algorithmById()returnsNone. The full id is not what you assumed. Print every id from the registry and copy the exact string.- The algorithm works in the toolbox and not from
processing.run(). Almost always the id again, or parameters passed under display names instead of parameter names.
Conclusion
A provider is a small class: loadAlgorithms() constructing fresh instances, a permanent lower-case id(), a translated name(), and an icon. Register it in initGui() keeping a reference on the plugin, remove it in unload() so reloads stay clean, and group algorithms once there are more than a few. In exchange, every algorithm you write gains batch execution, model chaining, scripting and command-line access for free.
Frequently Asked Questions
Do I need a plugin to publish algorithms? No — a script placed in the Processing scripts folder is enough for personal use. A provider inside a plugin is what makes a set of algorithms installable and updatable by other people.
Can one plugin register several providers? It can, but it is rarely right. Use groups within one provider instead; users look for your tools under your name.
How do users run my algorithm from the command line?qgis_process run parceltools:summariseparcels --INPUT=... --OUTPUT=..., once the plugin is installed — see Use the qgis_process Command Line Runner.
What happens if two providers use the same id? The second registration is rejected. Prefix the id with something specific to your organisation if a collision is plausible.
Should the provider be registered when the plugin has no GUI?
Yes. That is why initProcessing() is separate — algorithms are useful in a headless context, which is much of the point.
Can I hide an algorithm from the toolbox but keep it scriptable?
Yes: return the FlagHideFromToolbox flag from the algorithm. It stays available to models and scripts.
How do I add help text for my algorithms?
Implement shortHelpString() on each algorithm, returning a paragraph or two of HTML. It appears in the panel beside the parameters, which is where users look first and where a sentence about what the algorithm expects saves most support questions. shortDescription() provides the one-line summary shown in the toolbox tooltip.
Does the provider need to handle its own errors?
No. Exceptions raised inside an algorithm are caught by the framework, shown in the log and reported as a failed run. What the provider must not do is raise inside loadAlgorithms(), because that failure happens during registration and produces an empty group with no visible explanation.