2022.07.20:最近在学习JAVA的知识,以后就踏上JAVA的道路了。本人QT方面自认为还学的比较好,但是以后应该不会更这方面了,把以前写过QT方面的项目,写出来分享给大家。两个月前写的项目,除了景物识别外还有一些小功能,具体的细节可能描述不请了,结尾有完整代码及注释,如果能够帮助到你,倍感荣幸,祝大家学习愉快!
下面附上我这个项目在构建时候的参考网址,学益得智能硬件在B站上的视频。
Qt语音识别 | 百度语音识别应用_哔哩哔哩_bilibili
介绍一下我的系统都有什么功能,最主要的功能就是打开一张动物类的图片,可以通过百度AI来识别出图片中的动物是什么种类并以文字的形式进行反馈。除此之外还有一些微功能作为点缀,分别实现了程序启动时的GIF动图效果、简易GIF播放器、打点计时器、按钮长按功能、任务栏加载进度条。下面文章先从主要功能景物识别开始。
目录
一.景物识别功能
二.微功能
1.程序启动画面GIF动图功能
2.简易GIF播放器功能
3.打点计时器功能
4.按钮长按控制进度条功能
5.任务栏加载任务条功能
本景物识别功能主要原理就是利用了百度AI平台已经成熟的景物识别技术来在OpenCV上实现对自然景物图像的识别,整个流程如下:
1.在百度AI平台上创建应用,获取API KEY和Secret KEY。
2.根据获取到的API KEY和Secret KEY在OpenCV上通过编程向服务器发起请求来获取access_token。
3.打开待识别图像后对图像做base64编码和urlencode编码。
4.向服务器发起请求并处理服务器返回的数据。
首先设计界面,在QT中的UI界面中进行设计,使用了QLabel、QPushButton、QGroupBox、Line Edit等控件,设计方面比较简单,注意宽高的数值会让界面更美观,这里的icon都是在阿里巴巴矢量库找的,十分好用,也推荐给大家。(在这里我插入了背景图,十分简单,在后面我会说到)
iconfont-阿里巴巴矢量图标库
然后进行图像的编码处理 ,在源文件上添加一个C++ class文件主要用于对图像进行压缩处理,在此文件中写入函数对图片进行base64编码和urlencode编码。
下一步是获取access_token,此处较为重要,通过这个数值来与百度AI建立连接,调用百度成熟的图像识别技术,具体需要在百度智能云上注册账号,注册使用方法在我的另一篇文章有详细描述。基于Python的语音识别控制系统_兰斯洛特.的博客-CSDN博客_基于python的语音识别系统
利用API KEY和Secret KEY来获取access_token,新建一个类来用于向服务器发送请求来得到access_token,我们在这一步只需要一个url即可发送请求。在函数中写入一个死循环,如果收到了请求返回的数据则跳出死循环并读出数据。
最后进行景物识别,这里使用的百度智能云中的图像识别中的动物识别,还有其他很多种识别,像植物识别、车牌识别,百度智能云功能很多,使用时还是需要慢慢找一下。
利用获取到的access_token对百度AI识别平台中动物识别对应的url发出请求,并返回识别结果。这里返回的数据是一个数组,我们先把数组解析出来,然后进行判断是否已经解析出数组,解析成功后读取识别概率最大的第一个元素,类型为QJsonValue类型。最终将结果显示在lineEdit中,如果这中间的任意一个环节出现问题没有进入到解析数组的if语句中,则返回未知。
好现在我们来看看识别的结果,打开一张图片后点击开始识别,经过一段时间后会在右侧下方显示出识别的文字结果,经过了许多测试,识别结果还是比较好的。
下面附上代码(上面的图片有文件目录就不放了,代码顺序也是根据上面图片的顺序)
pro文件里要将这行进行添加:
QT += core gui network axcontainer winextras
image.h
#ifndef IMAGE_H
#define IMAGE_H
#include
#include
#include
#include
#include
class image
{
public:
image();
static QByteArray imageToBase64(QString filename);
};
#endif // IMAGE_H
image.cpp
#include "image.h"
image::image()
{
}
QByteArray image::imageToBase64(QString filename)
{
QImage img(filename);
QByteArray ba;
QBuffer buf(&ba); //用QByteArray构造QBuffer
buf.open(QIODevice::WriteOnly);
img.save(&buf, "JPG"); //把img写入QBuffer
QByteArray base64 = ba.toBase64(); //对图片做base64编码
QTextCodec *codec = QTextCodec::codecForName("UTF-8");
//对图片urlencode
QByteArray imgData = codec->fromUnicode(base64).toPercentEncoding();
return imgData;
}
http.h
#ifndef HTTP_H
#define HTTP_H
#include
#include
#include
#include
#include
#include
class Http : public QObject
{
Q_OBJECT
public:
Http();
static bool post_sync(QString Url, QMap header, QByteArray &requestData, QByteArray &replyData);
};
#endif // HTTP_H
http.cpp
#include "http.h"
Http::Http()
{
}
bool Http::post_sync(QString Url, QMap
header, QByteArray &requestData, QByteArray &replyData)
{
QNetworkAccessManager manager; //发送请求的动作
QNetworkRequest request; //请求的内容(包含url和头)
request.setUrl(Url);
QMapIterator it(header);
while (it.hasNext())
{
it.next();
request.setRawHeader(it.key().toLatin1(), it.value().toLatin1());
}
QNetworkReply *reply = manager.post(request, requestData);//接收返回结果
QEventLoop l;
connect(reply, &QNetworkReply::finished, &l, &QEventLoop::quit);
l.exec(); //死循环
if (reply != nullptr && reply->error() == QNetworkReply::NoError)
{
replyData = reply->readAll();
return true;
}
else
{
return false;
}
}
mainwindow.cpp
void MainWindow::on_pushButton_3_clicked()//开始识别
{
QByteArray img = image::imageToBase64(filename); //image=xxxxxxx
QByteArray imgData = "image=" + img; //body
//获取access_token
QByteArray replyData; //保存回复信息
QString url = QString(baiduTokenUrl).arg(client_id).arg(secret_id);
QMap header; //封装头部信息
header.insert(QString("Content-Type"), QString("application/x-www-form-urlencoded"));
QString accessToken;
bool result = Http::post_sync(url, header, imgData, replyData);
if (result)
{
QJsonObject obj = QJsonDocument::fromJson(replyData).object();
accessToken = obj.value("access_token").toString();
}
replyData.clear();
QString imgUrl = baiduImageUrl.arg(accessToken);
result = Http::post_sync(imgUrl, header, imgData, replyData);
if (result)
{
QJsonObject obj = QJsonDocument::fromJson(replyData).object();
QJsonValue val = obj.value("result");
if (val.isArray())
{
QJsonValue first = val.toArray().at(0);
if (first.isObject())
{
QString name = first.toObject().value("name").toString();
ui->lineEdit->setText(name);
return;
}
}
}
ui->lineEdit->setText("未知");
}
此功能在程序完全启动前显示一个启动画面,在程序完全启动后消失。Qt中提供了QSplashScreen类实现了在程序启动过程中显示启动画面的功能。由于启动画面通常在程序初始化时间较长的情况下出现,为了使程序初始化时间加长以显示启动画面,此处调用Sleep()函数,使窗口在初始化时休眠1000ms。图片首先出现1s,然后弹出主窗体。注意Sleep()函数函数加在了mainwindow.cpp的主方法中。
来看看结果,在代码中放动图即可,我只是截屏所以显示不出来动态效果。
下面附上代码:
main.cpp
#include "mainwindow.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QPixmap pixmap(557,386);//gif动画的尺寸大小
QSplashScreen splash(pixmap);//利用QPixmap对象创建一个QSplashScreen对象
QLabel label(&splash);
//设置label大小和位置
QMovie mv(":/new/prefix1/c.gif");
label.setMovie(&mv);
mv.start();
//显示此启动图片
splash.show();
splash.setCursor(Qt::BlankCursor);
for (int i = 0; i < 3000; i += mv.speed()) {
a.processEvents(); //使程序在显示启动画面的同时仍能响应鼠标等其他事件
Sleep(mv.speed()); // 延时
}
MainWindow w;
w.show();
splash.finish(&w); //在主体对象初始化完成后结束启动动画
return a.exec();
}
设计一个简易GIF播放器,可以播放GIF动画。其基本功能有载入文件、播放、暂停、停止、快进和快退。需要设置movie的缓存模式,这里我们要求当文件播放一遍后返回重新播放,所以使用CacheAll模式。updateFrameSlider()函数中,如果打开的movie存在,将激活所有控件。
在mainwindow.cpp的主方法中新建movie。
下面的方法都是通过槽函数与界面中的按钮控件进行连接。
来看一看结果:
这里的减速加速可以对GIF动图的播放速度进行控制,用到的就是上文提到的movie。
下面附上源码:(都是直接写在mainwindow.cpp中)
主方法:
//新建movie
movie =new QMovie(this);
movie->setCacheMode(QMovie::CacheAll);//设置movie的缓存模式
//movie的槽函数
connect(movie,SIGNAL(frameChanged(int)),this,SLOT(updateFrameSlider()));
槽函数:
void MainWindow::on_open_clicked()//打开GIF并播放
{
QString fileName=QFileDialog::getOpenFileName(this,"open a file",currentMovieDirectory);
if(!fileName.isEmpty()){
currentMovieDirectory=QFileInfo(fileName).path();
movie->stop();
ui->movieLabel->setMovie(movie);//将标签内容设置为movie
movie->setFileName(fileName);//设置文件名
movie->start();
//切换成暂停按钮
ui->play->setIconSize(QSize(30,30));
ui->play->setAutoRaise(true);
ui->play->setToolTip("Pause");//工具栏提示
//s改为暂停
s="暂停";
}
}
void MainWindow::on_play_clicked()//播放GIF
{
if(s=="播放"){
movie->start();
//切换成暂停按钮
ui->play->setIconSize(QSize(30,30));
ui->play->setAutoRaise(true);
ui->play->setToolTip("Pause");//工具栏提示
//s改为暂停
s="暂停";
}
else if(s=="暂停"){
movie->setPaused(true);//暂停
//切换为播放按钮
ui->play->setIconSize(QSize(30,30));
ui->play->setAutoRaise(true);
ui->play->setToolTip("Play");//工具栏提示
//s改为播放
s="播放";
}
}
void MainWindow::on_stop_clicked()//暂停GIF
{
movie->stop();
//如果是暂停,则切换为播放按钮
if(s=="暂停"){
//切换为播放按钮
ui->play->setIconSize(QSize(30,30));
ui->play->setAutoRaise(true);
ui->play->setToolTip("Play");//工具栏提示
//s改为播放
s="播放";
}
}
void MainWindow::on_back_clicked()//慢放GIF
{
qint32 currentSpeed=movie->speed();//获取当前速度
qDebug()<40){
movie->setSpeed(currentSpeed-20);
}
else{
movie->setSpeed(20);
}
}
void MainWindow::on_forward_clicked()//快放GIF
{
qint32 currentSpeed=movie->speed();//速度
qDebug()<setSpeed(currentSpeed+20);
}
else{
movie->setSpeed(500);
}
}
void MainWindow::updateFrameSlider()
{
bool hasFrame=(movie->currentFrameNumber()>=0);
//激活所有控件
ui->back->setEnabled(hasFrame);
ui->forward->setEnabled(hasFrame);
ui->stop->setEnabled(hasFrame);
}
计时器实现四个功能:开始计时、停止计时、暂停计时以及打点。当点击暂停时,开始按钮和停止按钮无法点击,此时暂停按钮变为继续。当点击停止时,开始按钮和暂停按钮无法点击,此时停止按钮变为清零。主要使用的是timer,注意使用时要在头文件中引入对应库。
来看一看效果:
作为微功能来讲还是比较有趣的。
下面附上源码:(同样都是直接写在mainwindow.cpp中)
主方法:
ptimer=new QTimer; //初始化对象
connect(ptimer,SIGNAL(timeout()),this,SLOT(updataTimeAndDisplay()));
槽函数:
void MainWindow::updataTimeAndDisplay()//定义更新和显示时间函数
{
QTime current=QTime::currentTime();//获取系统当前时间
int t=this->baseTime.msecsTo(current); //两者相减的时间之差
QTime showtime(0,0,0,0);//初始时间
showtime=showtime.addMSecs(t);//增加tms
showStr=showtime.toString("hh:mm:ss:zzz"); //转换为string类型
ui->lcdNumber->display(showStr); //显示
}
void MainWindow::on_btn_start_clicked()//开始
{
baseTime=QTime::currentTime();
ptimer->start(1);
}
void MainWindow::on_btn_stop_clicked()//停止
{
if(ui->btn_stop->text()=="停止"){
ptimer->stop();
ui->btn_stop->setText("清零");
ui->btn_start->setEnabled(false);//开始按钮无法点击
ui->btn_pause->setEnabled(false);
}else{
ui->lcdNumber->display("00:00:00:000") ;//清零
ui->textBrowser->clear();
ui->btn_stop->setText("停止");
ui->btn_start->setEnabled(true);//开始按钮可以点击
ui->btn_pause->setEnabled(true);
}
}
void MainWindow::on_btn_pause_clicked()//暂停
{
static QTime pauseTime; //暂停时间【静态】
if(ui->btn_pause->text()=="暂停"){
pauseTime=QTime::currentTime();//获取点击暂停时的当前时间
ptimer->stop();
ui->btn_pause->setText("继续");
ui->btn_start->setEnabled(false);//开始按钮无法点击
ui->btn_stop->setEnabled(false);
}else{
QTime cut=QTime::currentTime();//继续时的时间
int t=pauseTime.msecsTo(cut);//差值
baseTime=baseTime.addMSecs(t); //后延相应的tms继续计时
ptimer->start(1);
ui->btn_pause->setText("暂停");
ui->btn_start->setEnabled(true);//开始按钮可以点击
ui->btn_stop->setEnabled(true);
}
}
void MainWindow::on_btn_log_clicked()//打点
{
ui->textBrowser->append(showStr);//添加到textBrowser中
}
对button按钮添加一个长按事件(比如点击按钮,开始运动。松开按钮,运动停止)。主要用到的是setAutoRepeat函数。按钮长按点击后滑动条Slider做相应移动。
setAutoRepeat函数用于确定是否启用了自动恢复。如果启用了autoRepeat,则按下按钮时会定期发出pressed()、Release()和clicked()信号。默认情况下,autoRepeat处于关闭状态。初始延迟和重复间隔由autoRepeatDelay和autoRepeatInterval以毫秒为单位定义。
autoRepeatDelay函数保留自动重复的初始延迟。如果启用了autoRepeat,则autoRepeatDelay定义自动重复开始之前的初始延迟(以毫秒为单位)。
autoRepeatInterval函数保存自动重复的间隔。如果启用了autoRepeat,则autoRepeatInterval以毫秒为单位定义自动重复间隔的长度。
先看一看效果:
点击按钮后鼠标一直不松手,进度条会从零加载至一百,同时在控制台会输出从零到一百来显示进度条的加载。
下面附上源码:(直接写在mainwindow.cpp的主函数中即可)
ui->pushButton_4->setAutoRepeat(true); //启用长按
ui->pushButton_4->setAutoRepeatDelay(400);//触发长按的时间
ui->pushButton_4->setAutoRepeatInterval(50);//长按时click信号间隔
connect(ui->pushButton_4,&QPushButton::clicked,[&]{
qDebug()<horizontalSlider->setValue(i);
if(i>=100)i=100;//将最大值控制在100
});
任务栏进度条通常用于显示软件当前正在执行的任务的进度(如编译程序的进度、下载任务的进度)。本功能主要利用QWinTaskbarButton和QWinTaskbarProgress类实现任务栏进度条的显示。
Qt框架针对Windows提供了一个单独的模块WinExtras。这个模块中提供了一些类库和函数,用于实现Windows上特有的功能,如类型转换、句柄操作、窗口属性设置等。当然也包括了此次我们要说的任务栏进度条,QWinTaskbarButton和QWinTaskbarProgress类。
结果不是很明显,运行程序是会生成一个应用,此功能的作用就是让运行的应用在电脑下方状态栏中的图标有加载进度条的显示。来看一看效果:
像我们平时在网上下载东西加载时的状态。
下面附上源码:(同样都是写在mainwindow.cpp中)
主方法:
timer = new QTimer;
timer->setInterval(10);//设置时间间隔为100ms
timer->setSingleShot(false);//设置不是单次触发器
windowsTaskbarButton = new QWinTaskbarButton(this); //创建显示进度的任务栏按钮
connect(timer, &QTimer::timeout, this, &MainWindow::onTimeout);//连接定时器信号和对应的槽函数
connect(ui->pushButton_10, &QPushButton::clicked, this, &MainWindow::on_pushButton_10_clicked);//连接按钮点击信号和对应的槽函数
槽函数:
void MainWindow::on_pushButton_10_clicked()
{
windowsTaskbarButton->setWindow(windowHandle()); //将任务栏按钮与进度条关联,假设进度条是它自己的窗口
windowsTaskbarProgress = windowsTaskbarButton->progress();//设置进度指示器
windowsTaskbarProgress->setRange(0, 100); //设置最小值和最大值的值
timer->start(); //定时器启动
}
void MainWindow::onTimeout() {
qDebug()<value()+1;
windowsTaskbarProgress->setValue(windowsTaskbarProgress->value() + 1);//设置TaskbarProgress的当前值
windowsTaskbarProgress->show(); //显示TaskbarProgress
//当达到最大值时则计时器停止且重置进度指示器
if(windowsTaskbarProgress->value()==100)
{
timer->stop();//计时器停止
windowsTaskbarProgress->reset();//重置进度指示器
}
}
写在后面:项目在编写代码时确实花费了很大力气,也参考了很多文章,最初想把项目以文章的形式写出来是因为网上的代码很乱很杂,在找参考代码的时候也找了很久很久,希望每一个奋斗在QT领域的初学者都可以看到这篇文章,能够给予你们帮助。