OPC Client第5讲(wxwidgets):初始界面的事件处理;按照配置文件初始化界面的内容
接上一讲,即实现下述界面的事件处理代码;并且按照配置文件初始化界面的内容(三、)
事件处理的基础知识,见下述链接五、
OPC Client第3讲(wxwidgets):wxFormBuilder;基础框架;事件处理_wxwidgets 界面设计工具有哪些-CSDN博客
一、总体逻辑
1、涉及到事件处理的 控件
上图的每个序号都涉及到一个控件的事件处理,具体见二、
2、没有涉及到事件处理的 控件
上图中剩下的文本控件wxTextCtrl框,包括“选择机型和PLC”的勾选部分。即界面的初始化内容都是通过OnInitConf函数填的,没有涉及到事件处理。
二、按照一、中逻辑 实现 涉及到事件处理的控件
1、“选择机型和PLC”:OnCheckListBox函数
即下图的1 2 3
1>逻辑思考
先看“选择机型和PLC”这部分的实现代码(见上一讲三、2、2>和三、3、)
具体实现此界面的代码,在InitElement函数中,如下图
其中checkListBox、checkListBoxHCPlc 和 checkListBoxBHPlc三个变量都定义在.h中,在构造函数中实现初始化(为了简化代码)
所以,事件处理 涉及 checkListBox、checkListBoxHCPlc 和 checkListBoxBHPlc三个变量。
2>代码实现
1》三个变量分别绑定其事件函数,都是OnCheckListBox函数
- EVT_LISTBOX:用于wxListBox控件。
- 触发条件:当用户在wxListBox中选择一个不同的项时,该事件会被触发。
- EVT_CHECKLISTBOX:用于wxCheckListBox控件。
- 触发条件:当用户勾选或取消勾选wxCheckListBox中的任何一个选项时,该事件会被触发
注意下述链接五、
OPC Client第3讲(wxwidgets):wxFormBuilder;基础框架;事件处理_wxwidgets库-CSDN博客
综上,写出此部分的代码:
如下图,由于三个变量的Id都是wxID_ANY,所以在OnCheckListBox函数中需要先获取当前点击的选项的id【即传入参数wxCommandEvent& event的id】。
再根据判断条件 确认 当前勾选的选项是 哪个变量。
2》选中checkListBox
要求:
- 勾选了checkListBox里的选项后,默认勾选checkListBoxHCPlc里对应的禾川PLC
- 如果取消当前这个checkListBox,那么checkListBoxBHPlc和checkListBoxHCPlc对应的勾选都要取消
- 需要设置,checkListBoxHCPlc 和 checkListBoxBHPlc 两个选项只能勾选其一【在3》4》中实现】
区别:
“选中”指的是通过键盘方向键高亮的项(类似于焦点);而“勾选”才是用户点击复选框打钩的状态。
event.GetSelection()
- 这个方法是从 wxCommandEvent 对象中调用的。
- 它返回的是触发事件的那个特定项的索引(即用户最近一次点击或选择了哪个项)。
- 适用于在事件处理函数中获取用户最近一次操作的具体目标项。
checkListBox->GetSelection()
- 这是从 wxCheckListBox 控件对象本身调用的方法。
- 在 wxWidgets 中,对于 wxListBox 或类似的单选控件,GetSelection() 返回的是当前被选中的单个项的索引。如果没有任何项被选中,则返回值可能是 -1。
- 对于允许多选的控件(如 wxCheckListBox),这个方法通常并不适用于获取所有选中的项,而是用于获取最后一个被选中的项(如果有这样的实现)。
checkListBox->GetSelections(wxArrayInt&)
这是一个从 wxListBox 继承来的函数。
它返回的是 当前“选中”(高亮)的项索引列表
checkListBox->GetCheckedItems(wxArrayInt&)
获取所有被选中(勾选)项的索引列表。
这些方法直接从控件本身调用,可以用来查询当前控件的状态,而不仅仅是最近一次用户交互的结果。
3》选中checkListBoxHCPlc
相关基础知识见2》
要求:
- 需要设置,checkListBoxHCPlc 和 checkListBoxBHPlc 两个选项只能勾选其一
- 【新增】点击checkListBoxHCPlc时,如果checkListBox没有勾选,则自动勾选上
4》选中checkListBoxBHPlc
和3》完全相同,代码只是需要把3》中的HC与BH互换
2、“选择文件(右键)”:直接写在InitElement函数中了,在实现控件后直接用Bind的Lambda匿名方法
即下图的4 5
先看“选择文件(右键)”这部分的实现代码(见上一讲四、)
1>“标准对照(必需)”处的事件处理
用到的是下述链接六、2、的文件和目录对话框:wxFileDialog
OPC Client第4讲(wxwidgets):窗口布局基础知识;界面练习-CSDN博客
1》实现代码:创建wxFileDialog的参数
2》源代码累赘:不用再次调用event.Skip!
《1》对于事件处理机制:加上event.Skip()没用
先理解第3讲的事件处理运行过程五、1、3>
OPC Client第3讲(wxwidgets):wxFormBuilder;基础框架;事件处理-CSDN博客
上图代码中的理解:
- Lambda 表达式 没有父类,但是有父窗口[this],即InitFrame。【为什么?见上述链接五、1、3>的举例】
- 当加上event.Skip()后,wxMouseEvent&事件不属于wxCommand事件,所以这个事件不会向上传递到父窗口InitFrame里面。加上event.Skip()没用。
《2》wxTextCtrl还有一个wxMouseEvent类型的wxEVT_RIGHT_DOWN的默认行为:一旦你绑定了自己的事件处理函数,框架就不再自动执行默认行为
如何知道哪些事件有默认行为?——查阅官方文档
-
例如,
wxTextCtrl
对右键点击事件默认会显示一个上下文菜单,提供复制、粘贴等功能。 -
2>“批量连接”的事件处理
完全同1>
3、“确认”:OnConfirm函数【先看三、此处创建了MainFrame.h/.cpp】
即下图的6
1>为“确认”按钮绑定事件
2>事件处理函数OnConfirm的总体逻辑
1》更新三、中的config(额外创建变量config1,不改变config的值)
config1的Key值 等于 三、的Configuration.json文件里的Key值
2》把新的配置信息config1写入配置文件Configuration.json
《1》在my_lnit.h中 创建“写入配置文件WriteConfigurations”函数
《2》在InitFrame.cpp的OnConfirm函数中进行使用
3》关闭 InitFrame 对应的界面,打开 MainFrame界面,把1》的config1传给 MainFrame
《1》创建MainFrame.h和MainFrame.cpp
《2》在InitFrame.cpp的OnConfirm函数中进行使用
《3》在进入MainFrame后,即实现下图界面,具体内容见下一讲
4、“取消”:OnCancel函数
即下图的7
三、按照一、中逻辑 实现 没有涉及到事件处理的控件
上图中剩下的文本控件wxTextCtrl框,包括“选择机型和PLC”的勾选部分。即界面的初始化内容都是通过OnInitConf函数填的,没有涉及到事件处理。
1、OnInitConf函数作用:读取配置文件,初始化各个wxTextCtrl控件的默认值。
2、如何读取1、的配置文件?——新建my_lnit.h 和 my_lnit.cpp
仿照源代码,另一种更简便的写法如下
如上图,创建了读取配置文件的GetConfigurations函数。
在这个函数中,需要打开Configuration.json文件(下述2>),获取里面的数据,到InitFrame.cpp的config变量中。
1>给各个.h代码加上命名空间 + 把.h和.cpp文件放在对应文件夹中
1》新建include文件夹, 所有的命名空间都放在include文件夹下了
打开上图的include文件夹,再创建下图的五个文件夹。
下图的一个文件夹就是一个命名空间(主要用的是UaApp和uasdk),分别代表:缓冲区,json库,主项目相关,sdk相关
2》往1》的命名空间中移入.h文件,确保.cpp代码中#include <uasdk/my_Init.h>可以找到
在代码的“项目”->“属性”中,添加“附加包含目录的路径”
这里只是导入.h文件的路径,在下述链接四、3、
OPC Client第2讲:Visual Studio的C/C++使用教程;C++程序运行过程的各个文件;如何导入库?{静/动态库;更便捷的方法添加第三方库:使用属性表【配置使用wxwidgets】}_visual studio 运行c++-CSDN博客
如此,在.cpp中能引用uasdk文件夹下的.h文件了
3》在2》后出现问题:代码中头文件下的.h文件打不开了
即把.h和.cpp放在文件夹,怎么找到其的文件路径,并打开其代码
解决办法:
使用“添加现有项”并选择适当的过滤器
-
右键点击项目中的文件夹(”头文件“),然后选择“添加 -> 现有项...”。
-
导航到
D:\VisualStudio\OPC_Data\test\include
目录。 -
选择需要添加的
.h
文件。
同理,新建src文件夹,里面放入所有的.cpp文件。按照上述解决办法重新导入各个.cpp文件
4》【非必需】在每个.h代码中加上其属于哪个命名空间
这样不用每次在include文件夹里找
5》同理,修改InitFrame.h和InitFrame.cpp的文件位置和命名空间
main.cpp也要修改。
6》最后,所有的.h和.cpp文件都被放在了对应文件夹中。代码中也加入了每个.h应该放的命名空间
2>实现my_Init.h里的GetConfigurations函数
1》在文件夹中创建 Configuration.json 格式的文件,里面存放着初始的配置信息
注意,师傅发的源代码文件压缩包,这个Configuration.json文件是已经有了的,不用自己再创建
//所有的文件路径都是Debug目录下开始的,即exe文件所在的文件夹
再添加两个表格.xlsx在路径里面,因为在 Configuration.json文件 中需要这两个表格
Configuration.json文件里面存放着初始的配置信息
《1》注意:【自己写的Configuration.json文件 相比 师傅的做了修改】
1)师傅的——没必要用.json格式的文件
师傅的这个.json文件里存放的数据不是json格式,获取里面的数据也没有用到json包里的简便函数!
师傅用2》的.json库只是为了:在my_lnit.h的GetConfigurations函数中,声明的config变量 和 返回的数据是json格式。
需要读取两个.xlsx文件里的值
2)自己修改后的
就是改成json格式,利用2》的nlohmann json库里的函数 获取/修改数据
2》导入json库(GetConfigurations函数中,声明的config变量 和 返回的数据是json格式)
在源代码中选择了法2:
c++中nlohmann json的基本使用教程 - LiuYanYGZ - 博客园
不知道为什么,上述命令行报错说找不到json.hpp文件。那么换种方法,见下述链接
windows下使用#include <nlohmann/json.hpp>_nlohmann windows-CSDN博客
为了清楚知道nlohmann json有哪些函数可以用,把.hpp文件添加到项目目录中
《1》什么是hpp文件?
.hpp
更加明确地表示这是一个C++头文件,而 .h
则可能是C或C++的头文件。
然而,这并不是一个严格的规定,更多是一个社区内的约定俗成。在实际开发中,你可能会看到两者都被广泛使用。
《2》在代码中要用到此库的地方#include
3》实现GetConfigurations函数【见1》,自己写的对比师傅的优化了】
在这个函数中,需要打开Configuration.json文件(上述1》,文件操作见下述链接),利用nlohmann json(上述2》,使用见下述链接)里的函数获取里面的数据,到InitFrame.cpp的config变量中。
C++核心编程 | welcome to here
GitHub - nlohmann/json: JSON for Modern C++
4》上图中省略了 记录错误 的部分,见下一讲:创建logger.h和helper_t.h(3、中创建)里的gettime函数
3、实现InitFrame.h的OnInitConf函数
剩下的文本控件wxTextCtrl框,包括“选择机型和PLC”的勾选部分。即界面的初始化内容都是通过OnInitConf函数填的,没有涉及到事件处理。
在InitFrame构造函数中加入OnInitConf函数,如下图
实现OnInitConf函数。下图中省略了最外面的第二、三块的变量,其完全类似第一块
1>为什么不能直接odbcNameText->SetValue(config["clientname"]);
因为config["clientname"] 返回的是 json 类型的对象,不能直接传给wxTextctrl的SetValue。
- wxTextctrl的SetValue接受的是wxString 或者 std::string,但不接受 json 类型
- 为什么可以接受std::string?——存在隐式转换
2>上图红框:“选择机型和PLC”的勾选部分【创建helper_t.h】
split函数得自己实现!
- 在C++的标准库中确实没有直接名为
split
的函数,不过可以通过其他标准库功能来实现字符串分割的功能。常用的方法包括使用std::stringstream
(位于<sstream>
头文件中)
1》注意:getline函数的作用,以及分隔符开头/结尾、连续的分隔符:都会变成空字符串
下面开始在InitFrame.cpp的OnInitConf函数中进行调用