[{"data":1,"prerenderedAt":1246},["ShallowReactive",2],{"doc:\u002Fqgis-plugin-development\u002Fqt-designer-for-gis-interfaces\u002Fshow-messages-with-qgsmessagebar-pyqgis":3},{"id":4,"title":5,"body":6,"description":1235,"extension":1236,"meta":1237,"navigation":228,"path":1242,"seo":1243,"stem":1244,"__hash__":1245},"docs\u002Fqgis-plugin-development\u002Fqt-designer-for-gis-interfaces\u002Fshow-messages-with-qgsmessagebar-pyqgis\u002Findex.md","Show Messages with QgsMessageBar in PyQGIS",{"type":7,"value":8,"toc":1221},"minimark",[9,13,17,26,162,167,191,195,303,331,338,342,345,439,448,455,459,547,765,777,788,792,795,897,909,913,916,922,928,934,1019,1029,1033,1036,1039,1043,1077,1081,1131,1135,1138,1142,1148,1164,1177,1183,1187,1217],[10,11,5],"h1",{"id":12},"show-messages-with-qgsmessagebar-in-pyqgis",[14,15,16],"p",{},"The message bar is the strip that appears at the top of the map canvas, and it is the right place for almost everything a plugin needs to tell a user. It does not steal focus, it does not have to be dismissed, it stacks, and it can carry a button. A modal dialog does none of that, and a plugin that uses one for every notification is a plugin people stop using.",[14,18,19,20,25],{},"This recipe belongs to ",[21,22,24],"a",{"href":23},"\u002Fqgis-plugin-development\u002Fqt-designer-for-gis-interfaces\u002F","Qt Designer for GIS Interfaces",". It covers the levels and durations, adding a button or a whole widget, showing progress, logging alongside, and the cases where a dialog really is the right answer.",[14,27,28],{},[29,30,35,39,43,50,59,69,75,81,87,91,96,100,103,106,110,115,119,122,125,129,134,138,141,144,148,157],"svg",{"viewBox":31,"role":32,"ariaLabel":33,"xmlns":34},"0 0 760 320","img","The four message levels and their conventional use: info, success, warning and critical, with duration guidance for each","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg",[36,37,38],"title",{},"Four levels, four meanings",[40,41,42],"desc",{},"Info carries neutral progress notes and should disappear quickly. Success confirms a completed action and can be brief. Warning marks something the user should know but need not act on. Critical marks a failure and should stay until dismissed so it is not missed.",[44,45],"rect",{"x":46,"y":46,"width":47,"height":48,"fill":49},"0","760","320","#f6f3ea",[51,52,58],"text",{"x":53,"y":54,"style":55,"fill":56,"textAnchor":57},"380","26","text-anchor:middle;font-size:14px;font-weight:bold;font-family:sans-serif","#17211d","middle","Level sets the colour; duration sets whether it is missed",[44,60],{"x":61,"y":62,"width":63,"height":64,"rx":65,"fill":66,"stroke":67,"style":68},"24","46","712","52","8","#eff3ff","#2563eb","stroke-width:2.5",[51,70,74],{"x":71,"y":72,"style":73,"fill":67},"48","70","font-size:11px;font-weight:bold;font-family:sans-serif","Info",[51,76,80],{"x":77,"y":72,"style":78,"fill":79},"140","font-size:10.5px;font-family:sans-serif","#2f3b35","\"Loaded 412 features from roads.gpkg\"",[51,82,86],{"x":83,"y":72,"style":84,"fill":85},"560","font-size:10px;font-family:sans-serif","#59645f","duration 4–5 s",[51,88,90],{"x":77,"y":89,"style":84,"fill":85},"88","neutral, disposable",[44,92],{"x":61,"y":93,"width":63,"height":64,"rx":65,"fill":94,"stroke":95,"style":68},"108","#edf8e9","#15803d",[51,97,99],{"x":71,"y":98,"style":73,"fill":95},"132","Success",[51,101,102],{"x":77,"y":98,"style":78,"fill":79},"\"Export complete — 12 sheets written\"",[51,104,105],{"x":83,"y":98,"style":84,"fill":85},"duration 5–8 s",[51,107,109],{"x":77,"y":108,"style":84,"fill":85},"150","confirms the action finished",[44,111],{"x":61,"y":112,"width":63,"height":64,"rx":65,"fill":113,"stroke":114,"style":68},"170","#fdf2e2","#b45309",[51,116,118],{"x":71,"y":117,"style":73,"fill":114},"194","Warning",[51,120,121],{"x":77,"y":117,"style":78,"fill":79},"\"3 features skipped: invalid geometry\"",[51,123,124],{"x":83,"y":117,"style":84,"fill":85},"duration 10 s or 0",[51,126,128],{"x":77,"y":127,"style":84,"fill":85},"212","worth reading, not blocking",[44,130],{"x":61,"y":131,"width":63,"height":64,"rx":65,"fill":132,"stroke":133,"style":68},"232","#fffdf7","#b91c1c",[51,135,137],{"x":71,"y":136,"style":73,"fill":133},"256","Critical",[51,139,140],{"x":77,"y":136,"style":78,"fill":79},"\"Could not write to \u002Fdata\u002Foutput — read only\"",[51,142,143],{"x":83,"y":136,"style":84,"fill":85},"duration 0",[51,145,147],{"x":77,"y":146,"style":84,"fill":85},"274","stays until dismissed",[44,149],{"x":150,"y":151,"width":152,"height":153,"rx":154,"fill":155,"stroke":85,"style":156},"180","296","400","20","5","#efeadd","stroke-width:1.6",[51,158,161],{"x":53,"y":159,"style":160,"fill":79,"textAnchor":57},"311","text-anchor:middle;font-size:9.5px;font-family:sans-serif","duration 0 means the user must dismiss it",[163,164,166],"h2",{"id":165},"prerequisites","Prerequisites",[168,169,170,178],"ul",{},[171,172,173,177],"li",{},[174,175,176],"strong",{},"QGIS 3.34 LTR"," or newer with a GUI.",[171,179,180,181,185,186,190],{},"Access to ",[182,183,184],"code",{},"iface",", as covered in ",[21,187,189],{"href":188},"\u002Fpyqgis-fundamentals-environment-setup\u002Fqgis-python-console-basics\u002Fuse-iface-to-control-qgis-interface-pyqgis\u002F","using iface to control the QGIS interface",".",[163,192,194],{"id":193},"the-basic-call","The basic call",[196,197,202],"pre",{"className":198,"code":199,"language":200,"meta":201,"style":201},"language-python shiki shiki-themes github-dark","from qgis.core import Qgis\n\nbar = iface.messageBar()\n\nbar.pushMessage(\n    \"Export complete\",\n    \"12 sheets written to \u002Fdata\u002Foutput\",\n    level=Qgis.MessageLevel.Success,\n    duration=6,\n)\n","python","",[182,203,204,223,230,242,247,253,263,271,283,297],{"__ignoreMap":201},[205,206,209,213,217,220],"span",{"class":207,"line":208},"line",1,[205,210,212],{"class":211},"snl16","from",[205,214,216],{"class":215},"s95oV"," qgis.core ",[205,218,219],{"class":211},"import",[205,221,222],{"class":215}," Qgis\n",[205,224,226],{"class":207,"line":225},2,[205,227,229],{"emptyLinePlaceholder":228},true,"\n",[205,231,233,236,239],{"class":207,"line":232},3,[205,234,235],{"class":215},"bar ",[205,237,238],{"class":211},"=",[205,240,241],{"class":215}," iface.messageBar()\n",[205,243,245],{"class":207,"line":244},4,[205,246,229],{"emptyLinePlaceholder":228},[205,248,250],{"class":207,"line":249},5,[205,251,252],{"class":215},"bar.pushMessage(\n",[205,254,256,260],{"class":207,"line":255},6,[205,257,259],{"class":258},"sU2Wk","    \"Export complete\"",[205,261,262],{"class":215},",\n",[205,264,266,269],{"class":207,"line":265},7,[205,267,268],{"class":258},"    \"12 sheets written to \u002Fdata\u002Foutput\"",[205,270,262],{"class":215},[205,272,274,278,280],{"class":207,"line":273},8,[205,275,277],{"class":276},"s9osk","    level",[205,279,238],{"class":211},[205,281,282],{"class":215},"Qgis.MessageLevel.Success,\n",[205,284,286,289,291,295],{"class":207,"line":285},9,[205,287,288],{"class":276},"    duration",[205,290,238],{"class":211},[205,292,294],{"class":293},"sDLfK","6",[205,296,262],{"class":215},[205,298,300],{"class":207,"line":299},10,[205,301,302],{"class":215},")\n",[14,304,305,308,309,312,313,315,316,319,320,319,323,326,327,330],{},[174,306,307],{},"Breakdown:"," The two strings are a title and a body; the title is rendered bold and should be short enough to read at a glance, with the detail in the body. ",[182,310,311],{},"duration"," is in seconds and ",[182,314,46],{}," means the message stays until the user closes it — reserve that for critical messages, because a bar that accumulates undismissed notices covers the map. The convenience methods ",[182,317,318],{},"pushInfo",", ",[182,321,322],{},"pushSuccess",[182,324,325],{},"pushWarning"," and ",[182,328,329],{},"pushCritical"," take just the two strings and pick sensible defaults, and they are what most code should use.",[14,332,333,334,337],{},"On QGIS 3.28 and earlier the level constants are ",[182,335,336],{},"Qgis.Success"," and friends without the scoped enum; both spellings work on current releases.",[163,339,341],{"id":340},"adding-a-button","Adding a button",[14,343,344],{},"The message bar's real advantage over a status message is that it can offer an action.",[196,346,348],{"className":198,"code":347,"language":200,"meta":201,"style":201},"from qgis.PyQt.QtWidgets import QPushButton\n\nwidget = bar.createMessage(\"Export complete\", \"12 sheets written\")\n\nbutton = QPushButton(\"Open folder\")\nbutton.pressed.connect(lambda: open_output_folder())\nwidget.layout().addWidget(button)\n\nbar.pushWidget(widget, Qgis.MessageLevel.Success, duration=10)\n",[182,349,350,362,366,386,390,405,416,421,425],{"__ignoreMap":201},[205,351,352,354,357,359],{"class":207,"line":208},[205,353,212],{"class":211},[205,355,356],{"class":215}," qgis.PyQt.QtWidgets ",[205,358,219],{"class":211},[205,360,361],{"class":215}," QPushButton\n",[205,363,364],{"class":207,"line":225},[205,365,229],{"emptyLinePlaceholder":228},[205,367,368,371,373,376,379,381,384],{"class":207,"line":232},[205,369,370],{"class":215},"widget ",[205,372,238],{"class":211},[205,374,375],{"class":215}," bar.createMessage(",[205,377,378],{"class":258},"\"Export complete\"",[205,380,319],{"class":215},[205,382,383],{"class":258},"\"12 sheets written\"",[205,385,302],{"class":215},[205,387,388],{"class":207,"line":244},[205,389,229],{"emptyLinePlaceholder":228},[205,391,392,395,397,400,403],{"class":207,"line":249},[205,393,394],{"class":215},"button ",[205,396,238],{"class":211},[205,398,399],{"class":215}," QPushButton(",[205,401,402],{"class":258},"\"Open folder\"",[205,404,302],{"class":215},[205,406,407,410,413],{"class":207,"line":255},[205,408,409],{"class":215},"button.pressed.connect(",[205,411,412],{"class":211},"lambda",[205,414,415],{"class":215},": open_output_folder())\n",[205,417,418],{"class":207,"line":265},[205,419,420],{"class":215},"widget.layout().addWidget(button)\n",[205,422,423],{"class":207,"line":273},[205,424,229],{"emptyLinePlaceholder":228},[205,426,427,430,432,434,437],{"class":207,"line":285},[205,428,429],{"class":215},"bar.pushWidget(widget, Qgis.MessageLevel.Success, ",[205,431,311],{"class":276},[205,433,238],{"class":211},[205,435,436],{"class":293},"10",[205,438,302],{"class":215},[14,440,441,443,444,447],{},[174,442,307],{}," ",[182,445,446],{},"createMessage"," builds the bar item without showing it, so you can add widgets before pushing. Any Qt widget can go into that layout — a button, a combo box, a small progress bar — which is how the built-in \"layer added, zoom to it?\" style prompts work. Keeping the button's action short matters: the bar disappears after its duration, taking the button with it, so an action nobody performs in ten seconds is better placed in a dialog or a panel.",[14,449,450,451,454],{},"Note the closure: a lambda connected here captures whatever it references, and if it references ",[182,452,453],{},"self"," on a plugin that is later unloaded, the widget outlives the plugin. Connecting to a bound method of an object you keep is safer than capturing arbitrary state.",[163,456,458],{"id":457},"progress-in-the-bar","Progress in the bar",[14,460,461],{},[29,462,465,468,471,474,488,491,493,497,505,508,511,516,519,523,526,529,533,535,542],{"viewBox":463,"role":32,"ariaLabel":464,"xmlns":34},"0 0 760 300","A progress message in the bar during a long task, replaced by a success or failure message when the task ends",[36,466,467],{},"One message, three states",[40,469,470],{},"A long task pushes a message carrying a progress bar and no timeout. As the task runs the bar advances. When the task finishes the message is cleared and replaced with a success or failure notice that does time out.",[44,472],{"x":46,"y":46,"width":47,"height":473,"fill":49},"300",[475,476,477],"defs",{},[478,479,484],"marker",{"id":480,"viewBox":481,"refX":65,"refY":154,"markerWidth":482,"markerHeight":482,"orient":483},"mbProgArrow","0 0 10 10","7","auto-start-reverse",[485,486],"path",{"d":487,"fill":79},"M0 0 L10 5 L0 10 z",[51,489,490],{"x":53,"y":54,"style":55,"fill":56,"textAnchor":57},"Replace the progress message; never leave it behind",[44,492],{"x":61,"y":64,"width":63,"height":62,"rx":65,"fill":66,"stroke":67,"style":68},[51,494,496],{"x":71,"y":495,"style":78,"fill":67},"80","Exporting…",[44,498],{"x":150,"y":499,"width":500,"height":501,"rx":502,"fill":503,"stroke":67,"style":504},"66","420","16","4","#dbeafe","stroke-width:1.4",[44,506],{"x":150,"y":499,"width":507,"height":501,"rx":502,"fill":67},"168",[51,509,143],{"x":510,"y":495,"style":84,"fill":85},"628",[485,512],{"d":513,"fill":514,"stroke":79,"style":515},"M380 98 L380 128","none","stroke-width:2;marker-end:url(#mbProgArrow)",[44,517],{"x":61,"y":518,"width":63,"height":62,"rx":65,"fill":94,"stroke":95,"style":68},"136",[51,520,522],{"x":71,"y":521,"style":78,"fill":95},"164","Export complete — 12 sheets written",[51,524,525],{"x":510,"y":521,"style":84,"fill":85},"duration 6",[44,527],{"x":61,"y":528,"width":63,"height":62,"rx":65,"fill":132,"stroke":133,"style":68},"200",[51,530,532],{"x":71,"y":531,"style":78,"fill":133},"228","Export failed — see the log for details",[51,534,143],{"x":510,"y":531,"style":84,"fill":85},[44,536],{"x":537,"y":538,"width":539,"height":540,"rx":294,"fill":113,"stroke":114,"style":541},"120","262","520","28","stroke-width:1.8",[51,543,546],{"x":53,"y":544,"style":545,"fill":79,"textAnchor":57},"281","text-anchor:middle;font-size:10.5px;font-family:sans-serif","clearWidgets() before pushing the outcome, or both are visible at once",[196,548,550],{"className":198,"code":549,"language":200,"meta":201,"style":201},"from qgis.PyQt.QtWidgets import QProgressBar\nfrom qgis.PyQt.QtCore import Qt\n\n\nclass Exporter:\n    def start(self, total):\n        self.widget = iface.messageBar().createMessage(\"Exporting…\")\n        self.progress = QProgressBar()\n        self.progress.setMaximum(total)\n        self.progress.setAlignment(Qt.AlignLeft | Qt.AlignVCenter)\n        self.widget.layout().addWidget(self.progress)\n        iface.messageBar().pushWidget(self.widget, Qgis.MessageLevel.Info)\n\n    def advance(self, value):\n        self.progress.setValue(value)\n\n    def finish(self, ok, detail):\n        iface.messageBar().clearWidgets()\n        if ok:\n            iface.messageBar().pushSuccess(\"Export complete\", detail)\n        else:\n            iface.messageBar().pushCritical(\"Export failed\", detail)\n",[182,551,552,563,575,579,583,595,606,624,636,643,656,669,680,685,696,704,709,720,726,735,746,754],{"__ignoreMap":201},[205,553,554,556,558,560],{"class":207,"line":208},[205,555,212],{"class":211},[205,557,356],{"class":215},[205,559,219],{"class":211},[205,561,562],{"class":215}," QProgressBar\n",[205,564,565,567,570,572],{"class":207,"line":225},[205,566,212],{"class":211},[205,568,569],{"class":215}," qgis.PyQt.QtCore ",[205,571,219],{"class":211},[205,573,574],{"class":215}," Qt\n",[205,576,577],{"class":207,"line":232},[205,578,229],{"emptyLinePlaceholder":228},[205,580,581],{"class":207,"line":244},[205,582,229],{"emptyLinePlaceholder":228},[205,584,585,588,592],{"class":207,"line":249},[205,586,587],{"class":211},"class",[205,589,591],{"class":590},"svObZ"," Exporter",[205,593,594],{"class":215},":\n",[205,596,597,600,603],{"class":207,"line":255},[205,598,599],{"class":211},"    def",[205,601,602],{"class":590}," start",[205,604,605],{"class":215},"(self, total):\n",[205,607,608,611,614,616,619,622],{"class":207,"line":265},[205,609,610],{"class":293},"        self",[205,612,613],{"class":215},".widget ",[205,615,238],{"class":211},[205,617,618],{"class":215}," iface.messageBar().createMessage(",[205,620,621],{"class":258},"\"Exporting…\"",[205,623,302],{"class":215},[205,625,626,628,631,633],{"class":207,"line":273},[205,627,610],{"class":293},[205,629,630],{"class":215},".progress ",[205,632,238],{"class":211},[205,634,635],{"class":215}," QProgressBar()\n",[205,637,638,640],{"class":207,"line":285},[205,639,610],{"class":293},[205,641,642],{"class":215},".progress.setMaximum(total)\n",[205,644,645,647,650,653],{"class":207,"line":299},[205,646,610],{"class":293},[205,648,649],{"class":215},".progress.setAlignment(Qt.AlignLeft ",[205,651,652],{"class":211},"|",[205,654,655],{"class":215}," Qt.AlignVCenter)\n",[205,657,659,661,664,666],{"class":207,"line":658},11,[205,660,610],{"class":293},[205,662,663],{"class":215},".widget.layout().addWidget(",[205,665,453],{"class":293},[205,667,668],{"class":215},".progress)\n",[205,670,672,675,677],{"class":207,"line":671},12,[205,673,674],{"class":215},"        iface.messageBar().pushWidget(",[205,676,453],{"class":293},[205,678,679],{"class":215},".widget, Qgis.MessageLevel.Info)\n",[205,681,683],{"class":207,"line":682},13,[205,684,229],{"emptyLinePlaceholder":228},[205,686,688,690,693],{"class":207,"line":687},14,[205,689,599],{"class":211},[205,691,692],{"class":590}," advance",[205,694,695],{"class":215},"(self, value):\n",[205,697,699,701],{"class":207,"line":698},15,[205,700,610],{"class":293},[205,702,703],{"class":215},".progress.setValue(value)\n",[205,705,707],{"class":207,"line":706},16,[205,708,229],{"emptyLinePlaceholder":228},[205,710,712,714,717],{"class":207,"line":711},17,[205,713,599],{"class":211},[205,715,716],{"class":590}," finish",[205,718,719],{"class":215},"(self, ok, detail):\n",[205,721,723],{"class":207,"line":722},18,[205,724,725],{"class":215},"        iface.messageBar().clearWidgets()\n",[205,727,729,732],{"class":207,"line":728},19,[205,730,731],{"class":211},"        if",[205,733,734],{"class":215}," ok:\n",[205,736,738,741,743],{"class":207,"line":737},20,[205,739,740],{"class":215},"            iface.messageBar().pushSuccess(",[205,742,378],{"class":258},[205,744,745],{"class":215},", detail)\n",[205,747,749,752],{"class":207,"line":748},21,[205,750,751],{"class":211},"        else",[205,753,594],{"class":215},[205,755,757,760,763],{"class":207,"line":756},22,[205,758,759],{"class":215},"            iface.messageBar().pushCritical(",[205,761,762],{"class":258},"\"Export failed\"",[205,764,745],{"class":215},[14,766,767,769,770,772,773,776],{},[174,768,307],{}," Pushing with no ",[182,771,311],{}," argument means the message stays, which is what a progress indicator needs. ",[182,774,775],{},"clearWidgets()"," removes everything currently in the bar before the outcome is pushed — without it the progress message sits underneath the result until it is dismissed, which looks like the task is still running. Keeping the widget and the progress bar on the instance is required for the same reason every canvas object is: a local goes out of scope and is collected while Qt still refers to it.",[14,778,779,780,783,784,190],{},"This only works if the work yields to the event loop. A tight Python loop blocks repainting, so the bar freezes at zero — which is precisely the problem ",[182,781,782],{},"QgsTask"," exists to solve, as described in ",[21,785,787],{"href":786},"\u002Fqgis-plugin-development\u002Fbackground-tasks-and-plugin-performance\u002Frun-background-task-with-qgstask-pyqgis\u002F","running a background task with QgsTask",[163,789,791],{"id":790},"log-alongside-do-not-choose","Log alongside, do not choose",[14,793,794],{},"A message bar notice is transient by design. Anything worth investigating later belongs in the log as well.",[196,796,798],{"className":198,"code":797,"language":200,"meta":201,"style":201},"from qgis.core import QgsMessageLog\n\ndef report(title, detail, level=Qgis.MessageLevel.Info):\n    iface.messageBar().pushMessage(title, detail, level=level, duration=6)\n    QgsMessageLog.logMessage(f\"{title}: {detail}\", \"MyPlugin\", level=level)\n",[182,799,800,811,815,831,852],{"__ignoreMap":201},[205,801,802,804,806,808],{"class":207,"line":208},[205,803,212],{"class":211},[205,805,216],{"class":215},[205,807,219],{"class":211},[205,809,810],{"class":215}," QgsMessageLog\n",[205,812,813],{"class":207,"line":225},[205,814,229],{"emptyLinePlaceholder":228},[205,816,817,820,823,826,828],{"class":207,"line":232},[205,818,819],{"class":211},"def",[205,821,822],{"class":590}," report",[205,824,825],{"class":215},"(title, detail, level",[205,827,238],{"class":211},[205,829,830],{"class":215},"Qgis.MessageLevel.Info):\n",[205,832,833,836,839,841,844,846,848,850],{"class":207,"line":244},[205,834,835],{"class":215},"    iface.messageBar().pushMessage(title, detail, ",[205,837,838],{"class":276},"level",[205,840,238],{"class":211},[205,842,843],{"class":215},"level, ",[205,845,311],{"class":276},[205,847,238],{"class":211},[205,849,294],{"class":293},[205,851,302],{"class":215},[205,853,854,857,860,863,866,868,871,874,876,879,881,883,885,888,890,892,894],{"class":207,"line":249},[205,855,856],{"class":215},"    QgsMessageLog.logMessage(",[205,858,859],{"class":211},"f",[205,861,862],{"class":258},"\"",[205,864,865],{"class":293},"{",[205,867,36],{"class":215},[205,869,870],{"class":293},"}",[205,872,873],{"class":258},": ",[205,875,865],{"class":293},[205,877,878],{"class":215},"detail",[205,880,870],{"class":293},[205,882,862],{"class":258},[205,884,319],{"class":215},[205,886,887],{"class":258},"\"MyPlugin\"",[205,889,319],{"class":215},[205,891,838],{"class":276},[205,893,238],{"class":211},[205,895,896],{"class":215},"level)\n",[14,898,899,901,902,905,906,908],{},[174,900,307],{}," One helper doing both means a user sees the notice now and a support request can be answered from the log later. ",[182,903,904],{},"QgsMessageLog"," takes a tag — use your plugin's name consistently, because the log panel groups by it and a plugin that logs under several tags is much harder to follow. The log is also the only channel that works headless, so a helper like this is what lets the same code path serve an interactive plugin and a batch script, with ",[182,907,184],{}," guarded as described in the iface recipe.",[163,910,912],{"id":911},"writing-messages-people-can-act-on","Writing messages people can act on",[14,914,915],{},"The mechanics are easy; the wording is where most plugins fall down. Three rules cover it.",[14,917,918,921],{},[174,919,920],{},"Name what happened, not what the code did."," \"Export failed\" tells a user something; \"QgsVectorFileWriter returned error code 3\" tells them nothing they can use. Keep the technical detail for the log, where it belongs and where it will still be available tomorrow.",[14,923,924,927],{},[174,925,926],{},"Say what to do next when there is something to do."," \"Could not write to \u002Fdata\u002Foutput — the folder is read-only\" is a message a user can act on. \"Could not write output\" is a message they can only report. The difference is one clause and it removes a support round trip.",[14,929,930,933],{},[174,931,932],{},"Count things."," \"3 features skipped: invalid geometry\" is far more useful than \"some features were skipped\", because a user can tell at a glance whether three out of four thousand matters. Where a count is zero, consider not showing the message at all — a success notice for an operation that did nothing is noise.",[196,935,937],{"className":198,"code":936,"language":200,"meta":201,"style":201},"skipped = [f.id() for f in features if not f.geometry().isGeosValid()]\nif skipped:\n    report(\n        f\"{len(skipped)} features skipped\",\n        \"Their geometries are invalid — run Fix Geometries and try again.\",\n        Qgis.MessageLevel.Warning,\n    )\n",[182,938,939,970,977,982,1002,1009,1014],{"__ignoreMap":201},[205,940,941,944,946,949,952,955,958,961,964,967],{"class":207,"line":208},[205,942,943],{"class":215},"skipped ",[205,945,238],{"class":211},[205,947,948],{"class":215}," [f.id() ",[205,950,951],{"class":211},"for",[205,953,954],{"class":215}," f ",[205,956,957],{"class":211},"in",[205,959,960],{"class":215}," features ",[205,962,963],{"class":211},"if",[205,965,966],{"class":211}," not",[205,968,969],{"class":215}," f.geometry().isGeosValid()]\n",[205,971,972,974],{"class":207,"line":225},[205,973,963],{"class":211},[205,975,976],{"class":215}," skipped:\n",[205,978,979],{"class":207,"line":232},[205,980,981],{"class":215},"    report(\n",[205,983,984,987,989,992,995,997,1000],{"class":207,"line":244},[205,985,986],{"class":211},"        f",[205,988,862],{"class":258},[205,990,991],{"class":293},"{len",[205,993,994],{"class":215},"(skipped)",[205,996,870],{"class":293},[205,998,999],{"class":258}," features skipped\"",[205,1001,262],{"class":215},[205,1003,1004,1007],{"class":207,"line":249},[205,1005,1006],{"class":258},"        \"Their geometries are invalid — run Fix Geometries and try again.\"",[205,1008,262],{"class":215},[205,1010,1011],{"class":207,"line":255},[205,1012,1013],{"class":215},"        Qgis.MessageLevel.Warning,\n",[205,1015,1016],{"class":207,"line":265},[205,1017,1018],{"class":215},"    )\n",[14,1020,1021,1023,1024,1028],{},[174,1022,307],{}," Building the message from the actual count rather than from a fixed string is what keeps it honest across runs, and the second sentence names the remedy — in this case ",[21,1025,1027],{"href":1026},"\u002Fspatial-data-processing-automation\u002Fgeometry-operations-and-predicates\u002Ffix-invalid-geometries-pyqgis\u002F","fixing invalid geometries",", which is a real action the user can take without asking anyone. Guarding the whole thing on a non-empty list means a clean run says nothing, which is the correct amount to say.",[163,1030,1032],{"id":1031},"when-a-dialog-is-right-instead","When a dialog is right instead",[14,1034,1035],{},"Three cases genuinely need to block. A destructive action needs confirmation before it happens — deleting features, overwriting a file, committing a change that cannot be undone. A choice the code cannot proceed without needs an answer. And a long-form result — a validation report with forty lines — needs somewhere scrollable, which the bar is not.",[14,1037,1038],{},"Everything else is a message bar item. The test is simple: if the user could reasonably keep working without reading it, it does not deserve a dialog.",[163,1040,1042],{"id":1041},"qgis-version-compatibility","QGIS version compatibility",[14,1044,1045,1048,1049,1052,1053,1056,1057,1060,1061,1064,1065,319,1067,319,1069,326,1071,1073,1074,1076],{},[182,1046,1047],{},"QgsMessageBar"," and the ",[182,1050,1051],{},"pushMessage","\u002F",[182,1054,1055],{},"pushWidget"," API have been present since QGIS 2.x and are unchanged in 3.x. The ",[182,1058,1059],{},"Qgis.MessageLevel"," enum moved into the scoped namespace in 3.30, with the older ",[182,1062,1063],{},"Qgis.Info"," spellings retained. ",[182,1066,322],{},[182,1068,325],{},[182,1070,329],{},[182,1072,318],{}," were added in 3.4; on 3.0 use ",[182,1075,1051],{}," with an explicit level.",[163,1078,1080],{"id":1079},"troubleshooting","Troubleshooting",[168,1082,1083,1095,1101,1107,1113,1119],{},[171,1084,1085,443,1088,1090,1091,1094],{},[174,1086,1087],{},"The message never appears.",[182,1089,184],{}," is ",[182,1092,1093],{},"None",", or the code is running before the interface exists.",[171,1096,1097,1100],{},[174,1098,1099],{},"The progress bar never moves."," The work is blocking the event loop; move it to a task.",[171,1102,1103,1106],{},[174,1104,1105],{},"Two messages are stacked."," The earlier one had no duration and was not cleared.",[171,1108,1109,1112],{},[174,1110,1111],{},"The button does nothing."," The connection captured an object that was collected — connect to a bound method of something you keep.",[171,1114,1115,1118],{},[174,1116,1117],{},"The message is unreadable at a glance."," The title is carrying the whole sentence; put the detail in the body.",[171,1120,1121,1124,1125,1127,1128,190],{},[174,1122,1123],{},"Nothing is visible in a headless run."," Expected. Use ",[182,1126,904],{},", or ",[182,1129,1130],{},"print",[163,1132,1134],{"id":1133},"conclusion","Conclusion",[14,1136,1137],{},"Use the bar for everything the user does not have to answer, pick the level to match the meaning and the duration to match the importance, clear a progress message before pushing its outcome, and log everything you show. Reserve dialogs for confirmations and questions. That is most of what makes a plugin feel considerate rather than intrusive.",[163,1139,1141],{"id":1140},"frequently-asked-questions","Frequently Asked Questions",[14,1143,1144,1147],{},[174,1145,1146],{},"Can I show a message from a background thread?","\nNot directly — Qt widgets must be touched from the main thread. Emit a signal from the task and push the message in the slot, which runs on the main thread.",[14,1149,1150,1153,1154,1156,1157,1160,1161,1163],{},[174,1151,1152],{},"How do I dismiss a specific message?","\nKeep the widget returned by ",[182,1155,446],{}," and pass it to ",[182,1158,1159],{},"popWidget(widget)",". ",[182,1162,775],{}," removes all of them.",[14,1165,1166,1169,1170,1172,1173,190],{},[174,1167,1168],{},"Is there a message bar in a dialog?","\nYes — a ",[182,1171,1047],{}," can be added to any dialog's layout, which is the tidy way to report validation errors in a form, as covered in ",[21,1174,1176],{"href":1175},"\u002Fqgis-plugin-development\u002Fqt-designer-for-gis-interfaces\u002Fvalidate-plugin-dialog-input-pyqgis\u002F","validating plugin dialog input",[14,1178,1179,1182],{},[174,1180,1181],{},"Does the bar support rich text?","\nThe body accepts basic HTML, including links, which is a good way to point at a log file or a documentation page without a dialog.",[163,1184,1186],{"id":1185},"related","Related",[168,1188,1189,1195,1200,1206,1212],{},[171,1190,1191,1194],{},[21,1192,1193],{"href":23},"Qt Designer for GIS Interfaces in PyQGIS"," — the guide this recipe belongs to",[171,1196,1197],{},[21,1198,1199],{"href":1175},"Validate Plugin Dialog Input in PyQGIS",[171,1201,1202],{},[21,1203,1205],{"href":1204},"\u002Fqgis-plugin-development\u002Fbackground-tasks-and-plugin-performance\u002Fshow-plugin-progress-and-cancellation-pyqgis\u002F","Show Plugin Progress and Cancellation in PyQGIS",[171,1207,1208],{},[21,1209,1211],{"href":1210},"\u002Fpyqgis-fundamentals-environment-setup\u002Fdebugging-pyqgis-scripts\u002Flog-messages-to-qgis-message-log-pyqgis\u002F","Log Messages to the QGIS Message Log",[171,1213,1214],{},[21,1215,1216],{"href":188},"Use iface to Control the QGIS Interface in PyQGIS",[1218,1219,1220],"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 .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}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 .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}",{"title":201,"searchDepth":225,"depth":225,"links":1222},[1223,1224,1225,1226,1227,1228,1229,1230,1231,1232,1233,1234],{"id":165,"depth":225,"text":166},{"id":193,"depth":225,"text":194},{"id":340,"depth":225,"text":341},{"id":457,"depth":225,"text":458},{"id":790,"depth":225,"text":791},{"id":911,"depth":225,"text":912},{"id":1031,"depth":225,"text":1032},{"id":1041,"depth":225,"text":1042},{"id":1079,"depth":225,"text":1080},{"id":1133,"depth":225,"text":1134},{"id":1140,"depth":225,"text":1141},{"id":1185,"depth":225,"text":1186},"Give plugin users feedback that does not block them — message levels and durations, buttons and widgets in the bar, progress messages, and when a dialog is right instead.","md",{"slug":1238,"type":1239,"breadcrumb":1240,"datePublished":1241,"dateModified":1241},"show-messages-with-qgsmessagebar-pyqgis","article","Show Messages with QgsMessageBar","2026-09-05","\u002Fqgis-plugin-development\u002Fqt-designer-for-gis-interfaces\u002Fshow-messages-with-qgsmessagebar-pyqgis",{"title":5,"description":1235},"qgis-plugin-development\u002Fqt-designer-for-gis-interfaces\u002Fshow-messages-with-qgsmessagebar-pyqgis\u002Findex","LWg2MNKVU8NOsdGMEHUq72l_1BvXq4hUwkesBvZiJaY",1788563851984]