-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathinstall_dependencies.py
More file actions
141 lines (114 loc) · 4.45 KB
/
install_dependencies.py
File metadata and controls
141 lines (114 loc) · 4.45 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
import io
import sys
import site
import importlib
import platform
import subprocess
import os
from PyQt5.QtWidgets import QMessageBox
REQUIRED_PACKAGES = {
"wheel": "",
"hda": "",
"owslib": ""
}
_failed_packages = []
def ensure_dependencies_installed(iface=None):
"""
Tries to install/upgrade all required packages.
If any fail, they are skipped and noted for later summary.
"""
for package, version_spec in REQUIRED_PACKAGES.items():
package_spec = f"{package}{version_spec}"
try:
_install_package(package_spec, iface)
_activate_package_in_session(package)
except Exception as e:
_failed_packages.append(package_spec)
print(f"❌ Failed to install {package_spec}: {e}")
if _failed_packages:
_show_summary_dialog(iface)
def _install_package(package_spec, iface=None):
"""
Installs or upgrades a package using pip.
Uses the correct Python executable depending on platform.
"""
try:
system = platform.system().lower()
python_exec = sys.executable # default fallback
if system == "windows":
# sys.executable → qgis.exe → need to find python.exe
qgis_dir = os.path.dirname(sys.executable)
candidate = os.path.join(qgis_dir, "python.exe")
if os.path.exists(candidate):
python_exec = candidate
else:
raise RuntimeError("Could not locate QGIS Python interpreter (python.exe)")
elif system == "darwin":
# macOS: QGIS bundle → look for bin/python3
candidate = os.path.join(os.path.dirname(sys.executable), "bin", "python3")
if os.path.exists(candidate):
python_exec = candidate
else:
raise RuntimeError("Could not locate QGIS Python (python3) in bundle")
# On Linux (or fallback): use sys.executable
print(f"🔧 Installing: {package_spec} using {python_exec}")
args = [python_exec, "-m", "pip", "install", "--upgrade", package_spec]
if system == "linux":
args.insert(5, "--break-system-packages")
startupinfo = None
if platform.system().lower() == "windows":
startupinfo = subprocess.STARTUPINFO()
startupinfo.dwFlags |= subprocess.STARTF_USESHOWWINDOW
process = subprocess.run(
args,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
text=True,
startupinfo=startupinfo # <-- suppress console window
)
if process.returncode != 0:
raise RuntimeError(process.stderr.strip())
print(process.stdout.strip())
print(f"✅ Successfully installed: {package_spec}")
except Exception as e:
_show_error_dialog(package_spec, iface, e)
raise
def _activate_package_in_session(package):
"""
Ensures the package is available in the current session.
"""
if site.USER_SITE not in sys.path:
site.addsitedir(site.USER_SITE)
importlib.invalidate_caches()
try:
importlib.import_module(package)
print(f"🚀 Package '{package}' is now available.")
except ImportError as e:
print(f"❗ Could not import '{package}' after installation: {e}")
def _show_error_dialog(package_spec, iface, exception):
"""
Shows a message box when a single package fails to install.
"""
msg = QMessageBox()
msg.setIcon(QMessageBox.Critical)
msg.setWindowTitle("Dependency Installation Failed")
msg.setText(f"Could not install:\n{package_spec}")
msg.setInformativeText("Please install it manually in the Python Console.")
msg.setDetailedText(f"Error:\n{exception}\n\nTry:\npip install {package_spec} --break-system-packages")
open_console_btn = msg.addButton("Open Python Console", QMessageBox.ActionRole)
msg.addButton(QMessageBox.Close)
msg.exec_()
if iface and msg.clickedButton() == open_console_btn:
iface.actionShowPythonDialog().trigger()
def _show_summary_dialog(iface=None):
"""
Shows one final popup listing all failed packages.
"""
failed_list = "\n".join(_failed_packages)
summary = QMessageBox()
summary.setIcon(QMessageBox.Warning)
summary.setWindowTitle("Missing Dependencies")
summary.setText("The following packages could not be installed:")
summary.setDetailedText(failed_list)
summary.addButton(QMessageBox.Ok)
summary.exec_()