【开源工具】Python+PyQt5打造智能桌面单词记忆工具:悬浮窗+热键切换+自定义词库

📚【深度解析】Python+PyQt5打造智能桌面单词记忆工具:悬浮窗+热键切换+自定义词库

在这里插入图片描述
请添加图片描述

🌈 个人主页:创客白泽 - CSDN博客
🔥 系列专栏:🐍《Python开源项目实战》
💡 热爱不止于代码,热情源自每一个灵感闪现的夜晚。愿以开源之火,点亮前行之路。
👍 如果觉得这篇文章有帮助,欢迎您一键三连,分享给更多人哦

请添加图片描述

在这里插入图片描述

一、概述:当单词记忆遇上Python GUI

在英语学习过程中,高频重复是记忆单词的关键。传统背单词软件往往需要用户主动打开应用,而本项目的创新之处在于开发了一个桌面悬浮窗单词记忆工具,具有以下核心特点:

  • 无干扰学习:半透明悬浮窗始终置顶显示
  • 智能记忆算法:支持顺序/逆序/随机三种循环模式
  • 快捷交互:F8热键一键切换中英释义
  • 高度可定制:字体/颜色/间隔时间全面可配置
  • 轻量化设计:系统托盘运行,内存占用<50MB

本文将深入解析200+行代码的实现原理,并提供完整的可执行方案。

二、功能架构设计

在这里插入图片描述

2.1 核心功能模块

  1. 单词显示模块

    • 定时切换显示
    • 中英双语切换
    • 视觉样式定制
  2. 交互控制模块

    • 全局热键监听
    • 拖拽移动窗口
    • 系统托盘菜单
  3. 配置管理模块

    • QSettings持久化
    • 词库动态加载
    • 设置实时生效

2.2 技术选型对比

技术方案 优势 本项目选择原因
PyQt5 成熟GUI框架 跨平台支持好
pynput 全局热键监听 比win32api更简洁
QSettings 配置存储 无需额外数据库

三、效果展示

3.1 主界面效果

在这里插入图片描述

3.2 设置面板

在这里插入图片描述

3.3 系统托盘菜单

在这里插入图片描述

3.4 关于界面

在这里插入图片描述

四、实现步骤详解

4.1 环境准备

pip install PyQt5 pynput

4.2 词库文件格式

创建words.txt(每行一个单词+释义):

apple 苹果
banana 香蕉

4.3 核心类解析

4.3.1 热键监听线程
class HotkeyWorker(QObject):
    toggle_signal = pyqtSignal()
    
    def run(self):
        from pynput import keyboard
        
        def on_activate_f8():
            self.toggle_signal.emit()
            
        with keyboard.GlobalHotKeys({
   
   '<F8>': on_activate_f8}) as listener:
            listener.join()

关键点

  • 使用QObject实现跨线程信号通信
  • pynput库实现全局热键监听
  • 异常处理保障稳定性
4.3.2 主窗口类
class WordDisplayApp(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint)
        self.setAttribute(Qt.WA_TranslucentBackground)

窗口特性

  • FramelessWindowHint:无边框
  • WindowStaysOnTopHint:始终置顶
  • WA_TranslucentBackground:半透明效果

4.4 配置持久化实现

self.settings = QSettings("WordDisplay", "WordDisplayApp")

# 保存配置
self.settings.setValue("interval", self.interval)

# 读取配置
self.interval = self.settings.value("interval", 5, type=int)

存储位置

  • Windows:注册表HKEY_CURRENT_USER\Software\WordDisplay\WordDisplayApp
  • Mac:~/Library/Preferences/com.WordDisplay.WordDisplayApp.plist

五、代码深度解析

5.1 单词加载算法

def load_words(self):
    if self.order == "reverse":
        self.word_list.reverse()
    elif self.order == "random":
        random.shuffle(self.word_list)

记忆算法优化

  • 随机模式:避免固定顺序记忆
  • 逆序模式:强化尾部单词记忆
  • 间隔重复:通过定时器控制显示节奏

5.2 拖拽移动实现

def mousePressEvent(self, event):
    self.dragging = True
    self.offset = event.globalPos() - self.pos()

def mouseMoveEvent(self, event):
    if self.dragging:
        self.move(event.globalPos() - self.offset)

交互细节

  • 记录鼠标点击位置与窗口位置的偏移量
  • 实时计算新窗口位置
  • 鼠标释放时重置状态

5.3 系统托盘集成

self.tray_icon = QSystemTrayIcon(self)
tray_menu = QMenu()
exit_action = QAction("退出", self)
exit_action.triggered.connect(self.quit_app)
tray_menu.addAction(exit_action)

多平台适配

  • Windows/Mac/Linux通用实现
  • 双击图标显示/隐藏窗口
  • 右键弹出功能菜单

六、完整源码下载

import sys
import random
import os
from PyQt5.QtWidgets import (QApplication, QMainWindow, QLabel, QSystemTrayIcon,
                            QMenu, QAction, QWidget, QVBoxLayout, QPushButton,
                            QSpinBox, QComboBox, QColorDialog, QMessageBox, 
                            QDesktopWidget, QFileDialog)
from PyQt5.QtGui import QColor, QFont, QIcon, QPixmap, QCursor
from PyQt5.QtCore import Qt, QTimer, QSettings, QSize, QThread, pyqtSignal, QObject, QPoint

class HotkeyWorker(QObject):
    """处理全局快捷键的工作线程"""
    toggle_signal = pyqtSignal()
    error_signal = pyqtSignal(str)

    def __init__(self):
        super().__init__()
        self.listener = None

    def run(self):
        """启动热键监听"""
        try:
            from pynput import keyboard

            def on_activate_f8():
                self.toggle_signal.emit()

            with keyboard.GlobalHotKeys({
   
   '<F8>': on_activate_f8}) as self.listener:
                self.listener.join()
        except ImportError as e:
            self.error_signal.emit("未安装pynput库,无法使用F8快捷键功能")
        except Exception as e:
            self.error_signal.emit(f"快捷键初始化失败: {
     
     str(e)}")

    def stop(self):
        """停止热键监听"""
        if self.listener:
            self.listener.stop()

