使用的环境:ubuntu16.04,qt5.4.0.
注:写了三天半完成的,使用的线程与进程知识,切歌,显示歌词,控制声量,发送弹幕,查找歌曲,切换桌面背景,模式选择等功能均已实现。但是还是初学者,必然有许多漏洞与不足,希望指正。
部分功能解析:
1.当前时间的获取
QTimer * timer = new QTimer;
timer ->start(1000);
connect(timer,&QTimer::timeout,[=](){
//当1秒的时间结束,触发这个槽函数
QDateTime time = QDateTime::currentDateTime();
//获取当前的时间
QString str = time.toString("yyyy-MM-dd \n hh:mm:ss");
});
2.mplayer的使用
volume * 1:设置音量的大小
mute 1/0 :设置音量的静音与取消(1为暂停,0为取消)
pause :暂停与继续
seek value :设置快退与快进,取决与value的正负值
execlp("mplayer","myplayer","-idle","-slave","-quiet","-input","file=./a.txt","1.mp3",NULL);
//这一个语句就是调用mplayer进程的命令
//要尽量给其开辟一个新的进程或者是线程,因为其一旦运行,后面的代码就会被取代
//最后面的哪一个NULL参数不能够被省略
//每一个参数都有其的含义,可以看到前面两个参数都是mplayer,含义确实不一样地,第一个是可执行文件的名字,由他这个参数去查找命令,第二个参数是命令。
3.字符串的拼接
QString的使用与C++中的string并不是相同的,不能够通过+号来进行凭借(C++中能够直接+号拼接是因为这个+号被重载过了)
那么在qt中的QString怎么拼接呢
//从lineEdit获得到ui上输入的字符,并把其存放在text中
QString text = ui->lineEdit_saysomesthing->text();
QDateTime time = QDateTime::currentDateTime();
QString str = time.toString("yyyy-MM-dd hh:mm:ss"); //获取当前的时间
QString status = QString(" %1 %2").arg(text).arg(str);//字符串拼接
4.背景图的切换
我采用的是按钮,按钮按下,图片切换
//按键改变桌面
connect(ui->table_pushButton,&QPushButton::clicked,[=](){
table_flag++; // 图片的标志位
if(table_flag % 3 == 1 )
//给上图片的路径,这个pixmapPath是一个变量,定义在类中了,所以可以这么直接用上了
{
pixmapPath=(":/imag/photo_1.png");}
else if(table_flag % 3 == 2)
{
pixmapPath=(":/imag/photo_2.png");}
//这个updata()是必须要有的,用来更新桌面的背景
this->update();
});
5.歌词的切割
//歌词解析
//传入歌曲的名字,得到与之对应的歌词地址
void Widget::Parsing_lyrics(QString)
{
if(lrc.empty() == 0)
{
lrc.clear();
}
char buff[32]="";
//此时song_name[music_num]的类型是QString类型,需要转换为char * 类型,然后再进行其他的操作,此时获得到的是歌曲文件的地址
//由于我这个地方偷懒了,把歌曲的名字都改为数字了,如果想要使用,就要自己再想想怎么把歌曲地址通过组包解包,变换为歌词的地址
//在我这里是将./3.mp3---->./3.rlc
strcpy(buff,song_name[music_num].toUtf8().data()); // ./*.mp3
int n;
sscanf(buff,"./%d.mp3",&n);
printf("buff2: %d",n);
char buff3[32]="";
sprintf(buff3,"./%d.lrc",n);
// ./*.mp3 ------------------> ./*.lrc
printf("%s\n",buff3);
FILE * fp = fopen(buff3,"r");
if(fp == NULL)
{
perror("fopen");
}
char buff_lrc[128]="";
while(fgets(buff_lrc,sizeof(buff_lrc),fp) != NULL)
{
char *p = buff_lrc;
if(p[1] >= '0' && p[1] <= '9')
{
char * q = p;
while(*q == '[')
{
if(q[10] == ']')
{
q += 11;}
else
{
q+=10;}
}
while(*p == '[')
{
int m = 0, n = 0;
sscanf(p,"[%d:%d",&m,&n);
this->lrc.insert(m*60+n,q);
//printf("%d,%s\n",m*60+n,q);
p+=10;
}
}
}
fclose(fp);
}
6.自定义信号与槽
信号的来源选择使用emit发送,这个信号必须在类中定义哈,但是不用去实现
自定义的槽函数需要定义,并且实现
这是需要在.h中实现的
signals://这个信号也要自己写哦
void change_progess(int value);//自定义的信号
private slots://自定义槽函数
void changeProgess(int value);//自定义的槽函数
发出信号
emit p->change_progess(rate_of_progress);
//发出信号,外界接收,从而控制进度的显示
//获得信号改变进度条 connect(this,SIGNAL(change_progess(int)),this,SLOT(changeProgess(int)));
void Widget::changeProgess(int value)
{
ui->progressBar->setValue(value);
这个就是槽函数,改变页面上进度条
}
代码如下
.cpp:
#include "widget.h"
#include
#include
#include
#include
#include
#include
#include
#include "ui_widget.h"
#include
#include
#include
#include
#include
#include
#include
#include
void * father(void * arg);
void * father2(void * arg);
int gettime_flag;
int change_mode; //改变模式标志位
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
//创建无名管道
pipe(fd_pipe);
//创建有名管道
int ret2 = mkfifo("./a.txt",0666);
if(0 == ret2)
{
qDebug()<<"创建成功!\n"<setupUi(this);
change_mode = 0; //初始化模式为单曲播放模式
music_num = 0; //初始化播放曲目
say_num = 0; //初始化弹幕,从第0个开始进行滚动播放弹幕
volume = 70; //初始化音量
volume_flat=1; //静音标志位
this->pixmapPath=":/imag/photo_1.png"; //初始化桌面
get_song_name(); //调用该函数,获得所有的歌曲的名字
get_say(); //调用该函数,获得弹幕的初始化
table_flag=1; //初始化桌面壁纸
gettime_flag=0; //全局变量,用于按下暂停后记录的标志位,按下暂停后,不再获取时间
Parsing_lyrics(song_name[music_num]);//加载第一首歌的歌词,解析歌词,把歌词放入map容器中
this->fd = open("a.txt",O_WRONLY); //只写打开有名管道
pthread_t pth3,pth4;
pthread_create(&pth3,NULL,father,(void *)this); //创建子线程
pthread_detach(pth3);//线程分离
pthread_create(&pth4,NULL,father2,(void *)this);
pthread_detach(pth4);
//暂停或者继续
connect(ui->pause_pushButton,SIGNAL(clicked(bool)),this,SLOT(mystop(void)));
//杀死子进程,再关闭父进程
connect(ui->close_pushButton,SIGNAL(clicked(bool)),this,SLOT(myclose(void)));
//加大声量
connect(ui->volume_up_pushButton,SIGNAL(clicked(bool)),this,SLOT(turn_up_volume(void)));
//减小声量
connect(ui->volume_done_pushButton,SIGNAL(clicked(bool)),this,SLOT(reduce_the_volume(void)));
//播放下一首
connect(ui->next_pushButton,SIGNAL(clicked(bool)),this,SLOT(next_music(void)));
//播放上一首
connect(ui->before_pushButton,SIGNAL(clicked(bool)),this,SLOT(before_music(void)));
//查找歌曲功能,lambal表达式
connect(ui->find_pushButton,&QPushButton::clicked,[=](){
//获得这一行输入的歌曲名
QString text = ui->lineEdit->text();
//qDebug()<<"get lintEdit:"<::iterator it = song_name.begin();
QVector::iterator ret;
//在容器中查找有没有这个歌曲的名字,使用迭代器查找
ret = qFind(song_name.begin(),song_name.end(),text);
if(ret != song_name.end())
{
QMessageBox::information(this,"yes","find it!");
char buff[64]="";
sprintf(buff,"loadfile %s\n",text.toUtf8().data());
//如果找到了,进行组包之后,发送给有名管道,从而控制mplayer进行播放
write(this->fd,buff,strlen(buff));
//定义的函数,功能是传入指定的歌曲名字,然后进行歌词的解析,前提是你的文件目录下面得要有哈
Parsing_lyrics(song_name[music_num]);
}
else
{
QMessageBox::information(this,"sorry","not find it!");
}
});
//按键改变桌面
connect(ui->table_pushButton,&QPushButton::clicked,[=](){
table_flag++;
if(table_flag % 12 == 1 )
//给上图片的路径,这个pixmapPath是一个变量,定义在类中了,所以可以这么直接用上了
{pixmapPath=(":/imag/photo_1.png");}
else if(table_flag % 12 == 2)
{pixmapPath=(":/imag/photo_2.png");}
else if(table_flag % 12 == 3)
{pixmapPath=(":/imag/photo_3.png");}
else if(table_flag % 12 == 4)
{pixmapPath=(":/imag/photo_4.png");}
else if(table_flag % 12 == 5)
{pixmapPath=(":/imag/photo_5.png");}
else if(table_flag % 12 == 6)
{pixmapPath=(":/imag/photo_6.png");}
else if(table_flag % 12 == 7)
{pixmapPath=(":/imag/photo_7.png");}
else if(table_flag % 12 == 8)
{pixmapPath=(":/imag/photo_8.png");}
else if(table_flag % 12 == 9)
{pixmapPath=(":/imag/photo_9.png");}
else if(table_flag % 12 == 10)
{pixmapPath=(":/imag/photo_10.png");}
else if(table_flag % 12 == 11)
{pixmapPath=(":/imag/photo_11.png");}
//这个updata()是必须要有的,用来更新桌面的背景
this->update();
});
//获得信号改变进度条
connect(this,SIGNAL(change_progess(int)),this,SLOT(changeProgess(int)));
//动态显示歌词
connect(this,SIGNAL(lrc_time(int)),this,SLOT(show_lrc(int)));
//用定时器获取当前时间并赋给label_time
QTimer *timer = new QTimer;
timer->start(5000);
//每五秒更换一条弹幕
connect(timer,&QTimer::timeout,[=](){
if(say_num == (say.size()-1))
{
say_num = 0;
}
else
{
say_num++;
}
//每隔五秒显示容器里面的内容
ui->pushButton->setText(say[say_num]);
});
//获取从页面上输入的信息,并在后面加上当前的时间,并肩这个两条信息合并,插入到这个弹幕的容器中
connect(ui->pushButton_say,&QPushButton::clicked,[=](){
QString text = ui->lineEdit_saysomesthing->text();
QDateTime time = QDateTime::currentDateTime();
QString str = time.toString("hh:mm:ss"); //获取当前的时间
QString status = QString(" %1 %2").arg(text).arg(str);//字符串拼接
//将拼接的字符串放入容器中
this->say.push_back(status);
QMessageBox::information(this,"ok","insert suessful");
//插入结束了之后,清空lineEdit
ui->lineEdit_saysomesthing->clear();
});
}
}
//父线程用于向子线程发送消息(有名管道),来获得歌曲播放的时间,总时间,以及进度
void * father(void * arg)
{
Widget * p = (Widget *)arg;
while(1)
{
if(gettime_flag % 2 == 0)
{
//printf("father 1 now gettime_flag is :%d\n",gettime_flag);
write(p->fd,"get_time_pos\n",strlen("get_time_pos\n"));
usleep(100*100);
write(p->fd,"get_time_length\n",strlen("get_time_length\n"));
usleep(100*100);
write(p->fd,"get_percent_pos\n",strlen("get_percent_pos\n"));
usleep(100*1000);
}
else
{
//printf("stop 1\n");
sleep(1);
}
}
}
//通过无名管道,获取歌曲的状态信息
void * father2(void * arg)
{
Widget * p = (Widget *)arg;
int time_now,time_all;
while(1)
{
//printf("father 2 now gettime_flag is :%d\n",gettime_flag);
char buff[64]="";
read(p->fd_pipe[0],buff,sizeof(buff));
//printf("this is read son :%s",buff);
if(strncmp(buff,"ANS_TIME_POSITION",strlen("ANS_TIME_POSITION")) == 0)
{
//get time
sscanf(buff,"ANS_TIME_POSITION=%d",&time_now);
int minute = time_now / 60;
int second = time_now % 60;
char buf_time[32]="";
sprintf(buf_time,"%02d:%02d",minute,second);
p->ui->label_time->setText(buf_time);
emit p->lrc_time(time_now); //发出信号,外界接收,从而控制歌词的显示
}
if(strncmp(buff,"ANS_LENGTH",strlen("ANS_LENGTH")) == 0)
{
//get all time
sscanf(buff,"ANS_LENGTH=%d",&time_all);
int minute2 = time_all / 60;
int second2= time_all % 60;
char buf_time2[32]="";
sprintf(buf_time2,"%02d:%02d",minute2,second2);
p->ui->label_time_all->setText(buf_time2);
//printf("all time is : %d\n",time_all);
}
if(strncmp(buff,"ANS_PERCENT_POSITION",strlen("ANS_PERCENT_POSITION")) == 0)
{
int rate_of_progress;
sscanf(buff,"ANS_PERCENT_POSITION=%d",&rate_of_progress);
//printf("get rate of progress is %d\n",rate_of_progress);
emit p->change_progess(rate_of_progress);//发出信号,外界接收,从而控制进度的显示
}
}
}
Widget::~Widget()
{
delete ui;
}
//歌词解析
void Widget::Parsing_lyrics(QString)
{
if(lrc.empty() == 0)
{
lrc.clear();
}
char buff[32]="";
strcpy(buff,song_name[music_num].toUtf8().data()); // ./*.mp3
int n;
sscanf(buff,"./%d.mp3",&n);
printf("buff2: %d",n);
char buff3[32]="";
sprintf(buff3,"./%d.lrc",n);
// ./*.mp3 ------------------> ./*.lrc
printf("%s\n",buff3);
FILE * fp = fopen(buff3,"r");
if(fp == NULL)
{
perror("fopen");
}
char buff_lrc[128]="";
while(fgets(buff_lrc,sizeof(buff_lrc),fp) != NULL)
{
char *p = buff_lrc;
if(p[1] >= '0' && p[1] <= '9')
{
char * q = p;
while(*q == '[')
{
if(q[10] == ']')
{q += 11;}
else
{q+=10;}
}
while(*p == '[')
{
int m = 0, n = 0;
sscanf(p,"[%d:%d",&m,&n);
this->lrc.insert(m*60+n,q);
//printf("%d,%s\n",m*60+n,q);
p+=10;
}
}
}
fclose(fp);
}
//静态插入所有的歌曲的位置
void Widget::get_song_name(void)
{
this->song_name.push_back("./1.mp3");
this->song_name.push_back("./2.mp3");
this->song_name.push_back("./3.mp3");
this->song_name.push_back("./4.mp3");
this->song_name.push_back("./5.mp3");
this->song_name.push_back("./6.mp3");
this->song_name.push_back("./7.mp3");
this->song_name.push_back("./8.mp3");
this->song_name.push_back("./9.mp3");
this->song_name.push_back("./10.mp3");
this->song_name.push_back("./11.mp3");
this->song_name.push_back("./12.mp3");
this->song_name.push_back("./13.mp3");
}
//静态插入弹幕的消息
void Widget::get_say()
{
this->say.push_back("虽然只能输入英文");
this->say.push_back("有弹幕发就好");
this->say.push_back("虽然是滚动的");
this->say.push_back("但是,暂且就当这是一个功能吧");
this->say.push_back("将就着用吧");
this->say.push_back("周继是一个大帅逼!");
this->say.push_back("周继是一个大帅逼!!");
this->say.push_back("周继是一个大帅逼!!!");
this->say.push_back("周继是一个大帅逼!!!!");
}
void Widget::paintEvent(QPaintEvent *ev)
{
//绘制背景图
//定义一个画家QPainter
QPainter *p = new QPainter(this);//在主窗口上画图
//定义图片对象
QPixmap pix;
//pix加载一张图片
pix.load(this->pixmapPath);
//修改图片大小
pix.scaled(this->width(), this->height());
//画家花背景图
p->drawPixmap(0,0,this->width(), this->height(), pix);
}
void Widget::mystop(void)
{
//暂停或者播放
write(this->fd,"pause\n",strlen("pause\n"));
gettime_flag++;
//printf("pause now gettime_flag is :%d\n",gettime_flag);
//printf("写入管道字节数:%ld\n",write_num);
}
//杀死子进程,然后关闭父进程
void Widget::myclose(void)
{
//kill son
kill(0,9);
this->close();
}
//加声量
void Widget::turn_up_volume(void)
{
if(this->volume >= 100)
{
//printf("sorry,The current volume is at maximum\n");
qDebug()<<"sorry,The current volume is at maximum"<volume +=5;
//int fd = open("a.txt",O_WRONLY);
char buff[64]="";
sprintf(buff,"volume %d 1\n",this->volume);
write(this->fd,buff,strlen(buff));
}
}
//减声量
void Widget::reduce_the_volume(void)
{
if(this->volume <= 0)
{
printf("sorry,The current sound volume is at a minimum\n");
QMessageBox::information(this,"sorry","This is the minimum volume!");
}
else
{
this->volume -= 5;
//int fd = open("a.txt",O_WRONLY);
char buff[64]="";
sprintf(buff,"volume %d 1\n",this->volume);
ssize_t write_num = write(this->fd,buff,strlen(buff));
printf("写入管道:%s\n",buff);
}
}
//下一曲
void Widget::next_music(void)
{
if(music_num == song_name.size()-1)
{
printf("All the songs have been played\n");
QMessageBox::information(this,"sorry","this is lost one!");
}
else
{
music_num++;
Parsing_lyrics(song_name[music_num]);
//int fd = open("a.txt",O_WRONLY);
char buff[64]="";
printf("now song is :%s\n",song_name[music_num].toUtf8().data());
sprintf(buff,"loadfile %s\n",song_name[music_num].toUtf8().data());
write(this->fd,buff,strlen(buff));
}
}
//上一曲
void Widget::before_music(void)
{
if(music_num == 0)
{
printf("You can't go any further because this is the last song\n");
QMessageBox::information(this,"sorry","this is lost one!");
}
else
{
music_num--;
Parsing_lyrics(song_name[music_num]);
//int fd = open("a.txt",O_WRONLY);
char buff[64]="";
printf("now song is :%s\n",song_name[music_num].toUtf8().data());
sprintf(buff,"loadfile %s\n",song_name[music_num].toUtf8().data());
write(this->fd,buff,strlen(buff));
}
}
//根据获取时间信号来的动态控制歌词展示
void Widget::show_lrc(int values)
{
QFont ft;
ft.setPointSize(12);
ui->label_lrc_one->setFont(ft);
ui->label_lrc_two->setFont(ft);
QMap::iterator it = lrc.begin();
for(;it != lrc.end();it++)
{
if(it.key() == values)
{
ui->label_lrc_one->setText(lrc[values]);
if(lrc.upperBound(values).key() != lrc.end().key())
{
ui->label_lrc_two->setText(lrc.upperBound(values).value());
}
}
}
}
//静音按钮(这个是转到槽,没有用connect进行连接)
void Widget::on_novolume_pushButton_clicked()
{
if(this->volume_flat % 2 == 1)
{
write(this->fd,"mute 1\n",strlen("mute 1\n"));
}
else if(this->volume_flat % 2== 0)
{
write(this->fd,"mute 0\n",strlen("mute 0\n"));
}
this->volume_flat++;
}
//更改进度条的槽函数
void Widget::changeProgess(int value)
{
//响应进度到100时,切换歌曲,且只能切换一次
//设置随机数种子
srand(time(NULL));
ui->progressBar->setValue(value);
if(100 == value )
{
if(0 == change_mode)
{
printf("单曲播放模式,歌曲结束,等待切换下一首\n");
}
if(1 == change_mode)
{
printf("单曲循环模式\n");
char buff[64]="";
sprintf(buff,"loadfile %s\n",song_name[music_num].toUtf8().data());
write(this->fd,buff,strlen(buff));
}
if(2 == change_mode)
{
printf("顺序播放模式\n");
music_num++;
Parsing_lyrics(song_name[music_num]);
char buff[64]="";
printf("now song is :%s\n",song_name[music_num].toUtf8().data());
sprintf(buff,"loadfile %s\n",song_name[music_num].toUtf8().data());
write(this->fd,buff,strlen(buff));
}
if(3 == change_mode)
{
printf("随机播放模式\n");
music_num = rand()%13+1;
Parsing_lyrics(song_name[music_num]);
char buff[64]="";
printf("now song is :%s\n",song_name[music_num].toUtf8().data());
sprintf(buff,"loadfile %s\n",song_name[music_num].toUtf8().data());
write(this->fd,buff,strlen(buff));
}
usleep(500 * 1000); //防止一直切换,100这个值会接受到多次
}
}
void Widget::on_comboBox_currentIndexChanged(int index)
{
if(0 == index)
{
printf("单曲播放模式\n");
}
if(1 == index)
{
printf("单曲循环模式\n");
}
if(2 == index)
{
printf("顺序播放模式\n");
}
if(3 == index)
{
printf("随机播放模式\n");
}
change_mode = index;
}
.h:
#ifndef WIDGET_H
#define WIDGET_H
#include
#include
#include
#include
#include
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
int volume; //音量
QVector<QString> song_name; //存放所有的歌曲的名字
// say something
QVector<QString> say; //存放弹幕的容器
//lrc
QMap<int,QString> lrc; //存放歌词的容器
void Parsing_lyrics(QString); //歌词解析
int music_num;
int say_num;
void get_song_name(void);
void get_say(void);
int fd;
int volume_flat;
void paintEvent(QPaintEvent *ev);//重写画家事件
int fd_pipe[2];
QString pixmapPath;
int flag;
int table_flag;
Ui::Widget *ui;
public slots:
void mystop(void);
void myclose(void);
void turn_up_volume(void);
void reduce_the_volume(void);
void next_music(void);
void before_music(void);
//歌词显示
void show_lrc(int values);
private slots:
void on_novolume_pushButton_clicked();
void changeProgess(int value);
void on_comboBox_currentIndexChanged(int index);
signals:
void change_progess(int value);
void lrc_time(int value);
private:
};
#endif // WIDGET_H
界面文件