在上一篇博客中,使用C++和Qt框架完成了Wallpaper的雏形,现于之前的基础上,完善更多功能。

之前已经完成了打开文件,播放暂停,设置父窗体的功能,本文将不再详细说明。

最终效果(视频来自“大污师”的鲸落)

头文件support.h

这个头文件里专门放宏定义内容,便于修改

#ifndef SUPPORT_H
#define SUPPORT_H#define PLAYER_FILTER "视频(*.mp4 *.avi *.wmv);;图片(*.jpg *.png *.bmp);;所有文件(*.*)"
#define BUTTON_TEXT_PLAY "播放"
#define BUTTON_TEXT_PAUSE "暂停"#define HEKY_AUTORUN "HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"
#define OPEN_BROWSER_FILE_PATH "OpenBrowser.exe"
#define OPEN_REFRESHBAKGROUND_FILE_PATH "ReFreshBackground.exe"#define VOLUME_HIGH "image: url(:/Image/volume-high.png);"
#define VOLUME_LOW "image: url(:/Image/volume-low.png);"
#define VOLUME_OFF "image: url(:/Image/volume-off.png);"#define SHARED_MEMORY_KEY "wallpaper_by_dearxuan"#endif // SUPPORT_H

开机自启

显然每次开机都要手动打开软件非常麻烦,如果能让软件开机自启,就会方便很多

实现开机自启需要读写注册表

HKEY_CURRENT_USER\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run 

左边是是键名,右边是键值,键值指向文件地址,系统会在开机后执行对应文件,只要向注册表写入自己程序的地址,就可以实现开机自启。当关闭开机自启时,只要删除这一项就可以

//设置开机自启,flag表示是否开机自启
void Widget::AutoRun(bool flag) {QString appName = QApplication::applicationName();QString appPath = QApplication::applicationFilePath();appPath = appPath.replace("/", "\\");//打开注册表QSettings *reg = new QSettings(HEKY_AUTORUN,//宏定义,自启动的注册表位置,详细内容在头文件support.h里QSettings::NativeFormat);QString val = reg->value(appName).toString();//根据appName获取键值if (flag) {//打开开机自启if (val != appPath) {//键值不是本程序地址或者不存在,则写入键值reg->setValue(appName, appPath);}QString v = reg->value(appName).toString();//读取刚刚写入的键值if (v != appPath) {//写入失败ui->checkBox_1->setChecked(false);QMessageBox::warning(this, "拒绝访问", "添加注册表失败.", QMessageBox::Ok, QMessageBox::Ok);}} else {//关闭开机自启reg->remove(appName);//移除键值QString v = reg->value(appName).toString();if (v != "") {//键值仍然存在,移除失败ui->checkBox_1->setChecked(true);QMessageBox::warning(this, "拒绝访问", "移除注册表失败.", QMessageBox::Ok, QMessageBox::Ok);}}reg->deleteLater();//延迟删除
}

同时启动软件时还要判断是否打开了开机自启,如果打开了,则自动把checkbox1设置成勾选状态

