镜像自地址
https://github.com/a13zhy/a13rollcall.git
已同步 2026-10-20 03:17:47 +08:00
304 行
10 KiB
Python
304 行
10 KiB
Python
# -*- coding: utf-8 -*-
|
|
"""
|
|
A13课堂点名系统 - 现代化错误提示对话框
|
|
美观的错误展示窗口,支持错误详情查看、复制、报告等功能
|
|
"""
|
|
|
|
from PyQt5.QtWidgets import (
|
|
QDialog, QVBoxLayout, QHBoxLayout, QLabel, QPushButton,
|
|
QTextEdit, QFrame, QSizePolicy, QApplication, QWidget
|
|
)
|
|
from PyQt5.QtCore import Qt, QTimer, QPropertyAnimation, QEasingCurve, QSize
|
|
from PyQt5.QtGui import QFont, QColor, QPainter, QBrush, QPen, QIcon, QPixmap
|
|
|
|
|
|
class ErrorIconWidget(QWidget):
|
|
"""错误图标组件"""
|
|
|
|
def __init__(self, error_type="error", parent=None):
|
|
super().__init__(parent)
|
|
self.error_type = error_type
|
|
self.setFixedSize(64, 64)
|
|
|
|
def paintEvent(self, event):
|
|
painter = QPainter(self)
|
|
painter.setRenderHint(QPainter.Antialiasing)
|
|
|
|
colors = {
|
|
"error": ("#c42b1c", "#e81123"),
|
|
"warning": ("#ca5010", "#ff8c00"),
|
|
"info": ("#0067c0", "#0078d4"),
|
|
"success": ("#107c10", "#0e6c0e")
|
|
}
|
|
color1, color2 = colors.get(self.error_type, colors["error"])
|
|
|
|
painter.setPen(Qt.NoPen)
|
|
painter.setBrush(QBrush(QColor(color1)))
|
|
painter.drawEllipse(4, 4, 56, 56)
|
|
|
|
painter.setBrush(QBrush(QColor(color2)))
|
|
painter.drawEllipse(8, 8, 48, 48)
|
|
|
|
painter.setPen(QPen(QColor("white"), 4, Qt.SolidLine, Qt.RoundCap))
|
|
if self.error_type == "error":
|
|
painter.drawLine(22, 22, 42, 42)
|
|
painter.drawLine(42, 22, 22, 42)
|
|
elif self.error_type == "warning":
|
|
painter.drawLine(32, 18, 32, 36)
|
|
painter.drawEllipse(30, 40, 4, 4)
|
|
elif self.error_type == "info":
|
|
painter.drawLine(32, 18, 32, 22)
|
|
painter.drawEllipse(30, 14, 4, 4)
|
|
painter.drawLine(32, 28, 32, 44)
|
|
elif self.error_type == "success":
|
|
painter.drawLine(20, 32, 28, 40)
|
|
painter.drawLine(28, 40, 44, 24)
|
|
|
|
painter.end()
|
|
|
|
|
|
class ModernErrorDialog(QDialog):
|
|
"""
|
|
现代化错误提示对话框
|
|
美观的错误展示,支持详情查看、复制、报告
|
|
"""
|
|
|
|
def __init__(self, title="发生错误", message="", details="",
|
|
error_type="error", parent=None):
|
|
super().__init__(parent)
|
|
self.title_text = title
|
|
self.message_text = message
|
|
self.details_text = details
|
|
self.error_type = error_type
|
|
self._show_details = False
|
|
self._setup_ui()
|
|
self._setup_animation()
|
|
|
|
def _setup_ui(self):
|
|
self.setWindowTitle(self.title_text)
|
|
self.setMinimumWidth(480)
|
|
self.setMaximumWidth(600)
|
|
self.setWindowFlags(Qt.Dialog | Qt.FramelessWindowHint)
|
|
self.setAttribute(Qt.WA_TranslucentBackground)
|
|
|
|
main_container = QFrame()
|
|
main_container.setStyleSheet("""
|
|
QFrame {
|
|
background: white;
|
|
border-radius: 12px;
|
|
border: 1px solid #e0e0e0;
|
|
}
|
|
""")
|
|
|
|
shadow_layout = QVBoxLayout(self)
|
|
shadow_layout.setContentsMargins(12, 12, 12, 12)
|
|
shadow_layout.addWidget(main_container)
|
|
|
|
layout = QVBoxLayout(main_container)
|
|
layout.setContentsMargins(24, 24, 24, 20)
|
|
layout.setSpacing(16)
|
|
|
|
header = QHBoxLayout()
|
|
header.setSpacing(16)
|
|
|
|
self.icon_widget = ErrorIconWidget(self.error_type)
|
|
header.addWidget(self.icon_widget)
|
|
|
|
text_layout = QVBoxLayout()
|
|
text_layout.setSpacing(4)
|
|
|
|
title_label = QLabel(self.title_text)
|
|
title_label.setStyleSheet("""
|
|
font-size: 18px;
|
|
font-weight: 800;
|
|
color: #1a1a1a;
|
|
""")
|
|
text_layout.addWidget(title_label)
|
|
|
|
if self.message_text:
|
|
message_label = QLabel(self.message_text)
|
|
message_label.setStyleSheet("""
|
|
font-size: 13px;
|
|
color: #666666;
|
|
""")
|
|
message_label.setWordWrap(True)
|
|
text_layout.addWidget(message_label)
|
|
|
|
header.addLayout(text_layout, 1)
|
|
layout.addLayout(header)
|
|
|
|
self.details_container = QFrame()
|
|
self.details_container.setStyleSheet("""
|
|
QFrame {
|
|
background: #f8f8f8;
|
|
border-radius: 8px;
|
|
border: 1px solid #e8e8e8;
|
|
}
|
|
""")
|
|
details_layout = QVBoxLayout(self.details_container)
|
|
details_layout.setContentsMargins(12, 12, 12, 12)
|
|
details_layout.setSpacing(8)
|
|
|
|
details_header = QHBoxLayout()
|
|
details_title = QLabel("错误详情")
|
|
details_title.setStyleSheet("font-size: 12px; font-weight: 700; color: #333;")
|
|
details_header.addWidget(details_title)
|
|
details_header.addStretch()
|
|
details_layout.addLayout(details_header)
|
|
|
|
self.details_text = QTextEdit()
|
|
self.details_text.setReadOnly(True)
|
|
self.details_text.setMaximumHeight(150)
|
|
self.details_text.setStyleSheet("""
|
|
QTextEdit {
|
|
background: white;
|
|
border: 1px solid #e0e0e0;
|
|
border-radius: 6px;
|
|
padding: 8px;
|
|
font-family: Consolas, monospace;
|
|
font-size: 11px;
|
|
color: #333;
|
|
}
|
|
""")
|
|
self.details_text.setPlainText(self.details_text or "暂无详细信息")
|
|
details_layout.addWidget(self.details_text)
|
|
|
|
self.details_container.setVisible(False)
|
|
layout.addWidget(self.details_container)
|
|
|
|
btn_layout = QHBoxLayout()
|
|
btn_layout.setSpacing(8)
|
|
|
|
if self.details_text:
|
|
self.toggle_btn = QPushButton("显示详情")
|
|
self.toggle_btn.setStyleSheet("""
|
|
QPushButton {
|
|
background: transparent;
|
|
color: #0067c0;
|
|
border: 1px solid #0067c0;
|
|
border-radius: 6px;
|
|
padding: 8px 16px;
|
|
font-weight: 600;
|
|
font-size: 12px;
|
|
}
|
|
QPushButton:hover {
|
|
background: #e5f1fb;
|
|
}
|
|
""")
|
|
self.toggle_btn.setCursor(Qt.PointingHandCursor)
|
|
self.toggle_btn.clicked.connect(self._toggle_details)
|
|
btn_layout.addWidget(self.toggle_btn)
|
|
|
|
copy_btn = QPushButton("复制错误")
|
|
copy_btn.setStyleSheet("""
|
|
QPushButton {
|
|
background: transparent;
|
|
color: #666;
|
|
border: 1px solid #ddd;
|
|
border-radius: 6px;
|
|
padding: 8px 16px;
|
|
font-weight: 600;
|
|
font-size: 12px;
|
|
}
|
|
QPushButton:hover {
|
|
background: #f5f5f5;
|
|
}
|
|
""")
|
|
copy_btn.setCursor(Qt.PointingHandCursor)
|
|
copy_btn.clicked.connect(self._copy_error)
|
|
btn_layout.addWidget(copy_btn)
|
|
|
|
btn_layout.addStretch()
|
|
|
|
ok_btn = QPushButton("确定")
|
|
ok_btn.setStyleSheet("""
|
|
QPushButton {
|
|
background: #0067c0;
|
|
color: white;
|
|
border: none;
|
|
border-radius: 6px;
|
|
padding: 8px 24px;
|
|
font-weight: 700;
|
|
font-size: 12px;
|
|
}
|
|
QPushButton:hover {
|
|
background: #106ebe;
|
|
}
|
|
QPushButton:pressed {
|
|
background: #005a9e;
|
|
}
|
|
""")
|
|
ok_btn.setCursor(Qt.PointingHandCursor)
|
|
ok_btn.clicked.connect(self.accept)
|
|
ok_btn.setDefault(True)
|
|
btn_layout.addWidget(ok_btn)
|
|
|
|
layout.addLayout(btn_layout)
|
|
|
|
def _setup_animation(self):
|
|
self._opacity = 0.0
|
|
self.setWindowOpacity(0.0)
|
|
self._fade_timer = QTimer(self)
|
|
self._fade_timer.timeout.connect(self._fade_in_step)
|
|
self._fade_timer.start(16)
|
|
|
|
def _fade_in_step(self):
|
|
self._opacity += 0.08
|
|
if self._opacity >= 1.0:
|
|
self._opacity = 1.0
|
|
self._fade_timer.stop()
|
|
self.setWindowOpacity(self._opacity)
|
|
|
|
def _toggle_details(self):
|
|
self._show_details = not self._show_details
|
|
self.details_container.setVisible(self._show_details)
|
|
self.toggle_btn.setText("隐藏详情" if self._show_details else "显示详情")
|
|
if self._show_details:
|
|
self.setMinimumHeight(400)
|
|
else:
|
|
self.setMinimumHeight(0)
|
|
self.adjustSize()
|
|
|
|
def _copy_error(self):
|
|
try:
|
|
clipboard = QApplication.clipboard()
|
|
error_info = f"错误标题: {self.title_text}\n错误信息: {self.message_text}\n\n错误详情:\n{self.details_text}"
|
|
clipboard.setText(error_info)
|
|
self._show_toast("错误信息已复制到剪贴板")
|
|
except Exception as e:
|
|
print(f"复制失败: {e}")
|
|
|
|
def _show_toast(self, message):
|
|
try:
|
|
toast = QLabel(message, self)
|
|
toast.setStyleSheet("""
|
|
background: #333;
|
|
color: white;
|
|
padding: 8px 16px;
|
|
border-radius: 6px;
|
|
font-size: 12px;
|
|
""")
|
|
toast.adjustSize()
|
|
x = (self.width() - toast.width()) // 2
|
|
y = self.height() - 60
|
|
toast.move(x, y)
|
|
toast.show()
|
|
QTimer.singleShot(1500, toast.close)
|
|
except Exception:
|
|
pass
|
|
|
|
|
|
def show_error_dialog(title="发生错误", message="", details="",
|
|
error_type="error", parent=None):
|
|
"""显示现代化错误对话框的便捷函数"""
|
|
dialog = ModernErrorDialog(title, message, details, error_type, parent)
|
|
return dialog.exec_()
|
|
|
|
|
|
def show_exception_dialog(exception: Exception, title="程序异常", parent=None):
|
|
"""显示异常对话框的便捷函数"""
|
|
import traceback
|
|
details = traceback.format_exc()
|
|
message = str(exception)
|
|
return show_error_dialog(title, message, details, "error", parent)
|