[{"data":1,"prerenderedAt":840},["ShallowReactive",2],{"doc:\u002Fpyqgis-fundamentals-environment-setup\u002Fsetting-up-pycharm-for-qgis\u002Fbest-ide-for-qgis-plugin-development":3},{"id":4,"title":5,"body":6,"description":833,"extension":834,"meta":835,"navigation":227,"path":836,"seo":837,"stem":838,"__hash__":839},"docs\u002Fpyqgis-fundamentals-environment-setup\u002Fsetting-up-pycharm-for-qgis\u002Fbest-ide-for-qgis-plugin-development\u002Findex.md","Best IDE for QGIS Plugin Development",{"type":7,"value":8,"toc":827},"minimark",[9,13,35,40,156,160,185,192,539,543,565,627,642,746,753,757,815,823],[10,11,5],"h1",{"id":12},"best-ide-for-qgis-plugin-development",[14,15,16,17,21,22,26,27,30,31,34],"p",{},"The ",[18,19,20],"strong",{},"best IDE for QGIS plugin development"," is JetBrains PyCharm Professional, with Visual Studio Code as a highly capable lightweight alternative. PyCharm delivers the most reliable integrated debugger, accurate static analysis for compiled ",[23,24,25],"code",{},"qgis.*"," modules, and seamless environment routing when pointed to the QGIS-bundled Python interpreter. VS Code matches most functionality via the Python\u002FPylance extensions and ",[23,28,29],{},"debugpy",", but requires manual ",[23,32,33],{},"launch.json"," configuration for process attachment.",[36,37,39],"h3",{"id":38},"quick-compatibility-matrix","Quick Compatibility Matrix",[41,42,43,59],"table",{},[44,45,46],"thead",{},[47,48,49,53,56],"tr",{},[50,51,52],"th",{},"Component",[50,54,55],{},"Requirement",[50,57,58],{},"Notes",[60,61,62,76,89,105,118,139],"tbody",{},[47,63,64,70,73],{},[65,66,67],"td",{},[18,68,69],{},"QGIS",[65,71,72],{},"3.28 LTR or 3.34+",[65,74,75],{},"Python bindings match the installer’s bundled version",[47,77,78,83,86],{},[65,79,80],{},[18,81,82],{},"Python",[65,84,85],{},"3.9–3.12",[65,87,88],{},"Must exactly match QGIS internal build; avoid conda\u002Fsystem Python",[47,90,91,96,99],{},[65,92,93],{},[18,94,95],{},"PyQt",[65,97,98],{},"PyQt5 (≤3.32) \u002F PyQt6 (≥3.34)",[65,100,101,102],{},"IDE must resolve the correct Qt version for ",[23,103,104],{},"qgis.gui",[47,106,107,112,115],{},[65,108,109],{},[18,110,111],{},"PyCharm",[65,113,114],{},"Professional 2023.2+",[65,116,117],{},"Community edition lacks remote debugger attachment",[47,119,120,125,128],{},[65,121,122],{},[18,123,124],{},"VS Code",[65,126,127],{},"1.85+",[65,129,130,131,134,135,138],{},"Requires ",[23,132,133],{},"ms-python.python"," and ",[23,136,137],{},"ms-python.debugpy"," extensions",[47,140,141,146,149],{},[65,142,143],{},[18,144,145],{},"OS",[65,147,148],{},"Win\u002FmacOS\u002FLinux",[65,150,151,152,155],{},"Path separators and ",[23,153,154],{},"QGIS_PREFIX_PATH"," resolution differ",[36,157,159],{"id":158},"environment-routing-path-setup","Environment Routing & Path Setup",[14,161,162,163,166,167,170,171,174,175,178,179,184],{},"QGIS ships with a self-contained Python environment. External IDEs default to global or virtual environments, which lack compiled ",[23,164,165],{},"qgis.core",", ",[23,168,169],{},"qgis.analysis",", and Qt bindings. The most reliable approach is to configure your IDE’s interpreter to point directly to QGIS’s bundled ",[23,172,173],{},"python.exe"," (Windows\u002FOSGeo4W) or ",[23,176,177],{},"python3"," (macOS\u002FLinux). For step-by-step interpreter routing and workspace structuring, see ",[180,181,183],"a",{"href":182},"\u002Fpyqgis-fundamentals-environment-setup\u002Fsetting-up-pycharm-for-qgis\u002F","Setting Up PyCharm for QGIS",".",[14,186,187,188,191],{},"If direct interpreter pointing isn't feasible, use this runtime injection script before any ",[23,189,190],{},"import qgis"," statement:",[193,194,199],"pre",{"className":195,"code":196,"language":197,"meta":198,"style":198},"language-python shiki shiki-themes github-dark","import sys\nimport os\n\n# Adjust to your QGIS installation root\nQGIS_PREFIX = os.environ.get(\"QGIS_PREFIX_PATH\", r\"C:\\OSGeo4W\\apps\\qgis-ltr\")\nPYTHON_DIR = os.path.join(QGIS_PREFIX, \"python\")\n\n# Auto-detect PyQt version (QGIS ≥3.34 uses PyQt6, ≤3.32 uses PyQt5)\nPYQT_DIR = os.path.join(QGIS_PREFIX, \"python\", \"PyQt6\") if os.path.isdir(os.path.join(QGIS_PREFIX, \"python\", \"PyQt6\")) else os.path.join(QGIS_PREFIX, \"python\", \"PyQt5\")\n\n# Prepend QGIS paths\nfor p in (PYTHON_DIR, PYQT_DIR):\n if os.path.isdir(p) and p not in sys.path:\n sys.path.insert(0, p)\n\n# Set mandatory runtime variables\nos.environ[\"QGIS_PREFIX_PATH\"] = QGIS_PREFIX\nos.environ[\"QT_QPA_PLATFORM_PLUGIN_PATH\"] = os.path.join(QGIS_PREFIX, \"plugins\", \"platforms\")\n\nfrom qgis.core import QgsApplication, QgsProject\nfrom qgis.gui import QgsMapCanvas\n","python","",[23,200,201,214,222,229,236,289,309,314,320,381,386,392,416,439,451,456,462,479,507,512,526],{"__ignoreMap":198},[202,203,206,210],"span",{"class":204,"line":205},"line",1,[202,207,209],{"class":208},"snl16","import",[202,211,213],{"class":212},"s95oV"," sys\n",[202,215,217,219],{"class":204,"line":216},2,[202,218,209],{"class":208},[202,220,221],{"class":212}," os\n",[202,223,225],{"class":204,"line":224},3,[202,226,228],{"emptyLinePlaceholder":227},true,"\n",[202,230,232],{"class":204,"line":231},4,[202,233,235],{"class":234},"sAwPA","# Adjust to your QGIS installation root\n",[202,237,239,243,246,249,253,255,258,261,265,269,272,275,278,281,284,286],{"class":204,"line":238},5,[202,240,242],{"class":241},"sDLfK","QGIS_PREFIX",[202,244,245],{"class":208}," =",[202,247,248],{"class":212}," os.environ.get(",[202,250,252],{"class":251},"sU2Wk","\"QGIS_PREFIX_PATH\"",[202,254,166],{"class":212},[202,256,257],{"class":208},"r",[202,259,260],{"class":251},"\"",[202,262,264],{"class":263},"sns5M","C:",[202,266,268],{"class":267},"sRjNt","\\O",[202,270,271],{"class":263},"SGeo4W",[202,273,274],{"class":267},"\\a",[202,276,277],{"class":263},"pps",[202,279,280],{"class":267},"\\q",[202,282,283],{"class":263},"gis-ltr",[202,285,260],{"class":251},[202,287,288],{"class":212},")\n",[202,290,292,295,297,300,302,304,307],{"class":204,"line":291},6,[202,293,294],{"class":241},"PYTHON_DIR",[202,296,245],{"class":208},[202,298,299],{"class":212}," os.path.join(",[202,301,242],{"class":241},[202,303,166],{"class":212},[202,305,306],{"class":251},"\"python\"",[202,308,288],{"class":212},[202,310,312],{"class":204,"line":311},7,[202,313,228],{"emptyLinePlaceholder":227},[202,315,317],{"class":204,"line":316},8,[202,318,319],{"class":234},"# Auto-detect PyQt version (QGIS ≥3.34 uses PyQt6, ≤3.32 uses PyQt5)\n",[202,321,323,326,328,330,332,334,336,338,341,344,347,350,352,354,356,358,360,363,366,368,370,372,374,376,379],{"class":204,"line":322},9,[202,324,325],{"class":241},"PYQT_DIR",[202,327,245],{"class":208},[202,329,299],{"class":212},[202,331,242],{"class":241},[202,333,166],{"class":212},[202,335,306],{"class":251},[202,337,166],{"class":212},[202,339,340],{"class":251},"\"PyQt6\"",[202,342,343],{"class":212},") ",[202,345,346],{"class":208},"if",[202,348,349],{"class":212}," os.path.isdir(os.path.join(",[202,351,242],{"class":241},[202,353,166],{"class":212},[202,355,306],{"class":251},[202,357,166],{"class":212},[202,359,340],{"class":251},[202,361,362],{"class":212},")) ",[202,364,365],{"class":208},"else",[202,367,299],{"class":212},[202,369,242],{"class":241},[202,371,166],{"class":212},[202,373,306],{"class":251},[202,375,166],{"class":212},[202,377,378],{"class":251},"\"PyQt5\"",[202,380,288],{"class":212},[202,382,384],{"class":204,"line":383},10,[202,385,228],{"emptyLinePlaceholder":227},[202,387,389],{"class":204,"line":388},11,[202,390,391],{"class":234},"# Prepend QGIS paths\n",[202,393,395,398,401,404,407,409,411,413],{"class":204,"line":394},12,[202,396,397],{"class":208},"for",[202,399,400],{"class":212}," p ",[202,402,403],{"class":208},"in",[202,405,406],{"class":212}," (",[202,408,294],{"class":241},[202,410,166],{"class":212},[202,412,325],{"class":241},[202,414,415],{"class":212},"):\n",[202,417,419,422,425,428,430,433,436],{"class":204,"line":418},13,[202,420,421],{"class":208}," if",[202,423,424],{"class":212}," os.path.isdir(p) ",[202,426,427],{"class":208},"and",[202,429,400],{"class":212},[202,431,432],{"class":208},"not",[202,434,435],{"class":208}," in",[202,437,438],{"class":212}," sys.path:\n",[202,440,442,445,448],{"class":204,"line":441},14,[202,443,444],{"class":212}," sys.path.insert(",[202,446,447],{"class":241},"0",[202,449,450],{"class":212},", p)\n",[202,452,454],{"class":204,"line":453},15,[202,455,228],{"emptyLinePlaceholder":227},[202,457,459],{"class":204,"line":458},16,[202,460,461],{"class":234},"# Set mandatory runtime variables\n",[202,463,465,468,470,473,476],{"class":204,"line":464},17,[202,466,467],{"class":212},"os.environ[",[202,469,252],{"class":251},[202,471,472],{"class":212},"] ",[202,474,475],{"class":208},"=",[202,477,478],{"class":241}," QGIS_PREFIX\n",[202,480,482,484,487,489,491,493,495,497,500,502,505],{"class":204,"line":481},18,[202,483,467],{"class":212},[202,485,486],{"class":251},"\"QT_QPA_PLATFORM_PLUGIN_PATH\"",[202,488,472],{"class":212},[202,490,475],{"class":208},[202,492,299],{"class":212},[202,494,242],{"class":241},[202,496,166],{"class":212},[202,498,499],{"class":251},"\"plugins\"",[202,501,166],{"class":212},[202,503,504],{"class":251},"\"platforms\"",[202,506,288],{"class":212},[202,508,510],{"class":204,"line":509},19,[202,511,228],{"emptyLinePlaceholder":227},[202,513,515,518,521,523],{"class":204,"line":514},20,[202,516,517],{"class":208},"from",[202,519,520],{"class":212}," qgis.core ",[202,522,209],{"class":208},[202,524,525],{"class":212}," QgsApplication, QgsProject\n",[202,527,529,531,534,536],{"class":204,"line":528},21,[202,530,517],{"class":208},[202,532,533],{"class":212}," qgis.gui ",[202,535,209],{"class":208},[202,537,538],{"class":212}," QgsMapCanvas\n",[36,540,542],{"id":541},"debugger-attachment-workflow","Debugger Attachment Workflow",[544,545,546],"ol",{},[547,548,549,552,553,556,557,560,561,564],"li",{},[18,550,551],{},"PyCharm Professional:"," Create a ",[23,554,555],{},"Python Debug Server"," run configuration (default ",[23,558,559],{},"localhost:5678","). In your plugin’s ",[23,562,563],{},"__init__.py"," or target function, add:",[193,566,568],{"className":195,"code":567,"language":197,"meta":198,"style":198},"import pydevd_pycharm\npydevd_pycharm.settrace('localhost', port=5678, stdoutToServer=True, stderrToServer=True, suspend=False)\n",[23,569,570,577],{"__ignoreMap":198},[202,571,572,574],{"class":204,"line":205},[202,573,209],{"class":208},[202,575,576],{"class":212}," pydevd_pycharm\n",[202,578,579,582,585,587,591,593,596,598,601,603,606,608,611,613,615,617,620,622,625],{"class":204,"line":216},[202,580,581],{"class":212},"pydevd_pycharm.settrace(",[202,583,584],{"class":251},"'localhost'",[202,586,166],{"class":212},[202,588,590],{"class":589},"s9osk","port",[202,592,475],{"class":208},[202,594,595],{"class":241},"5678",[202,597,166],{"class":212},[202,599,600],{"class":589},"stdoutToServer",[202,602,475],{"class":208},[202,604,605],{"class":241},"True",[202,607,166],{"class":212},[202,609,610],{"class":589},"stderrToServer",[202,612,475],{"class":208},[202,614,605],{"class":241},[202,616,166],{"class":212},[202,618,619],{"class":589},"suspend",[202,621,475],{"class":208},[202,623,624],{"class":241},"False",[202,626,288],{"class":212},[14,628,629,630,633,634,637,638,641],{},"Start the debug server in PyCharm, then trigger the plugin in QGIS.\n2. ",[18,631,632],{},"VS Code:"," Add this ",[23,635,636],{},"attach"," configuration to ",[23,639,640],{},".vscode\u002Flaunch.json",":",[193,643,647],{"className":644,"code":645,"language":646,"meta":198,"style":198},"language-json shiki shiki-themes github-dark","{\n\"name\": \"Attach to QGIS\",\n\"type\": \"python\",\n\"request\": \"attach\",\n\"connect\": { \"host\": \"localhost\", \"port\": 5678 },\n\"justMyCode\": false,\n\"subProcess\": true\n}\n","json",[23,648,649,654,668,679,691,719,731,741],{"__ignoreMap":198},[202,650,651],{"class":204,"line":205},[202,652,653],{"class":212},"{\n",[202,655,656,659,662,665],{"class":204,"line":216},[202,657,658],{"class":241},"\"name\"",[202,660,661],{"class":212},": ",[202,663,664],{"class":251},"\"Attach to QGIS\"",[202,666,667],{"class":212},",\n",[202,669,670,673,675,677],{"class":204,"line":224},[202,671,672],{"class":241},"\"type\"",[202,674,661],{"class":212},[202,676,306],{"class":251},[202,678,667],{"class":212},[202,680,681,684,686,689],{"class":204,"line":231},[202,682,683],{"class":241},"\"request\"",[202,685,661],{"class":212},[202,687,688],{"class":251},"\"attach\"",[202,690,667],{"class":212},[202,692,693,696,699,702,704,707,709,712,714,716],{"class":204,"line":238},[202,694,695],{"class":241},"\"connect\"",[202,697,698],{"class":212},": { ",[202,700,701],{"class":241},"\"host\"",[202,703,661],{"class":212},[202,705,706],{"class":251},"\"localhost\"",[202,708,166],{"class":212},[202,710,711],{"class":241},"\"port\"",[202,713,661],{"class":212},[202,715,595],{"class":241},[202,717,718],{"class":212}," },\n",[202,720,721,724,726,729],{"class":204,"line":291},[202,722,723],{"class":241},"\"justMyCode\"",[202,725,661],{"class":212},[202,727,728],{"class":241},"false",[202,730,667],{"class":212},[202,732,733,736,738],{"class":204,"line":311},[202,734,735],{"class":241},"\"subProcess\"",[202,737,661],{"class":212},[202,739,740],{"class":241},"true\n",[202,742,743],{"class":204,"line":316},[202,744,745],{"class":212},"}\n",[14,747,748,749,752],{},"Insert ",[23,750,751],{},"import debugpy; debugpy.listen((\"localhost\", 5678)); debugpy.wait_for_client()"," in your plugin entry point. Start the VS Code debugger, then launch QGIS.",[36,754,756],{"id":755},"troubleshooting-fallbacks","Troubleshooting & Fallbacks",[758,759,760,773,796,806],"ul",{},[547,761,762,765,766,769,770,184],{},[18,763,764],{},"Architecture Mismatch:"," QGIS is strictly 64-bit. A 32-bit IDE interpreter will fail to load ",[23,767,768],{},"qgis._core",". Verify with ",[23,771,772],{},"python -c \"import platform; print(platform.architecture())\"",[547,774,775,782,783,786,787,790,791,134,793,795],{},[18,776,777,778,781],{},"Use ",[23,779,780],{},".pth"," Files for Static Resolution:"," Place a ",[23,784,785],{},"qgis_paths.pth"," file in your IDE’s ",[23,788,789],{},"site-packages"," containing absolute paths to QGIS’s ",[23,792,197],{},[23,794,95],{}," directories. This forces static resolution without modifying plugin source.",[547,797,798,801,802,805],{},[18,799,800],{},"Isolate Profile Conflicts:"," Launch QGIS with a clean profile: ",[23,803,804],{},"qgis --profile-name debug --configpath \u002Ftmp\u002Fqgis_debug",". Corrupted user profiles often mask IDE configuration errors.",[547,807,808,811,812,184],{},[18,809,810],{},"Fallback to QGIS Message Log:"," If firewalls block debugger ports, route output to QGIS’s built-in log: ",[23,813,814],{},"from qgis.core import QgsMessageLog, Qgis; QgsMessageLog.logMessage(\"Debug: state\", \"MyPlugin\", Qgis.Warning)",[14,816,817,818,822],{},"Environment alignment dictates plugin stability. Once the interpreter, Qt bindings, and debugger ports are synchronized, you gain full static analysis, step-through execution, and automated testing. For comprehensive dependency isolation strategies and workspace templates, review ",[180,819,821],{"href":820},"\u002Fpyqgis-fundamentals-environment-setup\u002F","PyQGIS Fundamentals & Environment Setup",". Proper path hygiene prevents ~90% of import and runtime failures.",[824,825,826],"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 .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}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 pre.shiki code .sns5M, html code.shiki .sns5M{--shiki-default:#DBEDFF}html pre.shiki code .sRjNt, html code.shiki .sRjNt{--shiki-default:#85E89D;--shiki-default-font-weight:bold}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":198,"searchDepth":216,"depth":216,"links":828},[829,830,831,832],{"id":38,"depth":224,"text":39},{"id":158,"depth":224,"text":159},{"id":541,"depth":224,"text":542},{"id":755,"depth":224,"text":756},"The best IDE for QGIS plugin development is JetBrains PyCharm Professional, with Visual Studio Code as a highly capable lightweight alternative. PyCharm delivers the most reliable integrated debugger, accurate static analysis for compiled qgis.* modules, and seamless environment routing when pointed to the QGIS-bundled Python interpreter. VS Code matches most functionality via the Python\u002FPylance extensions and debugpy, but requires manual launch.json configuration for process attachment.","md",{},"\u002Fpyqgis-fundamentals-environment-setup\u002Fsetting-up-pycharm-for-qgis\u002Fbest-ide-for-qgis-plugin-development",{"title":5,"description":833},"pyqgis-fundamentals-environment-setup\u002Fsetting-up-pycharm-for-qgis\u002Fbest-ide-for-qgis-plugin-development\u002Findex","qZAzqvVSmb38K0uLoAljB7GnU7zS0KAgb7uG-n4tg2k",1777824788941]