C++ 入門指南 4.01
單元 31 - 完成版的 encrypt_demo 專案
encrypt_demo 專案 (project) 中包括兩個標頭檔 (header file) 、兩個實作檔,以及一個 QML 介面檔案
enctypt.h
encryptcontroller.h
encrypt.cpp
encrypcontroller.cpp
main.qml
其中 encrypt.h 及 encrypt.cpp 在「軟體開發篇」發展完成, main.qml 在單元 27 - 使用 QML 設計視窗外觀發展,其餘便是實作按鈕的功能,新建、編碼及解碼均已完成,現在就剩下儲存、載入、清除與拷貝了。
encryptcontroller.h 要先加入宣告以下四個 Q_INVOKABLE 成員函數 (member function)
Q_INVOKABLE void save(); Q_INVOKABLE bool load(); Q_INVOKABLE void clear(); Q_INVOKABLE void copy();
save() 用作儲存, load() 用作載入, clear() 用作清除以及 copy() 用作拷貝。
然後換到 encryptcontroller.cpp ,這裡需要先引入必要的標頭檔
#include <QDebug> #include <QFile> #include <QDataStream> #include <QClipboard> #include <QGuiApplication>
QDebug 及 QGuiApplication 都是 Qt 專案需要, QFile 及 QDataStream 為檔案處理相關,至於 QClipboard 則是跟剪貼簿有關。
儲存按鈕的目的是存檔,以下為 save() 的實作
void EncryptController::save() { QFile file("encryptor"); file.open(QIODevice::WriteOnly); QDataStream out(&file); out << s2q(encryptObject->get_code_array()); }
先宣告 QFile 型態 (type) 的變數 (variable) file ,參數 (parameter) 字串 (string) 是檔案名稱 encryptor
QFile file("encryptor");
然後 file 呼叫 open() ,並以 QIODevice::WriteOnly 當參數,這是指 file 將以寫入模式開啟
file.open(QIODevice::WriteOnly);
接下來宣告 QDataStream 的變數 out ,並以 file 的參考 (reference) 當參數
QDataStream out(&file);
out 將會處理寫入的資料串流,這裡使用輸出運算子 (output operator) << 將密碼表寫入檔案 encryptor 中
out << s2q(encryptObject->get_code_array()); }
回到 main.qml ,儲存按鈕就是呼叫 save() ,然後在 display 顯示「已存檔」
Button { width: 50 text: "存檔" onClicked: { controller.save(); display.text = "已存檔" } }
載入按鈕是讀取 encryptor ,然後利用該內容設定 encryptObject 的密碼表
bool EncryptController::load() { QFile file("encryptor"); if (file.open(QIODevice::ReadOnly)) { QDataStream in(&file); QString temp; in >> temp; if (encryptObject == nullptr) { encryptObject = new Encrypt; } encryptObject->set_code_array(q2s(temp)); return true; } else { return false; } }
注意 load() 設計成回傳布林值 (Boolean) ,這叫做布林函數,之所以設計成布林函數 (Boolean function) ,待會在 main.qml 的部分解釋原因
bool EncryptController::load()
進入 load() ,同樣是先建立 QFile 型態的變數 file ,並以檔案名稱字串當參數,然後就是 if-else 的部分,
if (file.open(QIODevice::ReadOnly)) { QDataStream in(&file); QString temp; in >> temp; if (encryptObject == nullptr) { encryptObject = new Encrypt; } encryptObject->set_code_array(q2s(temp)); return true; } else { return false; }
簡單說,檔案預計以讀取模式 QIODevice::ReadOnly 開啟,如果開啟成功就回傳 true ,反之回傳 false ,也就是檔案不存在的話就回傳 false 。
如果 file 成功開啟 encryptor 檔案,接下來就是用 QDataStream 設定輸入串流,並用 QString 型態的變數 temp 接收從檔案讀取的內容
QDataStream in(&file); QString temp; in >> temp;
接下來先檢查 encryptObject 是不是 nullptr ,如果 encryptObject 是 nullptr ,代表使用者還沒按過新建按鈕,因此這裡要先新建 Encrypt 型態的 encryptObject ,然後再將檔案儲存的密碼表設定給 encryptObject
if (encryptObject == nullptr) { encryptObject = new Encrypt; } encryptObject->set_code_array(q2s(temp));
回到 main.qml ,載入按鈕就是呼叫 load() ,如果載入成功回傳 true ,就會在 display 顯示「已載入」,反之就顯示「無法載入」
Button { width: 50 text: "載入" onClicked: { if (controller.load()) { display.text = "已載入" } else { display.text = "無法載入" } } }
由於載入可能會失敗,因此將 load() 設計成布林函數,分成載入成功及失敗兩種情況來處理。
清除按鈕的目的是清空所有欄位,用作清除的 clear() 的實作如下
void EncryptController::clear() { m_userInput = ""; m_encodeResult = ""; m_decodeResult = ""; encryptObject = nullptr; }
清除就是把所有的設定歸零,也就是將相關資料成員 (data member) 設定成空字串或 nullptr ,清除按鈕在 main.qml 的實作如下
Button { width: 50 text: "清除" onClicked: { input.text = "" output.text = "" controller.clear() display.text = "已清除" } }
同樣是輸入、輸出的文字方塊設定為空字串,然後呼叫 clear() ,並在 display 顯示「已清除」。
至於拷貝按鈕則是把編碼 (encoding) 結果拷貝到系統剪貼簿,以下為 copy() 的實作
void EncryptController::copy() { QClipboard *clipboard = QGuiApplication::clipboard(); clipboard->setText(m_encodeResult); }
以上是先建立 QClipboard 型態的變數 clipboard ,注意 clipboard 是指標 (pointer) ,然後 clipboard 利用 setText() 將編碼結果複製到系統剪貼簿,底下繼續看到拷貝按鈕在 main.qml 的實作
Button { width: 50 text: "拷貝" onClicked: { controller.copy() display.text = "已拷貝到系統剪貼簿" } }
就是呼叫 copy() ,然後在 display 顯示「已拷貝到系統剪貼簿」。
來執行測試囉!先按新建,得到下圖的編碼結果,再來存檔
用新的密碼表編碼,然後按下載入
這樣就可以得到同樣的編碼結果
清除就是全部清空
拷貝可以將編碼結果複製貼到其他的軟體
好了, encrypt_demo 專案的所有功能大體完成,下一步是?
中英文術語對照 | |
---|---|
布林值 | Boolean |
布林函數 | Boolean function |
資料成員 | data member |
編碼 | encoding |
標頭檔 | header file |
成員函數 | member function |
輸出運算子 | output operator |
參數 | parameter |
指標 | pointer |
專案 | project |
參考 | reference |
字串 | string |
型態 | type |
變數 | variable |
重點整理 |
---|
1. 存檔及載入需要宣告 QFile 型態的檔案物件開啟檔案,然後用 QDataStream 寫入或讀取檔案內容。 |
2. 布林函數是回傳真假值的函數。 |
3. 清除按鈕是將所有設定歸零,也就是將變數成員或屬性設定為空字串或 nullptr 。 |
4. 拷貝是利用 QClipboard 型態的物件,將字串內容拷貝到系統剪貼簿。 |
問題與討論 |
---|
1. 將功能設計成布林函數有何優點? |
2. 為什麼檔案處理要分成讀取模式跟寫入模式? |
練習 |
---|
1. 承接上一個單元的猜數字遊戲,利用類別設計猜數字遊戲的計算核心,先以 guess.h 規劃軟體規格,至少要有以下的變數成員:儲存答案的 answer 、對的位置與數字的 A 、錯的位置與對的數字的 B ,累計猜測次數的 times 、猜測數字長度的 digit ,函數成員至少要有 setter 及 getter ,累計猜測次數的 AddTimes() ,計算 A 、 B 值的 ABCounter() ,判斷重複數字的 FindNumber() ,以及做檢測的 Test() 。 參考程式碼 |
2. 承上題,利用上一個單元建立答案的方式來實作 setter 與 getter ,可以另外設計 guess_demo.cpp 來測試程式碼是否能正確執行。 參考程式碼 |
3. 承上題,實作將猜測次數加一的 AddTimes() 。 參考程式碼 |
4. 承上題,實作計算 A 、 B 值的 ABCounter() ,注意 A 、 B 值要先歸零,然後用迴圈逐數字比較答案與猜測數字。 參考程式碼 |
5. 承上題,實作判斷是否有重複數字的 FindNumber() ,注意這應該是一個布林函數,用來判斷是否有重複數字。 參考程式碼 |
6. 承上題,實作檢測猜測結果的 Test() ,注意檢測需要是布林函數,猜對回傳 true ,猜錯回傳 false 。 參考程式碼 |
7. 承上題,將寫好的 guess.h 及 guess.cpp 加入到「單元 29 」練習 8 的 guess_demo 中,另外新增 GuessController 類別,請仿造 encryptcontroller.h 設計 guesscontroller.h ,至少需要一個 Q_PROPERTY 接收使用者輸入,也需要 test() 及判斷重複數字的 findNumber() ,如果答案是用字串,同樣需要 s2q() 與 q2s() 。 參考程式碼 |
8. 承上題,仿造 encryptcontroller.cpp 定義相關的 setter 與 getter 。 參考程式碼 |
9. 承上題,定義 test() 及 findNumber() 兩個布林函數。 參考程式碼 |
10. 承上題,實作 guess_demo 專案中 mail.qml 的按鈕實際功能,使之能用 GUI 進行遊戲。 參考程式碼 |
相關教學影片