[{"data":1,"prerenderedAt":1812},["ShallowReactive",2],{"doc:\u002Fqgis-plugin-development\u002Fqt-designer-for-gis-interfaces\u002Fuse-qgsmaplayercombobox-in-plugin-dialog-pyqgis":3},{"id":4,"title":5,"body":6,"description":1801,"extension":1802,"meta":1803,"navigation":310,"path":1808,"seo":1809,"stem":1810,"__hash__":1811},"docs\u002Fqgis-plugin-development\u002Fqt-designer-for-gis-interfaces\u002Fuse-qgsmaplayercombobox-in-plugin-dialog-pyqgis\u002Findex.md","Use QgsMapLayerComboBox in a Plugin Dialog",{"type":7,"value":8,"toc":1787},"minimark",[9,13,22,43,227,232,258,262,265,371,393,403,426,430,435,509,547,554,558,564,592,767,787,885,889,892,1095,1112,1121,1125,1128,1315,1339,1343,1346,1490,1514,1518,1524,1610,1614,1664,1668,1686,1690,1718,1727,1737,1751,1755,1783],[10,11,5],"h1",{"id":12},"use-qgsmaplayercombobox-in-a-plugin-dialog",[14,15,16,17,21],"p",{},"Almost every plugin dialog starts with \"which layer?\" and, very often, \"which field?\". Writing that by hand means populating a ",[18,19,20],"code",{},"QComboBox"," from the project, filtering it to the right geometry type, keeping it in step as layers are added and removed, and mapping the selected text back to a layer object. QGIS ships widgets that do all of it, and using them removes about forty lines and several bugs per dialog.",[14,23,24,25,30,31,34,35,38,39,42],{},"This recipe belongs to ",[26,27,29],"a",{"href":28},"\u002Fqgis-plugin-development\u002Fqt-designer-for-gis-interfaces\u002F","Qt Designer for GIS Interfaces",". It covers ",[18,32,33],{},"QgsMapLayerComboBox"," and its filters, linking a ",[18,36,37],{},"QgsFieldComboBox"," to it, promoting the widgets in Qt Designer so the ",[18,40,41],{},".ui"," file carries them, and the details that keep the dialog honest when the project changes underneath it.",[14,44,45],{},[46,47,52,56,60,67,84,93,103,109,118,123,126,130,134,138,141,145,153,161,166,171,175,179,185,190,192,196,200,208,213,217,220,223],"svg",{"viewBox":48,"role":49,"ariaLabel":50,"xmlns":51},"0 0 760 312","img","A layer combo box filtered to polygon layers driving a field combo box filtered to numeric fields, with both staying in step with the project automatically","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg",[53,54,55],"title",{},"Two linked widgets doing the bookkeeping",[57,58,59],"desc",{},"The layer combo box lists only the layers matching its filter and updates itself when the project changes. Selecting a layer sets it on the field combo box, which then lists only that layer's fields, filtered to the types the operation accepts. Neither needs manual population.",[61,62],"rect",{"x":63,"y":63,"width":64,"height":65,"fill":66},"0","760","312","#f6f3ea",[68,69,70],"defs",{},[71,72,79],"marker",{"id":73,"viewBox":74,"refX":75,"refY":76,"markerWidth":77,"markerHeight":77,"orient":78},"cbArrow","0 0 10 10","8","5","7","auto-start-reverse",[80,81],"path",{"d":82,"fill":83},"M0 0 L10 5 L0 10 z","#2f3b35",[85,86,92],"text",{"x":87,"y":88,"style":89,"fill":90,"textAnchor":91},"380","28","text-anchor:middle;font-size:14px;font-weight:bold;font-family:sans-serif","#17211d","middle","The widgets watch the project; you watch neither",[61,94],{"x":95,"y":96,"width":97,"height":98,"rx":99,"fill":100,"stroke":101,"style":102},"24","60","200","180","10","#fffdf7","#59645f","stroke-width:2",[85,104,108],{"x":105,"y":106,"style":107,"fill":90,"textAnchor":91},"124","86","text-anchor:middle;font-size:11px;font-weight:bold;font-family:sans-serif","the project",[61,110],{"x":111,"y":112,"width":113,"height":95,"rx":114,"fill":115,"stroke":116,"style":117},"46","102","156","4","#dbeafe","#2563eb","stroke-width:1.3",[85,119,122],{"x":105,"y":120,"style":121,"fill":83,"textAnchor":91},"119","text-anchor:middle;font-size:9.5px;font-family:sans-serif","parcels (polygon)",[61,124],{"x":111,"y":125,"width":113,"height":95,"rx":114,"fill":115,"stroke":116,"style":117},"132",[85,127,129],{"x":105,"y":128,"style":121,"fill":83,"textAnchor":91},"149","zones (polygon)",[61,131],{"x":111,"y":132,"width":113,"height":95,"rx":114,"fill":133,"stroke":101,"style":117},"162","#efeadd",[85,135,137],{"x":105,"y":136,"style":121,"fill":101,"textAnchor":91},"179","roads (line)",[61,139],{"x":111,"y":140,"width":113,"height":95,"rx":114,"fill":133,"stroke":101,"style":117},"192",[85,142,144],{"x":105,"y":143,"style":121,"fill":101,"textAnchor":91},"209","dem (raster)",[146,147],"line",{"x1":148,"y1":149,"x2":150,"y2":151,"stroke":83,"style":152},"230","150","272","126","stroke-width:2;marker-end:url(#cbArrow)",[61,154],{"x":155,"y":156,"width":157,"height":158,"rx":75,"fill":159,"stroke":116,"style":160},"280","88","216","76","#eff3ff","stroke-width:2.5",[85,162,33],{"x":163,"y":164,"style":165,"fill":116,"textAnchor":91},"388","114","text-anchor:middle;font-size:10.5px;font-weight:bold;font-family:monospace",[85,167,170],{"x":163,"y":168,"style":169,"fill":83,"textAnchor":91},"136","text-anchor:middle;font-size:10px;font-family:sans-serif","filter: PolygonLayer",[85,172,174],{"x":163,"y":173,"style":169,"fill":83,"textAnchor":91},"154","shows two of the four",[146,176],{"x1":163,"y1":177,"x2":163,"y2":178,"stroke":83,"style":152},"170","196",[85,180,184],{"x":181,"y":182,"style":183,"fill":101,"textAnchor":91},"450","188","text-anchor:middle;font-size:9.5px;font-family:monospace","layerChanged",[61,186],{"x":155,"y":187,"width":157,"height":158,"rx":75,"fill":188,"stroke":189,"style":160},"204","#edf8e9","#15803d",[85,191,37],{"x":163,"y":148,"style":165,"fill":189,"textAnchor":91},[85,193,195],{"x":163,"y":194,"style":169,"fill":83,"textAnchor":91},"252","filter: Numeric",[85,197,199],{"x":163,"y":198,"style":169,"fill":83,"textAnchor":91},"270","repopulates itself",[61,201],{"x":202,"y":203,"width":204,"height":205,"rx":99,"fill":206,"stroke":207,"style":160},"524","120","212","128","#fdf2e2","#b45309",[85,209,212],{"x":210,"y":149,"style":211,"fill":207,"textAnchor":91},"630","text-anchor:middle;font-size:10.5px;font-weight:bold;font-family:sans-serif","what you no longer write",[85,214,216],{"x":210,"y":215,"style":169,"fill":83,"textAnchor":91},"176","populate on open",[85,218,219],{"x":210,"y":178,"style":169,"fill":83,"textAnchor":91},"refresh on layersAdded",[85,221,222],{"x":210,"y":157,"style":169,"fill":83,"textAnchor":91},"map name back to layer",[85,224,226],{"x":210,"y":225,"style":169,"fill":83,"textAnchor":91},"236","handle the empty case",[228,229,231],"h2",{"id":230},"prerequisites","Prerequisites",[233,234,235,243,251],"ul",{},[236,237,238,242],"li",{},[239,240,241],"strong",{},"QGIS 3.34 LTR"," (bundled Python 3.12) or newer.",[236,244,245,246,250],{},"A plugin with a dialog, whether built in ",[26,247,249],{"href":248},"\u002Fqgis-plugin-development\u002Fqt-designer-for-gis-interfaces\u002Fload-ui-file-at-runtime-pyqgis\u002F","Qt Designer"," or in code.",[236,252,253,254,257],{},"The ",[18,255,256],{},"qgis.gui"," module, which is available in any plugin context.",[228,259,261],{"id":260},"the-layer-combo-box","The layer combo box",[14,263,264],{},"Three lines give a filtered, self-maintaining layer picker.",[266,267,272],"pre",{"className":268,"code":269,"language":270,"meta":271,"style":271},"language-python shiki shiki-themes github-dark","from qgis.gui import QgsMapLayerComboBox\nfrom qgis.core import QgsMapLayerProxyModel\n\ncombo = QgsMapLayerComboBox(self)\ncombo.setFilters(QgsMapLayerProxyModel.PolygonLayer)\ncombo.setAllowEmptyLayer(True, \"— choose a layer —\")\n\nlayer = combo.currentLayer()\n","python","",[18,273,274,292,305,312,331,337,355,360],{"__ignoreMap":271},[275,276,278,282,286,289],"span",{"class":146,"line":277},1,[275,279,281],{"class":280},"snl16","from",[275,283,285],{"class":284},"s95oV"," qgis.gui ",[275,287,288],{"class":280},"import",[275,290,291],{"class":284}," QgsMapLayerComboBox\n",[275,293,295,297,300,302],{"class":146,"line":294},2,[275,296,281],{"class":280},[275,298,299],{"class":284}," qgis.core ",[275,301,288],{"class":280},[275,303,304],{"class":284}," QgsMapLayerProxyModel\n",[275,306,308],{"class":146,"line":307},3,[275,309,311],{"emptyLinePlaceholder":310},true,"\n",[275,313,315,318,321,324,328],{"class":146,"line":314},4,[275,316,317],{"class":284},"combo ",[275,319,320],{"class":280},"=",[275,322,323],{"class":284}," QgsMapLayerComboBox(",[275,325,327],{"class":326},"sDLfK","self",[275,329,330],{"class":284},")\n",[275,332,334],{"class":146,"line":333},5,[275,335,336],{"class":284},"combo.setFilters(QgsMapLayerProxyModel.PolygonLayer)\n",[275,338,340,343,346,349,353],{"class":146,"line":339},6,[275,341,342],{"class":284},"combo.setAllowEmptyLayer(",[275,344,345],{"class":326},"True",[275,347,348],{"class":284},", ",[275,350,352],{"class":351},"sU2Wk","\"— choose a layer —\"",[275,354,330],{"class":284},[275,356,358],{"class":146,"line":357},7,[275,359,311],{"emptyLinePlaceholder":310},[275,361,363,366,368],{"class":146,"line":362},8,[275,364,365],{"class":284},"layer ",[275,367,320],{"class":280},[275,369,370],{"class":284}," combo.currentLayer()\n",[14,372,373,376,377,380,381,384,385,388,389,392],{},[239,374,375],{},"Breakdown:"," ",[18,378,379],{},"setFilters()"," takes a flag combination, so ",[18,382,383],{},"PolygonLayer | LineLayer"," accepts both and ",[18,386,387],{},"VectorLayer"," accepts any geometry. The widget listens to the project itself, so a layer added while the dialog is open appears immediately without any code on your part. ",[18,390,391],{},"setAllowEmptyLayer(True, …)"," adds a null entry, which matters because otherwise the first matching layer is preselected and a user who forgets to choose gets an operation on whatever happened to be first — the most common cause of a plugin doing something surprising.",[14,394,395,398,399,402],{},[18,396,397],{},"currentLayer()"," returns the layer object directly, or ",[18,400,401],{},"None"," when the empty entry is selected. That is the whole reason to use the widget: no name lookup, no ambiguity when two layers share a name.",[14,404,405,406,409,410,413,414,417,418,421,422,425],{},"Filters worth knowing beyond geometry type: ",[18,407,408],{},"HasGeometry"," excludes attribute-only tables, ",[18,411,412],{},"WritableLayer"," excludes read-only sources, and ",[18,415,416],{},"RasterLayer"," and ",[18,419,420],{},"MeshLayer"," select those types. They can be combined, and ",[18,423,424],{},"setExceptedLayerList()"," removes specific layers — useful for stopping a dialog offering its own output layer as an input.",[228,427,429],{"id":428},"link-a-field-combo-box","Link a field combo box",[14,431,432,434],{},[18,433,37],{}," lists the fields of whatever layer it is given.",[266,436,438],{"className":268,"code":437,"language":270,"meta":271,"style":271},"from qgis.gui import QgsFieldComboBox\nfrom qgis.core import QgsFieldProxyModel\n\nfield_combo = QgsFieldComboBox(self)\nfield_combo.setFilters(QgsFieldProxyModel.Numeric)\nfield_combo.setAllowEmptyFieldName(True)\n\ncombo.layerChanged.connect(field_combo.setLayer)\nfield_combo.setLayer(combo.currentLayer())\n",[18,439,440,451,462,466,480,485,494,498,503],{"__ignoreMap":271},[275,441,442,444,446,448],{"class":146,"line":277},[275,443,281],{"class":280},[275,445,285],{"class":284},[275,447,288],{"class":280},[275,449,450],{"class":284}," QgsFieldComboBox\n",[275,452,453,455,457,459],{"class":146,"line":294},[275,454,281],{"class":280},[275,456,299],{"class":284},[275,458,288],{"class":280},[275,460,461],{"class":284}," QgsFieldProxyModel\n",[275,463,464],{"class":146,"line":307},[275,465,311],{"emptyLinePlaceholder":310},[275,467,468,471,473,476,478],{"class":146,"line":314},[275,469,470],{"class":284},"field_combo ",[275,472,320],{"class":280},[275,474,475],{"class":284}," QgsFieldComboBox(",[275,477,327],{"class":326},[275,479,330],{"class":284},[275,481,482],{"class":146,"line":333},[275,483,484],{"class":284},"field_combo.setFilters(QgsFieldProxyModel.Numeric)\n",[275,486,487,490,492],{"class":146,"line":339},[275,488,489],{"class":284},"field_combo.setAllowEmptyFieldName(",[275,491,345],{"class":326},[275,493,330],{"class":284},[275,495,496],{"class":146,"line":357},[275,497,311],{"emptyLinePlaceholder":310},[275,499,500],{"class":146,"line":362},[275,501,502],{"class":284},"combo.layerChanged.connect(field_combo.setLayer)\n",[275,504,506],{"class":146,"line":505},9,[275,507,508],{"class":284},"field_combo.setLayer(combo.currentLayer())\n",[14,510,511,513,514,516,517,520,521,525,526,529,530,348,533,348,536,417,539,542,543,546],{},[239,512,375],{}," Connecting ",[18,515,184],{}," directly to ",[18,518,519],{},"setLayer"," is the whole linkage — no intermediate slot, because the signal emits a layer and the slot takes one. The explicit call afterwards is easy to omit and matters: signals only fire on ",[522,523,524],"em",{},"change",", so the field combo starts empty until the user picks a different layer. ",[18,527,528],{},"QgsFieldProxyModel.Numeric"," covers integers and doubles; ",[18,531,532],{},"String",[18,534,535],{},"Date",[18,537,538],{},"DateTime",[18,540,541],{},"Binary"," are the other filters, and ",[18,544,545],{},"AllTypes"," is the default.",[14,548,549,550,553],{},"Reading the selection is ",[18,551,552],{},"field_combo.currentField()",", which returns the field name as a string, or an empty string when the empty entry is chosen.",[228,555,557],{"id":556},"promote-the-widgets-in-qt-designer","Promote the widgets in Qt Designer",[14,559,560,561,563],{},"A dialog built in Designer can carry these widgets directly, which keeps the layout in the ",[18,562,41],{}," file where it belongs.",[14,565,566,567,569,570,573,574,576,577,579,580,348,582,348,585,348,588,591],{},"Drop an ordinary ",[18,568,20],{}," onto the form, right-click it and choose ",[522,571,572],{},"Promote to…",", then enter ",[18,575,33],{}," as the promoted class name and ",[18,578,256],{}," as the header file. Designer will show a plain combo box; QGIS substitutes the real widget at load time. The same procedure works for ",[18,581,37],{},[18,583,584],{},"QgsFileWidget",[18,586,587],{},"QgsColorButton",[18,589,590],{},"QgsExtentGroupBox"," and the rest of the QGIS widget set.",[266,593,595],{"className":268,"code":594,"language":270,"meta":271,"style":271},"from qgis.PyQt import uic\nimport os\n\nFORM, _ = uic.loadUiType(os.path.join(os.path.dirname(__file__), \"dialog.ui\"))\n\n\nclass MyDialog(QtWidgets.QDialog, FORM):\n    def __init__(self, parent=None):\n        super().__init__(parent)\n        self.setupUi(self)\n        self.layerCombo.setFilters(QgsMapLayerProxyModel.PolygonLayer)\n        self.fieldCombo.setFilters(QgsFieldProxyModel.Numeric)\n        self.layerCombo.layerChanged.connect(self.fieldCombo.setLayer)\n        self.fieldCombo.setLayer(self.layerCombo.currentLayer())\n",[18,596,597,609,616,620,645,649,653,681,698,712,725,733,741,754],{"__ignoreMap":271},[275,598,599,601,604,606],{"class":146,"line":277},[275,600,281],{"class":280},[275,602,603],{"class":284}," qgis.PyQt ",[275,605,288],{"class":280},[275,607,608],{"class":284}," uic\n",[275,610,611,613],{"class":146,"line":294},[275,612,288],{"class":280},[275,614,615],{"class":284}," os\n",[275,617,618],{"class":146,"line":307},[275,619,311],{"emptyLinePlaceholder":310},[275,621,622,625,628,630,633,636,639,642],{"class":146,"line":314},[275,623,624],{"class":326},"FORM",[275,626,627],{"class":284},", _ ",[275,629,320],{"class":280},[275,631,632],{"class":284}," uic.loadUiType(os.path.join(os.path.dirname(",[275,634,635],{"class":326},"__file__",[275,637,638],{"class":284},"), ",[275,640,641],{"class":351},"\"dialog.ui\"",[275,643,644],{"class":284},"))\n",[275,646,647],{"class":146,"line":333},[275,648,311],{"emptyLinePlaceholder":310},[275,650,651],{"class":146,"line":339},[275,652,311],{"emptyLinePlaceholder":310},[275,654,655,658,662,665,668,671,674,676,678],{"class":146,"line":357},[275,656,657],{"class":280},"class",[275,659,661],{"class":660},"svObZ"," MyDialog",[275,663,664],{"class":284},"(",[275,666,667],{"class":660},"QtWidgets",[275,669,670],{"class":284},".",[275,672,673],{"class":660},"QDialog",[275,675,348],{"class":284},[275,677,624],{"class":326},[275,679,680],{"class":284},"):\n",[275,682,683,686,689,692,694,696],{"class":146,"line":362},[275,684,685],{"class":280},"    def",[275,687,688],{"class":326}," __init__",[275,690,691],{"class":284},"(self, parent",[275,693,320],{"class":280},[275,695,401],{"class":326},[275,697,680],{"class":284},[275,699,700,703,706,709],{"class":146,"line":505},[275,701,702],{"class":326},"        super",[275,704,705],{"class":284},"().",[275,707,708],{"class":326},"__init__",[275,710,711],{"class":284},"(parent)\n",[275,713,715,718,721,723],{"class":146,"line":714},10,[275,716,717],{"class":326},"        self",[275,719,720],{"class":284},".setupUi(",[275,722,327],{"class":326},[275,724,330],{"class":284},[275,726,728,730],{"class":146,"line":727},11,[275,729,717],{"class":326},[275,731,732],{"class":284},".layerCombo.setFilters(QgsMapLayerProxyModel.PolygonLayer)\n",[275,734,736,738],{"class":146,"line":735},12,[275,737,717],{"class":326},[275,739,740],{"class":284},".fieldCombo.setFilters(QgsFieldProxyModel.Numeric)\n",[275,742,744,746,749,751],{"class":146,"line":743},13,[275,745,717],{"class":326},[275,747,748],{"class":284},".layerCombo.layerChanged.connect(",[275,750,327],{"class":326},[275,752,753],{"class":284},".fieldCombo.setLayer)\n",[275,755,757,759,762,764],{"class":146,"line":756},14,[275,758,717],{"class":326},[275,760,761],{"class":284},".fieldCombo.setLayer(",[275,763,327],{"class":326},[275,765,766],{"class":284},".layerCombo.currentLayer())\n",[14,768,769,771,772,774,775,777,778,781,782,786],{},[239,770,375],{}," The filters are set in code rather than in Designer because Designer does not know about the flag enums — the ",[18,773,41],{}," file holds the layout and the promotion, the constructor holds the configuration. Setting them in ",[18,776,708],{}," immediately after ",[18,779,780],{},"setupUi()"," keeps them next to the widget they configure. If QGIS's own custom widgets appear in Designer's widget box, the ",[26,783,785],{"href":784},"\u002Fqgis-plugin-development\u002Fqt-designer-for-gis-interfaces\u002Fuse-qgis-custom-widgets-in-qt-designer\u002F","QGIS custom widgets"," plugin is installed and promotion is unnecessary.",[14,788,789],{},[46,790,793,796,799,801,808,811,813,817,824,827,830,834,840,843,848,853,855,858,861,865,868,872,876,879,882],{"viewBox":791,"role":49,"ariaLabel":792,"xmlns":51},"0 0 760 230","A plain combo box in Qt Designer promoted to a QGIS widget class, so the ui file records the promotion and the real widget is substituted at load time",[53,794,795],{},"Promotion substitutes the real widget at load time",[57,797,798],{},"Qt Designer shows a plain combo box because it does not have the QGIS widget available. The ui file records the promoted class name and header. When uic loads the file at runtime it imports that class from qgis.gui and instantiates it in place of the plain widget.",[61,800],{"x":63,"y":63,"width":64,"height":148,"fill":66},[68,802,803],{},[71,804,806],{"id":805,"viewBox":74,"refX":75,"refY":76,"markerWidth":77,"markerHeight":77,"orient":78},"promArrow",[80,807],{"d":82,"fill":83},[85,809,810],{"x":87,"y":88,"style":89,"fill":90,"textAnchor":91},"Designer shows a placeholder; the runtime shows the real thing",[61,812],{"x":95,"y":96,"width":204,"height":149,"rx":99,"fill":206,"stroke":207,"style":160},[85,814,816],{"x":815,"y":156,"style":107,"fill":207,"textAnchor":91},"130","in Qt Designer",[61,818],{"x":819,"y":820,"width":821,"height":822,"rx":114,"fill":100,"stroke":101,"style":823},"48","106","164","30","stroke-width:1.5",[85,825,826],{"x":815,"y":151,"style":121,"fill":101,"textAnchor":91},"QComboBox (empty)",[85,828,829],{"x":815,"y":821,"style":169,"fill":83,"textAnchor":91},"right-click → Promote to…",[85,831,833],{"x":815,"y":832,"style":169,"fill":83,"textAnchor":91},"186","class + header",[146,835],{"x1":836,"y1":837,"x2":838,"y2":837,"stroke":83,"style":839},"242","135","284","stroke-width:2;marker-end:url(#promArrow)",[61,841],{"x":842,"y":96,"width":204,"height":149,"rx":99,"fill":159,"stroke":116,"style":160},"292",[85,844,847],{"x":845,"y":156,"style":846,"fill":116,"textAnchor":91},"398","text-anchor:middle;font-size:11px;font-weight:bold;font-family:monospace","dialog.ui",[85,849,852],{"x":845,"y":850,"style":851,"fill":83,"textAnchor":91},"116","text-anchor:middle;font-size:9px;font-family:monospace","\u003Ccustomwidget>",[85,854,33],{"x":845,"y":168,"style":851,"fill":83,"textAnchor":91},[85,856,857],{"x":845,"y":113,"style":851,"fill":83,"textAnchor":91},"header: qgis.gui",[85,859,860],{"x":845,"y":832,"style":169,"fill":83,"textAnchor":91},"recorded in the file",[146,862],{"x1":863,"y1":837,"x2":864,"y2":837,"stroke":83,"style":839},"510","552",[61,866],{"x":867,"y":96,"width":215,"height":149,"rx":99,"fill":188,"stroke":189,"style":160},"560",[85,869,871],{"x":870,"y":156,"style":107,"fill":189,"textAnchor":91},"648","at runtime",[61,873],{"x":874,"y":820,"width":168,"height":822,"rx":114,"fill":875,"stroke":189,"style":823},"580","#e8efe6",[85,877,878],{"x":870,"y":151,"style":121,"fill":83,"textAnchor":91},"parcels ▾",[85,880,881],{"x":870,"y":821,"style":169,"fill":83,"textAnchor":91},"populated, filtered,",[85,883,884],{"x":870,"y":832,"style":169,"fill":83,"textAnchor":91},"watching the project",[228,886,888],{"id":887},"reacting-to-the-chosen-layer","Reacting to the chosen layer",[14,890,891],{},"Once a layer is selected, the rest of the dialog usually needs to know about it — a preview count, a range derived from the data, a warning about the CRS.",[266,893,895],{"className":268,"code":894,"language":270,"meta":271,"style":271},"def on_layer_changed(self, layer):\n    if layer is None:\n        self.summaryLabel.setText(\"\")\n        return\n\n    count = layer.featureCount()\n    crs = layer.crs()\n    parts = [f\"{count:,} feature(s)\" if count >= 0 else \"feature count unknown\"]\n    if crs.isGeographic():\n        parts.append(f\"⚠ {crs.authid()} is geographic — distances are in degrees\")\n    else:\n        parts.append(f\"{crs.authid()}, units: {crs.mapUnits()}\")\n    self.summaryLabel.setText(\" · \".join(parts))\n",[18,896,897,908,925,937,942,946,956,966,1018,1025,1047,1054,1082],{"__ignoreMap":271},[275,898,899,902,905],{"class":146,"line":277},[275,900,901],{"class":280},"def",[275,903,904],{"class":660}," on_layer_changed",[275,906,907],{"class":284},"(self, layer):\n",[275,909,910,913,916,919,922],{"class":146,"line":294},[275,911,912],{"class":280},"    if",[275,914,915],{"class":284}," layer ",[275,917,918],{"class":280},"is",[275,920,921],{"class":326}," None",[275,923,924],{"class":284},":\n",[275,926,927,929,932,935],{"class":146,"line":307},[275,928,717],{"class":326},[275,930,931],{"class":284},".summaryLabel.setText(",[275,933,934],{"class":351},"\"\"",[275,936,330],{"class":284},[275,938,939],{"class":146,"line":314},[275,940,941],{"class":280},"        return\n",[275,943,944],{"class":146,"line":333},[275,945,311],{"emptyLinePlaceholder":310},[275,947,948,951,953],{"class":146,"line":339},[275,949,950],{"class":284},"    count ",[275,952,320],{"class":280},[275,954,955],{"class":284}," layer.featureCount()\n",[275,957,958,961,963],{"class":146,"line":357},[275,959,960],{"class":284},"    crs ",[275,962,320],{"class":280},[275,964,965],{"class":284}," layer.crs()\n",[275,967,968,971,973,976,979,982,985,988,991,994,997,1000,1003,1006,1009,1012,1015],{"class":146,"line":362},[275,969,970],{"class":284},"    parts ",[275,972,320],{"class":280},[275,974,975],{"class":284}," [",[275,977,978],{"class":280},"f",[275,980,981],{"class":351},"\"",[275,983,984],{"class":326},"{",[275,986,987],{"class":284},"count",[275,989,990],{"class":280},":,",[275,992,993],{"class":326},"}",[275,995,996],{"class":351}," feature(s)\"",[275,998,999],{"class":280}," if",[275,1001,1002],{"class":284}," count ",[275,1004,1005],{"class":280},">=",[275,1007,1008],{"class":326}," 0",[275,1010,1011],{"class":280}," else",[275,1013,1014],{"class":351}," \"feature count unknown\"",[275,1016,1017],{"class":284},"]\n",[275,1019,1020,1022],{"class":146,"line":505},[275,1021,912],{"class":280},[275,1023,1024],{"class":284}," crs.isGeographic():\n",[275,1026,1027,1030,1032,1035,1037,1040,1042,1045],{"class":146,"line":714},[275,1028,1029],{"class":284},"        parts.append(",[275,1031,978],{"class":280},[275,1033,1034],{"class":351},"\"⚠ ",[275,1036,984],{"class":326},[275,1038,1039],{"class":284},"crs.authid()",[275,1041,993],{"class":326},[275,1043,1044],{"class":351}," is geographic — distances are in degrees\"",[275,1046,330],{"class":284},[275,1048,1049,1052],{"class":146,"line":727},[275,1050,1051],{"class":280},"    else",[275,1053,924],{"class":284},[275,1055,1056,1058,1060,1062,1064,1066,1068,1071,1073,1076,1078,1080],{"class":146,"line":735},[275,1057,1029],{"class":284},[275,1059,978],{"class":280},[275,1061,981],{"class":351},[275,1063,984],{"class":326},[275,1065,1039],{"class":284},[275,1067,993],{"class":326},[275,1069,1070],{"class":351},", units: ",[275,1072,984],{"class":326},[275,1074,1075],{"class":284},"crs.mapUnits()",[275,1077,993],{"class":326},[275,1079,981],{"class":351},[275,1081,330],{"class":284},[275,1083,1084,1087,1089,1092],{"class":146,"line":743},[275,1085,1086],{"class":326},"    self",[275,1088,931],{"class":284},[275,1090,1091],{"class":351},"\" · \"",[275,1093,1094],{"class":284},".join(parts))\n",[14,1096,1097,376,1099,1102,1103,1106,1107,1111],{},[239,1098,375],{},[18,1100,1101],{},"featureCount()"," returns ",[18,1104,1105],{},"-1"," when the provider cannot answer cheaply — a large PostGIS table or a WFS layer — so a bare format string produces \"-1 features\" and looks like a bug. Warning about a geographic CRS here, rather than after the user has run the operation, is the single most useful thing a buffer dialog can do; the same check appears in ",[26,1108,1110],{"href":1109},"\u002Fspatial-data-processing-automation\u002Fcoordinate-reference-systems\u002Fset-project-crs-and-datum-transform-pyqgis\u002F","setting the project CRS"," because it causes trouble everywhere.",[14,1113,1114,1115,1117,1118,1120],{},"The signal to connect is ",[18,1116,184],{},", which emits the layer object, so this slot needs no lookup. Connecting it alongside the field combo's ",[18,1119,519],{}," means both happen from one user action with no ordering to manage.",[228,1122,1124],{"id":1123},"restore-and-remember-a-selection","Restore and remember a selection",[14,1126,1127],{},"A dialog that reopens with the user's last choice is noticeably better, and the widget makes it two lines.",[266,1129,1131],{"className":268,"code":1130,"language":270,"meta":271,"style":271},"from qgis.core import QgsProject, QgsSettings\n\n\ndef restore(self):\n    settings = QgsSettings()\n    layer_id = settings.value(\"my_plugin\u002Flast_layer\", \"\")\n    layer = QgsProject.instance().mapLayer(layer_id)\n    if layer is not None:\n        self.layerCombo.setLayer(layer)\n        self.fieldCombo.setField(settings.value(\"my_plugin\u002Flast_field\", \"\"))\n\n\ndef remember(self):\n    settings = QgsSettings()\n    layer = self.layerCombo.currentLayer()\n    settings.setValue(\"my_plugin\u002Flast_layer\", layer.id() if layer else \"\")\n    settings.setValue(\"my_plugin\u002Flast_field\", self.fieldCombo.currentField())\n",[18,1132,1133,1144,1148,1152,1162,1172,1191,1201,1216,1223,1239,1243,1247,1256,1264,1277,1301],{"__ignoreMap":271},[275,1134,1135,1137,1139,1141],{"class":146,"line":277},[275,1136,281],{"class":280},[275,1138,299],{"class":284},[275,1140,288],{"class":280},[275,1142,1143],{"class":284}," QgsProject, QgsSettings\n",[275,1145,1146],{"class":146,"line":294},[275,1147,311],{"emptyLinePlaceholder":310},[275,1149,1150],{"class":146,"line":307},[275,1151,311],{"emptyLinePlaceholder":310},[275,1153,1154,1156,1159],{"class":146,"line":314},[275,1155,901],{"class":280},[275,1157,1158],{"class":660}," restore",[275,1160,1161],{"class":284},"(self):\n",[275,1163,1164,1167,1169],{"class":146,"line":333},[275,1165,1166],{"class":284},"    settings ",[275,1168,320],{"class":280},[275,1170,1171],{"class":284}," QgsSettings()\n",[275,1173,1174,1177,1179,1182,1185,1187,1189],{"class":146,"line":339},[275,1175,1176],{"class":284},"    layer_id ",[275,1178,320],{"class":280},[275,1180,1181],{"class":284}," settings.value(",[275,1183,1184],{"class":351},"\"my_plugin\u002Flast_layer\"",[275,1186,348],{"class":284},[275,1188,934],{"class":351},[275,1190,330],{"class":284},[275,1192,1193,1196,1198],{"class":146,"line":357},[275,1194,1195],{"class":284},"    layer ",[275,1197,320],{"class":280},[275,1199,1200],{"class":284}," QgsProject.instance().mapLayer(layer_id)\n",[275,1202,1203,1205,1207,1209,1212,1214],{"class":146,"line":362},[275,1204,912],{"class":280},[275,1206,915],{"class":284},[275,1208,918],{"class":280},[275,1210,1211],{"class":280}," not",[275,1213,921],{"class":326},[275,1215,924],{"class":284},[275,1217,1218,1220],{"class":146,"line":505},[275,1219,717],{"class":326},[275,1221,1222],{"class":284},".layerCombo.setLayer(layer)\n",[275,1224,1225,1227,1230,1233,1235,1237],{"class":146,"line":714},[275,1226,717],{"class":326},[275,1228,1229],{"class":284},".fieldCombo.setField(settings.value(",[275,1231,1232],{"class":351},"\"my_plugin\u002Flast_field\"",[275,1234,348],{"class":284},[275,1236,934],{"class":351},[275,1238,644],{"class":284},[275,1240,1241],{"class":146,"line":727},[275,1242,311],{"emptyLinePlaceholder":310},[275,1244,1245],{"class":146,"line":735},[275,1246,311],{"emptyLinePlaceholder":310},[275,1248,1249,1251,1254],{"class":146,"line":743},[275,1250,901],{"class":280},[275,1252,1253],{"class":660}," remember",[275,1255,1161],{"class":284},[275,1257,1258,1260,1262],{"class":146,"line":756},[275,1259,1166],{"class":284},[275,1261,320],{"class":280},[275,1263,1171],{"class":284},[275,1265,1267,1269,1271,1274],{"class":146,"line":1266},15,[275,1268,1195],{"class":284},[275,1270,320],{"class":280},[275,1272,1273],{"class":326}," self",[275,1275,1276],{"class":284},".layerCombo.currentLayer()\n",[275,1278,1280,1283,1285,1288,1291,1293,1296,1299],{"class":146,"line":1279},16,[275,1281,1282],{"class":284},"    settings.setValue(",[275,1284,1184],{"class":351},[275,1286,1287],{"class":284},", layer.id() ",[275,1289,1290],{"class":280},"if",[275,1292,915],{"class":284},[275,1294,1295],{"class":280},"else",[275,1297,1298],{"class":351}," \"\"",[275,1300,330],{"class":284},[275,1302,1304,1306,1308,1310,1312],{"class":146,"line":1303},17,[275,1305,1282],{"class":284},[275,1307,1232],{"class":351},[275,1309,348],{"class":284},[275,1311,327],{"class":326},[275,1313,1314],{"class":284},".fieldCombo.currentField())\n",[14,1316,1317,1319,1320,1323,1324,1327,1328,1330,1331,1334,1335,670],{},[239,1318,375],{}," Storing the layer ",[522,1321,1322],{},"id"," rather than the name is what makes this correct when two layers share a name, and ",[18,1325,1326],{},"mapLayer(id)"," returning ",[18,1329,401],{}," handles the case where the remembered layer is no longer in the project — which is why the guard is not optional. ",[18,1332,1333],{},"setLayer()"," on a layer that the filter excludes silently does nothing, so a remembered raster does not appear in a polygon-filtered combo. Storing under a plugin-prefixed key follows the convention described in ",[26,1336,1338],{"href":1337},"\u002Fqgis-plugin-development\u002Fplugin-settings-and-localization\u002Fstore-plugin-settings-with-qgssettings\u002F","storing plugin settings with QgsSettings",[228,1340,1342],{"id":1341},"enable-the-button-only-when-the-choice-is-valid","Enable the button only when the choice is valid",[14,1344,1345],{},"The widgets emit signals on every change, which is exactly what a validation gate needs.",[266,1347,1349],{"className":268,"code":1348,"language":270,"meta":271,"style":271},"def wire_validation(self):\n    for signal in (self.layerCombo.layerChanged, self.fieldCombo.fieldChanged):\n        signal.connect(self.update_ok_state)\n    self.update_ok_state()\n\n\ndef update_ok_state(self, *_):\n    layer = self.layerCombo.currentLayer()\n    field = self.fieldCombo.currentField()\n    ok = layer is not None and bool(field) and layer.featureCount() > 0\n    self.buttonBox.button(QDialogButtonBox.Ok).setEnabled(ok)\n",[18,1350,1351,1360,1384,1394,1401,1405,1409,1425,1435,1447,1483],{"__ignoreMap":271},[275,1352,1353,1355,1358],{"class":146,"line":277},[275,1354,901],{"class":280},[275,1356,1357],{"class":660}," wire_validation",[275,1359,1161],{"class":284},[275,1361,1362,1365,1368,1371,1374,1376,1379,1381],{"class":146,"line":294},[275,1363,1364],{"class":280},"    for",[275,1366,1367],{"class":284}," signal ",[275,1369,1370],{"class":280},"in",[275,1372,1373],{"class":284}," (",[275,1375,327],{"class":326},[275,1377,1378],{"class":284},".layerCombo.layerChanged, ",[275,1380,327],{"class":326},[275,1382,1383],{"class":284},".fieldCombo.fieldChanged):\n",[275,1385,1386,1389,1391],{"class":146,"line":307},[275,1387,1388],{"class":284},"        signal.connect(",[275,1390,327],{"class":326},[275,1392,1393],{"class":284},".update_ok_state)\n",[275,1395,1396,1398],{"class":146,"line":314},[275,1397,1086],{"class":326},[275,1399,1400],{"class":284},".update_ok_state()\n",[275,1402,1403],{"class":146,"line":333},[275,1404,311],{"emptyLinePlaceholder":310},[275,1406,1407],{"class":146,"line":339},[275,1408,311],{"emptyLinePlaceholder":310},[275,1410,1411,1413,1416,1419,1422],{"class":146,"line":357},[275,1412,901],{"class":280},[275,1414,1415],{"class":660}," update_ok_state",[275,1417,1418],{"class":284},"(self, ",[275,1420,1421],{"class":280},"*",[275,1423,1424],{"class":284},"_):\n",[275,1426,1427,1429,1431,1433],{"class":146,"line":362},[275,1428,1195],{"class":284},[275,1430,320],{"class":280},[275,1432,1273],{"class":326},[275,1434,1276],{"class":284},[275,1436,1437,1440,1442,1444],{"class":146,"line":505},[275,1438,1439],{"class":284},"    field ",[275,1441,320],{"class":280},[275,1443,1273],{"class":326},[275,1445,1446],{"class":284},".fieldCombo.currentField()\n",[275,1448,1449,1452,1454,1456,1458,1460,1462,1465,1468,1471,1474,1477,1480],{"class":146,"line":714},[275,1450,1451],{"class":284},"    ok ",[275,1453,320],{"class":280},[275,1455,915],{"class":284},[275,1457,918],{"class":280},[275,1459,1211],{"class":280},[275,1461,921],{"class":326},[275,1463,1464],{"class":280}," and",[275,1466,1467],{"class":326}," bool",[275,1469,1470],{"class":284},"(field) ",[275,1472,1473],{"class":280},"and",[275,1475,1476],{"class":284}," layer.featureCount() ",[275,1478,1479],{"class":280},">",[275,1481,1482],{"class":326}," 0\n",[275,1484,1485,1487],{"class":146,"line":727},[275,1486,1086],{"class":326},[275,1488,1489],{"class":284},".buttonBox.button(QDialogButtonBox.Ok).setEnabled(ok)\n",[14,1491,1492,1494,1495,1498,1499,1502,1503,1505,1506,1509,1510,1513],{},[239,1493,375],{}," Accepting ",[18,1496,1497],{},"*_"," lets one slot serve signals with different argument counts, which avoids two nearly identical methods. Checking ",[18,1500,1501],{},"featureCount() > 0"," catches the empty-layer case that otherwise produces a confusing \"no results\" later — although note it returns ",[18,1504,1105],{}," for some providers when the count is unknown, so a strict ",[18,1507,1508],{},"> 0"," also disables the button for those and ",[18,1511,1512],{},"!= 0"," is safer. Calling the update once at the end sets the initial state, since no signal has fired yet.",[228,1515,1517],{"id":1516},"qgis-version-compatibility","QGIS version compatibility",[14,1519,1520,1521,1523],{},"The examples target ",[239,1522,241],{}," (Python 3.12).",[1525,1526,1527,1543],"table",{},[1528,1529,1530],"thead",{},[1531,1532,1533,1537,1540],"tr",{},[1534,1535,1536],"th",{},"QGIS version",[1534,1538,1539],{},"Python",[1534,1541,1542],{},"Notes",[1544,1545,1546,1562,1576,1589,1600],"tbody",{},[1531,1547,1548,1552,1555],{},[1549,1550,1551],"td",{},"3.16 LTR",[1549,1553,1554],{},"3.7",[1549,1556,1557,348,1559,1561],{},[18,1558,33],{},[18,1560,37],{}," and the proxy model filters present.",[1531,1563,1564,1567,1570],{},[1549,1565,1566],{},"3.22 LTR",[1549,1568,1569],{},"3.9",[1549,1571,1572,1575],{},[18,1573,1574],{},"setAllowEmptyLayer"," gains the placeholder text argument.",[1531,1577,1578,1581,1583],{},[1549,1579,1580],{},"3.28 LTR",[1549,1582,1569],{},[1549,1584,1585,1588],{},[18,1586,1587],{},"QgsMapLayerProxyModel"," filter flags unchanged.",[1531,1590,1591,1594,1597],{},[1549,1592,1593],{},"3.34 LTR",[1549,1595,1596],{},"3.12",[1549,1598,1599],{},"Baseline for this page.",[1531,1601,1602,1605,1607],{},[1549,1603,1604],{},"3.36+",[1549,1606,1596],{},[1549,1608,1609],{},"Filter enums gain a scoped form; the flat names remain as aliases.",[228,1611,1613],{"id":1612},"troubleshooting","Troubleshooting",[233,1615,1616,1622,1630,1636,1644,1653],{},[236,1617,1618,1621],{},[239,1619,1620],{},"The combo box is empty."," The filter excludes every layer in the project. Check the flags against the actual layer types.",[236,1623,1624,376,1627,1629],{},[239,1625,1626],{},"The field combo stays empty until the layer changes.",[18,1628,1333],{}," was not called once after wiring the signal.",[236,1631,1632,1635],{},[239,1633,1634],{},"A remembered layer is not restored."," It was stored by name, or the filter excludes it. Store the id and check the filter.",[236,1637,1638,1641,1642,670],{},[239,1639,1640],{},"The widget shows in Designer but not at runtime."," The promotion header is wrong; it must be ",[18,1643,256],{},[236,1645,1646,1649,1650,1652],{},[239,1647,1648],{},"The dialog offers its own output layer as an input."," Add it to ",[18,1651,424],{}," after creating it.",[236,1654,1655,1660,1661,1663],{},[239,1656,1657,1659],{},[18,1658,397],{}," returns None unexpectedly."," The empty entry is selected. That is what ",[18,1662,1574],{}," is for.",[228,1665,1667],{"id":1666},"conclusion","Conclusion",[14,1669,1670,1671,1673,1674,1676,1677,1679,1680,1682,1683,1685],{},"Use ",[18,1672,33],{}," with an explicit filter and an empty entry, link ",[18,1675,37],{}," through ",[18,1678,184],{}," and call ",[18,1681,519],{}," once afterwards, promote both in Designer with the ",[18,1684,256],{}," header, and remember the selection by layer id. The widgets handle project changes on their own, which is most of the code you would otherwise write and all of the bugs.",[228,1687,1689],{"id":1688},"frequently-asked-questions","Frequently Asked Questions",[14,1691,1692,1695,1696,1698,1699,1701,1702,1705,1706,348,1708,348,1711,417,1714,1717],{},[239,1693,1694],{},"Are there similar widgets for other inputs?","\nMany: ",[18,1697,584],{}," for paths, ",[18,1700,590],{}," for extents, ",[18,1703,1704],{},"QgsProjectionSelectionWidget"," for a CRS, ",[18,1707,587],{},[18,1709,1710],{},"QgsSpinBox",[18,1712,1713],{},"QgsDoubleSpinBox",[18,1715,1716],{},"QgsExpressionLineEdit",". All promote the same way.",[14,1719,1720,1723,1724,1726],{},[239,1721,1722],{},"Can I filter to layers with a particular field?","\nNot through the proxy model. Set ",[18,1725,424],{}," from a check you run yourself, and refresh it when the project changes.",[14,1728,1729,1732,1733,1736],{},[239,1730,1731],{},"Does the combo box include layers not in the layer tree?","\nIt follows the project's layer registry, so a layer added with ",[18,1734,1735],{},"addMapLayer(layer, False)"," appears even though it is not in the tree. Exclude it explicitly if that is wrong for your dialog.",[14,1738,1739,1742,1743,1746,1747,670],{},[239,1740,1741],{},"How do I make a processing algorithm's dialog do this?","\nIt already does — declaring a ",[18,1744,1745],{},"QgsProcessingParameterFeatureSource"," produces exactly these widgets automatically. See ",[26,1748,1750],{"href":1749},"\u002Fqgis-plugin-development\u002Fprocessing-provider-plugins\u002Fadd-parameters-to-processing-algorithm-pyqgis\u002F","adding parameters to a processing algorithm",[228,1752,1754],{"id":1753},"related","Related",[233,1756,1757,1762,1767,1772,1778],{},[236,1758,1759,1761],{},[26,1760,29],{"href":28}," — the guide this recipe belongs to",[236,1763,1764],{},[26,1765,1766],{"href":248},"Load a .ui File at Runtime in PyQGIS",[236,1768,1769],{},[26,1770,1771],{"href":784},"Use QGIS Custom Widgets in Qt Designer",[236,1773,1774],{},[26,1775,1777],{"href":1776},"\u002Fqgis-plugin-development\u002Fqt-designer-for-gis-interfaces\u002Fvalidate-plugin-dialog-input-pyqgis\u002F","Validate Plugin Dialog Input in PyQGIS",[236,1779,1780],{},[26,1781,1782],{"href":1337},"Store Plugin Settings with QgsSettings",[1784,1785,1786],"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 .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);}html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}",{"title":271,"searchDepth":294,"depth":294,"links":1788},[1789,1790,1791,1792,1793,1794,1795,1796,1797,1798,1799,1800],{"id":230,"depth":294,"text":231},{"id":260,"depth":294,"text":261},{"id":428,"depth":294,"text":429},{"id":556,"depth":294,"text":557},{"id":887,"depth":294,"text":888},{"id":1123,"depth":294,"text":1124},{"id":1341,"depth":294,"text":1342},{"id":1516,"depth":294,"text":1517},{"id":1612,"depth":294,"text":1613},{"id":1666,"depth":294,"text":1667},{"id":1688,"depth":294,"text":1689},{"id":1753,"depth":294,"text":1754},"Let users pick layers and fields properly — QgsMapLayerComboBox filters, QgsFieldComboBox linkage, promoting widgets in Qt Designer, and keeping the selection in step with the project.","md",{"slug":1804,"type":1805,"breadcrumb":1806,"datePublished":1807,"dateModified":1807},"use-qgsmaplayercombobox-in-plugin-dialog-pyqgis","article","Layer & Field Combo Boxes","2026-08-27","\u002Fqgis-plugin-development\u002Fqt-designer-for-gis-interfaces\u002Fuse-qgsmaplayercombobox-in-plugin-dialog-pyqgis",{"title":5,"description":1801},"qgis-plugin-development\u002Fqt-designer-for-gis-interfaces\u002Fuse-qgsmaplayercombobox-in-plugin-dialog-pyqgis\u002Findex","ptLRlRbdWRadUGEoz9NvQUkY4XiM3sf4nf7FYVSz1ko",1787823363161]