[{"data":1,"prerenderedAt":2213},["ShallowReactive",2],{"doc:\u002Fqgis-plugin-development\u002Fprocessing-provider-plugins":3},{"id":4,"title":5,"body":6,"description":2203,"extension":2204,"meta":2205,"navigation":391,"path":2209,"seo":2210,"stem":2211,"__hash__":2212},"docs\u002Fqgis-plugin-development\u002Fprocessing-provider-plugins\u002Findex.md","Processing Provider Plugins for QGIS",{"type":7,"value":8,"toc":2187},"minimark",[9,14,31,39,64,69,111,115,118,305,315,319,338,618,645,649,662,798,1002,1039,1043,1057,1111,1124,1128,1137,1275,1302,1306,1328,1443,1450,1454,1462,1566,1597,1601,1604,1719,1722,1725,1729,1732,1747,1760,1837,1850,1859,1863,1869,1925,1935,1939,1995,1999,2011,2022,2038,2047,2065,2071,2077,2087,2093,2099,2111,2117,2129,2133,2183],[10,11,13],"h1",{"id":12},"processing-provider-plugins","Processing Provider Plugins",[15,16,17],"p",{},[18,19,20,25,26,30],"em",{},[21,22,24],"a",{"href":23},"\u002F","Home"," → ",[21,27,29],{"href":28},"\u002Fqgis-plugin-development\u002F","QGIS Plugin Development"," → Processing Provider Plugins",[15,32,33,34,38],{},"A Processing provider is the cleanest way to ship reusable geoprocessing tools inside a QGIS plugin. Instead of bolting a dialog onto a toolbar button, you register your algorithms with the Processing framework, where they appear in the Toolbox alongside native GDAL, GRASS, and QGIS algorithms. From there they gain features for free: a generated parameter dialog, batch execution, history logging, the graphical Model Designer, and the ability to be called from ",[35,36,37],"code",{},"processing.run()"," in any other script. This page explains how to build that provider as part of a plugin, how the provider lifecycle fits the plugin lifecycle, and how this approach differs from dropping a standalone script into your processing scripts folder.",[15,40,41,42,45,46,50,51,54,55,58,59,63],{},"This guide builds on the ",[21,43,44],{"href":28},"QGIS Plugin Development Guide",". If you have not yet assembled a working plugin skeleton, start with ",[21,47,49],{"href":48},"\u002Fqgis-plugin-development\u002Fplugin-boilerplate-structure\u002F","QGIS Plugin Boilerplate & Structure",", because a provider needs the same ",[35,52,53],{},"__init__.py",", ",[35,56,57],{},"metadata.txt",", and lifecycle hooks. The provider replaces or supplements the UI-focused work covered in ",[21,60,62],{"href":61},"\u002Fqgis-plugin-development\u002Fqt-designer-for-gis-interfaces\u002F","Qt Designer for GIS Interfaces",": Processing generates the dialog for you, so you write parameters instead of widgets.",[65,66,68],"h2",{"id":67},"prerequisites","Prerequisites",[70,71,72,80,87,101,108],"ul",{},[73,74,75,79],"li",{},[76,77,78],"strong",{},"QGIS 3.34 LTR"," or later, with the Processing plugin enabled (it ships and activates by default).",[73,81,82,83,86],{},"A working plugin package that loads cleanly, as described in ",[21,84,85],{"href":48},"Plugin Boilerplate & Structure",".",[73,88,89,90,54,93,96,97,100],{},"Familiarity with the ",[35,91,92],{},"classFactory",[35,94,95],{},"initGui",", and ",[35,98,99],{},"unload"," lifecycle methods.",[73,102,103,104,86],{},"Comfort writing at least one algorithm class — see the child task ",[21,105,107],{"href":106},"\u002Fqgis-plugin-development\u002Fprocessing-provider-plugins\u002Fwrite-custom-processing-algorithm-pyqgis\u002F","Write a Custom Processing Algorithm in PyQGIS",[73,109,110],{},"An SVG or PNG icon for the provider (optional but recommended for Toolbox visibility).",[65,112,114],{"id":113},"provider-algorithms-and-the-toolbox","Provider, Algorithms, and the Toolbox",[15,116,117],{},"A provider is a container. It holds one or more algorithm classes and exposes identity metadata — a machine-readable id, a human-readable name, and an icon. When QGIS starts the Processing registry, it asks every registered provider to load its algorithms, then groups them under the provider's name in the Toolbox tree. The diagram below shows the relationship.",[119,120,125,126,125,130,125,134,125,141,125,150,125,159,125,165,125,169,125,173,125,179,125,185,125,190,125,196,125,202,125,210,125,215,125,218,125,222,125,225,125,229,125,233,125,235,125,237,125,244,125,250,125,256,125,261,125,266,125,270,125,274,125,278,125,282,125,285,125,288],"svg",{"viewBox":121,"role":122,"ariaLabel":123,"xmlns":124},"0 0 760 360","img","Diagram showing a QGIS plugin registering one Processing provider that contains several algorithms, which surface in the Processing Toolbox","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[127,128,129],"title",{},"Provider, algorithms, and Toolbox relationship",[131,132,133],"desc",{},"A plugin's initProcessing method registers a single provider with the processing registry. The provider groups multiple algorithm classes, and each becomes an entry in the Processing Toolbox.",[135,136],"rect",{"x":137,"y":137,"width":138,"height":139,"fill":140},"0","760","360","#f6f3ea",[135,142],{"x":143,"y":143,"width":144,"height":145,"rx":146,"fill":147,"stroke":148,"style":149},"24","200","120","8","#fffdf7","#0f766e","stroke-width:2.5",[151,152,158],"text",{"x":153,"y":154,"fill":155,"style":156,"textAnchor":157},"124","58","#17211d","text-anchor:middle;font-family:sans-serif;font-size:16px;font-weight:bold","middle","Plugin",[151,160,164],{"x":153,"y":161,"fill":162,"style":163,"textAnchor":157},"86","#2f3b35","text-anchor:middle;font-family:monospace;font-size:12px","classFactory()",[151,166,168],{"x":153,"y":167,"fill":162,"style":163,"textAnchor":157},"106","initProcessing()",[151,170,172],{"x":153,"y":171,"fill":162,"style":163,"textAnchor":157},"126","unload()",[135,174],{"x":175,"y":176,"width":144,"height":177,"rx":146,"fill":147,"stroke":178,"style":149},"280","40","88","#b45309",[151,180,184],{"x":181,"y":182,"fill":155,"style":183,"textAnchor":157},"380","74","text-anchor:middle;font-family:sans-serif;font-size:15px;font-weight:bold","Provider",[151,186,189],{"x":181,"y":187,"fill":162,"style":188,"textAnchor":157},"98","text-anchor:middle;font-family:monospace;font-size:11px","QgsProcessingProvider",[191,192],"line",{"x1":193,"y1":194,"x2":175,"y2":194,"stroke":162,"style":195},"224","84","stroke-width:2.5;marker-end:url(#ar)",[151,197,201],{"x":198,"y":199,"fill":162,"style":200,"textAnchor":157},"252","76","text-anchor:middle;font-family:sans-serif;font-size:10px","register",[135,203],{"x":175,"y":204,"width":205,"height":206,"rx":207,"fill":147,"stroke":208,"style":209},"170","150","44","6","#2563eb","stroke-width:2",[151,211,214],{"x":212,"y":213,"fill":162,"style":188,"textAnchor":157},"355","197","Algorithm A",[135,216],{"x":175,"y":217,"width":205,"height":206,"rx":207,"fill":147,"stroke":208,"style":209},"226",[151,219,221],{"x":212,"y":220,"fill":162,"style":188,"textAnchor":157},"253","Algorithm B",[135,223],{"x":175,"y":224,"width":205,"height":206,"rx":207,"fill":147,"stroke":208,"style":209},"282",[151,226,228],{"x":212,"y":227,"fill":162,"style":188,"textAnchor":157},"309","Algorithm C",[191,230],{"x1":181,"y1":231,"x2":212,"y2":204,"stroke":162,"style":232},"128","stroke-width:2;marker-end:url(#ar)",[191,234],{"x1":181,"y1":231,"x2":212,"y2":217,"stroke":162,"style":232},[191,236],{"x1":181,"y1":231,"x2":212,"y2":224,"stroke":162,"style":232},[135,238],{"x":239,"y":176,"width":240,"height":241,"rx":146,"fill":242,"stroke":243,"style":149},"520","216","286","#26322d","#15803d",[151,245,249],{"x":246,"y":247,"fill":248,"style":183,"textAnchor":157},"628","70","#d9f99d","Processing Toolbox",[151,251,255],{"x":252,"y":253,"fill":248,"style":254},"540","110","font-family:monospace;font-size:12px","▾ My Tools",[151,257,260],{"x":258,"y":205,"fill":248,"style":259},"556","font-family:monospace;font-size:11px","▾ Vector",[151,262,265],{"x":263,"y":264,"fill":248,"style":259},"572","180","• Algorithm A",[151,267,269],{"x":263,"y":268,"fill":248,"style":259},"206","• Algorithm B",[151,271,273],{"x":258,"y":272,"fill":248,"style":259},"244","▾ Raster",[151,275,277],{"x":263,"y":276,"fill":248,"style":259},"274","• Algorithm C",[191,279],{"x1":280,"y1":281,"x2":239,"y2":264,"stroke":162,"style":195},"430","192",[191,283],{"x1":280,"y1":284,"x2":239,"y2":268,"stroke":162,"style":195},"248",[191,286],{"x1":280,"y1":287,"x2":239,"y2":276,"stroke":162,"style":195},"304",[289,290,291,292,125],"defs",{},"\n    ",[293,294,300,301,291],"marker",{"id":295,"markerWidth":296,"markerHeight":296,"refX":297,"refY":298,"orient":299},"ar","9","7","4.5","auto","\n      ",[302,303],"path",{"d":304,"fill":162},"M0,0 L9,4.5 L0,9 z",[15,306,307,308,311,312,86],{},"The key insight is that the provider does not run anything itself. It is a registry entry that advertises algorithms. All real work happens inside the algorithm classes, each of which subclasses ",[35,309,310],{},"QgsProcessingAlgorithm"," and implements ",[35,313,314],{},"processAlgorithm()",[65,316,318],{"id":317},"subclassing-qgsprocessingprovider","Subclassing QgsProcessingProvider",[15,320,321,322,54,325,96,328,331,332,334,335,86],{},"The provider class overrides a handful of methods. Three are mandatory in practice: ",[35,323,324],{},"id()",[35,326,327],{},"name()",[35,329,330],{},"loadAlgorithms()",". The rest customize presentation. The ",[35,333,330],{}," method is where you instantiate every algorithm and call ",[35,336,337],{},"addAlgorithm()",[339,340,345],"pre",{"className":341,"code":342,"language":343,"meta":344,"style":344},"language-python shiki shiki-themes github-dark","import os\nfrom qgis.PyQt.QtGui import QIcon\nfrom qgis.core import QgsProcessingProvider\n\nfrom .area_classifier_algorithm import AreaClassifierAlgorithm\nfrom .point_density_algorithm import PointDensityAlgorithm\n\n\nclass MyToolsProvider(QgsProcessingProvider):\n\n    def loadAlgorithms(self):\n        self.addAlgorithm(AreaClassifierAlgorithm())\n        self.addAlgorithm(PointDensityAlgorithm())\n\n    def id(self):\n        # Stable, lowercase, no spaces. Forms part of every algorithm id.\n        return \"mytools\"\n\n    def name(self):\n        # Human-readable label shown as the Toolbox group header.\n        return \"My Tools\"\n\n    def longName(self):\n        return \"My Tools — vector and raster utilities\"\n\n    def icon(self):\n        path = os.path.join(os.path.dirname(__file__), \"icons\", \"provider.svg\")\n        return QIcon(path)\n","python","",[35,346,347,359,373,386,393,406,419,424,429,447,452,464,474,482,487,497,504,514,519,529,535,543,548,558,566,571,581,610],{"__ignoreMap":344},[348,349,351,355],"span",{"class":191,"line":350},1,[348,352,354],{"class":353},"snl16","import",[348,356,358],{"class":357},"s95oV"," os\n",[348,360,362,365,368,370],{"class":191,"line":361},2,[348,363,364],{"class":353},"from",[348,366,367],{"class":357}," qgis.PyQt.QtGui ",[348,369,354],{"class":353},[348,371,372],{"class":357}," QIcon\n",[348,374,376,378,381,383],{"class":191,"line":375},3,[348,377,364],{"class":353},[348,379,380],{"class":357}," qgis.core ",[348,382,354],{"class":353},[348,384,385],{"class":357}," QgsProcessingProvider\n",[348,387,389],{"class":191,"line":388},4,[348,390,392],{"emptyLinePlaceholder":391},true,"\n",[348,394,396,398,401,403],{"class":191,"line":395},5,[348,397,364],{"class":353},[348,399,400],{"class":357}," .area_classifier_algorithm ",[348,402,354],{"class":353},[348,404,405],{"class":357}," AreaClassifierAlgorithm\n",[348,407,409,411,414,416],{"class":191,"line":408},6,[348,410,364],{"class":353},[348,412,413],{"class":357}," .point_density_algorithm ",[348,415,354],{"class":353},[348,417,418],{"class":357}," PointDensityAlgorithm\n",[348,420,422],{"class":191,"line":421},7,[348,423,392],{"emptyLinePlaceholder":391},[348,425,427],{"class":191,"line":426},8,[348,428,392],{"emptyLinePlaceholder":391},[348,430,432,435,439,442,444],{"class":191,"line":431},9,[348,433,434],{"class":353},"class",[348,436,438],{"class":437},"svObZ"," MyToolsProvider",[348,440,441],{"class":357},"(",[348,443,189],{"class":437},[348,445,446],{"class":357},"):\n",[348,448,450],{"class":191,"line":449},10,[348,451,392],{"emptyLinePlaceholder":391},[348,453,455,458,461],{"class":191,"line":454},11,[348,456,457],{"class":353},"    def",[348,459,460],{"class":437}," loadAlgorithms",[348,462,463],{"class":357},"(self):\n",[348,465,467,471],{"class":191,"line":466},12,[348,468,470],{"class":469},"sDLfK","        self",[348,472,473],{"class":357},".addAlgorithm(AreaClassifierAlgorithm())\n",[348,475,477,479],{"class":191,"line":476},13,[348,478,470],{"class":469},[348,480,481],{"class":357},".addAlgorithm(PointDensityAlgorithm())\n",[348,483,485],{"class":191,"line":484},14,[348,486,392],{"emptyLinePlaceholder":391},[348,488,490,492,495],{"class":191,"line":489},15,[348,491,457],{"class":353},[348,493,494],{"class":469}," id",[348,496,463],{"class":357},[348,498,500],{"class":191,"line":499},16,[348,501,503],{"class":502},"sjoCn","        # Stable, lowercase, no spaces. Forms part of every algorithm id.\n",[348,505,507,510],{"class":191,"line":506},17,[348,508,509],{"class":353},"        return",[348,511,513],{"class":512},"sU2Wk"," \"mytools\"\n",[348,515,517],{"class":191,"line":516},18,[348,518,392],{"emptyLinePlaceholder":391},[348,520,522,524,527],{"class":191,"line":521},19,[348,523,457],{"class":353},[348,525,526],{"class":437}," name",[348,528,463],{"class":357},[348,530,532],{"class":191,"line":531},20,[348,533,534],{"class":502},"        # Human-readable label shown as the Toolbox group header.\n",[348,536,538,540],{"class":191,"line":537},21,[348,539,509],{"class":353},[348,541,542],{"class":512}," \"My Tools\"\n",[348,544,546],{"class":191,"line":545},22,[348,547,392],{"emptyLinePlaceholder":391},[348,549,551,553,556],{"class":191,"line":550},23,[348,552,457],{"class":353},[348,554,555],{"class":437}," longName",[348,557,463],{"class":357},[348,559,561,563],{"class":191,"line":560},24,[348,562,509],{"class":353},[348,564,565],{"class":512}," \"My Tools — vector and raster utilities\"\n",[348,567,569],{"class":191,"line":568},25,[348,570,392],{"emptyLinePlaceholder":391},[348,572,574,576,579],{"class":191,"line":573},26,[348,575,457],{"class":353},[348,577,578],{"class":437}," icon",[348,580,463],{"class":357},[348,582,584,587,590,593,596,599,602,604,607],{"class":191,"line":583},27,[348,585,586],{"class":357},"        path ",[348,588,589],{"class":353},"=",[348,591,592],{"class":357}," os.path.join(os.path.dirname(",[348,594,595],{"class":469},"__file__",[348,597,598],{"class":357},"), ",[348,600,601],{"class":512},"\"icons\"",[348,603,54],{"class":357},[348,605,606],{"class":512},"\"provider.svg\"",[348,608,609],{"class":357},")\n",[348,611,613,615],{"class":191,"line":612},28,[348,614,509],{"class":353},[348,616,617],{"class":357}," QIcon(path)\n",[15,619,620,623,624,626,627,630,631,633,634,636,637,640,641,644],{},[76,621,622],{},"Breakdown:"," ",[35,625,324],{}," returns a permanent identifier — never change it after release, because saved Processing models and scripts reference algorithms as ",[35,628,629],{},"provider_id:algorithm_id",". ",[35,632,327],{}," is the display label and may change freely. ",[35,635,330],{}," is called by the framework every time algorithms are refreshed, so it must create fresh instances; do not cache them. ",[35,638,639],{},"icon()"," returns a ",[35,642,643],{},"QIcon","; if you omit it, the provider shows a generic gear.",[65,646,648],{"id":647},"registering-the-provider-in-initprocessing","Registering the Provider in initProcessing",[15,650,651,652,654,655,658,659,661],{},"A plugin exposes its provider through a dedicated ",[35,653,168],{}," method that runs alongside the usual GUI setup. Registration uses the global Processing registry, retrieved from ",[35,656,657],{},"QgsApplication.processingRegistry()",". Crucially, you must remove the provider in ",[35,660,172],{}," so disabling or reloading the plugin does not leave a stale entry.",[119,663,125,666,125,669,125,672,125,674,125,687,125,691,125,695,125,700,125,705,125,709,125,712,125,714,125,717,125,721,125,723,125,726,125,730,125,734,125,736,125,739,125,741,125,745,125,748,125,754,125,758,125,764,125,768,125,773,125,778,125,784,125,789,125,794],{"viewBox":664,"role":122,"ariaLabel":665,"xmlns":124},"0 0 760 430","Symmetrical provider lifecycle: on enable, initGui calls initProcessing which calls addProvider; on disable or reload, unload calls removeProvider. The registry should hold the provider exactly once. Omitting removeProvider stacks a duplicate group in the Toolbox on every reload.",[127,667,668],{},"Provider registration and teardown lifecycle",[131,670,671],{},"Two mirrored paths feed the Processing registry. The enable path (initGui to addProvider) registers the provider; the teardown path (unload to removeProvider) removes it. Balanced, the registry holds My Tools once. If removeProvider is skipped, each reload stacks another duplicate provider group.",[135,673],{"x":137,"y":137,"width":138,"height":280,"fill":140},[289,675,291,676,291,681,125],{},[293,677,679],{"id":678,"markerWidth":296,"markerHeight":296,"refX":297,"refY":298,"orient":299},"arwPD",[302,680],{"d":304,"fill":162},[293,682,684],{"id":683,"markerWidth":296,"markerHeight":296,"refX":297,"refY":298,"orient":299},"arwRedPD",[302,685],{"d":304,"fill":686},"#b91c1c",[151,688,690],{"x":689,"y":176,"fill":148,"style":183,"textAnchor":157},"190","Enable \u002F reload → register",[151,692,694],{"x":693,"y":176,"fill":178,"style":183,"textAnchor":157},"570","Disable \u002F reload → remove",[135,696],{"x":247,"y":697,"width":698,"height":699,"rx":146,"fill":147,"stroke":148,"style":149},"56","240","50",[151,701,704],{"x":689,"y":702,"fill":155,"style":703,"textAnchor":157},"80","text-anchor:middle;font-family:monospace;font-size:14px;font-weight:bold","initGui() → initProcessing()",[151,706,708],{"x":689,"y":187,"fill":162,"style":707,"textAnchor":157},"text-anchor:middle;font-family:sans-serif;font-size:11px","enable hook (QGIS calls this)",[135,710],{"x":711,"y":697,"width":698,"height":699,"rx":146,"fill":147,"stroke":178,"style":149},"450",[151,713,172],{"x":693,"y":702,"fill":155,"style":703,"textAnchor":157},[151,715,716],{"x":693,"y":187,"fill":162,"style":707,"textAnchor":157},"disable \u002F reload hook",[191,718],{"x1":689,"y1":167,"x2":689,"y2":719,"stroke":162,"style":720},"136","stroke-width:2.5;marker-end:url(#arwPD)",[191,722],{"x1":693,"y1":167,"x2":693,"y2":719,"stroke":162,"style":720},[135,724],{"x":247,"y":725,"width":698,"height":697,"rx":146,"fill":242,"stroke":243,"style":149},"138",[151,727,729],{"x":689,"y":728,"fill":248,"style":163,"textAnchor":157},"163","registry.addProvider(",[151,731,733],{"x":689,"y":732,"fill":248,"style":163,"textAnchor":157},"181","self.provider)",[135,735],{"x":711,"y":725,"width":698,"height":697,"rx":146,"fill":242,"stroke":178,"style":149},[151,737,738],{"x":693,"y":728,"fill":248,"style":163,"textAnchor":157},"registry.removeProvider(",[151,740,733],{"x":693,"y":732,"fill":248,"style":163,"textAnchor":157},[191,742],{"x1":689,"y1":743,"x2":284,"y2":744,"stroke":162,"style":720},"194","250",[191,746],{"x1":693,"y1":743,"x2":747,"y2":744,"stroke":162,"style":720},"512",[151,749,753],{"x":750,"y":751,"fill":243,"style":752,"textAnchor":157},"212","230","text-anchor:middle;font-family:sans-serif;font-size:12px;font-weight:bold","+ add",[151,755,757],{"x":756,"y":751,"fill":178,"style":752,"textAnchor":157},"548","− remove",[135,759],{"x":760,"y":198,"width":761,"height":762,"rx":763,"fill":147,"stroke":208,"style":149},"210","340","66","10",[151,765,767],{"x":181,"y":175,"fill":155,"style":766,"textAnchor":157},"text-anchor:middle;font-family:sans-serif;font-size:14px;font-weight:bold","Processing registry",[151,769,772],{"x":181,"y":770,"fill":162,"style":771,"textAnchor":157},"303","text-anchor:middle;font-family:sans-serif;font-size:12px","balanced → holds “My Tools” exactly once",[191,774],{"x1":181,"y1":775,"x2":181,"y2":776,"stroke":686,"style":777},"318","352","stroke-width:2.5;stroke-dasharray:6 4;marker-end:url(#arwRedPD)",[151,779,783],{"x":780,"y":761,"fill":686,"style":781,"textAnchor":782},"392","text-anchor:start;font-family:sans-serif;font-size:11px","start","removeProvider() skipped",[135,785],{"x":253,"y":786,"width":252,"height":787,"rx":146,"fill":147,"stroke":686,"style":788},"354","60","stroke-width:2.5;stroke-dasharray:7 5",[151,790,793],{"x":181,"y":791,"fill":686,"style":792,"textAnchor":157},"379","text-anchor:middle;font-family:sans-serif;font-size:13px;font-weight:bold","Unbalanced teardown",[151,795,797],{"x":181,"y":796,"fill":162,"style":771,"textAnchor":157},"400","every reload stacks another duplicate “My Tools” group in the Toolbox",[339,799,801],{"className":341,"code":800,"language":343,"meta":344,"style":344},"from qgis.core import QgsApplication\nfrom .provider import MyToolsProvider\n\n\nclass MyToolsPlugin:\n    def __init__(self, iface):\n        self.iface = iface\n        self.provider = None\n\n    def initProcessing(self):\n        \"\"\"Create and register the provider with the Processing registry.\"\"\"\n        self.provider = MyToolsProvider()\n        QgsApplication.processingRegistry().addProvider(self.provider)\n\n    def initGui(self):\n        # Called by QGIS when the plugin is enabled.\n        self.initProcessing()\n\n    def unload(self):\n        # Symmetrical teardown — required to avoid duplicate providers on reload.\n        if self.provider is not None:\n            QgsApplication.processingRegistry().removeProvider(self.provider)\n            self.provider = None\n",[35,802,803,814,826,830,834,844,854,866,878,882,891,896,907,918,922,931,936,943,947,956,961,982,991],{"__ignoreMap":344},[348,804,805,807,809,811],{"class":191,"line":350},[348,806,364],{"class":353},[348,808,380],{"class":357},[348,810,354],{"class":353},[348,812,813],{"class":357}," QgsApplication\n",[348,815,816,818,821,823],{"class":191,"line":361},[348,817,364],{"class":353},[348,819,820],{"class":357}," .provider ",[348,822,354],{"class":353},[348,824,825],{"class":357}," MyToolsProvider\n",[348,827,828],{"class":191,"line":375},[348,829,392],{"emptyLinePlaceholder":391},[348,831,832],{"class":191,"line":388},[348,833,392],{"emptyLinePlaceholder":391},[348,835,836,838,841],{"class":191,"line":395},[348,837,434],{"class":353},[348,839,840],{"class":437}," MyToolsPlugin",[348,842,843],{"class":357},":\n",[348,845,846,848,851],{"class":191,"line":408},[348,847,457],{"class":353},[348,849,850],{"class":469}," __init__",[348,852,853],{"class":357},"(self, iface):\n",[348,855,856,858,861,863],{"class":191,"line":421},[348,857,470],{"class":469},[348,859,860],{"class":357},".iface ",[348,862,589],{"class":353},[348,864,865],{"class":357}," iface\n",[348,867,868,870,873,875],{"class":191,"line":426},[348,869,470],{"class":469},[348,871,872],{"class":357},".provider ",[348,874,589],{"class":353},[348,876,877],{"class":469}," None\n",[348,879,880],{"class":191,"line":431},[348,881,392],{"emptyLinePlaceholder":391},[348,883,884,886,889],{"class":191,"line":449},[348,885,457],{"class":353},[348,887,888],{"class":437}," initProcessing",[348,890,463],{"class":357},[348,892,893],{"class":191,"line":454},[348,894,895],{"class":512},"        \"\"\"Create and register the provider with the Processing registry.\"\"\"\n",[348,897,898,900,902,904],{"class":191,"line":466},[348,899,470],{"class":469},[348,901,872],{"class":357},[348,903,589],{"class":353},[348,905,906],{"class":357}," MyToolsProvider()\n",[348,908,909,912,915],{"class":191,"line":476},[348,910,911],{"class":357},"        QgsApplication.processingRegistry().addProvider(",[348,913,914],{"class":469},"self",[348,916,917],{"class":357},".provider)\n",[348,919,920],{"class":191,"line":484},[348,921,392],{"emptyLinePlaceholder":391},[348,923,924,926,929],{"class":191,"line":489},[348,925,457],{"class":353},[348,927,928],{"class":437}," initGui",[348,930,463],{"class":357},[348,932,933],{"class":191,"line":499},[348,934,935],{"class":502},"        # Called by QGIS when the plugin is enabled.\n",[348,937,938,940],{"class":191,"line":506},[348,939,470],{"class":469},[348,941,942],{"class":357},".initProcessing()\n",[348,944,945],{"class":191,"line":516},[348,946,392],{"emptyLinePlaceholder":391},[348,948,949,951,954],{"class":191,"line":521},[348,950,457],{"class":353},[348,952,953],{"class":437}," unload",[348,955,463],{"class":357},[348,957,958],{"class":191,"line":531},[348,959,960],{"class":502},"        # Symmetrical teardown — required to avoid duplicate providers on reload.\n",[348,962,963,966,969,971,974,977,980],{"class":191,"line":537},[348,964,965],{"class":353},"        if",[348,967,968],{"class":469}," self",[348,970,872],{"class":357},[348,972,973],{"class":353},"is",[348,975,976],{"class":353}," not",[348,978,979],{"class":469}," None",[348,981,843],{"class":357},[348,983,984,987,989],{"class":191,"line":545},[348,985,986],{"class":357},"            QgsApplication.processingRegistry().removeProvider(",[348,988,914],{"class":469},[348,990,917],{"class":357},[348,992,993,996,998,1000],{"class":191,"line":550},[348,994,995],{"class":469},"            self",[348,997,872],{"class":357},[348,999,589],{"class":353},[348,1001,877],{"class":469},[15,1003,1004,1006,1007,1009,1010,1013,1014,1017,1018,1020,1021,1024,1025,1027,1028,1031,1032,1034,1035,1038],{},[76,1005,622],{}," Keeping ",[35,1008,168],{}," separate from ",[35,1011,1012],{},"initGui()"," mirrors the convention used by the Plugin Builder template and keeps the registration logic discoverable. ",[35,1015,1016],{},"addProvider()"," triggers ",[35,1019,330],{}," immediately. The ",[35,1022,1023],{},"removeProvider()"," call in ",[35,1026,172],{}," is the single most common omission; without it, the ",[21,1029,1030],{"href":48},"Plugin Reloader"," will stack duplicate \"My Tools\" groups in the Toolbox every time you reload. Storing the provider on ",[35,1033,914],{}," and guarding with ",[35,1036,1037],{},"is not None"," makes teardown idempotent.",[65,1040,1042],{"id":1041},"grouping-algorithms-within-the-provider","Grouping Algorithms Within the Provider",[15,1044,1045,1046,1049,1050,1053,1054,1056],{},"A provider can hold dozens of algorithms, so grouping keeps the Toolbox navigable. Grouping is controlled per algorithm, not by the provider, through the algorithm's ",[35,1047,1048],{},"group()"," and ",[35,1051,1052],{},"groupId()"," methods. Algorithms sharing the same ",[35,1055,1052],{}," collapse under one sub-node beneath the provider header.",[339,1058,1060],{"className":341,"code":1059,"language":343,"meta":344,"style":344},"# Inside an algorithm class:\ndef group(self):\n    return \"Vector\"        # Display label for the sub-group\n\ndef groupId(self):\n    return \"vector\"        # Stable identifier used for matching\n",[35,1061,1062,1067,1077,1088,1092,1101],{"__ignoreMap":344},[348,1063,1064],{"class":191,"line":350},[348,1065,1066],{"class":502},"# Inside an algorithm class:\n",[348,1068,1069,1072,1075],{"class":191,"line":361},[348,1070,1071],{"class":353},"def",[348,1073,1074],{"class":437}," group",[348,1076,463],{"class":357},[348,1078,1079,1082,1085],{"class":191,"line":375},[348,1080,1081],{"class":353},"    return",[348,1083,1084],{"class":512}," \"Vector\"",[348,1086,1087],{"class":502},"        # Display label for the sub-group\n",[348,1089,1090],{"class":191,"line":388},[348,1091,392],{"emptyLinePlaceholder":391},[348,1093,1094,1096,1099],{"class":191,"line":395},[348,1095,1071],{"class":353},[348,1097,1098],{"class":437}," groupId",[348,1100,463],{"class":357},[348,1102,1103,1105,1108],{"class":191,"line":408},[348,1104,1081],{"class":353},[348,1106,1107],{"class":512}," \"vector\"",[348,1109,1110],{"class":502},"        # Stable identifier used for matching\n",[15,1112,1113,1115,1116,1118,1119,1121,1122,86],{},[76,1114,622],{}," Return an empty string from both methods to place an algorithm directly under the provider with no sub-group. As with the provider id, treat ",[35,1117,1052],{}," as stable and ",[35,1120,1048],{}," as cosmetic. Choose group names that read well next to native QGIS groups — \"Vector geometry\", \"Raster terrain\", \"Data management\" — so users can guess where your tools live. The full algorithm class that these methods belong to is built step by step in ",[21,1123,107],{"href":106},[65,1125,1127],{"id":1126},"provider-icons-and-branding","Provider Icons and Branding",[15,1129,1130,1131,1133,1134,1136],{},"Icons appear in three places: the provider header in the Toolbox, each algorithm entry, and the generated algorithm dialog title bar. The provider's ",[35,1132,639],{}," supplies a default that algorithms inherit unless they override their own ",[35,1135,639],{},". Prefer SVG so the icon scales across high-DPI displays.",[339,1138,1140],{"className":341,"code":1139,"language":343,"meta":344,"style":344},"import os\nfrom qgis.PyQt.QtGui import QIcon\nfrom qgis.core import QgsProcessingAlgorithm\n\n\nclass AreaClassifierAlgorithm(QgsProcessingAlgorithm):\n\n    def icon(self):\n        path = os.path.join(\n            os.path.dirname(__file__), \"icons\", \"classify.svg\")\n        return QIcon(path)\n\n    def svgIconPath(self):\n        # Used where QGIS needs a path rather than a QIcon object.\n        return os.path.join(\n            os.path.dirname(__file__), \"icons\", \"classify.svg\")\n",[35,1141,1142,1148,1158,1169,1173,1177,1190,1194,1202,1211,1229,1235,1239,1248,1253,1259],{"__ignoreMap":344},[348,1143,1144,1146],{"class":191,"line":350},[348,1145,354],{"class":353},[348,1147,358],{"class":357},[348,1149,1150,1152,1154,1156],{"class":191,"line":361},[348,1151,364],{"class":353},[348,1153,367],{"class":357},[348,1155,354],{"class":353},[348,1157,372],{"class":357},[348,1159,1160,1162,1164,1166],{"class":191,"line":375},[348,1161,364],{"class":353},[348,1163,380],{"class":357},[348,1165,354],{"class":353},[348,1167,1168],{"class":357}," QgsProcessingAlgorithm\n",[348,1170,1171],{"class":191,"line":388},[348,1172,392],{"emptyLinePlaceholder":391},[348,1174,1175],{"class":191,"line":395},[348,1176,392],{"emptyLinePlaceholder":391},[348,1178,1179,1181,1184,1186,1188],{"class":191,"line":408},[348,1180,434],{"class":353},[348,1182,1183],{"class":437}," AreaClassifierAlgorithm",[348,1185,441],{"class":357},[348,1187,310],{"class":437},[348,1189,446],{"class":357},[348,1191,1192],{"class":191,"line":421},[348,1193,392],{"emptyLinePlaceholder":391},[348,1195,1196,1198,1200],{"class":191,"line":426},[348,1197,457],{"class":353},[348,1199,578],{"class":437},[348,1201,463],{"class":357},[348,1203,1204,1206,1208],{"class":191,"line":431},[348,1205,586],{"class":357},[348,1207,589],{"class":353},[348,1209,1210],{"class":357}," os.path.join(\n",[348,1212,1213,1216,1218,1220,1222,1224,1227],{"class":191,"line":449},[348,1214,1215],{"class":357},"            os.path.dirname(",[348,1217,595],{"class":469},[348,1219,598],{"class":357},[348,1221,601],{"class":512},[348,1223,54],{"class":357},[348,1225,1226],{"class":512},"\"classify.svg\"",[348,1228,609],{"class":357},[348,1230,1231,1233],{"class":191,"line":454},[348,1232,509],{"class":353},[348,1234,617],{"class":357},[348,1236,1237],{"class":191,"line":466},[348,1238,392],{"emptyLinePlaceholder":391},[348,1240,1241,1243,1246],{"class":191,"line":476},[348,1242,457],{"class":353},[348,1244,1245],{"class":437}," svgIconPath",[348,1247,463],{"class":357},[348,1249,1250],{"class":191,"line":484},[348,1251,1252],{"class":502},"        # Used where QGIS needs a path rather than a QIcon object.\n",[348,1254,1255,1257],{"class":191,"line":489},[348,1256,509],{"class":353},[348,1258,1210],{"class":357},[348,1260,1261,1263,1265,1267,1269,1271,1273],{"class":191,"line":499},[348,1262,1215],{"class":357},[348,1264,595],{"class":469},[348,1266,598],{"class":357},[348,1268,601],{"class":512},[348,1270,54],{"class":357},[348,1272,1226],{"class":512},[348,1274,609],{"class":357},[15,1276,1277,1279,1280,1283,1284,1287,1288,1290,1291,1294,1295,1298,1299,86],{},[76,1278,622],{}," Resolve icon paths with ",[35,1281,1282],{},"os.path.dirname(__file__)"," rather than a relative string, because Processing does not guarantee the working directory. Implement ",[35,1285,1286],{},"svgIconPath()"," in addition to ",[35,1289,639],{}," so help generation and the Model Designer can locate the asset. If you compiled icons into a Qt resource file (",[35,1292,1293],{},"resources_rc.py","), reference them with the ",[35,1296,1297],{},":\u002F"," prefix, for example ",[35,1300,1301],{},"QIcon(\":\u002Fplugins\u002Fmytools\u002Fclassify.svg\")",[65,1303,1305],{"id":1304},"provider-algorithms-vs-script-algorithms","Provider Algorithms vs. Script Algorithms",[15,1307,1308,1309,1311,1312,1315,1316,1319,1320,1323,1324,1327],{},"Both approaches subclass ",[35,1310,310],{},", but they are deployed and discovered differently. A ",[18,1313,1314],{},"script algorithm"," is a single ",[35,1317,1318],{},".py"," file dropped into the user's ",[35,1321,1322],{},"processing\u002Fscripts\u002F"," folder (or added via the Toolbox script editor). It appears under the built-in \"Scripts\" provider and requires no plugin. A ",[18,1325,1326],{},"provider algorithm"," ships inside a plugin, is grouped under your own branded provider, and is installable and updatable through the QGIS Plugin Repository.",[1329,1330,1331,1347],"table",{},[1332,1333,1334],"thead",{},[1335,1336,1337,1341,1344],"tr",{},[1338,1339,1340],"th",{},"Aspect",[1338,1342,1343],{},"Script algorithm",[1338,1345,1346],{},"Provider plugin algorithm",[1348,1349,1350,1365,1376,1390,1410,1421,1432],"tbody",{},[1335,1351,1352,1356,1362],{},[1353,1354,1355],"td",{},"Packaging",[1353,1357,1358,1359,1361],{},"Single ",[35,1360,1318],{}," file",[1353,1363,1364],{},"Full plugin package",[1335,1366,1367,1370,1373],{},[1353,1368,1369],{},"Discovery",[1353,1371,1372],{},"Built-in \"Scripts\" provider",[1353,1374,1375],{},"Your named provider",[1335,1377,1378,1381,1384],{},[1353,1379,1380],{},"Distribution",[1353,1382,1383],{},"Manual copy or shared file",[1353,1385,1386],{},[21,1387,1389],{"href":1388},"\u002Fqgis-plugin-development\u002Fpublishing-to-the-qgis-plugin-repository\u002F","QGIS Plugin Repository",[1335,1391,1392,1395,1398],{},[1353,1393,1394],{},"Grouping\u002Fbranding",[1353,1396,1397],{},"Limited",[1353,1399,1400,1401,23,1404,23,1407],{},"Full control via ",[35,1402,1403],{},"id",[35,1405,1406],{},"name",[35,1408,1409],{},"icon",[1335,1411,1412,1415,1418],{},[1353,1413,1414],{},"Auto-update",[1353,1416,1417],{},"None",[1353,1419,1420],{},"Through plugin manager",[1335,1422,1423,1426,1429],{},[1353,1424,1425],{},"Multiple algorithms",[1353,1427,1428],{},"One file each",[1353,1430,1431],{},"Many in one provider",[1335,1433,1434,1437,1440],{},[1353,1435,1436],{},"Bundled resources",[1353,1438,1439],{},"Awkward",[1353,1441,1442],{},"Natural (icons, data, helpers)",[15,1444,1445,1446,86],{},"For a one-off utility you keep on a single workstation, a script is faster. For anything shared with a team, distributed publicly, or composed of several related tools, a provider plugin is the right unit. The algorithm code itself is nearly identical, which means you can prototype as a script and later move the same class into a provider with minimal changes. To learn how the resulting algorithms are invoked programmatically, see ",[21,1447,1449],{"href":1448},"\u002Fspatial-data-processing-automation\u002Fbatch-processing-with-pyqgis\u002Frun-processing-algorithm-from-script\u002F","Run a Processing Algorithm from a Script",[65,1451,1453],{"id":1452},"calling-your-providers-algorithms","Calling Your Provider's Algorithms",[15,1455,1456,1457,1459,1460,86],{},"Once registered, your algorithms are addressable by their fully qualified id, ",[35,1458,629],{},". This is what makes a provider so much more useful than a button: any script, model, or other plugin can run your tool through ",[35,1461,37],{},[339,1463,1465],{"className":341,"code":1464,"language":343,"meta":344,"style":344},"import processing\n\nresult = processing.run(\"mytools:areaclassifier\", {\n    \"INPUT\": \"\u002Fdata\u002Fparcels.gpkg|layername=parcels\",\n    \"THRESHOLD\": 1000.0,\n    \"OUTPUT\": \"memory:classified\",\n})\nclassified_layer = result[\"OUTPUT\"]\nprint(classified_layer.featureCount(), \"features classified\")\n",[35,1466,1467,1474,1478,1494,1508,1520,1532,1537,1553],{"__ignoreMap":344},[348,1468,1469,1471],{"class":191,"line":350},[348,1470,354],{"class":353},[348,1472,1473],{"class":357}," processing\n",[348,1475,1476],{"class":191,"line":361},[348,1477,392],{"emptyLinePlaceholder":391},[348,1479,1480,1483,1485,1488,1491],{"class":191,"line":375},[348,1481,1482],{"class":357},"result ",[348,1484,589],{"class":353},[348,1486,1487],{"class":357}," processing.run(",[348,1489,1490],{"class":512},"\"mytools:areaclassifier\"",[348,1492,1493],{"class":357},", {\n",[348,1495,1496,1499,1502,1505],{"class":191,"line":388},[348,1497,1498],{"class":512},"    \"INPUT\"",[348,1500,1501],{"class":357},": ",[348,1503,1504],{"class":512},"\"\u002Fdata\u002Fparcels.gpkg|layername=parcels\"",[348,1506,1507],{"class":357},",\n",[348,1509,1510,1513,1515,1518],{"class":191,"line":395},[348,1511,1512],{"class":512},"    \"THRESHOLD\"",[348,1514,1501],{"class":357},[348,1516,1517],{"class":469},"1000.0",[348,1519,1507],{"class":357},[348,1521,1522,1525,1527,1530],{"class":191,"line":408},[348,1523,1524],{"class":512},"    \"OUTPUT\"",[348,1526,1501],{"class":357},[348,1528,1529],{"class":512},"\"memory:classified\"",[348,1531,1507],{"class":357},[348,1533,1534],{"class":191,"line":421},[348,1535,1536],{"class":357},"})\n",[348,1538,1539,1542,1544,1547,1550],{"class":191,"line":426},[348,1540,1541],{"class":357},"classified_layer ",[348,1543,589],{"class":353},[348,1545,1546],{"class":357}," result[",[348,1548,1549],{"class":512},"\"OUTPUT\"",[348,1551,1552],{"class":357},"]\n",[348,1554,1555,1558,1561,1564],{"class":191,"line":431},[348,1556,1557],{"class":469},"print",[348,1559,1560],{"class":357},"(classified_layer.featureCount(), ",[348,1562,1563],{"class":512},"\"features classified\"",[348,1565,609],{"class":357},[15,1567,1568,1570,1571,1574,1575,1577,1578,1580,1581,1584,1585,1589,1590,1592,1593,1596],{},[76,1569,622],{}," The id ",[35,1572,1573],{},"mytools:areaclassifier"," combines the provider's ",[35,1576,324],{}," with the algorithm's ",[35,1579,327],{},". The parameter dictionary keys are the parameter names you defined in ",[35,1582,1583],{},"initAlgorithm()",". Because the call goes through Processing, the algorithm participates in batch runs and can be chained — see ",[21,1586,1588],{"href":1587},"\u002Fspatial-data-processing-automation\u002Fchaining-processing-algorithms\u002F","Chaining Processing Algorithms"," for composing several into a pipeline. Use ",[35,1591,37],{}," for headless execution; use ",[35,1594,1595],{},"processing.runAndLoadResults()"," when you want the output added to the active project.",[65,1598,1600],{"id":1599},"why-a-provider-is-worth-the-extra-work","Why a provider is worth the extra work",[15,1602,1603],{},"Wrapping logic as a Processing algorithm rather than a plugin dialog costs a little more code and buys a set of capabilities that would each take considerable effort to build by hand.",[15,1605,1606],{},[119,1607,1610,1613,1616,1619,1630,1635,1641,1646,1650,1696,1701,1703,1706,1708,1711,1713],{"viewBox":1608,"role":122,"ariaLabel":1609,"xmlns":124},"0 0 760 254","Capabilities a Processing algorithm gains for free: the toolbox entry, batch mode, the graphical modeller, history, headless invocation and a generated dialog",[127,1611,1612],{},"What registering as an algorithm gives you",[131,1614,1615],{},"One algorithm registered with a provider appears in the Processing toolbox, gains a batch-processing mode, can be dropped into the graphical modeller, is recorded in the history log, can be called from a headless script, and gets an automatically generated parameter dialog. None of these need any additional code.",[135,1617],{"x":137,"y":137,"width":138,"height":1618,"fill":140},"254",[289,1620,1621],{},[293,1622,1627],{"id":1623,"viewBox":1624,"refX":146,"refY":1625,"markerWidth":297,"markerHeight":297,"orient":1626},"provGainArrow","0 0 10 10","5","auto-start-reverse",[302,1628],{"d":1629,"fill":148},"M0 0 L10 5 L0 10 z",[151,1631,1634],{"x":181,"y":1632,"style":1633,"fill":155,"textAnchor":157},"26","text-anchor:middle;font-size:14px;font-weight:bold;font-family:sans-serif","One registration, six capabilities you did not write",[135,1636],{"x":1637,"y":1638,"width":1639,"height":1640,"rx":763,"fill":242,"stroke":148,"style":149},"16","96","176","72",[151,1642,1645],{"x":1643,"y":153,"style":1644,"fill":248,"textAnchor":157},"104","text-anchor:middle;font-size:11.5px;font-weight:bold;font-family:sans-serif","your algorithm",[151,1647,314],{"x":1643,"y":1648,"style":1649,"fill":248,"textAnchor":157},"146","text-anchor:middle;font-size:10.5px;font-family:sans-serif",[1651,1652,1654,1660,1665,1667,1670,1673,1677,1680,1683,1687,1691,1693],"g",{"style":1653},"font-size:11px;font-family:sans-serif",[135,1655],{"x":1656,"y":1657,"width":240,"height":1658,"rx":207,"fill":147,"stroke":243,"style":1659},"272","46","34","stroke-width:1.8",[151,1661,1664],{"x":181,"y":1662,"style":1663,"fill":162,"textAnchor":157},"68","text-anchor:middle","a toolbox entry",[135,1666],{"x":1656,"y":177,"width":240,"height":1658,"rx":207,"fill":147,"stroke":243,"style":1659},[151,1668,1669],{"x":181,"y":253,"style":1663,"fill":162,"textAnchor":157},"a generated parameter dialog",[135,1671],{"x":1656,"y":1672,"width":240,"height":1658,"rx":207,"fill":147,"stroke":243,"style":1659},"130",[151,1674,1676],{"x":181,"y":1675,"style":1663,"fill":162,"textAnchor":157},"152","batch mode over many inputs",[135,1678],{"x":1656,"y":1679,"width":240,"height":1658,"rx":207,"fill":147,"stroke":243,"style":1659},"172",[151,1681,1682],{"x":181,"y":743,"style":1663,"fill":162,"textAnchor":157},"a node in the modeller",[135,1684],{"x":1685,"y":177,"width":1686,"height":1658,"rx":207,"fill":147,"stroke":208,"style":1659},"524","220",[151,1688,1690],{"x":1689,"y":253,"style":1663,"fill":162,"textAnchor":157},"634","an entry in the history log",[135,1692],{"x":1685,"y":1672,"width":1686,"height":1658,"rx":207,"fill":147,"stroke":208,"style":1659},[151,1694,1695],{"x":1689,"y":1675,"style":1663,"fill":162,"textAnchor":157},"callable from a headless script",[191,1697],{"x1":281,"y1":1698,"x2":1699,"y2":762,"stroke":148,"style":1700},"118","268","stroke-width:2;marker-end:url(#provGainArrow)",[191,1702],{"x1":281,"y1":171,"x2":1699,"y2":167,"stroke":148,"style":1700},[191,1704],{"x1":281,"y1":725,"x2":1699,"y2":1705,"stroke":148,"style":1700},"148",[191,1707],{"x1":281,"y1":1648,"x2":1699,"y2":689,"stroke":148,"style":1700},[191,1709],{"x1":1710,"y1":167,"x2":239,"y2":167,"stroke":208,"style":1700},"488",[191,1712],{"x1":1710,"y1":1705,"x2":239,"y2":1705,"stroke":208,"style":1700},[151,1714,1718],{"x":181,"y":1715,"style":1716,"fill":1717,"textAnchor":157},"234","text-anchor:middle;font-size:11px;font-family:sans-serif","#59645f","a dialog-based plugin gets exactly none of these",[15,1720,1721],{},"The modeller entry is the one that changes how people use your work. An algorithm can be chained into someone else's workflow without them reading your code or even knowing it is yours — they drag it onto a canvas, wire its inputs, and it behaves like every built-in tool. That composability is why the Processing framework exists, and it is not something a dialog-based plugin can offer at any price.",[15,1723,1724],{},"Batch mode is a close second. The generated batch interface lets a user run your algorithm over a folder of inputs with per-row parameter overrides, complete with a progress bar and cancellation — a feature that would take a substantial amount of interface code to build and maintain yourself.",[65,1726,1728],{"id":1727},"provider-identity-and-why-it-must-be-stable","Provider identity and why it must be stable",[15,1730,1731],{},"A provider is identified by three strings, and each is used somewhere different. Getting them right at the start avoids a migration later.",[15,1733,1734,1735,1738,1739,1742,1743,1746],{},"The ",[76,1736,1737],{},"provider id"," is the namespace that prefixes every algorithm — ",[35,1740,1741],{},"acme"," produces ",[35,1744,1745],{},"acme:buffer_and_clip",". It appears in every saved model, every script that calls your algorithm, and every history entry. Changing it breaks all of them silently, because a model referencing a missing algorithm id simply fails to load that node. Choose it once, keep it lowercase and short, and never revise it.",[15,1748,1734,1749,1752,1753,1049,1756,1759],{},[76,1750,1751],{},"algorithm name"," is the second half of that identifier and has the same constraint. The ",[76,1754,1755],{},"display name",[76,1757,1758],{},"group"," are pure presentation and can be changed freely, including being translated, because nothing stores them.",[339,1761,1763],{"className":341,"code":1762,"language":343,"meta":344,"style":344},"class AcmeProvider(QgsProcessingProvider):\n    def id(self):\n        return \"acme\"                 # never change this\n\n    def name(self):\n        return \"Acme Tools\"           # safe to change or translate\n\n    def loadAlgorithms(self):\n        self.addAlgorithm(BufferAndClipAlgorithm())\n",[35,1764,1765,1778,1786,1796,1800,1808,1818,1822,1830],{"__ignoreMap":344},[348,1766,1767,1769,1772,1774,1776],{"class":191,"line":350},[348,1768,434],{"class":353},[348,1770,1771],{"class":437}," AcmeProvider",[348,1773,441],{"class":357},[348,1775,189],{"class":437},[348,1777,446],{"class":357},[348,1779,1780,1782,1784],{"class":191,"line":361},[348,1781,457],{"class":353},[348,1783,494],{"class":469},[348,1785,463],{"class":357},[348,1787,1788,1790,1793],{"class":191,"line":375},[348,1789,509],{"class":353},[348,1791,1792],{"class":512}," \"acme\"",[348,1794,1795],{"class":502},"                 # never change this\n",[348,1797,1798],{"class":191,"line":388},[348,1799,392],{"emptyLinePlaceholder":391},[348,1801,1802,1804,1806],{"class":191,"line":395},[348,1803,457],{"class":353},[348,1805,526],{"class":437},[348,1807,463],{"class":357},[348,1809,1810,1812,1815],{"class":191,"line":408},[348,1811,509],{"class":353},[348,1813,1814],{"class":512}," \"Acme Tools\"",[348,1816,1817],{"class":502},"           # safe to change or translate\n",[348,1819,1820],{"class":191,"line":421},[348,1821,392],{"emptyLinePlaceholder":391},[348,1823,1824,1826,1828],{"class":191,"line":426},[348,1825,457],{"class":353},[348,1827,460],{"class":437},[348,1829,463],{"class":357},[348,1831,1832,1834],{"class":191,"line":431},[348,1833,470],{"class":469},[348,1835,1836],{"class":357},".addAlgorithm(BufferAndClipAlgorithm())\n",[15,1838,1839,623,1841,1843,1844,1846,1847,1849],{},[76,1840,622],{},[35,1842,324],{}," is the permanent one and the only string with compatibility implications. ",[35,1845,327],{}," is what a user sees at the top of the toolbox group and is free to change. ",[35,1848,330],{}," is called by QGIS when the provider is registered and again whenever the provider is refreshed, so it must construct fresh algorithm instances rather than reusing stored ones — the framework clones them per run, as described in the algorithm lifecycle.",[15,1851,1852,1853,1855,1856,1858],{},"Registration and removal must be symmetric, exactly as for expression functions and map tools. Registering the provider in ",[35,1854,168],{}," and removing it in ",[35,1857,172],{}," is what allows the plugin to be reloaded during development without a duplicate-id error, which is otherwise the first thing you hit after the first successful run.",[65,1860,1862],{"id":1861},"qgis-version-compatibility","QGIS Version Compatibility",[15,1864,1865,1866,1868],{},"Examples target ",[76,1867,78],{}," (Python 3.12). The provider API has been stable across recent releases.",[1329,1870,1871,1884],{},[1332,1872,1873],{},[1335,1874,1875,1878,1881],{},[1338,1876,1877],{},"QGIS release",[1338,1879,1880],{},"Python",[1338,1882,1883],{},"Provider notes",[1348,1885,1886,1904,1915],{},[1335,1887,1888,1891,1894],{},[1353,1889,1890],{},"3.28 LTR",[1353,1892,1893],{},"3.9",[1353,1895,1896,1049,1898,1900,1901,86],{},[35,1897,189],{},[35,1899,1016],{}," identical; use ",[35,1902,1903],{},"from qgis.PyQt.QtGui import QIcon",[1335,1905,1906,1909,1912],{},[1353,1907,1908],{},"3.34 LTR",[1353,1910,1911],{},"3.12",[1353,1913,1914],{},"Baseline for this guide; full SVG icon support.",[1335,1916,1917,1920,1922],{},[1353,1918,1919],{},"3.40 \u002F 3.44",[1353,1921,1911],{},[1353,1923,1924],{},"No breaking provider changes; Model Designer improvements benefit branded providers.",[15,1926,1927,1928,1931,1932,1934],{},"Across all three lines, the registration pattern (",[35,1929,1930],{},"QgsApplication.processingRegistry().addProvider()"," \u002F ",[35,1933,1023],{},") is unchanged, so a provider written against 3.34 loads without modification on 3.28 and 3.44.",[65,1936,1938],{"id":1937},"key-takeaways","Key Takeaways",[70,1940,1941,1947,1962,1976,1986,1989],{},[73,1942,1943,1944,1946],{},"A provider is a registry container; the real logic lives in ",[35,1945,310],{}," subclasses it holds.",[73,1948,1949,1950,54,1952,96,1954,1956,1957,1049,1959,1961],{},"Override ",[35,1951,324],{},[35,1953,327],{},[35,1955,330],{}," at minimum; treat ",[35,1958,324],{},[35,1960,1052],{}," as permanent identifiers.",[73,1963,1964,1965,1967,1968,1970,1971,1973,1974,86],{},"Register in ",[35,1966,168],{}," with ",[35,1969,1016],{}," and always pair it with ",[35,1972,1023],{}," in ",[35,1975,172],{},[73,1977,1978,1979,54,1981,96,1983,1985],{},"Group and brand algorithms through ",[35,1980,1048],{},[35,1982,1052],{},[35,1984,639],{}," for a navigable Toolbox.",[73,1987,1988],{},"Choose a provider plugin over a script when you need distribution, branding, auto-update, or multiple related tools.",[73,1990,1991,1992,86],{},"Registered algorithms are callable everywhere via ",[35,1993,1994],{},"processing.run(\"provider_id:algorithm_id\", {...})",[65,1996,1998],{"id":1997},"frequently-asked-questions","Frequently Asked Questions",[15,2000,2001,2004,2005,2007,2008,2010],{},[76,2002,2003],{},"Why does my provider appear twice in the Toolbox after editing the plugin?","\nYou reloaded the plugin without calling ",[35,2006,1023],{},". The previous instance stayed registered. Implement the symmetrical ",[35,2009,172],{}," shown above, then disable and re-enable the plugin once to clear the orphaned entry.",[15,2012,2013,2016,2017,2019,2020,86],{},[76,2014,2015],{},"Do I need a separate dialog for each algorithm?","\nNo. The Processing framework auto-generates a parameter dialog from the parameters you declare in ",[35,2018,1583],{},". This is the main efficiency gain over the manual Qt approach in ",[21,2021,62],{"href":61},[15,2023,2024,2027,2028,2030,2031,1049,2034,2037],{},[76,2025,2026],{},"Can one provider mix vector and raster algorithms?","\nYes. A provider has no type restriction. Use distinct ",[35,2029,1052],{}," values such as ",[35,2032,2033],{},"vector",[35,2035,2036],{},"raster"," so the Toolbox organizes them under separate sub-nodes.",[15,2039,2040,2043,2044,2046],{},[76,2041,2042],{},"How do users find my provider's algorithms after installing the plugin?","\nThey open the Processing Toolbox (Ctrl+Alt+T) and look for the group whose header matches your ",[35,2045,327],{},". Algorithms are also searchable by display name in the Toolbox filter box.",[15,2048,2049,2055,2056,2058,2059,2061,2062,2064],{},[76,2050,2051,2052,2054],{},"Is ",[35,2053,168],{}," a required method name?","\nNo, it is a convention. QGIS only calls ",[35,2057,1012],{},". You may register the provider directly in ",[35,2060,1012],{},", but factoring it into ",[35,2063,168],{}," keeps the code readable and matches the Plugin Builder template.",[15,2066,2067,2070],{},[76,2068,2069],{},"Can one plugin register more than one provider?","\nIt can, but there is rarely a reason to. A single provider with several groups organises the toolbox just as well and keeps registration and removal simple.",[15,2072,2073,2076],{},[76,2074,2075],{},"What happens if two plugins use the same provider id?","\nThe second registration fails and its algorithms never appear. Provider ids share one namespace across every installed plugin, which is why a short, distinctive, project-specific id matters.",[15,2078,2079,2082,2083,2086],{},[76,2080,2081],{},"Do provider algorithms work in headless scripts?","\nYes, as long as the plugin providing them is installed and loaded. ",[35,2084,2085],{},"processing.run(\"acme:my_algorithm\", …)"," behaves exactly like a built-in call, which is one of the main reasons to write algorithms rather than dialogs.",[15,2088,2089,2092],{},[76,2090,2091],{},"Should algorithm parameters have default values?","\nWherever a sensible one exists. A dialog that opens ready to run with reasonable defaults is far more approachable than one presenting eight empty fields, and defaults cost nothing to supply.",[15,2094,2095,2098],{},[76,2096,2097],{},"How do I group algorithms in the toolbox?","\nReturn a group name and a group id from each algorithm. Algorithms sharing a group id appear together beneath the provider, which is what keeps a toolkit of a dozen tools navigable.",[15,2100,2101,2104,2105,2107,2108,2110],{},[76,2102,2103],{},"Can an algorithm call another algorithm?","\nYes — ",[35,2106,37],{}," works inside ",[35,2109,314],{},", and passing the context and feedback objects through means cancellation and progress reporting continue to work across the nested call. That is how most composite algorithms are built, and it avoids reimplementing operations that already exist.",[15,2112,2113,2116],{},[76,2114,2115],{},"Do I need to write a dialog for my algorithm?","\nNo. Processing generates one from the parameters you declare, complete with layer pickers, file choosers and validation. Writing your own is only worth it when the interaction genuinely cannot be expressed as a list of parameters.",[15,2118,2119,2122,2123,2125,2126,2128],{},[76,2120,2121],{},"Does a provider need an icon?","\nNo, but supplying one makes the toolbox group recognisable at a glance. Return a ",[35,2124,643],{}," from the provider's ",[35,2127,639],{}," method, built from a compiled resource path so it resolves wherever the plugin is installed.",[65,2130,2132],{"id":2131},"related","Related",[70,2134,2135,2139,2143,2147,2152,2158,2162,2169,2176],{},[73,2136,2137],{},[21,2138,44],{"href":28},[73,2140,2141],{},[21,2142,49],{"href":48},[73,2144,2145],{},[21,2146,62],{"href":61},[73,2148,2149],{},[21,2150,2151],{"href":1388},"Publishing to the QGIS Plugin Repository",[73,2153,2154],{},[21,2155,2157],{"href":2156},"\u002Fqgis-plugin-development\u002Ftesting-and-ci-for-plugins\u002F","Testing & CI for QGIS Plugins",[73,2159,2160],{},[21,2161,107],{"href":106},[73,2163,2164,2168],{},[21,2165,2167],{"href":2166},"\u002Fqgis-plugin-development\u002Fprocessing-provider-plugins\u002Fregister-processing-provider-in-plugin\u002F","Register a Processing Provider in a Plugin"," — publish your algorithms in the toolbox, batch mode, the modeller and the command line.",[73,2170,2171,2175],{},[21,2172,2174],{"href":2173},"\u002Fqgis-plugin-development\u002Fprocessing-provider-plugins\u002Fadd-parameters-to-processing-algorithm-pyqgis\u002F","Add Parameters to a Processing Algorithm"," — declare inputs the framework can build a dialog from, and read them correctly.",[73,2177,2178,2182],{},[21,2179,2181],{"href":2180},"\u002Fqgis-plugin-development\u002Fprocessing-provider-plugins\u002Freport-progress-and-cancel-processing-algorithm\u002F","Report Progress and Cancellation in a Processing Algorithm"," — an honest progress bar and a Cancel button that actually works.",[2184,2185,2186],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sjoCn, html code.shiki .sjoCn{--shiki-default:#9AA79F}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}",{"title":344,"searchDepth":361,"depth":361,"links":2188},[2189,2190,2191,2192,2193,2194,2195,2196,2197,2198,2199,2200,2201,2202],{"id":67,"depth":361,"text":68},{"id":113,"depth":361,"text":114},{"id":317,"depth":361,"text":318},{"id":647,"depth":361,"text":648},{"id":1041,"depth":361,"text":1042},{"id":1126,"depth":361,"text":1127},{"id":1304,"depth":361,"text":1305},{"id":1452,"depth":361,"text":1453},{"id":1599,"depth":361,"text":1600},{"id":1727,"depth":361,"text":1728},{"id":1861,"depth":361,"text":1862},{"id":1937,"depth":361,"text":1938},{"id":1997,"depth":361,"text":1998},{"id":2131,"depth":361,"text":2132},"Package custom PyQGIS algorithms as a Processing provider. Subclass QgsProcessingProvider, register it in initProcessing, group algorithms, and add icons.","md",{"slug":12,"type":2206,"breadcrumb":13,"datePublished":2207,"dateModified":2208},"guide","2025-09-15","2026-07-18","\u002Fqgis-plugin-development\u002Fprocessing-provider-plugins",{"title":5,"description":2203},"qgis-plugin-development\u002Fprocessing-provider-plugins\u002Findex","JMw-TQhAqrE942iOqD4wFDwi5ZDEgALc0LGDpIDNNAs",1787823363043]