】——细节打磨与总结)
【基于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){isMovetrue;//...}voidWidget::mouseMoveEvent(QMouseEvent*event){if(event-buttons()Qt::LeftButtonisMove){// 鼠标开始移动出窗口进行调整不变值就是鼠标相对于窗口左上角坐标的差值dragPositionthis-move(event-globalPos()-dragPosition);}}voidWidget::onBtFormClick(intid){isMovefalse;//...}4. 添加系统托盘当点击关闭按钮时不让播放器直接退出而是将窗口隐藏掉窗⼝的图标在系统托盘位置当在播放器图标上右键单击时弹出菜单让用户选择是显示窗口还是继续退出。#includeQSystemTrayIconvoidWidget::initUi(){QSystemTrayIcon*trayIconnewQSystemTrayIcon(this);trayIcon-setIcon(QIcon(:/images/tubiao.png));QMenu*trayMenunewQMenu(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的实例即同⼀个机器上可运行多份程序实例多个实例同时运行有以下缺陷多个实例同时运行可能会导致资源浪费如内存、CPU效率等如果应用程序涉及对共享数据的修改多个程序同时运行可能会导致数据不⼀致问题若多个实例尝试访问同⼀资源时如文件、数据库等可能会导致冲突或错误另外用户体验不是很好多个实例操作时容易混淆因此有时禁止程序多开即⼀个应用程序只能运行⼀个实例也称为单实例应⽤程序或单例应用程序。在Qt中禁止程序多开的方式有好几种常用的有QLockFile和QSharedMemory两种下面分别介绍。5.1 使用 QLockFile 实现单实例QLockFile是 Qt 提供的基于文件锁的类通过尝试锁定一个特定的锁文件来判断程序是否已经在运行。如果锁文件已被其他进程锁定说明程序已经在运行。// main.cpp 中使用 QLockFile 实现单实例#includeQApplication#includeQLockFile#includeQMessageBox#includeQDir#includeQStandardPathsintmain(intargc,char*argv[]){QApplicationa(argc,argv);// 获取系统临时目录并创建锁文件路径QString lockFilePathQDir::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 实现单实例#includeQApplication#includeQSharedMemory#includeQMessageBoxintmain(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 outputDEFINESQT_NO_DEBUG_OUTPUT7. 项目打包程序开发完成后需要将其打包成可独立运行的 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、Qt5Networkwindeployqt会自动检测并部署对应的库。8. 总结