参考:http://blog.csdn.net/liutingxi0709/article/details/51983137
新建一个Qt Widgets Application的项目,类名为MediaPlayer,基类为QMainWindow,自动生成头文件和源文件
项目的进行过程:
首先双击mediaplayer.ui文件,打开Qt Designer,拖几个需要的控件,包括一个Label用于播放视频,objectName属性为label_player,一个Horizontal Slider用于控制进度,objectName属性为slider_progress,三个PushButton,名字改为音量、播放、打开,objectName属性分别为pushButton_volume,pushButton_play_and_pause,pushButton_open_file,再根据需求放几个Spacer。将除了Label以外的多个控件同时选中,右键,用一个Horizontal Layout(水平布局)进行对齐,如图:
同理,将Label控件和上述水平布局同时选中,添加一个栅格布局或者垂直布局:
在没有控件的空白处,右击-布局-垂直布局,添加一个顶级布局:
设置各个控件的sizePolicy(尺寸策略),常用策略包括:
例如在本项目中,可以把Label的垂直策略和Slider的水平策略设置为Expandint。
其余的一些常规属性修改和设置。最后基本界面如下:
完成简单的布局设计之后,就进入下一步
首先打开Qt的工程文件*.pro,向其中添加引用项:
QT += core gui multimedia multimediawidgets
点击 构建-执行qmake,或者在项目窗口中右击执行qmake,使.pro文件配置生效,在mediaplayer.cpp中添加头文件,并定义全局变量
#include
#include
#include
#include
#include
#include
#include
//播放视频的全局变量
QVBoxLayout* layout_video;//布局
QMediaPlayer* player; //播放器
QVideoWidget* widget; //视频播放控件
//播放状态,true为播放,false为暂停
bool play_state;
//是否重新载入视频
bool if_reload=false;
在载入ui时,先禁用播放/暂停按钮和音量按钮:
MediaPlayer::MediaPlayer(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MediaPlayer)
{
ui->setupUi(this);
ui->pushButton_play_and_pause->setEnabled(false);
ui->pushButton_volume->setEnabled(false);
}
在Designer中右击“打开”pushButton控件,“转到槽”->”clicked()”->”ok”,槽函数如下:
void MediaPlayer::on_pushButton_open_file_clicked()
{
//选择视频文件
QString filename = QFileDialog::getOpenFileName(this,tr("选择视频文件"),".",tr("视频格式(*.avi *.mp4 *.flv *.mkv)"));
QFile file(filename);
if(!file.open(QIODevice::ReadOnly))
{
QMessageBox::information(NULL, "Title", "Content", QMessageBox::Ok, QMessageBox::Ok);
return;
}
if(if_reload)
{//重新加载视频时,重置以下变量
delete layout_video;
delete player;
delete widget;
delete timer;
}
if_reload = true;
//实例化三个全局变量
layout_video = new QVBoxLayout;
player = new QMediaPlayer;
widget = new QVideoWidget;
//设置视频播放区域与Label的边距
layout_video->setMargin(1);
//根据label_player尺寸设置播放区域
widget->resize(ui->label_player->size());
layout_video->addWidget(widget);
ui->label_player->setLayout(layout_video);
player->setVideoOutput(widget);
//设置播放器
player->setMedia(QUrl::fromLocalFile(filename));
//play_state为true表示播放,false表示暂停
play_state = true;
//启用播放/暂停按钮,并将其文本设置为“暂停”
ui->pushButton_play_and_pause->setEnabled(true);
ui->pushButton_play_and_pause->setText("暂停");
//播放器开启
player->play();
}
到这一步,就已经可以播放视频了。接下来我们添加“播放/暂停”按钮的槽函数:
void MediaPlayer::on_pushButton_play_and_pause_clicked()
{
//反转播放状态
if(play_state)
{
player->pause();
ui->pushButton_play_and_pause->setText("播放");
}
else
{
player->play();
ui->pushButton_play_and_pause->setText("暂停");
}
play_state = !play_state;
}
好了,打开测试一下:
如果播放时出现只有声音而没有画面的情况,那就是解码器的问题。在写这个程序时找了久的原因,最后在这篇博文里找到了解决方案: [ CN小黑 极客人生] 推荐安装K-Lite解码器。至此,基于QMediaPlayer的核心播放功能已经编写完成,接下来我们通过QSlider对播放进度和音量进行控制。
QSlider类继承自QAbstractSlider类,可以参考 [ 官方文档 ],其自带的信号如下:
对于播放器来说,其进度条应该有两种控制方式,一是拖动,二是点击。 其中我们需要用到sliderMoved()和sliderReleased()两种信号来实现拖动功能,对于点击,QSlider的mousePressEvent()默认的方式是,点击之后跳跃一定的固定距离,无法实现“指哪打哪”,因此我们需要对mousePressEvent()进行重写。具体方法将在下一节中介绍。
首先在ui载入时将Slider禁用,等到文件载入时才启用。然后连接三组信号槽,第一组是由重载的mousePressEvent事件发送的,为避免混淆换了个costomSliderClicked的名字(下一节中有类的构造方法)。其余两组是QSlider自带的sliderMoved和sliderReleased信号。
MediaPlayer::MediaPlayer(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MediaPlayer)
{
ui->setupUi(this);
ui->pushButton_play_and_pause->setEnabled(false);
ui->pushButton_volume->setEnabled(false);
ui->slider_progress->setEnabled(false);
connect(ui->slider_progress,&CustomSlider::costomSliderClicked,this,&MediaPlayer::slider_progress_clicked);
connect(ui->slider_progress,&CustomSlider::sliderMoved,this,&MediaPlayer::slider_progress_moved);
connect(ui->slider_progress,&CustomSlider::sliderReleased,this,&MediaPlayer::slider_progress_released);
}
首先我们需要配合定时器QTimer实现Slider随播放进度而移动。 之所以选择QTimer,而不是player的positionChanged信号来驱动,是因为positionChange和后面要用的QSlider两个信号槽互相修改,容易出现冲突。在mediaplayer.cpp中定义全局变量:
//与Slider有关的播放控制变量
QTimer * timer;
int maxValue = 1000;//设置进度条的最大值
在on_pushButton_open_file_clicked槽函数中添加Slider和Timer的相关代码,并将timer连接至onTimerOut槽函数:
void MediaPlayer::on_pushButton_open_file_clicked()
{
//前面部分代码与第2篇中相同//
//启用slider并设置范围
ui->slider_progress->setEnabled(true);
ui->slider_progress->setRange(0,maxValue);
timer = new QTimer();
timer->setInterval(1000);//如果想看起来流畅些,可以把时间间隔调小,如100ms
timer->start();
//将timer连接至onTimerOut槽函数
connect(timer, SIGNAL(timeout()), this, SLOT(onTimerOut()));
}
添加定时器的槽函数onTimerOut(),其原理就是根据一定的间隔(本例中为1000ms)刷新Slider的值,这个值是根据播放器player的position(当前位置)和duration(总时长)计算出来的:
void MediaPlayer::onTimerOut()
{
ui->slider_progress->setValue(player->position()*maxValue/player->duration());
}
到这里就实现了Slider随进度移动的功能,接下来添加控制代码。 三个槽函数分别对应单击、拖动和释放。在拖动过程中,可以先暂停计时器,等用户拖动完成释放之后,再重启定时器。这样防止用户在拖动过程中滑块依然按照定时器触发进行移动,瞎跳,闹心。
void MediaPlayer::slider_progress_clicked()
{
player->setPosition(ui->slider_progress->value()*player->duration()/maxValue);
}
void MediaPlayer::slider_progress_moved()
{
//暂时停止计时器,在用户拖动过程中不修改slider的值
timer->stop();
player->setPosition(ui->slider_progress->value()*player->duration()/maxValue);
}
void MediaPlayer::slider_progress_released()
{
//用户释放滑块后,重启定时器
timer->start();
}
在mediaplayer.h中添加槽函数的声明
private slots:
void on_pushButton_open_file_clicked();
void on_pushButton_play_and_pause_clicked();
void onTimerOut();
void slider_progress_clicked();
void slider_progress_moved();
void slider_progress_released();
音量控制的方法与进度控制非常相似,可以直接使用重载后的CustomSlider类。这里我们不在Designer中拖入控件,而是通过手动实现的方式来添加音量控制的Slider。首先在mediaplayer.cpp的构造函数中继续添加初始化内容
MediaPlayer::MediaPlayer(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MediaPlayer)
{
/*与上述相同*/
/*
……
*/
//手动设置slider_volume 包括初始化 方向 禁用,以及槽函数
slider_volume = new CustomSlider(this);
slider_volume->setOrientation(Qt::Vertical);
slider_volume->setEnabled(false);
slider_volume->hide();
//由于不涉及到slider值的刷新,因此只需对move和自定义click两个信号进行处理,并且可以共用一个槽函数
connect(slider_volume,&CustomSlider::costomSliderClicked,this,&MediaPlayer::slider_volume_changed);
connect(slider_volume,&CustomSlider::sliderMoved,this,&MediaPlayer::slider_volume_changed);
}
槽函数
//音量控制Slider的槽函数
void MediaPlayer::slider_volume_changed()
{
player->setVolume(slider_volume->value());
}
音量控制按钮的槽函数,通过hide()和show()方法,实现音量控制Slider的唤出和收起
bool state_slider_volume = false;
void MediaPlayer::on_pushButton_volume_clicked()
{
if(state_slider_volume)
{
slider_volume->hide();
}
else
{
slider_volume->setValue(player->volume());
//计算位置,使其位于音量控制按钮的上方
slider_volume->setGeometry(QRect(ui->pushButton_volume->pos().rx()+0.5*ui->pushButton_volume->width()-15, ui->pushButton_volume->y()-100 , 30, 102));
slider_volume->show();
}
state_slider_volume = !state_slider_volume;
}
在mediaplayer.h中继续添加槽函数的声明
private slots:
void slider_volume_changed();
void on_pushButton_volume_clicked();
private:
CustomSlider *slider_volume;
到这里,QSlider的移动与控制功能就已经完成了,但是实际上我们需要重载QSlider的mousePressEvent以实现指哪打哪的效果,因此需要继承一个新类CustomSlider,详情见下一节。
前面提到,对于点击,QSlider的mousePressEvent()默认的方式是,点击之后跳跃一定的固定距离,无法实现“指哪打哪”。
想要实现单击跳转至任意位置,有两种方案:
对于后者掌握还不太熟练,而且项目中后续需要用到多个Slider,封装成子类来用会比较方便。所以还是根据 [ 冬南风 的博客 ]通过重载来完成这一功能。与原博稍有区别,原博采用的方式是重载mouseMovedEvent之后,向父窗口发送自定义事件event type,这样就可以在父窗口中捕获这个事件进行处理。
我的选择是自定义了一个costomSliderClicked信号,在mouseMovedEvent执行时将该信号发出,然后统一用槽函数处理。槽函数的具体写法在上一篇中已经做了介绍。
第一步是给QSlider添加一个子类。在项目窗口中,右击工程的根目录,添加新文件,选择C++ Class,填写基类为QSlider。我这里将其命名为CustomSlider。
customslider.h
#ifndef CUSTOMSLIDER_H
#define CUSTOMSLIDER_H
#include
#include
#include
class CustomSlider : public QSlider
{
Q_OBJECT
public:
CustomSlider(QWidget *parent = 0) : QSlider(parent)
{
}
protected:
void mousePressEvent(QMouseEvent *ev);//重写QSlider的mousePressEvent事件
signals:
void costomSliderClicked();//自定义的鼠标单击信号,用于捕获并处理
};
#endif // CUSTOMSLIDER_H
customslider.cpp
#include "customslider.h"
void CustomSlider::mousePressEvent(QMouseEvent *ev)
{
//注意应先调用父类的鼠标点击处理事件,这样可以不影响拖动的情况
QSlider::mousePressEvent(ev);
//获取鼠标的位置,这里并不能直接从ev中取值(因为如果是拖动的话,鼠标开始点击的位置没有意义了)
double pos = ev->pos().x() / (double)width();
setValue(pos * (maximum() - minimum()) + minimum());
//发送自定义的鼠标单击信号
emit costomSliderClicked();
}
然后,打开Designer,右击slider,选择“提升为”
填写继承的子类名,勾选
然后在mediaplayer中加入子类的头文件customslider.h,在上一节中已经介绍了信号槽的连接与槽函数的写法。至此,一个基于QMediaPlayer的简易视频播放器就完成了,包括了视频播放、进度控制和音量控制等基本功能,并且能很好地支持绝大多数的常见格式。(音量的效果最终没有出来,还需查找一下原因)