1. 导入模块部分

import os
import sys
import random
from PyQt5.QtGui import *
from PyQt5.QtCore import *
from PyQt5.QtWidgets import *
  • 作用:导入程序依赖的模块。
    • os:用于文件路径处理(如拼接目录、判断文件是否存在)。
    • sys:用于程序退出(sys.exit())。
    • random:用于随机选择动画和对话内容。
    • PyQt5相关模块:提供 GUI 组件(窗口、标签、托盘等)、核心功能(定时器、事件处理)和绘图支持(GIF 显示、图标等)。

2. 主类定义:DesktopPet(QWidget)

该类继承自QWidget(PyQt5 的基础窗口类),是整个桌面宠物的核心,包含所有功能实现。

3. 初始化方法:__init__

def __init__(self, parent=None, **kwargs):
    super(DesktopPet, self).__init__(parent,** kwargs)
    # 基础设置
    self.scale_factor = 0.6  # 缩放比例
    self.is_dragging = False  # 拖动状态标记

    # 路径设置
    self.project_dir = r"C:\Users\30685\Desktop\py\蜡笔小新桌宠"  # 项目根目录
    self.normal_dir = os.path.join(self.project_dir, "normal")  # 正常动画目录
    self.click_dir = os.path.join(self.project_dir, "click")  # 点击动画目录

    # 文件路径
    self.click_gif_path = os.path.join(self.click_dir, "click.gif")
    self.default_gif_path = os.path.join(self.normal_dir, "normal1.gif")
    self.tray_icon_path = os.path.join(self.normal_dir, "tigerIcon.jpg")
    self.dialog_path = os.path.join(self.normal_dir, "dialog.txt")

    # 初始化组件
    self.init_window()
    self.init_tray()
    self.init_pet_resources()
    self.init_interaction()

    # 显示窗口
    self.adjustSize()
    self.random_position()
    self.show()

  • 作用:初始化桌面宠物的基础属性、资源路径,并调用其他初始化方法完成程序启动。
  • 关键细节
    • scale_factor:控制动画的缩放比例(0.6 表示缩小到原图的 60%)。
    • 路径设置:明确了程序依赖的资源文件(GIF 动画、图标、对话文本)的位置,需确保实际文件与路径对应,否则会报错。
    • 初始化方法调用:通过init_window等方法分模块初始化,代码结构清晰。
    • 窗口显示:启动时随机位置显示,并自动调整大小。

4. 窗口基础设置:init_window

def init_window(self):
    # 无边框、置顶、透明背景
    self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.SubWindow)
    self.setAutoFillBackground(False)
    self.setAttribute(Qt.WA_TranslucentBackground, True)
    self.repaint()
  • 作用:设置窗口的基础样式,使其符合 “桌宠” 的视觉效果。
  • 关键细节
    • Qt.FramelessWindowHint:去除窗口边框(使桌宠看起来像独立的角色,而非传统窗口)。
    • Qt.WindowStaysOnTopHint:窗口置顶(确保桌宠始终显示在其他窗口上方)。
    • Qt.WA_TranslucentBackground:透明背景(避免 GIF 动画周围出现白色边框,提升融合感)。

5. 系统托盘设置:init_tray

def init_tray(self):
    # 创建托盘菜单
    self.tray_menu = QMenu(self)
    font = QFont("SimHei")  # 支持中文显示
    self.tray_menu.setFont(font)

    # 菜单动作
    quit_action = QAction('退出', self, triggered=self.quit)
    quit_action.setIcon(QIcon(self.tray_icon_path))

    show_action = QAction('显示', self, triggered=self.show_window)

    # 添加菜单动作
    self.tray_menu.addAction(quit_action)
    self.tray_menu.addAction(show_action)

    # 初始化托盘
    self.tray = QSystemTrayIcon(self)
    self.tray.setIcon(QIcon(self.tray_icon_path))
    self.tray.setContextMenu(self.tray_menu)
    self.tray.show()

  • 作用:创建系统托盘图标和右键菜单,方便用户操作(如退出程序、显示隐藏的桌宠)。
  • 关键细节
    • 中文支持:通过QFont("SimHei")设置字体,确保菜单中的中文正常显示。
    • 托盘菜单功能:
      • “退出”:调用quit方法关闭程序。
      • “显示”:调用show_window方法恢复隐藏的桌宠。
    • 托盘图标:设置托盘显示的图标,增强辨识度。

6. 资源初始化:init_pet_resources

