整体思路:
首先我设置了用户的登录注册以及 删除,
效果如图:
注册界面如图(我家的狗狗):
登录成功以后进入播放器播放音乐
进入播放器效果以及播放效果如图:
登录注册的思路:我在这里新建了一个文件用来保存注册的用户和密码
登陆的时候扫描文件看是否有相对应的用户和密码
注册的时候在添加,本来想尝试数据库的,但是数据库连接qt太麻烦了
所以索性直接用文件了
登录界面代码:
#include "widget.h"
#include "ui_widget.h"
#include<QDateTime>
#include <QPainter>
#include <QPixmap>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{ ui->setupUi(this);
// 创建 QMovie 并设置其大小
mv2 = new QMovie(":/image/10.gif");
mv2->setScaledSize(ui->mf->size());
// 将 QMovie 设置到 QLabel 中
ui->mf->setMovie(mv2);
// 开始播放动画
mv2->start();
// 创建 p_new 和 p_new2 对象
p_new = new NewWidget;
p_new2 = new Register;
//监听新窗口的回退信号
connect(p_new,SIGNAL(goBack()),this,SLOT(goBackFunc()));
connect(p_new2,SIGNAL(goBack1()),this,SLOT(goBackFunc()));
mv=new QMovie(":/image/fddm.gif");
ui->label->setMovie(mv);
mv->start();
}
Widget::~Widget()
{
delete ui;
delete p_new;
delete p_new2;
}
#include<QMessageBox>
#include<QDebug>
void Widget::on_loginbtn_clicked()
{ static int count = 0;
QString name=ui->userEdit->text();
QString pass=ui->passwordEdit->text();
if((name!=NULL) && (pass!=NULL))
{
FILE * fp;
FILE * fp4; //定义一个文件流指针
if((fp = fopen("D:/05code/1.txt","r+"))==NULL)
{
perror("fail to open!");
}
if((fp4 = fopen("D:/05code/3.txt","a+"))==NULL)
{
perror("fail to open!");
}
char temp[300];
while(fgets(temp,300,fp) != NULL)
{
qDebug()<<"正在检查是否是否有匹配的id和密码";
QString dtn = QString(strtok(temp,":"));
QString dtp = QString(strtok(NULL,"\n"));
qDebug()<<dtn<<" "<<dtp;
if((dtn == name ) && (dtp == pass))
{
QDateTime currentDateTime = QDateTime::currentDateTime();
QString formattedDateTime = currentDateTime.toString("yyyy-MM-dd hh:mm:ss");
fputs("用户", fp4);
fputs(dtn.toUtf8().constData(), fp4); // toUtf8() 将 QString 转换为 QByteArray
fputs("密码:", fp4);
fputs(dtp.toUtf8().constData(), fp4);
fputs("进行登录 ", fp4);
fputs("当前时间:", fp4);
fputs(formattedDateTime.toUtf8(), fp4);
fputs("\n",fp4);
//QMessageBox::information(this,"恭喜","登录成功!");
//然后跳转新窗口
this->hide(); //子窗口不能够使父窗口隐藏!(需要自定义信号与槽)
p_new->show();
count++;
}
}
if(count == 0)
{
QMessageBox::critical(this,"错误!","密码或账号有误!");
}
fclose(fp);
fclose(fp4);
}
else
{
QMessageBox::critical(this,"错误!","密码为空");
}
}
void Widget::on_canceBtn_clicked()
{
ui->userEdit->setText("");
ui->passwordEdit->setText("");
}
void Widget::goBackFunc()
{
//新窗口隐藏
p_new->hide();
this->show();
p_new2->hide();
}
void Widget::on_pushButton_clicked()
{
this->hide();
p_new2->show();
}
注册界面的代码:
#include "register.h"
#include "ui_register.h"
#include <QMessageBox>
#include <QDebug>
#include <QByteArray>
#include <QString>
#include <QPainter>
#include <QPixmap>
Register::Register(QWidget *parent) :
QWidget(parent),
ui(new Ui::Register)
{
ui->setupUi(this);
}
Register::~Register()
{
delete ui;
}
void Register::paintEvent(QPaintEvent *event)
{
Q_UNUSED(event)
QPainter painter(this);
painter.setRenderHint(QPainter::Antialiasing);
painter.drawPixmap(rect(),QPixmap(":/new/prefix1/image/gougou.jpg"));
}
void Register::on_pushButton_clicked()
{
QString name,pass,pass2;
name = ui->namelineEdit->text();
pass = ui->passwordlineEdit->text();
pass2 = ui->passwordlineEdit2->text();
//首先判断三个输入框有没有没为空
if((name != "")&&(pass != "")&&(pass2 != "") )
{
if(pass == pass2)
{
qDebug()<<"格式正确";
FILE * fp;
if((fp = fopen("D:/05code/1.txt","r+"))==NULL)
{
perror("fail to open!");
}
char temp[728];
while(fgets(temp,728,fp) != NULL)
{
qDebug()<<"正在检查是否重名";
QString dtn = QString(strtok(temp,":"));
if(dtn == name)
{
QMessageBox::critical(this,"错误","该用户名已经存在");
return ;
}
}
fclose(fp);
FILE * fp1;
if((fp1 = fopen("D:/05code/1.txt","a+"))==NULL)
{
perror("fail to fopen");
}
else
{
QByteArray qtname = ui->namelineEdit->text().toLatin1();
QByteArray qtpass = ui->passwordlineEdit->text().toLatin1();
char * tn = qtname.data();
char * tp = qtpass.data();
fputs(tn,fp);
fputs(":",fp);
fputs(tp,fp);
fputs("\n",fp);
qDebug()<<" 写入文件成功";
QMessageBox::information(this,"恭喜","注册成功!");
}
fclose(fp1);
}
else
{
QMessageBox::critical(this,"错误","两次输入的密码不一致!");
}
}
else
{
QMessageBox::critical(this,"错误","密码或账号为空!");
}
// 重新定位文件指针到文件开头
emit goBack1();
}
界面切换的思路如图:
播放器代码的思路如图:
父进程中开俩个线程,俩个线程一个负责发信号,一个负责收信号,
通过dup(fd[1],1)改变子进程的写的文件描述符,使得子进程每次接收到父进程的信号后输出的信息流都在无名管道中,这样另一个线程一直读无名管道的fd[0]就可以知道当前的播放时间,播放进度,播放音乐的总时间,
另一个线程持续向有名管道中发消息让子进程进行响应。
(1)首先创建线程和进程
创建进程(注意要改变子进程的输出文件描述符,因为默认是1,把这个文件描述符改为无名管道的写段这样父子进程就可以进行通信了,父进程可以接收到子进程回应的歌曲时间消息和进度消息):
void Widget::mplayer_init()
{
//创建无名管道
pipe(fd);
// 创建有名管道
mkfifo("fifo_cmd",0666);
// 创建父子进程(子进程启动mplayee)
pid_t pid =fork();
if(pid ==0)
{
//关闭文件描述符0
// ::close(0);
//将文件描述符1 定向到fd[1] (无名管道的写端)
dup2(fd[1],1);
//int count=Widget::Ifmp3();
// 启动mplayer
std::vector<std::string> mp3Files = ScanMP3Files("./res/music_mp3/");
execlp("mplayer","mplayer","-ac","mad","-slave","-quiet",\
"-idle","-input","file=./fifo_cmd",NULL);
}
mplayerPID=pid;
// 打开有名管道
fifoFd = open("fifo_cmd",O_WRONLY );
if(fifoFd < 0)
{
qDebug()<<"打开有名管道失败"<< endl;
_exit(-1);
}
}
// 1 初始化mplayer()创建 无名管道 有名管道
mplayer_init();
// 2. 创建一个发送指令的线程
pthread_t sendTid;
pthread_create(&sendTid,NULL, sendCmdFunc,this);
pthread_detach(sendTid);
// 3. 创建一个接收mplayer应答的线程
pthread_t recvTid;
pthread_create(&recvTid, NULL,recvFromMplayer, this);
pthread_detach(recvTid);
(2)接下来进行完成线程的基本代码
发送给子进程消息的线程
void *sendCmdFunc(void *arg)
{
Widget *p= (Widget *)arg;
while(1)
{
//发送获取进度的指令
write(p->fifoFd,"get_percent_pos\n",strlen("get_percent_pos\n"));
// usleep 微秒
usleep(500*100);
//发送获取当前时间的指令 将命令写到 有名管道里面
write(p->fifoFd,"get_time_pos\n",strlen("get_time_pos\n"));
//发送获取当前时间长度的请求
write(p->fifoFd,"get_time_length\n",strlen("get_time_length\n"));
usleep(500*10);
//发送获取的歌曲名
write(p->fifoFd,"get_file_name\n",strlen("get_file_name\n"));
usleep(500*10);
}
}
从无名管道中接受消息的接收线程:
void *recvFromMplayer(void *arg)
{
Widget *p= (Widget *)arg;
while(1)
{
char buf[128];
int len = read(p->fd[0],buf,sizeof(buf));
if(len>0)
{
// 对buf的内容做出判断
if(strncmp(buf,"ANS_PERCENT_POSITION",strlen("ANS_PERCENT_POSITION"))==0)
{
//收到的是进度信息
int perPos=0;
//count++;
sscanf(buf,"ANS_PERCENT_POSITION=%d", &perPos);
emit p->percentPosSignal(perPos);
}
else if ((strncmp(buf,"ANS_TIME_POSITION",strlen("ANS_TIME_POSITION"))==0)||(strncmp(buf,"ANS_LENGTH",strlen("ANS_LENGTH"))==0))
{
if(strncmp(buf,"ANS_TIME_POSITION",strlen("ANS_TIME_POSITION"))==0)
{
//收到的是shijian信息
perTime=0;
sscanf(buf,"ANS_TIME_POSITION=%d", &perTime);
char tmp[32];
sprintf(tmp,"%02d:%02d",perTime/60,perTime%60);
emit p->percentTimeSignal(QString(tmp));
}
else if(strncmp(buf,"ANS_LENGTH",strlen("ANS_LENGTH"))==0)
{
//收到的是zshijian信息
totaltime=0;
sscanf(buf,"ANS_LENGTH=%d", &totaltime);
char tmp[32];
sprintf(tmp,"%02d:%02d",totaltime/60,totaltime%60);
emit p->percentAllTimeSignal(QString(tmp));
}else if(strncmp(buf,"ANS_FILENAME ",strlen("ANS_FILENAME "))==0) {
qDebug("bbbd");
char fileName[128];
qDebug("bbbd");
sscanf(buf, "ANS_FILENAME=%127s", fileName);
std::string fileNameStr(fileName);
qDebug() << "提取到的文件名:" << fileNameStr.c_str();
}
}
}
}
}
ui界面设计如下:
搜索本地歌曲的槽函数:
//扫描当前文件是否有MP3文件有的话加进去
void Widget::on_FlushPushbutton_clicked()
{
// 清空列表
ui->listWidget->clear();
// 1. 打开目录
DIR *dir = opendir("./res/music_mp3/");
if (dir == NULL)
{
perror("打开目录失败\n");
return;
}
//printf("打开成功\n");
// 2. 从目录句柄 dir 逐个读取文件并判断是否为 MP3 文件
while (1)
{
struct dirent *entry = readdir(dir);
if (entry == NULL) // 扫描完成
break;
if ((entry->d_type & DT_REG) == DT_REG)
{
// 获取文件名
QString filename = QString::fromUtf8(entry->d_name);
// 检查文件扩展名是否为 ".mp3",不区分大小写
if (filename.toLower().endsWith(".mp3"))
{
// 如果是 MP3 文件,添加到 listWidget
ui->listWidget->addItem(filename);
}
}
}
closedir(dir);
}
添加歌曲的槽函数
/add槽函数
void Widget::on_AddButton_clicked()
{
const QString filePath = QFileDialog::getOpenFileName(this, "选择音乐",
"./res/music_mp3/","MP3 文件 (*.mp3)");
if (!filePath.isEmpty())
{
QString fileName = QFileInfo(filePath).fileName();
ui->listWidget->addItem(fileName);
}
}
将歌词存到map表中
QMap<QString, QString> parseLyricsFile(const QString &filename)
{
QMap<QString, QString> lyricsMap;
QFile file(filename);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
{
qDebug() << "无法打开文件";
return lyricsMap;
}
QTextStream in(&file);
in.setCodec("UTF-8");
QString currentLyric = "";
while (!in.atEnd())
{
QString line = in.readLine();
// 假设时间标签的格式为 "[00:02.00]"
if (line.startsWith("[") && line.contains("]"))
{
QString timeTag = line.mid(1, line.indexOf("]") - 1); // 提取时间标签
currentLyric = line.mid(line.indexOf("]") + 1).trimmed(); // 提取歌词内容
lyricsMap[timeTag] = currentLyric;
}
else
{
currentLyric += "\n" + line.trimmed(); // 合并多行歌词内容
if (!currentLyric.isEmpty())
{
lyricsMap[""] = currentLyric; // 使用空字符串作为键存储多行歌词
}
}
}
file.close();
return lyricsMap;
}
我想实现的目的是点击刷新列表然后出现MP3文件,双击它开始播放,所以设计了双击鼠标事件的槽函数
//声明2个全局变量
QString lrc1;
QString filename="./res/lrc/"+lrc1;
然后,写双击事件的槽函数,往管道里面写命令使音乐播放
定义的全局变量代表着歌词的更新,这样就可以实现歌词和歌曲的同步了
//double槽函数
void Widget::on_listWidget_doubleClicked(const QModelIndex &index)
{
QListWidgetItem *selectedItem = ui->listWidget->currentItem();
if (selectedItem != NULL) {
QString mp3FileName = selectedItem->text(); // 获取当前选中的 mp3 文件名
std::string path = "./res/music_mp3/";
// 构造对应的 lrc 文件名,假设 mp3 和 lrc 文件名一致,只是扩展名不同
QString lrcFileName = mp3FileName;
lrcFileName.replace(".mp3", ".lrc");
// 更新类成员变量 lrc1
lrc1 = lrcFileName;
std::string command = "loadfile " + path + mp3FileName.toStdString() + "\n";
// 更新 filename 变量
filename = "./res/lrc/" + lrc1;
write(fifoFd, command.c_str(), command.size());
} else {
// 处理未选择项的情况
qDebug() << "未选择任何项";
}
clearlabel();
}
定义了一个定时器来实现歌词的更新,这样刚刚声明的全局变量就可以起到作用了:
void Widget::updateLyrics()
{
QMap<QString, QString> lyricsMap = parseLyricsFile(filename);
QString targetTime = ui->allTime->text().left(5);
for (auto it = lyricsMap.constBegin(); it != lyricsMap.constEnd(); ++it)
{
QString timeTag = it.key().left(5);
if (timeTag == targetTime)
{
if (it.value().isNull()) {
break;
}
count++;
updateLabel(it.value());
break;
}
}
}
void Widget::updateLabel(const QString &lyric)
{
QLabel *labels[] = {
ui->label, ui->label_2, ui->label_3, ui->label_4, ui->label_5, ui->label_6, ui->label_7
};
for (int i = 0; i < 7; ++i)
{
labels[i]->setText("");
labels[i]->setStyleSheet("color: black;");
}
if (count >= 1 && count <= 7)
{
QLabel *label = labels[count - 1];
label->setText(lyric);
label->setStyleSheet("color: red;");
}
}
下一首的槽函数
//下一曲槽函数
void Widget::on_Next_clicked()
{
QListWidgetItem *selectedItem = ui->listWidget->currentItem();
int nextRow = -1; // 初始化为无效值
if (selectedItem != NULL) {
int currentRow = ui->listWidget->row(selectedItem); // 获取当前选中行的行号
nextRow = currentRow + 1; // 获取下一行的行号
}
if (nextRow >= 0 && nextRow < ui->listWidget->count()) {
QListWidgetItem *nextItem = ui->listWidget->item(nextRow);
QString nextContent = nextItem->text(); // 获取下一行的文本内容
//修改歌词变量
QString lrcFileName = nextContent;
lrcFileName.replace(".mp3", ".lrc");
lrc1 = lrcFileName;
filename = "./res/lrc/" + lrc1;
std::string path = "./res/music_mp3/";
std::string command = "loadfile " + path + nextContent.toStdString() + "\n";
write(fifoFd, command.c_str(), command.size());
// 设置下一行为当前行
ui->listWidget->setCurrentRow(nextRow);
} else if (ui->listWidget->count() > 0) {
// 如果下一行不存在,但列表不为空,则自动提取第一行的内容
QListWidgetItem *firstItem = ui->listWidget->item(0);
QString firstContent = firstItem->text(); // 获取第一行的文本内容
//修改歌词变量
QString lrcFileName = firstContent;
lrcFileName.replace(".mp3", ".lrc");
lrc1 = lrcFileName;
filename = "./res/lrc/" + lrc1;
std::string path = "./res/music_mp3/";
std::string command = "loadfile " + path + firstContent.toStdString() + "\n";
write(fifoFd, command.c_str(), command.size());
// 设置第一行为当前行
ui->listWidget->setCurrentRow(0);
} else {
// 处理未选择项的情况和空列表的情况
qDebug() << "未选择任何项或列表为空";
}
clearlabel();
}
上一首的槽函数
void Widget::on_Back_clicked()
{
QListWidgetItem *selectedItem = ui->listWidget->currentItem();
int prevRow = -1; // 初始化为无效值
if (selectedItem != NULL) {
int currentRow = ui->listWidget->row(selectedItem); // 获取当前选中行的行号
prevRow = currentRow - 1; // 获取上一行的行号
}
if (prevRow >= 0) {
QListWidgetItem *prevItem = ui->listWidget->item(prevRow);
QString prevContent = prevItem->text(); // 获取上一行的文本内容
//修改歌词变量
QString lrcFileName = prevContent;
lrcFileName.replace(".mp3", ".lrc");
lrc1 = lrcFileName;
filename = "./res/lrc/" + lrc1;
std::string path = "./res/music_mp3/";
std::string command = "loadfile " + path + prevContent.toStdString() + "\n";
write(fifoFd, command.c_str(), command.size());
// 设置上一行为当前行
ui->listWidget->setCurrentRow(prevRow);
} else if (ui->listWidget->count() > 0) {
// 如果上一行不存在,但列表不为空,则自动跳转到最后一行
int lastRow = ui->listWidget->count() - 1;
QListWidgetItem *lastItem = ui->listWidget->item(lastRow);
QString lastContent = lastItem->text(); // 获取最后一行的文本内容
//修改歌词变量
QString lrcFileName = lastContent;
lrcFileName.replace(".mp3", ".lrc");
lrc1 = lrcFileName;
filename = "./res/lrc/" + lrc1;
std::string path = "./res/music_mp3/";
std::string command = "loadfile " + path + lastContent.toStdString() + "\n";
write(fifoFd, command.c_str(), command.size());
// 设置最后一行为当前行
ui->listWidget->setCurrentRow(lastRow);
} else {
// 处理未选择项的情况和空列表的情况
qDebug() << "未选择任何项或列表为空";
}
clearlabel();
}
还有一些零碎的功能比如音量调节什么的网上都可以找到相应的代码,把他写入管道里面就可以了,那些功能在下面的总代码中会体现出来
音乐播放器代码总代码:
widget.cpp
#include "widget.h"
#include "ui_widget.h"
#include <QFileDialog>
#include <QTimer>
#include <QFileInfo>
#include <QDebug>
#include <QTextStream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <dirent.h>
int mplayerPID;
bool playing = true;
int fifoFd;
int count = 0;
int totaltime = 0;
int perTime = 0;
QString filename;
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
this->setFixedSize(1024, 600);
// 设置背景图片
QPixmap backgroundImage("./res/button_style/2.jpg");
backgroundImage = backgroundImage.scaled(this->size(), Qt::IgnoreAspectRatio);
QPalette palette = this->palette();
palette.setBrush(QPalette::Window, QBrush(backgroundImage));
this->setPalette(palette);
// 初始化mplayer()创建无名管道有名管道
mplayer_init();
// 创建一个发送指令的线程
pthread_t sendTid;
pthread_create(&sendTid, NULL, sendCmdFunc, this);
pthread_detach(sendTid);
// 创建一个接收mplayer应答的线程
pthread_t recvTid;
pthread_create(&recvTid, NULL, recvFromMplayer, this);
pthread_detach(recvTid);
// 连接信号槽
connect(this, SIGNAL(percentPosSignal(int)), ui->musicPosSlider, SLOT(setValue(int)));
connect(this, SIGNAL(percentTimeSignal(QString)), ui->allTime, SLOT(setText(QString)));
connect(this, SIGNAL(percentAllTimeSignal(QString)), ui->Alltime1, SLOT(setText(QString)));
timer = new QTimer(this);
timer->setInterval(1002);
connect(timer, SIGNAL(timeout()), this, SLOT(updateLyrics()));
timer->start();
}
Widget::~Widget()
{
delete ui;
kill(-getpid(), 2);
delete timer;
}
void Widget::updateLyrics()
{
QMap<QString, QString> lyricsMap = parseLyricsFile(filename);
QString targetTime = ui->allTime->text().left(5);
for (auto it = lyricsMap.constBegin(); it != lyricsMap.constEnd(); ++it)
{
QString timeTag = it.key().left(5);
if (timeTag == targetTime)
{
if (it.value().isNull()) {
break;
}
count++;
updateLabel(it.value());
break;
}
}
}
void Widget::updateLabel(const QString &lyric)
{
QLabel *labels[] = {
ui->label, ui->label_2, ui->label_3, ui->label_4, ui->label_5, ui->label_6, ui->label_7
};
for (int i = 0; i < 7; ++i)
{
labels[i]->setText("");
labels[i]->setStyleSheet("color: black;");
}
if (count >= 1 && count <= 7)
{
QLabel *label = labels[count - 1];
label->setText(lyric);
label->setStyleSheet("color: red;");
}
}
// 其他函数不变...
//获取MP3路径
std::vector<std::string> ScanMP3Files(const std::string& directory) {
std::vector<std::string> mp3Files;
DIR* dir = opendir(directory.c_str());
if (dir == NULL) {
perror("打开目录失败");
return mp3Files;
}
struct dirent* entry;
while ((entry = readdir(dir)) != NULL) {
if (entry->d_type == DT_REG) {
std::string filename = entry->d_name;
if (filename.size() >= 4 && filename.substr(filename.size() - 4) == ".mp3") {
mp3Files.push_back(directory + "/" + filename);
}
}
}
closedir(dir);
return mp3Files;
}
void Widget::mplayer_init()
{
//创建无名管道
pipe(fd);
// 创建有名管道
mkfifo("fifo_cmd",0666);
// 创建父子进程(子进程启动mplayee)
pid_t pid =fork();
if(pid ==0)
{
//关闭文件描述符0
// ::close(0);
//将文件描述符1 定向到fd[1] (无名管道的写端)
dup2(fd[1],1);
//int count=Widget::Ifmp3();
// 启动mplayer
std::vector<std::string> mp3Files = ScanMP3Files("./res/music_mp3/");
execlp("mplayer","mplayer","-ac","mad","-slave","-quiet",\
"-idle","-input","file=./fifo_cmd",NULL);
}
mplayerPID=pid;
// 打开有名管道
fifoFd = open("fifo_cmd",O_WRONLY );
if(fifoFd < 0)
{
qDebug()<<"打开有名管道失败"<< endl;
_exit(-1);
}
}
void *sendCmdFunc(void *arg)
{
Widget *p= (Widget *)arg;
while(1)
{
//发送获取进度的指令
write(p->fifoFd,"get_percent_pos\n",strlen("get_percent_pos\n"));
// usleep 微秒
usleep(500*100);
//发送获取当前时间的指令 将命令写到 有名管道里面
write(p->fifoFd,"get_time_pos\n",strlen("get_time_pos\n"));
//发送获取当前时间长度的请求
write(p->fifoFd,"get_time_length\n",strlen("get_time_length\n"));
usleep(500*10);
//发送获取的歌曲名
write(p->fifoFd,"get_file_name\n",strlen("get_file_name\n"));
usleep(500*10);
}
}
void *recvFromMplayer(void *arg)
{
Widget *p= (Widget *)arg;
while(1)
{
char buf[128];
int len = read(p->fd[0],buf,sizeof(buf));
if(len>0)
{
// 对buf的内容做出判断
if(strncmp(buf,"ANS_PERCENT_POSITION",strlen("ANS_PERCENT_POSITION"))==0)
{
//收到的是进度信息
int perPos=0;
//count++;
sscanf(buf,"ANS_PERCENT_POSITION=%d", &perPos);
emit p->percentPosSignal(perPos);
}
else if ((strncmp(buf,"ANS_TIME_POSITION",strlen("ANS_TIME_POSITION"))==0)||(strncmp(buf,"ANS_LENGTH",strlen("ANS_LENGTH"))==0))
{
if(strncmp(buf,"ANS_TIME_POSITION",strlen("ANS_TIME_POSITION"))==0)
{
//收到的是shijian信息
perTime=0;
sscanf(buf,"ANS_TIME_POSITION=%d", &perTime);
char tmp[32];
sprintf(tmp,"%02d:%02d",perTime/60,perTime%60);
emit p->percentTimeSignal(QString(tmp));
}
else if(strncmp(buf,"ANS_LENGTH",strlen("ANS_LENGTH"))==0)
{
//收到的是zshijian信息
totaltime=0;
sscanf(buf,"ANS_LENGTH=%d", &totaltime);
char tmp[32];
sprintf(tmp,"%02d:%02d",totaltime/60,totaltime%60);
emit p->percentAllTimeSignal(QString(tmp));
}else if(strncmp(buf,"ANS_FILENAME ",strlen("ANS_FILENAME "))==0) {
qDebug("bbbd");
char fileName[128];
qDebug("bbbd");
sscanf(buf, "ANS_FILENAME=%127s", fileName);
std::string fileNameStr(fileName);
qDebug() << "提取到的文件名:" << fileNameStr.c_str();
}
}
}
}
}
//扫描当前文件是否有MP3文件有的话加进去
void Widget::on_FlushPushbutton_clicked()
{
// 清空列表
ui->listWidget->clear();
// 1. 打开目录
DIR *dir = opendir("./res/music_mp3/");
if (dir == NULL)
{
perror("打开目录失败\n");
return;
}
//printf("打开成功\n");
// 2. 从目录句柄 dir 逐个读取文件并判断是否为 MP3 文件
while (1)
{
struct dirent *entry = readdir(dir);
if (entry == NULL) // 扫描完成
break;
if ((entry->d_type & DT_REG) == DT_REG)
{
// 获取文件名
QString filename = QString::fromUtf8(entry->d_name);
// 检查文件扩展名是否为 ".mp3",不区分大小写
if (filename.toLower().endsWith(".mp3"))
{
// 如果是 MP3 文件,添加到 listWidget
ui->listWidget->addItem(filename);
}
}
}
closedir(dir);
}
//刷新槽函数
void Widget::on_AddButton_clicked()
{
const QString filePath = QFileDialog::getOpenFileName(this, "选择音乐",
"./res/music_mp3/","MP3 文件 (*.mp3)");
if (!filePath.isEmpty())
{
QString fileName = QFileInfo(filePath).fileName();
ui->listWidget->addItem(fileName);
}
}
//槽函数
void Widget::Widget::on_pushButton_clicked(){
char speedCommand[128] = "speed_set 0.8\n";
write(fifoFd, speedCommand, strlen(speedCommand));
}
//下一曲槽函数
void Widget::on_Next_clicked()
{
QListWidgetItem *selectedItem = ui->listWidget->currentItem();
int nextRow = -1; // 初始化为无效值
if (selectedItem != NULL) {
int currentRow = ui->listWidget->row(selectedItem); // 获取当前选中行的行号
nextRow = currentRow + 1; // 获取下一行的行号
}
if (nextRow >= 0 && nextRow < ui->listWidget->count()) {
QListWidgetItem *nextItem = ui->listWidget->item(nextRow);
QString nextContent = nextItem->text(); // 获取下一行的文本内容
//修改歌词变量
QString lrcFileName = nextContent;
lrcFileName.replace(".mp3", ".lrc");
lrc1 = lrcFileName;
filename = "./res/lrc/" + lrc1;
std::string path = "./res/music_mp3/";
std::string command = "loadfile " + path + nextContent.toStdString() + "\n";
write(fifoFd, command.c_str(), command.size());
// 设置下一行为当前行
ui->listWidget->setCurrentRow(nextRow);
} else if (ui->listWidget->count() > 0) {
// 如果下一行不存在,但列表不为空,则自动提取第一行的内容
QListWidgetItem *firstItem = ui->listWidget->item(0);
QString firstContent = firstItem->text(); // 获取第一行的文本内容
//修改歌词变量
QString lrcFileName = firstContent;
lrcFileName.replace(".mp3", ".lrc");
lrc1 = lrcFileName;
filename = "./res/lrc/" + lrc1;
std::string path = "./res/music_mp3/";
std::string command = "loadfile " + path + firstContent.toStdString() + "\n";
write(fifoFd, command.c_str(), command.size());
// 设置第一行为当前行
ui->listWidget->setCurrentRow(0);
} else {
// 处理未选择项的情况和空列表的情况
qDebug() << "未选择任何项或列表为空";
}
clearlabel();
}
//shang一曲槽函数
void Widget::on_listWidget_doubleClicked(const QModelIndex &index)
{
QListWidgetItem *selectedItem = ui->listWidget->currentItem();
if (selectedItem != NULL) {
QString mp3FileName = selectedItem->text(); // 获取当前选中的 mp3 文件名
std::string path = "./res/music_mp3/";
// 构造对应的 lrc 文件名,假设 mp3 和 lrc 文件名一致,只是扩展名不同
QString lrcFileName = mp3FileName;
lrcFileName.replace(".mp3", ".lrc");
// 更新类成员变量 lrc1
lrc1 = lrcFileName;
std::string command = "loadfile " + path + mp3FileName.toStdString() + "\n";
// 更新 filename 变量
filename = "./res/lrc/" + lrc1;
write(fifoFd, command.c_str(), command.size());
} else {
// 处理未选择项的情况
qDebug() << "未选择任何项";
}
clearlabel();
}
void Widget::on_Back_clicked()
{
QListWidgetItem *selectedItem = ui->listWidget->currentItem();
int prevRow = -1; // 初始化为无效值
if (selectedItem != NULL) {
int currentRow = ui->listWidget->row(selectedItem); // 获取当前选中行的行号
prevRow = currentRow - 1; // 获取上一行的行号
}
if (prevRow >= 0) {
QListWidgetItem *prevItem = ui->listWidget->item(prevRow);
QString prevContent = prevItem->text(); // 获取上一行的文本内容
//修改歌词变量
QString lrcFileName = prevContent;
lrcFileName.replace(".mp3", ".lrc");
lrc1 = lrcFileName;
filename = "./res/lrc/" + lrc1;
std::string path = "./res/music_mp3/";
std::string command = "loadfile " + path + prevContent.toStdString() + "\n";
write(fifoFd, command.c_str(), command.size());
// 设置上一行为当前行
ui->listWidget->setCurrentRow(prevRow);
} else if (ui->listWidget->count() > 0) {
// 如果上一行不存在,但列表不为空,则自动跳转到最后一行
int lastRow = ui->listWidget->count() - 1;
QListWidgetItem *lastItem = ui->listWidget->item(lastRow);
QString lastContent = lastItem->text(); // 获取最后一行的文本内容
//修改歌词变量
QString lrcFileName = lastContent;
lrcFileName.replace(".mp3", ".lrc");
lrc1 = lrcFileName;
filename = "./res/lrc/" + lrc1;
std::string path = "./res/music_mp3/";
std::string command = "loadfile " + path + lastContent.toStdString() + "\n";
write(fifoFd, command.c_str(), command.size());
// 设置最后一行为当前行
ui->listWidget->setCurrentRow(lastRow);
} else {
// 处理未选择项的情况和空列表的情况
qDebug() << "未选择任何项或列表为空";
}
clearlabel();
}
void Widget::on_Pause_clicked()
{
if(playing){
playing=false;
kill(mplayerPID,SIGSTOP);
}
else{
playing=true;
kill(mplayerPID,SIGCONT);
}
}
QMap<QString, QString> parseLyricsFile(const QString &filename)
{
QMap<QString, QString> lyricsMap;
QFile file(filename);
if (!file.open(QIODevice::ReadOnly | QIODevice::Text))
{
qDebug() << "无法打开文件";
return lyricsMap;
}
QTextStream in(&file);
in.setCodec("UTF-8");
QString currentLyric = "";
while (!in.atEnd())
{
QString line = in.readLine();
// 假设时间标签的格式为 "[00:02.00]"
if (line.startsWith("[") && line.contains("]"))
{
QString timeTag = line.mid(1, line.indexOf("]") - 1); // 提取时间标签
currentLyric = line.mid(line.indexOf("]") + 1).trimmed(); // 提取歌词内容
lyricsMap[timeTag] = currentLyric;
}
else
{
currentLyric += "\n" + line.trimmed(); // 合并多行歌词内容
if (!currentLyric.isEmpty())
{
lyricsMap[""] = currentLyric; // 使用空字符串作为键存储多行歌词
}
}
}
file.close();
return lyricsMap;
}
void Widget::on_musicPosSlider_sliderMoved(int position)
{
//当前时间=总时间*(进度/总进度)
perTime=(int)(totaltime*1.0*(position/100.0));
if(perTime>totaltime)
return;
}
// 在滑块释放时不立即继续播放,而是等待用户再次手动控制播放(例如点击"播放"按钮)
void Widget::on_musicPosSlider_sliderReleased()
{
char cmd[128] = "";
sprintf(cmd, "seek %d 2\n", perTime);
write(fifoFd, cmd, strlen(cmd));
clearlabel();
}
void Widget::clearlabel()
{
ui->label->clear();
ui->label_2->clear();
ui->label_3->clear();
ui->label_4->clear();
ui->label_5->clear();
ui->label_6->clear();
ui->label_7->clear();
count=0;
}
void Widget::on_BackTimePushButton_clicked()
{
write(fifoFd,"seek -10 0\n",strlen("seek -10 0\n"));
clearlabel();
}
void Widget::on_NextTimePushButton_clicked()
{
write(fifoFd,"seek +10 0\n",strlen("seek +10 0\n"));
clearlabel();
}
void Widget::on_horizontalSlider_valueChanged(int value)
{
QString volume_command="volume ";
volume_command.append(QString::number(value));
volume_command.append(" 1\n");
write(fifoFd,volume_command.toUtf8(),volume_command.size());
}
void Widget::on_pushButton_2_clicked()
{
char speedCommand[128] = "speed_set 2.0\n";
write(fifoFd, speedCommand, strlen(speedCommand));
}
widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <pthread.h>
#include <QDebug>
#include <fcntl.h>
#include <string.h>
#include <QSlider>
#include <signal.h>
#include <QString>
#include <stdio.h>
#include <dirent.h>
#include<QFileInfo>
#include<QFileDialog>
#include <vector>
#include <QMouseEvent>
#include <QFile>
#include <QTextStream>
#include <QMap>
#include <QTimer>
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
friend void *recvFromMplayer(void *arg);
friend void *sendCmdFunc(void *arg);
public:
explicit Widget(QWidget *parent = 0);
//将歌词存到map表中
void InTime();
//将歌词显示出来
void ShowLrc();
//歌词的存入
~Widget();
// mplayer的初始化
void mplayer_init(void);
//判断当前有多少MP3
int Ifmp3();
//void highlightCurrentLyrics();
void clearlabel();
private:
Ui::Widget *ui;
//clock
QTimer *timer;
// 创建无名管道
int fd[2];
//创建有名管道
int fifoFd;
signals:
//关于时间和进度条的3信号
void percentPosSignal(int value);
void percentTimeSignal(QString);
void percentAllTimeSignal(QString);
private slots:
void on_FlushPushbutton_clicked();
void on_pushButton_clicked();
void on_AddButton_clicked();
void on_Next_clicked();
void on_listWidget_doubleClicked(const QModelIndex &index);
void on_Back_clicked();
void on_Pause_clicked();
void updateLyrics();
void on_musicPosSlider_sliderPressed();
void on_musicPosSlider_sliderReleased();
void on_musicPosSlider_sliderMoved(int position);
void on_BackTimePushButton_clicked();
void on_NextTimePushButton_clicked();
void on_horizontalSlider_valueChanged(int value);
void on_comboBox_currentIndexChanged(const QString &arg1);
void on_pushButton_2_clicked();
};
#endif // WIDGET_H