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.
@@ -0,0 +1,210 @@
1
+ # -----------------------------------------------------------
2
+ #
3
+ # Profile
4
+ # Copyright (C) 2025 Damiano Lombardi
5
+ # -----------------------------------------------------------
6
+ #
7
+ # licensed under the terms of GNU GPL 2
8
+ #
9
+ # This program is free software; you can redistribute it and/or modify
10
+ # it under the terms of the GNU General Public License as published by
11
+ # the Free Software Foundation; either version 2 of the License, or
12
+ # (at your option) any later version.
13
+ #
14
+ # This program is distributed in the hope that it will be useful,
15
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ # GNU General Public License for more details.
18
+ #
19
+ # You should have received a copy of the GNU General Public License along
20
+ # with this program; if not, print to the Free Software Foundation, Inc.,
21
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22
+ #
23
+ # ---------------------------------------------------------------------
24
+
25
+ import psycopg
26
+ from pgserviceparser import service_config as pgserviceparser_service_config
27
+ from pgserviceparser import service_names as pgserviceparser_service_names
28
+ from pgserviceparser import write_service as pgserviceparser_write_service
29
+ from qgis.PyQt.QtCore import Qt
30
+ from qgis.PyQt.QtWidgets import QDialog, QMessageBox
31
+
32
+ from ..utils.plugin_utils import PluginUtils, logger
33
+ from ..utils.qt_utils import OverrideCursor
34
+
35
+ DIALOG_UI = PluginUtils.get_ui_class("database_create_dialog.ui")
36
+
37
+
38
+ class DatabaseCreateDialog(QDialog, DIALOG_UI):
39
+ def __init__(self, selected_service=None, parent=None):
40
+ QDialog.__init__(self, parent)
41
+ self.setupUi(self)
42
+
43
+ self.existingService_comboBox.clear()
44
+ for service_name in pgserviceparser_service_names():
45
+ self.existingService_comboBox.addItem(service_name)
46
+
47
+ if selected_service:
48
+ self.existingService_comboBox.setCurrentText(selected_service)
49
+
50
+ self.existingService_comboBox.currentIndexChanged.connect(self._serviceChanged)
51
+
52
+ self.enterManually_radioButton.toggled.connect(self._enterManuallyToggled)
53
+
54
+ self.parameters_ssl_comboBox.clear()
55
+ self.parameters_ssl_comboBox.addItem("Not set", None)
56
+ notSetFont = self.parameters_ssl_comboBox.font()
57
+ notSetFont.setItalic(True)
58
+ self.parameters_ssl_comboBox.setItemData(0, notSetFont, Qt.ItemDataRole.FontRole)
59
+ self.parameters_ssl_comboBox.addItem("disable", "disable")
60
+ self.parameters_ssl_comboBox.addItem("allow", "allow")
61
+ self.parameters_ssl_comboBox.addItem("prefer", "prefer")
62
+ self.parameters_ssl_comboBox.addItem("require", "require")
63
+ self.parameters_ssl_comboBox.addItem("verify-ca", "verify-ca")
64
+ self.parameters_ssl_comboBox.addItem("verify-full", "verify-full")
65
+
66
+ self.database_lineEdit.textChanged.connect(self._databaseTextChanged)
67
+
68
+ self.buttonBox.accepted.connect(self._accept)
69
+
70
+ if self.existingService_comboBox.count() > 0:
71
+ self._serviceChanged()
72
+
73
+ def created_service_name(self):
74
+ return self.service_lineEdit.text()
75
+
76
+ def _serviceChanged(self):
77
+ service_name = self.existingService_comboBox.currentText()
78
+ service_config = pgserviceparser_service_config(service_name)
79
+
80
+ service_host = service_config.get("host", None)
81
+ service_port = service_config.get("port", None)
82
+ service_ssl = service_config.get("sslmode", None)
83
+ service_dbname = service_config.get("dbname", None)
84
+ service_user = service_config.get("user", None)
85
+ service_password = service_config.get("password", None)
86
+
87
+ self.parameters_host_lineEdit.setText(service_host)
88
+ self.parameters_port_lineEdit.setText(service_port)
89
+
90
+ parameter_ssl_index = self.parameters_ssl_comboBox.findData(service_ssl)
91
+ self.parameters_ssl_comboBox.setCurrentIndex(parameter_ssl_index)
92
+ self.parameters_user_lineEdit.setText(service_user)
93
+ self.parameters_password_lineEdit.setText(service_password)
94
+
95
+ self.database_lineEdit.setText(service_dbname)
96
+
97
+ def _enterManuallyToggled(self, checked):
98
+ self.parameters_frame.setEnabled(checked)
99
+
100
+ def _databaseTextChanged(self, text):
101
+ self.database_label.setText(text)
102
+
103
+ def _accept(self):
104
+ service_name = self.created_service_name()
105
+
106
+ if service_name == "":
107
+ QMessageBox.critical(self, "Error", "Please enter a service name.")
108
+ return
109
+
110
+ # Check if the service name is already in use
111
+ if service_name in pgserviceparser_service_names():
112
+ QMessageBox.critical(
113
+ self, "Error", self.tr(f"Service name '{service_name}' already exists.")
114
+ )
115
+ return
116
+
117
+ new_database_name = self.database_lineEdit.text()
118
+ if new_database_name == "":
119
+ QMessageBox.critical(self, "Error", "Please enter a database name.")
120
+ return
121
+
122
+ database_connection = None
123
+ try:
124
+ with OverrideCursor(Qt.CursorShape.WaitCursor):
125
+ database_connection = psycopg.connect(**self._get_connection_parameters())
126
+ database_connection.autocommit = True
127
+ with database_connection.cursor() as cursor:
128
+ cursor.execute(
129
+ psycopg.sql.SQL("CREATE DATABASE {}").format(
130
+ psycopg.sql.Identifier(new_database_name)
131
+ )
132
+ )
133
+
134
+ except Exception as e:
135
+ errorText = self.tr(f"Error creating the new database:\n{e}.")
136
+ logger.error(errorText)
137
+ QMessageBox.critical(self, "Error", errorText)
138
+ return
139
+
140
+ finally:
141
+ if database_connection:
142
+ database_connection.close()
143
+
144
+ # Proceed with writing the new service configuration
145
+ service_settings = self._get_new_service_settings()
146
+
147
+ try:
148
+ pgserviceparser_write_service(
149
+ service_name=service_name,
150
+ settings=service_settings,
151
+ create_if_not_found=True,
152
+ )
153
+ except Exception as e:
154
+ errorText = self.tr(f"Error writing the new service configuration:\n{e}.")
155
+ logger.error(errorText)
156
+ QMessageBox.critical(self, "Error", errorText)
157
+ return
158
+
159
+ super().accept()
160
+
161
+ def _get_connection_parameters(self):
162
+ """
163
+ Returns a dictionary of connection parameters suitable for psycopg.connect().
164
+ Uses manual input if 'Enter manually' is checked, otherwise uses the selected service name.
165
+ """
166
+ settings = dict()
167
+ if self.enterManually_radioButton.isChecked():
168
+ settings.update(self._get_manual_connection_parameters())
169
+ else:
170
+ # Use the selected service name
171
+ service_name = self.existingService_comboBox.currentText()
172
+ if service_name:
173
+ settings["service"] = service_name
174
+
175
+ return settings
176
+
177
+ def _get_new_service_settings(self):
178
+ settings = dict()
179
+
180
+ if self.enterManually_radioButton.isChecked():
181
+ settings.update(self._get_manual_connection_parameters())
182
+ else:
183
+ # Copy settings from the selected existing service
184
+ service_name = self.existingService_comboBox.currentText()
185
+ existing_settings = pgserviceparser_service_config(service_name)
186
+ settings.update(existing_settings)
187
+ # Overwrite dbname with the new database name
188
+ if self.database_lineEdit.text():
189
+ settings["dbname"] = self.database_lineEdit.text()
190
+
191
+ return settings
192
+
193
+ def _get_manual_connection_parameters(self):
194
+ parameters = dict()
195
+
196
+ # Collect parameters from manual input fields
197
+ if self.parameters_host_lineEdit.text():
198
+ parameters["host"] = self.parameters_host_lineEdit.text()
199
+ if self.parameters_port_lineEdit.text():
200
+ parameters["port"] = self.parameters_port_lineEdit.text()
201
+ if self.parameters_ssl_comboBox.currentData():
202
+ parameters["sslmode"] = self.parameters_ssl_comboBox.currentData()
203
+ if self.parameters_user_lineEdit.text():
204
+ parameters["user"] = self.parameters_user_lineEdit.text()
205
+ if self.parameters_password_lineEdit.text():
206
+ parameters["password"] = self.parameters_password_lineEdit.text()
207
+ if self.database_lineEdit.text():
208
+ parameters["dbname"] = self.database_lineEdit.text()
209
+
210
+ return parameters
@@ -0,0 +1,100 @@
1
+ # -----------------------------------------------------------
2
+ #
3
+ # Profile
4
+ # Copyright (C) 2025 Damiano Lombardi
5
+ # -----------------------------------------------------------
6
+ #
7
+ # licensed under the terms of GNU GPL 2
8
+ #
9
+ # This program is free software; you can redistribute it and/or modify
10
+ # it under the terms of the GNU General Public License as published by
11
+ # the Free Software Foundation; either version 2 of the License, or
12
+ # (at your option) any later version.
13
+ #
14
+ # This program is distributed in the hope that it will be useful,
15
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ # GNU General Public License for more details.
18
+ #
19
+ # You should have received a copy of the GNU General Public License along
20
+ # with this program; if not, print to the Free Software Foundation, Inc.,
21
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22
+ #
23
+ # ---------------------------------------------------------------------
24
+
25
+ import psycopg
26
+ from pgserviceparser import service_config as pgserviceparser_service_config
27
+ from qgis.PyQt.QtCore import Qt
28
+ from qgis.PyQt.QtWidgets import QDialog, QMessageBox
29
+
30
+ from ..utils.plugin_utils import PluginUtils
31
+ from ..utils.qt_utils import OverrideCursor
32
+
33
+ DIALOG_UI = PluginUtils.get_ui_class("database_duplicate_dialog.ui")
34
+
35
+
36
+ class DatabaseDuplicateDialog(QDialog, DIALOG_UI):
37
+ def __init__(self, selected_service=None, parent=None):
38
+ QDialog.__init__(self, parent)
39
+ self.setupUi(self)
40
+
41
+ self.existingService_label.setText(selected_service)
42
+
43
+ self.__existing_service_config = pgserviceparser_service_config(selected_service)
44
+ self.existingDatabase_label.setText(self.__existing_service_config.get("dbname", ""))
45
+
46
+ self.buttonBox.accepted.connect(self._accept)
47
+
48
+ def _accept(self):
49
+
50
+ if self.newDatabase_lineEdit.text() == "":
51
+ QMessageBox.critical(self, "Error", "Please enter a database name.")
52
+ return
53
+
54
+ if self.newService_lineEdit.text() == "":
55
+ QMessageBox.critical(self, "Error", "Please enter a service name.")
56
+ return
57
+
58
+ service_name = self.existingService_label.text()
59
+ try:
60
+ database_connection = psycopg.connect(service=service_name)
61
+
62
+ except Exception as exception:
63
+ errorText = self.tr(f"Can't connect to service '{service_name}':\n{exception}.")
64
+ QMessageBox.critical(self, "Error", errorText)
65
+ return
66
+
67
+ # Duplicate the database
68
+ new_database_name = self.newDatabase_lineEdit.text()
69
+ try:
70
+ database_connection.autocommit = True
71
+ with OverrideCursor(Qt.CursorShape.WaitCursor):
72
+ with database_connection.cursor() as cursor:
73
+ cursor.execute(
74
+ f"CREATE DATABASE {new_database_name} TEMPLATE {self.__existing_service_config.get('dbname')}"
75
+ )
76
+ except psycopg.Error as e:
77
+ errorText = self.tr(f"Error duplicating database:\n{e}.")
78
+ QMessageBox.critical(self, "Error", errorText)
79
+ return
80
+ finally:
81
+ database_connection.close()
82
+
83
+ # Create new service configuration
84
+ new_service_name = self.newService_lineEdit.text()
85
+ new_service_config = pgserviceparser_service_config(
86
+ new_service_name,
87
+ dbname=new_database_name,
88
+ host=self.__existing_service_config.get("host", ""),
89
+ port=self.__existing_service_config.get("port", ""),
90
+ user=self.__existing_service_config.get("user", ""),
91
+ password=self.__existing_service_config.get("password", ""),
92
+ )
93
+ try:
94
+ pgserviceparser_service_config.write_service_config(new_service_config)
95
+ except Exception as e:
96
+ errorText = self.tr(f"Error writing new service configuration:\n{e}.")
97
+ QMessageBox.critical(self, "Error", errorText)
98
+ return
99
+
100
+ super().accept()
@@ -0,0 +1,177 @@
1
+ import logging
2
+
3
+ from qgis.PyQt.QtCore import QAbstractItemModel, QModelIndex, QSortFilterProxyModel, Qt
4
+ from qgis.PyQt.QtWidgets import QAbstractItemView, QApplication, QStyle, QWidget
5
+
6
+ from ..utils.plugin_utils import LoggingBridge, PluginUtils
7
+
8
+ DIALOG_UI = PluginUtils.get_ui_class("logs_widget.ui")
9
+
10
+
11
+ COLUMNS = ["Level", "Module", "Message"]
12
+
13
+
14
+ class LogModel(QAbstractItemModel):
15
+ def __init__(self, parent=None):
16
+ QAbstractItemModel.__init__(self, parent)
17
+ self.logs = []
18
+
19
+ def add_log(self, log):
20
+ self.beginInsertRows(QModelIndex(), len(self.logs), len(self.logs))
21
+ self.logs.append(log)
22
+ self.endInsertRows()
23
+
24
+ def headerData(self, section: int, orientation: Qt.Orientation, role: Qt.ItemDataRole = None):
25
+ if role == Qt.ItemDataRole.DisplayRole and orientation == Qt.Orientation.Horizontal:
26
+ return COLUMNS[section]
27
+ return None
28
+
29
+ def rowCount(self, parent=None):
30
+ return len(self.logs)
31
+
32
+ def columnCount(self, parent=None):
33
+ return len(COLUMNS)
34
+
35
+ def data(self, index: QModelIndex, role: Qt.ItemDataRole = None):
36
+ if not index.isValid() or role != Qt.ItemDataRole.DisplayRole:
37
+ return None
38
+ if (
39
+ index.row() < 0
40
+ or index.row() >= len(self.logs)
41
+ or index.column() < 0
42
+ or index.column() >= len(COLUMNS)
43
+ ):
44
+ return None
45
+ log = self.logs[index.row()]
46
+ return log[COLUMNS[index.column()]]
47
+
48
+ def index(self, row: int, column: int, parent=None):
49
+ if row < 0 or row >= len(self.logs) or column < 0 or column >= len(COLUMNS):
50
+ return QModelIndex()
51
+ return self.createIndex(row, column)
52
+
53
+ def parent(self, index: QModelIndex):
54
+ if not index.isValid():
55
+ return QModelIndex()
56
+ return QModelIndex()
57
+
58
+ def flags(self, index: QModelIndex):
59
+ return (
60
+ Qt.ItemFlag.ItemIsEnabled
61
+ | Qt.ItemFlag.ItemIsSelectable
62
+ | Qt.ItemFlag.ItemNeverHasChildren
63
+ )
64
+
65
+
66
+ class LogFilterProxyModel(QSortFilterProxyModel):
67
+ LEVELS = ["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"]
68
+
69
+ def __init__(self, parent=None):
70
+ super().__init__(parent)
71
+ self.level_filter = None
72
+
73
+ def setLevelFilter(self, level):
74
+ self.level_filter = level
75
+ self.invalidateFilter()
76
+
77
+ def filterAcceptsRow(self, source_row, source_parent):
78
+ model = self.sourceModel()
79
+ index_level = model.index(source_row, 0, source_parent) # Level column
80
+ index_message = model.index(source_row, 2, source_parent) # Message column
81
+ index_module = model.index(source_row, 1, source_parent) # Module column
82
+ # Level filter (show entries with at least the selected level)
83
+ if self.level_filter and self.level_filter != "ALL":
84
+ level = model.data(index_level, Qt.ItemDataRole.DisplayRole)
85
+ try:
86
+ filter_idx = self.LEVELS.index(self.level_filter)
87
+ level_idx = self.LEVELS.index(level)
88
+ if level_idx < filter_idx:
89
+ return False
90
+ except ValueError:
91
+ return False
92
+ # Text filter (from QLineEdit)
93
+ filter_text = self.filterRegularExpression().pattern()
94
+ if filter_text:
95
+ msg = model.data(index_message, Qt.ItemDataRole.DisplayRole) or ""
96
+ mod = model.data(index_module, Qt.ItemDataRole.DisplayRole) or ""
97
+ if filter_text.lower() not in msg.lower() and filter_text.lower() not in mod.lower():
98
+ return False
99
+ return True
100
+
101
+
102
+ class LogsWidget(QWidget, DIALOG_UI):
103
+
104
+ def __init__(self, parent=None):
105
+ QWidget.__init__(self, parent)
106
+ self.setupUi(self)
107
+ self.loggingBridge = LoggingBridge(
108
+ level=logging.NOTSET, excluded_modules=["urllib3.connectionpool"]
109
+ )
110
+ self.logs_model = LogModel(self)
111
+
112
+ # Use custom proxy model
113
+ self.proxy_model = LogFilterProxyModel(self)
114
+ self.proxy_model.setSourceModel(self.logs_model)
115
+ self.proxy_model.setFilterCaseSensitivity(Qt.CaseSensitivity.CaseInsensitive)
116
+ self.proxy_model.setFilterKeyColumn(-1)
117
+
118
+ self.logs_treeView.setModel(self.proxy_model)
119
+ self.logs_treeView.setAlternatingRowColors(True)
120
+ self.logs_treeView.setSelectionBehavior(QAbstractItemView.SelectionBehavior.SelectRows)
121
+ self.logs_treeView.setSelectionMode(QAbstractItemView.SelectionMode.SingleSelection)
122
+ self.logs_treeView.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
123
+ self.loggingBridge.loggedLine.connect(self.__logged_line)
124
+ logging.getLogger().addHandler(self.loggingBridge)
125
+
126
+ self.logs_level_comboBox.addItems(
127
+ [
128
+ "DEBUG",
129
+ "INFO",
130
+ "WARNING",
131
+ "ERROR",
132
+ "CRITICAL",
133
+ ]
134
+ )
135
+ self.logs_level_comboBox.currentTextChanged.connect(self.proxy_model.setLevelFilter)
136
+ self.logs_level_comboBox.setCurrentText("INFO")
137
+
138
+ self.logs_openFile_toolButton.setIcon(
139
+ QApplication.style().standardIcon(QStyle.StandardPixmap.SP_FileIcon)
140
+ )
141
+ self.logs_openFolder_toolButton.setIcon(
142
+ QApplication.style().standardIcon(QStyle.StandardPixmap.SP_DirOpenIcon)
143
+ )
144
+ self.logs_clear_toolButton.setIcon(
145
+ QApplication.style().standardIcon(QStyle.StandardPixmap.SP_TitleBarCloseButton)
146
+ )
147
+ self.logs_openFile_toolButton.clicked.connect(self.__logsOpenFileClicked)
148
+ self.logs_openFolder_toolButton.clicked.connect(self.__logsOpenFolderClicked)
149
+ self.logs_clear_toolButton.clicked.connect(self.__logsClearClicked)
150
+ self.logs_filter_LineEdit.textChanged.connect(self.proxy_model.setFilterFixedString)
151
+
152
+ def close(self):
153
+ # uninstall the logging bridge
154
+ logging.getLogger().removeHandler(self.loggingBridge)
155
+
156
+ def __logged_line(self, record, line):
157
+
158
+ log_entry = {
159
+ "Level": record.levelname,
160
+ "Module": record.name,
161
+ "Message": record.msg,
162
+ }
163
+
164
+ self.logs_model.add_log(log_entry)
165
+
166
+ # Automatically scroll to the bottom of the logs
167
+ scroll_bar = self.logs_treeView.verticalScrollBar()
168
+ scroll_bar.setValue(scroll_bar.maximum())
169
+
170
+ def __logsOpenFileClicked(self):
171
+ PluginUtils.open_log_file()
172
+
173
+ def __logsOpenFolderClicked(self):
174
+ PluginUtils.open_logs_folder()
175
+
176
+ def __logsClearClicked(self):
177
+ self.logs_model.clear()
@@ -0,0 +1,168 @@
1
+ # -----------------------------------------------------------
2
+ #
3
+ # Profile
4
+ # Copyright (C) 2025 Damiano Lombardi
5
+ # -----------------------------------------------------------
6
+ #
7
+ # licensed under the terms of GNU GPL 2
8
+ #
9
+ # This program is free software; you can redistribute it and/or modify
10
+ # it under the terms of the GNU General Public License as published by
11
+ # the Free Software Foundation; either version 2 of the License, or
12
+ # (at your option) any later version.
13
+ #
14
+ # This program is distributed in the hope that it will be useful,
15
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
16
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
+ # GNU General Public License for more details.
18
+ #
19
+ # You should have received a copy of the GNU General Public License along
20
+ # with this program; if not, print to the Free Software Foundation, Inc.,
21
+ # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22
+ #
23
+ # ---------------------------------------------------------------------
24
+
25
+
26
+ import os
27
+ import sys
28
+
29
+ from qgis.PyQt.QtCore import QUrl
30
+ from qgis.PyQt.QtGui import QAction, QDesktopServices
31
+ from qgis.PyQt.QtWidgets import (
32
+ QDialog,
33
+ QMenuBar,
34
+ )
35
+
36
+ from ..utils.plugin_utils import PluginUtils, logger
37
+ from .about_dialog import AboutDialog
38
+ from .settings_dialog import SettingsDialog
39
+
40
+ libs_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "libs"))
41
+ if libs_path not in sys.path:
42
+ sys.path.insert(0, libs_path)
43
+
44
+ from .database_connection_widget import DatabaseConnectionWidget # noqa: E402
45
+ from .logs_widget import LogsWidget # noqa: E402
46
+ from .module_selection_widget import ModuleSelectionWidget # noqa: E402
47
+ from .module_widget import ModuleWidget # noqa: E402
48
+ from .project_widget import ProjectWidget # noqa: E402
49
+
50
+ DIALOG_UI = PluginUtils.get_ui_class("main_dialog.ui")
51
+
52
+
53
+ class MainDialog(QDialog, DIALOG_UI):
54
+
55
+ def __init__(self, modules_config, parent=None):
56
+ QDialog.__init__(self, parent)
57
+ self.setupUi(self)
58
+
59
+ self.buttonBox.rejected.connect(self.__closeDialog)
60
+ self.buttonBox.helpRequested.connect(self.__helpRequested)
61
+
62
+ # Init GUI Modules
63
+ self.__moduleSelectionWidget = ModuleSelectionWidget(modules_config, self)
64
+ self.moduleSelection_groupBox.layout().addWidget(self.__moduleSelectionWidget)
65
+
66
+ # Init GUI Database
67
+ self.__databaseConnectionWidget = DatabaseConnectionWidget(self)
68
+ self.db_groupBox.layout().addWidget(self.__databaseConnectionWidget)
69
+
70
+ # Init GUI Module Info
71
+ self.__moduleWidget = ModuleWidget(self)
72
+ self.module_tab.layout().addWidget(self.__moduleWidget)
73
+
74
+ # Init GUI Project
75
+ self.__projectWidget = ProjectWidget(self)
76
+ self.project_tab.layout().addWidget(self.__projectWidget)
77
+
78
+ # Init GUI Logs
79
+ self.__logsWidget = LogsWidget(self)
80
+ self.logs_groupBox.layout().addWidget(self.__logsWidget)
81
+
82
+ # Add menubar
83
+ self.menubar = QMenuBar(self)
84
+ # On macOS, setNativeMenuBar(False) to show the menu bar inside the dialog window
85
+ if sys.platform == "darwin":
86
+ self.menubar.setNativeMenuBar(False)
87
+ self.layout().setMenuBar(self.menubar)
88
+
89
+ # Settings action
90
+ settings_action = QAction(self.tr("Settings"), self)
91
+ settings_action.triggered.connect(self.__open_settings_dialog)
92
+
93
+ # About action
94
+ about_action = QAction(self.tr("About"), self)
95
+ about_action.triggered.connect(self.__show_about_dialog)
96
+
97
+ # Add actions to menubar
98
+ self.menubar.addAction(settings_action)
99
+ self.menubar.addAction(about_action)
100
+
101
+ self.__moduleSelectionWidget.signal_loadingStarted.connect(
102
+ self.__moduleSelection_loadingStarted
103
+ )
104
+ self.__moduleSelectionWidget.signal_loadingFinished.connect(
105
+ self.__moduleSelection_loadingFinished
106
+ )
107
+
108
+ self.__databaseConnectionWidget.signal_connectionChanged.connect(
109
+ self.__databaseConnectionWidget_connectionChanged
110
+ )
111
+
112
+ self.module_tab.setEnabled(False)
113
+ self.plugin_tab.setEnabled(False)
114
+ self.project_tab.setEnabled(False)
115
+
116
+ logger.info("Ready.")
117
+
118
+ def __closeDialog(self):
119
+ self.__moduleSelectionWidget.close()
120
+ self.__logsWidget.close()
121
+ self.accept()
122
+
123
+ def __helpRequested(self):
124
+ help_page = "https://github.com/oqtopus/Oqtopus"
125
+ logger.info(f"Opening help page {help_page}")
126
+ QDesktopServices.openUrl(QUrl(help_page))
127
+
128
+ def __open_settings_dialog(self):
129
+ dlg = SettingsDialog(self)
130
+ dlg.exec()
131
+
132
+ def __show_about_dialog(self):
133
+ dialog = AboutDialog(self)
134
+ dialog.exec()
135
+
136
+ def __moduleSelection_loadingStarted(self):
137
+ self.db_groupBox.setEnabled(False)
138
+ self.module_tab.setEnabled(False)
139
+ self.plugin_tab.setEnabled(False)
140
+ self.project_tab.setEnabled(False)
141
+
142
+ def __moduleSelection_loadingFinished(self):
143
+ self.db_groupBox.setEnabled(True)
144
+
145
+ module_package = self.__moduleSelectionWidget.getSelectedModulePackage()
146
+ if module_package is None:
147
+ return
148
+
149
+ if self.__moduleSelectionWidget.lastError() is not None:
150
+ return
151
+
152
+ self.module_tab.setEnabled(True)
153
+
154
+ if module_package.asset_plugin is not None:
155
+ self.plugin_tab.setEnabled(True)
156
+
157
+ if module_package.asset_project is not None:
158
+ self.project_tab.setEnabled(True)
159
+
160
+ self.__moduleWidget.setModulePackage(
161
+ self.__moduleSelectionWidget.getSelectedModulePackage()
162
+ )
163
+ self.__projectWidget.setModulePackage(
164
+ self.__moduleSelectionWidget.getSelectedModulePackage()
165
+ )
166
+
167
+ def __databaseConnectionWidget_connectionChanged(self):
168
+ self.__moduleWidget.setDatabaseConnection(self.__databaseConnectionWidget.getConnection())