diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index e428cd4..da82109 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -64,4 +64,3 @@ repos:
ci:
autofix_prs: true
autoupdate_schedule: quarterly
-
diff --git a/pg_service_parser/config.py b/pg_service_parser/config.py
index 65003ad..d294428 100644
--- a/pg_service_parser/config.py
+++ b/pg_service_parser/config.py
@@ -1,4 +1,3 @@
import os.path
-
DEFAULT_PG_SERVICE_PATH = os.path.expanduser("~/.pg_service.conf")
diff --git a/pg_service_parser/core/__init__.py b/pg_service_parser/core/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/pg_service_parser/core/item_models.py b/pg_service_parser/core/item_models.py
new file mode 100644
index 0000000..1419205
--- /dev/null
+++ b/pg_service_parser/core/item_models.py
@@ -0,0 +1,72 @@
+from qgis.PyQt.QtCore import QAbstractTableModel, Qt
+from qgis.PyQt.QtGui import QFont
+
+
+class ServiceConfigModel(QAbstractTableModel):
+ KEY_COL = 0
+ VALUE_COL = 1
+
+ def __init__(self, service_name, service_config):
+ super().__init__()
+ self.__service_name = service_name
+ self.__model_data = service_config
+ self.__dirty = False
+
+ def rowCount(self, parent):
+ return len(self.__model_data)
+
+ def columnCount(self, parent):
+ return 2
+
+ def data(self, index, role=Qt.DisplayRole):
+ if not index.isValid():
+ return None
+
+ key = list(self.__model_data.keys())[index.row()]
+ if role == Qt.DisplayRole:
+ if index.column() == self.KEY_COL:
+ return key
+ elif index.column() == self.VALUE_COL:
+ return self.__model_data[key]
+ elif role == Qt.EditRole and index.column() == self.VALUE_COL:
+ return self.__model_data[key]
+ elif role == Qt.FontRole and index.column() == self.KEY_COL:
+ font = QFont()
+ font.setBold(True)
+ return font
+
+ return None
+
+ def setData(self, index, value, role=Qt.EditRole) -> bool:
+ if not index.isValid():
+ return False
+
+ key = list(self.__model_data.keys())[index.row()]
+ if value != self.__model_data[key]:
+ self.__model_data[key] = value
+ self.__dirty = True
+ return True
+
+ return False
+
+ def flags(self, idx):
+ if not idx.isValid():
+ return ~Qt.ItemIsSelectable & ~Qt.ItemIsEnabled
+
+ _flags = Qt.ItemIsSelectable | Qt.ItemIsEnabled
+ if idx.column() == self.KEY_COL:
+ return _flags
+ elif idx.column() == self.VALUE_COL:
+ return _flags | Qt.ItemIsEditable
+
+ def is_dirty(self):
+ return self.__dirty
+
+ def service_config(self):
+ return self.__model_data.copy()
+
+ def service_name(self):
+ return self.__service_name
+
+ def set_not_dirty(self):
+ self.__dirty = False
diff --git a/pg_service_parser/pg_service_parser_wrapper.py b/pg_service_parser/core/pg_service_parser_wrapper.py
similarity index 76%
rename from pg_service_parser/pg_service_parser_wrapper.py
rename to pg_service_parser/core/pg_service_parser_wrapper.py
index 5b12bc7..66e3a74 100644
--- a/pg_service_parser/pg_service_parser_wrapper.py
+++ b/pg_service_parser/core/pg_service_parser_wrapper.py
@@ -1,14 +1,14 @@
import os.path
+from typing import List, Optional
import pgserviceparser
-from typing import (List,
- Optional)
def conf_path() -> str:
path = pgserviceparser.conf_path()
return path if os.path.exists(path) else None
+
def service_names(conf_file_path: Optional[str] = None) -> List[str]:
return pgserviceparser.service_names(conf_file_path)
@@ -18,9 +18,9 @@ def service_config(service_name: str, conf_file_path: Optional[str] = None) -> d
def write_service_settings(
- service_name: str,
- settings: dict,
- conf_file_path: Optional[str] = None,
+ service_name: str,
+ settings: dict,
+ conf_file_path: Optional[str] = None,
) -> bool:
"""Returns true if it could write the settings to the file.
@@ -39,13 +39,15 @@ def write_service_settings(
return False
-def create_service(service_name: str, settings: dict, conf_file_path: Optional[str] = None) -> bool:
+def create_service(
+ service_name: str, settings: dict, conf_file_path: Optional[str] = None
+) -> bool:
config = pgserviceparser.full_config(conf_file_path)
if service_name in config:
return False
config.add_section(service_name)
- with open(conf_file_path or pgserviceparser.conf_path(), 'w') as f:
+ with open(conf_file_path or pgserviceparser.conf_path(), "w") as f:
config.write(f)
if service_name in config:
@@ -54,7 +56,9 @@ def create_service(service_name: str, settings: dict, conf_file_path: Optional[s
return False
-def copy_service_settings(source_service_name: str, target_service_name: str, conf_file_path: Optional[str] = None) -> bool:
+def copy_service_settings(
+ source_service_name: str, target_service_name: str, conf_file_path: Optional[str] = None
+) -> bool:
settings = pgserviceparser.service_config(source_service_name, conf_file_path)
config = pgserviceparser.full_config(conf_file_path)
@@ -67,12 +71,17 @@ def copy_service_settings(source_service_name: str, target_service_name: str, co
return res
-if __name__ == '__main__':
+if __name__ == "__main__":
assert service_names() == []
# Add new service
- _settings = {'host': 'localhost', 'port': '5432', 'user': 'postgres', 'password': 'secret',
- 'dbname': 'qgis_test_db'}
+ _settings = {
+ "host": "localhost",
+ "port": "5432",
+ "user": "postgres",
+ "password": "secret",
+ "dbname": "qgis_test_db",
+ }
assert create_service("qgis-test", _settings)
assert service_names() == ["qgis-test"]
@@ -82,8 +91,13 @@ def copy_service_settings(source_service_name: str, target_service_name: str, co
assert service_config("qgis-demo") == _settings
# Add new service
- _settings = {'host': 'localhost', 'port': '5433', 'user': 'admin', 'password': 'secret',
- 'dbname': 'qgis_test_db2'}
+ _settings = {
+ "host": "localhost",
+ "port": "5433",
+ "user": "admin",
+ "password": "secret",
+ "dbname": "qgis_test_db2",
+ }
assert create_service("qgis-new-test", _settings)
assert service_names() == ["qgis-test", "qgis-demo", "qgis-new-test"]
assert service_config("qgis-new-test") == _settings
diff --git a/pg_service_parser/gui/dlg_pg_service.py b/pg_service_parser/gui/dlg_pg_service.py
index 7395522..484924c 100644
--- a/pg_service_parser/gui/dlg_pg_service.py
+++ b/pg_service_parser/gui/dlg_pg_service.py
@@ -1,15 +1,18 @@
-from qgis.PyQt.QtCore import (Qt,
- pyqtSlot)
-from qgis.PyQt.QtWidgets import (QDialog,
- QSizePolicy)
from qgis.gui import QgsMessageBar
-
-from pg_service_parser.pg_service_parser_wrapper import (conf_path,
- copy_service_settings,
- service_names)
+from qgis.PyQt.QtCore import Qt, pyqtSlot
+from qgis.PyQt.QtWidgets import QDialog, QMessageBox, QSizePolicy
+
+from pg_service_parser.core.item_models import ServiceConfigModel
+from pg_service_parser.core.pg_service_parser_wrapper import (
+ conf_path,
+ copy_service_settings,
+ service_config,
+ service_names,
+ write_service_settings,
+)
from pg_service_parser.utils import get_ui_class
-DIALOG_UI = get_ui_class('pg_service_dialog.ui')
+DIALOG_UI = get_ui_class("pg_service_dialog.ui")
COPY_TAB_INDEX = 0
EDIT_TAB_INDEX = 1
@@ -23,18 +26,24 @@ def __init__(self, parent):
conf_file_path = conf_path()
if not conf_file_path:
self.lblConfFile.setText("Config file not found!")
- self.lblConfFile.setToolTip("Set your PGSERVICEFILE environment variable and reopen the dialog.")
+ self.lblConfFile.setToolTip(
+ "Set your PGSERVICEFILE environment variable and reopen the dialog."
+ )
self.txtConfFile.setVisible(False)
self.tabWidget.setEnabled(False)
return
+ self.__edit_model = None
+
self.txtConfFile.setText(conf_file_path)
self.lblWarning.setVisible(False)
- self.tabWidget.setTabEnabled(EDIT_TAB_INDEX, False) # Not yet implemented
self.radOverwrite.toggled.connect(self.__update_target_controls)
self.btnCopyService.clicked.connect(self.__copy_service)
self.cboSourceService.currentIndexChanged.connect(self.__source_service_changed)
+ self.tabWidget.currentChanged.connect(self.__current_tab_changed)
+ self.cboEditService.currentIndexChanged.connect(self.__edit_service_changed)
+ self.btnUpdateService.clicked.connect(self.__update_service_clicked)
self.__initialize_copy_services()
self.__update_target_controls(True)
@@ -72,6 +81,16 @@ def __initialize_copy_services(self):
self.cboSourceService.addItems(service_names())
self.cboSourceService.setCurrentText(current_text)
+ def __initialize_edit_services(self):
+ self.__edit_model = None
+ current_text = self.cboEditService.currentText() # Remember latest currentText
+ self.cboEditService.blockSignals(True) # Avoid triggering custom slot while clearing
+ self.cboEditService.clear()
+ self.cboEditService.blockSignals(False)
+ self.cboEditService.addItems(service_names())
+ self.cboEditService.setCurrentText(current_text)
+
+ @pyqtSlot()
def __copy_service(self):
# Validations
if self.radCreate.isChecked():
@@ -79,14 +98,20 @@ def __copy_service(self):
self.bar.pushInfo("PG service", "Enter a service name and try again.")
return
elif self.txtNewService.text().strip() in service_names():
- self.bar.pushWarning("PG service", "Service name already exists! Change it and try again.")
+ self.bar.pushWarning(
+ "PG service", "Service name already exists! Change it and try again."
+ )
return
elif self.radOverwrite.isChecked():
if not self.cboTargetService.currentText():
self.bar.pushInfo("PG service", "Select a valid target service and try again.")
return
- target_service = self.cboTargetService.currentText() if self.radOverwrite.isChecked() else self.txtNewService.text().strip()
+ target_service = (
+ self.cboTargetService.currentText()
+ if self.radOverwrite.isChecked()
+ else self.txtNewService.text().strip()
+ )
if copy_service_settings(self.cboSourceService.currentText(), target_service):
self.bar.pushSuccess("PG service", f"PG service copied to '{target_service}'!")
@@ -94,3 +119,49 @@ def __copy_service(self):
self.__initialize_copy_services() # Reflect the newly added service
else:
self.bar.pushWarning("PG service", "There was a problem copying the service!")
+
+ @pyqtSlot(int)
+ def __current_tab_changed(self, index):
+ if index == COPY_TAB_INDEX:
+ # self.__initialize_copy_services()
+ pass # For now, services to be copied won't be altered in other tabs
+ elif index == EDIT_TAB_INDEX:
+ self.__initialize_edit_services()
+
+ @pyqtSlot(int)
+ def __edit_service_changed(self, index):
+ target_service = self.cboEditService.currentText()
+ if self.__edit_model and self.__edit_model.is_dirty():
+ if (
+ not QMessageBox.question(
+ self,
+ "Pending edits",
+ "There are pending edits for service '{}'. Are you sure you want to discard them?".format(
+ self.__edit_model.service_name()
+ ),
+ QMessageBox.Yes | QMessageBox.No,
+ QMessageBox.No,
+ )
+ == QMessageBox.Yes
+ ):
+
+ self.cboEditService.blockSignals(True)
+ self.cboEditService.setCurrentText(self.__edit_model.service_name())
+ self.cboEditService.blockSignals(False)
+ return
+
+ self.__edit_model = ServiceConfigModel(target_service, service_config(target_service))
+ self.tblServiceConfig.setModel(self.__edit_model)
+
+ @pyqtSlot()
+ def __update_service_clicked(self):
+ if self.__edit_model and self.__edit_model.is_dirty():
+ target_service = self.cboEditService.currentText()
+ res = write_service_settings(target_service, self.__edit_model.service_config())
+ if res:
+ self.bar.pushSuccess("PG service", f"PG service '{target_service}' updated!")
+ self.__edit_model.set_not_dirty()
+ else:
+ self.bar.pushWarning("PG service", "There was a problem updating the service!")
+ else:
+ self.bar.pushInfo("PG service", "Edit the service configuration and try again.")
diff --git a/pg_service_parser/imgs/warning.svg b/pg_service_parser/imgs/warning.svg
index c6b8c23..539b63e 100644
--- a/pg_service_parser/imgs/warning.svg
+++ b/pg_service_parser/imgs/warning.svg
@@ -1 +1 @@
-
\ No newline at end of file
+
diff --git a/pg_service_parser/metadata.txt b/pg_service_parser/metadata.txt
index 366d50f..3e6e196 100644
--- a/pg_service_parser/metadata.txt
+++ b/pg_service_parser/metadata.txt
@@ -14,4 +14,4 @@ tracker=https://github.com/opengisch/qgis-pg-service-parser-plugin/issues
repository=https://github.com/opengisch/qgis-pg-service-parser-plugin
icon=images/icon.png
experimental=False
-deprecated=False
\ No newline at end of file
+deprecated=False
diff --git a/pg_service_parser/pg_service_parser_plugin.py b/pg_service_parser/pg_service_parser_plugin.py
index df0d78f..126586f 100644
--- a/pg_service_parser/pg_service_parser_plugin.py
+++ b/pg_service_parser/pg_service_parser_plugin.py
@@ -3,13 +3,13 @@
from pg_service_parser.gui.dlg_pg_service import PgServiceDialog
-class PgServiceParserPlugin():
+class PgServiceParserPlugin:
def __init__(self, iface):
self.iface = iface
self.action = None
def initGui(self):
- self.action = QAction('Go!', self.iface.mainWindow())
+ self.action = QAction("Go!", self.iface.mainWindow())
self.action.triggered.connect(self.run)
self.iface.addToolBarIcon(self.action)
diff --git a/pg_service_parser/ui/pg_service_dialog.ui b/pg_service_parser/ui/pg_service_dialog.ui
index 83f617a..a9901c7 100644
--- a/pg_service_parser/ui/pg_service_dialog.ui
+++ b/pg_service_parser/ui/pg_service_dialog.ui
@@ -205,13 +205,31 @@
-
-
+
+
+ QAbstractItemView::DoubleClicked|QAbstractItemView::EditKeyPressed
+
true
-
- 0
+
+ QAbstractItemView::SingleSelection
+
+
+ QAbstractItemView::SelectRows
+
+ Qt::DotLine
+
+
+ false
+
+
+ true
+
+
+ false
+
-
@@ -230,7 +248,7 @@
-
-
+
Update service
diff --git a/pg_service_parser/utils.py b/pg_service_parser/utils.py
index 02a9388..9c7f47f 100644
--- a/pg_service_parser/utils.py
+++ b/pg_service_parser/utils.py
@@ -1,6 +1,6 @@
import os
-from qgis.PyQt.uic import (loadUiType,
- loadUi)
+
+from qgis.PyQt.uic import loadUiType
from pg_service_parser.config import DEFAULT_PG_SERVICE_PATH
@@ -16,14 +16,8 @@ def get_ui_class(ui_file):
def get_ui_file_path(ui_file) -> str:
- os.path.sep.join(ui_file.split('/'))
- ui_file_path = os.path.abspath(
- os.path.join(
- os.path.dirname(__file__),
- 'ui',
- ui_file
- )
- )
+ os.path.sep.join(ui_file.split("/"))
+ ui_file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), "ui", ui_file))
return ui_file_path