class WordDisplayApp(QMainWindow):
    def __init__(self):
        super().__init__()
        
        # 初始化设置
        self.settings = QSettings("WordDisplay", "WordDisplayApp")
        self.load_settings()
        
        # 初始化UI
        self.init_ui()
        
        # 加载单词数据
        self.word_list = []
        self.current_word_file = "words.txt"  # 默认词库文件
        self.load_words()
        
        # 设置当前单词索引
        self.current_index = 0
        
        # 显示第一个单词
        self.show_word()
        
        # 设置定时器
        self.timer = QTimer()
        self.timer.timeout.connect(self.show_next_word)
        self.timer.start(self.interval * 1000)
        
        # 中文释义是否显示
        self.show_translation = False
        
        # 初始化热键线程
        self.init_hotkey_thread()
        
        # 拖动相关变量
        self.dragging = False
        self.offset = QPoint()

    def init_hotkey_thread(self):
        """初始化热键监听线程"""
        self.hotkey_thread = QThread()
        self.hotkey_worker = HotkeyWorker()
        self.hotkey_worker.moveToThread(self.hotkey_thread)
        
        # 连接信号
        self.hotkey_worker.toggle_signal.connect(self.toggle_translation)
        self.hotkey_worker.error_signal.connect(self.show_error_message)
        
        # 启动线程
        self.hotkey_thread.started.connect(self.hotkey_worker.run)
        self.hotkey_thread.start()

    def show_error_message(self, message):
        """显示错误消息"""
        QMessageBox.warning(self, "警告", message)

    def init_ui(self):
        """初始化用户界面"""
        self.setWindowTitle("单词显示")
        self.setWindowFlags(Qt.FramelessWindowHint | Qt.WindowStaysOnTopHint | Qt.Tool)
        self.setAttribute(Qt.WA_TranslucentBackground)
        
        # 创建主部件
        main_widget = QWidget()
        self.setCentralWidget(main_widget)
        
        # 布局
        layout = QVBoxLayout()
        layout.setContentsMargins(10, 10, 10, 10)
        main_widget.setLayout(layout)
        
        # 英文单词标签
        self.word_label = QLabel("单词加载中...")
        self.word_label.setAlignment(Qt.AlignCenter)
        self.word_label.setFont(QFont("Arial", self.english_font_size))
        self.word_label.setStyleSheet(f"color: {
     
     self.word_color.name()};")
        
        # 中文翻译标签
        self.translation_label = QLabel()
        self.translation_label.setAlignment(Qt.AlignCenter)
        self.translation_label.setFont(QFont("微软雅黑", self.chinese_font_size))
        self.translation_label.setStyleSheet(f"color: {
     
     self.translation_color.name()};")
        self.translation_label.hide()
        
        # 添加到布局
        layout.addWidget(self.word_label)
        layout.addWidget(self.translation_label)
        
        # 系统托盘图标
        self.init_system_tray()
        
        # 移动到右上角
        self.move_to_top_right()

    def init_system_tray(self):
        """初始化系统托盘图标"""
        self.tray_icon = QSystemTrayIcon(self)
        
        # 设置图标
        icon_path = os.path.join(os.path.dirname(__file__), "icon.ico")
        if os.path.exists(icon_path):
            self.tray_icon.setIcon(QIcon(icon_path))
        else:
            # 创建默认图标
            pixmap = QPixmap(QSize(64, 64))
            pixmap.fill(Qt.transparent)
            self.tray_icon.setIcon(QIcon(pixmap))
            QMessageBox.warning(self, "图标未找到", f"未找到图标文件: {
     
     icon_path}")
        
        # 创建托盘菜单
        tray_menu = QMenu()
        
        # 显示/隐藏主窗口
        toggle_action = QAction("显示/隐藏", self)
        toggle_action.triggered.connect(self.toggle_visibility)
        tray_menu.addAction(toggle_action)
        
        # 重置位置
        reset_pos_action = QAction("重置位置", self)
        reset_pos_action.triggered.connect(self.move_to_top_right)
        tray_menu.addAction(reset_pos_action)
        
        # 设置
        settings_action = QAction("设置", self)
        settings_action.triggered.connect(self.show_settings_dialog)
        tray_menu.addAction(settings_action)
        
        # 加载词库
        load_dict_action = QAction("加载词库", self)
        load_dict_action.triggered.connect(self.load_new_dictionary)
        tray_menu.addAction(load_dict_action)
        
        # 退出
        exit_action = QAction("退出", self)
        exit_action.triggered.connect(self.quit_app)
        tray_menu.addAction(exit_action)
        
        self.tray_icon.setContextMenu(tray_menu)
        self.tray_icon.show()
        
        # 托盘图标点击事件
        self.tray_icon.activated.connect(self.on_tray_icon_activated)

    def on_tray_icon_activated(self, reason):
        """托盘图标点击事件处理"""
        if reason == QSystemTrayIcon.DoubleClick:
            self.toggle_visibility()

    def move_to_top_right(self):
        """将窗口移动到屏幕右上角"""
        screen = QDesktopWidget().screenGeometry()
        self.move(screen.width() - self.width() - 20, 20)

    def mousePressEvent(self, event):
        """鼠标按下事件"""
        if event.button() == Qt.LeftButton:
            self.dragging = True
            self.offset = event.globalPos() - self.pos()
            event.accept(
评论 27
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

创客白泽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值