[{"data":1,"prerenderedAt":1481},["ShallowReactive",2],{"doc:\u002Fqgis-plugin-development\u002Fprocessing-provider-plugins\u002Fregister-processing-provider-in-plugin":3},{"id":4,"title":5,"body":6,"description":1470,"extension":1471,"meta":1472,"navigation":274,"path":1477,"seo":1478,"stem":1479,"__hash__":1480},"docs\u002Fqgis-plugin-development\u002Fprocessing-provider-plugins\u002Fregister-processing-provider-in-plugin\u002Findex.md","Register a Processing Provider in a Plugin",{"type":7,"value":8,"toc":1457},"minimark",[9,13,22,31,186,191,225,229,487,514,518,707,722,821,825,912,927,931,934,950,980,984,987,1093,1107,1181,1185,1252,1259,1263,1323,1327,1345,1349,1355,1361,1374,1380,1389,1399,1413,1422,1426,1453],[10,11,5],"h1",{"id":12},"register-a-processing-provider-in-a-plugin",[14,15,16,17,21],"p",{},"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 ",[18,19,20],"code",{},"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.",[14,23,24,25,30],{},"This recipe belongs to ",[26,27,29],"a",{"href":28},"\u002Fqgis-plugin-development\u002Fprocessing-provider-plugins\u002F","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.",[14,32,33],{},[34,35,40,44,48,55,72,81,90,95,101,110,116,120,123,127,130,134,137,140,143,147,150,153,157,160,168,172,174,178,182],"svg",{"viewBox":36,"role":37,"ariaLabel":38,"xmlns":39},"0 0 760 278","img","Diagram showing an algorithm registered through a provider becoming available to the toolbox, the batch runner, the graphical modeller, Python scripts and the command line runner","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg",[41,42,43],"title",{},"What registering buys you",[45,46,47],"desc",{},"A single algorithm class registered through a provider becomes available in five places at once: the Processing toolbox, the batch processing interface, the graphical modeller, any Python script calling processing dot run, and the qgis process command line runner. None of these require additional code from the plugin author.",[49,50],"rect",{"x":51,"y":51,"width":52,"height":53,"fill":54},"0","760","278","#f6f3ea",[56,57,58],"defs",{},[59,60,67],"marker",{"id":61,"viewBox":62,"refX":63,"refY":64,"markerWidth":65,"markerHeight":65,"orient":66},"provArrow","0 0 10 10","8","5","7","auto-start-reverse",[68,69],"path",{"d":70,"fill":71},"M0 0 L10 5 L0 10 z","#0f766e",[73,74,80],"text",{"x":75,"y":76,"style":77,"fill":78,"textAnchor":79},"380","28","text-anchor:middle;font-size:14px;font-weight:bold;font-family:sans-serif","#17211d","middle","Write it once, and it appears in five places",[49,82],{"x":83,"y":84,"width":85,"height":86,"rx":87,"fill":88,"stroke":71,"style":89},"272","48","216","66","10","#eef7f4","stroke-width:2.5",[73,91,94],{"x":75,"y":92,"style":93,"fill":71,"textAnchor":79},"74","text-anchor:middle;font-size:12px;font-weight:bold;font-family:sans-serif","your algorithm",[73,96,100],{"x":75,"y":97,"style":98,"fill":99,"textAnchor":79},"96","text-anchor:middle;font-size:11px;font-family:sans-serif","#2f3b35","registered through a provider",[49,102],{"x":103,"y":104,"width":105,"height":106,"rx":63,"fill":107,"stroke":108,"style":109},"12","164","136","72","#fffdf7","#59645f","stroke-width:2",[73,111,115],{"x":112,"y":113,"style":114,"fill":78,"textAnchor":79},"80","192","text-anchor:middle;font-size:11px;font-weight:bold;font-family:sans-serif","toolbox",[73,117,119],{"x":112,"y":118,"style":98,"fill":99,"textAnchor":79},"212","searchable dialog",[49,121],{"x":122,"y":104,"width":105,"height":106,"rx":63,"fill":107,"stroke":108,"style":109},"160",[73,124,126],{"x":125,"y":113,"style":114,"fill":78,"textAnchor":79},"228","batch",[73,128,129],{"x":125,"y":118,"style":98,"fill":99,"textAnchor":79},"many inputs at once",[49,131],{"x":132,"y":104,"width":133,"height":106,"rx":63,"fill":107,"stroke":108,"style":109},"308","144",[73,135,136],{"x":75,"y":113,"style":114,"fill":78,"textAnchor":79},"modeller",[73,138,139],{"x":75,"y":118,"style":98,"fill":99,"textAnchor":79},"chained with others",[49,141],{"x":142,"y":104,"width":105,"height":106,"rx":63,"fill":107,"stroke":108,"style":109},"464",[73,144,146],{"x":145,"y":113,"style":114,"fill":78,"textAnchor":79},"532","processing.run",[73,148,149],{"x":145,"y":118,"style":98,"fill":99,"textAnchor":79},"from any script",[49,151],{"x":152,"y":104,"width":105,"height":106,"rx":63,"fill":107,"stroke":108,"style":109},"612",[73,154,156],{"x":155,"y":113,"style":114,"fill":78,"textAnchor":79},"680","qgis process",[73,158,159],{"x":155,"y":118,"style":98,"fill":99,"textAnchor":79},"command line",[161,162],"line",{"x1":163,"y1":164,"x2":165,"y2":166,"stroke":71,"style":167},"320","114","100","158","stroke-width:2;marker-end:url(#provArrow)",[161,169],{"x1":170,"y1":164,"x2":171,"y2":166,"stroke":71,"style":167},"350","240",[161,173],{"x1":75,"y1":164,"x2":75,"y2":166,"stroke":71,"style":167},[161,175],{"x1":176,"y1":164,"x2":177,"y2":166,"stroke":71,"style":167},"410","520",[161,179],{"x1":180,"y1":164,"x2":181,"y2":166,"stroke":71,"style":167},"440","660",[73,183,185],{"x":75,"y":184,"style":98,"fill":108,"textAnchor":79},"264","Progress, cancellation, logging and output handling all come from the framework",[187,188,190],"h2",{"id":189},"prerequisites","Prerequisites",[192,193,194,202,218],"ul",{},[195,196,197,201],"li",{},[198,199,200],"strong",{},"QGIS 3.34 LTR"," (bundled Python 3.12) or newer.",[195,203,204,205,208,209,212,213,217],{},"A plugin skeleton with ",[18,206,207],{},"initGui()"," and ",[18,210,211],{},"unload()"," — see ",[26,214,216],{"href":215},"\u002Fqgis-plugin-development\u002Fplugin-boilerplate-structure\u002F","Plugin Boilerplate and Structure",".",[195,219,220,221,217],{},"At least one algorithm class; writing one is covered in ",[26,222,224],{"href":223},"\u002Fqgis-plugin-development\u002Fprocessing-provider-plugins\u002Fwrite-custom-processing-algorithm-pyqgis\u002F","Write a Custom Processing Algorithm in PyQGIS",[187,226,228],{"id":227},"write-the-provider","Write the provider",[230,231,236],"pre",{"className":232,"code":233,"language":234,"meta":235,"style":235},"language-python shiki shiki-themes github-dark","from qgis.core import QgsProcessingProvider\nfrom qgis.PyQt.QtGui import QIcon\n\nfrom .algorithms.summarise_parcels import SummariseParcelsAlgorithm\nfrom .algorithms.split_by_ward import SplitByWardAlgorithm\n\n\nclass ParcelToolsProvider(QgsProcessingProvider):\n\n    def loadAlgorithms(self):\n        for algorithm in (SummariseParcelsAlgorithm(), SplitByWardAlgorithm()):\n            self.addAlgorithm(algorithm)\n\n    def id(self):\n        return \"parceltools\"\n\n    def name(self):\n        return self.tr(\"Parcel Tools\")\n\n    def longName(self):\n        return self.tr(\"Parcel Tools for local authority GIS\")\n\n    def icon(self):\n        return QIcon(\":\u002Fplugins\u002Fparcel_tools\u002Ficon.png\")\n","python","",[18,237,238,256,269,276,289,302,307,312,331,336,348,363,373,378,388,398,403,413,430,435,445,459,464,474],{"__ignoreMap":235},[239,240,242,246,250,253],"span",{"class":161,"line":241},1,[239,243,245],{"class":244},"snl16","from",[239,247,249],{"class":248},"s95oV"," qgis.core ",[239,251,252],{"class":244},"import",[239,254,255],{"class":248}," QgsProcessingProvider\n",[239,257,259,261,264,266],{"class":161,"line":258},2,[239,260,245],{"class":244},[239,262,263],{"class":248}," qgis.PyQt.QtGui ",[239,265,252],{"class":244},[239,267,268],{"class":248}," QIcon\n",[239,270,272],{"class":161,"line":271},3,[239,273,275],{"emptyLinePlaceholder":274},true,"\n",[239,277,279,281,284,286],{"class":161,"line":278},4,[239,280,245],{"class":244},[239,282,283],{"class":248}," .algorithms.summarise_parcels ",[239,285,252],{"class":244},[239,287,288],{"class":248}," SummariseParcelsAlgorithm\n",[239,290,292,294,297,299],{"class":161,"line":291},5,[239,293,245],{"class":244},[239,295,296],{"class":248}," .algorithms.split_by_ward ",[239,298,252],{"class":244},[239,300,301],{"class":248}," SplitByWardAlgorithm\n",[239,303,305],{"class":161,"line":304},6,[239,306,275],{"emptyLinePlaceholder":274},[239,308,310],{"class":161,"line":309},7,[239,311,275],{"emptyLinePlaceholder":274},[239,313,315,318,322,325,328],{"class":161,"line":314},8,[239,316,317],{"class":244},"class",[239,319,321],{"class":320},"svObZ"," ParcelToolsProvider",[239,323,324],{"class":248},"(",[239,326,327],{"class":320},"QgsProcessingProvider",[239,329,330],{"class":248},"):\n",[239,332,334],{"class":161,"line":333},9,[239,335,275],{"emptyLinePlaceholder":274},[239,337,339,342,345],{"class":161,"line":338},10,[239,340,341],{"class":244},"    def",[239,343,344],{"class":320}," loadAlgorithms",[239,346,347],{"class":248},"(self):\n",[239,349,351,354,357,360],{"class":161,"line":350},11,[239,352,353],{"class":244},"        for",[239,355,356],{"class":248}," algorithm ",[239,358,359],{"class":244},"in",[239,361,362],{"class":248}," (SummariseParcelsAlgorithm(), SplitByWardAlgorithm()):\n",[239,364,366,370],{"class":161,"line":365},12,[239,367,369],{"class":368},"sDLfK","            self",[239,371,372],{"class":248},".addAlgorithm(algorithm)\n",[239,374,376],{"class":161,"line":375},13,[239,377,275],{"emptyLinePlaceholder":274},[239,379,381,383,386],{"class":161,"line":380},14,[239,382,341],{"class":244},[239,384,385],{"class":368}," id",[239,387,347],{"class":248},[239,389,391,394],{"class":161,"line":390},15,[239,392,393],{"class":244},"        return",[239,395,397],{"class":396},"sU2Wk"," \"parceltools\"\n",[239,399,401],{"class":161,"line":400},16,[239,402,275],{"emptyLinePlaceholder":274},[239,404,406,408,411],{"class":161,"line":405},17,[239,407,341],{"class":244},[239,409,410],{"class":320}," name",[239,412,347],{"class":248},[239,414,416,418,421,424,427],{"class":161,"line":415},18,[239,417,393],{"class":244},[239,419,420],{"class":368}," self",[239,422,423],{"class":248},".tr(",[239,425,426],{"class":396},"\"Parcel Tools\"",[239,428,429],{"class":248},")\n",[239,431,433],{"class":161,"line":432},19,[239,434,275],{"emptyLinePlaceholder":274},[239,436,438,440,443],{"class":161,"line":437},20,[239,439,341],{"class":244},[239,441,442],{"class":320}," longName",[239,444,347],{"class":248},[239,446,448,450,452,454,457],{"class":161,"line":447},21,[239,449,393],{"class":244},[239,451,420],{"class":368},[239,453,423],{"class":248},[239,455,456],{"class":396},"\"Parcel Tools for local authority GIS\"",[239,458,429],{"class":248},[239,460,462],{"class":161,"line":461},22,[239,463,275],{"emptyLinePlaceholder":274},[239,465,467,469,472],{"class":161,"line":466},23,[239,468,341],{"class":244},[239,470,471],{"class":320}," icon",[239,473,347],{"class":248},[239,475,477,479,482,485],{"class":161,"line":476},24,[239,478,393],{"class":244},[239,480,481],{"class":248}," QIcon(",[239,483,484],{"class":396},"\":\u002Fplugins\u002Fparcel_tools\u002Ficon.png\"",[239,486,429],{"class":248},[14,488,489,492,493,496,497,501,502,505,506,509,510,513],{},[198,490,491],{},"Breakdown:"," ",[18,494,495],{},"loadAlgorithms()"," is called by the framework when the provider is registered and after every refresh, and must construct ",[498,499,500],"em",{},"fresh"," algorithm instances each time — reusing one is a subtle source of state leaking between runs. ",[18,503,504],{},"id()"," is the identifier that appears in every algorithm's full name as ",[18,507,508],{},"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. ",[18,511,512],{},"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.",[187,515,517],{"id":516},"register-and-unregister-it","Register and unregister it",[230,519,521],{"className":232,"code":520,"language":234,"meta":235,"style":235},"from qgis.core import QgsApplication\nfrom .provider import ParcelToolsProvider\n\n\nclass ParcelTools:\n    def __init__(self, iface):\n        self.iface = iface\n        self.provider = None\n\n    def initProcessing(self):\n        self.provider = ParcelToolsProvider()\n        QgsApplication.processingRegistry().addProvider(self.provider)\n\n    def initGui(self):\n        self.initProcessing()\n\n    def unload(self):\n        if self.provider is not None:\n            QgsApplication.processingRegistry().removeProvider(self.provider)\n            self.provider = None\n",[18,522,523,534,546,550,554,564,574,588,600,604,613,624,635,639,648,655,659,668,688,697],{"__ignoreMap":235},[239,524,525,527,529,531],{"class":161,"line":241},[239,526,245],{"class":244},[239,528,249],{"class":248},[239,530,252],{"class":244},[239,532,533],{"class":248}," QgsApplication\n",[239,535,536,538,541,543],{"class":161,"line":258},[239,537,245],{"class":244},[239,539,540],{"class":248}," .provider ",[239,542,252],{"class":244},[239,544,545],{"class":248}," ParcelToolsProvider\n",[239,547,548],{"class":161,"line":271},[239,549,275],{"emptyLinePlaceholder":274},[239,551,552],{"class":161,"line":278},[239,553,275],{"emptyLinePlaceholder":274},[239,555,556,558,561],{"class":161,"line":291},[239,557,317],{"class":244},[239,559,560],{"class":320}," ParcelTools",[239,562,563],{"class":248},":\n",[239,565,566,568,571],{"class":161,"line":304},[239,567,341],{"class":244},[239,569,570],{"class":368}," __init__",[239,572,573],{"class":248},"(self, iface):\n",[239,575,576,579,582,585],{"class":161,"line":309},[239,577,578],{"class":368},"        self",[239,580,581],{"class":248},".iface ",[239,583,584],{"class":244},"=",[239,586,587],{"class":248}," iface\n",[239,589,590,592,595,597],{"class":161,"line":314},[239,591,578],{"class":368},[239,593,594],{"class":248},".provider ",[239,596,584],{"class":244},[239,598,599],{"class":368}," None\n",[239,601,602],{"class":161,"line":333},[239,603,275],{"emptyLinePlaceholder":274},[239,605,606,608,611],{"class":161,"line":338},[239,607,341],{"class":244},[239,609,610],{"class":320}," initProcessing",[239,612,347],{"class":248},[239,614,615,617,619,621],{"class":161,"line":350},[239,616,578],{"class":368},[239,618,594],{"class":248},[239,620,584],{"class":244},[239,622,623],{"class":248}," ParcelToolsProvider()\n",[239,625,626,629,632],{"class":161,"line":365},[239,627,628],{"class":248},"        QgsApplication.processingRegistry().addProvider(",[239,630,631],{"class":368},"self",[239,633,634],{"class":248},".provider)\n",[239,636,637],{"class":161,"line":375},[239,638,275],{"emptyLinePlaceholder":274},[239,640,641,643,646],{"class":161,"line":380},[239,642,341],{"class":244},[239,644,645],{"class":320}," initGui",[239,647,347],{"class":248},[239,649,650,652],{"class":161,"line":390},[239,651,578],{"class":368},[239,653,654],{"class":248},".initProcessing()\n",[239,656,657],{"class":161,"line":400},[239,658,275],{"emptyLinePlaceholder":274},[239,660,661,663,666],{"class":161,"line":405},[239,662,341],{"class":244},[239,664,665],{"class":320}," unload",[239,667,347],{"class":248},[239,669,670,673,675,677,680,683,686],{"class":161,"line":415},[239,671,672],{"class":244},"        if",[239,674,420],{"class":368},[239,676,594],{"class":248},[239,678,679],{"class":244},"is",[239,681,682],{"class":244}," not",[239,684,685],{"class":368}," None",[239,687,563],{"class":248},[239,689,690,693,695],{"class":161,"line":432},[239,691,692],{"class":248},"            QgsApplication.processingRegistry().removeProvider(",[239,694,631],{"class":368},[239,696,634],{"class":248},[239,698,699,701,703,705],{"class":161,"line":437},[239,700,369],{"class":368},[239,702,594],{"class":248},[239,704,584],{"class":244},[239,706,599],{"class":368},[14,708,709,711,712,715,716,718,719,721],{},[198,710,491],{}," 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 ",[18,713,714],{},"initProcessing()"," from ",[18,717,207],{}," is a small courtesy that lets a headless context register the algorithms without building any interface. The unregister in ",[18,720,211],{}," 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.",[14,723,724],{},[34,725,728,731,734,737,740,747,751,758,763,768,774,777,781,784,789,793,796,801,804,808,810,814,817],{"viewBox":726,"role":37,"ariaLabel":727,"xmlns":39},"0 0 760 250","Comparison of a plugin that unregisters its provider on unload with one that does not, showing duplicate provider groups accumulating in the toolbox after repeated reloads",[41,729,730],{},"Reloading with and without unregistering",[45,732,733],{},"With the provider removed in unload, each reload replaces the previous registration and the toolbox shows one group. Without it, each reload adds another group with the same name, and the older ones call into code that has been torn down, producing errors that appear to come from the algorithm rather than from the registration.",[49,735],{"x":51,"y":51,"width":52,"height":736,"fill":54},"250",[73,738,739],{"x":75,"y":76,"style":77,"fill":78,"textAnchor":79},"Three reloads later",[49,741],{"x":742,"y":84,"width":743,"height":744,"rx":87,"fill":745,"stroke":746,"style":89},"20","348","180","#edf8e9","#15803d",[73,748,750],{"x":749,"y":92,"style":93,"fill":746,"textAnchor":79},"194","removeProvider in unload()",[49,752],{"x":753,"y":754,"width":755,"height":756,"rx":757,"fill":107,"stroke":71,"style":109},"44","92","300","34","6",[73,759,762],{"x":760,"y":164,"style":761,"fill":71},"60","font-size:11px;font-weight:bold;font-family:sans-serif","Parcel Tools",[49,764],{"x":760,"y":765,"width":766,"height":76,"rx":64,"fill":88,"stroke":108,"style":767},"132","284","stroke-width:1.2",[73,769,773],{"x":770,"y":771,"style":772,"fill":99},"76","151","font-size:11px;font-family:sans-serif","Summarise parcels",[49,775],{"x":760,"y":776,"width":766,"height":76,"rx":64,"fill":88,"stroke":108,"style":767},"166",[73,778,780],{"x":770,"y":779,"style":772,"fill":99},"185","Split by ward",[73,782,783],{"x":749,"y":85,"style":114,"fill":746,"textAnchor":79},"one group, current code",[49,785],{"x":786,"y":84,"width":743,"height":744,"rx":87,"fill":787,"stroke":788,"style":89},"392","#fdf2e2","#b91c1c",[73,790,792],{"x":791,"y":92,"style":93,"fill":788,"textAnchor":79},"566","unregister forgotten",[49,794],{"x":795,"y":754,"width":755,"height":76,"rx":64,"fill":107,"stroke":108,"style":767},"416",[73,797,800],{"x":798,"y":799,"style":772,"fill":108},"432","111","Parcel Tools — reload 1, dead",[49,802],{"x":795,"y":803,"width":755,"height":76,"rx":64,"fill":107,"stroke":108,"style":767},"126",[73,805,807],{"x":798,"y":806,"style":772,"fill":108},"145","Parcel Tools — reload 2, dead",[49,809],{"x":795,"y":122,"width":755,"height":76,"rx":64,"fill":107,"stroke":108,"style":767},[73,811,813],{"x":798,"y":812,"style":772,"fill":108},"179","Parcel Tools — reload 3, dead",[49,815],{"x":795,"y":749,"width":755,"height":816,"rx":64,"fill":54,"stroke":788,"style":109},"24",[73,818,820],{"x":798,"y":819,"style":761,"fill":788},"211","and one that works, if you can find it",[187,822,824],{"id":823},"confirm-it-registered","Confirm it registered",[230,826,828],{"className":232,"code":827,"language":234,"meta":235,"style":235},"from qgis.core import QgsApplication\n\nregistry = QgsApplication.processingRegistry()\nprint([p.id() for p in registry.providers()])\n\nalgorithm = registry.algorithmById(\"parceltools:summariseparcels\")\nprint(algorithm.displayName() if algorithm else \"not found\")\n",[18,829,830,840,844,854,873,877,892],{"__ignoreMap":235},[239,831,832,834,836,838],{"class":161,"line":241},[239,833,245],{"class":244},[239,835,249],{"class":248},[239,837,252],{"class":244},[239,839,533],{"class":248},[239,841,842],{"class":161,"line":258},[239,843,275],{"emptyLinePlaceholder":274},[239,845,846,849,851],{"class":161,"line":271},[239,847,848],{"class":248},"registry ",[239,850,584],{"class":244},[239,852,853],{"class":248}," QgsApplication.processingRegistry()\n",[239,855,856,859,862,865,868,870],{"class":161,"line":278},[239,857,858],{"class":368},"print",[239,860,861],{"class":248},"([p.id() ",[239,863,864],{"class":244},"for",[239,866,867],{"class":248}," p ",[239,869,359],{"class":244},[239,871,872],{"class":248}," registry.providers()])\n",[239,874,875],{"class":161,"line":291},[239,876,275],{"emptyLinePlaceholder":274},[239,878,879,882,884,887,890],{"class":161,"line":304},[239,880,881],{"class":248},"algorithm ",[239,883,584],{"class":244},[239,885,886],{"class":248}," registry.algorithmById(",[239,888,889],{"class":396},"\"parceltools:summariseparcels\"",[239,891,429],{"class":248},[239,893,894,896,899,902,904,907,910],{"class":161,"line":309},[239,895,858],{"class":368},[239,897,898],{"class":248},"(algorithm.displayName() ",[239,900,901],{"class":244},"if",[239,903,356],{"class":248},[239,905,906],{"class":244},"else",[239,908,909],{"class":396}," \"not found\"",[239,911,429],{"class":248},[14,913,914,916,917,919,920,923,924,926],{},[198,915,491],{}," 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, ",[18,918,495],{}," 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 ",[18,921,922],{},"None"," here with the provider present almost always means the algorithm's own ",[18,925,512],{}," differs from what you expected — the framework lower-cases it and strips nothing else.",[187,928,930],{"id":929},"refresh-during-development","Refresh during development",[14,932,933],{},"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.",[230,935,937],{"className":232,"code":936,"language":234,"meta":235,"style":235},"QgsApplication.processingRegistry().providerById(\"parceltools\").refreshAlgorithms()\n",[18,938,939],{"__ignoreMap":235},[239,940,941,944,947],{"class":161,"line":241},[239,942,943],{"class":248},"QgsApplication.processingRegistry().providerById(",[239,945,946],{"class":396},"\"parceltools\"",[239,948,949],{"class":248},").refreshAlgorithms()\n",[14,951,952,492,954,957,958,960,961,964,965,967,968,972,973,976,977,979],{},[198,953,491],{},[18,955,956],{},"refreshAlgorithms()"," calls ",[18,959,495],{}," again, which picks up new or removed ",[498,962,963],{},"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 ",[18,966,207],{},"; the Plugin Reloader workflow in ",[26,969,971],{"href":970},"\u002Fqgis-plugin-development\u002Fplugin-boilerplate-structure\u002Freload-qgis-plugin-without-restart\u002F","Reload a QGIS Plugin Without Restarting"," is the practical answer, and it works correctly ",[498,974,975],{},"only"," if ",[18,978,211],{}," removes the provider. This is the single most common reason a Processing plugin becomes annoying to develop.",[187,981,983],{"id":982},"group-algorithms-sensibly","Group algorithms sensibly",[14,985,986],{},"Once a provider carries more than a handful of algorithms, grouping matters. Groups come from the algorithms rather than the provider:",[230,988,990],{"className":232,"code":989,"language":234,"meta":235,"style":235},"class SummariseParcelsAlgorithm(QgsProcessingAlgorithm):\n    def group(self):\n        return self.tr(\"Reporting\")\n\n    def groupId(self):\n        return \"reporting\"\n\n    def name(self):\n        return \"summariseparcels\"\n\n    def displayName(self):\n        return self.tr(\"Summarise parcels by ward\")\n",[18,991,992,1006,1015,1028,1032,1041,1048,1052,1060,1067,1071,1080],{"__ignoreMap":235},[239,993,994,996,999,1001,1004],{"class":161,"line":241},[239,995,317],{"class":244},[239,997,998],{"class":320}," SummariseParcelsAlgorithm",[239,1000,324],{"class":248},[239,1002,1003],{"class":320},"QgsProcessingAlgorithm",[239,1005,330],{"class":248},[239,1007,1008,1010,1013],{"class":161,"line":258},[239,1009,341],{"class":244},[239,1011,1012],{"class":320}," group",[239,1014,347],{"class":248},[239,1016,1017,1019,1021,1023,1026],{"class":161,"line":271},[239,1018,393],{"class":244},[239,1020,420],{"class":368},[239,1022,423],{"class":248},[239,1024,1025],{"class":396},"\"Reporting\"",[239,1027,429],{"class":248},[239,1029,1030],{"class":161,"line":278},[239,1031,275],{"emptyLinePlaceholder":274},[239,1033,1034,1036,1039],{"class":161,"line":291},[239,1035,341],{"class":244},[239,1037,1038],{"class":320}," groupId",[239,1040,347],{"class":248},[239,1042,1043,1045],{"class":161,"line":304},[239,1044,393],{"class":244},[239,1046,1047],{"class":396}," \"reporting\"\n",[239,1049,1050],{"class":161,"line":309},[239,1051,275],{"emptyLinePlaceholder":274},[239,1053,1054,1056,1058],{"class":161,"line":314},[239,1055,341],{"class":244},[239,1057,410],{"class":320},[239,1059,347],{"class":248},[239,1061,1062,1064],{"class":161,"line":333},[239,1063,393],{"class":244},[239,1065,1066],{"class":396}," \"summariseparcels\"\n",[239,1068,1069],{"class":161,"line":338},[239,1070,275],{"emptyLinePlaceholder":274},[239,1072,1073,1075,1078],{"class":161,"line":350},[239,1074,341],{"class":244},[239,1076,1077],{"class":320}," displayName",[239,1079,347],{"class":248},[239,1081,1082,1084,1086,1088,1091],{"class":161,"line":365},[239,1083,393],{"class":244},[239,1085,420],{"class":368},[239,1087,423],{"class":248},[239,1089,1090],{"class":396},"\"Summarise parcels by ward\"",[239,1092,429],{"class":248},[14,1094,1095,492,1097,208,1100,1103,1104,1106],{},[198,1096,491],{},[18,1098,1099],{},"group()",[18,1101,1102],{},"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. ",[18,1105,512],{}," 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.",[14,1108,1109],{},[34,1110,1113,1116,1119,1122,1129,1132,1136,1140,1144,1149,1153,1156,1162,1165,1170,1173,1177],{"viewBox":1111,"role":37,"ariaLabel":1112,"xmlns":39},"0 0 760 244","Diagram showing how a provider identifier and an algorithm name combine into the full algorithm identifier used by scripts, models and the command line",[41,1114,1115],{},"The identifier your users will type",[45,1117,1118],{},"The provider identifier and the algorithm name join with a colon to form the full algorithm identifier. That string is what a script passes to processing dot run, what a saved model stores, and what the command line runner accepts, so renaming either half breaks every existing use.",[49,1120],{"x":51,"y":51,"width":52,"height":1121,"fill":54},"244",[56,1123,1124],{},[59,1125,1127],{"id":1126,"viewBox":62,"refX":63,"refY":64,"markerWidth":65,"markerHeight":65,"orient":66},"idArrow",[68,1128],{"d":70,"fill":71},[73,1130,1131],{"x":75,"y":76,"style":77,"fill":78,"textAnchor":79},"Two names you can never change again",[49,1133],{"x":760,"y":1134,"width":171,"height":1135,"rx":63,"fill":88,"stroke":71,"style":89},"54","56",[73,1137,1139],{"x":744,"y":1138,"style":114,"fill":71,"textAnchor":79},"78","provider id()",[73,1141,1143],{"x":744,"y":1142,"style":98,"fill":99,"textAnchor":79},"98","parceltools",[49,1145],{"x":1146,"y":1134,"width":171,"height":1135,"rx":63,"fill":1147,"stroke":1148,"style":89},"460","#eff3ff","#2563eb",[73,1150,1152],{"x":1151,"y":1138,"style":114,"fill":1148,"textAnchor":79},"580","algorithm name()",[73,1154,1155],{"x":1151,"y":1142,"style":98,"fill":99,"textAnchor":79},"summariseparcels",[49,1157],{"x":1158,"y":1159,"width":1160,"height":1161,"rx":63,"fill":107,"stroke":78,"style":89},"200","134","360","46",[73,1163,508],{"x":75,"y":1164,"style":93,"fill":78,"textAnchor":79},"163",[161,1166],{"x1":1158,"y1":1167,"x2":163,"y2":1168,"stroke":71,"style":1169},"110","128","stroke-width:2;marker-end:url(#idArrow)",[161,1171],{"x1":1172,"y1":1167,"x2":180,"y2":1168,"stroke":71,"style":1169},"560",[73,1174,1176],{"x":75,"y":1175,"style":98,"fill":99,"textAnchor":79},"206","stored in every saved model, script and command line invocation",[73,1178,1180],{"x":75,"y":125,"style":98,"fill":1179,"textAnchor":79},"#b45309","rename either half and all of them break, silently",[187,1182,1184],{"id":1183},"qgis-version-compatibility","QGIS version compatibility",[1186,1187,1188,1204],"table",{},[1189,1190,1191],"thead",{},[1192,1193,1194,1198,1201],"tr",{},[1195,1196,1197],"th",{},"QGIS version",[1195,1199,1200],{},"Python",[1195,1202,1203],{},"Notes",[1205,1206,1207,1221,1231,1242],"tbody",{},[1192,1208,1209,1213,1216],{},[1210,1211,1212],"td",{},"3.22 LTR",[1210,1214,1215],{},"3.9",[1210,1217,1218,1220],{},[18,1219,327],{},", registry add and remove as described.",[1192,1222,1223,1226,1228],{},[1210,1224,1225],{},"3.28 LTR",[1210,1227,1215],{},[1210,1229,1230],{},"Identical.",[1192,1232,1233,1236,1239],{},[1210,1234,1235],{},"3.34 LTR",[1210,1237,1238],{},"3.12",[1210,1240,1241],{},"Baseline for this page.",[1192,1243,1244,1247,1249],{},[1210,1245,1246],{},"3.40 \u002F 3.44",[1210,1248,1238],{},[1210,1250,1251],{},"Identical registration; the toolbox gained better search and provider-level settings.",[14,1253,1254,1255,1258],{},"Providers can also declare ",[18,1256,1257],{},"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.",[187,1260,1262],{"id":1261},"troubleshooting","Troubleshooting",[192,1264,1265,1278,1286,1297,1303,1314],{},[195,1266,1267,1270,1271,1274,1275,1277],{},[198,1268,1269],{},"The group never appears in the toolbox."," The provider was not kept as an attribute, or ",[18,1272,1273],{},"addProvider()"," was never reached because ",[18,1276,207],{}," raised earlier.",[195,1279,1280,492,1283,1285],{},[198,1281,1282],{},"The group appears empty.",[18,1284,495],{}," raised — usually an import error in an algorithm module. Check the Python error log.",[195,1287,1288,492,1291,1294,1295,217],{},[198,1289,1290],{},"Duplicate groups after reloading.",[18,1292,1293],{},"removeProvider()"," is missing from ",[18,1296,211],{},[195,1298,1299,1302],{},[198,1300,1301],{},"Edits to an algorithm have no effect."," Module caching. Reload the plugin rather than refreshing the provider.",[195,1304,1305,1313],{},[198,1306,1307,1310,1311,217],{},[18,1308,1309],{},"algorithmById()"," returns ",[18,1312,922],{}," The full id is not what you assumed. Print every id from the registry and copy the exact string.",[195,1315,1316,1322],{},[198,1317,1318,1319,217],{},"The algorithm works in the toolbox and not from ",[18,1320,1321],{},"processing.run()"," Almost always the id again, or parameters passed under display names instead of parameter names.",[187,1324,1326],{"id":1325},"conclusion","Conclusion",[14,1328,1329,1330,1332,1333,1335,1336,1338,1339,1341,1342,1344],{},"A provider is a small class: ",[18,1331,495],{}," constructing fresh instances, a permanent lower-case ",[18,1334,504],{},", a translated ",[18,1337,512],{},", and an icon. Register it in ",[18,1340,207],{}," keeping a reference on the plugin, remove it in ",[18,1343,211],{}," 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.",[187,1346,1348],{"id":1347},"frequently-asked-questions","Frequently Asked Questions",[14,1350,1351,1354],{},[198,1352,1353],{},"Do I need a plugin to publish algorithms?","\nNo — 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.",[14,1356,1357,1360],{},[198,1358,1359],{},"Can one plugin register several providers?","\nIt can, but it is rarely right. Use groups within one provider instead; users look for your tools under your name.",[14,1362,1363,1366,1369,1370,217],{},[198,1364,1365],{},"How do users run my algorithm from the command line?",[18,1367,1368],{},"qgis_process run parceltools:summariseparcels --INPUT=... --OUTPUT=...",", once the plugin is installed — see ",[26,1371,1373],{"href":1372},"\u002Fpyqgis-fundamentals-environment-setup\u002Fheadless-qgis-and-server-automation\u002Fuse-qgis-process-command-line-runner\u002F","Use the qgis_process Command Line Runner",[14,1375,1376,1379],{},[198,1377,1378],{},"What happens if two providers use the same id?","\nThe second registration is rejected. Prefix the id with something specific to your organisation if a collision is plausible.",[14,1381,1382,1385,1386,1388],{},[198,1383,1384],{},"Should the provider be registered when the plugin has no GUI?","\nYes. That is why ",[18,1387,714],{}," is separate — algorithms are useful in a headless context, which is much of the point.",[14,1390,1391,1394,1395,1398],{},[198,1392,1393],{},"Can I hide an algorithm from the toolbox but keep it scriptable?","\nYes: return the ",[18,1396,1397],{},"FlagHideFromToolbox"," flag from the algorithm. It stays available to models and scripts.",[14,1400,1401,1404,1405,1408,1409,1412],{},[198,1402,1403],{},"How do I add help text for my algorithms?","\nImplement ",[18,1406,1407],{},"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. ",[18,1410,1411],{},"shortDescription()"," provides the one-line summary shown in the toolbox tooltip.",[14,1414,1415,1418,1419,1421],{},[198,1416,1417],{},"Does the provider need to handle its own errors?","\nNo. 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 ",[18,1420,495],{},", because that failure happens during registration and produces an empty group with no visible explanation.",[187,1423,1425],{"id":1424},"related","Related",[192,1427,1428,1433,1437,1443,1449],{},[195,1429,1430,1432],{},[26,1431,29],{"href":28}," — the guide this recipe belongs to",[195,1434,1435],{},[26,1436,224],{"href":223},[195,1438,1439],{},[26,1440,1442],{"href":1441},"\u002Fqgis-plugin-development\u002Fprocessing-provider-plugins\u002Fadd-parameters-to-processing-algorithm-pyqgis\u002F","Add Parameters to a Processing Algorithm",[195,1444,1445],{},[26,1446,1448],{"href":1447},"\u002Fqgis-plugin-development\u002Fprocessing-provider-plugins\u002Freport-progress-and-cancel-processing-algorithm\u002F","Report Progress and Cancellation in a Processing Algorithm",[195,1450,1451],{},[26,1452,971],{"href":970},[1454,1455,1456],"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 .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":235,"searchDepth":258,"depth":258,"links":1458},[1459,1460,1461,1462,1463,1464,1465,1466,1467,1468,1469],{"id":189,"depth":258,"text":190},{"id":227,"depth":258,"text":228},{"id":516,"depth":258,"text":517},{"id":823,"depth":258,"text":824},{"id":929,"depth":258,"text":930},{"id":982,"depth":258,"text":983},{"id":1183,"depth":258,"text":1184},{"id":1261,"depth":258,"text":1262},{"id":1325,"depth":258,"text":1326},{"id":1347,"depth":258,"text":1348},{"id":1424,"depth":258,"text":1425},"Publish your algorithms inside the Processing toolbox — write a QgsProcessingProvider, add and remove it in initGui and unload, get the icons and identifiers right, and keep the provider reloadable during development.","md",{"slug":1473,"type":1474,"breadcrumb":1475,"datePublished":1476,"dateModified":1476},"register-processing-provider-in-plugin","article","Register a Provider","2026-08-15","\u002Fqgis-plugin-development\u002Fprocessing-provider-plugins\u002Fregister-processing-provider-in-plugin",{"title":5,"description":1470},"qgis-plugin-development\u002Fprocessing-provider-plugins\u002Fregister-processing-provider-in-plugin\u002Findex","60vz2FSS51OmPIwAmxyzRrDwwXZUruvQKXH9jTpwaHQ",1786789584642]