def init_pet_resources(self):
    # 创建主布局
    main_layout = QVBoxLayout(self)
    main_layout.setContentsMargins(0, 0, 0, 0)

    # 图片标签(显示动画)
    self.image_label = QLabel(self)
    self.image_label.setAlignment(Qt.AlignCenter)

    # 对话标签(显示文本)
    self.talk_label = QLabel(self)
    self.talk_label.setWordWrap(True)
    self.talk_label.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Preferred)
    self.talk_label.setFont(QFont("KaiTi", 10))
    self.talk_label.setStyleSheet("""
        color: blue; 
        background-color: rgba(255,255,255,150);
        padding: 5px; 
        border-radius: 3px; 
    """)
    self.talk_label.setAlignment(Qt.AlignCenter)

    # 添加到布局
    main_layout.addWidget(self.image_label)
    main_layout.addWidget(self.talk_label)
    self.setLayout(main_layout)

    # 加载对话内容和动画
    self.load_dialogs()
    self.load_normal_animations()
    self.update_animation(self.default_gif_path)
  • 作用:初始化显示动画和对话的 UI 组件,并加载资源文件。
  • 关键细节
    • 布局管理:使用QVBoxLayout(垂直布局),将动画标签(image_label)和对话标签(talk_label)上下排列。
    • 对话标签样式:
      • WordWrap(True):文本自动换行(避免对话过长超出显示范围)。
      • 样式表:设置文本颜色(蓝色)、半透明背景(rgba (255,255,255,150))、内边距和圆角,使对话看起来像 “气泡”。
    • 资源加载:调用load_dialogsload_normal_animations加载文本和动画资源,并默认显示normal1.gif

7. 交互逻辑初始化:init_interaction

def init_interaction(self):
    self.state = 0  # 0: 待机状态, 1: 点击/拖动状态

    # 正常动画切换定时器(3秒一次)
    self.normal_anim_timer = QTimer()
    self.normal_anim_timer.timeout.connect(self.switch_normal_animation)
    self.normal_anim_timer.start(3000)

    # 对话切换定时器(5秒一次)
    self.talk_timer = QTimer()
    self.talk_timer.timeout.connect(self.update_talk)
    self.talk_timer.start(5000)

    # 初始对话
    self.update_talk()
  • 作用:初始化桌宠的自动交互逻辑(定时切换动画和对话)。
  • 关键细节
    • state:状态标记(0 = 待机,1 = 被点击 / 拖动),用于控制不同状态下的动画和对话显示。
    • 定时器(QTimer):
      • normal_anim_timer:3 秒触发一次,调用switch_normal_animation随机切换正常状态的动画。
      • talk_timer:5 秒触发一次,调用update_talk随机切换对话内容。

8. 资源加载方法

load_dialogs(加载对话内容)
def load_dialogs(self):
    self.dialogs = []
    try:
        with open(self.dialog_path, "r", encoding="utf-8") as f:
            self.dialogs = [line.strip() for line in f if line.strip()]
    except FileNotFoundError:
        self.dialogs = [
            "你好呀!我是你的桌面宠物~",
            "今天过得怎么样?",
            "天气真好,适合出去走走呢!",
            "拖动我可以改变位置哦~"
        ]
  • 作用:从dialog.txt文件加载对话文本,若文件不存在则使用默认对话。
  • 关键细节
    • 容错处理:若dialog.txt缺失,程序不会崩溃,而是使用预设的默认对话。
    • 中文支持:打开文件时指定encoding="utf-8",避免中文乱码。
load_normal_animations(加载正常状态动画)
def load_normal_animations(self):
    self.normal_gifs = []
    if os.path.exists(self.normal_dir):
        for file in os.listdir(self.normal_dir):
            if file.lower().endswith(".gif") and "click" not in file.lower():
                self.normal_gifs.append(os.path.join(self.normal_dir, file))

    # 如果没有找到正常动画,使用默认动画
    if not self.normal_gifs:
        self.normal_gifs.append(self.default_gif_path)
  • 作用:加载normal目录下的所有 GIF 动画(排除包含 “click” 的文件,避免与点击动画混淆)。
  • 关键细节
    • 筛选逻辑:只加载.gif文件,且排除文件名含 “click” 的文件,确保正常状态动画的纯净性。
    • 容错处理:若目录为空或无符合条件的文件,默认添加normal1.gif,避免程序报错。

9. 动画和对话更新方法

