Feature: Timeout-Einstellung und asynchrone DB-Abfrage mit Abbrechen-Dialog
DB-Abfragen laufen nun in einem Hintergrund-Thread mit QProgressDialog, sodass die UI nicht mehr einfriert. connect_timeout wird als konfigurierbarer Parameter (1-300s, Standard: 10) im Connection-String übergeben. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -89,6 +89,7 @@ class PostgreSqlDb(BaseModel):
|
|||||||
username: str
|
username: str
|
||||||
password: str
|
password: str
|
||||||
ssl_mode: SSLMode = SSLMode.PREFER
|
ssl_mode: SSLMode = SSLMode.PREFER
|
||||||
|
timeout: int = 10
|
||||||
|
|
||||||
|
|
||||||
class Project(BaseModel):
|
class Project(BaseModel):
|
||||||
|
|||||||
@@ -19,8 +19,14 @@ class DatabaseTestThread(QThread):
|
|||||||
def run(self):
|
def run(self):
|
||||||
"""Führt den Datenbanktest in einem separaten Thread aus."""
|
"""Führt den Datenbanktest in einem separaten Thread aus."""
|
||||||
try:
|
try:
|
||||||
uri = f"postgresql://{self.connection_data['username']}:{self.connection_data['password']}@{self.connection_data['host']}:{self.connection_data['port']}/{self.connection_data['database']}"
|
timeout = self.connection_data.get("timeout", 10)
|
||||||
|
uri = (
|
||||||
|
f"postgresql://{self.connection_data['username']}:{self.connection_data['password']}"
|
||||||
|
f"@{self.connection_data['host']}:{self.connection_data['port']}"
|
||||||
|
f"/{self.connection_data['database']}"
|
||||||
|
f"?connect_timeout={timeout}"
|
||||||
|
)
|
||||||
|
|
||||||
# Datenbankverbindung testen
|
# Datenbankverbindung testen
|
||||||
r = pl.read_database_uri(
|
r = pl.read_database_uri(
|
||||||
query="SELECT 1",
|
query="SELECT 1",
|
||||||
@@ -106,6 +112,7 @@ class PostgreSqlConfigDialog(QDialog):
|
|||||||
self.ui.usernameEdit.setText(data.get("username", ""))
|
self.ui.usernameEdit.setText(data.get("username", ""))
|
||||||
self.ui.passwordEdit.setText(data.get("password", ""))
|
self.ui.passwordEdit.setText(data.get("password", ""))
|
||||||
self.ui.sslModeComboBox.setCurrentText(data.get("ssl_mode", "prefer"))
|
self.ui.sslModeComboBox.setCurrentText(data.get("ssl_mode", "prefer"))
|
||||||
|
self.ui.timeoutSpinBox.setValue(data.get("timeout", 10))
|
||||||
|
|
||||||
def get_data(self):
|
def get_data(self):
|
||||||
"""Gibt die eingegebenen Daten zurück."""
|
"""Gibt die eingegebenen Daten zurück."""
|
||||||
@@ -126,4 +133,5 @@ class PostgreSqlConfigDialog(QDialog):
|
|||||||
"username": self.ui.usernameEdit.text().strip(),
|
"username": self.ui.usernameEdit.text().strip(),
|
||||||
"password": self.ui.passwordEdit.text(), # Passwort kann leer sein
|
"password": self.ui.passwordEdit.text(), # Passwort kann leer sein
|
||||||
"ssl_mode": self.ui.sslModeComboBox.currentText(),
|
"ssl_mode": self.ui.sslModeComboBox.currentText(),
|
||||||
|
"timeout": self.ui.timeoutSpinBox.value(),
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,7 +138,27 @@
|
|||||||
</item>
|
</item>
|
||||||
</widget>
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
|
<item row="7" column="0">
|
||||||
|
<widget class="QLabel" name="timeoutLabel">
|
||||||
|
<property name="text">
|
||||||
|
<string>Timeout (s):</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
<item row="7" column="1">
|
<item row="7" column="1">
|
||||||
|
<widget class="QSpinBox" name="timeoutSpinBox">
|
||||||
|
<property name="minimum">
|
||||||
|
<number>1</number>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>300</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>10</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item row="8" column="1">
|
||||||
<widget class="QPushButton" name="testConnectionButton">
|
<widget class="QPushButton" name="testConnectionButton">
|
||||||
<property name="text">
|
<property name="text">
|
||||||
<string>Verbindung testen</string>
|
<string>Verbindung testen</string>
|
||||||
|
|||||||
+170
-156
@@ -1,156 +1,170 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
################################################################################
|
################################################################################
|
||||||
## Form generated from reading UI file 'PostgreSqlConfigDialog.ui'
|
## Form generated from reading UI file 'PostgreSqlConfigDialog.ui'
|
||||||
##
|
##
|
||||||
## Created by: Qt User Interface Compiler version 6.9.1
|
## Created by: Qt User Interface Compiler version 6.10.1
|
||||||
##
|
##
|
||||||
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
## WARNING! All changes made in this file will be lost when recompiling UI file!
|
||||||
################################################################################
|
################################################################################
|
||||||
|
|
||||||
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
|
||||||
QMetaObject, QObject, QPoint, QRect,
|
QMetaObject, QObject, QPoint, QRect,
|
||||||
QSize, QTime, QUrl, Qt)
|
QSize, QTime, QUrl, Qt)
|
||||||
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
|
||||||
QFont, QFontDatabase, QGradient, QIcon,
|
QFont, QFontDatabase, QGradient, QIcon,
|
||||||
QImage, QKeySequence, QLinearGradient, QPainter,
|
QImage, QKeySequence, QLinearGradient, QPainter,
|
||||||
QPalette, QPixmap, QRadialGradient, QTransform)
|
QPalette, QPixmap, QRadialGradient, QTransform)
|
||||||
from PySide6.QtWidgets import (QAbstractButton, QApplication, QComboBox, QDialog,
|
from PySide6.QtWidgets import (QAbstractButton, QApplication, QComboBox, QDialog,
|
||||||
QDialogButtonBox, QFormLayout, QLabel, QLineEdit,
|
QDialogButtonBox, QFormLayout, QLabel, QLineEdit,
|
||||||
QPushButton, QSizePolicy, QSpinBox, QVBoxLayout,
|
QPushButton, QSizePolicy, QSpinBox, QVBoxLayout,
|
||||||
QWidget)
|
QWidget)
|
||||||
|
|
||||||
class Ui_PostgreSqlConfigDialog(object):
|
class Ui_PostgreSqlConfigDialog(object):
|
||||||
def setupUi(self, PostgreSqlConfigDialog):
|
def setupUi(self, PostgreSqlConfigDialog):
|
||||||
if not PostgreSqlConfigDialog.objectName():
|
if not PostgreSqlConfigDialog.objectName():
|
||||||
PostgreSqlConfigDialog.setObjectName(u"PostgreSqlConfigDialog")
|
PostgreSqlConfigDialog.setObjectName(u"PostgreSqlConfigDialog")
|
||||||
PostgreSqlConfigDialog.resize(397, 268)
|
PostgreSqlConfigDialog.resize(397, 268)
|
||||||
PostgreSqlConfigDialog.setModal(True)
|
PostgreSqlConfigDialog.setModal(True)
|
||||||
self.verticalLayout = QVBoxLayout(PostgreSqlConfigDialog)
|
self.verticalLayout = QVBoxLayout(PostgreSqlConfigDialog)
|
||||||
self.verticalLayout.setObjectName(u"verticalLayout")
|
self.verticalLayout.setObjectName(u"verticalLayout")
|
||||||
self.formLayout = QFormLayout()
|
self.formLayout = QFormLayout()
|
||||||
self.formLayout.setObjectName(u"formLayout")
|
self.formLayout.setObjectName(u"formLayout")
|
||||||
self.nameLabel = QLabel(PostgreSqlConfigDialog)
|
self.nameLabel = QLabel(PostgreSqlConfigDialog)
|
||||||
self.nameLabel.setObjectName(u"nameLabel")
|
self.nameLabel.setObjectName(u"nameLabel")
|
||||||
|
|
||||||
self.formLayout.setWidget(0, QFormLayout.ItemRole.LabelRole, self.nameLabel)
|
self.formLayout.setWidget(0, QFormLayout.ItemRole.LabelRole, self.nameLabel)
|
||||||
|
|
||||||
self.nameEdit = QLineEdit(PostgreSqlConfigDialog)
|
self.nameEdit = QLineEdit(PostgreSqlConfigDialog)
|
||||||
self.nameEdit.setObjectName(u"nameEdit")
|
self.nameEdit.setObjectName(u"nameEdit")
|
||||||
|
|
||||||
self.formLayout.setWidget(0, QFormLayout.ItemRole.FieldRole, self.nameEdit)
|
self.formLayout.setWidget(0, QFormLayout.ItemRole.FieldRole, self.nameEdit)
|
||||||
|
|
||||||
self.hostLabel = QLabel(PostgreSqlConfigDialog)
|
self.hostLabel = QLabel(PostgreSqlConfigDialog)
|
||||||
self.hostLabel.setObjectName(u"hostLabel")
|
self.hostLabel.setObjectName(u"hostLabel")
|
||||||
|
|
||||||
self.formLayout.setWidget(1, QFormLayout.ItemRole.LabelRole, self.hostLabel)
|
self.formLayout.setWidget(1, QFormLayout.ItemRole.LabelRole, self.hostLabel)
|
||||||
|
|
||||||
self.hostEdit = QLineEdit(PostgreSqlConfigDialog)
|
self.hostEdit = QLineEdit(PostgreSqlConfigDialog)
|
||||||
self.hostEdit.setObjectName(u"hostEdit")
|
self.hostEdit.setObjectName(u"hostEdit")
|
||||||
|
|
||||||
self.formLayout.setWidget(1, QFormLayout.ItemRole.FieldRole, self.hostEdit)
|
self.formLayout.setWidget(1, QFormLayout.ItemRole.FieldRole, self.hostEdit)
|
||||||
|
|
||||||
self.portLabel = QLabel(PostgreSqlConfigDialog)
|
self.portLabel = QLabel(PostgreSqlConfigDialog)
|
||||||
self.portLabel.setObjectName(u"portLabel")
|
self.portLabel.setObjectName(u"portLabel")
|
||||||
|
|
||||||
self.formLayout.setWidget(2, QFormLayout.ItemRole.LabelRole, self.portLabel)
|
self.formLayout.setWidget(2, QFormLayout.ItemRole.LabelRole, self.portLabel)
|
||||||
|
|
||||||
self.portSpinBox = QSpinBox(PostgreSqlConfigDialog)
|
self.portSpinBox = QSpinBox(PostgreSqlConfigDialog)
|
||||||
self.portSpinBox.setObjectName(u"portSpinBox")
|
self.portSpinBox.setObjectName(u"portSpinBox")
|
||||||
self.portSpinBox.setMinimum(1)
|
self.portSpinBox.setMinimum(1)
|
||||||
self.portSpinBox.setMaximum(65535)
|
self.portSpinBox.setMaximum(65535)
|
||||||
self.portSpinBox.setValue(5432)
|
self.portSpinBox.setValue(5432)
|
||||||
|
|
||||||
self.formLayout.setWidget(2, QFormLayout.ItemRole.FieldRole, self.portSpinBox)
|
self.formLayout.setWidget(2, QFormLayout.ItemRole.FieldRole, self.portSpinBox)
|
||||||
|
|
||||||
self.databaseLabel = QLabel(PostgreSqlConfigDialog)
|
self.databaseLabel = QLabel(PostgreSqlConfigDialog)
|
||||||
self.databaseLabel.setObjectName(u"databaseLabel")
|
self.databaseLabel.setObjectName(u"databaseLabel")
|
||||||
|
|
||||||
self.formLayout.setWidget(3, QFormLayout.ItemRole.LabelRole, self.databaseLabel)
|
self.formLayout.setWidget(3, QFormLayout.ItemRole.LabelRole, self.databaseLabel)
|
||||||
|
|
||||||
self.databaseEdit = QLineEdit(PostgreSqlConfigDialog)
|
self.databaseEdit = QLineEdit(PostgreSqlConfigDialog)
|
||||||
self.databaseEdit.setObjectName(u"databaseEdit")
|
self.databaseEdit.setObjectName(u"databaseEdit")
|
||||||
|
|
||||||
self.formLayout.setWidget(3, QFormLayout.ItemRole.FieldRole, self.databaseEdit)
|
self.formLayout.setWidget(3, QFormLayout.ItemRole.FieldRole, self.databaseEdit)
|
||||||
|
|
||||||
self.usernameLabel = QLabel(PostgreSqlConfigDialog)
|
self.usernameLabel = QLabel(PostgreSqlConfigDialog)
|
||||||
self.usernameLabel.setObjectName(u"usernameLabel")
|
self.usernameLabel.setObjectName(u"usernameLabel")
|
||||||
|
|
||||||
self.formLayout.setWidget(4, QFormLayout.ItemRole.LabelRole, self.usernameLabel)
|
self.formLayout.setWidget(4, QFormLayout.ItemRole.LabelRole, self.usernameLabel)
|
||||||
|
|
||||||
self.usernameEdit = QLineEdit(PostgreSqlConfigDialog)
|
self.usernameEdit = QLineEdit(PostgreSqlConfigDialog)
|
||||||
self.usernameEdit.setObjectName(u"usernameEdit")
|
self.usernameEdit.setObjectName(u"usernameEdit")
|
||||||
|
|
||||||
self.formLayout.setWidget(4, QFormLayout.ItemRole.FieldRole, self.usernameEdit)
|
self.formLayout.setWidget(4, QFormLayout.ItemRole.FieldRole, self.usernameEdit)
|
||||||
|
|
||||||
self.passwordLabel = QLabel(PostgreSqlConfigDialog)
|
self.passwordLabel = QLabel(PostgreSqlConfigDialog)
|
||||||
self.passwordLabel.setObjectName(u"passwordLabel")
|
self.passwordLabel.setObjectName(u"passwordLabel")
|
||||||
|
|
||||||
self.formLayout.setWidget(5, QFormLayout.ItemRole.LabelRole, self.passwordLabel)
|
self.formLayout.setWidget(5, QFormLayout.ItemRole.LabelRole, self.passwordLabel)
|
||||||
|
|
||||||
self.passwordEdit = QLineEdit(PostgreSqlConfigDialog)
|
self.passwordEdit = QLineEdit(PostgreSqlConfigDialog)
|
||||||
self.passwordEdit.setObjectName(u"passwordEdit")
|
self.passwordEdit.setObjectName(u"passwordEdit")
|
||||||
self.passwordEdit.setEchoMode(QLineEdit.EchoMode.Password)
|
self.passwordEdit.setEchoMode(QLineEdit.EchoMode.Password)
|
||||||
|
|
||||||
self.formLayout.setWidget(5, QFormLayout.ItemRole.FieldRole, self.passwordEdit)
|
self.formLayout.setWidget(5, QFormLayout.ItemRole.FieldRole, self.passwordEdit)
|
||||||
|
|
||||||
self.sslModeLabel = QLabel(PostgreSqlConfigDialog)
|
self.sslModeLabel = QLabel(PostgreSqlConfigDialog)
|
||||||
self.sslModeLabel.setObjectName(u"sslModeLabel")
|
self.sslModeLabel.setObjectName(u"sslModeLabel")
|
||||||
|
|
||||||
self.formLayout.setWidget(6, QFormLayout.ItemRole.LabelRole, self.sslModeLabel)
|
self.formLayout.setWidget(6, QFormLayout.ItemRole.LabelRole, self.sslModeLabel)
|
||||||
|
|
||||||
self.sslModeComboBox = QComboBox(PostgreSqlConfigDialog)
|
self.sslModeComboBox = QComboBox(PostgreSqlConfigDialog)
|
||||||
self.sslModeComboBox.addItem("")
|
self.sslModeComboBox.addItem("")
|
||||||
self.sslModeComboBox.addItem("")
|
self.sslModeComboBox.addItem("")
|
||||||
self.sslModeComboBox.addItem("")
|
self.sslModeComboBox.addItem("")
|
||||||
self.sslModeComboBox.addItem("")
|
self.sslModeComboBox.addItem("")
|
||||||
self.sslModeComboBox.addItem("")
|
self.sslModeComboBox.addItem("")
|
||||||
self.sslModeComboBox.addItem("")
|
self.sslModeComboBox.addItem("")
|
||||||
self.sslModeComboBox.setObjectName(u"sslModeComboBox")
|
self.sslModeComboBox.setObjectName(u"sslModeComboBox")
|
||||||
|
|
||||||
self.formLayout.setWidget(6, QFormLayout.ItemRole.FieldRole, self.sslModeComboBox)
|
self.formLayout.setWidget(6, QFormLayout.ItemRole.FieldRole, self.sslModeComboBox)
|
||||||
|
|
||||||
self.testConnectionButton = QPushButton(PostgreSqlConfigDialog)
|
self.timeoutLabel = QLabel(PostgreSqlConfigDialog)
|
||||||
self.testConnectionButton.setObjectName(u"testConnectionButton")
|
self.timeoutLabel.setObjectName(u"timeoutLabel")
|
||||||
|
|
||||||
self.formLayout.setWidget(7, QFormLayout.ItemRole.FieldRole, self.testConnectionButton)
|
self.formLayout.setWidget(7, QFormLayout.ItemRole.LabelRole, self.timeoutLabel)
|
||||||
|
|
||||||
|
self.timeoutSpinBox = QSpinBox(PostgreSqlConfigDialog)
|
||||||
self.verticalLayout.addLayout(self.formLayout)
|
self.timeoutSpinBox.setObjectName(u"timeoutSpinBox")
|
||||||
|
self.timeoutSpinBox.setMinimum(1)
|
||||||
self.buttonBox = QDialogButtonBox(PostgreSqlConfigDialog)
|
self.timeoutSpinBox.setMaximum(300)
|
||||||
self.buttonBox.setObjectName(u"buttonBox")
|
self.timeoutSpinBox.setValue(10)
|
||||||
self.buttonBox.setOrientation(Qt.Orientation.Horizontal)
|
|
||||||
self.buttonBox.setStandardButtons(QDialogButtonBox.StandardButton.Cancel|QDialogButtonBox.StandardButton.Ok)
|
self.formLayout.setWidget(7, QFormLayout.ItemRole.FieldRole, self.timeoutSpinBox)
|
||||||
self.buttonBox.setCenterButtons(True)
|
|
||||||
|
self.testConnectionButton = QPushButton(PostgreSqlConfigDialog)
|
||||||
self.verticalLayout.addWidget(self.buttonBox)
|
self.testConnectionButton.setObjectName(u"testConnectionButton")
|
||||||
|
|
||||||
|
self.formLayout.setWidget(8, QFormLayout.ItemRole.FieldRole, self.testConnectionButton)
|
||||||
self.retranslateUi(PostgreSqlConfigDialog)
|
|
||||||
self.buttonBox.accepted.connect(PostgreSqlConfigDialog.accept)
|
|
||||||
self.buttonBox.rejected.connect(PostgreSqlConfigDialog.reject)
|
self.verticalLayout.addLayout(self.formLayout)
|
||||||
|
|
||||||
QMetaObject.connectSlotsByName(PostgreSqlConfigDialog)
|
self.buttonBox = QDialogButtonBox(PostgreSqlConfigDialog)
|
||||||
# setupUi
|
self.buttonBox.setObjectName(u"buttonBox")
|
||||||
|
self.buttonBox.setOrientation(Qt.Orientation.Horizontal)
|
||||||
def retranslateUi(self, PostgreSqlConfigDialog):
|
self.buttonBox.setStandardButtons(QDialogButtonBox.StandardButton.Cancel|QDialogButtonBox.StandardButton.Ok)
|
||||||
PostgreSqlConfigDialog.setWindowTitle(QCoreApplication.translate("PostgreSqlConfigDialog", u"PostgreSQL Datenbank Konfiguration", None))
|
self.buttonBox.setCenterButtons(True)
|
||||||
self.nameLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Name:", None))
|
|
||||||
self.hostLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Host:", None))
|
self.verticalLayout.addWidget(self.buttonBox)
|
||||||
self.hostEdit.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"localhost", None))
|
|
||||||
self.portLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Port:", None))
|
|
||||||
self.databaseLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Datenbank:", None))
|
self.retranslateUi(PostgreSqlConfigDialog)
|
||||||
self.usernameLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Benutzername:", None))
|
self.buttonBox.accepted.connect(PostgreSqlConfigDialog.accept)
|
||||||
self.passwordLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Passwort:", None))
|
self.buttonBox.rejected.connect(PostgreSqlConfigDialog.reject)
|
||||||
self.sslModeLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"SSL-Modus:", None))
|
|
||||||
self.sslModeComboBox.setItemText(0, QCoreApplication.translate("PostgreSqlConfigDialog", u"disable", None))
|
QMetaObject.connectSlotsByName(PostgreSqlConfigDialog)
|
||||||
self.sslModeComboBox.setItemText(1, QCoreApplication.translate("PostgreSqlConfigDialog", u"allow", None))
|
# setupUi
|
||||||
self.sslModeComboBox.setItemText(2, QCoreApplication.translate("PostgreSqlConfigDialog", u"prefer", None))
|
|
||||||
self.sslModeComboBox.setItemText(3, QCoreApplication.translate("PostgreSqlConfigDialog", u"require", None))
|
def retranslateUi(self, PostgreSqlConfigDialog):
|
||||||
self.sslModeComboBox.setItemText(4, QCoreApplication.translate("PostgreSqlConfigDialog", u"verify-ca", None))
|
PostgreSqlConfigDialog.setWindowTitle(QCoreApplication.translate("PostgreSqlConfigDialog", u"PostgreSQL Datenbank Konfiguration", None))
|
||||||
self.sslModeComboBox.setItemText(5, QCoreApplication.translate("PostgreSqlConfigDialog", u"verify-full", None))
|
self.nameLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Name:", None))
|
||||||
|
self.hostLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Host:", None))
|
||||||
self.testConnectionButton.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Verbindung testen", None))
|
self.hostEdit.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"localhost", None))
|
||||||
# retranslateUi
|
self.portLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Port:", None))
|
||||||
|
self.databaseLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Datenbank:", None))
|
||||||
|
self.usernameLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Benutzername:", None))
|
||||||
|
self.passwordLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Passwort:", None))
|
||||||
|
self.sslModeLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"SSL-Modus:", None))
|
||||||
|
self.sslModeComboBox.setItemText(0, QCoreApplication.translate("PostgreSqlConfigDialog", u"disable", None))
|
||||||
|
self.sslModeComboBox.setItemText(1, QCoreApplication.translate("PostgreSqlConfigDialog", u"allow", None))
|
||||||
|
self.sslModeComboBox.setItemText(2, QCoreApplication.translate("PostgreSqlConfigDialog", u"prefer", None))
|
||||||
|
self.sslModeComboBox.setItemText(3, QCoreApplication.translate("PostgreSqlConfigDialog", u"require", None))
|
||||||
|
self.sslModeComboBox.setItemText(4, QCoreApplication.translate("PostgreSqlConfigDialog", u"verify-ca", None))
|
||||||
|
self.sslModeComboBox.setItemText(5, QCoreApplication.translate("PostgreSqlConfigDialog", u"verify-full", None))
|
||||||
|
|
||||||
|
self.timeoutLabel.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Timeout (s):", None))
|
||||||
|
self.testConnectionButton.setText(QCoreApplication.translate("PostgreSqlConfigDialog", u"Verbindung testen", None))
|
||||||
|
# retranslateUi
|
||||||
|
|
||||||
|
|||||||
+86
-29
@@ -10,13 +10,35 @@ import logging
|
|||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
import polars as pl
|
import polars as pl
|
||||||
from PySide6.QtWidgets import QMessageBox
|
from PySide6.QtCore import QThread, Signal, Qt
|
||||||
|
from PySide6.QtWidgets import QMessageBox, QProgressDialog
|
||||||
|
|
||||||
from conf import app_settings, TreeNode, XslFile
|
from conf import app_settings, TreeNode, XslFile
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseQueryThread(QThread):
|
||||||
|
"""Thread für asynchrone Datenbankabfragen."""
|
||||||
|
|
||||||
|
query_completed = Signal(object) # pl.DataFrame
|
||||||
|
query_failed = Signal(str) # Fehlermeldung
|
||||||
|
|
||||||
|
def __init__(self, sql_query, connection_string):
|
||||||
|
super().__init__()
|
||||||
|
self.sql_query = sql_query
|
||||||
|
self.connection_string = connection_string
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
try:
|
||||||
|
df = pl.read_database_uri(self.sql_query, self.connection_string, engine="connectorx").sort(
|
||||||
|
["reporttyp_bez", "report_bez", "repfile_bez"]
|
||||||
|
)
|
||||||
|
self.query_completed.emit(df)
|
||||||
|
except Exception as e:
|
||||||
|
self.query_failed.emit(str(e))
|
||||||
|
|
||||||
|
|
||||||
class DatabaseMixin:
|
class DatabaseMixin:
|
||||||
"""
|
"""
|
||||||
Mixin für Datenbank-Operationen.
|
Mixin für Datenbank-Operationen.
|
||||||
@@ -31,7 +53,7 @@ class DatabaseMixin:
|
|||||||
def on_load_from_fn2_clicked(self):
|
def on_load_from_fn2_clicked(self):
|
||||||
"""
|
"""
|
||||||
Wird ausgeführt, wenn der Button "lade aus FN2" geklickt wird.
|
Wird ausgeführt, wenn der Button "lade aus FN2" geklickt wird.
|
||||||
Führt SQL-Abfrage aus und aktualisiert die Projekt-Nodes.
|
Startet SQL-Abfrage asynchron mit Fortschrittsdialog.
|
||||||
"""
|
"""
|
||||||
logger.debug("Button 'lade aus FN2' wurde geklickt!")
|
logger.debug("Button 'lade aus FN2' wurde geklickt!")
|
||||||
|
|
||||||
@@ -52,29 +74,68 @@ class DatabaseMixin:
|
|||||||
QMessageBox.warning(self, "Warnung", "PostgreSQL-Datenbank-Konfiguration nicht gefunden.")
|
QMessageBox.warning(self, "Warnung", "PostgreSQL-Datenbank-Konfiguration nicht gefunden.")
|
||||||
return
|
return
|
||||||
|
|
||||||
# Führe SQL-Abfrage aus
|
# SQL-Abfrage und Connection-String vorbereiten
|
||||||
df = self._execute_sql_query(db_config)
|
sql_query, connection_string = self._prepare_sql_query(db_config)
|
||||||
if df is None:
|
if sql_query is None:
|
||||||
return # Fehler bereits angezeigt
|
return # Fehler bereits angezeigt
|
||||||
|
|
||||||
# Verarbeite die Daten wie in readCsv.py
|
# Fortschrittsdialog erstellen
|
||||||
new_nodes = self._process_sql_data(df)
|
self._db_progress = QProgressDialog("Verbinde mit Datenbank...", "Abbrechen", 0, 0, self)
|
||||||
|
self._db_progress.setWindowTitle("Datenbank-Abfrage")
|
||||||
|
self._db_progress.setWindowModality(Qt.WindowModal)
|
||||||
|
self._db_progress.setMinimumDuration(0)
|
||||||
|
|
||||||
# Merge mit vorhandenen Nodes
|
# Query-Thread erstellen und starten
|
||||||
self._merge_nodes_with_existing(new_nodes)
|
self._db_query_thread = DatabaseQueryThread(sql_query, connection_string)
|
||||||
|
self._db_query_thread.query_completed.connect(self._on_db_query_completed)
|
||||||
# Speichere die aktualisierten Projekt-Einstellungen
|
self._db_query_thread.query_failed.connect(self._on_db_query_failed)
|
||||||
self._save_project_settings()
|
self._db_query_thread.finished.connect(self._cleanup_db_query)
|
||||||
|
self._db_progress.canceled.connect(self._on_db_query_canceled)
|
||||||
# Lade das Projekt neu
|
self._db_query_thread.start()
|
||||||
self._load_nodes_to_tree()
|
|
||||||
|
|
||||||
# QMessageBox.information(self, "Erfolg", "Daten erfolgreich aus FN2 geladen und Projekt aktualisiert!")
|
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
logger.error(f"Fehler beim Laden aus FN2: {e}")
|
logger.error(f"Fehler beim Laden aus FN2: {e}")
|
||||||
QMessageBox.critical(self, "Fehler", f"Fehler beim Laden aus FN2:\n{str(e)}")
|
QMessageBox.critical(self, "Fehler", f"Fehler beim Laden aus FN2:\n{str(e)}")
|
||||||
|
|
||||||
|
def _on_db_query_completed(self, df):
|
||||||
|
"""Wird aufgerufen, wenn die Datenbankabfrage erfolgreich war."""
|
||||||
|
# Ignoriere Ergebnis, falls der Benutzer abgebrochen hat
|
||||||
|
if hasattr(self, "_db_progress") and self._db_progress.wasCanceled():
|
||||||
|
return
|
||||||
|
|
||||||
|
try:
|
||||||
|
new_nodes = self._process_sql_data(df)
|
||||||
|
self._merge_nodes_with_existing(new_nodes)
|
||||||
|
self._save_project_settings()
|
||||||
|
self._load_nodes_to_tree()
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"Fehler beim Verarbeiten der DB-Daten: {e}")
|
||||||
|
QMessageBox.critical(self, "Fehler", f"Fehler beim Verarbeiten der Daten:\n{str(e)}")
|
||||||
|
|
||||||
|
def _on_db_query_failed(self, error_msg):
|
||||||
|
"""Wird aufgerufen, wenn die Datenbankabfrage fehlgeschlagen ist."""
|
||||||
|
if hasattr(self, "_db_progress") and self._db_progress.wasCanceled():
|
||||||
|
return
|
||||||
|
|
||||||
|
error = f"Fehler beim Ausführen der SQL-Abfrage: {error_msg}"
|
||||||
|
logger.error(error)
|
||||||
|
QMessageBox.critical(self, "Fehler", error)
|
||||||
|
|
||||||
|
def _on_db_query_canceled(self):
|
||||||
|
"""Wird aufgerufen, wenn der Benutzer die Abfrage abbricht."""
|
||||||
|
logger.info("Datenbankabfrage vom Benutzer abgebrochen")
|
||||||
|
|
||||||
|
def _cleanup_db_query(self):
|
||||||
|
"""Räumt nach der Datenbankabfrage auf."""
|
||||||
|
if hasattr(self, "_db_progress"):
|
||||||
|
self._db_progress.canceled.disconnect(self._on_db_query_canceled)
|
||||||
|
self._db_progress.close()
|
||||||
|
self._db_progress.deleteLater()
|
||||||
|
del self._db_progress
|
||||||
|
if hasattr(self, "_db_query_thread"):
|
||||||
|
self._db_query_thread.deleteLater()
|
||||||
|
del self._db_query_thread
|
||||||
|
|
||||||
def _get_database_config(self, db_id):
|
def _get_database_config(self, db_id):
|
||||||
"""
|
"""
|
||||||
Holt die Datenbank-Konfiguration anhand der ID.
|
Holt die Datenbank-Konfiguration anhand der ID.
|
||||||
@@ -90,29 +151,27 @@ class DatabaseMixin:
|
|||||||
return db
|
return db
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def _execute_sql_query(self, db_config):
|
def _prepare_sql_query(self, db_config):
|
||||||
"""
|
"""
|
||||||
Führt die SQL-Abfrage aus der data.sql Datei aus.
|
Bereitet SQL-Abfrage und Connection-String vor.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
db_config: PostgreSQL-Datenbank-Konfiguration
|
db_config: PostgreSQL-Datenbank-Konfiguration
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
pl.DataFrame|None: Die Abfrageergebnisse oder None bei Fehler
|
tuple[str, str]|tuple[None, None]: (sql_query, connection_string) oder (None, None) bei Fehler
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
# Lade SQL-Abfrage aus Datei
|
|
||||||
sql_file_path = Path("src/res/data.sql")
|
sql_file_path = Path("src/res/data.sql")
|
||||||
if not sql_file_path.exists():
|
if not sql_file_path.exists():
|
||||||
QMessageBox.critical(self, "Fehler", f"SQL-Datei nicht gefunden: {sql_file_path}")
|
QMessageBox.critical(self, "Fehler", f"SQL-Datei nicht gefunden: {sql_file_path}")
|
||||||
return None
|
return None, None
|
||||||
|
|
||||||
with open(sql_file_path, "r", encoding="utf-8") as f:
|
with open(sql_file_path, "r", encoding="utf-8") as f:
|
||||||
sql_query = f.read()
|
sql_query = f.read()
|
||||||
|
|
||||||
logger.debug(f"SQL-Abfrage geladen: {len(sql_query)} Zeichen")
|
logger.debug(f"SQL-Abfrage geladen: {len(sql_query)} Zeichen")
|
||||||
|
|
||||||
# Verbindung zur PostgreSQL-Datenbank herstellen
|
|
||||||
connection_string = (
|
connection_string = (
|
||||||
"postgresql://"
|
"postgresql://"
|
||||||
f"{db_config.username}:"
|
f"{db_config.username}:"
|
||||||
@@ -121,19 +180,17 @@ class DatabaseMixin:
|
|||||||
f"{db_config.port}/"
|
f"{db_config.port}/"
|
||||||
f"{db_config.database}?"
|
f"{db_config.database}?"
|
||||||
f"sslmode={db_config.ssl_mode.value}"
|
f"sslmode={db_config.ssl_mode.value}"
|
||||||
|
f"&connect_timeout={db_config.timeout}"
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.info(f"Verbinde zu PostgreSQL: {db_config.host}:{db_config.port}/{db_config.database}")
|
logger.info(f"Verbinde zu PostgreSQL: {db_config.host}:{db_config.port}/{db_config.database}")
|
||||||
|
|
||||||
df = pl.read_database_uri(sql_query, connection_string, engine="connectorx").sort(
|
return sql_query, connection_string
|
||||||
["reporttyp_bez", "report_bez", "repfile_bez"]
|
|
||||||
)
|
|
||||||
return df
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_msg = f"Fehler beim Ausführen der SQL-Abfrage: {str(e)}"
|
error_msg = f"Fehler beim Vorbereiten der SQL-Abfrage: {str(e)}"
|
||||||
logger.error(error_msg)
|
logger.error(error_msg)
|
||||||
QMessageBox.critical(self, "Fehler", error_msg)
|
QMessageBox.critical(self, "Fehler", error_msg)
|
||||||
return None
|
return None, None
|
||||||
|
|
||||||
def _process_sql_data(self, df):
|
def _process_sql_data(self, df):
|
||||||
"""
|
"""
|
||||||
|
|||||||
Reference in New Issue
Block a user