接上一講,即實現下述界面的事件處理代碼;并且按照配置文件初始化界面的內容(三、)
?事件處理的基礎知識,見下述鏈接五、
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函數中進行調用