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

您所在的位置:网站首页 请换一个动态壁纸软件 C++动态壁纸软件的开发(含源文件)

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

2024-02-17 05:37| 来源: 网络整理| 查看: 265

在上一篇博客中,使用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 checkBox_2->isChecked() volumeSlider->value() 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,当用户释放鼠标后自动把滑块位置设置成1000 ui->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(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(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~20 ui->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~20 ui->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



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3