通过一个实际案例来介绍开发程序的流程和QT的各个模块。
开发程序流程包括系统结构设计、模块设计、层次划分、模块实现、模块间通信、模块匹配。
QT模块包括在界面设计CSS样式、web网站文件的上传和下载、应用程序实例检测、外部动态链接库调用、系统托盘管理、查看网络状态、执行外部进程、进程间通过Windows消息通信、INI和JSON格式文件读写、使用install shield打包和发布程序。
目录
1.系统结构设计
以“远程传输与控制系统”为例从功能上分为面向底层硬件部分和顶层通信部分;在模块类别上,系统分为硬件层、软件层、网络层、跨语言通信层、数据层。
1.1 总体结构
软件层和硬件层分隔,因为硬件层要操作身份证读卡器,所以调用逻辑与软件层有区别。网络层与跨语言层本属于网络通信范畴。但在系统中一部分是传统的HTTP和TCP通信,另一部分是跨语言hessian协议调用远端Java服务。两者实现技术和手段差别很大,因此在系统设计中将它们分开。
数据层严格来说是传统C/S结构中的服务器端程序。
1.2 软件层
软件层实现系统的大部分功能。
界面登录:登录界面要进行CSS样式处理,登录后信息要保存到系统配置文件中。同时,登录界面不能通过关闭窗口退出,只有正确登录后,才可以最小化到托盘。
系统托盘:用户对硬件操作的结果以实时消息显示在托盘程序图标中的消息框,当用户需要时,单击即可重现消息。同时,单击图标右键,弹出的快捷菜单中包括硬件设备状态、业务状态、注销用户、退出程序。
后台执行:进入托盘状态后,系统会启动timer,定期进行业务轮询。
强制升级:使用网络层从指定的网址读取升级程序版本。升级前,系统自动关闭自身进程。
1.3 硬件层
首要任务是与硬件设备建立关联。采用动态库DLL建立关联。
然后是操作硬件设备。
1.4 网络层
先通过HTTP协议在指定网址读取升级程序,如果发现新版本,则下载相应升级程序。升级程序是打包好的EXE文件。
1.5 跨语言通信层
在操作数据库方面,基于JavaEE的web技术是十分成熟。因此在数据层采用JavaEE实现业务服务,在客户端通过QT。两者连接采用比较成熟的hessian协议。
1.6 数据层
数据库采用PostgreSQL。硬件设备读取后的数据经过处理,以JSON串形式存储在数据表中。
2. 软件层
开发一个软件登录界面,登录后界面最小化进入托盘管理,在后台运行。
2.1 程序窗口
2.1.1 对话框开发
首先创建一个QDialog类,然后在ui文件构建一个下面的界面。
2.1.2 图片资源管理
label控件可以通过加载图片来达到美化对话框效果。
首先,选择“文件”——“新建文件或项目”。然后在“文件与类”中选择QT,再选择Qt Resource File,自定义文件名。
然后, 右击.qrc文件,选择添加现有文件,把图片添加进去。
最后, 在ui文件中,对需要添加图片的label控件右击,选择“改变样式表”,然后点击“添加资源”,选择“image”或“background-image”,选择对应图片添加。
注:Qt虽然原则上支持PNG、JPEG等多种格式的图片,但对PNG支持最好。如果程序中使用JPEG等其他格式图片,则程序打包时要加上相应的DLL,否则图片在用户的计算机上无法显示。而且有时加载了支持JPEG的DLL,也无法正常显示。所以建议在Qt中使用PNG格式的图片。
2.1.3 CSS样式表
Qt允许使用CSS样式表优化界面。这是Qt在各种开发平台中所独有的特色。在ui界面空白处右击(不要单击label等控件),选择“改变样式表”,在编辑栏输入下面代码:
2.2 按钮响应
单击按钮应该有对应时间发生。
选择“登录”按钮右击,选择“转到槽”。然后选择第一个函数,表示响应单击事件。
接着在dialog.cpp中就会自动生成on_pushButton_clicked()函数。
最后,在函数编写下面代码。
2.3 托盘管理
程序登录后直接进入Windows系统托盘。Qt程序是跨平台程序,由于有些平台没有系统托盘的概念,因此需要针对Windows系统托盘进行特殊处理。
2.3.1 后台图标
增加托盘概念需要在Qt添加QSystemTrayIcon头文件,代码如下所示。
2.3.2 事件劫持
现在单击运行程序的X按钮,程序将直接退出。因为单击X按钮后程序进入系统托盘,然后接着执行Qt关闭对话框的默认动作,退出程序。
Qt中,处理关闭事件函数名为closeEvent(),在这个函数中“劫持”关闭事件,然后做自己的处理。
注:“关闭”按钮事件被拦截后,需要使用任务管理器将进程关闭。
2.4 菜单管理
程序进入后台运行后,用户对程序的所有操作都只能通过托盘图标进行,因此要对托盘图标设计菜单,实现用户接口功能。
单击和右击托盘图标一般都会出现不同的菜单。右击托盘图标显示系统的功能菜单;单击托盘图标显示系统消息菜单。
2.4.1 鼠标右键动作
鼠标右击会显示功能菜单。在Qt中显示功能菜单需要2个信息,对应头文件分别是<QAction>和<QMenu>。
实现菜单需要先定义QAction变量;然后将QAction变量当做参数生成QMenu变量,这个QMenu变量就是菜单项;最后使用定义的托盘图标变量,使用setContextMenu函数,将生成的菜单加入托盘图标中。
2.4.1 鼠标左键动作
鼠标左键的动作相比于右键更为复杂:第一步是响应鼠标左键的消息,Qt4之前是用SIGNAL(actived(QSystemTrayIcon::ActivationReason))消息,用户要自定义SLOT函数来处理SLOT(myIconActivated(QSystemTrayIcon::ActivationReason))消息;第二步是定义显示的消息内容;第三步是显示消息。
Windows10上的显示结果如下所示:
2.5 单实例管理
对于大多数程序来说,程序运行后,如果用户再次点击运行程序,那么程序会打开第二个实例,比如IE浏览器,每打开一个就会打开一个实例。对于某些程序来说,它不希望用户再次点击程序时执行新的进程,而是希望程序已单实例的形式运行,就比如远程控制与运输系统。
单实例管理的实现技术有很多种。比如,在系统目录下生产一个文件,里面加上配置信息,如APPStarted=yes,程序退出时再改成APPStarted=no。只有在no的情况下才启动进程。但在硬盘上配置文件容易出现错误。
远程传输与控制系统通过在内存中分配一块标识来完成单实例的判断。
3. web网络服务模块
3.1 网络模块类
早期Qt针对多种协议实现了相应的类,包括QtcpSocket,QFtp等,在Qt5之后,Qt将所有网络协议模块封装在一起,形成单独的网络接口,对应于QtNetwork模块中的QNetworkAccessManager类和QNetworkReply类。除非遇到要处理底层网络的情况,不建议再用QtcpSocket类。
QNetworkAccessManager类支持应用程序发送网络请求和接收网络应答。只要创建QNetworkAccessManager对象,并在创建后进行网络发哦送请求的通用配置和设置,如代理和缓存的配置以及相关的其他信号,就可以实现网络通信。
网络通信的实时状态和通信结果可以使用应答信号获取,这个应答信号就是QNetworkReply类。QNetworkReply类包含发送给QNetworkAccessManager请求的所有应答数据,包含一个URL和一些首部信息。QNetworkReply是一个顺序访问的QIODevice,一旦数据从对象中读取出来,那么对象就不再持有这些数据。
QNetworkReply类有几个关键函数用于操作网络通信和获取网络数据:
1)downloadprogress()表示当前网络数据传输数量(不一定准确);
2)finished()在网络通信结束时被触发,不再对应答数据进行更新;
3)read()和readAll()可以读取数据;
4)close()调用后放弃所有应答信息。
//当网络通信完成时,发出信号
connect(pReply, QNetworkReply::finished, this, myFinished);
/*在下载大文件时,需要为用户提供下载进度。
*myDownloadProgress提供qint64 bytesReceived和qint64 bytes