//判断是否打开开机自启
bool isAutoRun() {QString appName = QApplication::applicationName();QString appPath = QApplication::applicationFilePath();appPath = appPath.replace("/", "\\");QSettings *reg = new QSettings(HEKY_AUTORUN,//宏定义,在头文件support.h里面QSettings::Registry64Format);//如果注册表键值就是本程序的地址,表示打开了开机自启QString val = reg->value(appName).toString();return val == appPath;
}

任务栏托盘角标

当点击“叉叉”时,关闭窗口,但是不退出程序,用户通过点击任务栏角标再次打开窗口

在Widget.h里定义QSystemTrayIcon

public:Widget(QWidget *parent = nullptr);~Widget();About about;QVideoWidget videoWidget;QMediaPlayer player;QSystemTrayIcon tray;//任务栏托盘角标QMediaPlaylist *list;

在程序启动时添加菜单,绑定事件,并显示

tray.setToolTip("Wallpaper");//任务栏托盘小图标的标题
tray.setIcon(QIcon(":Icon/wallpaper.ico"));//任务栏托盘小图标
QMenu *menu = new QMenu();
QAction *action_showDialog = new QAction("打开主窗口");
QAction *action_exit = new QAction("退出");
menu->addAction(action_showDialog);//添加菜单
menu->addAction(action_exit);//添加菜单
tray.setContextMenu(menu);//绑定菜单
//绑定托盘小图标点击事件
connect(&tray, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(OnSystemTrayClicked(QSystemTrayIcon::ActivationReason)));
//绑定菜单点击事件
connect(action_showDialog, SIGNAL(triggered(bool)), this, SLOT(onShowDialogClick()));
connect(action_exit, SIGNAL(triggered(bool)), this, SLOT(on_exitButton_clicked()));
tray.show();//显示小图标

点击“打开主窗口”时显示窗口

//点击了任务栏托盘小图标
void Widget::OnSystemTrayClicked(QSystemTrayIcon::ActivationReason reason) {if (reason == QSystemTrayIcon::Trigger) {this->showNormal();//如果点击了任务栏托盘小图标,则显示本窗口}
}

点击“退出”时相当于点击主窗口里面的“退出”按钮,所以直接绑定到“退出”按钮的点击事件

//退出按钮点击事件,同时任务栏小图标的”退出“菜单也会执行这个函数
void Widget::on_exitButton_clicked() {if (!firstPlay) {//还没有打开视频,不需要刷新背景ReFreshBackground();}this->hide();//隐藏窗口WriteSetting();//写入设置qApp->exit(0);//退出
}

二进制读写文件

在程序退出时,自动保存设置,包括:是否自动播放上一次视频,当前音量,当前视频文件地址。

用txt来保存虽然写入很方便,但是读取时候需要自己转换,非常痛苦,而且txt能被人轻易编辑,造成程序崩溃,所以采用二进制方法,防止用户随意编辑。设置将会被保存到当前路径下的setting文件(没有后缀)。

//读取设置
void Widget::ReadSetting() {QFile file("setting");//设置文件,不需要后缀bool n = false;//是否加载上一次视频int v = 100;//音量if (file.open(QIODevice::ReadOnly)) {//文件存在QDataStream input(&file);input >> n >> v >> path;//读取}file.close();ui->checkBox_2->setChecked(n);ui->volumeSlider->setValue(v);if(!n) path = "";
}
//写入设置
void Widget::WriteSetting() {QFile file("setting");if (file.open(QIODevice::WriteOnly)) {QDataStream output(&file);output << ui->checkBox_2->isChecked() << ui->volumeSlider->value() << path;}file.close();
}

在程序打开时现执行ReadSetting()来读取设置,并自动设置音量,路径等。

音量

添加volumeSlider滑块来控制音量,将范围设置成0~1000,这样可以减小滑动时的卡顿感,但是在处理数据时要把数值除以10,并在用户释放鼠标时自动修正滑块位置,比如把998修正为1000

//音量滑块值改变事件
void Widget::on_volumeSlider_valueChanged(int value) {//音量滑块的范围为0~1000,音量有效值是0~100,设置1000是为了减小卡顿感觉value = (value + 5) / 10;//四舍五入除以10ui->volumeLabel->setText(QString::number(value));if (value > 50 && VolumeNowPic != 2) {//高音量VolumeNowPic = 2;ui->volumePic->setStyleSheet(VOLUME_HIGH);} else if (value > 0 && value < 50 && VolumeNowPic != 1) {//低音量ui->volumePic->setStyleSheet(VOLUME_LOW);VolumeNowPic = 1;} else if (value == 0 && VolumeNowPic != 0) {//关闭音量ui->volumePic->setStyleSheet(VOLUME_OFF);VolumeNowPic = 0;}player.setVolume(value);//设置音量
}//音量滑块鼠标释放事件
void Widget::on_volumeSlider_sliderReleased() {//当滑块位置在999时,此时音量是100,当用户释放鼠标后自动把滑块位置设置成1000ui->volumeSlider->setValue(player.volume() * 10);
}

当用户点击音量图标时,用户希望能够立即静音,但是又不能改变滑块位置,如果用户再次点击或者移动滑块,则关闭静音,类似windows的音量设置那样。

上面的代码里使用了VolumeNowPic这个变量,这个是用来保存当前图标的,0,1,2分别表示关,低音量,高音量。引入这个变量是用来判断当前图标和修改后的图标,如果一致就放弃修改。

音量图标使用QLabel来放置

由于QLabel不支持点击事件,所以我们要自己添加。

ui->volumePic->installEventFilter(this);//注册音量Label的点击事件

Widget.h里写上

在cpp里写上

//事件监听
bool Widget::eventFilter(QObject *obj, QEvent *event) {//文件Label被点击if (obj == ui->fileLabel && event->type() == QEvent::MouseButtonPress) {QMouseEvent *mouse = static_cast<QMouseEvent *>(event);if (mouse->button() == Qt::LeftButton && path != "") {//如果是左键,则打开文件位置QProcess process;QString p = path.replace("/", "\\");QString cmd = "explorer /select," + p;process.startDetached(cmd);}}//音量Label被点击if (obj == ui->volumePic && event->type() == QEvent::MouseButtonPress) {QMouseEvent *mouse = static_cast<QMouseEvent *>(event);if (mouse->button() == Qt::LeftButton) {if (player.volume() == 0) {//音量是0,恢复volumeSlider上的正常音量on_volumeSlider_valueChanged(ui->volumeSlider->value());} else {//音量不是0,静音player.setVolume(0);ui->volumePic->setStyleSheet(VOLUME_OFF);VolumeNowPic = 0;}}}return false;
}

上面那个是点击文件QLabel打开文件位置的函数,第二个if语句才是重点,先判断音量是否为0,如果是0,则执行一次value_changed事件,这样就会自动根据滑块位置设置音量大小,如果滑块就在0的位置上,那么执行这个函数也什么都不会发生。如果音量不是0,则把音量改成0,同时修改VolumeNowPic为0,表示当前显示的是静音图片。代码里的VOLUME_OFF是我自己定义的,在头文件support.h里面

播放速度

与音量滑块同理,速度滑动栏的范围是50~1000,最终结果除以50,因为速度不能为0,所以实际值应该在0.1~2.0。当切换速度时,视频会明显黑屏以下,因此切换次数越少越好,当用户滑动滑块时,会不断触发value_changed,所以首先想到把切换速度写在释放鼠标的函数里,但是用户使用滚轮时,不会触发鼠标事件,所以还是需要在value_changed里写。我们可以设置一个布尔变量,来指示value_changed是否可用,当用户按下鼠标时,把这个值设置成false,这样就不会切换速度,如果用户使用滚轮或者点击滑动栏,仍然会执行切换速度,但是切换次数远少于移动鼠标时的value_changed次数,这是可以接受的。

bool sliderEnable = true;//速度滑块的value_changed监听是否可用
//播放速度滑块鼠标释放事件
void Widget::on_rateSlider_sliderReleased() {int rate = (ui->rateSlider->value() + 25) / 50;//24舍25入除以50,此时rate的范围是1~20ui->rateSlider->setValue(rate * 50);//乘上50,校准滑块位置sliderEnable = true;//value监听可用on_rateSlider_valueChanged(ui->rateSlider->value());//执行一次value监听事件,修改player的播放速度
}//播放速度滑块value改变事件
void Widget::on_rateSlider_valueChanged(int value) {//范围是50~1000,对应速度0.1~2.0,默认为1.0,设置1000是为了减小卡顿感int rate = (value + 25) / 50;//24舍25入除以50,此时rate的范围是1~20ui->rateLabel->setText(QString::number(rate / 10.0, 'f', 1));//显示数值的Label,把rate除以10并取小数点后一位if (sliderEnable) {//如果value监听可用//在修改播放速度时会有明显黑屏,为了防止过多黑屏影响视觉感受,当用户滑动滑块时不改变速度,当用户释放鼠标时再改变//当用户使用滚轮,或者点击滑动栏,触发stepadd或stepsub时,需要改变播放速度player.setPlaybackRate(rate / 10.0);//设置播放速度}
}//速度滑块点击事件
void Widget::on_rateSlider_sliderPressed() {sliderEnable = false;//value监听不可用,防止过多黑屏,当用户使用滚轮时不触发本事件,因此value监听有效
}

加载视频

在之前已经实现了加载视频的功能,现在原有基础上进行扩展。

之前直接把整个过程写到了按钮事件里,是因为当时还没有自动加载视频功能,必须通过按钮打开视频,而现在程序支持自动打开上一次视频,这就要求我们把加载过程单独写成一个函数。

//加载视频文件
void Widget::LoadFile(const QString file) {QFile video(file);if (!video.exists()) {//文件不存在ui->fileLabel->setText("未打开文件");ui->playButton->setText(BUTTON_TEXT_PLAY);ui->playButton->setEnabled(false);path = "";ui->fileLabel->setToolTip(NULL);ui->fileLabel->setCursor(Qt::ArrowCursor);//默认鼠标样式return;}QFileInfo fi = QFileInfo(path);//文件信息this->ui->fileLabel->setText("正在加载:" + fi.fileName());//文件Label显示文件名path = file;if (firstPlay) {//是否是第一次播放firstPlay = false;HWND hwnd = (HWND) videoWidget.winId();//获取视频窗体的句柄SetBackground(hwnd);//把视频窗体设置成背景窗体的子窗体videoWidget.setWindowFlags(Qt::FramelessWindowHint);videoWidget.setAspectRatioMode(Qt::IgnoreAspectRatio);videoWidget.showFullScreen();list = new QMediaPlaylist;}//清理原有的播放列表,加入视频list->clear();list->addMedia(QMediaContent(QUrl::fromLocalFile(file)));list->setPlaybackMode(QMediaPlaylist::CurrentItemInLoop);//循环播放player.setPlaylist(list);player.setVideoOutput(&videoWidget);//设置输出窗体player.play();ui->playButton->setText(BUTTON_TEXT_PAUSE);ui->playButton->setEnabled(true);ui->fileLabel->setText("正在播放:" + fi.fileName());ui->fileLabel->setToolTip(path.replace("/", "\\"));ui->fileLabel->setCursor(Qt::PointingHandCursor);//“指向”鼠标样式
}

在按钮事件里调用该函数

//点击了“打开”按钮,打开文件
void Widget::on_openButton_clicked() {QString file = QFileDialog::getOpenFileName(this,"打开文件",path,//初始地址PLAYER_FILTER);//过滤器,仅显示支持的格式if (!file.isEmpty()) {LoadFile(file);}
}

关于

“关于”窗体里面添加作者信息,并增加“刷新壁纸”和“清理注册表”功能

当程序异常退出,会导致背景显示异常,点击“刷新壁纸”可以让背景正常显示。

卸载软件之前,可以取消开机自启,或者点击“清理注册表”,来删除启动项。

EXE文件:https://dearx.lanzoui.com/iLUQGs80y2h

源代码:https://dearx.lanzoui.com/iw8x8s80u5g

C++动态壁纸软件的开发(含源文件)相关推荐

  1. 使用C++实现的仿Wallpaper动态壁纸软件(含源文件)

    在上一篇博客中,已经介绍了wallpaper的实现原理,并用C#实现,本文将使用C++和Qt框架实现,由于之前已经介绍过具体思路,本文将跳过分析过程,直接上代码. 新建Qt项目,系统自动生成widge ...

  2. QT开发实战-动态壁纸软件

    动态壁纸软件开发 项目源代码在下面链接获取: ----------------------------- 开发者:CodeSharkSJ 希望此项目能加强你对Qt的应用 文章目录 项目图与开发环境 核 ...

  3. 推荐5款动态壁纸软件(Win+Android)

    前言 随着硬件的迭代升级,在大多日常使用中,电脑性能都处于过剩状态 所以从前有些鸡肋且会让电脑运行变卡顿的动态壁纸,也成为了很多朋友让桌面更加生动美观的选择 不过即便使用动态壁纸的用户越来越多,Win ...

  4. 大佬用 WinUI 3 和 WPF 做了一个开源的动态壁纸软件

    说到动态壁纸软件,相信很多人都听说过或用过 Wallpaper Engine, 这是 Steam Store 一个好评如潮的壁纸软件,售价为 18 元.我之前也一直在使用,当然今天介绍的是另外一个壁纸 ...

  5. Live Wallpaper HD for Mac(天气动态壁纸软件)

    Live Wallpaper HD for Mac是Mac平台上一款具有动态壁纸.详细天气预报.模拟时钟功能的天气动态壁纸软件,Live Wallpaper HD 中文破解版包含14张高清动态手机或电 ...

  6. wallpaper动态壁纸软件

    对于长期烦恼找不到好看的动态壁纸的小伙伴们来说,wallpaper动态壁纸(软件下载地址)绝对是救命仙丹,这是由吾爱论坛网友自制分享的一款超好用的动态壁纸软件,内置Steam和bilibili解析,可 ...

  7. 推荐三款动态壁纸软件,足够让你的桌面惊艳!

    点击上方"码农的后花园",选择"星标" 公众号 精选文章,第一时间送达 今天给大家推荐三款动态壁纸软件,给你的桌面添加一点精彩,心情也更美呢 ~ 1,WinDy ...

  8. antd tree 动态添加_推荐三款动态壁纸软件,足够让你的桌面惊艳!

    点击上方"码农的后花园",选择"星标" 公众号 精选文章,第一时间送达今天给大家推荐三款动态壁纸软件,给你的桌面添加一点精彩,心情也更美呢 ~ 1,WinDyn ...

  9. bazel 链接第三方动态库_Windows最好用的动态壁纸软件,用了都说好!!

    Wallpaper Engine相信不少小伙伴都听说过,是一款非常专业的动态壁纸软件,目前在Steam售价19元,其实也不算贵,但是个人总觉得花这钱整一个动态壁纸软件,不是很划算,毕竟不是必需品啊!用 ...

  10. 柚子壁纸为什么自动安装_Wallpaper Engine,Windows最好用的动态壁纸软件,附教程!...

    Wallpaper Engine相信不少小伙伴都听说过,是一款非常专业的动态壁纸软件,目前在Steam售价19元,其实也不算贵,但是个人总觉得花这钱整一个动态壁纸软件,不是很划算,毕竟不是必需品啊! ...

最新文章

  1. 使用maven搭建ssm框架的javaweb项目
  2. java方法示例注释 @_Java 8中的功能接口是什么? @功能注释和示例
  3. 【概念集锦】之 shim和polyfill
  4. 测试面试题集-接口测试
  5. 电脑护眼模式_电脑手机护眼小工具
  6. Ubuntu安装wine
  7. 职场7条小tips,一定有一条说到你心坎里
  8. 计算机在运行表格的时候很慢,win7系统打开excel2007表格很慢的解决方法
  9. 《算法笔记》9.7 堆
  10. 数据结构与算法邓俊辉——(二)
  11. Maven学习(基础部分)
  12. 帆软报表决策系统忘记密码了,使用重置密码插件
  13. JavaScript利用回调函数完成次序循环打印“红黄绿”灯
  14. 文献阅读笔记:Smart Homes that Monitor Breathing and Heart Rate
  15. 使用javascript和jquery获取类方法
  16. Java小农养成记第五天
  17. 员工考勤记录 java
  18. 【Web技术】1114- 一文彻底读懂ESLint
  19. 关于IBM Cognos认证
  20. Kook机器人开发日志01

热门文章

  1. 程序人生 - 给IT新人的15点建议:苦逼程序员的辛酸反省与总结
  2. 信息检索导论要点整理
  3. SDOI2019R2翻车记
  4. 一次装系统惨痛的翻车经历
  5. matplotlib绘制随机漫步
  6. 跨步电压和接触电压的区别及联系
  7. 小米tts语音引擎下载_在手机和 AIoT 双战场打拼的小爱同学,会把语音助手带向何方?...
  8. WC2020 打铁记
  9. 数据可视化案例(四)——新零售数据大脑,助力智慧零售
  10. 新一代信息技术与互联网的资源观!