Initial commit
Dieser Commit ist enthalten in:
316
views/widgets/modern_message_box.py
Normale Datei
316
views/widgets/modern_message_box.py
Normale Datei
@ -0,0 +1,316 @@
|
||||
"""
|
||||
Moderne, schöne MessageBox als Alternative zu den hässlichen Qt Standard-Dialogen
|
||||
"""
|
||||
|
||||
from PyQt5.QtWidgets import (QDialog, QVBoxLayout, QHBoxLayout, QLabel,
|
||||
QPushButton, QFrame, QGraphicsDropShadowEffect)
|
||||
from PyQt5.QtCore import Qt, QTimer, pyqtSignal
|
||||
from PyQt5.QtGui import QFont, QPixmap, QPainter, QColor, QIcon
|
||||
|
||||
class ModernMessageBox(QDialog):
|
||||
"""Moderne, schöne MessageBox mit glasmorphism Design"""
|
||||
|
||||
clicked = pyqtSignal(str) # "ok", "cancel", "yes", "no"
|
||||
|
||||
def __init__(self, parent=None, title="", message="", msg_type="info", buttons=None):
|
||||
super().__init__(parent)
|
||||
self.msg_type = msg_type.lower()
|
||||
self.result_value = None
|
||||
|
||||
if buttons is None:
|
||||
buttons = ["OK"]
|
||||
|
||||
self.init_ui(title, message, buttons)
|
||||
self.setup_animations()
|
||||
|
||||
def init_ui(self, title, message, buttons):
|
||||
"""Initialisiert die moderne UI"""
|
||||
|
||||
# Dialog-Eigenschaften
|
||||
self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint)
|
||||
self.setAttribute(Qt.WA_TranslucentBackground, True)
|
||||
self.setModal(True)
|
||||
self.setFixedSize(400, 250)
|
||||
|
||||
# Hauptlayout
|
||||
main_layout = QVBoxLayout(self)
|
||||
main_layout.setContentsMargins(20, 20, 20, 20)
|
||||
|
||||
# Container mit Glasmorphism-Effekt
|
||||
self.container = QFrame()
|
||||
self.container.setObjectName("messageContainer")
|
||||
|
||||
# Schatten-Effekt
|
||||
shadow = QGraphicsDropShadowEffect()
|
||||
shadow.setBlurRadius(30)
|
||||
shadow.setXOffset(0)
|
||||
shadow.setYOffset(10)
|
||||
shadow.setColor(QColor(0, 0, 0, 80))
|
||||
self.container.setGraphicsEffect(shadow)
|
||||
|
||||
# Container-Layout
|
||||
container_layout = QVBoxLayout(self.container)
|
||||
container_layout.setSpacing(20)
|
||||
container_layout.setContentsMargins(30, 25, 30, 25)
|
||||
|
||||
# Header mit Icon und Titel
|
||||
header_layout = QHBoxLayout()
|
||||
|
||||
# Icon basierend auf Nachrichtentyp
|
||||
icon_label = QLabel()
|
||||
icon_label.setFixedSize(32, 32)
|
||||
icon_label.setAlignment(Qt.AlignCenter)
|
||||
|
||||
icon_text = self.get_icon_for_type(self.msg_type)
|
||||
icon_label.setText(icon_text)
|
||||
icon_label.setObjectName(f"icon{self.msg_type.title()}")
|
||||
|
||||
# Titel
|
||||
title_label = QLabel(title)
|
||||
title_label.setObjectName("messageTitle")
|
||||
title_label.setWordWrap(True)
|
||||
|
||||
header_layout.addWidget(icon_label)
|
||||
header_layout.addWidget(title_label, 1)
|
||||
header_layout.setSpacing(15)
|
||||
|
||||
# Nachricht
|
||||
message_label = QLabel(message)
|
||||
message_label.setObjectName("messageText")
|
||||
message_label.setWordWrap(True)
|
||||
message_label.setAlignment(Qt.AlignLeft | Qt.AlignTop)
|
||||
|
||||
# Buttons
|
||||
button_layout = QHBoxLayout()
|
||||
button_layout.addStretch()
|
||||
|
||||
for i, button_text in enumerate(buttons):
|
||||
btn = QPushButton(button_text)
|
||||
btn.setObjectName("messageButton" if i == 0 else "messageButtonSecondary")
|
||||
btn.setMinimumHeight(35)
|
||||
btn.setMinimumWidth(80)
|
||||
btn.clicked.connect(lambda checked, text=button_text.lower(): self.button_clicked(text))
|
||||
button_layout.addWidget(btn)
|
||||
if i < len(buttons) - 1:
|
||||
button_layout.addSpacing(10)
|
||||
|
||||
# Layout zusammenfügen
|
||||
container_layout.addLayout(header_layout)
|
||||
container_layout.addWidget(message_label, 1)
|
||||
container_layout.addLayout(button_layout)
|
||||
|
||||
main_layout.addWidget(self.container)
|
||||
|
||||
# Stil anwenden
|
||||
self.apply_styles()
|
||||
|
||||
def get_icon_for_type(self, msg_type):
|
||||
"""Gibt das passende Icon für den Nachrichtentyp zurück"""
|
||||
icons = {
|
||||
"info": "ℹ️",
|
||||
"success": "✅",
|
||||
"warning": "⚠️",
|
||||
"error": "❌",
|
||||
"critical": "🚨",
|
||||
"question": "❓"
|
||||
}
|
||||
return icons.get(msg_type, "ℹ️")
|
||||
|
||||
def apply_styles(self):
|
||||
"""Wendet die modernen Styles an"""
|
||||
|
||||
# Farben basierend auf Typ
|
||||
colors = {
|
||||
"info": {
|
||||
"bg": "rgba(59, 130, 246, 0.1)",
|
||||
"border": "rgba(59, 130, 246, 0.3)",
|
||||
"accent": "#3B82F6"
|
||||
},
|
||||
"success": {
|
||||
"bg": "rgba(34, 197, 94, 0.1)",
|
||||
"border": "rgba(34, 197, 94, 0.3)",
|
||||
"accent": "#22C55E"
|
||||
},
|
||||
"warning": {
|
||||
"bg": "rgba(245, 158, 11, 0.1)",
|
||||
"border": "rgba(245, 158, 11, 0.3)",
|
||||
"accent": "#F59E0B"
|
||||
},
|
||||
"error": {
|
||||
"bg": "rgba(239, 68, 68, 0.1)",
|
||||
"border": "rgba(239, 68, 68, 0.3)",
|
||||
"accent": "#EF4444"
|
||||
},
|
||||
"critical": {
|
||||
"bg": "rgba(220, 38, 38, 0.1)",
|
||||
"border": "rgba(220, 38, 38, 0.3)",
|
||||
"accent": "#DC2626"
|
||||
},
|
||||
"question": {
|
||||
"bg": "rgba(168, 85, 247, 0.1)",
|
||||
"border": "rgba(168, 85, 247, 0.3)",
|
||||
"accent": "#A855F7"
|
||||
}
|
||||
}
|
||||
|
||||
color_scheme = colors.get(self.msg_type, colors["info"])
|
||||
|
||||
style = f"""
|
||||
QDialog {{
|
||||
background: transparent;
|
||||
}}
|
||||
|
||||
#messageContainer {{
|
||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||
stop:0 rgba(255, 255, 255, 0.95),
|
||||
stop:1 rgba(248, 250, 252, 0.95));
|
||||
border: 1px solid {color_scheme['border']};
|
||||
border-radius: 16px;
|
||||
backdrop-filter: blur(10px);
|
||||
}}
|
||||
|
||||
#messageTitle {{
|
||||
font-family: 'Segoe UI', Arial, sans-serif;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
margin: 0;
|
||||
}}
|
||||
|
||||
#messageText {{
|
||||
font-family: 'Segoe UI', Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
color: #475569;
|
||||
line-height: 1.5;
|
||||
}}
|
||||
|
||||
#icon{self.msg_type.title()} {{
|
||||
font-size: 24px;
|
||||
background: {color_scheme['bg']};
|
||||
border: 1px solid {color_scheme['border']};
|
||||
border-radius: 16px;
|
||||
padding: 4px;
|
||||
}}
|
||||
|
||||
#messageButton {{
|
||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||
stop:0 {color_scheme['accent']},
|
||||
stop:1 {color_scheme['accent']});
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 8px;
|
||||
font-family: 'Segoe UI', Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
padding: 8px 16px;
|
||||
}}
|
||||
|
||||
#messageButton:hover {{
|
||||
background: qlineargradient(x1:0, y1:0, x2:0, y2:1,
|
||||
stop:0 {color_scheme['accent']},
|
||||
stop:1 {color_scheme['accent']});
|
||||
transform: translateY(-1px);
|
||||
}}
|
||||
|
||||
#messageButton:pressed {{
|
||||
transform: translateY(0px);
|
||||
}}
|
||||
|
||||
#messageButtonSecondary {{
|
||||
background: rgba(148, 163, 184, 0.1);
|
||||
color: #64748b;
|
||||
border: 1px solid rgba(148, 163, 184, 0.3);
|
||||
border-radius: 8px;
|
||||
font-family: 'Segoe UI', Arial, sans-serif;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
padding: 8px 16px;
|
||||
}}
|
||||
|
||||
#messageButtonSecondary:hover {{
|
||||
background: rgba(148, 163, 184, 0.2);
|
||||
border-color: rgba(148, 163, 184, 0.5);
|
||||
}}
|
||||
"""
|
||||
|
||||
self.setStyleSheet(style)
|
||||
|
||||
def setup_animations(self):
|
||||
"""Setzt Animationen ein"""
|
||||
# Sanftes Einblenden
|
||||
self.setWindowOpacity(0)
|
||||
|
||||
self.fade_timer = QTimer()
|
||||
self.fade_timer.timeout.connect(self.fade_in)
|
||||
self.opacity = 0
|
||||
self.fade_timer.start(16) # ~60 FPS
|
||||
|
||||
def fade_in(self):
|
||||
"""Fade-in Animation"""
|
||||
self.opacity += 0.1
|
||||
if self.opacity >= 1:
|
||||
self.opacity = 1
|
||||
self.fade_timer.stop()
|
||||
self.setWindowOpacity(self.opacity)
|
||||
|
||||
def button_clicked(self, button_text):
|
||||
"""Behandelt Button-Clicks"""
|
||||
self.result_value = button_text
|
||||
self.clicked.emit(button_text)
|
||||
self.accept()
|
||||
|
||||
def mousePressEvent(self, event):
|
||||
"""Ermöglicht das Verschieben des Dialogs"""
|
||||
if event.button() == Qt.LeftButton:
|
||||
self.drag_position = event.globalPos() - self.frameGeometry().topLeft()
|
||||
event.accept()
|
||||
|
||||
def mouseMoveEvent(self, event):
|
||||
"""Verschiebt den Dialog"""
|
||||
if event.buttons() == Qt.LeftButton and hasattr(self, 'drag_position'):
|
||||
self.move(event.globalPos() - self.drag_position)
|
||||
event.accept()
|
||||
|
||||
|
||||
# Convenience-Funktionen für einfache Nutzung
|
||||
def show_info(parent=None, title="Information", message="", buttons=None):
|
||||
"""Zeigt eine moderne Info-MessageBox"""
|
||||
if buttons is None:
|
||||
buttons = ["OK"]
|
||||
dialog = ModernMessageBox(parent, title, message, "info", buttons)
|
||||
return dialog.exec_()
|
||||
|
||||
def show_success(parent=None, title="Erfolg", message="", buttons=None):
|
||||
"""Zeigt eine moderne Erfolg-MessageBox"""
|
||||
if buttons is None:
|
||||
buttons = ["OK"]
|
||||
dialog = ModernMessageBox(parent, title, message, "success", buttons)
|
||||
return dialog.exec_()
|
||||
|
||||
def show_warning(parent=None, title="Warnung", message="", buttons=None):
|
||||
"""Zeigt eine moderne Warnung-MessageBox"""
|
||||
if buttons is None:
|
||||
buttons = ["OK"]
|
||||
dialog = ModernMessageBox(parent, title, message, "warning", buttons)
|
||||
return dialog.exec_()
|
||||
|
||||
def show_error(parent=None, title="Fehler", message="", buttons=None):
|
||||
"""Zeigt eine moderne Fehler-MessageBox"""
|
||||
if buttons is None:
|
||||
buttons = ["OK"]
|
||||
dialog = ModernMessageBox(parent, title, message, "error", buttons)
|
||||
return dialog.exec_()
|
||||
|
||||
def show_critical(parent=None, title="Kritischer Fehler", message="", buttons=None):
|
||||
"""Zeigt eine moderne kritische Fehler-MessageBox"""
|
||||
if buttons is None:
|
||||
buttons = ["OK"]
|
||||
dialog = ModernMessageBox(parent, title, message, "critical", buttons)
|
||||
return dialog.exec_()
|
||||
|
||||
def show_question(parent=None, title="Frage", message="", buttons=None):
|
||||
"""Zeigt eine moderne Frage-MessageBox"""
|
||||
if buttons is None:
|
||||
buttons = ["Ja", "Nein"]
|
||||
dialog = ModernMessageBox(parent, title, message, "question", buttons)
|
||||
return dialog.exec_()
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren