from PyQt5.QtWidgets import (QWidget, QVBoxLayout, QHBoxLayout, QLabel, QLineEdit, QPushButton, QFrame, QCheckBox, QSpacerItem, QSizePolicy) from PyQt5.QtCore import Qt, pyqtSignal, QPropertyAnimation, QEasingCurve, QPoint, QSize, QTimer, QPointF from PyQt5.QtGui import QPixmap, QPainter, QLinearGradient, QColor, QIcon, QFont, QBrush class LoginPage(QWidget): login_success = pyqtSignal(str) def __init__(self): super().__init__() self.init_ui() self.setup_animations() self.gradient_angle = 0 self.particle_timer = QTimer(self) self.particle_timer.timeout.connect(self.update_particles) self.particle_timer.start(100) # 更新粒子位置的定时器 self.particles = self.create_particles() # 添加回车键提交支持 self.username_input.returnPressed.connect(self.handle_login) self.password_input.returnPressed.connect(self.handle_login) def init_ui(self): self.setObjectName("loginPage") self.setMinimumSize(800, 600) # 主要布局 main_layout = QHBoxLayout() main_layout.setContentsMargins(0, 0, 0, 0) main_layout.setSpacing(0) self.setLayout(main_layout) # 左侧装饰区 - 添加玻璃态效果 self.left_frame = QWidget() # 改为QWidget以便自定义绘制 self.left_frame.setObjectName("leftDecor") self.left_frame.setSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding) # 在左侧装饰区添加艺术字体和版本信息 left_layout = QVBoxLayout(self.left_frame) left_layout.setContentsMargins(40, 40, 40, 40) left_layout.addStretch() # 艺术字体标题 title_container = QFrame() title_container.setObjectName("titleContainer") title_container.setStyleSheet("background-color: rgba(0, 0, 0, 0.2); border-radius: 15px;") title_layout = QVBoxLayout(title_container) title_layout.setContentsMargins(30, 30, 30, 30) app_title = QLabel("奶酪云工具箱") app_title.setObjectName("appTitle") app_title.setAlignment(Qt.AlignCenter) app_title.setStyleSheet(""" #appTitle { font-size: 56px; font-weight: bold; color: white; font-family: 'SimHei', 'Microsoft YaHei', sans-serif; text-shadow: 0 0 10px rgba(255, 255, 255, 0.5), 0 0 20px rgba(99, 102, 241, 0.8), 0 0 30px rgba(99, 102, 241, 0.6), 0 0 40px rgba(99, 102, 241, 0.4); letter-spacing: 5px; } """) subtitle = QLabel("高效 · 安全 · 云端协作") subtitle.setObjectName("subtitle") subtitle.setAlignment(Qt.AlignCenter) subtitle.setStyleSheet(""" #subtitle { font-size: 18px; color: rgba(255, 255, 255, 0.85); letter-spacing: 3px; margin-top: 10px; } """) title_layout.addWidget(app_title) title_layout.addWidget(subtitle) # 版本信息 version_info = QWidget() version_layout = QVBoxLayout(version_info) version_layout.setAlignment(Qt.AlignBottom | Qt.AlignCenter) version_label = QLabel("版本 0.0.1") version_label.setObjectName("versionLabel") version_label.setAlignment(Qt.AlignCenter) version_label.setStyleSheet(""" #versionLabel { color: rgba(255, 255, 255, 0.7); font-size: 14px; } """) author_label = QLabel("作者:年糕崽崽") author_label.setObjectName("authorLabel") author_label.setAlignment(Qt.AlignCenter) author_label.setStyleSheet(""" #authorLabel { color: rgba(255, 255, 255, 0.7); font-size: 14px; margin-top: 5px; } """) version_layout.addWidget(version_label) version_layout.addWidget(author_label) # 添加到左侧布局 left_layout.addWidget(title_container) left_layout.addStretch() left_layout.addWidget(version_info) # 右侧登录区 - 深色背景 right_frame = QFrame() right_frame.setFixedWidth(400) right_frame.setObjectName("rightFrame") right_frame.setStyleSheet(""" #rightFrame { background-color: rgba(45, 55, 72, 0.85); border-left: 1px solid rgba(255, 255, 255, 0.1); backdrop-filter: blur(10px); } """) main_layout.addWidget(self.left_frame, 3) main_layout.addWidget(right_frame, 1) # 右侧登录区布局 login_layout = QVBoxLayout(right_frame) login_layout.setContentsMargins(40, 80, 40, 40) login_layout.setSpacing(0) # 标题 - 浅色文字 title_layout = QVBoxLayout() title_layout.setSpacing(10) welcome_label = QLabel("欢迎回来") welcome_label.setObjectName("welcomeLabel") welcome_label.setAlignment(Qt.AlignCenter) logo_label = QLabel() logo_pixmap = QPixmap(":/icons/app_icon.png").scaled(80, 80, Qt.KeepAspectRatio, Qt.SmoothTransformation) # 使图标变为白色以适应深色背景 painter = QPainter(logo_pixmap) painter.setCompositionMode(QPainter.CompositionMode_SourceIn) painter.fillRect(logo_pixmap.rect(), Qt.white) painter.end() logo_label.setPixmap(logo_pixmap) logo_label.setAlignment(Qt.AlignCenter) title_label = QLabel("文档处理中心") title_label.setObjectName("titleLabel") title_label.setAlignment(Qt.AlignCenter) title_layout.addWidget(welcome_label) title_layout.addWidget(logo_label) title_layout.addWidget(title_label) # 表单区 form_frame = QFrame() form_frame.setObjectName("formFrame") form_layout = QVBoxLayout(form_frame) form_layout.setContentsMargins(0, 40, 0, 0) form_layout.setSpacing(25) # 用户名输入框 username_frame = QFrame() username_frame.setObjectName("inputFrame") username_layout = QHBoxLayout(username_frame) username_layout.setContentsMargins(15, 0, 15, 0) username_layout.setSpacing(10) username_icon = QLabel() username_pixmap = QPixmap(":/icons/user.png").scaled(24, 24, Qt.KeepAspectRatio, Qt.SmoothTransformation) # 使图标变为白色 painter = QPainter(username_pixmap) painter.setCompositionMode(QPainter.CompositionMode_SourceIn) painter.fillRect(username_pixmap.rect(), Qt.white) painter.end() username_icon.setPixmap(username_pixmap) self.username_input = QLineEdit() self.username_input.setObjectName("usernameInput") self.username_input.setPlaceholderText("请输入用户名") self.username_input.setStyleSheet(""" #usernameInput { border: none; background: transparent; font-size: 15px; color: #e2e8f0; padding: 12px 0; } #usernameInput:focus { outline: none; } """) self.username_input.setAttribute(Qt.WA_MacShowFocusRect, 0) username_layout.addWidget(username_icon) username_layout.addWidget(self.username_input) # 密码输入框 password_frame = QFrame() password_frame.setObjectName("inputFrame") password_layout = QHBoxLayout(password_frame) password_layout.setContentsMargins(15, 0, 15, 0) password_layout.setSpacing(10) password_icon = QLabel() password_pixmap = QPixmap(":/icons/lock.png").scaled(24, 24, Qt.KeepAspectRatio, Qt.SmoothTransformation) # 使图标变为白色 painter = QPainter(password_pixmap) painter.setCompositionMode(QPainter.CompositionMode_SourceIn) painter.fillRect(password_pixmap.rect(), Qt.white) painter.end() password_icon.setPixmap(password_pixmap) self.password_input = QLineEdit() self.password_input.setObjectName("passwordInput") self.password_input.setPlaceholderText("请输入密码") self.password_input.setEchoMode(QLineEdit.Password) self.password_input.setStyleSheet(""" #passwordInput { border: none; background: transparent; font-size: 15px; color: #e2e8f0; padding: 12px 0; } #passwordInput:focus { outline: none; } """) self.password_input.setAttribute(Qt.WA_MacShowFocusRect, 0) # 显示/隐藏密码按钮 self.toggle_password_btn = QPushButton() self.toggle_password_btn.setObjectName("togglePasswordBtn") # 加载图标并设置为白色 eye_off_pixmap = QPixmap(":/icons/eye_off.png").scaled(24, 24, Qt.KeepAspectRatio, Qt.SmoothTransformation) painter = QPainter(eye_off_pixmap) painter.setCompositionMode(QPainter.CompositionMode_SourceIn) painter.fillRect(eye_off_pixmap.rect(), Qt.white) painter.end() self.toggle_password_btn.setIcon(QIcon(eye_off_pixmap)) self.toggle_password_btn.setIconSize(QSize(18, 18)) self.toggle_password_btn.setStyleSheet("background: transparent; border: none;") self.toggle_password_btn.clicked.connect(self.toggle_password_visibility) password_layout.addWidget(password_icon) password_layout.addWidget(self.password_input) password_layout.addWidget(self.toggle_password_btn) # 记住我选项 remember_layout = QHBoxLayout() remember_layout.setContentsMargins(15, 0, 0, 0) self.remember_check = QCheckBox("记住我") self.remember_check.setObjectName("rememberCheck") self.remember_check.setStyleSheet(""" #rememberCheck { color: #cbd5e0; font-size: 13px; } """) forget_btn = QPushButton("忘记密码?") forget_btn.setObjectName("forgetPassword") forget_btn.setStyleSheet(""" #forgetPassword { color: #a0aec0; font-size: 13px; background: transparent; border: none; text-decoration: none; } #forgetPassword:hover { color: #818cf8; text-decoration: underline; } """) remember_layout.addWidget(self.remember_check) remember_layout.addStretch() remember_layout.addWidget(forget_btn) # 错误提示 self.error_label = QLabel() self.error_label.setObjectName("errorLabel") self.error_label.setStyleSheet(""" #errorLabel { color: #fc8181; font-size: 13px; padding-left: 15px; min-height: 20px; } """) self.error_label.hide() # 登录按钮 login_btn = QPushButton("登录") login_btn.setObjectName("loginBtn") login_btn.clicked.connect(self.handle_login) # 添加表单元素 form_layout.addWidget(username_frame) form_layout.addWidget(password_frame) form_layout.addLayout(remember_layout) form_layout.addWidget(self.error_label) form_layout.addWidget(login_btn, 0, Qt.AlignCenter) # 底部区域 footer_layout = QVBoxLayout() footer_layout.setContentsMargins(0, 30, 0, 0) footer_layout.setSpacing(10) signup_layout = QHBoxLayout() signup_layout.setAlignment(Qt.AlignCenter) signup_label = QLabel("没有账户?") signup_label.setStyleSheet("color: #a0aec0; font-size: 13px;") signup_btn = QPushButton("立即注册") signup_btn.setObjectName("signupBtn") signup_btn.setStyleSheet(""" #signupBtn { background: transparent; border: none; color: #818cf8; font-weight: 600; font-size: 13px; padding: 0 5px; } #signupBtn:hover { text-decoration: underline; } """) signup_layout.addWidget(signup_label) signup_layout.addWidget(signup_btn) footer_layout.addLayout(signup_layout, Qt.AlignCenter) # 添加所有组件到主布局 login_layout.addLayout(title_layout) login_layout.addWidget(form_frame) login_layout.addLayout(footer_layout) # 应用样式 self.apply_styles() def apply_styles(self): # 设置全局样式 - 暗色主题 self.setStyleSheet(""" /* 登录页面背景渐变 - 深色 */ #loginPage { background: qlineargradient(x1:0, y1:0, x2:1, y2:1, stop:0 #1a202c, stop:1 #2d3748); } /* 左侧装饰区玻璃效果 - 深色 */ #leftDecor { background: rgba(45, 55, 72, 0.15); border-radius: 0px; backdrop-filter: blur(15px); border: 1px solid rgba(255, 255, 255, 0.05); border-left: none; } /* 欢迎文本 - 浅色 */ #welcomeLabel { font-size: 22px; font-weight: 300; color: #e2e8f0; letter-spacing: 2px; } /* 主标题 - 浅色 */ #titleLabel { font-size: 28px; font-weight: bold; color: #ffffff; margin-bottom: 15px; letter-spacing: 1px; } /* 输入框容器 - 深色 */ #inputFrame { background: rgba(30, 41, 59, 0.5); border: 1px solid #4a5568; border-radius: 8px; padding: 5px 0; transition: all 0.3s ease; } #inputFrame:hover { border-color: #5b6e91; box-shadow: 0 2px 8px rgba(99, 102, 241, 0.2); } #inputFrame:focus-within { border-color: #818cf8; box-shadow: 0 2px 12px rgba(99, 102, 241, 0.3); } /* 登录按钮 - 紫色主题 */ #loginBtn { background-color: #6366f1; color: white; border-radius: 8px; padding: 14px 30px; font-size: 16px; font-weight: 600; border: none; min-width: 240px; transition: all 0.3s ease; } #loginBtn:hover { background-color: #4f46e5; transform: translateY(-2px); box-shadow: 0 4px 12px rgba(99, 102, 241, 0.4); } #loginBtn:pressed { transform: translateY(0); box-shadow: 0 2px 6px rgba(99, 102, 241, 0.3); } """) def setup_animations(self): # 创建输入框动画 self.username_anim = QPropertyAnimation(self.username_input, b"pos") self.username_anim.setDuration(300) self.username_anim.setEasingCurve(QEasingCurve.OutQuad) self.password_anim = QPropertyAnimation(self.password_input, b"pos") self.password_anim.setDuration(300) self.password_anim.setEasingCurve(QEasingCurve.OutQuad) def create_particles(self): particles = [] for _ in range(30): # 创建30个粒子 size = 2 + 5 * (0.5) # 随机大小 x = 50 + 700 * (0.5) # 随机X位置 y = 50 + 500 * (0.5) # 随机Y位置 speed_x = -1 + 2 * (0.5) # X方向速度 speed_y = -1 + 2 * (0.5) # Y方向速度 color = QColor( # 随机蓝色调 int(100 + 155 * (0.5)), int(180 + 75 * (0.5)), int(255 - 50 * (0.5)), int(50 + 100 * (0.5)) ) particles.append({ 'x': x, 'y': y, 'size': size, 'speed_x': speed_x, 'speed_y': speed_y, 'color': color }) return particles def update_particles(self): for p in self.particles: # 更新粒子位置 p['x'] += p['speed_x'] p['y'] += p['speed_y'] # 边界反弹 if p['x'] <= 0 or p['x'] >= self.width(): p['speed_x'] *= -1 if p['y'] <= 0 or p['y'] >= self.height(): p['speed_y'] *= -1 self.left_frame.update() # 触发重绘 def toggle_password_visibility(self): if self.password_input.echoMode() == QLineEdit.Password: self.password_input.setEchoMode(QLineEdit.Normal) # 更新眼睛图标 eye_on_pixmap = QPixmap(":/icons/eye_on.png").scaled(24, 24, Qt.KeepAspectRatio, Qt.SmoothTransformation) painter = QPainter(eye_on_pixmap) painter.setCompositionMode(QPainter.CompositionMode_SourceIn) painter.fillRect(eye_on_pixmap.rect(), Qt.white) painter.end() self.toggle_password_btn.setIcon(QIcon(eye_on_pixmap)) else: self.password_input.setEchoMode(QLineEdit.Password) # 更新眼睛图标 eye_off_pixmap = QPixmap(":/icons/eye_off.png").scaled(24, 24, Qt.KeepAspectRatio, Qt.SmoothTransformation) painter = QPainter(eye_off_pixmap) painter.setCompositionMode(QPainter.CompositionMode_SourceIn) painter.fillRect(eye_off_pixmap.rect(), Qt.white) painter.end() self.toggle_password_btn.setIcon(QIcon(eye_off_pixmap)) def handle_login(self): username = self.username_input.text().strip() password = self.password_input.text().strip() # 验证登录 if not username: self.show_error("请输入用户名") self.animate_error(self.username_input) return if not password: self.show_error("请输入密码") self.animate_error(self.password_input) return # 实际登录验证 if username in ["liqi", "qiqi991012"] and password == "qiqi991012": self.error_label.hide() self.login_success.emit(username) else: self.show_error("用户名或密码错误") self.animate_error(self.password_input) def animate_error(self, input_field): # 保存原始位置 original_pos = input_field.pos() # 创建抖动动画 animation = QPropertyAnimation(input_field, b"pos") animation.setDuration(70) animation.setLoopCount(3) animation.setEasingCurve(QEasingCurve.InOutSine) # 左右各抖动3次 positions = [ QPoint(original_pos.x() + 5, original_pos.y()), QPoint(original_pos.x() - 5, original_pos.y()), original_pos ] for i, pos in enumerate(positions): animation.setKeyValueAt(i / len(positions), pos) animation.start() def show_error(self, message): self.error_label.setText(message) self.error_label.show() # 添加红色边框效果(暗色主题版本) self.username_input.parent().setStyleSheet("#inputFrame { border: 1px solid #fc8181; }") self.password_input.parent().setStyleSheet("#inputFrame { border: 1px solid #fc8181; }") # 2秒后恢复 QTimer.singleShot(2000, self.reset_styles) def reset_styles(self): self.username_input.parent().setStyleSheet("#inputFrame { border: 1px solid #4a5568; }") self.password_input.parent().setStyleSheet("#inputFrame { border: 1px solid #4a5568; }") def paintEvent(self, event): # 绘制背景渐变 - 深色主题 painter = QPainter(self) painter.setRenderHint(QPainter.Antialiasing) # 创建渐变背景 gradient = QLinearGradient(0, 0, self.width(), self.height()) gradient.setColorAt(0, QColor("#1a202c")) gradient.setColorAt(1, QColor("#2d3748")) painter.fillRect(self.rect(), gradient) # 绘制装饰性气泡 - 深色主题 painter.setPen(Qt.NoPen) # 左上角大泡 - 使用深紫色 painter.setBrush(QColor(79, 70, 229, 30)) painter.drawEllipse(QPoint(50, 70), 150, 150) # 左下角中泡 - 使用深蓝色 painter.setBrush(QColor(49, 46, 129, 40)) painter.drawEllipse(QPoint(100, 450), 80, 80) # 右边中泡 - 使用深青色 painter.setBrush(QColor(15, 23, 42, 50)) painter.drawEllipse(QPoint(600, 150), 100, 100) # 中央小泡 - 使用深绿色 painter.setBrush(QColor(6, 78, 59, 60)) painter.drawEllipse(QPoint(250, 300), 50, 50) super().paintEvent(event) # 专门绘制左侧装饰区域的粒子效果 if self.left_frame.isVisible(): # 保存当前绘画状态 painter.save() # 将绘画区域限制在左侧框架内 painter.translate(self.left_frame.pos()) painter.setClipRect(self.left_frame.rect()) # 绘制动态粒子 for p in self.particles: painter.setBrush(QBrush(p['color'])) painter.setPen(Qt.NoPen) painter.drawEllipse(QPointF(p['x'], p['y']), p['size'], p['size']) # 绘制发光的光斑背景 gradient = QLinearGradient(0, 0, self.width(), self.height()) gradient.setColorAt(0, QColor(99, 102, 241, 20)) # 紫色 gradient.setColorAt(0.5, QColor(79, 70, 229, 40)) # 更亮的紫色 gradient.setColorAt(1, QColor(49, 46, 129, 20)) # 深紫色 painter.setBrush(QBrush(gradient)) painter.drawRect(0, 0, self.left_frame.width(), self.left_frame.height()) painter.restore() # 使用示例 if __name__ == "__main__": from PyQt5.QtWidgets import QApplication import sys app = QApplication(sys.argv) login_page = LoginPage() login_page.login_success.connect(lambda username: print(f"登录成功: {username}")) login_page.show() sys.exit(app.exec_())