oqtopus 0.1.14__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- oqtopus/__init__.py +4 -0
- oqtopus/core/module.py +115 -0
- oqtopus/core/module_asset.py +16 -0
- oqtopus/core/module_package.py +118 -0
- oqtopus/core/modules_config.py +11 -0
- oqtopus/core/package_prepare_task.py +148 -0
- oqtopus/gui/__init__.py +0 -0
- oqtopus/gui/about_dialog.py +61 -0
- oqtopus/gui/database_connection_widget.py +154 -0
- oqtopus/gui/database_create_dialog.py +210 -0
- oqtopus/gui/database_duplicate_dialog.py +100 -0
- oqtopus/gui/logs_widget.py +177 -0
- oqtopus/gui/main_dialog.py +168 -0
- oqtopus/gui/module_selection_widget.py +350 -0
- oqtopus/gui/module_widget.py +199 -0
- oqtopus/gui/parameters_groupbox.py +75 -0
- oqtopus/gui/project_widget.py +170 -0
- oqtopus/gui/settings_dialog.py +37 -0
- oqtopus/oqtopus.py +67 -0
- oqtopus/oqtopus_plugin.py +184 -0
- oqtopus/ui/__init__.py +0 -0
- oqtopus/utils/__init__.py +0 -0
- oqtopus/utils/plugin_utils.py +172 -0
- oqtopus/utils/qt_utils.py +94 -0
- oqtopus/utils/tmmtlogging.py +50 -0
- oqtopus/utils/translation.py +84 -0
- oqtopus-0.1.14.dist-info/METADATA +363 -0
- oqtopus-0.1.14.dist-info/RECORD +33 -0
- oqtopus-0.1.14.dist-info/WHEEL +5 -0
- oqtopus-0.1.14.dist-info/licenses/LICENSE +339 -0
- oqtopus-0.1.14.dist-info/top_level.txt +2 -0
- tests/__init__.py +12 -0
- tests/test_plugin_load.py +22 -0
@@ -0,0 +1,170 @@
|
|
1
|
+
import os
|
2
|
+
import shutil
|
3
|
+
|
4
|
+
from qgis.PyQt.QtWidgets import QFileDialog, QMessageBox, QWidget
|
5
|
+
|
6
|
+
from ..core.module_package import ModulePackage
|
7
|
+
from ..utils.plugin_utils import PluginUtils
|
8
|
+
from ..utils.qt_utils import CriticalMessageBox, QtUtils
|
9
|
+
|
10
|
+
DIALOG_UI = PluginUtils.get_ui_class("project_widget.ui")
|
11
|
+
|
12
|
+
|
13
|
+
class ProjectWidget(QWidget, DIALOG_UI):
|
14
|
+
|
15
|
+
def __init__(self, parent=None):
|
16
|
+
QWidget.__init__(self, parent)
|
17
|
+
self.setupUi(self)
|
18
|
+
|
19
|
+
self.project_install_pushButton.clicked.connect(self.__projectInstallClicked)
|
20
|
+
self.project_seeChangelog_pushButton.clicked.connect(self.__projectSeeChangelogClicked)
|
21
|
+
|
22
|
+
self.__current_module_package = None
|
23
|
+
|
24
|
+
def setModulePackage(self, module_package: ModulePackage):
|
25
|
+
self.__current_module_package = module_package
|
26
|
+
self.__packagePrepareGetProjectFilename()
|
27
|
+
|
28
|
+
def __packagePrepareGetProjectFilename(self):
|
29
|
+
asset_project = self.__current_module_package.asset_project
|
30
|
+
if asset_project is None:
|
31
|
+
self.project_info_label.setText(
|
32
|
+
self.tr("No project asset available for this module version.")
|
33
|
+
)
|
34
|
+
QtUtils.setForegroundColor(self.project_info_label, PluginUtils.COLOR_WARNING)
|
35
|
+
QtUtils.setFontItalic(self.project_info_label, True)
|
36
|
+
return
|
37
|
+
|
38
|
+
# Search for QGIS project file in self.__package_dir
|
39
|
+
project_file_dir = os.path.join(asset_project.package_dir, "project")
|
40
|
+
|
41
|
+
# Check if the directory exists
|
42
|
+
if not os.path.exists(project_file_dir):
|
43
|
+
self.project_info_label.setText(
|
44
|
+
self.tr(f"Project directory '{project_file_dir}' does not exist.")
|
45
|
+
)
|
46
|
+
QtUtils.setForegroundColor(self.project_info_label, PluginUtils.COLOR_WARNING)
|
47
|
+
QtUtils.setFontItalic(self.project_info_label, True)
|
48
|
+
return
|
49
|
+
|
50
|
+
self.__project_file = None
|
51
|
+
for root, dirs, files in os.walk(project_file_dir):
|
52
|
+
for file in files:
|
53
|
+
if file.endswith(".qgz") or file.endswith(".qgs"):
|
54
|
+
self.__project_file = os.path.join(root, file)
|
55
|
+
break
|
56
|
+
|
57
|
+
if self.__project_file:
|
58
|
+
break
|
59
|
+
|
60
|
+
if self.__project_file is None:
|
61
|
+
self.project_info_label.setText(
|
62
|
+
self.tr(f"No QGIS project file (.qgz or .qgs) found into {project_file_dir}."),
|
63
|
+
)
|
64
|
+
QtUtils.setForegroundColor(self.project_info_label, PluginUtils.COLOR_WARNING)
|
65
|
+
QtUtils.setFontItalic(self.db_database_label, True)
|
66
|
+
return
|
67
|
+
|
68
|
+
self.project_info_label.setText(
|
69
|
+
self.tr(self.__project_file),
|
70
|
+
)
|
71
|
+
QtUtils.setForegroundColor(self.project_info_label, PluginUtils.COLOR_GREEN)
|
72
|
+
QtUtils.setFontItalic(self.db_database_label, False)
|
73
|
+
|
74
|
+
def __projectInstallClicked(self):
|
75
|
+
|
76
|
+
if self.__current_module_package is None:
|
77
|
+
QMessageBox.warning(
|
78
|
+
self,
|
79
|
+
self.tr("Error"),
|
80
|
+
self.tr("Please select a module and version first."),
|
81
|
+
)
|
82
|
+
return
|
83
|
+
|
84
|
+
if self.module_package_comboBox.currentData() is None:
|
85
|
+
QMessageBox.warning(
|
86
|
+
self,
|
87
|
+
self.tr("Error"),
|
88
|
+
self.tr("Please select a module version first."),
|
89
|
+
)
|
90
|
+
return
|
91
|
+
|
92
|
+
asset_project = self.module_package_comboBox.currentData().asset_project
|
93
|
+
if asset_project is None:
|
94
|
+
QMessageBox.warning(
|
95
|
+
self,
|
96
|
+
self.tr("Error"),
|
97
|
+
self.tr("No project asset available for this module version."),
|
98
|
+
)
|
99
|
+
return
|
100
|
+
|
101
|
+
package_dir = asset_project.package_dir
|
102
|
+
if package_dir is None:
|
103
|
+
CriticalMessageBox(
|
104
|
+
self.tr("Error"), self.tr("No valid package directory available."), None, self
|
105
|
+
).exec()
|
106
|
+
return
|
107
|
+
|
108
|
+
# Search for QGIS project file in package_dir
|
109
|
+
project_file_dir = os.path.join(package_dir, "project")
|
110
|
+
|
111
|
+
# Check if the directory exists
|
112
|
+
if not os.path.exists(project_file_dir):
|
113
|
+
CriticalMessageBox(
|
114
|
+
self.tr("Error"),
|
115
|
+
self.tr(f"Project directory '{project_file_dir}' does not exist."),
|
116
|
+
None,
|
117
|
+
self,
|
118
|
+
).exec()
|
119
|
+
return
|
120
|
+
|
121
|
+
self.__project_file = None
|
122
|
+
for root, dirs, files in os.walk(project_file_dir):
|
123
|
+
print(f"Searching for QGIS project file in {root}: {files}")
|
124
|
+
for file in files:
|
125
|
+
if file.endswith(".qgz") or file.endswith(".qgs"):
|
126
|
+
self.__project_file = os.path.join(root, file)
|
127
|
+
break
|
128
|
+
|
129
|
+
if self.__project_file:
|
130
|
+
break
|
131
|
+
|
132
|
+
if self.__project_file is None:
|
133
|
+
CriticalMessageBox(
|
134
|
+
self.tr("Error"),
|
135
|
+
self.tr(f"No QGIS project file (.qgz or .qgs) found into {project_file_dir}."),
|
136
|
+
None,
|
137
|
+
self,
|
138
|
+
).exec()
|
139
|
+
return
|
140
|
+
|
141
|
+
install_destination = QFileDialog.getExistingDirectory(
|
142
|
+
self,
|
143
|
+
self.tr("Select installation directory"),
|
144
|
+
"",
|
145
|
+
QFileDialog.Option.ShowDirsOnly,
|
146
|
+
)
|
147
|
+
|
148
|
+
if not install_destination:
|
149
|
+
return
|
150
|
+
|
151
|
+
# Copy the project file to the selected directory
|
152
|
+
try:
|
153
|
+
shutil.copy(self.__project_file, install_destination)
|
154
|
+
QMessageBox.information(
|
155
|
+
self,
|
156
|
+
self.tr("Project installed"),
|
157
|
+
self.tr(
|
158
|
+
f"Project file '{self.__project_file}' has been copied to '{install_destination}'."
|
159
|
+
),
|
160
|
+
)
|
161
|
+
except Exception as e:
|
162
|
+
QMessageBox.critical(
|
163
|
+
self,
|
164
|
+
self.tr("Error"),
|
165
|
+
self.tr(f"Failed to copy project file: {e}"),
|
166
|
+
)
|
167
|
+
return
|
168
|
+
|
169
|
+
def __projectSeeChangelogClicked(self):
|
170
|
+
self.__seeChangeLogClicked()
|
@@ -0,0 +1,37 @@
|
|
1
|
+
from qgis.PyQt.QtWidgets import QApplication, QDialog, QMessageBox, QStyle
|
2
|
+
|
3
|
+
from ..utils.plugin_utils import PluginUtils
|
4
|
+
|
5
|
+
DIALOG_UI = PluginUtils.get_ui_class("settings_dialog.ui")
|
6
|
+
|
7
|
+
|
8
|
+
class SettingsDialog(QDialog, DIALOG_UI):
|
9
|
+
def __init__(self, parent=None):
|
10
|
+
QDialog.__init__(self, parent)
|
11
|
+
self.setupUi(self)
|
12
|
+
|
13
|
+
self.githubToken_lineEdit.setText(PluginUtils.get_github_token())
|
14
|
+
|
15
|
+
self.helpButton.setIcon(
|
16
|
+
QApplication.style().standardIcon(QStyle.StandardPixmap.SP_DialogHelpButton)
|
17
|
+
)
|
18
|
+
self.helpButton.clicked.connect(self.__show_github_token_help)
|
19
|
+
|
20
|
+
def accept(self):
|
21
|
+
PluginUtils.set_github_token(self.githubToken_lineEdit.text())
|
22
|
+
super().accept()
|
23
|
+
|
24
|
+
def __show_github_token_help(self):
|
25
|
+
QMessageBox.information(
|
26
|
+
self,
|
27
|
+
"GitHub Access Token Help",
|
28
|
+
"<b>GitHub Access Token</b><br>"
|
29
|
+
"Oqtopus needs to download release data from GitHub to work properly. "
|
30
|
+
"GitHub limits the number of requests that can be made without authentication. "
|
31
|
+
"A personal access token is required to access private repositories or to increase API rate limits.<br><br>"
|
32
|
+
"To generate a token:<br>"
|
33
|
+
"1. Go to <a href='https://github.com/settings/tokens'>GitHub Personal Access Tokens</a>.<br>"
|
34
|
+
"2. Click <b>Generate new token</b>.<br>"
|
35
|
+
"3. Select the <code>repo</code> scope for most operations.<br>"
|
36
|
+
"4. Copy and paste the generated token here.",
|
37
|
+
)
|
oqtopus/oqtopus.py
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
import sys
|
2
|
+
import types
|
3
|
+
from pathlib import Path
|
4
|
+
|
5
|
+
import yaml
|
6
|
+
|
7
|
+
# Create fake qgis.PyQt modules that point to PyQt5 modules
|
8
|
+
try:
|
9
|
+
pyqt_core = __import__("PyQt6.QtCore", fromlist=[""])
|
10
|
+
pyqt_gui = __import__("PyQt6.QtGui", fromlist=[""])
|
11
|
+
pyqt_network = __import__("PyQt6.QtNetwork", fromlist=[""])
|
12
|
+
pyqt_widgets = __import__("PyQt6.QtWidgets", fromlist=[""])
|
13
|
+
pyqt_uic = __import__("PyQt6.uic", fromlist=[""])
|
14
|
+
except ModuleNotFoundError:
|
15
|
+
pyqt_core = __import__("PyQt5.QtCore", fromlist=[""])
|
16
|
+
pyqt_gui = __import__("PyQt5.QtGui", fromlist=[""])
|
17
|
+
pyqt_network = __import__("PyQt5.QtNetwork", fromlist=[""])
|
18
|
+
pyqt_widgets = __import__("PyQt5.QtWidgets", fromlist=[""])
|
19
|
+
pyqt_uic = __import__("PyQt5.uic", fromlist=[""])
|
20
|
+
|
21
|
+
# Create the qgis, qgis.PyQt, and submodules in sys.modules
|
22
|
+
qgis = types.ModuleType("qgis")
|
23
|
+
pyqt = types.ModuleType("qgis.PyQt")
|
24
|
+
pyqt.QtCore = pyqt_core
|
25
|
+
pyqt.QtGui = pyqt_gui
|
26
|
+
pyqt.QtNetwork = pyqt_network
|
27
|
+
pyqt.QtWidgets = pyqt_widgets
|
28
|
+
pyqt.uic = pyqt_uic
|
29
|
+
|
30
|
+
qgis.PyQt = pyqt
|
31
|
+
sys.modules["qgis"] = qgis
|
32
|
+
sys.modules["qgis.PyQt"] = pyqt
|
33
|
+
sys.modules["qgis.PyQt.QtCore"] = pyqt_core
|
34
|
+
sys.modules["qgis.PyQt.QtGui"] = pyqt_gui
|
35
|
+
sys.modules["qgis.PyQt.QtNetwork"] = pyqt_network
|
36
|
+
sys.modules["qgis.PyQt.QtWidgets"] = pyqt_widgets
|
37
|
+
sys.modules["qgis.PyQt.uic"] = pyqt_uic
|
38
|
+
|
39
|
+
from qgis.PyQt.QtGui import QIcon # noqa: E402
|
40
|
+
|
41
|
+
from .core.modules_config import ModulesConfig # noqa: E402
|
42
|
+
from .gui.main_dialog import MainDialog # noqa: E402
|
43
|
+
from .utils.plugin_utils import PluginUtils # noqa: E402
|
44
|
+
|
45
|
+
|
46
|
+
def main():
|
47
|
+
app = pyqt_widgets.QApplication(sys.argv)
|
48
|
+
icon = QIcon("oqtopus/icons/oqtopus-logo.png")
|
49
|
+
app.setWindowIcon(icon)
|
50
|
+
|
51
|
+
PluginUtils.init_logger()
|
52
|
+
|
53
|
+
conf_path = Path(__file__).parent / "default_config.yaml"
|
54
|
+
|
55
|
+
with conf_path.open() as f:
|
56
|
+
data = yaml.safe_load(f)
|
57
|
+
modules_config = ModulesConfig(**data)
|
58
|
+
|
59
|
+
dialog = MainDialog(modules_config)
|
60
|
+
dialog.setWindowIcon(icon)
|
61
|
+
dialog.show()
|
62
|
+
|
63
|
+
sys.exit(app.exec())
|
64
|
+
|
65
|
+
|
66
|
+
if __name__ == "__main__":
|
67
|
+
main()
|
@@ -0,0 +1,184 @@
|
|
1
|
+
from pathlib import Path
|
2
|
+
|
3
|
+
import yaml
|
4
|
+
from qgis.PyQt.QtGui import QIcon
|
5
|
+
from qgis.PyQt.QtWidgets import QAction, QApplication
|
6
|
+
|
7
|
+
from oqtopus.core.modules_config import ModulesConfig
|
8
|
+
|
9
|
+
from .gui.about_dialog import AboutDialog
|
10
|
+
from .gui.main_dialog import MainDialog
|
11
|
+
from .utils.plugin_utils import PluginUtils, logger
|
12
|
+
|
13
|
+
|
14
|
+
class OqtopusPlugin:
|
15
|
+
|
16
|
+
def __init__(self, iface):
|
17
|
+
"""Constructor.
|
18
|
+
|
19
|
+
:param iface: An interface instance that will be passed to this class
|
20
|
+
which provides the hook by which you can manipulate the QGIS
|
21
|
+
application at run time.
|
22
|
+
:type iface: QgsInterface
|
23
|
+
"""
|
24
|
+
# Save reference to the QGIS interface
|
25
|
+
self.iface = iface
|
26
|
+
self.canvas = iface.mapCanvas()
|
27
|
+
|
28
|
+
self.__version__ = PluginUtils.get_plugin_version()
|
29
|
+
|
30
|
+
PluginUtils.init_logger()
|
31
|
+
|
32
|
+
logger.info("")
|
33
|
+
logger.info(f"Starting {PluginUtils.PLUGIN_NAME} plugin version {self.__version__}")
|
34
|
+
|
35
|
+
self.actions = []
|
36
|
+
self.main_menu_name = self.tr(f"&{PluginUtils.PLUGIN_NAME}")
|
37
|
+
|
38
|
+
conf_path = Path(__file__).parent / "default_config.yaml"
|
39
|
+
|
40
|
+
with conf_path.open() as f:
|
41
|
+
data = yaml.safe_load(f)
|
42
|
+
self.modules_config = ModulesConfig(**data)
|
43
|
+
|
44
|
+
# noinspection PyMethodMayBeStatic
|
45
|
+
def tr(self, source_text):
|
46
|
+
"""
|
47
|
+
This does not inherit from QObject but for the translation to work
|
48
|
+
:rtype : unicode
|
49
|
+
:param source_text: The text to translate
|
50
|
+
:return: The translated text
|
51
|
+
"""
|
52
|
+
# noinspection PyTypeChecker,PyArgumentList,PyCallByClass
|
53
|
+
return QApplication.translate("OqtopusPlugin", source_text)
|
54
|
+
|
55
|
+
def add_action(
|
56
|
+
self,
|
57
|
+
icon_path,
|
58
|
+
text,
|
59
|
+
callback,
|
60
|
+
enabled_flag=True,
|
61
|
+
add_to_menu=True,
|
62
|
+
add_to_toolbar=True,
|
63
|
+
status_tip=None,
|
64
|
+
whats_this=None,
|
65
|
+
parent=None,
|
66
|
+
):
|
67
|
+
"""Add a toolbar icon to the toolbar.
|
68
|
+
|
69
|
+
:param icon_path: Path to the icon for this action. Can be a resource
|
70
|
+
path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
|
71
|
+
:type icon_path: str
|
72
|
+
|
73
|
+
:param text: Text that should be shown in menu items for this action.
|
74
|
+
:type text: str
|
75
|
+
|
76
|
+
:param callback: Function to be called when the action is triggered.
|
77
|
+
:type callback: function
|
78
|
+
|
79
|
+
:param enabled_flag: A flag indicating if the action should be enabled
|
80
|
+
by default. Defaults to True.
|
81
|
+
:type enabled_flag: bool
|
82
|
+
|
83
|
+
:param add_to_menu: Flag indicating whether the action should also
|
84
|
+
be added to the menu. Defaults to True.
|
85
|
+
:type add_to_menu: bool
|
86
|
+
|
87
|
+
:param add_to_toolbar: Flag indicating whether the action should also
|
88
|
+
be added to the toolbar. Defaults to True.
|
89
|
+
:type add_to_toolbar: bool
|
90
|
+
|
91
|
+
:param status_tip: Optional text to show in a popup when mouse pointer
|
92
|
+
hovers over the action.
|
93
|
+
:type status_tip: str
|
94
|
+
|
95
|
+
:param parent: Parent widget for the new action. Defaults None.
|
96
|
+
:type parent: QWidget
|
97
|
+
|
98
|
+
:param whats_this: Optional text to show in the status bar when the
|
99
|
+
mouse pointer hovers over the action.
|
100
|
+
|
101
|
+
:returns: The action that was created. Note that the action is also
|
102
|
+
added to self.actions list.
|
103
|
+
:rtype: QAction
|
104
|
+
"""
|
105
|
+
|
106
|
+
icon = QIcon(icon_path)
|
107
|
+
action = QAction(icon, text, parent)
|
108
|
+
action.triggered.connect(callback)
|
109
|
+
action.setEnabled(enabled_flag)
|
110
|
+
|
111
|
+
if status_tip is not None:
|
112
|
+
action.setStatusTip(status_tip)
|
113
|
+
|
114
|
+
if whats_this is not None:
|
115
|
+
action.setWhatsThis(whats_this)
|
116
|
+
|
117
|
+
if add_to_toolbar:
|
118
|
+
# Adds plugin icon to Plugins toolbar
|
119
|
+
self.iface.addToolBarIcon(action)
|
120
|
+
|
121
|
+
if add_to_menu:
|
122
|
+
self.iface.addPluginToMenu(self.main_menu_name, action)
|
123
|
+
|
124
|
+
self.actions.append(action)
|
125
|
+
|
126
|
+
return action
|
127
|
+
|
128
|
+
def initGui(self):
|
129
|
+
"""Create the menu entries and toolbar icons inside the QGIS GUI."""
|
130
|
+
self.add_action(
|
131
|
+
icon_path=PluginUtils.get_plugin_icon_path("oqtopus-logo.png"),
|
132
|
+
text=self.tr("Show &main dialog"),
|
133
|
+
callback=self.show_main_dialog,
|
134
|
+
parent=self.iface.mainWindow(),
|
135
|
+
)
|
136
|
+
self.add_action(
|
137
|
+
icon_path=None,
|
138
|
+
text=self.tr("Show &log folder"),
|
139
|
+
callback=self.show_logs_folder,
|
140
|
+
parent=self.iface.mainWindow(),
|
141
|
+
add_to_toolbar=False,
|
142
|
+
)
|
143
|
+
self.add_action(
|
144
|
+
icon_path=PluginUtils.get_plugin_icon_path("oqtopus-logo.png"),
|
145
|
+
text=self.tr("&About"),
|
146
|
+
callback=self.show_about_dialog,
|
147
|
+
parent=self.iface.mainWindow(),
|
148
|
+
add_to_toolbar=False,
|
149
|
+
)
|
150
|
+
|
151
|
+
self._get_main_menu_action().setIcon(
|
152
|
+
PluginUtils.get_plugin_icon("oqtopus-logo.png"),
|
153
|
+
)
|
154
|
+
|
155
|
+
def unload(self):
|
156
|
+
"""Removes the plugin menu item and icon from QGIS GUI."""
|
157
|
+
for action in self.actions:
|
158
|
+
self.iface.removePluginMenu(self.main_menu_name, action)
|
159
|
+
self.iface.removeToolBarIcon(action)
|
160
|
+
|
161
|
+
def show_main_dialog(self):
|
162
|
+
main_dialog = MainDialog(self.modules_config, self.iface.mainWindow())
|
163
|
+
main_dialog.exec()
|
164
|
+
|
165
|
+
def show_logs_folder(self):
|
166
|
+
PluginUtils.open_logs_folder()
|
167
|
+
|
168
|
+
def show_about_dialog(self):
|
169
|
+
about_dialog = AboutDialog(self.iface.mainWindow())
|
170
|
+
about_dialog.exec()
|
171
|
+
|
172
|
+
def _get_main_menu_action(self):
|
173
|
+
actions = self.iface.pluginMenu().actions()
|
174
|
+
result_actions = [action for action in actions if action.text() == self.main_menu_name]
|
175
|
+
|
176
|
+
# OSX does not support & in the menu title
|
177
|
+
if not result_actions:
|
178
|
+
result_actions = [
|
179
|
+
action
|
180
|
+
for action in actions
|
181
|
+
if action.text() == self.main_menu_name.replace("&", "")
|
182
|
+
]
|
183
|
+
|
184
|
+
return result_actions[0]
|
oqtopus/ui/__init__.py
ADDED
File without changes
|
File without changes
|
@@ -0,0 +1,172 @@
|
|
1
|
+
"""
|
2
|
+
/***************************************************************************
|
3
|
+
Plugin Utils
|
4
|
+
-------------------
|
5
|
+
begin : 28.4.2018
|
6
|
+
copyright : (C) 2018 by OPENGIS.ch
|
7
|
+
email : matthias@opengis.ch
|
8
|
+
***************************************************************************/
|
9
|
+
|
10
|
+
/***************************************************************************
|
11
|
+
* *
|
12
|
+
* This program is free software; you can redistribute it and/or modify *
|
13
|
+
* it under the terms of the GNU General Public License as published by *
|
14
|
+
* the Free Software Foundation; either version 2 of the License, or *
|
15
|
+
* (at your option) any later version. *
|
16
|
+
* *
|
17
|
+
***************************************************************************/
|
18
|
+
"""
|
19
|
+
|
20
|
+
import logging
|
21
|
+
import os
|
22
|
+
from logging import LogRecord
|
23
|
+
from logging.handlers import TimedRotatingFileHandler
|
24
|
+
|
25
|
+
from qgis.PyQt.QtCore import (
|
26
|
+
QDir,
|
27
|
+
QFileInfo,
|
28
|
+
QObject,
|
29
|
+
QSettings,
|
30
|
+
QStandardPaths,
|
31
|
+
QUrl,
|
32
|
+
pyqtSignal,
|
33
|
+
)
|
34
|
+
from qgis.PyQt.QtGui import QColor, QDesktopServices, QIcon
|
35
|
+
from qgis.PyQt.uic import loadUiType
|
36
|
+
|
37
|
+
logger = logging.getLogger("oqtopus")
|
38
|
+
|
39
|
+
|
40
|
+
class PluginUtils:
|
41
|
+
|
42
|
+
PLUGIN_NAME = "Oqtopus"
|
43
|
+
|
44
|
+
logsDirectory = ""
|
45
|
+
|
46
|
+
COLOR_GREEN = QColor(12, 167, 137)
|
47
|
+
COLOR_WARNING = QColor(255, 165, 0)
|
48
|
+
|
49
|
+
@staticmethod
|
50
|
+
def plugin_root_path():
|
51
|
+
"""
|
52
|
+
Returns the root path of the plugin
|
53
|
+
"""
|
54
|
+
return os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir))
|
55
|
+
|
56
|
+
@staticmethod
|
57
|
+
def plugin_temp_path():
|
58
|
+
plugin_basename = PluginUtils.plugin_root_path().split(os.sep)[-1]
|
59
|
+
|
60
|
+
plugin_temp_dir = os.path.join(
|
61
|
+
QStandardPaths.writableLocation(QStandardPaths.StandardLocation.TempLocation),
|
62
|
+
plugin_basename,
|
63
|
+
)
|
64
|
+
if not os.path.exists(plugin_temp_dir):
|
65
|
+
os.makedirs(plugin_temp_dir)
|
66
|
+
|
67
|
+
return plugin_temp_dir
|
68
|
+
|
69
|
+
@staticmethod
|
70
|
+
def get_plugin_icon_path(icon_filename):
|
71
|
+
return os.path.join(PluginUtils.plugin_root_path(), "icons", icon_filename)
|
72
|
+
|
73
|
+
@staticmethod
|
74
|
+
def get_plugin_icon(icon_filename):
|
75
|
+
return QIcon(PluginUtils.get_plugin_icon_path(icon_filename=icon_filename))
|
76
|
+
|
77
|
+
@staticmethod
|
78
|
+
def get_ui_class(ui_file):
|
79
|
+
"""Get UI Python class from .ui file.
|
80
|
+
Can be filename.ui or subdirectory/filename.ui
|
81
|
+
:param ui_file: The file of the ui in svir.ui
|
82
|
+
:type ui_file: str
|
83
|
+
"""
|
84
|
+
os.path.sep.join(ui_file.split("/"))
|
85
|
+
ui_file_path = os.path.abspath(os.path.join(PluginUtils.plugin_root_path(), "ui", ui_file))
|
86
|
+
return loadUiType(ui_file_path)[0]
|
87
|
+
|
88
|
+
@staticmethod
|
89
|
+
def get_metadata_file_path():
|
90
|
+
return os.path.join(PluginUtils.plugin_root_path(), "metadata.txt")
|
91
|
+
|
92
|
+
@staticmethod
|
93
|
+
def get_plugin_version():
|
94
|
+
ini_text = QSettings(PluginUtils.get_metadata_file_path(), QSettings.Format.IniFormat)
|
95
|
+
return ini_text.value("version")
|
96
|
+
|
97
|
+
@staticmethod
|
98
|
+
def init_logger():
|
99
|
+
PluginUtils.logsDirectory = f"{PluginUtils.plugin_root_path()}/logs"
|
100
|
+
|
101
|
+
directory = QDir(PluginUtils.logsDirectory)
|
102
|
+
if not directory.exists():
|
103
|
+
directory.mkpath(PluginUtils.logsDirectory)
|
104
|
+
|
105
|
+
if directory.exists():
|
106
|
+
logfile = QFileInfo(directory, "Oqtopus.log")
|
107
|
+
|
108
|
+
# Handler for files rotation, create one log per day
|
109
|
+
rotationHandler = TimedRotatingFileHandler(
|
110
|
+
logfile.filePath(), when="midnight", backupCount=10
|
111
|
+
)
|
112
|
+
|
113
|
+
# Configure logging
|
114
|
+
logging.basicConfig(
|
115
|
+
level=logging.DEBUG,
|
116
|
+
format="%(asctime)s %(levelname)-7s %(message)s",
|
117
|
+
handlers=[rotationHandler],
|
118
|
+
)
|
119
|
+
else:
|
120
|
+
logger.error(f"Can't create log files directory '{PluginUtils.logsDirectory}'.")
|
121
|
+
|
122
|
+
@staticmethod
|
123
|
+
def open_logs_folder():
|
124
|
+
print(f"Opening logs folder {PluginUtils.logsDirectory}")
|
125
|
+
QDesktopServices.openUrl(QUrl.fromLocalFile(PluginUtils.logsDirectory))
|
126
|
+
|
127
|
+
@staticmethod
|
128
|
+
def open_log_file():
|
129
|
+
log_file_path = os.path.join(PluginUtils.logsDirectory, "Oqtopus.log")
|
130
|
+
if os.path.exists(log_file_path):
|
131
|
+
QDesktopServices.openUrl(QUrl.fromLocalFile(log_file_path))
|
132
|
+
else:
|
133
|
+
logger.error(f"Log file '{log_file_path}' does not exist.")
|
134
|
+
|
135
|
+
@staticmethod
|
136
|
+
def get_github_token():
|
137
|
+
settings = QSettings()
|
138
|
+
return settings.value("oqtopus/github_token", type=str)
|
139
|
+
|
140
|
+
@staticmethod
|
141
|
+
def set_github_token(token: str):
|
142
|
+
settings = QSettings()
|
143
|
+
settings.setValue("oqtopus/github_token", token)
|
144
|
+
|
145
|
+
@staticmethod
|
146
|
+
def get_github_headers():
|
147
|
+
token = PluginUtils.get_github_token()
|
148
|
+
headers = {}
|
149
|
+
if token:
|
150
|
+
headers["Authorization"] = f"token {token}"
|
151
|
+
return headers
|
152
|
+
|
153
|
+
|
154
|
+
class LoggingBridge(logging.Handler, QObject):
|
155
|
+
|
156
|
+
loggedLine = pyqtSignal(LogRecord, str)
|
157
|
+
|
158
|
+
def __init__(self, level=logging.NOTSET, excluded_modules=[]):
|
159
|
+
QObject.__init__(self)
|
160
|
+
logging.Handler.__init__(self, level)
|
161
|
+
|
162
|
+
self.formatter = logging.Formatter("%(asctime)s - %(levelname)s - %(name)s - %(message)s")
|
163
|
+
|
164
|
+
self.excluded_modules = excluded_modules
|
165
|
+
|
166
|
+
def filter(self, record):
|
167
|
+
return record.name not in self.excluded_modules
|
168
|
+
|
169
|
+
def emit(self, record):
|
170
|
+
log_entry = self.format(record)
|
171
|
+
print(log_entry)
|
172
|
+
self.loggedLine.emit(record, log_entry)
|