[{"data":1,"prerenderedAt":1169},["ShallowReactive",2],{"doc:\u002Fpyqgis-fundamentals-environment-setup\u002Fworking-with-qgis-projects\u002Fadd-and-remove-layers-from-project-pyqgis":3},{"id":4,"title":5,"body":6,"description":1158,"extension":1159,"meta":1160,"navigation":238,"path":1165,"seo":1166,"stem":1167,"__hash__":1168},"docs\u002Fpyqgis-fundamentals-environment-setup\u002Fworking-with-qgis-projects\u002Fadd-and-remove-layers-from-project-pyqgis\u002Findex.md","Add and Remove Layers from a Project in PyQGIS",{"type":7,"value":8,"toc":1146},"minimark",[9,13,30,39,171,176,202,206,398,416,419,428,437,441,448,520,551,652,656,659,691,710,800,803,830,846,850,853,879,899,903,972,981,985,1045,1049,1060,1064,1070,1082,1088,1105,1111,1115,1142],[10,11,5],"h1",{"id":12},"add-and-remove-layers-from-a-project-in-pyqgis",[14,15,16,17,21,22,25,26,29],"p",{},"The first PyQGIS surprise almost everybody meets is a layer that loads successfully, reports ",[18,19,20],"code",{},"isValid()"," as ",[18,23,24],{},"True",", and then disappears the moment the script finishes. Nothing went wrong: a layer that has not been added to the project is owned only by your Python variable, and when that variable goes out of scope the layer is destroyed. Adding it to ",[18,27,28],{},"QgsProject"," transfers ownership, which is what makes it persist, appear in the Layers panel, render on the canvas and get written into the project file.",[14,31,32,33,38],{},"This recipe belongs to ",[34,35,37],"a",{"href":36},"\u002Fpyqgis-fundamentals-environment-setup\u002Fworking-with-qgis-projects\u002F","Working with QGIS Projects in PyQGIS",". It covers adding one layer or many, choosing whether they show up in the panel, removing them cleanly, and the ownership rules that explain the odd behaviour beginners run into.",[14,40,41],{},[42,43,48,52,56,63,80,89,99,105,115,120,126,130,134,139,144,149,153,156,159,161,163,166,168],"svg",{"viewBox":44,"role":45,"ariaLabel":46,"xmlns":47},"0 0 760 262","img","Diagram contrasting a layer referenced only by a Python variable which is destroyed when the script ends against a layer added to the project which is owned by it and survives","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg",[49,50,51],"title",{},"Who owns the layer decides whether it survives",[53,54,55],"desc",{},"On the left a layer object is referenced only by a local Python variable; when the function returns, the reference is dropped and the underlying C++ object is destroyed, so nothing appears on the map. On the right the same layer is passed to addMapLayer, the project takes ownership, and the layer stays alive, renders, and is written into the project file.",[57,58],"rect",{"x":59,"y":59,"width":60,"height":61,"fill":62},"0","760","262","#f6f3ea",[64,65,66],"defs",{},[67,68,75],"marker",{"id":69,"viewBox":70,"refX":71,"refY":72,"markerWidth":73,"markerHeight":73,"orient":74},"ownArrow","0 0 10 10","8","5","7","auto-start-reverse",[76,77],"path",{"d":78,"fill":79},"M0 0 L10 5 L0 10 z","#2f3b35",[81,82,88],"text",{"x":83,"y":84,"style":85,"fill":86,"textAnchor":87},"380","28","text-anchor:middle;font-size:14px;font-weight:bold;font-family:sans-serif","#17211d","middle","A layer nobody owns is a layer nobody sees",[57,90],{"x":91,"y":92,"width":93,"height":94,"rx":95,"fill":96,"stroke":97,"style":98},"20","48","348","192","10","#fdf2e2","#b91c1c","stroke-width:2.5",[81,100,104],{"x":101,"y":102,"style":103,"fill":97,"textAnchor":87},"194","74","text-anchor:middle;font-size:12px;font-weight:bold;font-family:sans-serif","local variable only",[57,106],{"x":107,"y":108,"width":109,"height":110,"rx":111,"fill":112,"stroke":113,"style":114},"44","90","300","40","6","#fffdf7","#59645f","stroke-width:1.5",[81,116,119],{"x":101,"y":117,"style":118,"fill":79,"textAnchor":87},"115","text-anchor:middle;font-size:11px;font-family:sans-serif","layer = QgsVectorLayer(path, name, \"ogr\")",[121,122],"line",{"x1":101,"y1":123,"x2":101,"y2":124,"stroke":97,"style":125},"130","152","stroke-width:2;marker-end:url(#ownArrow)",[57,127],{"x":107,"y":128,"width":109,"height":129,"rx":111,"fill":62,"stroke":113,"style":114},"158","34",[81,131,133],{"x":101,"y":132,"style":118,"fill":79,"textAnchor":87},"180","function returns, reference dropped",[57,135],{"x":107,"y":136,"width":109,"height":137,"rx":111,"fill":112,"stroke":97,"style":138},"198","30","stroke-width:2",[81,140,143],{"x":101,"y":141,"style":142,"fill":97,"textAnchor":87},"218","text-anchor:middle;font-size:11px;font-weight:bold;font-family:sans-serif","destroyed — nothing on the map",[57,145],{"x":146,"y":92,"width":93,"height":94,"rx":95,"fill":147,"stroke":148,"style":98},"392","#edf8e9","#15803d",[81,150,152],{"x":151,"y":102,"style":103,"fill":148,"textAnchor":87},"566","added to the project",[57,154],{"x":155,"y":108,"width":109,"height":110,"rx":111,"fill":112,"stroke":113,"style":114},"416",[81,157,158],{"x":151,"y":117,"style":118,"fill":79,"textAnchor":87},"project.addMapLayer(layer)",[121,160],{"x1":151,"y1":123,"x2":151,"y2":124,"stroke":148,"style":125},[57,162],{"x":155,"y":128,"width":109,"height":129,"rx":111,"fill":62,"stroke":113,"style":114},[81,164,165],{"x":151,"y":132,"style":118,"fill":79,"textAnchor":87},"project holds the only reference it needs",[57,167],{"x":155,"y":136,"width":109,"height":137,"rx":111,"fill":112,"stroke":148,"style":138},[81,169,170],{"x":151,"y":141,"style":142,"fill":148,"textAnchor":87},"renders, saves, keeps its id",[172,173,175],"h2",{"id":174},"prerequisites","Prerequisites",[177,178,179,187,190],"ul",{},[180,181,182,186],"li",{},[183,184,185],"strong",{},"QGIS 3.34 LTR"," (bundled Python 3.12) or newer.",[180,188,189],{},"A dataset to load — a GeoPackage, shapefile or raster on disk is enough.",[180,191,192,193,196,197,201],{},"Familiarity with loading layers; if ",[18,194,195],{},"QgsVectorLayer"," is new, start at ",[34,198,200],{"href":199},"\u002Fpyqgis-fundamentals-environment-setup\u002Fqgis-python-console-basics\u002F","QGIS Python Console Basics",".",[172,203,205],{"id":204},"add-a-layer","Add a layer",[207,208,213],"pre",{"className":209,"code":210,"language":211,"meta":212,"style":212},"language-python shiki shiki-themes github-dark","from qgis.core import QgsProject, QgsVectorLayer, QgsRasterLayer\n\nproject = QgsProject.instance()\n\nroads = QgsVectorLayer(\"\u002Fdata\u002Fcity.gpkg|layername=roads\", \"Roads\", \"ogr\")\ndem = QgsRasterLayer(\"\u002Fdata\u002Fterrain\u002Fdem.tif\", \"Terrain\")\n\nfor layer in (roads, dem):\n    if not layer.isValid():\n        raise RuntimeError(f\"{layer.name()} failed to load: {layer.error().summary()}\")\n\nproject.addMapLayer(roads)\nproject.addMapLayer(dem)\n","python","",[18,214,215,233,240,252,257,286,307,312,327,339,381,386,392],{"__ignoreMap":212},[216,217,219,223,227,230],"span",{"class":121,"line":218},1,[216,220,222],{"class":221},"snl16","from",[216,224,226],{"class":225},"s95oV"," qgis.core ",[216,228,229],{"class":221},"import",[216,231,232],{"class":225}," QgsProject, QgsVectorLayer, QgsRasterLayer\n",[216,234,236],{"class":121,"line":235},2,[216,237,239],{"emptyLinePlaceholder":238},true,"\n",[216,241,243,246,249],{"class":121,"line":242},3,[216,244,245],{"class":225},"project ",[216,247,248],{"class":221},"=",[216,250,251],{"class":225}," QgsProject.instance()\n",[216,253,255],{"class":121,"line":254},4,[216,256,239],{"emptyLinePlaceholder":238},[216,258,260,263,265,268,272,275,278,280,283],{"class":121,"line":259},5,[216,261,262],{"class":225},"roads ",[216,264,248],{"class":221},[216,266,267],{"class":225}," QgsVectorLayer(",[216,269,271],{"class":270},"sU2Wk","\"\u002Fdata\u002Fcity.gpkg|layername=roads\"",[216,273,274],{"class":225},", ",[216,276,277],{"class":270},"\"Roads\"",[216,279,274],{"class":225},[216,281,282],{"class":270},"\"ogr\"",[216,284,285],{"class":225},")\n",[216,287,289,292,294,297,300,302,305],{"class":121,"line":288},6,[216,290,291],{"class":225},"dem ",[216,293,248],{"class":221},[216,295,296],{"class":225}," QgsRasterLayer(",[216,298,299],{"class":270},"\"\u002Fdata\u002Fterrain\u002Fdem.tif\"",[216,301,274],{"class":225},[216,303,304],{"class":270},"\"Terrain\"",[216,306,285],{"class":225},[216,308,310],{"class":121,"line":309},7,[216,311,239],{"emptyLinePlaceholder":238},[216,313,315,318,321,324],{"class":121,"line":314},8,[216,316,317],{"class":221},"for",[216,319,320],{"class":225}," layer ",[216,322,323],{"class":221},"in",[216,325,326],{"class":225}," (roads, dem):\n",[216,328,330,333,336],{"class":121,"line":329},9,[216,331,332],{"class":221},"    if",[216,334,335],{"class":221}," not",[216,337,338],{"class":225}," layer.isValid():\n",[216,340,342,345,349,352,355,358,361,364,367,370,372,375,377,379],{"class":121,"line":341},10,[216,343,344],{"class":221},"        raise",[216,346,348],{"class":347},"sDLfK"," RuntimeError",[216,350,351],{"class":225},"(",[216,353,354],{"class":221},"f",[216,356,357],{"class":270},"\"",[216,359,360],{"class":347},"{",[216,362,363],{"class":225},"layer.name()",[216,365,366],{"class":347},"}",[216,368,369],{"class":270}," failed to load: ",[216,371,360],{"class":347},[216,373,374],{"class":225},"layer.error().summary()",[216,376,366],{"class":347},[216,378,357],{"class":270},[216,380,285],{"class":225},[216,382,384],{"class":121,"line":383},11,[216,385,239],{"emptyLinePlaceholder":238},[216,387,389],{"class":121,"line":388},12,[216,390,391],{"class":225},"project.addMapLayer(roads)\n",[216,393,395],{"class":121,"line":394},13,[216,396,397],{"class":225},"project.addMapLayer(dem)\n",[14,399,400,403,404,407,408,411,412,415],{},[183,401,402],{},"Breakdown:"," Validating before adding keeps invalid layers out of the project entirely, which is what you want in a script — an invalid layer added to a project is saved into the file and reappears as a broken entry every time it is opened. ",[18,405,406],{},"error().summary()"," gives the provider's own message, which is far more useful than \"layer failed\": it distinguishes a missing file from a missing driver from a layer name that does not exist inside the container. ",[18,409,410],{},"addMapLayer()"," returns the layer on success and ",[18,413,414],{},"None"," on failure, so the return value is worth checking when the input is not under your control.",[14,417,418],{},"Adding several layers at once is a single call, and is meaningfully faster than a loop because the canvas refreshes once rather than per layer:",[207,420,422],{"className":209,"code":421,"language":211,"meta":212,"style":212},"project.addMapLayers([roads, dem, parcels])\n",[18,423,424],{"__ignoreMap":212},[216,425,426],{"class":121,"line":218},[216,427,421],{"class":225},[14,429,430,432,433,436],{},[183,431,402],{}," ",[18,434,435],{},"addMapLayers()"," takes a list and returns the list of layers actually added. On a project with many listeners — a running QGIS with several plugins — the difference between one batch call and twenty individual ones is visible to the user as a single redraw instead of a flicker per layer.",[172,438,440],{"id":439},"add-without-showing-it-in-the-panel","Add without showing it in the panel",[14,442,443,444,447],{},"The second argument decides whether the layer joins the layer tree. Passing ",[18,445,446],{},"False"," registers it while leaving the panel untouched, which is what you want for intermediate results, for layers you are about to place inside a specific group, and for anything a plugin needs to keep alive without cluttering the user's map.",[207,449,451],{"className":209,"code":450,"language":211,"meta":212,"style":212},"project.addMapLayer(scratch, False)                 # registered, not shown\n\nroot = project.layerTreeRoot()\ngroup = root.findGroup(\"Analysis\") or root.addGroup(\"Analysis\")\ngroup.insertLayer(0, scratch)                       # now it appears, exactly where you chose\n",[18,452,453,467,471,481,507],{"__ignoreMap":212},[216,454,455,458,460,463],{"class":121,"line":218},[216,456,457],{"class":225},"project.addMapLayer(scratch, ",[216,459,446],{"class":347},[216,461,462],{"class":225},")                 ",[216,464,466],{"class":465},"sjoCn","# registered, not shown\n",[216,468,469],{"class":121,"line":235},[216,470,239],{"emptyLinePlaceholder":238},[216,472,473,476,478],{"class":121,"line":242},[216,474,475],{"class":225},"root ",[216,477,248],{"class":221},[216,479,480],{"class":225}," project.layerTreeRoot()\n",[216,482,483,486,488,491,494,497,500,503,505],{"class":121,"line":254},[216,484,485],{"class":225},"group ",[216,487,248],{"class":221},[216,489,490],{"class":225}," root.findGroup(",[216,492,493],{"class":270},"\"Analysis\"",[216,495,496],{"class":225},") ",[216,498,499],{"class":221},"or",[216,501,502],{"class":225}," root.addGroup(",[216,504,493],{"class":270},[216,506,285],{"class":225},[216,508,509,512,514,517],{"class":121,"line":259},[216,510,511],{"class":225},"group.insertLayer(",[216,513,59],{"class":347},[216,515,516],{"class":225},", scratch)                       ",[216,518,519],{"class":465},"# now it appears, exactly where you chose\n",[14,521,522,524,525,528,529,531,532,535,536,540,541,544,545,547,548,550],{},[183,523,402],{}," With ",[18,526,527],{},"addToLegend"," set to ",[18,530,446],{}," the layer is in ",[18,533,534],{},"project.mapLayers()"," but has no node in the tree, so it does not render and does not appear in the panel — although it ",[537,538,539],"em",{},"is"," still written into the project file when saved. The two-step form is the only way to control position: the one-argument call always inserts at the top of the tree. ",[18,542,543],{},"findGroup()"," returning ",[18,546,414],{}," for a missing group is why the ",[18,549,499],{}," idiom appears in most scripts that build structure.",[14,552,553],{},[42,554,557,560,563,566,569,575,580,584,588,592,596,599,604,606,610,612,616,620,624,626,629,631,634,638,642,644,647,649],{"viewBox":555,"role":45,"ariaLabel":556,"xmlns":47},"0 0 760 250","Decision table showing when to add a layer with the legend flag true, when to add it with the flag false and place it manually, and when not to add it to the project at all",[49,558,559],{},"Three ways to bring a layer into a script",[53,561,562],{},"Three rows compare approaches. Adding with the legend flag true suits a layer the user should see immediately at the top of the panel. Adding with the flag false and inserting into the tree suits a layer that belongs in a specific group or position. Not adding at all suits a purely temporary layer used inside one function, which must not outlive it.",[57,564],{"x":59,"y":59,"width":60,"height":565,"fill":62},"250",[81,567,568],{"x":83,"y":84,"style":85,"fill":86,"textAnchor":87},"Pick by what the user should end up seeing",[57,570],{"x":571,"y":572,"width":573,"height":137,"rx":111,"fill":574,"stroke":113,"style":114},"16","46","240","#efeadd",[81,576,579],{"x":577,"y":578,"style":142,"fill":86,"textAnchor":87},"136","66","the call",[57,581],{"x":582,"y":572,"width":583,"height":137,"rx":111,"fill":574,"stroke":113,"style":114},"264","230",[81,585,587],{"x":586,"y":578,"style":142,"fill":86,"textAnchor":87},"379","what happens",[57,589],{"x":590,"y":572,"width":591,"height":137,"rx":111,"fill":574,"stroke":113,"style":114},"502","242",[81,593,595],{"x":594,"y":578,"style":142,"fill":86,"textAnchor":87},"623","use it when",[57,597],{"x":571,"y":598,"width":573,"height":92,"rx":111,"fill":147,"stroke":148,"style":114},"82",[81,600,603],{"x":137,"y":601,"style":602,"fill":79},"111","font-size:11px;font-family:sans-serif","addMapLayer(layer)",[57,605],{"x":582,"y":598,"width":583,"height":92,"rx":111,"fill":112,"stroke":113,"style":114},[81,607,609],{"x":608,"y":601,"style":602,"fill":79},"278","registered and shown on top",[57,611],{"x":590,"y":598,"width":591,"height":92,"rx":111,"fill":112,"stroke":113,"style":114},[81,613,615],{"x":614,"y":601,"style":602,"fill":79},"516","the result the user asked for",[57,617],{"x":571,"y":577,"width":573,"height":92,"rx":111,"fill":618,"stroke":619,"style":114},"#eff3ff","#2563eb",[81,621,623],{"x":137,"y":622,"style":602,"fill":79},"165","addMapLayer(layer, False)",[57,625],{"x":582,"y":577,"width":583,"height":92,"rx":111,"fill":112,"stroke":113,"style":114},[81,627,628],{"x":608,"y":622,"style":602,"fill":79},"registered, you place it",[57,630],{"x":590,"y":577,"width":591,"height":92,"rx":111,"fill":112,"stroke":113,"style":114},[81,632,633],{"x":614,"y":622,"style":602,"fill":79},"it belongs in a group",[57,635],{"x":571,"y":636,"width":573,"height":92,"rx":111,"fill":96,"stroke":637,"style":114},"190","#b45309",[81,639,641],{"x":137,"y":640,"style":602,"fill":79},"219","no add at all",[57,643],{"x":582,"y":636,"width":583,"height":92,"rx":111,"fill":112,"stroke":113,"style":114},[81,645,646],{"x":608,"y":640,"style":602,"fill":79},"dies with your variable",[57,648],{"x":590,"y":636,"width":591,"height":92,"rx":111,"fill":112,"stroke":113,"style":114},[81,650,651],{"x":614,"y":640,"style":602,"fill":79},"strictly temporary work",[172,653,655],{"id":654},"remove-a-layer","Remove a layer",[14,657,658],{},"Removal takes the layer id, not the layer object, and it destroys the layer — every Python reference to it becomes dangerous immediately.",[207,660,662],{"className":209,"code":661,"language":211,"meta":212,"style":212},"layer_id = roads.id()\nproject.removeMapLayer(layer_id)\nroads = None                                  # do not touch the old reference again\n",[18,663,664,674,679],{"__ignoreMap":212},[216,665,666,669,671],{"class":121,"line":218},[216,667,668],{"class":225},"layer_id ",[216,670,248],{"class":221},[216,672,673],{"class":225}," roads.id()\n",[216,675,676],{"class":121,"line":235},[216,677,678],{"class":225},"project.removeMapLayer(layer_id)\n",[216,680,681,683,685,688],{"class":121,"line":242},[216,682,262],{"class":225},[216,684,248],{"class":221},[216,686,687],{"class":347}," None",[216,689,690],{"class":465},"                                  # do not touch the old reference again\n",[14,692,693,432,695,698,699,701,702,705,706,709],{},[183,694,402],{},[18,696,697],{},"removeMapLayer()"," takes the layer out of both the registry and the layer tree, then deletes the underlying C++ object. Any surviving Python variable is now a wrapper around freed memory, and calling a method on it crashes QGIS outright rather than raising a Python exception — one of the few ways PyQGIS can take the whole application down. Setting the variable to ",[18,700,414],{}," immediately after removal is a cheap habit that prevents it. To remove several, ",[18,703,704],{},"removeMapLayers([id1, id2])"," takes a list of ids, and ",[18,707,708],{},"removeAllMapLayers()"," empties the project.",[14,711,712],{},[42,713,716,719,722,725,732,735,741,746,750,753,757,760,764,768,771,776,780,784,787,790,793,797],{"viewBox":714,"role":45,"ariaLabel":715,"xmlns":47},"0 0 760 244","Sequence showing a layer being removed from the project, the underlying object destroyed, and a surviving Python variable now pointing at freed memory",[49,717,718],{},"What a removed layer leaves behind",[53,720,721],{},"Removing a layer by identifier takes it out of the registry and the layer tree and destroys the underlying object. A Python variable that still referenced it now points at freed memory, and calling any method on it crashes the application rather than raising an exception, which is why the reference should be cleared immediately.",[57,723],{"x":59,"y":59,"width":60,"height":724,"fill":62},"244",[64,726,727],{},[67,728,730],{"id":729,"viewBox":70,"refX":71,"refY":72,"markerWidth":73,"markerHeight":73,"orient":74},"rmArrow",[76,731],{"d":78,"fill":79},[81,733,734],{"x":83,"y":84,"style":85,"fill":86,"textAnchor":87},"The variable outlives the object it points at",[57,736],{"x":91,"y":737,"width":738,"height":578,"rx":71,"fill":739,"stroke":740,"style":138},"60","200","#eef7f4","#0f766e",[81,742,745],{"x":743,"y":744,"style":142,"fill":740,"textAnchor":87},"120","86","removeMapLayer(id)",[81,747,749],{"x":743,"y":748,"style":118,"fill":79,"textAnchor":87},"108","registry and tree cleared",[57,751],{"x":752,"y":737,"width":738,"height":578,"rx":71,"fill":96,"stroke":637,"style":138},"256",[81,754,756],{"x":755,"y":744,"style":142,"fill":637,"textAnchor":87},"356","object destroyed",[81,758,759],{"x":755,"y":748,"style":118,"fill":79,"textAnchor":87},"the C++ side is gone",[57,761],{"x":762,"y":737,"width":763,"height":578,"rx":71,"fill":96,"stroke":97,"style":98},"492","248",[81,765,767],{"x":766,"y":744,"style":142,"fill":97,"textAnchor":87},"616","your variable still exists",[81,769,770],{"x":766,"y":748,"style":118,"fill":79,"textAnchor":87},"and now points at nothing",[121,772],{"x1":773,"y1":774,"x2":565,"y2":774,"stroke":79,"style":775},"220","93","stroke-width:2;marker-end:url(#rmArrow)",[121,777],{"x1":778,"y1":774,"x2":779,"y2":774,"stroke":79,"style":775},"456","486",[57,781],{"x":743,"y":782,"width":573,"height":783,"rx":71,"fill":112,"stroke":97,"style":138},"156","62",[81,785,786],{"x":573,"y":132,"style":142,"fill":97,"textAnchor":87},"layer.name() now",[81,788,789],{"x":573,"y":738,"style":118,"fill":79,"textAnchor":87},"crashes QGIS outright",[57,791],{"x":792,"y":782,"width":573,"height":783,"rx":71,"fill":112,"stroke":148,"style":138},"400",[81,794,796],{"x":795,"y":132,"style":142,"fill":148,"textAnchor":87},"520","layer = None first",[81,798,799],{"x":795,"y":738,"style":118,"fill":79,"textAnchor":87},"and the mistake is impossible",[14,801,802],{},"Removing by name, which is what most scripts actually want, goes through a lookup:",[207,804,806],{"className":209,"code":805,"language":211,"meta":212,"style":212},"for layer in project.mapLayersByName(\"Scratch\"):\n    project.removeMapLayer(layer.id())\n",[18,807,808,825],{"__ignoreMap":212},[216,809,810,812,814,816,819,822],{"class":121,"line":218},[216,811,317],{"class":221},[216,813,320],{"class":225},[216,815,323],{"class":221},[216,817,818],{"class":225}," project.mapLayersByName(",[216,820,821],{"class":270},"\"Scratch\"",[216,823,824],{"class":225},"):\n",[216,826,827],{"class":121,"line":235},[216,828,829],{"class":225},"    project.removeMapLayer(layer.id())\n",[14,831,832,432,834,837,838,841,842,845],{},[183,833,402],{},[18,835,836],{},"mapLayersByName()"," returns a list because names are not unique — two layers can happily be called \"Scratch\", and a script that assumes ",[18,839,840],{},"[0]"," will remove one of them and leave the other. Iterating the list handles both cases. Collect the ids first if you need to remove layers while iterating the registry, because mutating ",[18,843,844],{},"mapLayers()"," during a loop over it is undefined behaviour in the same way as mutating a Python dictionary while iterating.",[172,847,849],{"id":848},"replace-a-layers-data-without-removing-it","Replace a layer's data without removing it",[14,851,852],{},"Removing and re-adding is often the wrong tool: it loses the styling, the layer id, and every reference from layouts, joins and relations. Re-pointing the existing layer keeps all of it.",[207,854,856],{"className":209,"code":855,"language":211,"meta":212,"style":212},"roads.setDataSource(\"\u002Fdata\u002Fcity_2026.gpkg|layername=roads\",\n                    roads.name(), roads.providerType())\nroads.triggerRepaint()\n",[18,857,858,869,874],{"__ignoreMap":212},[216,859,860,863,866],{"class":121,"line":218},[216,861,862],{"class":225},"roads.setDataSource(",[216,864,865],{"class":270},"\"\u002Fdata\u002Fcity_2026.gpkg|layername=roads\"",[216,867,868],{"class":225},",\n",[216,870,871],{"class":121,"line":235},[216,872,873],{"class":225},"                    roads.name(), roads.providerType())\n",[216,875,876],{"class":121,"line":242},[216,877,878],{"class":225},"roads.triggerRepaint()\n",[14,880,881,432,883,886,887,890,891,894,895,898],{},[183,882,402],{},[18,884,885],{},"setDataSource()"," swaps the underlying data while the layer object — and therefore its id — stays the same, so a layout that draws this layer keeps working and the symbology is preserved. The provider type must be passed unchanged unless you really are switching provider, for instance from ",[18,888,889],{},"ogr"," to ",[18,892,893],{},"postgres",". ",[18,896,897],{},"triggerRepaint()"," asks the canvas to redraw; without it the map can show the old rendering until something else forces a refresh.",[172,900,902],{"id":901},"qgis-version-compatibility","QGIS version compatibility",[904,905,906,922],"table",{},[907,908,909],"thead",{},[910,911,912,916,919],"tr",{},[913,914,915],"th",{},"QGIS version",[913,917,918],{},"Python",[913,920,921],{},"Notes",[923,924,925,937,947,958],"tbody",{},[910,926,927,931,934],{},[928,929,930],"td",{},"3.22 LTR",[928,932,933],{},"3.9",[928,935,936],{},"All calls behave as described.",[910,938,939,942,944],{},[928,940,941],{},"3.28 LTR",[928,943,933],{},[928,945,946],{},"Identical.",[910,948,949,952,955],{},[928,950,951],{},"3.34 LTR",[928,953,954],{},"3.12",[928,956,957],{},"Baseline for this page.",[910,959,960,963,965],{},[928,961,962],{},"3.40 \u002F 3.44",[928,964,954],{},[928,966,967,968,971],{},"Identical; ",[18,969,970],{},"addMapLayer"," gained no signature changes, and layer removal is unchanged.",[14,973,974,977,978,980],{},[18,975,976],{},"QgsMapLayerRegistry",", seen in pre-3.0 examples still circulating online, no longer exists — its methods moved onto ",[18,979,28],{}," and the names are otherwise the same. Any snippet importing it is written for QGIS 2 and will need more than this one change.",[172,982,984],{"id":983},"troubleshooting","Troubleshooting",[177,986,987,998,1004,1020,1026,1036],{},[180,988,989,992,993,21,995,997],{},[183,990,991],{},"The layer loads but never appears."," It was not added to the project, or it was added with ",[18,994,527],{},[18,996,446],{}," and never inserted into a tree node.",[180,999,1000,1003],{},[183,1001,1002],{},"QGIS crashes right after removing a layer."," Something still holds a Python reference to the removed layer. Clear your references before removing, and never keep long-lived module-level references to layers.",[180,1005,1006,1013,1014,1016,1017,1019],{},[183,1007,1008,1010,1011,201],{},[18,1009,410],{}," returns ",[18,1012,414],{}," The layer was invalid. Check ",[18,1015,20],{}," and ",[18,1018,406],{}," first — the project refuses invalid layers only in some builds, so relying on it is not portable.",[180,1021,1022,1025],{},[183,1023,1024],{},"Duplicate layers pile up on repeated runs."," The script adds without checking. Look up by name first and either reuse or remove the existing one before adding.",[180,1027,1028,1031,1032,1035],{},[183,1029,1030],{},"Removing by name removed the wrong layer."," Names are not unique. Store the id returned by ",[18,1033,1034],{},"layer.id()"," when you add the layer, and remove by that.",[180,1037,1038,1041,1042,1044],{},[183,1039,1040],{},"The panel shows the layer twice."," A tree node was inserted for a layer that was already in the tree. Add with ",[18,1043,446],{}," when you intend to place it yourself.",[172,1046,1048],{"id":1047},"conclusion","Conclusion",[14,1050,1051,1053,1054,1056,1057,1059],{},[18,1052,410],{}," transfers ownership and is what keeps a layer alive; pass ",[18,1055,446],{}," as the second argument when you want to control where it appears, then insert a node into the layer tree yourself. Remove by id, drop your Python references immediately afterwards, and prefer ",[18,1058,885],{}," over remove-and-re-add whenever the layer's styling and identity should survive.",[172,1061,1063],{"id":1062},"frequently-asked-questions","Frequently Asked Questions",[14,1065,1066,1069],{},[183,1067,1068],{},"Why does my layer disappear when the function ends?","\nBecause nothing owns it. Add it to the project — or, for a genuinely temporary layer, keep it alive by holding a reference for as long as you need it.",[14,1071,1072,1081],{},[183,1073,1074,1075,1016,1077,1080],{},"What is the difference between ",[18,1076,970],{},[18,1078,1079],{},"addMapLayers","?","\nOnly batching. The plural form takes a list, emits fewer signals and triggers one canvas refresh, which is noticeably smoother when adding many layers at once.",[14,1083,1084,1087],{},[183,1085,1086],{},"Can I add the same layer object to two projects?","\nNo. Ownership is exclusive; the second project would try to take ownership of an object the first already owns. Build a second layer from the same data source instead.",[14,1089,1090,1093,1094,1096,1097,1100,1101,201],{},[183,1091,1092],{},"How do I add a layer to a specific position in the panel?","\nRegister it with ",[18,1095,623],{},", then call ",[18,1098,1099],{},"insertLayer(index, layer)"," on the root or on a group node — see ",[34,1102,1104],{"href":1103},"\u002Fpyqgis-fundamentals-environment-setup\u002Fworking-with-qgis-projects\u002Forganise-layer-tree-groups-pyqgis\u002F","Organise the Layer Tree with Groups in PyQGIS",[14,1106,1107,1110],{},[183,1108,1109],{},"Does removing a layer delete the underlying file?","\nNo. Removal affects the project only; the GeoPackage, shapefile or database table is untouched.",[172,1112,1114],{"id":1113},"related","Related",[177,1116,1117,1122,1128,1132,1138],{},[180,1118,1119,1121],{},[34,1120,37],{"href":36}," — the guide this recipe belongs to",[180,1123,1124],{},[34,1125,1127],{"href":1126},"\u002Fpyqgis-fundamentals-environment-setup\u002Fworking-with-qgis-projects\u002Fsave-and-load-qgis-project-pyqgis\u002F","Save and Load a QGIS Project in PyQGIS",[180,1129,1130],{},[34,1131,1104],{"href":1103},[180,1133,1134],{},[34,1135,1137],{"href":1136},"\u002Fqgis-plugin-development\u002Fcustom-map-tools-and-canvas-interaction\u002Fconnect-layer-signals-pyqgis\u002F","Connect Layer Signals in PyQGIS",[180,1139,1140],{},[34,1141,200],{"href":199},[1143,1144,1145],"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 .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 .sjoCn, html code.shiki .sjoCn{--shiki-default:#9AA79F}",{"title":212,"searchDepth":235,"depth":235,"links":1147},[1148,1149,1150,1151,1152,1153,1154,1155,1156,1157],{"id":174,"depth":235,"text":175},{"id":204,"depth":235,"text":205},{"id":439,"depth":235,"text":440},{"id":654,"depth":235,"text":655},{"id":848,"depth":235,"text":849},{"id":901,"depth":235,"text":902},{"id":983,"depth":235,"text":984},{"id":1047,"depth":235,"text":1048},{"id":1062,"depth":235,"text":1063},{"id":1113,"depth":235,"text":1114},"Register layers with QgsProject, control whether they appear in the Layers panel, remove them without leaving dangling references, and avoid the garbage-collection trap that makes a loaded layer vanish.","md",{"slug":1161,"type":1162,"breadcrumb":1163,"datePublished":1164,"dateModified":1164},"add-and-remove-layers-from-project-pyqgis","article","Add & Remove Layers","2026-08-15","\u002Fpyqgis-fundamentals-environment-setup\u002Fworking-with-qgis-projects\u002Fadd-and-remove-layers-from-project-pyqgis",{"title":5,"description":1158},"pyqgis-fundamentals-environment-setup\u002Fworking-with-qgis-projects\u002Fadd-and-remove-layers-from-project-pyqgis\u002Findex","T6GUv1oh_ldPqXOZH7FjbW-9emuAtp7XE3fM3PSPwNk",1786789584634]