示教器二次开发项目教程
介绍
为了方便用户快速上手 Qt 的一些简单操作,之后我们使用一个点胶机项目源码作为例子,这是一个简单的项目,里面涉及到的主要是一些通讯操作,实际逻辑功能很少,作为一个简单的入门项目还是非常不错的。
· 使用二次开发制作一个用户管理界面
账户管理界面在工艺中非常常见,通过给不同的账户赋予不同的权限,我们就能更好的管理用户在使用产品的时候避免误操作,在本章中,我们使用二次开发提供的接口来制作一个简单的通用的账户管理界面。
以点胶机项目为例,首先我们双击 Forms 下的 ui 文件来进入 QT 设计师界面。
账户界面主要由以下几个控件构成:\ Label 标签控件:常用于添加文字、标题、注释等,根据使用方法也能做成指示灯等不同的形式。\ ComboBox: 一个可以下拉的控件,里面可以放任意数量的可选项,可以通过接口 setCurrentIndex() 进行切换,在本例中,里面包含了“操作员”,“技术员”,“管理员”这三个元素。\ Line Edit 输入框:常用于读取用户的输入。\ Stacked Widget :可以翻页的窗口界面,可以通过 setCurrentIndex()接口进行页面跳转。
例如右上角的操作员按钮的槽函数
当我们点击该按钮时,通过 getAccount()这个接口来获取当前的用户,然后按照不同的情况给用户类型跳转来切换当前账户的显示,最后将 Stacked Widget 翻页到用户管理的页面。
QComboBox 的触发信号中比较常用的有以下两个:
其中参数为 QString 的代表控件内元素的名字,而 int 代表第几个元素。我们以 QString 类型为例子来看看它的槽函数:
当我们进行切换账户动作时候,触发该槽函数通过对比当前账户和切换后的账户,我们就能实现高权限用户在向低权限用户切换的时候隐藏密码的输入框,反之则显示密码的输入框。
到这一步,我们已经有一个可以切换账户的下拉栏了,下一步就是制作密码输入框,输入密码的部分我们使用 Line Edit 控件,然后在确认按钮的槽函数里读取密码的值,并和预设的密码进行比较来判断密码是否正确。Line Edit 我们预设了两种提升:DigitalLineEdit 和 LineEditWidget,前者附带一个数字小键盘,后者附带一个标准键盘。
此处用到了切换用户的接口:
我们将下拉栏的元素,即操作员/技术员/管理员和 Line Edit 上的密码作为参数传递给这个接口,同时判断密码是否正确,若正确则打出切换成功字样,同时根据当前的用户权限等级来修改一些控件的操作权限,如下所示:
setEnabled(bool) 是 QT 自带的接口,用于修改控件的可操作性。\ 至此,我们已经完成了用户界面的制作。
· 参数的传输,储存和读取
每一种工艺都包含了很多种参数,比如工件的尺寸,机器人原点的位置,机器人运动时候的速度等等。我们不可能每次使用机器人工艺的时候都重新输入一遍这些参数,因此我们就需要把这些参数打包成工艺号,这样只要通过切换工艺号就能快速实现不同规格的参数切换。\ 下图是一个参数界面,这里包含了点胶工艺中的各种参数,我们要做的就是让用户在示教器上输入了这些参数之后能够保存的控制器端并且生成一个文件用于记录这些参数,在下次使用的时候直接读取这些参数。
在开始之前,我们先简单的学习一个数据交换格式,Json JSON(JavaScript Object Notation, JS 对象简谱) 是一种轻量级的数据交换格式。它基于 ECMAScript (欧洲计算机协会制定的 js 规范)的一个子集,采用完全独立于编程语言的文本格式来存储和表示数据。简洁和清晰的层次结构使得 JSON 成为理想的数据交换语言。 易于人阅读和编写,同时也易于机器解析和生成,并有效地提升网络传输效率。(来自百度百科)
我们大部分的数据都是以 Json 格式进行传输的,比如下面的槽函数:
Json::Value root; //创建一个名为 root 的 Json 对象\ Json::FastWriter wt; //创建一个名为 wt 的 Json 快速写入对象,用法是:\ Json::FastWriter.write(Json::Value) root["act"] = "get_current_pos"; //在 root 对象中建立一个名为 act 的元素,值为字符串 "get_current_pos"
Json 的优势在于可以非常方便清晰的给不同的参数命名分类,并实现打包传输,同时它还有很多实用的接口,这个在之后我们用到的时候再一一介绍。
· 示教器端发送参数
和前面中通讯的方法一样,本例中我们也从示教器端发送开始。\ 首先我们在头文件类对象的公有定义中添加如下格式的 QList 对象,QList 的使用方法和 C++中的 vector 容器类似,用于存放控件对象,当我们需要批量操作同类型的控件时,用 QList 进行管理会比较方便。
在 cpp 中我们把与参数页面相关的控件加入到对应的 QList 中:
并在下方将它们的默认状态设置为不可修改的状态:
之后我们再来看修改按钮的槽函数部分。\ 当按钮名称为修改的时候,我们将与参数有关的控件修改为可操作,同时将按钮名称改成保存。
而当按钮名称为保存的时候,我们先对用户输入的参数的合理性进行判断,如果用户输入的参数不在取值范围内,则报错返回,具体实现形式如下:
最后一部分则是将参数以 Json 格式发送给控制器,并且在发送完成后将所有的控件再次修改为不可操作状态,将保存按钮重新修改为修改按钮。
需要注意的是,在 QT 中,所有控件上的字符都是以 QString 形式储存的,因此我们在传输前需要根据其参数格式进行 toInt() toDouble() toStdString 的转换,若参数格式不对会导致程序崩溃,这一点也是非常常见的示教器程序崩溃的原因。\ 给参数填入任意数值后保存,可以看到下面输出了我们发送的以 Json 格式发送的字符串通讯,这代表我们的参数已经发送给控制器了,到这一步,我们参数通讯的第一部分完成。
按照发 - 收 - 发 - 收的顺序,此处推荐先看控制器的参数接收部分《控制器二次开发例程教学》,这样方便理解。
· 示教器端接收参数
下一步我们来写示教器的接收部分,widgetmanager.cpp 中已经预留了接收接口,我们多做一步把它作为跳板把参数传到我们的 settingparawidget.cpp 中:
只需要用协议号作为 if 的判断条件,就能清晰地区分使用各协议号,需要注意的是不同的元件在接收参数的时候显示的方法是不一样的,在此举几个常用的元件使用方法:
ComboBox -> setCurrentIndex
Laber -> setText
LineEdit -> setText
在 QT 中,所有显示出来的字符都是 QString 形式,不能直接用 int、double 或者 string 等格式,更不能直接把 Json::Value 对象当参数,写错格式转换是一个常见的崩溃原因。常用的 QString 转换方法有 QString::number(int/double)数字型 (下图中的 'f',2 代表浮点型,保留两位小数) Qstring::fromStdString(string)字符型。
其中后五个 IO 的 setCheck 是自定义的元件,具体效果可以自己使用一下。\ 最后只要在跳转到参数界面的按钮的槽函数里加上获取参数的指令就完成了。
到这一步,参数的传递、储存和读取我们都已经完成,之后就可以自由的在示教器端修改控制器的参数,并且实时生效。
小练习
1、尝试用 demo 自己写一个参数储存功能,最好多用几种元件。\ 2、思考一下在只用一个协议号的情况下,怎么利用 Json 的节点实现多种功能。\ 3、可以自行看一看 SwitchButton 的用法,自己做一个小按钮控件来控制某些布尔型的参数,比如某些 IO 开关。
· 制作一个点位记录界面
在上一节里面我们已经学会了如何传输参数和保存,这一节我们制作一个储存点位的表格,要求有点位增加、删除、记录当前点位、运动到该点四个功能,点位需要包含点位坐标、形态和轨迹。\ 主要用的控件是 QTabelWidget,这个控件里面的每一个小格里面称为一个 item,每一个 item 的属性和 Qlabel 控件差不多,也可以把 item 替换为其他控件。
我们首先看增加点位的槽函数:
前半部分是自上而下索引到第一行空的 item 项目,同时判断是否达到了我们限制的最大的 20 个点位数。后半部分给这一行的每一个 item 添加值,并把其中两个 item 替换为 QComboBox 控件。
之后再看删除点位部分:
删除点位的槽函数被分为三个部分,第一部分是统计当前已有的点位,第二部分是把选中需要删除的点位之后的所有行向上移动一格,第三部分是删掉最后一个点位。很多表格类需要按顺序排列的界面都可以沿用这个思路进行删除的操作,即从选中行开始之后整体替换前一行,再把最后一行删除。
接下来看覆盖点位的槽函数:
通过前面的学习,这个槽函数很好懂,就是给控制器发了一段 recover_pos 的字符串,为了防止用户短时间内多次点击导致通讯阻塞,我还在这里加了一段点击后禁用此按钮,只有在控制器回复了之后才能再次点击此按钮。再看看控制器部分,控制器部分主要负责在收到请求后把当前机器人点位发送给示教器。
收到控制器的回信之后,和前几章一样,我们只需要把收到的数据现在示教器上即可。
“运动至此”功能也类似,将选中行的点位发送给控制器,再由控制器控制机器人即可。
最后保存的时候,把 QTabelWidget 上所有的参数转为 Json 格式传给控制器记录即可。
对应的控制器端实现,请参考《控制器二次开发例程教学》
小练习
制作一个 IO 检测界面:\ 通过上面的学习,我们已经基本掌握了控制器和示教器的通讯方法,接下来就做一点实践内容,尝试制作一个实时读取 IO 信号并且显示在示教器界面功能。\ 要实现实时读取 IO 信号,我们肯定不能用按钮来获取 IO 信号,不然每次刷新都得点击一次按钮太麻烦了,完全做不到实时,这里我们就要用到 QT 的一个信号函数 QTimer:
这里_pGetIoInputStatusTimer 是一个 QTimer 对象,这三句的效果是创建一个 400ms 的计时器,并且绑定到我们的槽函数上。QTimer 使用 start()和 stop()来控制,这样我们就能做成进入 IO 显示页面的时候开启查询 IO,退出页面的时候关闭查询 IO,节约内存。控制器端我们使用接口 NRC_ReadDigIn(int port)来读取对应的 IO 信号再传回给示教器显示即可,具体实现方式可以自己尝试下,原理和最开始的 hello 通讯大同小异。