ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

【基于Qt5仿音乐播放器(六)】——细节打磨与总结

【基于Qt5仿音乐播放器(六)】——细节打磨与总结

【基于Qt5仿音乐播放器(六)】——细节打磨

  • 1. 设置主窗口图案
  • 2. 窗口最小化
  • 3. BtForm偶尔窗口乱移问题
  • 4. 添加系统托盘
  • 5. 保证程序只运行一次
    • 5.1 使用 QLockFile 实现单实例
    • 5.2 使用 QSharedMemory 实现单实例
    • 5.3 两种方式对比
  • 6. 禁止qDebug()输出
  • 7. 项目打包
    • 7.1 使用 windeployqt 部署依赖库
  • 8. 总结

1. 设置主窗口图案

// widget.cpp中新增voidWidget::initUi(){/....setWindowIcon(QIcon(":/images/tubiao.png"));}

2. 窗口最小化

由于窗口中控件并非全部基于Widget布局,有些控件的位置是计算死得,窗口最大化时有些控件可能无法适配尺寸,因此这里不做窗口最大化。

// widget.cpp中新增voidWidget::on_min_clicked(){this->showMinimized();}voidWidget::on_skin_clicked(){QMessageBox::information(this,"温馨提⽰","⼩哥哥正在加班紧急⽀持中...");}voidWidget::on_max_clicked(){QMessageBox::information(this,"温馨提⽰","⼩哥哥正在加班紧急⽀持中...");}

3. BtForm偶尔窗口乱移问题

当我们鼠标长按在BtForm的时候,一旦移动鼠标就会发现窗口会进行偏移。这是因为子窗口事件不能传递给父窗口事件,例如在WIdget放置了一个自定义的窗口childWIdget,假如这两个窗口都设置了mousePressEvent事件,这个时候如果在chilWIdget中触发这个事件他不会传递给父窗口WIdget中。而我们代码中的在widget中的mousePressEvent就不会进行触发,只会触发BtForm中的mousePressEvent,而widge中的dragPosition记录了鼠标点击时的坐标,于是当我们点击BtForm,由于widget中的mousePressEvent并没有触发,导致dragPosition记录的还是上一次鼠标点击的坐标,所以发生了窗口闪现的偏移。而解决方法也很简单,我们只需要让鼠标放在BtForm移动鼠标时不能进行窗口移动即可,只需要多添加一个bool ismove类型的成员变量,当我们点击在btForm以外时isMove为true可移动,当我们鼠标在btForm时,isMove为false不能移动。

