基于QT制作的天气指数人机交互界面
闲来无事,打算做一个人机交互界面,用来查看各地区的天气指数。
项目的代码文件我放在了自己的github里面,以上是链接,想要的可以自行下载,以下说明最好参看代码学习,效果更好。
先来看看最后能做成的样子吧。
OK,先来讲讲最初设想的大概流程和思路吧,我用的是C++语言,开发平台用的是QT,这是可以跨平台做界面开发的软件。
运用的点大致有:做界面用的组件,布局,样式,定时器,网络, 事件
做这个项目的前提是必须要对QT和C++有一定的熟练度,所以有些太基础的东西比如新建工程这一些我就不会细讲。
我的项目步骤细分的话可以分为七步:(步骤没有绝对的先后之分,看自己思路习惯)
第一步:规划好ui界面布局;
第二步:界面上方显示系统日期和系统时间;
第三步:可查询天气,利用接收cJSOn数据并解析,分别将当天温度、感冒指数、各天的日期、高低温以及天气图片输出;
第四步:将各控件变透明并点击右上角“皮肤”按钮更换窗口背景图片;
第五步:最下端滚动显示“你好,这是天气查询显示平台”;
第六步:通过左上方输入框输入城市,点击“搜索”后更新ui界面各天气信息;
第七步:点击最右上角“”,退出ui界面;
====================================================================================
以下就是步骤的详细思路:
第一步:规划好ui界面框架
我创建的工程名为WeatherQuery,主文件main.cpp,weatherquery.cpp,weatherquery.h,weatherquery.ui。
构思往往是做项目的第一步,下手之前,我花了点时间整理了下思路,并把ui界面大致规划好了,如下:
相信只要对QT有些基础的同学要完成这一步一定不难,先布局好了大概样子,记得要将各个控件对命名号名字,以免混淆,而暂时没想到的细节可以后面慢慢补上。
第二步:上方显示系统日期和系统时间
这一步需要用到定时器来获取系统日期和时间,所以到这三个头文件:
#include <QTime> #include <QDate> #include <QTimer> |
需要在文件weatherquery.h的类中添加一个定时器对象:
private: QTimer mtime; //时间对 |
在weatherquery.cpp的构造函数中,打开定时器并设置好定时时间,再将定时器信号与槽函数关联,这样就是每当过了xx秒,系统就会自动执行一遍槽函数,
WeatherQuery::WeatherQuery(QWidget *parent) : QMainWindow(parent), ui(new Ui::WeatherQuery) { ui->setupUi(this);
//显示系统时间 //启动定时器 mtime.start(); //设置定时时间 mtime.setInterval(1000); //将定时器信号与槽函数向关联,每1s执行依次槽函数 connect(&mtime,SIGNAL(timeout()),this,SLOT(update_time())); } |
而槽函数中就写了自动获取系统日期和时间的代码,并把获取到的信息显示在ui界面对应的控件上,这个的显示时间和日期的控件我用了LCD Number这个控件;
void WeatherQuery::update_time() { //获取当前系统年月日 QString System_Date = QDate::currentDate().toString("yyyy-MM-dd"); ui->DateLcdNumber->display(System_Date); qDebug()<<System_Date;
//获取当前系统时分秒 QString System_Time = QTime::currentTime().toString("hh:mm:ss"); ui->TimeLcdNumber->display(System_Time); qDebug()<<System_Time; } |
记得槽函数要在weatherquery.hz类中要进行声明哦,声明方式也可以不用手打,有快捷方式,在Refactor中选择“添加private slots声明”就可以了。
第三步:接收cJSOn数据并解析
这是这个项目中较为核心的一步,所以各个天气的数据,都是从cJSON数据的得到的,而这个cJSON是怎么来的,网上有一些专门查询天气指数的网站(说是服务器应该更准确点),比如说这个:
http://wthrcdn.etouch.cn/weather_mini?citykey=101280101
只要在浏览器网址上输入这个地址,后面的101280101是我自行加上去的,此ID是广州城市的ID,说明你将得到的是广州市的天气指数的CJSON数据,里面的内容是这样的:
如果看不懂,可以借助一些工具,比如cJSON在线视图查看器,这个工具百度一下就有了。
在“JOSN数据”中复制黏贴进cJSON数据,然后点击“视图”,就能全面清晰看到天气数据,如下图:
当然了,以上的操作,我们都要用代码来实现,包括接收cJOSN数据,解析并获取自己想要的某一个天气指数等。
在接收到cJSON数据之前,当然先要在向远方的浏览器服务器发送网址请求
http://wthrcdn.etouch.cn/weather_mini?citykey=xxxxxxx |
后面的“xxxxx”就是对应城市的城市ID。
在这里,我们称之为发送请求报文,这个HTTP网络操作,不借助浏览器,用代码来实现,首先需要用到这几个网络头文件
#include <QNetworkAccessManager> #include <QNetworkRequest> #include <QNetworkReply> |
又因为需要解析cJOSN数据,又需要这几个头文件
#include <QJsonObject> #include <QJsonArray> #include <QJsonValue> #include <QJsonDocument> #include <QJsonParseError> |
还有要注意的一点是,因为要用到网络,所以必须要在weatherquery.pro中接入网络模块QT += network,按下面这么直接添加“network”就可以了
其次需要在.h类中定义一个网络管理器对象
因为我们需要一运行ui界面就显示出一个默认城市的天气指数,所以需要在构造函数写代码,记住,构造函数里的程序一运行自动执行一次,只有一次!
当我们在ui界面左上角输入城市,点击搜索按钮后就会进入另一个函数中再次发送请求报文,获取新的从cJOSN数据,将新的数据解析显示出来,不过由于搜索框的代码还没写,就先搁置了,先写运行默认城市的代码。
先发送一个请求报文:
//发送报文 QUr lurl("http://wthrcdn.etouch.cn/weather_mini?citykey=101280101"); QNetworkRequest request(url); manager.get(request); |
一旦这个报文发送出去了,manager管理器就会产生一个timeout信号出来
//管理器manager发送报文后就执行槽函数 connect(&manager,SIGNAL(finished(QNetworkReply*)),this,SLOT(read_cjson(QNetworkReply*))); |
这个关联操作的意思是,一旦manager管理器发送了timeout操作,就自动执行解析CJSO数据的槽函read_cjson(QNetworkReply*),槽函数都是自定义函数,在这个槽函数中,这个需要传一个参数,传什么参数呢,传的就是接收回来的cJOSN数据。
这里有一个小陷阱,一般思路都是先发送报文再关联槽函数,实际上,关联操作要在发送报文操作之前,原因很简单,如果在需要多系发送报文的情况下,要是在你发送报文的期间,上一次发送的报文已经有cjOSN数据传回来了,因为你的关联操作在后,所以根本不能执行解析c'JOSN数据的函数。
所以正常应该都是这样的:
这里的城市ID就先固定了广州,默认一运行就是广州的天气指数
接下来就是写你的用来解析接收回来的cJOSN数据的解析槽函数read_cJjosn(QNetworkReply *reply)了
值得一提的是,一切接收回来的cjson数据,都会被放在QNetworkReply这个类中,只要在这个类中提取就可以了,所以我们才需要将传参定义QNetworkReply类型,这样cJOSN数据的地址就放在指针reply中了
接下来,开始写代码,开始解析~~~~
好了,如果不信的话,可以将接收回来的cjosn数据通过qDebug打印出来,不过因为是qDebug打印的,所以打印出来的cjosn中会有很多的转义字符,这个如果用cjOSN在线视图查看器是看不出个123来的。
Ok.接下来开始解析,何为解析呢,就是在看起来乱的像条蛇的的cjosn数据中挑选出你想要的某一个确定的数据。
我们以下面的图来边看图边写代码,这样会对思路更加清晰:
下面的代码,都要配合上面的图去看,这样一看就能理解为什么代码要这么写了;
由于forecase这个第二子根是一个数组,所以直接在数组中寻找:
这样,就可以在ui界面上显示日期,城市等信息了,但是这样还不够,要知道,我们设计的ui界面上还有显示各种高温、低温、风向、天气类型和相配的图片等,所以就有了下面的代码:
上面红色框框的,就算根据不同的天气类型配合显示不同的天气图片了,因为我们解析出来的天气类型,都放在了type这个变量中,将这个变量作为函数传入WeatherType()函数中,函数就会根据type的内容返回一个字符串,这个字符串中就包含了需要显示的图片信息,利用返回值typemsg就可以直接显示对应的图片了,因为WeatherType()里面是这样写的:
各种天气图片也一并放在了链接里的工程中了。
因为要显示今天,昨天,明天,后面的天气指数,代码优点多,就暂且这样吧,方法手段都是一样的。
第四步:将各控件变透明并点击右上角“皮肤”按钮更换窗口背景图片
既然是背景图片了,那么一定要把控件设置成透明才行,否则的话,插入的图片会直接掩盖点整个界面,那就很尴尬了。
既然要把整个ui界面的控件变透明,那就要在整个ui界面的整体窗口进行设置,点中窗口空白部分(切记不要点到控件),右击选择“改变样式表”,然后点中“添加颜色”:
这里要注意的一点是一定点中如上图的紫色圆圈,这样才会有一系列样式选择项出来,在弹出来的样式选择项中选中“background-color”,意为选择背景颜色,然后会弹出来下图这样的一个颜色选择框:
看到Alpha通道那个选择项了吗,那就是设置透明度的地方,将其设置为0,整个ui界面的控件就变透明了,为我们通过插入背景图片改变ui界面的背景打通了一条路。
接下来,我们就可以插入背景图片了,在一开始的时候,我就在ui界面放了一个Push按钮控件:
就是图上那个红色的圈圈,那这个按钮的背景为什么变成了一件衣服的样子,那是因为我在这个按钮上插入了一张背景图片嘛。
这个相当简单,就点中那个按钮,右击——》“改变样式表”,如下图那样,一样要点中那个下三角的小标签哦。
在弹出来的样式选择框中选择“background-image”,然后就会一个下图的资源文件框:
里头就有我找来的各种图片,包括那张衣服图片,选中,点击“OK”即可,样式编辑表就变成了下面那样:
虽然编译运行的时候,确实按钮的背景颜色变了,但是此外还需要考虑另外一点,按照逻辑来说,为了更准确具备一种“按下”的柑橘,应该是鼠标一点击按钮,按钮的颜色就要改变一下,以此来营造一种有按钮按下的假象,因此,需要在“编辑样式表”中进一步如下的代码设置:
Ok,这样运行时候就会有“按下”按钮背景图片改变的样子,然后进行更改整个ui界面背景图片的大操作:
依旧是那个按钮,右击,选择“转到槽”,选中下图的clicked()单击信号函数,这个信号函数的意思是:一旦点击了按钮,就会执行槽函数一次,点击几次,槽函数就执行几次.
点击“OK”,在跳转出来的槽函数中写入代码:
此处,通过按钮更改窗口背景的操作,完成,真实的效果,运行试一下就知道了。
在讲第五点的时候,有个地方如要提醒一下,就是上文几次讲到的资源文件,所谓资源文件,就是存放了各种资源的文件,很明显,我在这个资源文件下放的是一个图片文件夹。
因为这个资源文件new/prefix1/images是我一开始就放入工程中,所以一点开就有了,那么如果从一无所有中开始添加资源文件呢,下面就大概讲讲:
点击工程文件,右击“选择添加新文件”,如下图:
在弹出来的新建文件选择框中QT模板中的“Qt Resource File”,意为添加QT框架的资源文件,如下图,先点击“choose”,然后命名即可,我之前的命名是image,为了避免命名重复,就叫“aimages”吧,依次“下一步”、“完成”即可。
这样就在“资源”工程文件中见到新建的images.qrc文件,这个就是新建的资源文件,如下图,记得要把找到的图片文件夹拉进工程文件中哦,要不然资源文件可上哪儿去找图片?
点击“添加”,并选择“添加前缀”,这样就把放图片文件夹路径加了一个“new/prefixs”前缀;再点“添加文件”,再“打开文件”框中找到放图片的文件夹,按“ctrl”+A讲所有图片添加进来。
看,然后图片资源就如下图那样进来了:
这样就可以在编辑样式表中“添加文件”中看看到有资源文件的存在了:
曾经发生一个状况,就是资源文件中忽然找不到资源文件了,并且编译出现以下错误:
:-1: error: cannot open output file debug\WeatherQuery.exe: Permission denied
解决办法很简单:进入任务管理器,将程序进程关掉,再重新运行,因为有可能是后台有Qt程序的.exe在运行,关掉就行了。
第五步:最下端滚动显示“你好,这是天气查询显示平台”
在做这一步之前,我重新写了一个QT工程RollTest,里面的功能就只是将一串字符串在窗口中从右到左滚动显示而过,里面的头文件rolltest.h和源文件rolltest.cpp,界面文件rolltest.ui都在WeatherQuery工程文件中都可以找到(因为这几个文件都是后面移过去的,下文会讲)。
因为这样的滚动操作需要用到绘图工具,所以rolltest.h中需要用到这几个头文件:
#include <QWidget> #include <QPaintEvent> #include <QTimer> #include <QPainter> |
首先在rolltest.h中,创建几个私有对象:
然后在rolltest.c的构造函数中,一运行就自动执行一遍以下的构造函数:
那槽函数pushButton_clicked()中的功能就相当简单了,就是不断改变字符串的显示坐标,以此来产生不断滚动的现象,如下图:
其中update()的作用就是不断刷新窗口,只有不断刷新了,窗口内字符串的移动状态新状态才会被不断更新显示,同时,这个函数内部,就有自动调用PaintEvent()绘图事件函数的代码,而这个绘制事件函数,我们就用来写滚动显示的正式代码,废话少说,看图:
下面还有一个小小的操作,我们知道下图的rollStr中存放的是要进行滚动的字符串,我们要把这个变量变得可以进行由外部进行赋值,即通过WeatherQuery工程中的文件来进行设置。
如下图进行右击,选择变成Settter成员变量:
一旦rilltest.cpp中出现下面的函数,那说明就是变量改变成功了,此字符串要赋值成什么句子,可有外部*决定。
好了,到这里,那么滚动显示的代码就写好了,不过这里要提醒一下,这是我们重新开一个工程rolltest写的代码,并没有写在天气预报查询工程WeatherQuery里面,所以我们要做的,就是移植过去,将rolltest工程中的rolltest.h,rolltest.cpp,rolltest.ui三个核心文件复制粘贴到天气查询工程WeatherQuery中,复制完成后还要注意还要在工程里用“添加现有文件”添加进入哦。
然后,就在WeatherQuery的UI界面中,找一个空白地方,放置一个“Widget”窗口控件
为什么要用这个呢,这相当于一个容器,将我们刚刚复制过来的几个rolltest文件固定在里面执行,这样,字符串滚动显示就被固定在了这个容器限制这个地方,字符串不会滚动着滚动着就跑到其他地方去,此外还有一个原因,因为我们要把这个Widget窗口控件提升,一旦涉及提升操作,大部分都是用这个Widget控件的。
然后就会出现一个“提升的窗口部件”这个框,按下图进行操作:
最后,点击”添加“,在点击“提升”即可。
一旦提升完成,那么这个Widget窗口控件的类型就不再是Widget类型,而是下面图这个类型了:
这样就行了,不用再写代码了,因为Widget控件已经提升为了“RollTest”类型了,RollTest中执行什么功能,这个Widget控件就会执行什么功能,没错,RollTest中执行的是字符串滚动功能,那这个Widget控件就会执行字符串滚动功能。
好了,如果细心,就会捕捉一个问题,那么这个滚动显示要显示的句子,到底该怎么决定呢,嗯,没错,这要解决一下;
因为我们已经把rolltest.cpp中的StringStr变量变成了可以进行外部赋值,那我们就外部赋值哦,这里说的外部赋值指的是由rolltest.cpp这个文件以外的其他文件进行赋值,看图就懂了:
第六步:通过左上方输入框输入城市,点击“搜索”后更新ui界面各天气信息
UI界面中我插入了一个输入框,所谓输入框,就算可以直接输入文字内容的组件,而且我在输入框中靠左侧又添加了一个搜索按钮,如下图:
然后按单击信号形式转到槽函数,因为那个“放大镜”是按钮组件,设置成单击信号的形式就代表着每当输入城市名字,按下搜索按钮后,就会调到槽函数,执行如下图的程序:
此处的难点在于红色框框,为什么这条代码就可以得到对应城市的ID号呢,因为我们用到了MAP容器,需要用到头文件:
#include <QMap> |
而且需要在.h函数中添加对象:
在构造函数中:
这个citykeys.txt是网上找来的比较全的城市ID文本资料,我放在了build-WeatherQueryDesktop_Qt_5_7_0_MinGW_32bit-Debug文件夹中,要把上面的代码,一步一步调试打印出来,就会很好理解从字符串解析出字符串解析这种操作的原理了。
第七步:点击最右上角“”,退出ui界面
这是七个步骤中最省时间的一个部分,直接点击,右键转到槽函数,以单击信号的形式打开,在跳转到的槽函数中,写关闭代码就行。
void WeatherQuery::on_CloseBt_clicked()
{
this->hide();
}
这只是一步把界面窗口隐藏的代码而已,并不是真的关闭,好吧,我承认这一步做的很随便,其实应该是在槽函数中关闭各种各样的文件,或者直接写强制关闭UI界面运行进程的代码即可。
推荐阅读