[{"data":1,"prerenderedAt":1885},["ShallowReactive",2],{"doc:\u002Fqgis-plugin-development\u002Fplugin-boilerplate-structure\u002Fcreate-qgis-plugin-with-plugin-builder":3},{"id":4,"title":5,"body":6,"description":1873,"extension":1874,"meta":1875,"navigation":946,"path":1881,"seo":1882,"stem":1883,"__hash__":1884},"docs\u002Fqgis-plugin-development\u002Fplugin-boilerplate-structure\u002Fcreate-qgis-plugin-with-plugin-builder\u002Findex.md","Create a QGIS Plugin with Plugin Builder 3",{"type":7,"value":8,"toc":1857},"minimark",[9,13,37,51,306,309,314,357,360,387,391,401,404,455,477,507,511,520,529,532,633,637,655,658,692,723,729,741,762,766,780,795,828,835,839,846,866,870,876,984,991,1302,1332,1336,1339,1444,1448,1451,1559,1563,1566,1664,1676,1680,1701,1719,1740,1746,1752,1756,1771,1775,1781,1796,1815,1831,1835,1853],[10,11,5],"h1",{"id":12},"create-a-qgis-plugin-with-plugin-builder-3",[14,15,16,17,21,22,25,26,29,30,32,33,36],"p",{},"Writing a plugin skeleton by hand is error-prone: a single typo in ",[18,19,20],"code",{},"metadata.txt"," or a missing ",[18,23,24],{},"classFactory"," keeps your plugin out of the manager. Plugin Builder 3 removes that friction. It is itself a QGIS plugin that runs a wizard, asks a handful of questions, and emits a complete, loadable plugin directory — ",[18,27,28],{},"__init__.py",", ",[18,31,20],{},", a main class wired into the toolbar and menu, a Qt Designer dialog, a ",[18,34,35],{},"resources.qrc",", and a build helper. This page walks through running the wizard, compiling resources, deploying the output to your profile, enabling it, and replacing the placeholder logic with your own.",[14,38,39,40,45,46,50],{},"This task is part of ",[41,42,44],"a",{"href":43},"\u002Fqgis-plugin-development\u002Fplugin-boilerplate-structure\u002F","Plugin Boilerplate & Structure",", itself one strand of ",[41,47,49],{"href":48},"\u002Fqgis-plugin-development\u002F","QGIS Plugin Development",". If you would rather understand every generated file before running the wizard, read the boilerplate guide first: Plugin Builder produces exactly the structure described there, so the two pair well.",[52,53,58,59,58,63,58,67,58,74,58,83,58,89,58,58,272],"svg",{"viewBox":54,"role":55,"ariaLabel":56,"xmlns":57},"0 0 760 312","img","The five-stage Plugin Builder workflow: fill the wizard form, inspect the generated plugin folder, compile the resources with pyrcc5 or pb_tool, deploy the folder to your profile python\u002Fplugins directory, then enable the plugin so its toolbar button and menu entry appear in QGIS","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[60,61,62],"title",{},"Plugin Builder 3 workflow, wizard to enabled plugin",[64,65,66],"desc",{},"Five labelled boxes feed left to right. Stage 1, the wizard, collects identity, template, and author fields. Stage 2 emits a loadable scaffold folder containing __init__.py, metadata.txt, the main class, the dialog .ui, and resources.qrc. Stage 3 compiles the .qrc into an importable .py module with pyrcc5 or pb_tool. Stage 4 deploys the folder into the profile python\u002Fplugins directory. Stage 5 enables the plugin, adding a toolbar button and menu entry in QGIS.",[68,69],"rect",{"x":70,"y":70,"width":71,"height":72,"fill":73},"0","760","312","#f6f3ea",[75,76,82],"text",{"x":77,"y":78,"fill":79,"style":80,"textAnchor":81},"380","30","#17211d","text-anchor:middle;font-family:sans-serif;font-size:16px;font-weight:bold","middle","From wizard to enabled plugin in five stages",[75,84,88],{"x":77,"y":85,"fill":86,"style":87,"textAnchor":81},"50","#2f3b35","text-anchor:middle;font-family:sans-serif;font-size:12px","each stage consumes the output of the one before it",[90,91,93,93,94,93,103,93,110,93,115,93,120,93,125,93,130,93,134,93,138,93,146,93,93,152,93,155,93,158,93,161,93,164,93,167,93,171,93,173,93,176,93,179,93,93,181,93,184,93,186,93,189,93,192,93,195,93,199,93,203,93,207,93,212,93,215,93,93,219,93,222,93,225,93,227,93,230,93,233,93,236,93,239,93,242,93,93,245,93,248,93,251,93,254,93,257,93,260,93,263,93,266,93,269,58],"g",{"style":92},"font-family:sans-serif","\n    ",[68,95],{"x":96,"y":97,"width":98,"height":99,"rx":100,"fill":101,"stroke":79,"style":102},"16","70","128","210","8","#fffdf7","stroke-width:2",[104,105],"circle",{"cx":106,"cy":107,"r":108,"fill":109},"80","98","14","#0f766e",[75,111,114],{"x":106,"y":112,"fill":101,"style":113,"textAnchor":81},"103","text-anchor:middle;font-size:14px;font-weight:bold","1",[75,116,119],{"x":106,"y":117,"fill":79,"style":118,"textAnchor":81},"132","text-anchor:middle;font-size:13px;font-weight:bold","Wizard",[75,121,124],{"x":106,"y":122,"fill":86,"style":123,"textAnchor":81},"150","text-anchor:middle;font-size:10.5px","answer the form",[75,126,129],{"x":78,"y":127,"fill":86,"style":128},"176","font-size:11px","· identity",[75,131,133],{"x":78,"y":132,"fill":86,"style":128},"194","· template",[75,135,137],{"x":78,"y":136,"fill":86,"style":128},"212","· author + URLs",[68,139],{"x":140,"y":141,"width":142,"height":143,"rx":144,"fill":145},"28","232","104","24","4","#26322d",[75,147,151],{"x":106,"y":148,"fill":149,"style":150,"textAnchor":81},"248","#d9f99d","text-anchor:middle;font-family:monospace;font-size:10px","Plugin Builder 3",[68,153],{"x":154,"y":97,"width":98,"height":99,"rx":100,"fill":101,"stroke":79,"style":102},"166",[104,156],{"cx":157,"cy":107,"r":108,"fill":109},"230",[75,159,160],{"x":157,"y":112,"fill":101,"style":113,"textAnchor":81},"2",[75,162,163],{"x":157,"y":117,"fill":79,"style":118,"textAnchor":81},"Scaffold",[75,165,166],{"x":157,"y":122,"fill":86,"style":123,"textAnchor":81},"loadable folder",[75,168,28],{"x":169,"y":127,"fill":86,"style":170},"180","font-family:monospace;font-size:9.5px",[75,172,20],{"x":169,"y":132,"fill":86,"style":170},[75,174,175],{"x":169,"y":136,"fill":86,"style":170},"main class .py",[75,177,178],{"x":169,"y":157,"fill":86,"style":170},"dialog .ui",[75,180,35],{"x":169,"y":148,"fill":86,"style":170},[68,182],{"x":183,"y":97,"width":98,"height":99,"rx":100,"fill":101,"stroke":79,"style":102},"316",[104,185],{"cx":77,"cy":107,"r":108,"fill":109},[75,187,188],{"x":77,"y":112,"fill":101,"style":113,"textAnchor":81},"3",[75,190,191],{"x":77,"y":117,"fill":79,"style":118,"textAnchor":81},"Compile",[75,193,194],{"x":77,"y":122,"fill":86,"style":123,"textAnchor":81},".qrc → .py",[75,196,198],{"x":77,"y":197,"fill":86,"style":123,"textAnchor":81},"182","embed the icon into",[75,200,202],{"x":77,"y":201,"fill":86,"style":123,"textAnchor":81},"198","a resource module",[68,204],{"x":205,"y":206,"width":85,"height":143,"rx":144,"fill":145},"328","220",[75,208,211],{"x":209,"y":210,"fill":149,"style":150,"textAnchor":81},"353","236","pyrcc5",[68,213],{"x":214,"y":206,"width":85,"height":143,"rx":144,"fill":145},"382",[75,216,218],{"x":217,"y":210,"fill":149,"style":150,"textAnchor":81},"407","pb_tool",[68,220],{"x":221,"y":97,"width":98,"height":99,"rx":100,"fill":101,"stroke":79,"style":102},"466",[104,223],{"cx":224,"cy":107,"r":108,"fill":109},"530",[75,226,144],{"x":224,"y":112,"fill":101,"style":113,"textAnchor":81},[75,228,229],{"x":224,"y":117,"fill":79,"style":118,"textAnchor":81},"Deploy",[75,231,232],{"x":224,"y":122,"fill":86,"style":123,"textAnchor":81},"copy to profile",[75,234,235],{"x":224,"y":197,"fill":86,"style":123,"textAnchor":81},"folder name must",[75,237,238],{"x":224,"y":201,"fill":86,"style":123,"textAnchor":81},"equal module name",[68,240],{"x":241,"y":206,"width":142,"height":143,"rx":144,"fill":145},"478",[75,243,244],{"x":224,"y":210,"fill":149,"style":150,"textAnchor":81},"python\u002Fplugins\u002F",[68,246],{"x":247,"y":97,"width":98,"height":99,"rx":100,"fill":101,"stroke":79,"style":102},"616",[104,249],{"cx":250,"cy":107,"r":108,"fill":109},"680",[75,252,253],{"x":250,"y":112,"fill":101,"style":113,"textAnchor":81},"5",[75,255,256],{"x":250,"y":117,"fill":79,"style":118,"textAnchor":81},"Enable",[75,258,259],{"x":250,"y":122,"fill":86,"style":123,"textAnchor":81},"tick in manager",[75,261,262],{"x":250,"y":197,"fill":86,"style":123,"textAnchor":81},"button + menu",[75,264,265],{"x":250,"y":201,"fill":86,"style":123,"textAnchor":81},"appear in QGIS",[68,267],{"x":268,"y":206,"width":142,"height":143,"rx":144,"fill":145},"628",[75,270,271],{"x":250,"y":210,"fill":149,"style":150,"textAnchor":81},"toolbar + menu",[90,273,93,274,93,280,93,285,93,289,93,292,93,296,93,299,93,303,58],{"fill":109,"stroke":109},[275,276],"line",{"x1":277,"y1":278,"x2":279,"y2":278,"style":102},"146","175","160",[281,282],"polygon",{"points":283,"stroke":284},"160,170 166,175 160,180","none",[275,286],{"x1":287,"y1":278,"x2":288,"y2":278,"style":102},"296","310",[281,290],{"points":291,"stroke":284},"310,170 316,175 310,180",[275,293],{"x1":294,"y1":278,"x2":295,"y2":278,"style":102},"446","460",[281,297],{"points":298,"stroke":284},"460,170 466,175 460,180",[275,300],{"x1":301,"y1":278,"x2":302,"y2":278,"style":102},"596","610",[281,304],{"points":305,"stroke":284},"610,170 616,175 610,180",[14,307,308],{},"The end-to-end flow has five stages — run the wizard, inspect the output, compile resources, deploy to your profile, and enable — each covered as a step below.",[310,311,313],"h2",{"id":312},"prerequisites","Prerequisites",[315,316,317,325,334,340,354],"ul",{},[318,319,320,324],"li",{},[321,322,323],"strong",{},"QGIS 3.34 LTR"," (bundles Python 3.12) or any 3.x release with a working Python console",[318,326,327,329,330,333],{},[321,328,151],{}," installed via ",[18,331,332],{},"Plugins → Manage and Install Plugins",", then search for \"Plugin Builder\" and install",[318,335,336,339],{},[321,337,338],{},"Plugin Reloader"," (optional but strongly recommended) for fast iteration without restarting QGIS",[318,341,342,343,346,347,349,350,353],{},"A resource compiler on your ",[18,344,345],{},"PATH"," — ",[18,348,211],{}," (ships with PyQt5) or ",[18,351,352],{},"pyqgis"," toolchain access",[318,355,356],{},"Write access to your QGIS profile plugins directory",[14,358,359],{},"The profile plugins directory is where QGIS scans for installed plugins:",[315,361,362,371,379],{},[318,363,364,367,368],{},[321,365,366],{},"Windows:"," ",[18,369,370],{},"%APPDATA%\\QGIS\\QGIS3\\profiles\\default\\python\\plugins\\",[318,372,373,367,376],{},[321,374,375],{},"macOS:",[18,377,378],{},"~\u002FLibrary\u002FApplication Support\u002FQGIS\u002FQGIS3\u002Fprofiles\u002Fdefault\u002Fpython\u002Fplugins\u002F",[318,380,381,367,384],{},[321,382,383],{},"Linux:",[18,385,386],{},"~\u002F.local\u002Fshare\u002FQGIS\u002FQGIS3\u002Fprofiles\u002Fdefault\u002Fpython\u002Fplugins\u002F",[310,388,390],{"id":389},"step-1-run-the-plugin-builder-wizard","Step 1: Run the Plugin Builder Wizard",[14,392,393,394,397,398,400],{},"Launch the wizard from ",[18,395,396],{},"Plugins → Plugin Builder → Plugin Builder",". The form is divided into pages; the values you enter map directly onto the generated ",[18,399,20],{}," and class names, so choose them deliberately.",[14,402,403],{},"The first page collects identity:",[315,405,406,422,431,443],{},[318,407,408,346,411,414,415,418,419,421],{},[321,409,410],{},"Class name",[18,412,413],{},"PascalCase",", e.g. ",[18,416,417],{},"FieldAreaCalculator",". This becomes the main plugin class and the value returned by ",[18,420,24],{},".",[318,423,424,427,428,421],{},[321,425,426],{},"Plugin name"," — the human-readable label shown in the plugin manager and menu, e.g. ",[18,429,430],{},"Field Area Calculator",[318,432,433,346,436,414,439,442],{},[321,434,435],{},"Module name",[18,437,438],{},"snake_case",[18,440,441],{},"field_area_calculator",". This becomes the output folder name and the Python module, so it must be a valid identifier with no spaces or hyphens.",[318,444,445,448,449,452,453,421],{},[321,446,447],{},"Description"," and ",[321,450,451],{},"About"," — short and long summaries; both land in ",[18,454,20],{},[14,456,457,458,461,462,465,466,469,470,473,474,421],{},"The second page asks for the ",[321,459,460],{},"plugin template",". Choose ",[321,463,464],{},"\"Tool button with dialog\""," for a classic dialog-based tool, ",[321,467,468],{},"\"Tool button with dock widget\""," for a persistent panel, or ",[321,471,472],{},"\"Processing provider\""," if you are building algorithms. For this walkthrough select ",[321,475,476],{},"Tool button with dialog",[14,478,479,480,483,484,487,488,29,491,494,495,498,499,502,503,506],{},"Subsequent pages collect the ",[321,481,482],{},"menu location"," (Vector, Raster, Database, Web, or a custom plugin menu), text strings, and ",[321,485,486],{},"author details"," (",[18,489,490],{},"author",[18,492,493],{},"email",", the tracker\u002Frepository\u002Fhomepage URLs, and the minimum QGIS version). Set ",[321,496,497],{},"Minimum QGIS version"," to ",[18,500,501],{},"3.34"," to match the LTR baseline. Finally choose an ",[321,504,505],{},"output directory"," — generate into a scratch folder, not directly into your live plugins directory, so you can compile resources before deploying.",[310,508,510],{"id":509},"step-2-inspect-the-generated-file-tree","Step 2: Inspect the Generated File Tree",[14,512,513,514,367,517,519],{},"With ",[321,515,516],{},"module name",[18,518,441],{},", Plugin Builder writes a folder like this:",[521,522,527],"pre",{"className":523,"code":525,"language":75,"meta":526},[524],"language-text","field_area_calculator\u002F\n├── __init__.py\n├── metadata.txt\n├── field_area_calculator.py          # main plugin class\n├── field_area_calculator_dialog.py   # dialog wrapper class\n├── field_area_calculator_dialog_base.ui  # Qt Designer layout\n├── resources.qrc                     # Qt resource manifest\n├── resources.py                      # compiled (may need regeneration)\n├── icon.png\n├── Makefile\n├── pb_tool.cfg                       # pb_tool build config\n├── plugin_upload.py\n├── pylintrc\n├── README.html\n├── help\u002F\n├── i18n\u002F\n│   └── af.ts\n├── scripts\u002F\n└── test\u002F\n","",[18,528,525],{"__ignoreMap":526},[14,530,531],{},"The files that matter day to day:",[315,533,534,545,561,583,602,610,621],{},[318,535,536,540,541,544],{},[321,537,538],{},[18,539,28],{}," — contains the ",[18,542,543],{},"classFactory(iface)"," function that imports and returns your main class. QGIS calls this during discovery.",[318,546,547,551,552,555,556,560],{},[321,548,549],{},[18,550,20],{}," — every value you typed in the wizard, now in ",[18,553,554],{},"[general]"," key\u002Fvalue form. This is the single source of truth the plugin manager reads. The companion page on ",[41,557,559],{"href":558},"\u002Fqgis-plugin-development\u002Fpublishing-to-the-qgis-plugin-repository\u002Fwrite-metadata-txt-qgis-plugin\u002F","Write metadata.txt for a QGIS Plugin"," covers each key in depth.",[318,562,563,568,569,29,572,29,575,578,579,582],{},[321,564,565],{},[18,566,567],{},"field_area_calculator.py"," — the main class with ",[18,570,571],{},"initGui()",[18,573,574],{},"unload()",[18,576,577],{},"run()",", and the helper ",[18,580,581],{},"add_action()",". This is where your toolbar button and menu entry are registered.",[318,584,585,590,591,594,595,598,599,421],{},[321,586,587],{},[18,588,589],{},"field_area_calculator_dialog.py"," — a thin ",[18,592,593],{},"QDialog"," subclass that loads the ",[18,596,597],{},".ui"," file with ",[18,600,601],{},"uic.loadUiType",[318,603,604,609],{},[321,605,606],{},[18,607,608],{},"field_area_calculator_dialog_base.ui"," — the editable Qt Designer layout. Open it in Qt Designer to add widgets.",[318,611,612,616,617,620],{},[321,613,614],{},[18,615,35],{}," — lists ",[18,618,619],{},"icon.png"," (and any other assets) for the Qt resource system.",[318,622,623,632],{},[321,624,625,628,629],{},[18,626,627],{},"Makefile"," \u002F ",[18,630,631],{},"pb_tool.cfg"," — two alternative build front-ends; you use one or the other to compile and deploy.",[310,634,636],{"id":635},"step-3-compile-the-resources","Step 3: Compile the Resources",[14,638,639,640,643,644,647,648,650,651,654],{},"The generated main class imports icons through a compiled resource module (",[18,641,642],{},"from .resources import *","). The ",[18,645,646],{},".qrc"," manifest is plain XML; it must be turned into importable Python with ",[18,649,211],{},". Even though Plugin Builder ships a ",[18,652,653],{},"resources.py",", regenerate it so it matches your local PyQt5 version and any icon you swapped in.",[14,656,657],{},"Run this from inside the plugin folder:",[521,659,663],{"className":660,"code":661,"language":662,"meta":526,"style":526},"language-bash shiki shiki-themes github-dark","cd field_area_calculator\npyrcc5 -o resources.py resources.qrc\n","bash",[18,664,665,677],{"__ignoreMap":526},[666,667,669,673],"span",{"class":275,"line":668},1,[666,670,672],{"class":671},"sDLfK","cd",[666,674,676],{"class":675},"sU2Wk"," field_area_calculator\n",[666,678,680,683,686,689],{"class":275,"line":679},2,[666,681,211],{"class":682},"svObZ",[666,684,685],{"class":671}," -o",[666,687,688],{"class":675}," resources.py",[666,690,691],{"class":675}," resources.qrc\n",[14,693,694,367,697,699,700,702,703,706,707,709,710,713,714,716,717,720,721,421],{},[321,695,696],{},"Breakdown:",[18,698,211],{}," reads ",[18,701,35],{},", base64-encodes every referenced ",[18,704,705],{},"\u003Cfile>"," (here, ",[18,708,619],{},"), and writes a Python module that registers them with Qt's virtual resource filesystem under paths like ",[18,711,712],{},":\u002Fplugins\u002Ffield_area_calculator\u002Ficon.png",". Because the icon is embedded, the deployed plugin needs no loose PNG at runtime. If ",[18,715,211],{}," is not found, install it with ",[18,718,719],{},"pip install pyqt5-tools"," outside QGIS, or use the OSGeo4W shell on Windows where it is already on the ",[18,722,345],{},[14,724,725,726,728],{},"If you prefer not to manage the command yourself, ",[18,727,218],{}," wraps it. From the plugin folder:",[521,730,732],{"className":660,"code":731,"language":662,"meta":526,"style":526},"pb_tool compile\n",[18,733,734],{"__ignoreMap":526},[666,735,736,738],{"class":275,"line":668},[666,737,218],{"class":682},[666,739,740],{"class":675}," compile\n",[14,742,743,367,745,699,748,750,751,448,753,755,756,758,759,421],{},[321,744,696],{},[18,746,747],{},"pb_tool compile",[18,749,631],{},", finds every ",[18,752,646],{},[18,754,597],{}," listed there, and runs the appropriate compiler for each. It is the more portable option in CI because it does not depend on remembering individual ",[18,757,211],{}," invocations. Install it once with ",[18,760,761],{},"pip install pb_tool",[310,763,765],{"id":764},"step-4-deploy-to-the-profile-plugins-directory","Step 4: Deploy to the Profile Plugins Directory",[14,767,768,769,772,773,776,777,779],{},"QGIS only loads plugins from a profile's ",[18,770,771],{},"python\u002Fplugins"," directory. Move the compiled folder there. The cleanest approach during development is ",[18,774,775],{},"pb_tool deploy",", which copies only the files declared in ",[18,778,631],{}," and skips build artifacts:",[521,781,783],{"className":660,"code":782,"language":662,"meta":526,"style":526},"pb_tool deploy -y\n",[18,784,785],{"__ignoreMap":526},[666,786,787,789,792],{"class":275,"line":668},[666,788,218],{"class":682},[666,790,791],{"class":675}," deploy",[666,793,794],{"class":671}," -y\n",[14,796,797,367,799,802,803,806,807,809,810,813,814,817,818,820,821,824,825,827],{},[321,798,696],{},[18,800,801],{},"deploy"," reads the ",[18,804,805],{},"home_plugin"," path resolved from your profile and copies the source files plus the freshly compiled ",[18,808,653],{}," into ",[18,811,812],{},"...\u002Fpython\u002Fplugins\u002Ffield_area_calculator\u002F",". The ",[18,815,816],{},"-y"," flag skips the interactive confirmation, which is convenient when you redeploy frequently. The ",[18,819,627],{}," equivalent is ",[18,822,823],{},"make deploy",", which depends on ",[18,826,211],{}," and a Unix-like shell.",[14,829,830,831,834],{},"If you would rather copy by hand, drop the entire ",[18,832,833],{},"field_area_calculator\u002F"," folder into the profile path listed in the prerequisites. Make sure the folder name exactly equals the module name — a mismatch causes an import failure.",[310,836,838],{"id":837},"step-5-enable-and-test-the-plugin","Step 5: Enable and Test the Plugin",[14,840,841,842,845],{},"Open ",[18,843,844],{},"Plugins → Manage and Install Plugins → Installed",". Your plugin appears in the list; tick its checkbox to enable it. A new toolbar button and a menu entry under the location you chose in the wizard should appear immediately.",[14,847,848,849,852,853,855,856,858,859,861,862,865],{},"Watch the Python console (",[18,850,851],{},"Plugins → Python Console",") while enabling — any traceback during ",[18,854,571],{}," prints there. Click the button: the generated ",[18,857,577],{}," simply shows the empty dialog. To iterate, edit the source in the profile directory, then use ",[321,860,338],{}," (its toolbar button or ",[18,863,864],{},"Ctrl+F5",") to reload without restarting QGIS.",[310,867,869],{"id":868},"step-6-customize-the-generated-code","Step 6: Customize the Generated Code",[14,871,872,873,875],{},"The scaffold is a starting point, not a finished plugin. The default ",[18,874,577],{}," looks like this:",[521,877,881],{"className":878,"code":879,"language":880,"meta":526,"style":526},"language-python shiki shiki-themes github-dark","def run(self):\n    \"\"\"Run method that performs all the real work.\"\"\"\n    if self.first_start:\n        self.first_start = False\n        self.dlg = FieldAreaCalculatorDialog()\n\n    self.dlg.show()\n    result = self.dlg.exec_()\n    if result:\n        pass\n","python",[18,882,883,896,901,913,928,941,948,957,970,978],{"__ignoreMap":526},[666,884,885,889,892],{"class":275,"line":668},[666,886,888],{"class":887},"snl16","def",[666,890,891],{"class":682}," run",[666,893,895],{"class":894},"s95oV","(self):\n",[666,897,898],{"class":275,"line":679},[666,899,900],{"class":675},"    \"\"\"Run method that performs all the real work.\"\"\"\n",[666,902,904,907,910],{"class":275,"line":903},3,[666,905,906],{"class":887},"    if",[666,908,909],{"class":671}," self",[666,911,912],{"class":894},".first_start:\n",[666,914,916,919,922,925],{"class":275,"line":915},4,[666,917,918],{"class":671},"        self",[666,920,921],{"class":894},".first_start ",[666,923,924],{"class":887},"=",[666,926,927],{"class":671}," False\n",[666,929,931,933,936,938],{"class":275,"line":930},5,[666,932,918],{"class":671},[666,934,935],{"class":894},".dlg ",[666,937,924],{"class":887},[666,939,940],{"class":894}," FieldAreaCalculatorDialog()\n",[666,942,944],{"class":275,"line":943},6,[666,945,947],{"emptyLinePlaceholder":946},true,"\n",[666,949,951,954],{"class":275,"line":950},7,[666,952,953],{"class":671},"    self",[666,955,956],{"class":894},".dlg.show()\n",[666,958,960,963,965,967],{"class":275,"line":959},8,[666,961,962],{"class":894},"    result ",[666,964,924],{"class":887},[666,966,909],{"class":671},[666,968,969],{"class":894},".dlg.exec_()\n",[666,971,973,975],{"class":275,"line":972},9,[666,974,906],{"class":887},[666,976,977],{"class":894}," result:\n",[666,979,981],{"class":275,"line":980},10,[666,982,983],{"class":887},"        pass\n",[14,985,986,987,990],{},"Replace the ",[18,988,989],{},"pass"," block with real logic. The example below reads the active vector layer, sums polygon areas in the layer's CRS units, and reports the total — turning the empty wizard output into a working tool:",[521,992,994],{"className":878,"code":993,"language":880,"meta":526,"style":526},"from qgis.core import Qgis, QgsWkbTypes\n\n\ndef run(self):\n    \"\"\"Show the dialog and total the polygon areas on confirmation.\"\"\"\n    if self.first_start:\n        self.first_start = False\n        self.dlg = FieldAreaCalculatorDialog()\n\n    self.dlg.show()\n    if not self.dlg.exec_():\n        return\n\n    layer = self.iface.activeLayer()\n    if layer is None or layer.geometryType() != QgsWkbTypes.PolygonGeometry:\n        self.iface.messageBar().pushMessage(\n            \"Field Area Calculator\",\n            \"Select a polygon layer first.\",\n            level=Qgis.Warning,\n            duration=4,\n        )\n        return\n\n    total = sum(feature.geometry().area() for feature in layer.getFeatures())\n    self.iface.messageBar().pushMessage(\n        \"Field Area Calculator\",\n        f\"Total area: {total:,.2f} {layer.crs().mapUnits()} units\",\n        level=Qgis.Success,\n        duration=6,\n    )\n",[18,995,996,1010,1014,1018,1026,1031,1039,1049,1059,1063,1069,1082,1088,1093,1106,1132,1140,1149,1157,1169,1181,1187,1192,1197,1223,1230,1238,1272,1283,1296],{"__ignoreMap":526},[666,997,998,1001,1004,1007],{"class":275,"line":668},[666,999,1000],{"class":887},"from",[666,1002,1003],{"class":894}," qgis.core ",[666,1005,1006],{"class":887},"import",[666,1008,1009],{"class":894}," Qgis, QgsWkbTypes\n",[666,1011,1012],{"class":275,"line":679},[666,1013,947],{"emptyLinePlaceholder":946},[666,1015,1016],{"class":275,"line":903},[666,1017,947],{"emptyLinePlaceholder":946},[666,1019,1020,1022,1024],{"class":275,"line":915},[666,1021,888],{"class":887},[666,1023,891],{"class":682},[666,1025,895],{"class":894},[666,1027,1028],{"class":275,"line":930},[666,1029,1030],{"class":675},"    \"\"\"Show the dialog and total the polygon areas on confirmation.\"\"\"\n",[666,1032,1033,1035,1037],{"class":275,"line":943},[666,1034,906],{"class":887},[666,1036,909],{"class":671},[666,1038,912],{"class":894},[666,1040,1041,1043,1045,1047],{"class":275,"line":950},[666,1042,918],{"class":671},[666,1044,921],{"class":894},[666,1046,924],{"class":887},[666,1048,927],{"class":671},[666,1050,1051,1053,1055,1057],{"class":275,"line":959},[666,1052,918],{"class":671},[666,1054,935],{"class":894},[666,1056,924],{"class":887},[666,1058,940],{"class":894},[666,1060,1061],{"class":275,"line":972},[666,1062,947],{"emptyLinePlaceholder":946},[666,1064,1065,1067],{"class":275,"line":980},[666,1066,953],{"class":671},[666,1068,956],{"class":894},[666,1070,1072,1074,1077,1079],{"class":275,"line":1071},11,[666,1073,906],{"class":887},[666,1075,1076],{"class":887}," not",[666,1078,909],{"class":671},[666,1080,1081],{"class":894},".dlg.exec_():\n",[666,1083,1085],{"class":275,"line":1084},12,[666,1086,1087],{"class":887},"        return\n",[666,1089,1091],{"class":275,"line":1090},13,[666,1092,947],{"emptyLinePlaceholder":946},[666,1094,1096,1099,1101,1103],{"class":275,"line":1095},14,[666,1097,1098],{"class":894},"    layer ",[666,1100,924],{"class":887},[666,1102,909],{"class":671},[666,1104,1105],{"class":894},".iface.activeLayer()\n",[666,1107,1109,1111,1114,1117,1120,1123,1126,1129],{"class":275,"line":1108},15,[666,1110,906],{"class":887},[666,1112,1113],{"class":894}," layer ",[666,1115,1116],{"class":887},"is",[666,1118,1119],{"class":671}," None",[666,1121,1122],{"class":887}," or",[666,1124,1125],{"class":894}," layer.geometryType() ",[666,1127,1128],{"class":887},"!=",[666,1130,1131],{"class":894}," QgsWkbTypes.PolygonGeometry:\n",[666,1133,1135,1137],{"class":275,"line":1134},16,[666,1136,918],{"class":671},[666,1138,1139],{"class":894},".iface.messageBar().pushMessage(\n",[666,1141,1143,1146],{"class":275,"line":1142},17,[666,1144,1145],{"class":675},"            \"Field Area Calculator\"",[666,1147,1148],{"class":894},",\n",[666,1150,1152,1155],{"class":275,"line":1151},18,[666,1153,1154],{"class":675},"            \"Select a polygon layer first.\"",[666,1156,1148],{"class":894},[666,1158,1160,1164,1166],{"class":275,"line":1159},19,[666,1161,1163],{"class":1162},"s9osk","            level",[666,1165,924],{"class":887},[666,1167,1168],{"class":894},"Qgis.Warning,\n",[666,1170,1172,1175,1177,1179],{"class":275,"line":1171},20,[666,1173,1174],{"class":1162},"            duration",[666,1176,924],{"class":887},[666,1178,144],{"class":671},[666,1180,1148],{"class":894},[666,1182,1184],{"class":275,"line":1183},21,[666,1185,1186],{"class":894},"        )\n",[666,1188,1190],{"class":275,"line":1189},22,[666,1191,1087],{"class":887},[666,1193,1195],{"class":275,"line":1194},23,[666,1196,947],{"emptyLinePlaceholder":946},[666,1198,1200,1203,1205,1208,1211,1214,1217,1220],{"class":275,"line":1199},24,[666,1201,1202],{"class":894},"    total ",[666,1204,924],{"class":887},[666,1206,1207],{"class":671}," sum",[666,1209,1210],{"class":894},"(feature.geometry().area() ",[666,1212,1213],{"class":887},"for",[666,1215,1216],{"class":894}," feature ",[666,1218,1219],{"class":887},"in",[666,1221,1222],{"class":894}," layer.getFeatures())\n",[666,1224,1226,1228],{"class":275,"line":1225},25,[666,1227,953],{"class":671},[666,1229,1139],{"class":894},[666,1231,1233,1236],{"class":275,"line":1232},26,[666,1234,1235],{"class":675},"        \"Field Area Calculator\"",[666,1237,1148],{"class":894},[666,1239,1241,1244,1247,1250,1253,1256,1259,1262,1265,1267,1270],{"class":275,"line":1240},27,[666,1242,1243],{"class":887},"        f",[666,1245,1246],{"class":675},"\"Total area: ",[666,1248,1249],{"class":671},"{",[666,1251,1252],{"class":894},"total",[666,1254,1255],{"class":887},":,.2f",[666,1257,1258],{"class":671},"}",[666,1260,1261],{"class":671}," {",[666,1263,1264],{"class":894},"layer.crs().mapUnits()",[666,1266,1258],{"class":671},[666,1268,1269],{"class":675}," units\"",[666,1271,1148],{"class":894},[666,1273,1275,1278,1280],{"class":275,"line":1274},28,[666,1276,1277],{"class":1162},"        level",[666,1279,924],{"class":887},[666,1281,1282],{"class":894},"Qgis.Success,\n",[666,1284,1286,1289,1291,1294],{"class":275,"line":1285},29,[666,1287,1288],{"class":1162},"        duration",[666,1290,924],{"class":887},[666,1292,1293],{"class":671},"6",[666,1295,1148],{"class":894},[666,1297,1299],{"class":275,"line":1298},30,[666,1300,1301],{"class":894},"    )\n",[14,1303,1304,367,1306,1309,1310,1313,1314,1317,1318,813,1321,1324,1325,1327,1328,421],{},[321,1305,696],{},[18,1307,1308],{},"self.iface.activeLayer()"," returns the layer highlighted in the Layers panel. The guard rejects anything that is not a polygon layer via ",[18,1311,1312],{},"geometryType()",". ",[18,1315,1316],{},"feature.geometry().area()"," returns the planar area in the layer's CRS units, so the result is only meaningful in a projected CRS — for geodesic measurements you would use ",[18,1319,1320],{},"QgsDistanceArea",[18,1322,1323],{},"messageBar()"," calls surface feedback without a blocking dialog. To add inputs to the dialog itself — for example a layer picker — open ",[18,1326,608],{}," in Qt Designer and follow the toolbar and widget patterns in ",[41,1329,1331],{"href":1330},"\u002Fqgis-plugin-development\u002Fplugin-boilerplate-structure\u002Fadd-toolbar-button-to-qgis-plugin\u002F","Add a Toolbar Button to a QGIS Plugin",[310,1333,1335],{"id":1334},"what-the-wizard-generates-and-what-you-delete","What the wizard generates, and what you delete",[14,1337,1338],{},"A freshly generated plugin contains more scaffolding than most plugins ever need. Knowing which files are load-bearing makes the first clean-up safe.",[14,1340,1341],{},[52,1342,1345,1348,1351,1355,1360,1367,1372,1386,1391,1394,1397,1400,1415,1418,1423,1427,1441],{"viewBox":1343,"role":55,"ariaLabel":1344,"xmlns":57},"0 -2 760 252","The generated file tree split into files that must stay, files that are usually kept and files most plugins delete",[60,1346,1347],{},"Which generated files are load-bearing",[64,1349,1350],{},"Three groups. Essential: the init file with classFactory, the main plugin module, and metadata.txt. Usually kept: the dialog module, its ui file, resources and the compiled resources module. Commonly deleted: the sample test suite, the help documentation scaffold, the Makefile and the pylint configuration.",[68,1352],{"x":70,"y":1353,"width":71,"height":1354,"fill":73},"-2","252",[75,1356,1359],{"x":77,"y":1357,"style":1358,"fill":79,"textAnchor":81},"26","text-anchor:middle;font-size:14px;font-weight:bold;font-family:sans-serif","Three tiers in a generated plugin",[68,1361],{"x":96,"y":1362,"width":210,"height":1363,"rx":1364,"fill":101,"stroke":1365,"style":1366},"46","188","10","#b91c1c","stroke-width:2.5",[75,1368,1371],{"x":1369,"y":97,"style":1370,"fill":1365,"textAnchor":81},"134","text-anchor:middle;font-size:11.5px;font-weight:bold;font-family:sans-serif","never delete",[90,1373,1375,1379,1383],{"style":1374},"font-size:11px;font-family:monospace",[75,1376,28],{"x":1377,"y":1378,"fill":86},"40","100",[75,1380,1382],{"x":1377,"y":1381,"fill":86},"126","my_plugin.py",[75,1384,20],{"x":1377,"y":1385,"fill":86},"152",[75,1387,1390],{"x":1369,"y":1388,"style":1389,"fill":86,"textAnchor":81},"192","text-anchor:middle;font-size:10.5px;font-family:sans-serif","QGIS will not load",[75,1392,1393],{"x":1369,"y":99,"style":1389,"fill":1365,"textAnchor":81},"the plugin without these",[68,1395],{"x":1396,"y":1362,"width":210,"height":1363,"rx":1364,"fill":101,"stroke":109,"style":1366},"262",[75,1398,1399],{"x":77,"y":97,"style":1370,"fill":109,"textAnchor":81},"usually keep",[90,1401,1402,1406,1409,1411],{"style":1374},[75,1403,1405],{"x":1404,"y":1378,"fill":86},"286","my_plugin_dialog.py",[75,1407,1408],{"x":1404,"y":1381,"fill":86},"my_plugin_dialog.ui",[75,1410,35],{"x":1404,"y":1385,"fill":86},[75,1412,1414],{"x":1404,"y":1413,"fill":86},"178","resources_rc.py",[75,1416,1417],{"x":77,"y":136,"style":1389,"fill":86,"textAnchor":81},"unless you have no dialog",[68,1419],{"x":1420,"y":1362,"width":210,"height":1363,"rx":1364,"fill":101,"stroke":1421,"style":1422},"508","#59645f","stroke-width:2;stroke-dasharray:5 4",[75,1424,1426],{"x":1425,"y":97,"style":1370,"fill":1421,"textAnchor":81},"626","commonly deleted",[90,1428,1429,1433,1436,1438],{"style":1374},[75,1430,1432],{"x":1431,"y":1378,"fill":86},"532","test\u002F",[75,1434,1435],{"x":1431,"y":1381,"fill":86},"help\u002F",[75,1437,627],{"x":1431,"y":1385,"fill":86},[75,1439,1440],{"x":1431,"y":1413,"fill":86},"pylintrc",[75,1442,1443],{"x":1425,"y":136,"style":1389,"fill":1421,"textAnchor":81},"replace with your own tooling",[310,1445,1447],{"id":1446},"regenerating-versus-editing","Regenerating versus editing",[14,1449,1450],{},"The wizard is a one-time scaffold, not a tool you return to. Running it again over an existing plugin overwrites your work, so the moment to make structural decisions is before the first run.",[14,1452,1453],{},[52,1454,1457,1460,1463,1466,1480,1483,1490,1495,1499,1503,1520,1525,1528,1531,1533,1541,1543,1546,1555],{"viewBox":1455,"role":55,"ariaLabel":1456,"xmlns":57},"0 0 760 234","A timeline showing the wizard run once at the start followed by manual edits, contrasted with a second wizard run overwriting those edits",[60,1458,1459],{},"Run the wizard once",[64,1461,1462],{},"The correct sequence runs the wizard once, then all subsequent changes are made by hand in version control. The failure mode runs the wizard a second time to change one setting, which regenerates the files and overwrites every manual edit made since the first run.",[68,1464],{"x":70,"y":70,"width":71,"height":1465,"fill":73},"234",[1467,1468,1469],"defs",{},[1470,1471,1476],"marker",{"id":1472,"viewBox":1473,"refX":100,"refY":253,"markerWidth":1474,"markerHeight":1474,"orient":1475},"pbWizArrow","0 0 10 10","7","auto-start-reverse",[1477,1478],"path",{"d":1479,"fill":1421},"M0 0 L10 5 L0 10 z",[75,1481,1482],{"x":77,"y":1357,"style":1358,"fill":79,"textAnchor":81},"The wizard scaffolds; it does not maintain",[75,1484,1489],{"x":122,"y":1485,"style":1486,"fill":1487,"textAnchor":1488},"86","text-anchor:end;font-size:11.5px;font-weight:bold;font-family:sans-serif","#15803d","end","correct",[275,1491],{"x1":1492,"y1":106,"x2":1493,"y2":106,"stroke":1421,"style":1494},"164","716","stroke-width:2;marker-end:url(#pbWizArrow)",[104,1496],{"cx":1497,"cy":106,"r":1498,"fill":1487},"200","9",[75,1500,1502],{"x":1497,"y":1501,"style":1389,"fill":1487,"textAnchor":81},"62","wizard",[90,1504,1505,1508,1511,1514,1517],{"fill":109},[104,1506],{"cx":1507,"cy":106,"r":1293},"300",[104,1509],{"cx":1510,"cy":106,"r":1293},"390",[104,1512],{"cx":1513,"cy":106,"r":1293},"480",[104,1515],{"cx":1516,"cy":106,"r":1293},"570",[104,1518],{"cx":1519,"cy":106,"r":1293},"660",[75,1521,1524],{"x":1513,"y":1522,"style":1523,"fill":86,"textAnchor":81},"108","text-anchor:middle;font-size:11px;font-family:sans-serif","hand edits, in version control",[75,1526,1527],{"x":122,"y":127,"style":1486,"fill":1365,"textAnchor":1488},"destructive",[275,1529],{"x1":1492,"y1":1530,"x2":1493,"y2":1530,"stroke":1421,"style":1494},"170",[104,1532],{"cx":1497,"cy":1530,"r":1498,"fill":1487},[90,1534,1535,1537,1539],{"fill":109},[104,1536],{"cx":1507,"cy":1530,"r":1293},[104,1538],{"cx":1510,"cy":1530,"r":1293},[104,1540],{"cx":1513,"cy":1530,"r":1293},[104,1542],{"cx":1516,"cy":1530,"r":1498,"fill":1365},[75,1544,1545],{"x":1516,"y":1385,"style":1389,"fill":1365,"textAnchor":81},"wizard again",[90,1547,1549,1551,1553],{"fill":1421,"fillOpacity":1548},0.4,[104,1550],{"cx":1507,"cy":1530,"r":1364},[104,1552],{"cx":1510,"cy":1530,"r":1364},[104,1554],{"cx":1513,"cy":1530,"r":1364},[75,1556,1558],{"x":1513,"y":1557,"style":1523,"fill":1365,"textAnchor":81},"204","every edit since the first run is gone",[310,1560,1562],{"id":1561},"qgis-version-compatibility","QGIS Version Compatibility",[14,1564,1565],{},"Plugin Builder 3 targets the QGIS 3.x \u002F PyQt5 line. The generated code is portable across recent releases, but the resource compiler and a few signal names differ by version.",[1567,1568,1569,1588],"table",{},[1570,1571,1572],"thead",{},[1573,1574,1575,1579,1582,1585],"tr",{},[1576,1577,1578],"th",{},"Component",[1576,1580,1581],{},"QGIS 3.28 LTR (Py 3.9)",[1576,1583,1584],{},"QGIS 3.34 LTR (Py 3.12)",[1576,1586,1587],{},"QGIS 3.40 \u002F 3.44 (Py 3.12)",[1589,1590,1591,1609,1630,1651],"tbody",{},[1573,1592,1593,1597,1601,1605],{},[1594,1595,1596],"td",{},"Resource compiler",[1594,1598,1599],{},[18,1600,211],{},[1594,1602,1603],{},[18,1604,211],{},[1594,1606,1607],{},[18,1608,211],{},[1573,1610,1611,1614,1620,1624],{},[1594,1612,1613],{},"Dialog exec",[1594,1615,1616,1619],{},[18,1617,1618],{},"exec_()"," works",[1594,1621,1622,1619],{},[18,1623,1618],{},[1594,1625,1626,1627],{},"prefer ",[18,1628,1629],{},"exec()",[1573,1631,1632,1637,1642,1646],{},[1594,1633,1634],{},[18,1635,1636],{},"qgisMinimumVersion",[1594,1638,1639],{},[18,1640,1641],{},"3.28",[1594,1643,1644],{},[18,1645,501],{},[1594,1647,1648],{},[18,1649,1650],{},"3.40",[1573,1652,1653,1656,1659,1661],{},[1594,1654,1655],{},"Qt",[1594,1657,1658],{},"Qt 5",[1594,1660,1658],{},[1594,1662,1663],{},"Qt 5 (Qt 6 builds emerging)",[14,1665,1666,1667,1669,1670,1672,1673,1675],{},"Set ",[18,1668,1636],{}," no higher than the oldest QGIS you intend to support. The deprecated ",[18,1671,1618],{}," still works on 3.34 but emits a warning on the newest builds; ",[18,1674,1629],{}," is safe on all 3.x releases.",[310,1677,1679],{"id":1678},"troubleshooting","Troubleshooting",[14,1681,1682,1685,1686,1688,1689,1691,1692,1696,1697,1700],{},[321,1683,1684],{},"Plugin does not appear after deploying."," The folder name must equal the module name and contain ",[18,1687,28],{}," with a ",[18,1690,24],{},". Confirm you deployed to the ",[1693,1694,1695],"em",{},"active"," profile's plugins directory — check ",[18,1698,1699],{},"Settings → User Profiles"," for which profile is loaded.",[14,1702,1703,1708,1709,1711,1712,1715,1716,1718],{},[321,1704,1705,421],{},[18,1706,1707],{},"ModuleNotFoundError: No module named '...resources'"," You skipped compilation, or ",[18,1710,653],{}," did not deploy. Run ",[18,1713,1714],{},"pyrcc5 -o resources.py resources.qrc"," (or ",[18,1717,747],{},") and redeploy.",[14,1720,1721,1724,1725,1727,1728,1730,1731,1733,1734,1736,1737,1739],{},[321,1722,1723],{},"Icon shows as a broken placeholder."," The ",[18,1726,646],{}," path and the actual file disagree, or ",[18,1729,653],{}," is stale. Open ",[18,1732,35],{},", confirm the ",[18,1735,705],{}," entry matches ",[18,1738,619],{},", recompile, and reload.",[14,1741,1742,1745],{},[321,1743,1744],{},"Wizard rejects the module name."," Plugin Builder requires a valid Python identifier. Use lowercase letters, digits, and underscores only — no hyphens or spaces.",[14,1747,1748,1751],{},[321,1749,1750],{},"Changes do not take effect."," QGIS caches imported modules. Use Plugin Reloader rather than re-ticking the checkbox, which does not always re-import edited files.",[310,1753,1755],{"id":1754},"conclusion","Conclusion",[14,1757,1758,1759,1761,1762,1764,1765,1767,1768,1770],{},"Plugin Builder 3 gives you a loadable, conventional plugin in minutes, eliminating the most common boilerplate mistakes. The workflow is consistent: run the wizard, compile the ",[18,1760,646],{}," with ",[18,1763,211],{}," or ",[18,1766,218],{},", deploy to your profile, enable, and then replace the placeholder ",[18,1769,577],{}," with real logic. From here, the natural next steps are wiring richer toolbar actions and building out the dialog interface, both of which build directly on the scaffold you just generated.",[310,1772,1774],{"id":1773},"frequently-asked-questions","Frequently Asked Questions",[14,1776,1777,1780],{},[321,1778,1779],{},"Do I need Plugin Builder installed to ship the plugin?"," No. Plugin Builder only generates the source. Once created, the plugin is self-contained and your users never need Plugin Builder — they just install your packaged zip.",[14,1782,1783,1789,1790,1792,1793,1795],{},[321,1784,1785,1786,1788],{},"Should I commit ",[18,1787,653],{}," to version control?"," It is generated, so many developers gitignore it and compile in CI with ",[18,1791,747],{},". If your contributors lack ",[18,1794,211],{},", committing it is a pragmatic convenience.",[14,1797,1798,1801,1802,1804,1805,1808,1809,1811,1812,1814],{},[321,1799,1800],{},"What is the difference between the Makefile and pb_tool?"," Both compile resources and deploy. The ",[18,1803,627],{}," assumes a Unix shell and ",[18,1806,1807],{},"make","; ",[18,1810,218],{}," is a cross-platform Python CLI driven by ",[18,1813,631],{},", which makes it the friendlier choice on Windows and in CI.",[14,1816,1817,1820,1821,1823,1824,1715,1827,1830],{},[321,1818,1819],{},"Can I change the menu location after generation?"," Yes. The menu is set in ",[18,1822,571],{}," via ",[18,1825,1826],{},"self.iface.addPluginToMenu",[18,1828,1829],{},"addPluginToVectorMenu",", etc.). Edit that call rather than regenerating the whole plugin.",[310,1832,1834],{"id":1833},"related","Related",[315,1836,1837,1845,1849],{},[318,1838,1839,1840,1842,1843],{},"Up: ",[41,1841,44],{"href":43}," — part of ",[41,1844,49],{"href":48},[318,1846,1847],{},[41,1848,1331],{"href":1330},[318,1850,1851],{},[41,1852,559],{"href":558},[1854,1855,1856],"style",{},"html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}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);}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":526,"searchDepth":679,"depth":679,"links":1858},[1859,1860,1861,1862,1863,1864,1865,1866,1867,1868,1869,1870,1871,1872],{"id":312,"depth":679,"text":313},{"id":389,"depth":679,"text":390},{"id":509,"depth":679,"text":510},{"id":635,"depth":679,"text":636},{"id":764,"depth":679,"text":765},{"id":837,"depth":679,"text":838},{"id":868,"depth":679,"text":869},{"id":1334,"depth":679,"text":1335},{"id":1446,"depth":679,"text":1447},{"id":1561,"depth":679,"text":1562},{"id":1678,"depth":679,"text":1679},{"id":1754,"depth":679,"text":1755},{"id":1773,"depth":679,"text":1774},{"id":1833,"depth":679,"text":1834},"Scaffold a working QGIS plugin with Plugin Builder 3. Fill the wizard, compile resources, deploy to your profile, enable it, and customize the generated code.","md",{"slug":1876,"type":1877,"breadcrumb":1878,"datePublished":1879,"dateModified":1880},"create-qgis-plugin-with-plugin-builder","article","Create with Plugin Builder 3","2025-09-22","2026-07-18","\u002Fqgis-plugin-development\u002Fplugin-boilerplate-structure\u002Fcreate-qgis-plugin-with-plugin-builder",{"title":5,"description":1873},"qgis-plugin-development\u002Fplugin-boilerplate-structure\u002Fcreate-qgis-plugin-with-plugin-builder\u002Findex","BCbDN-KKaEtgDhWCb9KCFOVnFCoLqMcE4hdnnACaDAk",1787823362927]