voidWidget::mousePressEvent(QMouseEvent*event){isMove=true;//...}voidWidget::mouseMoveEvent(QMouseEvent*event){if(event->buttons()==Qt::LeftButton&&isMove){// 鼠标开始移动出,窗口进行调整,不变值就是,鼠标相对于窗口左上角坐标的差值dragPositionthis->move(event->globalPos()-dragPosition);}}voidWidget::onBtFormClick(intid){isMove=false;//...}

4. 添加系统托盘

当点击关闭按钮时,不让播放器直接退出,而是将窗口隐藏掉,窗⼝的图标在系统托盘位置,当在播放器图标上右键单击时,弹出菜单让用户选择是显示窗口还是继续退出。

#include<QSystemTrayIcon>voidWidget::initUi(){QSystemTrayIcon*trayIcon=newQSystemTrayIcon(this);trayIcon->setIcon(QIcon(":/images/tubiao.png"));QMenu*trayMenu=newQMenu(this);trayMenu->addAction("打开",this,&QWidget::showNormal);trayMenu->addSeparator();trayMenu->addAction("退出",this,&Widget::quit);trayIcon->setContextMenu(trayMenu);trayIcon->show();// .....}voidWidget::on_quit_clicked(){this->hide();}voidWidget::quit(){// 写入数据musicList.writeToDB();// 关闭数据库sqlite.close();// 退出this->close();}

5. 保证程序只运行一次

现在每点击⼀次QQMusic.exe,都会创建⼀个QQMusic的实例,即同⼀个机器上可运行多份程序实例,多个实例同时运行有以下缺陷:

  1. 多个实例同时运行可能会导致资源浪费,如内存、CPU效率等
  2. 如果应用程序涉及对共享数据的修改,多个程序同时运行可能会导致数据不⼀致问题
  3. 若多个实例尝试访问同⼀资源时,如文件、数据库等,可能会导致冲突或错误
  4. 另外,用户体验不是很好,多个实例操作时容易混淆

因此有时禁止程序多开,即⼀个应用程序只能运行⼀个实例,也称为单实例应⽤程序或单例应用程序。

在Qt中,禁止程序多开的方式有好几种,常用的有QLockFile和QSharedMemory两种,下面分别介绍。

5.1 使用 QLockFile 实现单实例

QLockFile是 Qt 提供的基于文件锁的类,通过尝试锁定一个特定的锁文件来判断程序是否已经在运行。如果锁文件已被其他进程锁定,说明程序已经在运行。

// main.cpp 中使用 QLockFile 实现单实例#include<QApplication>#include<QLockFile>#include<QMessageBox>#include<QDir>#include<QStandardPaths>intmain(intargc,char*argv[]){QApplicationa(argc,argv);// 获取系统临时目录,并创建锁文件路径QString lockFilePath=QDir::tempPath()+"/QQMusic.lock";QLockFilelockFile(lockFilePath);// 尝试锁定锁文件,如果失败说明程序已经在运行if(!lockFile.tryLock(100)){QMessageBox::information(nullptr,"QQMusic","QQMusic已经在运⾏...");return0;}Widget w;w.show();returna.exec();}

说明:

  • QDir::tempPath()获取系统临时目录,锁文件放在这里可以避免权限问题。
  • tryLock(100)尝试锁定文件,参数 100 表示最多等待 100 毫秒,超时则返回false。
  • 当程序正常退出时,QLockFile对象析构会自动释放锁;如果程序异常崩溃,锁文件可能会残留,但QLockFile会自动检测并处理过期锁。

5.2 使用 QSharedMemory 实现单实例

QSharedMemory是 Qt 提供的共享内存类,是进程间通信的一种机制。由于相同 key 值的共享内存只能存在一份,因此在程序启动时可以检测共享内存是否已经被创建,如果已经创建则说明程序已经在运行,否则程序还没有运行。

// main.cpp 中使用 QSharedMemory 实现单实例#include<QApplication>#include<QSharedMemory>#include<QMessageBox>intmain(intargc,char*argv[]){QApplicationa(argc,argv);// 实现只能实例化一个实例QSharedMemoryshareMemory("QQMusic");// 如果共享内存已经被占用了,说明已经有实例存在了if(shareMemory.attach()){QMessageBox::information(nullptr,"QQMusic","QQMusic已经在运⾏...");return0;}// 没有就创建一个shareMemory.create(1);Widget w;w.show();returna.exec();}

说明:

  • attach()尝试连接到已存在的共享内存,如果成功说明程序已经在运行。
  • create(1)创建大小为 1 字节的共享内存,作为程序正在运行的标记。
  • 程序退出时,QSharedMemory对象析构会自动释放共享内存。

5.3 两种方式对比

对比项QLockFileQSharedMemory
实现原理基于文件锁基于共享内存
跨平台性良好良好
异常崩溃处理自动检测过期锁系统自动回收
代码复杂度较低较低
适用场景简单单实例控制需要进程间通信时更灵活

两种方式都能很好地实现单实例运行,你可以根据项目需求选择其中一种。推荐使用QLockFile,因为它更简单且对异常崩溃的处理更完善。

6. 禁止qDebug()输出

要逐个删除程序中qDebug的打印太⿇烦,可以再配置⽂件中通过添加以下语句,禁止qDebug输出:

#banqDebug outputDEFINES+=QT_NO_DEBUG_OUTPUT

7. 项目打包

程序开发完成后,需要将其打包成可独立运行的 exe 文件,方便在没有安装 Qt 开发环境的机器上运行。下面以 Windows 平台为例,介绍基于 Qt5 的打包流程。

7.1 使用 windeployqt 部署依赖库

Qt 程序在发布时,需要将程序依赖的 Qt 动态库(如 Qt5Core.dll、Qt5Gui.dll、Qt5Widgets.dll 等)一起打包。windeployqt是 Qt 官方提供的部署工具,可以自动扫描 exe 依赖的 Qt 库并复制到指定目录。

第一步:编译 Release 版本

在 Qt Creator 中,将构建模式切换为Release,然后重新编译项目,生成 release 版本的QQMusic.exe。

第二步:创建发布目录并复制 exe

将新建⼀个⽂件,将release模式可执行程序拷贝到这个新目录里面

第三步:运行 windeployqt 部署依赖库

进入新建的文件夹,按shift+鼠标右击打开powershell

执行完成后,windeployqt ./QQMusic.exe会自动将程序运行所需的 Qt 动态库、插件(如 platforms、imageformats 等)复制到发布目录中。

第四步:补充非 Qt 依赖

如果程序还依赖其他第三方库(如 sqlite3.dll、ffmpeg.dll 等),需要手动将这些动态库复制到发布目录中:

copy D:\QtProject\3rdparty\sqlite3.dll D:\QQMusicRelease\

注意事项:

  • windeployqt需要与编译时使用的 Qt 版本和编译器一致,否则可能部署错误的库。
  • 部署完成后,建议在没有安装 Qt 的干净机器上测试运行,确保所有依赖都已包含。
  • 如果程序使用了 Qt 的某些模块(如 Qt5Sql、Qt5Network),windeployqt会自动检测并部署对应的库。

8. 总结

返回列表