软件类别:本地视频文件播放器
软件设计、实现的思路和方法:
类名:player,继承自 QMainWindow
自定义类:video,继承自 QVideoWidget
*注:
- 播放部分格式文件需要安装解码器,安装包(LAVfilters)已打包一起发送
- 影音文件已打包发送,可直接用于软件测试
主要功能和槽函数:
private slots: |
1、界面设计
ui-designer
布局代码
//将widget设置为中心layout
widget=new QWidget;
this->setCentralWidget(widget);
//水平布局,控制按钮
QBoxLayout *ctlLayout = new QHBoxLayout;
ctlLayout->addWidget(ui->open);
ctlLayout->addWidget(ui->playBtn);
ctlLayout->addWidget(ui->next);
ctlLayout->addWidget(ui->progress);
ctlLayout->addWidget(ui->fullscreenBtn);
ctlLayout->addWidget(ui->min);
//调整水平布局部件比例
ctlLayout->setStretchFactor(ui->playBtn,1);
ctlLayout->setStretchFactor(ui->progress,10);
ctlLayout->setStretchFactor(ui->fullscreenBtn,1);
ctlLayout->setStretchFactor(ui->min,1);
//垂直布局:视频播放器、水平控制布局
QBoxLayout *mainLayout = new QVBoxLayout;
mainLayout->addWidget(videowidget);
mainLayout->addLayout(ctlLayout);
//设置布局
widget->setLayout(mainLayout);
this->setWindowTitle("lxm的播放器");
//设置按钮透明
ui->playBtn->setFlat(true);
ui->open->setFlat(true);
ui->fullscreenBtn->setFlat(true);
ui->min->setFlat(true);
ui->next->setFlat(true);
//设置图标
//所有图片均采用相对路径,并保存在资源文件下
QIcon icon1;
icon1.addFile(":/image/video_player_783px_1234870_easyicon.net.png");
setWindowIcon(icon1);
QIcon icon;
icon.addFile(":/image/next_596px_1229355_easyicon.net.png");
ui->playBtn->setIcon(icon);
QIcon icon3;
icon3.addFile(":/image/File_Box_808px_1145730_easyicon.net.png");
ui->open->setIcon(icon3);
icon.addFile(":/image/fullscreen_128px_1181910_easyicon.net.png");
ui->fullscreenBtn->setIcon(icon);
QIcon icon2;
icon2.addFile(":/image/button_arrow_up_200px_1189268_easyicon.net.png");
ui->min->setIcon(icon2);
QIcon icon7;
icon7.addFile(":/image/next_596px_1229354_easyicon.net.png");
ui->next->setIcon(icon7);
//设置按钮文字提示
ui->open->setToolTip(tr("打开文件"));
ui->playBtn->setToolTip(tr("播放"));
ui->fullscreenBtn->setToolTip(tr("全屏"));
ui->min->setToolTip(tr("小窗置顶"));
ui->next->setToolTip(tr("下一项"));软件界面:
2、文件批量载入
- 信号和槽:
QObject::connect(ui->open,SIGNAL (clicked()),this,SLOT(openFile()));
实现思路:
见代码注释功能截图:
实现方法:
void player::openFile()
{
//打开工程文件所在文件夹,文件夹中放置了影音文件
//list中存入文件名,使用QStringList和getOpenFileNames实现批量导入
QStringList list=QFileDialog::getOpenFileNames(this);
for(int k=0;k<list.size();k++)
{
//path保存歌曲路径
QString path=QDir::toNativeSeparators(list.at(k));
//将该路径倒入到playlist中
playlist->addMedia(QUrl::fromLocalFile(path));
//同路径中提取出文件名
QString name=path.split("\\").last();
}
//导入后自动开始播放
if(mediaplayer->state()!=1)//判断当前是否出于播放状态
play_and_pause();//播放
}
3、视频播放和暂停
- 信号和槽:
QObject::connect(ui->playBtn,SIGNAL(clicked()),this,SLOT(play_and_pause()));
- 实现思路:
当视频为停止或暂停状态时,按钮play图标为“播放”,提示为“播放”,点击事件可以是视频变为播放状态;当视频为播放状态时,改变为暂停图标,按钮提示为“暂停”。 - 快捷键:空格
- 功能截图:
播放状态
暂停状态 - 实现方法:
void player::play_and_pause()
{
switch (mediaplayer->state()) {
case 1://playing
{
QIcon icon;
icon.addFile(":/image/wedding_video_783px_1219421_easyicon.net.png");//改变图标
ui->playBtn->setIcon(icon);
mediaplayer->pause();//暂停播放
ui->playBtn->setToolTip(tr("播放"));
break;
}
default://stop&paused
{
QIcon icon;
icon.addFile(":/image/pause_button_195px_1197803_easyicon.net.png");
ui->playBtn->setIcon(icon);
mediaplayer->play();//播放
ui->playBtn->setToolTip(tr("暂停"));
}
}
}
4、进度条位置的调节
- 信号和槽:
QObject::connect(mediaplayer,SIGNAL(positionChanged(qint64)),this,SLOT(positionChanged(qint64)));
QObject::connect(mediaplayer,SIGNAL(durationChanged(qint64)),this,SLOT(getDuration())); - 实现思路:
获取视频总时长和滑块可调节长度计算出换算比例,使滑块位置和视频播放位置相对应,具体见下方代码注释 - 实现方法:
void player::positionChanged(qint64 position)
{
//这里的position指的是视频的播放对应的时间
//按照比例设置进度条位置
ui->progress->setValue(position/time2);
}
void player::setPosition(qint64 position)
{
//获取mediaplayer进度调整位置
mediaplayer->setPosition(position);
}
void player::on_progress_sliderMoved(int position)
{
//position为滑块位置,position*time2为滑块位置应对应的视频时刻
setPosition(position*time2);
}
void player::getDuration()
{
//获取视频文件时间长度
time=mediaplayer->duration();
//horizontalbar的长度为100,time2为比例系数
time2=(int)time/100;
}5、小窗置顶功能
- 信号和槽:
QObject::connect(ui->min,SIGNAL(clicked()),this,SLOT(minimize()));
- 功能描述:
将该程序置顶于所有窗口,点击置顶按钮会提示“双击快捷操作”,使用双击方式置顶\取消置顶则不会提示,使使用更加流畅 - 功能截图:
- 未置顶状态
- 置顶状态(可以在看视频的同时操作其它软件)
- 未置顶状态
- 实现方法:
//置顶功能
void player::up()
{
if (m_flags == NULL)
{
//将播放器设置为置顶
m_flags = windowFlags();
setWindowFlags(m_flags | Qt::WindowStaysOnTopHint);
show();
//变换图标和按钮提示
QIcon icon2;
icon2.addFile(":/image/down.png");
ui->min->setIcon(icon2);
ui->min->setToolTip(tr("取消窗口置顶"));
}
else
{
//取消播放器置顶
m_flags = nullptr;
setWindowFlags(m_flags);
show();
//变换图标和按钮提示
QIcon icon2;
icon2.addFile(":/image/button_arrow_up_200px_1189268_easyicon.net.png");
ui->min->setIcon(icon2);
ui->min->setToolTip(tr("小窗置顶"));
}
}//槽函数
void player::minimize()
{
QMessageBox::about(this, tr("Tips"), tr("双击进入/退出小窗"));
up();
}//鼠标双击快捷操作
void player::mouseDoubleClickEvent(QMouseEvent*)
{
up();
}
6、全屏功能
- 信号和槽:
QObject::connect(ui->fullscreenBtn,SIGNAL(clicked()),this,SLOT(fullScr()));
- 实现思路:
首先对mediaplayer的全屏状态进行判断,若为非全屏状态,使用enter键或点击全屏按钮均可进入全屏,若为全屏状态,点击enter键退出全屏。点击全屏按钮会提示“快捷键enter”,而直接点击enter键进入全屏则不会,使使用更加流畅 - 功能截图:
- 实现方法:
player类:在video类也写入槽函数:void player::fullScr()//全屏功能
{
if(mediaplayer->state()==1)//若正在播放,则可进入全屏状态
{
//全屏前需要取消小窗置顶
setWindowFlags(nullptr);
show();
//全屏
videowidget->setFullScreen(true);
QMessageBox::about(this, tr("Tips"), tr("enter键进入/退出全屏"));
}
else {
{
//当前不在播放视频,无法进入全屏模式
videowidget->setFullScreen(false);
}
}
}
解决全屏后player类父窗口无法接收到信号的问题void video::keyPressEvent(QKeyEvent *keyset)//全屏和全屏退出
{
if(keyset->key()==16777220)//回车键
{
if(this->isFullScreen())//如果当前出于全屏状态
{
//退出全屏
this->setWindowFlags (Qt::SubWindow);
this->showNormal();
}
else
{
//进入全屏
this->setFullScreen(true);
}
}
}
7、播放下一项
- 信号和槽
QObject::connect(ui->next,SIGNAL(clicked()),this,SLOT(next_song()));
- 实现思路
将进度条拉至最后 - 截图:
- 实现方法
void player::next_song()
{
setPosition(100*time2);
} - 注意事项
//需将mediaplayer设置为循环播放,否则会在播放最后一项时再点击next会导致程序退出 |
8、可作为音频播放器使用
- 功能描述:支持音频播放,支持播放列表中同时存在视频和音频
- 功能截图:
遇到的问题和解决方法
(*注:部分细节问题以列入代码注释中)
- ui->designer中的控件运行之后界面不显示
- 被设置为中心layout的widget所覆盖,需添加QVBoxLayout,分离widget和按钮控件
- 进度条无法正常使用
- 需正确设置进度条位置和视频播放位置的换算比例(time2)
- durationChanged()函数执行后,文件时间长度值time依然为0
- 重写时间获取槽函数
QObject::connect(mediaplayer,SIGNAL(positionChanged(qint64)),this,SLOT(positionChanged(qint64)));
QObject::connect(mediaplayer,SIGNAL(durationChanged(qint64)),this,SLOT(getDuration()));
void player::getDuration()
{
//获取视频文件时间长度
time=mediaplayer->duration();
//horizontalbar的长度为100,time2为比例系数
time2=(int)time/100;
}
- 重写时间获取槽函数
- 全屏后无法退出
- 全屏后信号被子窗口(video)接收,父窗口(player)无法接收到信号,而全屏相关函数的信号接收对象是父窗口,故无法接收到信号。
- 所以,只需在video类中也写入全屏函数即可,详见“6、全屏功能”
- 程序每次运行只能播放一首曲目
- 改用QStringList记录文件名,优化playlist写入方式,详见上文“2、文件批量载入”
- “下一项”功能受限,导入的文件只能播放一次
- 需将mediaplayer设置为循环播放,否则会在播放最后一项时再点击next会导致程序退出
playlist->setPlaybackMode(QMediaPlaylist::Loop);
- 需将mediaplayer设置为循环播放,否则会在播放最后一项时再点击next会导致程序退出
All articles in this blog are licensed under CC BY-NC-SA 4.0 unless stating additionally.