Skip to content

Commit 30855cd

Browse files
committed
add a GUI standalone app
1 parent 0cfa136 commit 30855cd

File tree

9 files changed

+913
-0
lines changed

9 files changed

+913
-0
lines changed

pgserviceparser/gui/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
"""pgserviceparser GUI - A graphical interface for managing PostgreSQL connection service files."""
2+
3+
from pgserviceparser.gui.compat import QtWidgets # noqa: F401

pgserviceparser/gui/__main__.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""Entry point for pgserviceparser GUI."""
2+
3+
import sys
4+
5+
try:
6+
from pgserviceparser.gui.compat import QtWidgets
7+
except ImportError:
8+
print(
9+
"Error: PyQt6 is required for the GUI.\n" "Install it with: pip install pgserviceparser[gui]",
10+
file=sys.stderr,
11+
)
12+
sys.exit(1)
13+
14+
QApplication = QtWidgets.QApplication
15+
16+
from pgserviceparser.gui.main_window import MainWindow
17+
18+
19+
def main():
20+
app = QApplication(sys.argv)
21+
app.setApplicationName("pgserviceparser-gui")
22+
window = MainWindow()
23+
window.show()
24+
sys.exit(app.exec())
25+
26+
27+
if __name__ == "__main__":
28+
main()

pgserviceparser/gui/compat.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
"""Qt compatibility shim.
2+
3+
Uses ``qgis.PyQt`` when running inside QGIS (works with both Qt 5 and Qt 6),
4+
otherwise falls back to ``PyQt6``.
5+
"""
6+
7+
try:
8+
from qgis.PyQt import QtCore, QtGui, QtWidgets # noqa: F401
9+
except ImportError:
10+
try:
11+
from PyQt6 import QtCore, QtGui, QtWidgets # noqa: F401
12+
except ImportError:
13+
raise ImportError("PyQt6 is required for the GUI. " "Install it with: pip install pgserviceparser[gui]")
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
"""Item delegates for service configuration editing."""
2+
3+
from pgserviceparser.gui.compat import QtCore, QtWidgets
4+
5+
Qt = QtCore.Qt
6+
QComboBox = QtWidgets.QComboBox
7+
QFileDialog = QtWidgets.QFileDialog
8+
QLineEdit = QtWidgets.QLineEdit
9+
QStyledItemDelegate = QtWidgets.QStyledItemDelegate
10+
QWidget = QtWidgets.QWidget
11+
12+
from pgserviceparser.gui.setting_model import ServiceConfigModel
13+
from pgserviceparser.service_settings import WidgetType
14+
15+
16+
class ServiceConfigDelegate(QStyledItemDelegate):
17+
"""Delegate that provides appropriate editors for service settings."""
18+
19+
def __init__(self, parent: QWidget | None = None):
20+
super().__init__(parent)
21+
22+
def createEditor(self, parent: QWidget, option, index):
23+
if ServiceConfigModel.is_custom_widget_cell(index):
24+
meta = index.data(Qt.ItemDataRole.UserRole)
25+
widget_type = meta["widget_type"]
26+
config = meta.get("config") or {}
27+
28+
if widget_type == WidgetType.COMBOBOX:
29+
widget = QComboBox(parent)
30+
for value in config.get("values", []):
31+
widget.addItem(value, value)
32+
widget.currentIndexChanged.connect(self._commit_and_close)
33+
return widget
34+
35+
elif widget_type == WidgetType.PASSWORD:
36+
widget = QLineEdit(parent)
37+
widget.setEchoMode(QLineEdit.EchoMode.Password)
38+
widget.editingFinished.connect(self._commit_and_close)
39+
return widget
40+
41+
elif widget_type == WidgetType.FILE:
42+
# For file settings, open a dialog immediately
43+
file_filter = config.get("filter", "")
44+
title = config.get("title", "Select file")
45+
path, _ = QFileDialog.getOpenFileName(parent, title, "", file_filter)
46+
if path:
47+
# Set directly and close
48+
model = index.model()
49+
model.setData(index, path, Qt.ItemDataRole.EditRole)
50+
return None # No persistent editor needed
51+
52+
return super().createEditor(parent, option, index)
53+
54+
def _commit_and_close(self):
55+
editor = self.sender()
56+
self.commitData.emit(editor)
57+
self.closeEditor.emit(editor)
58+
59+
def setEditorData(self, editor: QWidget, index):
60+
if ServiceConfigModel.is_custom_widget_cell(index):
61+
meta = index.data(Qt.ItemDataRole.UserRole)
62+
widget_type = meta["widget_type"]
63+
value = index.data(Qt.ItemDataRole.EditRole)
64+
65+
if widget_type == WidgetType.COMBOBOX and isinstance(editor, QComboBox):
66+
editor.setCurrentText(value or "")
67+
return
68+
elif widget_type == WidgetType.PASSWORD and isinstance(editor, QLineEdit):
69+
editor.setText(value or "")
70+
return
71+
72+
super().setEditorData(editor, index)
73+
74+
def setModelData(self, editor: QWidget, model, index):
75+
if ServiceConfigModel.is_custom_widget_cell(index):
76+
meta = index.data(Qt.ItemDataRole.UserRole)
77+
widget_type = meta["widget_type"]
78+
79+
if widget_type == WidgetType.COMBOBOX and isinstance(editor, QComboBox):
80+
model.setData(index, editor.currentData(), Qt.ItemDataRole.EditRole)
81+
return
82+
elif widget_type == WidgetType.PASSWORD and isinstance(editor, QLineEdit):
83+
model.setData(index, editor.text(), Qt.ItemDataRole.EditRole)
84+
return
85+
86+
super().setModelData(editor, model, index)

pgserviceparser/gui/main_window.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
"""Main window for pgserviceparser GUI."""
2+
3+
from pgserviceparser.gui.compat import QtWidgets
4+
5+
QMainWindow = QtWidgets.QMainWindow
6+
7+
from pgserviceparser.gui.service_widget import ServiceWidget
8+
9+
10+
class MainWindow(QMainWindow):
11+
def __init__(self):
12+
super().__init__()
13+
self.setWindowTitle("PG Service Parser")
14+
self.setMinimumSize(600, 400)
15+
16+
self._service_widget = ServiceWidget()
17+
self.setCentralWidget(self._service_widget)

0 commit comments

Comments
 (0)