update_animation(核心动画显示方法)
def update_animation(self, gif_path):
    # 检查文件是否存在
    if not os.path.exists(gif_path):
        print(f"警告: 动画文件不存在 - {gif_path}")
        return

    # 创建QMovie对象
    self.movie = QMovie(gif_path)
    if not self.movie.isValid():
        print(f"警告: 无法加载动画 - {gif_path}")
        return

    # 获取原始尺寸并等比例缩放
    original_rect = self.movie.frameRect()
    original_width = original_rect.width()
    original_height = original_rect.height()

    # 容错处理
    if original_width <= 0 or original_height <= 0:
        original_width, original_height = 200, 200

    # 计算缩放后尺寸
    scaled_width = int(original_width * self.scale_factor)
    scaled_height = int(original_height * self.scale_factor)

    # 应用缩放并显示
    self.movie.setScaledSize(QSize(scaled_width, scaled_height))
    self.image_label.setMovie(self.movie)
    self.movie.start()

    # 调整对话标签宽度
    self.talk_label.setMaximumWidth(scaled_width + 20)
  • 作用:加载并显示指定路径的 GIF 动画,并按比例缩放。
  • 关键细节
    • 文件检查:先判断 GIF 文件是否存在、是否有效,避免因资源缺失导致程序异常。
    • 缩放逻辑:根据scale_factor计算缩放后的尺寸,确保动画大小统一且符合预期。
    • 对话标签适配:根据动画宽度调整对话标签的最大宽度,避免对话文本过长超出动画范围。
 switch_normal_animation(切换正常动画)
def switch_normal_animation(self):
    if self.state == 0 and self.normal_gifs:  # 只有在待机状态才切换
        random_gif = random.choice(self.normal_gifs)
        self.update_animation(random_gif)

  • 作用:在待机状态下(state=0)随机切换正常动画,增加动态感。
update_talk(更新对话内容)
def update_talk(self):
    if self.state == 0 and self.dialogs:  # 待机状态
        self.talk_label.setText(random.choice(self.dialogs))
    else:  # 点击/拖动状态
        self.talk_label.setText("正在拖动我呢~")
    self.talk_label.adjustSize()
    self.adjustSize()
  • 作用:根据当前状态更新对话内容(待机时随机显示预设对话,拖动时显示固定文本)。

10. 鼠标交互事件(拖动和点击响应)

mousePressEvent(鼠标按下时)
def mousePressEvent(self, event):
    if event.button() == Qt.LeftButton:
        self.is_dragging = True
        self.drag_start_pos = event.globalPos() - self.pos()

        # 切换到点击状态并播放点击动画
        self.state = 1
        self.update_talk()  # 更新对话
        self.update_animation(self.click_gif_path)  # 显示click.gif

    event.accept()

  • 作用:响应鼠标左键按下事件,开始拖动并切换到 “点击状态”。
  • 关键细节
    • 记录拖动起点:drag_start_pos用于计算鼠标移动时窗口的新位置。
    • 状态切换:state设为 1,同时更新对话(显示 “正在拖动我呢~”)和动画(显示click.gif)。
mouseMoveEvent(鼠标移动时)
def mouseMoveEvent(self, event):
    if self.is_dragging and event.buttons() & Qt.LeftButton:
        # 拖动过程中保持点击状态和click.gif
        self.move(event.globalPos() - self.drag_start_pos)
    event.accept()
  • 作用:响应鼠标移动事件,实现窗口拖动功能(根据鼠标位置计算窗口新坐标)。
mouseReleaseEvent(鼠标释放时)
def mouseReleaseEvent(self, event):
    if self.is_dragging:
        self.is_dragging = False
        # 释放鼠标后恢复待机状态
        self.state = 0
        self.update_talk()
        self.switch_normal_animation()  # 切换回正常动画
    event.accept()
  • 作用:响应鼠标释放事件,结束拖动并恢复 “待机状态”(切换回正常动画和随机对话)。

11. 其他辅助方法

  • random_position:程序启动时随机设置窗口在屏幕上的位置(避免每次都显示在同一个地方)。
  • quit:退出程序(关闭窗口并终止应用)。
  • show_window:从隐藏状态恢复窗口(将透明度设为 1 并显示)。
  • contextMenuEvent:右键菜单(提供 “隐藏” 和 “退出” 选项,隐藏时窗口透明度设为 0)。

12. 程序入口

if __name__ == '__main__':
    # 确保中文显示正常
    app = QApplication(sys.argv)
    app.setFont(QFont("SimHei", 10))

    # 创建并运行宠物
    pet = DesktopPet()
    sys.exit(app.exec_())

  • 作用:启动程序,初始化应用实例并创建DesktopPet对象,进入事件循环。
  • 关键细节app.setFont(QFont("SimHei", 10))确保全局中文显示正常(避免菜单、对话等文本乱码)。

总结

这段代码通过 PyQt5 实现了一个功能完整的桌面宠物程序,核心特点包括:

  1. 视觉效果:无边框、透明背景、置顶显示,使桌宠与桌面自然融合。
  2. 交互功能:支持鼠标拖动(改变位置)、点击响应(切换动画和对话)。
  3. 自动行为:定时切换动画和对话,提升生动感。
  4. 用户操作:系统托盘和右键菜单提供退出、隐藏等功能,方便管理。

使用时需注意:确保normalclick目录下有对应的 GIF 动画、图标和对话文件,路径与代码中的设置一致,否则可能出现资源加载失败的问题。

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