前言:
博主说:在学习和使用Qt框架制作ui的过程中,渐渐发现自己可以搞一个智能机器人的ui界面用于聊天和前端控制底层的Ros框架,于是就开始了断断续续长达5day的项目开发周期。
技术栈:在这个项目里,首先就是要用到Qt的设计框架,用到了一个QListWidget类作为聊天窗口,一个QlineEdit作为聊天输入框,还有一些QPushButton作为控制交互单元,一些QLabel作为显示单元,当然还有布局器等等...最后就是用到http请求和js解析。
项目流程:语音录入->语音识别转文字->文字内容上传得到Ai回答->Ai回答内容转语音
项目实现:
1、 http类包:
说明:
由于Qt没有自带的一个比较称心如意的类来做http请求,所以我们要封装一个基于QNetworkAccessManager类的post方法的一个类用于发送和接收网络请求。
实现:
1.http.h文件(需要在.pro文件中加核心 multimedia multimediawidgets network)
#ifndef HTTP_H #define HTTP_H #include #include #include #include #include #include #include class Http : public QObject { Q_OBJECT public: explicit Http(QObject *parent = nullptr); bool post_sync(QString Url,QMapheader,QByteArray requesdata,QByteArray&replydata); signals: public slots: }; #endif // HTTP_H
2.http.cpp文件
#include "http.h" Http::Http(QObject *parent) : QObject(parent) { } bool Http::post_sync(QString Url, QMap header, QByteArray requesdata, QByteArray &replydata) { //发送请求的对象 QNetworkAccessManager manager; //请求对象 QNetworkRequest request; request.setUrl(Url); QMapIteratorit(header); while(it.hasNext()) { it.next(); request.setRawHeader(it.key().toLatin1(),it.value().toLatin1()); } QNetworkReply *reply=manager.post(request,requesdata); QEventLoop loop; connect(reply,&QNetworkReply::finished,&loop,&QEventLoop::quit); loop.exec();//reply 发出信号 结束循环 if(reply!=NULL&&reply->error()==QNetworkReply::NoError) { replydata=reply->readAll(); return true; } else { QMessageBox::warning(NULL,"网络请求","请求失败"); return false; } }
2、audio类包:
说明:
利用自带的QAudioInput类封装一个直接将音频转.pcm音频文件的类,用于后续语言识别传内容用。
实现:
1.audio.h文件
#ifndef AUDIO_H #define AUDIO_H #include #include #include #include #include #include #include class Audio : public QObject { Q_OBJECT public: explicit Audio(QObject *parent = nullptr); void startAudio(QString filename); void stopAudio(); signals: public slots: private: QAudioInput * m_audio; QFile *m_file; }; #endif // AUDIO_H
2.audio.cpp文件
#include "audio.h" Audio::Audio(QObject *parent) : QObject(parent) { } void Audio::startAudio(QString filename) { QAudioDeviceInfo device=QAudioDeviceInfo::defaultInputDevice();//获取输入设备地址 if(device.isNull()) { QMessageBox::warning(NULL,"警告","录音设备不存在"); } else { QAudioFormat m_format; //采样频率 m_format.setSampleRate(16000); //设置通道数 m_format.setChannelCount(1); //设置位深 m_format.setSampleSize(16); //设置编码 m_format.setCodec("audio/pcm"); //判断设备是否支持该格式 if(!device.isFormatSupported(m_format)) { m_format=device.nearestFormat(m_format);//用最近格式代替 } //打开文件 m_file=new QFile; m_file->setFileName(filename); m_file->open(QIODevice::WriteOnly); //开始录音 m_audio=new QAudioInput(m_format,this); m_audio->start(m_file); } } void Audio::stopAudio() { m_audio->stop(); m_file->close(); delete m_file; m_file=NULL; }
3、speech类包:
说明:
利于第二步生成的语言文件上传百度语音识别短语音识别标准版API - 语音技术 (baidu.com),转文字。(其中需要在百度网页创建应用生成自己的client_id(API Key)和client_secret(Secret Key) 来获取 Token 值)创建应用:百度智能云-管理中心 (baidu.com)
实现:
1.speech.h文件
#ifndef SPEECH_H #define SPEECH_H #include #include "http.h" #include #include #include #include #include #include #include const QString baiduTokenUrl="https://openapi.baidu.com/oauth/2.0/token?grant_type=client_credentials&client_id=%1&client_secret=%2"; const QString client_id="你的应用API Key"; const QString client_secret="你的应用Secret Key"; //语音识别 const QString baiduSpeechnUrl="https://vop.baidu.com/pro_api?dev_pid=80001&cuid=%1&token=%2"; class Speech : public QObject { Q_OBJECT public: explicit Speech(QObject *parent = nullptr); QString speechIdentify(QString filename); QString getJsonValue(QByteArray ba,QString key); signals: public slots: }; #endif // SPEECH_H
2.speech.cpp文件
#include "speech.h" Speech::Speech(QObject *parent) : QObject(parent) { } #include QString Speech::speechIdentify(QString filename) { QString accesstoken; QString tokenUrl=QString(baiduTokenUrl).arg(client_id).arg(client_secret); QMap header; //头信息 header.insert(QString("Content-Type"),QString("audio/pcm;rate=16000")); QByteArray requwsdata;//发送内容 QByteArray replydata;//返回信息 Http m_http; bool result=m_http.post_sync(tokenUrl,header,requwsdata,replydata); if(result) { QString key="access_token"; accesstoken=getJsonValue(replydata,key); } else { QMessageBox::critical(NULL,"网络错误","获取token数据失败"); return ""; } QString speechUrl=QString(baiduSpeechnUrl).arg(QHostInfo::localHostName()).arg(accesstoken); QFile file; file.setFileName(filename); file.open(QIODevice::ReadOnly); requwsdata=file.readAll(); file.close(); replydata.clear(); result=m_http.post_sync(speechUrl,header,requwsdata,replydata); if(result) { QString key="result"; QString text=getJsonValue(replydata,key); return text; } QMessageBox::critical(NULL,"网络错误","获取识别数据失败"); return ""; } QString Speech::getJsonValue(QByteArray ba, QString key) { QJsonParseError parseerror; QJsonDocument jsondocument=QJsonDocument::fromJson(ba,&parseerror); if(parseerror.error==QJsonParseError::NoError) { if(jsondocument.isObject())//如果是js对象 { QJsonObject jsonobj=jsondocument.object();//转换为对象 if(jsonobj.contains(key))//判断有无key键对应值 { QJsonValue jsonval=jsonobj.value(key);//得到val值 if(jsonval.isString())//是否为字符串 { return jsonval.toString(); } else if(jsonval.isArray()) { QJsonArray arr=jsonval.toArray();//转化为标准json数组 QJsonValue jv=arr.at(0);//取出第0个值 return jv.toString(); } } } } return ""; }
4、answer类包:
说明:
利用上一个类函数转化生成的文字或者自己在文本编辑器里生成的文字上传百度Ai然后得到Ai回答。这里用到的是百度UNIT百度智能对话平台UNIT (baidu.com)平台,不仅要创建应用百度智能云-管理中心 (baidu.com)还需要自己创建机器人和添加功能,要用到自己机器人的编号(ID)。
实现:
1.answer.h文件(这个文件同speech需要该应用的client_id(API Key)和client_secret(Secret Key) 来获取 Token 值))
#ifndef ANSWER_H #define ANSWER_H #include #include "http.h" #include #include #include #include #include #include const QString answerToken="https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=%1&client_secret=%2"; const QString answerclient_id="你的应用API Key"; const QString answerclient_secret="你的应用secret Key"; //ai url const QString baiduAiurl="https://aip.baidubce.com/rpc/2.0/unit/service/v3/chat?access_token=%1"; class Answer : public QObject { Q_OBJECT public: explicit Answer(QObject *parent = nullptr); QString getAnswer(QString question); QString getJsonValue(QByteArray ba, QString key); signals: public slots: }; #endif // ANSWER_H
2.answer.cpp文件(这个文件需要你的机器人id号)
#include "answer.h" Answer::Answer(QObject *parent) : QObject(parent) { } QString Answer::getAnswer(QString question) { //获取token值 QString accesstoken; QString tokenUrl=QString(answerToken).arg(answerclient_id).arg(answerclient_secret); QMap header; //头信息 header.insert(QString("Content-Type"),QString("application/x-www-form-urlencoded")); QByteArray requwsdata;//发送内容 QByteArray replydata;//返回信息 Http m_http; bool result=m_http.post_sync(tokenUrl,header,requwsdata,replydata); if(result) { QString key="access_token"; accesstoken=getJsonValue(replydata,key); } else { QMessageBox::critical(NULL,"网络错误","获取token数据失败"); return ""; } QString aiUrl=QString(baiduAiurl).arg(accesstoken); replydata.clear(); requwsdata =(QString("{\"version\":\"3.0\",\"service_id\":\"你的机器人id号\",\"session_id\":\"\",\"log_id\":\"7758521\",\"request\":{\"terminal_id\":\"88888\",\"query\":\"%1\"}}").arg(question)).toUtf8(); // json格式 转utf8 result=m_http.post_sync(aiUrl,header,requwsdata,replydata); if(result) { QByteArray arr=replydata; QJsonObject obj=QJsonDocument::fromJson(arr).object(); QJsonValue val=obj.value("result"); QJsonValue re_val=val.toObject().value("responses");//找出回复对象 QJsonValue re_val1=re_val[0].toObject().value("actions");//找出回复对象中actions对象 0的置信度最高 QJsonValue re_val2=re_val1[0].toObject().value("say"); return re_val2.toString(); } QMessageBox::critical(NULL,"网络错误","获取识别数据失败"); return ""; } QString Answer::getJsonValue(QByteArray ba, QString key) { QJsonParseError parseerror; QJsonDocument jsondocument=QJsonDocument::fromJson(ba,&parseerror); if(parseerror.error==QJsonParseError::NoError) { if(jsondocument.isObject())//如果是js对象 { QJsonObject jsonobj=jsondocument.object();//转换为对象 if(jsonobj.contains(key))//判断有无key键对应值 { QJsonValue jsonval=jsonobj.value(key);//得到val值 if(jsonval.isString())//是否为字符串 { return jsonval.toString(); } else if(jsonval.isArray()) { QJsonArray arr=jsonval.toArray();//转化为标准json数组 QJsonValue jv=arr.at(0);//取出第0个值 return jv.toString(); } } } } return ""; }
5、sound类包:
说明:
这个类是用于对机器人回答内容进行一个转语言,可以实现机器人说话;这里同样需要创建一个应用在线语音合成_高度拟人的语音合成服务-百度AI开放平台 (baidu.com),选择短文本合成,得到client_id(API Key)和client_secret(Secret Key) 来获取 Token 值)。
实现:
1.sound.h文件
#ifndef SOUND_H #define SOUND_H #include #include "http.h" #include #include #include #include #include #include #include #include const QString soundToken="https://aip.baidubce.com/oauth/2.0/token?grant_type=client_credentials&client_id=%1&client_secret=%2"; const QString soundclient_id="你的应用API Key"; const QString soundclient_secret="你的应用secret Key"; //ai url const QString baiduSoundurl="https://tsn.baidu.com/text2audio?"; const QString SoundFilepPath="2.mp3"; class Sound : public QObject { Q_OBJECT public: explicit Sound(QObject *parent = nullptr); bool getSound(QString text,int spd=6,int per=1); QString getJsonValue(QByteArray ba, QString key); signals: void finsh_speek(); public slots: private: }; #endif // SOUND_H
2.sound.cpp文件
#include "sound.h" #include Sound::Sound(QObject *parent) : QObject(parent) { } bool Sound::getSound(QString text,int spd,int per) { //获取token值 QString accesstoken; QString tokenUrl=QString(soundToken).arg(soundclient_id).arg(soundclient_secret); QMap header; //头信息 header.insert(QString("Content-Type"),QString("application/x-www-form-urlencoded")); QByteArray requwsdata;//发送内容 QByteArray replydata;//返回信息 Http m_http; bool result=m_http.post_sync(tokenUrl,header,requwsdata,replydata); if(result) { QString key="access_token"; accesstoken=getJsonValue(replydata,key); } else { QMessageBox::critical(NULL,"网络错误","获取token数据失败"); return ""; } QString soundUrl=QString("tex=%1&lan=zh&spd=%4&per=%5&cuid=%2&ctp=1&aue=3&tok=%3").arg(text).arg(QHostInfo::localHostName()).arg(accesstoken).arg(spd).arg(per); replydata.clear(); requwsdata =soundUrl.toUtf8(); // json格式 转utf8 result=m_http.post_sync(baiduSoundurl,header,requwsdata,replydata); if(result) { QFile file(SoundFilepPath); file.open(QIODevice::WriteOnly); file.write(replydata); file.close(); QMediaPlayer * player = new QMediaPlayer; player->setMedia(QUrl::fromLocalFile(SoundFilepPath)); player->setVolume(50); player->play(); connect(player, &QMediaPlayer::stateChanged, [&](QMediaPlayer::State state) { if (state == QMediaPlayer::StoppedState) { emit finsh_speek(); delete player; player=NULL; } }); return true; } QMessageBox::critical(NULL,"网络错误","获取识别数据失败"); return false; } QString Sound::getJsonValue(QByteArray ba, QString key) { QJsonParseError parseerror; QJsonDocument jsondocument=QJsonDocument::fromJson(ba,&parseerror); if(parseerror.error==QJsonParseError::NoError) { if(jsondocument.isObject())//如果是js对象 { QJsonObject jsonobj=jsondocument.object();//转换为对象 if(jsonobj.contains(key))//判断有无key键对应值 { QJsonValue jsonval=jsonobj.value(key);//得到val值 if(jsonval.isString())//是否为字符串 { return jsonval.toString(); } else if(jsonval.isArray()) { QJsonArray arr=jsonval.toArray();//转化为标准json数组 QJsonValue jv=arr.at(0);//取出第0个值 return jv.toString(); } } } } return ""; }
6、widget类包:
说明:
这个类就是对整体ui做一个设计,博主只是做了一个简单的ui,具体的设计可以根据自己的想法来,代码中有很多图片是没有的所以直接cp是会报错哦。我这里推荐一个图标网站iconfont-阿里巴巴矢量图标库,可以下载一些好看的图标。
实现:
1.widget.h文件
#ifndef WIDGET_H #define WIDGET_H #include #include #include #include #include #include #include #include #include //按键按下头文件 #include #include #include #include const QString AudioFilepPath="1.pcm"; namespace Ui { class Widget; } class Widget : public QWidget { Q_OBJECT public: explicit Widget(QWidget *parent = 0); ~Widget(); private slots: bool textTosound(QString text); void on_btn_hold_pressed(); void on_btn_hold_released(); void on_btn_send_clicked(); void on_pushButton_clicked(); void on_text_itemDoubleClicked(QListWidgetItem *item); void keyPressEvent(QKeyEvent * event); void mouseMoveEvent(QMouseEvent *event); bool event(QEvent *event); void finsh_speek(); void veryFast(); void Fast(); void Ave(); void verySlow(); void Slow(); void Man(); void Woman(); void Child(); void Other(); void Exit(); void Max(); void Min(); void Boy(); void Girl(); private: Ui::Widget *ui; Audio *audio; QMovie *movie,*movie1; Sound *so; bool play_end=true; QMenuBar *menu; int Sex=1;//性别 int Spd=5;//语速 int Per=1;//音色 }; #endif // WIDGET_H
2.widget.cpp
#include "widget.h" #include "ui_widget.h" Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); this->setWindowTitle("智能聊天机器人"); this->setWindowIcon(QIcon(QPixmap(":/img/机器人.png"))); //this->setWindowFlags(Qt::SplashScreen|Qt::WindowstaysOnTopHint|Qt::FramelessWindowHint); //setWindowFlags(Qt::FramelessWindowHint); // setAttribute(Qt::WA_TranslucentBackground); menu=new QMenuBar(this); menu->setStyleSheet("color:rgb(255,255,255);background-color:rgb(0,0,0);gridline-color:rgb(0,0,0);font: 14pt '楷体';"); menu->setMinimumSize(400,25); menu->setMaximumSize(400,25); //menu->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); QMenu*sex=new QMenu("用户性别"); QMenu*spd=new QMenu("Ai语速"); QMenu*per=new QMenu("Ai音色"); QMenu*window=new QMenu("窗口设置"); spd->setStyleSheet("color:rgb(0,0,0);background-color:rgb(255,255,255);font: 14pt '楷体';"); per->setStyleSheet("color:withe;background-color:rgb(255,255,255);"); menu->addMenu(sex); menu->addMenu(per); menu->addMenu(spd); menu->addMenu(window); /*性别*/ QAction *boy=new QAction("男生"); QAction *girl=new QAction("女生"); boy->setIcon(QIcon(":/img/boy.png")); girl->setIcon(QIcon(":/img/girl.png")); sex->addAction(boy); sex->addAction(girl); connect(boy,&QAction::triggered,this,&Widget::Boy); connect(girl,&QAction::triggered,this,&Widget::Girl); /*语速*/ QAction *very_fast=new QAction("非常快");//14 QAction *fast=new QAction("快");//10 QAction *ave=new QAction("一般");//6 QAction *slow=new QAction("慢");//3 QAction *very_slow=new QAction("非常慢");//0 very_fast->setIcon(QIcon(":/img/very_fast.png")); fast->setIcon(QIcon(":/img/fast.png")); ave->setIcon(QIcon(":/img/ave.png")); very_slow->setIcon(QIcon(":/img/very_slow.png")); slow->setIcon(QIcon(":/img/slow.png")); spd->addAction(very_fast); spd->addAction(fast); spd->addAction(ave); spd->addAction(slow); spd->addAction(very_slow); connect(very_fast,&QAction::triggered,this,&Widget::veryFast); connect(fast,&QAction::triggered,this,&Widget::Fast); connect(ave,&QAction::triggered,this,&Widget::Ave); connect(slow,&QAction::triggered,this,&Widget::Slow); connect(very_slow,&QAction::triggered,this,&Widget::verySlow); /*音色*/ QAction *man=new QAction("男声");//1 QAction *woman=new QAction("女声");//0 QAction *child=new QAction("童声");//4 QAction *other=new QAction("高级声");//111 man->setIcon(QIcon(":/img/man.png")); woman->setIcon(QIcon(":/img/woman.png")); child->setIcon(QIcon(":/img/child.png")); other->setIcon(QIcon(":/img/other.png")); per->addAction(man); per->addAction(woman); per->addAction(child); per->addAction(other); connect(man,&QAction::triggered,this,&Widget::Man); connect(woman,&QAction::triggered,this,&Widget::Woman); connect(child,&QAction::triggered,this,&Widget::Child); connect(other,&QAction::triggered,this,&Widget::Other); /*系统设置*/ QAction *exit=new QAction("退出"); QAction *max=new QAction("最大化"); QAction *min=new QAction("最小化"); exit->setIcon(QIcon(":/img/exit.png")); max->setIcon(QIcon(":/img/max.png")); min->setIcon(QIcon(":/img/min.png")); window->addAction(exit); window->addAction(max); window->addAction(min); connect(exit,&QAction::triggered,this,&Widget::Exit); connect(max,&QAction::triggered,this,&Widget::Max); connect(min,&QAction::triggered,this,&Widget::Min); ui->line_word->setPlaceholderText("请输入内容");//提示信息 ui->text->setIconSize(QSize(50,50)); ui->text->setWordWrap(true); //ui->label->setWordWrap(true); ui->text->setAutoScroll(true); ui->movie->setScaledContents(true); // ui->label->setScaledContents(true); ui->label_2->setScaledContents(true); movie=new QMovie(this); movie1=new QMovie(this); ui->movie->setMovie(movie); ui->label_2->setMovie(movie1); movie->setFileName(":/img/麦克风.gif"); movie1->setFileName(":/img/play.gif"); movie->start(); movie->stop(); movie1->start(); movie1->stop(); } Widget::~Widget() { delete ui; } bool Widget::textTosound(QString text) { so=new Sound; connect(so,&Sound::finsh_speek,this,&Widget::finsh_speek); so->getSound(text,Spd,Per); } void Widget::on_btn_hold_pressed() { if(play_end) { movie->start(); audio=new Audio; audio->startAudio(AudioFilepPath); } else { QMessageBox::critical(NULL,"注意","我知道你很急,但是你别急,让我说完"); } } void Widget::on_btn_hold_released() { if(play_end) { play_end=false; audio->stopAudio(); movie->stop(); QTime time; QString t=time.currentTime().toString(); Speech m_speech; QString text=m_speech.speechIdentify(AudioFilepPath); QListWidgetItem *item_user=new QListWidgetItem; if(Sex) item_user->setIcon(QIcon(":/img/boy.png")); else item_user->setIcon(QIcon(":/img/girl.png")); item_user->setText(t+":\n"+text); item_user->setTextColor(QColor(255,255,0)); ui->text->addItem(item_user); ui->text->setCurrentRow(ui->text->count()-1); Answer ai; QListWidgetItem *item_ai=new QListWidgetItem; switch (Per) { case 1: item_ai->setIcon(QIcon(":/img/rman.png")); break; case 0: item_ai->setIcon(QIcon(":/img/rwoman.png")); break; case 4: item_ai->setIcon(QIcon(":/img/rchild.png")); break; case 111: item_ai->setIcon(QIcon(":/img/rother.png")); break; } QString aitext=ai.getAnswer(text); if(aitext=="你好啊~"&&Sex==1) aitext="小哥哥,你好"; else if(aitext=="你好啊~"&&Sex==0) aitext="小姐姐,你好"; item_ai->setText(t+":\n"+aitext); ui->text->addItem(item_ai); ui->text->setCurrentRow(ui->text->count()-1); //ui->label->setText(t+":"+aitext); movie1->start(); textTosound(aitext); } } void Widget::on_btn_send_clicked() { if(play_end) { play_end=false; QString text=ui->line_word->text(); ui->line_word->clear(); QTime time; QString t=time.currentTime().toString(); QListWidgetItem *item_user=new QListWidgetItem; if(Sex) item_user->setIcon(QIcon(":/img/boy.png")); else item_user->setIcon(QIcon(":/img/girl.png")); item_user->setText(t+":\n"+text); item_user->setTextColor(QColor(255,255,0)); ui->text->addItem(item_user); ui->text->setCurrentRow(ui->text->count()-1); Answer ai; QListWidgetItem *item_ai=new QListWidgetItem; switch (Per) { case 1: item_ai->setIcon(QIcon(":/img/rman.png")); break; case 0: item_ai->setIcon(QIcon(":/img/rwoman.png")); break; case 4: item_ai->setIcon(QIcon(":/img/rchild.png")); break; case 111: item_ai->setIcon(QIcon(":/img/rother.png")); break; } QString aitext=ai.getAnswer(text); if(aitext=="你好啊~"&&Sex==1) aitext="小哥哥,你好"; else if(aitext=="你好啊~"&&Sex==0) aitext="小姐姐,你好"; item_ai->setText(t+":\n"+aitext); ui->text->addItem(item_ai); ui->text->setCurrentRow(ui->text->count()-1); //ui->label->setText(t+":"+aitext); movie1->start(); textTosound(aitext); } else { QMessageBox::critical(NULL,"注意","我知道你很急,但是你别急,让我说完"); } } void Widget::on_pushButton_clicked()//删除记录 { QListWidgetItem *it; int count=ui->text->count(); for(int i=0;itext->takeItem(ui->text->count()-1); ui->text->removeItemWidget(it); } //ui->label->clear(); } void Widget::on_text_itemDoubleClicked(QListWidgetItem *item) { //ui->label->setText(ui->text->currentItem()->text()); } void Widget::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Return) { on_btn_send_clicked(); } } bool Widget::event(QEvent *event) { if(event->type()==QEvent::KeyPress)//是否为按键按压事件 { keyPressEvent(static_cast(event));//把用户按压键盘事件相关信息指针强转为keyPressEvent事件处理函数的参数类型 return true; } return QWidget::event(event);//不敢兴趣的交给基类 } void Widget::finsh_speek() { play_end=true; movie1->stop(); } void Widget::veryFast() { Spd=14; } void Widget::Fast() { Spd=10; } void Widget::Ave() { Spd=6; } void Widget::verySlow() { Spd=3; } void Widget::Slow() { Spd=0; } void Widget::Man() { Per=1; } void Widget::Woman() { Per=0; } void Widget::Child() { Per=4; } void Widget::Other() { Per=111; } void Widget::Exit() { this->close(); } void Widget::Max() { if(this->isMaximized()) { showNormal(); } else { showMaximized(); } } void Widget::Min() { showMinimized(); } void Widget::Boy() { Sex=1; } void Widget::Girl() { Sex=0; } void Widget::mouseMoveEvent(QMouseEvent *event) { // this->move(event->globalPos().x()-this->width()/2,event->globalPos().y()); }
结束语:
多的不说,少的不唠,这个项目还是挺锻炼个人的能力的,需要自己细心去研究;特别是js解析,博主是通过百度unit平台的机器人在线对话查看json代码的模块看懂的。
效果图:
百度网盘源码链接:
链接:https://pan.baidu.com/s/1rJ6pcuRNY-hpYvCzKnRkAg 提取码:xs66