Qt通讯录(语音输入+发邮件+发短信+头像)

    技术2022-07-21  90

    Qt通讯录(语音输入+发邮件+发短信+头像)

    项目背景功能运行程序关键技术语音识别ASR发送电子邮件 AddressBook5.0Gitee地址

    项目背景

    作为一个Qt小白,迫于大一下学期的C++大作业的需要,我看了两天的黑马程序员Qt视频教程,学有小成,便操刀上阵了。捣鼓了一个星期,写了3000多行代码,终于实现得差不多了。期间历程坎坷,感受颇多,因此在这里写下我的第一篇博客。

    功能

    1.新建/编辑/删除联系人; 2.按姓名/出生日期对联系人进行排序; 3.按照关键字来查找联系人; 4.给联系人打电话(形式上); 5.语音识别输入; 6.给联系人发邮件并自动生成贺信; 7.修改登录密码或其他用户信息; 8.登录账号密码保护和账号注销。

    运行程序

    bilibili网址

    Qt个人通讯录介绍

    关键技术

    由于代码比较多且复杂,我就不一一贴出来,下面展示关键技术,代码下载链接在文末。

    语音识别ASR

    asr.h

    //功能:启动录音设备,对数据编码上传网络,调用百度语音识别ASR,获取识别文本 #ifndef ASR_H #define ASR_H #include <QMainWindow> #include <QtNetwork> #include <QJsonDocument> #include <QJsonParseError> #include <QDebug> #include<QAudioInput> #include<QMediaPlayer> #include<QMessageBox> #include<QTime> #include<QSettings> #include<QKeyEvent> #include<QString> #include<QUrl> class ASR : public QMainWindow { Q_OBJECT public: int isStop=1; //填写API_KEY和Secret_Key const QString API_KEY=""; const QString Secret_Key=""; QString Access_Token=""; //vedio to text const QString VOP_URL="http://vop.baidu.com/server_api"; //Text to vedio const QString TSN_URL="http://tsn.baidu.com/text2audio"; QString Get_Token_URL="https://aip.baidubce.com/oauth/2.0/token"; QString MAC_cuid=""; //语音识别的结果 QString voice_get_ans; explicit ASR(QWidget *parent = 0); ~ASR(); QAudioDeviceInfo SpeechCurrentDevice; QAudioInput* audio_input=NULL; QBuffer* JsonBuffer=NULL; void AudioInit(); void TokenInit(); void getMacAddress(); //更改voice_get_ans void changeBaiduAudioAns(QString str); bool JudgeTokenTime(); void startASR(); void stopASR(); signals: void outputASR(QString ASRtext); public slots: //识别声音获得文字 void Baidu_VoiceToText_replyFinish(QNetworkReply* reply); void get_Token_slot(QNetworkReply* reply); }; #endif // ASR_H

    asr.cpp

    #include "asr.h" ASR::ASR(QWidget *parent) : QMainWindow(parent) { AudioInit(); getMacAddress(); TokenInit(); } //录音设备初始化 void ASR::AudioInit() { const auto &&availableDevices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); if(!availableDevices.isEmpty()) { SpeechCurrentDevice = availableDevices.first(); QAudioFormat format; format.setSampleRate(8000); format.setChannelCount(1); format.setSampleSize(16); format.setSampleType(QAudioFormat::SignedInt); format.setByteOrder(QAudioFormat::LittleEndian); format.setCodec("audio/pcm"); audio_input = new QAudioInput(SpeechCurrentDevice, format, this); } } //百度语音识别令牌初始化 void ASR::TokenInit() { if(JudgeTokenTime()) return; QUrl url=QUrl(Get_Token_URL+ "?grant_type=client_credentials"+ "&client_id="+API_KEY+ "&client_secret="+Secret_Key); QNetworkAccessManager *manager=new QNetworkAccessManager(this); QNetworkRequest request(url); connect(manager,SIGNAL(finished(QNetworkReply *)),this,SLOT(get_Token_slot(QNetworkReply *))); manager->get(request); } //若成功得到令牌则响应 void ASR::get_Token_slot(QNetworkReply *reply) { QByteArray JsonStr=reply->readAll(); QJsonObject acceptedData(QJsonDocument::fromJson(JsonStr).object()); if(acceptedData.contains("access_token")) { QJsonValue json_value = acceptedData.take("access_token"); Access_Token=json_value.toString(); qDebug()<<"success get token:"<<Access_Token; } } ASR::~ASR() { } //更改voice_get_ans值的函数,用于发射信号 void ASR::changeBaiduAudioAns(QString str) { //去掉末尾的句号并更新voice_get_ans this->voice_get_ans=str.remove(str.length()-1,1); qDebug()<<"识别声音结果 "<<this->voice_get_ans; //发射信号告知已成功识别语音,并传递文本voice_get_ans emit this->outputASR(voice_get_ans); } //判断令牌是否失效,每次生成的令牌使用期为一个月,过期后需再次申请 bool ASR::JudgeTokenTime() { QSettings *setting=new QSettings("GetTokenTime.ini",QSettings::IniFormat); //当前时间 QDateTime current_date_time=QDateTime::currentDateTime(); QDateTime file_date_time; //读取的文件时间 QString file_date_str=setting->value("/TIME/last_time").toString(); file_date_time=QDateTime::fromString(file_date_str,"yyyy-MM-dd"); // qDebug()<<"file_date_time"<<file_date_time; uint stime = file_date_time.toTime_t(); uint etime = current_date_time.toTime_t(); int ndaysec = 24*60*60; int Day= (etime - stime)/(ndaysec) + ((etime - stime)%(ndaysec)+(ndaysec-1))/(ndaysec) - 1; //Token 有效期内 if(Day<30) return true; else { //写时间 setting->beginGroup("TIME"); setting->setValue("last_time",current_date_time.toString("yyyy-MM-dd")); setting->endGroup(); return false; } } //获取本机MAC地址 void ASR::getMacAddress() { QList<QNetworkInterface> nets = QNetworkInterface::allInterfaces();// 获取所有网络接口列表 int nCnt = nets.count(); for(int i = 0; i < nCnt; i ++) { // 如果此网络接口被激活并且正在运行并且不是回环地址,则就是我们需要找的Mac地址 if(nets[i].flags().testFlag(QNetworkInterface::IsUp) && nets[i].flags().testFlag(QNetworkInterface::IsRunning) && !nets[i].flags().testFlag(QNetworkInterface::IsLoopBack)) { MAC_cuid = nets[i].hardwareAddress(); break; } } MAC_cuid.replace(":","-"); qDebug()<<"自动获取的MAC地址 "<<MAC_cuid; } //声音转成文本 void ASR::Baidu_VoiceToText_replyFinish(QNetworkReply *reply) { qDebug()<<"进到槽函数了"; QByteArray JsonStr=reply->readAll(); qDebug()<<"返回信息 "<<JsonStr; QJsonObject acceptedData(QJsonDocument::fromJson(JsonStr).object()); if(acceptedData.contains("err_no")) { QJsonValue version_value = acceptedData.take("err_no"); if(version_value.isDouble()) { int version = version_value.toVariant().toInt(); if(version == 3300){ // textEdit -> append("错误代码:3300-输入参数不正确,请仔细核对文档及参照demo,核对输入参数");//重新写入文本 }else if(version == 3301){ // textEdit -> append("错误代码:3301-音频质量过差,请上传清晰的音频");//重新写入文本 QMessageBox::information(NULL,tr("识别失败"),tr("请等待一秒后开始录音,确认录音质量良好")); }else if(version == 3302){ // textEdit -> append("错误代码:3302-鉴权失败,token字段校验失败。请用appkey 和 app secret生成");//重新写入文本 }else if(version == 3303){ // textEdit -> append("错误代码:3303-转pcm失败,如下2个原因 1. wav amr音频转码失败,即音频有问题。2. 服务端问题,请将api返回结果反馈至论坛或者QQ群");//重新写入文本 }else if(version == 3304){ // textEdit -> append("错误代码:3304-用户的请求QPS超限,请降低识别api请求频率 (qps以appId计算,移动端如果共用则累计)");//重新写入文本 }else if(version == 3305){ // textEdit -> append("错误代码:3305-用户的日pv(日请求量)超限,请“申请提高配额”,如果暂未通过,请降低日请求量");//重新写入文本 }else if(version == 3307){ // textEdit -> append("错误代码:3307-语音服务器后端识别出错问题,请将api返回结果反馈至论坛或者QQ群");//重新写入文本 }else if(version == 3308){ // textEdit -> append("错误代码:3308-音频过长,音频时长不超过60s,请将音频时长截取为60s以下");//重新写入文本 }else if(version == 3309){ // textEdit -> append("错误代码:3309-音频数据问题,服务端无法将音频转为pcm格式,可能是长度问题,音频格式问题等。 请将输入的音频时长截取为60s以下,并核对下音频的编码,是否是8K或者16K, 16bits,单声道。");//重新写入文本 }else if(version == 3310){ // textEdit -> append("错误代码:3310-输入的音频文件过大,语音文件共有3种输入方式: json 里的speech 参数(base64后); 直接post 二进制数据,及callback参数里url。 分别对应三种情况:json超过10M;直接post的语音文件超过10M;callback里回调url的音频文件超过10M");//重新写入文本 }else if(version == 3311){ // textEdit -> append("错误代码:3311-采样率rate参数不在选项里,目前rate参数仅提供8000,16000两种,填写4000即会有此错误");//重新写入文本 }else if(version == 3312){ // textEdit -> append("错误代码:3312-音频格式format参数不在选项里,目前格式仅仅支持pcm,wav或amr,如填写mp3即会有此错误");//重新写入文本 } // return; } } if(!acceptedData["result"].isNull()) { QString message=acceptedData["result"].toArray()[0].toString(); qDebug()<<message; changeBaiduAudioAns(message); } } //开始语音识别 void ASR::startASR() { JsonBuffer=new QBuffer; JsonBuffer->open(QIODevice::ReadWrite); qDebug()<<"录音设备访问成功..."; audio_input->start(JsonBuffer); qDebug()<<"录音设备运行成功..."; isStop=0; } //结束语音识别 void ASR::stopASR() { qDebug()<<"结束录音"; audio_input->stop(); const auto &sendData=JsonBuffer->data(); JsonBuffer->deleteLater(); //必须加this QNetworkRequest request(QUrl(this->VOP_URL)); request.setRawHeader("Content-Type", "application/json"); qDebug()<<"jason_speech:request设置头成功 ..."; QNetworkAccessManager *manager=new QNetworkAccessManager(this); connect(manager,SIGNAL(finished(QNetworkReply *)),this,SLOT(Baidu_VoiceToText_replyFinish(QNetworkReply *))); //这里应该放在百度类里面 QJsonObject append; //设置json请求格式 append["format"] = "pcm"; append["rate"] = 8000; append["channel"] = 1; append["token"] = Access_Token; append["lan"] = "zh"; append["cuid"] = MAC_cuid; append["speech"] = QString(sendData.toBase64()); append["len"] = sendData.size(); //发送HTTP post请求 manager->post(request,QJsonDocument(append).toJson()); isStop=1; }

    调用方式

    //在mainwindow.h中声明一个ASR对象 ASR *asrMainWindow=new ASR; //在mainwindow.cpp中提供键盘按键事件函数,F2:语音输入 void MainWindow::keyPressEvent(QKeyEvent *event) { if(event->key()==Qt::Key_F2) { if(asrMainWindow->isStop==1) { asrMainWindow->startASR(); } else { asrMainWindow->stopASR(); } } } //信号和槽 connect(asrMainWindow,&ASR::outputASR,[=](QString ASRtext){ qDebug()<<ASRtext; });

    发送电子邮件

    发送电子邮件的功能由sendemail文件夹里的28个文件实现。 调用方法 这里实现的是163和qq邮箱的发送,其它大部分邮箱同样也能实现

    //功能:其UI文件显示发送邮件界面,并显示进度条,用户在进度条达到100%之前可以取消发送邮件,否则会弹出邮箱SMTP服务授权码输入窗口,若授权码正确即可发送邮件 //核心代码为sendmail包,可实现发邮件功能(简单文本,富文本,附件,图片等),也可以群发邮件 //判断发件人邮箱为那种类型的邮箱 //若为163邮箱则设置服务器为smtp.163.com,端口为25,通讯协议为TcpConnection;若为qq邮箱则设置服务器为smtp.qq.com,端口为465,通讯协议为SslConnection; //由于此应用为通讯录管理系统,所以我只是用它实现了发送简单文本给一位联系人的功能。 //发送成功后会显示当前邮件的发送时间yyyy.mm.dd H:M:S dddd年月日 时分秒 星期几 //需输入邮箱SMTP服务授权码(不是邮箱密码),方能在第三方客户端发送邮件 //开通QQ邮箱的SMTP服务教程:https://jingyan.baidu.com/article/5225f26b017159e6fa0908ff.html //实例化发送邮件对象 SmtpClient smtp("smtp.163.com",25,SmtpClient::TcpConnection); if(senderEmail.contains("@163.com")) { qDebug()<<"邮件类型1"; } if(senderEmail.contains("@qq.com")) { qDebug()<<"邮件类型2"; smtp.host="smtp.qq.com"; smtp.port=465; smtp.setConnectionType(SmtpClient::SslConnection); } smtp.setUser(senderEmail); QFile file("EmailPassword.txt"); file.open(QFile::ReadOnly); QString str=file.readLine(); file.close(); smtp.setPassword(str); //构建邮件主题,包含发件人收件人附件等. MimeMessage message; message.setSender(new EmailAddress(senderEmail)); //逐个添加收件人 message.addRecipient(new EmailAddress(receiverEmail)); //构建邮件标题 message.setSubject(QStringLiteral("你的一位联系人发来的信息")); //构建邮件正文 MimeText text; text.setText(emailMessage); message.addPart(&text); if (!smtp.connectToHost()){ ui->label_2->setText("发送失败"); QMessageBox::critical(this,"错误","服务器连接失败!"); return; } if (!smtp.login()){ ui->label_2->setText("发送失败"); QMessageBox::critical(this,"错误","用户登录失败!"); return; } if (!smtp.sendMail(message)){ ui->label_2->setText("发送失败"); QMessageBox::critical(this,"错误","邮件发送失败!"); return; }else{ QMessageBox::information(this,"提示","邮件发送成功!"); } smtp.quit();

    其它调用方法

    //粗略步骤: //第一步:布局好界面,控件命名好,建议用pascal命名法。 //第二步:准备好sendemailapi文件夹,里面存放的公开的第三方的支持发送附件和富文本的MIME协议文件。添加到项目中。 //第三步:实例化邮件发送对象 SmtpClient smtp(ui->cboxServer->currentText(), ui->cboxPort->currentText().toInt(), ui->ckSSL->isChecked()?SmtpClient::SslConnection:SmtpClient::TcpConnection); smtp.setUser(ui->txtSender->text()); smtp.setPassword(ui->txtSenderPwd->text()); //第四步:构建邮件主题,包含发件人收件人附件等 MimeMessage message; message.setSender(new EmailAddress(ui->txtSenderAddr->text())); //逐个添加收件人 QStringList receiver = ui->txtReceiverAddr->text().split(';'); for (int i = 0; i < receiver.size(); i++){ message.addRecipient(new EmailAddress(receiver.at(i))); } //构建邮件标题 message.setSubject(ui->txtTitle->text()); //构建邮件正文 MimeHtml text; text.setHtml(ui->txtContent->toHtml()); // //如果直接发送简单文本,使用以下方法 // MimeText text; // text.setText("测试简单文本发送!"); message.addPart(&text); //构建附件 QString atta=ui->txtAtta->text(); if (atta!=""){ QStringList attas=atta.split(";"); foreach (QString tempAtta, attas) { QFile *file=new QFile(tempAtta); if (file->exists()){ message.addPart(new MimeAttachment(file)); } } } //第五步:登录服务器发送邮件 if (!smtp.connectToHost()){ QMessageBox::critical(this,"错误","服务器连接失败!"); return; } if (!smtp.login()){ QMessageBox::critical(this,"错误","用户登录失败!"); return; } if (!smtp.sendMail(message)){ QMessageBox::critical(this,"错误","邮件发送失败!"); return; }else{ QMessageBox::information(this,"错误","邮件发送成功!"); } smtp.quit();

    AddressBook5.0

    更新时间:2020.7.12 新增加了发短信和设置头像的功能,并更改为英文版软件。 软件展示视频地址:https://www.bilibili.com/video/BV1Ct4y1U7jw/

    Gitee地址

    2021-1-25 09:40:27 Gitee地址

    Processed: 0.009, SQL: 9