316 Zeilen
10 KiB
Python
316 Zeilen
10 KiB
Python
"""
|
||
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_() |