計算器介面及其模組介紹如下:
點開設定按鈕,有三個選項,功能如下:
我們點選檢視歷史,可以看到效果:
計算器整體功能簡單,就是基本的四則運算,加上儲存歷史記錄的功能,可以用來學習QT和SQLite。
我們會使用三個軟體,分別為VS+QT(提供介面UI)+SQLite(提供資料庫)。
不會QT和SQLite沒關係。這裡不涉及很深入的知識,手把手教學,包教包會。
下面就從設定開發環境開始。
這裡有VS的下載連結,自行取用:VS_Community下載
雙擊VS_Community.exe,傻瓜式安裝(我們只需要C++開發環境),不再詳述。
這裡有Qt的離線安裝包網址,自行取用:Qt離線安裝包下載
雙擊開啟qt-opensource-windows-x86-5.12-8.exe,出現歡迎介面,點 next:
登入Qt賬戶:
直接下一步,然後選擇資料夾,這個自由選擇,只要知道自己裝在哪就好了。
選擇安裝的元件,如下:
一直下一步,直至安裝完成。
開啟VS,找到標題列的 擴充套件,點選子選項 管理擴充套件:
在管理擴充套件中安裝Qt VS Tools:
下載安裝完成後,關閉VS的所有視窗,會自動應用更改,你只需要最後點選 「modify」 按鈕確認即可。
注意: 再次開啟VS有可能會出現下圖錯誤,這時兩種嘗試方法:
之後設定環境變數,WIN+Q組合鍵開啟搜尋,鍵入 「編輯系統環境變數」 ,選中開啟:
開啟VS,找到依次點選:擴充套件 -> Qt VS Tools -> Qt Options,VS會自動為你選中QT的msvc,如果不報錯,設定完成。
注意:如果出現報錯,資訊顯示如:The following error occured:These Qt version are inaccessible
,那麼需要以下操作:
重新開啟 Qt Options,進行下面操作:
刪除原先的Qt Version,只保留我們新新增的,然後將其設為預設:
不報錯,就設定完成了。
這裡提供SQLite的元件下載地址,大家自行取用:SQLite下載
將兩個壓縮包放到合適的位置,分別解壓即可。
設定環境變數,仍舊是在Path裡面,新增SQLite的路徑:
按下 WIN+Q 組合鍵開啟搜尋,輸出 「cmd」 ,然後回車,開啟命令列,輸入sqlite3,回車:
開啟VS,點選建立新專案,找到 Qt Empty Application 專案,點選之後進入下一步:
設定專案名稱和位置大家自選就好了,如下:
設定QT工程,一路預設就好了:
現在編譯程式會產生如下的報錯,因為我們用的MSVC2017,版本落後,需要升級:
我們需要按照提示,重定向目標工具集,首先右擊解決方案,找到重定向目標解決方案,單擊即可:
出現重定向專案視窗,確定即可:
再次生成解決方案即可成功。
在Source Files裡面新增main.cpp:
輸入以下程式碼:
//引入Qt視窗應用程式
#include <QtWidgets/QApplication>
int main(int argc, char* argv[]) {
// 開啟Qt應用環境
QApplication a(argc, argv);
// 結束Qt應用環境
return a.exec();
}
這裡只有Qt的基本應用環境的開啟和關閉,沒有生成任何介面。
我們需要為專案新增QT的模組,否則連線會出錯,如下:
首先右鍵單擊專案,找到 屬性,單擊,進入屬性設定介面:
然後在屬性設定介面進行如下操作,進入模組選擇介面:
選擇 Core GUI SQL Widget 四個模組,點選Finish即可:
點選確定,再次編譯執行,通過。
右鍵單擊專案,遊標移到新增,點選Add Qt Class,如圖:
新增一個名為CalculatorMine的類,如下:
設定類的屬性:
為建構函式新增預設引數 Q_NULLPTR,否則編譯會報錯:
我們已經有了計算器類,那麼就可以在main函數中新增它的物件。在main中新增以下程式碼:
// 建立一個計算器物件
CalculatorMine calculatorMine;
// 執行計算器物件的介面
calculatorMine.show();
新增之後的main函數如下:
執行程式,得到基本的視窗,效果如下:
在資原始檔夾中新增UI介面,首先右擊 Resource Files,遊標移到 新增,單擊 新建項,如下:
然後根據提示進行設定:
Qt設計師介面會自動開啟,或者你雙擊Resource Files下的CalculatorMine.ui也可以開啟,如下:
這個時候,你還查詢不到ui介面的標頭檔案,需要將ui檔案編譯之後得到,如下操作:
編譯完成之後,在外部依賴項中有可能可以找到 ui_CalculatorMine.h 檔案,找不到沒事,不影響。
QT預設的視窗名稱和標題為mianWindow,我們可以改為自己想要的:
重新編譯CalculatorMine.ui檔案。一般來說,UI介面的更改在VS中重新編譯.ui檔案即可生效,如果不行,重新啟動VS即可。
有了UI類,我們就可以範例化計算器的UI了,在CalculatorMine.h中新增的程式碼如下:
下一步我們需要初始化物件,並展示其UI。更改CalculatorMine.cpp檔案:
我們執行程式,可以看到效果:
面板功能介紹如下:
滑鼠左鍵點選Vertical Layout不放,將其拖放到UI介面效果欄中,如下:
調整UI介面和水平佈局,使其大小合適(調整方式就是拖動邊緣),效果如下:
先新增一個Text Browser進入上一個水平佈局中,可以看到其佔據了所有的介面:
我們應該調整其大小,令其佔據空間合適,這時候就要用到屬性編輯器,操作如下:
以同樣的方式編輯其顯示字型的大小,把裡面顯示的字型調大一點,看著舒服:
拖一個水平佈局進去,效果如下:
在第二個水平佈局中拖入5個垂直佈局,可以採用如下方式:
在每個垂直佈局中分別拖入4個按鈕(Push Button),效果如下:
然後我們修改按鈕大小和按鈕字型大小,ctrl+點選 逐個選中所有按鈕,用屬性編輯器:
雙擊按鈕可以編輯按鈕的顯示文字,編輯成如下的效果:
VS編輯控制元件都需要其名稱,為了方便使用,這裡給所有的按鈕都對應一個名字,雙擊其物件即可編輯,效果如下:
新增選單,雙擊 「在這裡輸入」 ,然後輸入 「設定」 ,回車即可,如下:
點選設定,雙擊 「在這裡輸入」 ,然後輸入 「檢視歷史」 ,回車即可,如下:
重複新增 「關閉歷史」 ,「清除歷史」。最終效果如下:
**注意:**如果不能輸入漢字,可以從其他地方複製過來。
下一步仍舊是修改物件名稱,便於使用,修改後的結果如下:
首先,使用Ctrl+S儲存對介面的修改,整個介面就算設計完成了。
然後返回VS,編譯CalculatorMine.ui。
最後編譯執行專案,效果如下:
這裡首先為計算器類新增我們用到的成員變數,在CalculatorMine.h的類中新增如下程式碼:
private:
// 新增一個計算器UI的範例,UI介面的名稱空間為Ui
Ui::CalculatorMineWindow* ui;
// 定義數位按鈕組(numButtonGroup)和操作符按鈕組(operatorButtonGroup)。按鈕組內的按鈕都是相似的,可以共用一個槽函數
QButtonGroup* numButtonGroup;
QButtonGroup* operatorButtonGroup;
// 定義儲存操作符(operatorChar)和運算元(storedNum)以及運算結果(resultNum)的變數
QChar operatorChar;
double storedNum;
double resultNum;
// 定義標誌位,isOperatorClicked標記是否已經點選了操作符,isStoredNum標記是否有一個已經儲存了一個數位
bool isOperatorClicked;
bool isStoredNum;
// 定義輸入數位限制,輸入數位的數量不能超過numLimit
const int numLimit = 16;
註釋應該比較清楚,新增進去的效果如下:
在這裡,我們用到了QButtonGroup這個類,所以需要將其標頭檔案包括一下,輸入以下程式碼:
#include <QButtonGroup>
在檔案中的效果如下:
之後我們需要在CalculatorMine.cpp中為成員變數作初始化,在CalculatorMine類別建構函式中新增以下程式碼:
// 初始化儲存變數和標誌位變數
isOperatorClicked = false;
isStoredNum = false;
storedNum = 0;
resultNum = 0;
// 初始化按鈕組
numButtonGroup = new QButtonGroup;
operatorButtonGroup = new QButtonGroup;
// 為數位按鈕組新增按鈕: 0 1 2 3 4 5 6 7 8 9
numButtonGroup->addButton(ui->pushButton1, 1);
numButtonGroup->addButton(ui->pushButton2, 2);
numButtonGroup->addButton(ui->pushButton3, 3);
numButtonGroup->addButton(ui->pushButton4, 4);
numButtonGroup->addButton(ui->pushButton5, 5);
numButtonGroup->addButton(ui->pushButton6, 6);
numButtonGroup->addButton(ui->pushButton7, 7);
numButtonGroup->addButton(ui->pushButton8, 8);
numButtonGroup->addButton(ui->pushButton9, 9);
numButtonGroup->addButton(ui->pushButton0, 0);
// 為操作符按鈕組新增按鈕:+ - * / =
operatorButtonGroup->addButton(ui->pushButtonAdd, 1);
operatorButtonGroup->addButton(ui->pushButtonSub, 2);
operatorButtonGroup->addButton(ui->pushButtonMul, 3);
operatorButtonGroup->addButton(ui->pushButtonDiv, 4);
operatorButtonGroup->addButton(ui->pushButtonEqual, 5);
註釋是比較清楚的,其在VS中的效果如下:
我們已經有了各種控制元件和所有需要的成員變數,下一步就是定義槽函數,執行按鈕功能。
在CalculatorMine.h檔案的CalculatorMine的類中新增以下程式碼:
// 定義一系列槽函數(與控制元件的訊號連線),當點選計算器的某個鍵時,執行。
private slots:
// 當數位按鈕組中的某個鍵被點選時呼叫,用來更新textBrowser等。
void NumButtonClicked(QAbstractButton*);
// 當操作符按鈕組中的某個鍵被點選時呼叫,用來更新textBrower和執行運算等。
void OperatorButtonClicked(QAbstractButton*);
// 當C按鈕被點選時呼叫,用來清空textBrowser和各種變數。
void PushButtonCClicked();
// 當Del按鈕被點選時呼叫,用來刪除一個輸入數位。
void PushButtonDelClicked();
// 當%按鈕被點選時呼叫,用來將運算元除100。
void PushButonPercentClicked();
// 當+/-按鈕被點選時呼叫,用來將運算元取負。
void PushButtonReverseClicked();
// 當=按鈕被點選時呼叫,用來計算和顯示結果。
void PushButtonEqualClicked();
// 當Point按鈕被點選時呼叫,用來增加小數點。
void PushButtonPointClicked();
// 當檢視歷史被點選時呼叫,用來和資料庫通訊,獲得歷史記錄並顯示。
void SearchHistory();
// 用來清空歷史顯示
void CloseHistory();
// 用來清空資料庫和顯示。
void ClearHistory();
private:
// 用來執行運算(在操作符被點選時呼叫),並與資料庫通訊,儲存資料
void CalculateResult();
這裡的註釋也是比較清楚的,看一下在VS中的效果:
函數宣告之後再CalculatorMine.cpp中實現,在檔案末尾新增以下程式碼:
void CalculatorMine::NumButtonClicked(QAbstractButton* numButton) {
}
void CalculatorMine::OperatorButtonClicked(QAbstractButton* operatorButton) {
}
void CalculatorMine::CalculateResult() {
}
void CalculatorMine::PushButtonCClicked() {
}
void CalculatorMine::PushButtonDelClicked() {
}
void CalculatorMine::PushButonPercentClicked() {
}
void CalculatorMine::PushButtonReverseClicked() {
}
void CalculatorMine::PushButtonEqualClicked() {
}
void CalculatorMine::PushButtonPointClicked() {
}
void CalculatorMine::SearchHistory() {
}
void CalculatorMine::CloseHistory() {
}
void CalculatorMine::ClearHistory() {
}
這不需要多說,就是一個函數實現的框架,在VS中的效果如下:
最後,我們需要連線按鈕訊號和我們定義的槽函數,在Calculator類別建構函式中新增以下程式碼:
// 連線按鈕組和按鈕組的槽函數
numButtonGroup->connect(numButtonGroup, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(NumButtonClicked(QAbstractButton*)));
operatorButtonGroup->connect(operatorButtonGroup, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(OperatorButtonClicked(QAbstractButton*)));
// 連線普通按鈕和他們對應的槽函數
connect(ui->pushButtonC, SIGNAL(clicked()), this, SLOT(PushButtonCClicked()));
connect(ui->pushButtonDel, SIGNAL(clicked()), this, SLOT(PushButtonDelClicked()));
connect(ui->pushButtonPercent, SIGNAL(clicked()), this, SLOT(PushButonPercentClicked()));
connect(ui->pushButtonReverse, SIGNAL(clicked()), this, SLOT(PushButtonReverseClicked()));
connect(ui->pushButtonEqual, SIGNAL(clicked()), this, SLOT(PushButtonEqualClicked()));
connect(ui->pushButtonPoint, SIGNAL(clicked()), this, SLOT(PushButtonPointClicked()));
// 連線選單項和他們對應的槽函數
connect(ui->actionSearchHistory, SIGNAL(triggered()), this, SLOT(SearchHistory()));
connect(ui->actionCloseHistory, SIGNAL(triggered()), this, SLOT(CloseHistory()));
connect(ui->actionClearHistory, SIGNAL(triggered()), this, SLOT(ClearHistory()));
就是一個函數connect,用來連線訊號和槽,VS中效果如下:
這裡用到了資料庫的操作,因此要引入資料庫的庫。在CalculatorMine.h中新增以下程式碼:
#include <QtSql>
在VS中的效果如下:
下面開始運算元據庫了,在CalculatorMine的建構函式中(連線選單項程式碼之前)新增以下程式碼:
// 設定與資料庫的連線。 資料庫的型別為「QSQLITE」,即SQLite; 連線的名稱為「sqliteMine」(隨意設定);
// 連線資料庫的主機地址為:127.0.0.1,即迴環地址(自己);要操作的資料庫名稱為 「HistoryData.db」(隨意設定);
// 連線的使用者為root使用者,使用預設密碼123456
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "sqliteMine");
db.setHostName("127,0,0,1");
db.setDatabaseName("HistoryData.db");
db.setUserName("root");
db.setPassword("123456");
if (!db.open()) {
ui->textBrowser->setText("can't open database");
}
註釋挺清楚的,程式碼在VS中的效果如下:
所有的準備工作均已完成,下面我們就要實現按鈕的功能了。
我們點選一個數位按鈕時,呼叫NumGroupClicked函數,因此在改函數中新增程式碼:
// 用textShow接收顯示器上的顯示文字,用來執行判斷等
QString textShow = ui->textBrowser->toPlainText();
// 如果上一個點選的是操作符按鈕,那麼就代表要輸入第二個運算元了,此時清空顯示,操作符標誌置否。
if (isOperatorClicked) {
textShow.clear();
isOperatorClicked = false;
}
// 如果輸入的數位數量過多,那麼就不能輸入了,直接返回
if (textShow.length() >= numLimit) {
return;
}
// 為textShow追加按鈕上的文字(0/1/2/3/4/5/6/7/8/9)
textShow.append(numButton->text());
// 將textShow的文字顯示到計算器顯示器上
ui->textBrowser->setText(textShow);
程式碼註釋比較清楚,在VS中顯示其效果:
我們執行程式,在計算器的按鈕面板上點選數位按鈕,發現會出現相應的數位:
在OperatorButtonClicked函數中新增以下程式碼:
// 如果上一個點選的也是操作符,那麼替換掉,將操作符儲存在operatorChar中。
if (isOperatorClicked) {
operatorChar = operatorButton->text().at(0);
}
else {
// 如果已經儲存的有數位,那麼證明輸入了兩個數位,可以運算了,進行運算。
if (isStoredNum) {
CalculateResult();
}
else {
// 如果沒存數位,證明是第一次點選操作符,那麼把顯示屏上的數位存起來,這是第一個運算元
QString textShow = ui->textBrowser->toPlainText();
storedNum = textShow.toDouble();
isStoredNum = true;
}
// 操作符標誌位設定為真,儲存操作符
isOperatorClicked = true;
operatorChar = operatorButton->text().at(0);
}
註釋挺清楚的,就不多解釋了。
CalculateResult不是槽函數,是點選操作符需要呼叫的函數,新增如下程式碼:
// 用textShow記錄顯示器介面的數位。
QString textShow = ui->textBrowser->toPlainText();
// 第一個運算元是儲存的運算元,第二個操作時為顯示屏上顯示的數位。
double previousNum = storedNum;
double presentNum = textShow.toDouble();
// 根據操作符(+/-/*/÷)的不同,對兩個運算元執行不同的運算,儲存到resultNum中。
if (operatorChar == '+') {
resultNum = previousNum + presentNum;
}
else if (operatorChar == '-') {
resultNum = previousNum - presentNum;
}
else if (operatorChar == '*') {
resultNum = previousNum * presentNum;
}
else if (operatorChar == '/') {
resultNum = previousNum / presentNum;
}
// 計算的結果可以作為下一次計算的第一個運算元,便於連續運算。
storedNum = resultNum;
// 將數位轉為QString型別的字串,限制其串長度,賦值給texxtShow,顯示在顯示屏上。
textShow = QString::number(resultNum, 'g', numLimit);
ui->textBrowser->setText(textShow);
// 使用連線 "sqliteMine" 連入資料庫,這樣就可以存取我們建立的資料庫HistoryData了
QSqlDatabase db = QSqlDatabase::database("sqliteMine");
// 使用db初始化一個資料庫操作的物件,用來對資料庫進行管理
QSqlQuery query(db);
// 定義一個字串,字串內容是SQLite的命令,用於建立表。
// CREATE TABLE IF NOT EXISTS :標準的SQL命令,意味著如果後面這個表名 history 不存在的話,那麼建立一個新表。
// history是表的名字,previousNum是第一個列的列名,double是第一列的資料型別。很清楚,一共四列。
QString createTableString = "CREATE TABLE IF NOT EXISTS history(previousNum double, operator varchar, presentNum double, resultNum double)";
// 執行建立表的命令,如果建立不成功,在顯示器中進行提示。
bool success = query.exec(createTableString);
if (!success) {
ui->textBrowser->setText("can't create table");
}
// 再次定義一個SQLite命令的字串,用於插入一行資料。
// INSERT INTO :標準的SQL命令,向後面的表 history 中插入資料。
// (previousNum, operator, presentNum, resultNum) : 插入資料列的列名,可以用不一樣的順序,但是列名一定是表中的列。
// VALUES('%1', '%2', '%3', '%4') : VALUES裡面就是插入資料的值,這裡的‘%1’等是預留位置,真正的值是arg()裡面的資料。
QString insertDataString = QString("INSERT INTO history(previousNum, operator, presentNum, resultNum) VALUES('%1', '%2', '%3', '%4')").arg(previousNum).arg(operatorChar).arg(presentNum).arg(resultNum);
// 執行插入命令,如果插入不成功,那麼在顯示器中進行提示。
success = query.exec(insertDataString);
if (!success) {
ui->textBrowser->setText("can't insert data");
}
註釋清楚,不多解釋。
剩下的槽函數更加簡單,所以直接放程式碼了(都有註釋):
void CalculatorMine::PushButtonCClicked() {
// 清除顯示器和倆標誌位即可
ui->textBrowser->clear();
isOperatorClicked = false;
isStoredNum = false;
}
void CalculatorMine::PushButtonDelClicked() {
// 刪除顯示器上的最後一個數位後再顯示
QString textShow = ui->textBrowser->toPlainText();
if (textShow.length() == 0) {
return;
}
textShow.chop(1);
ui->textBrowser->setText(textShow);
}
void CalculatorMine::PushButonPercentClicked() {
// 把顯示器上的數位除以100之後再顯示
QString textShow = ui->textBrowser->toPlainText();
double percentResult = 0.01 * textShow.toDouble();
textShow = QString::number(percentResult, 'g', numLimit);
ui->textBrowser->setText(textShow);
}
void CalculatorMine::PushButtonReverseClicked() {
// 把顯示器上的數位取負之後再顯示
QString textShow = ui->textBrowser->toPlainText();
double reverseResult = -1 * textShow.toDouble();
textShow = QString::number(reverseResult, 'g', numLimit);
ui->textBrowser->setText(textShow);
}
void CalculatorMine::PushButtonEqualClicked() {
// 如果符合運算標準,執行運算操作,運算元標誌位置假
QString textShow = ui->textBrowser->toPlainText();
if (!isStoredNum || textShow.length() == 0 || isOperatorClicked) {
return;
}
CalculateResult();
isStoredNum = false;
}
void CalculatorMine::PushButtonPointClicked() {
// 如果顯示器上已經有了小數點,直接返回
QString textShow = ui->textBrowser->toPlainText();
if (textShow.length() >= (numLimit - 1) || textShow.contains('.', Qt::CaseSensitive)) {
return;
}
// 如果顯示器沒有數位,那麼那麼就是0-1之間的小數,先加入一個0
if (textShow.length() == 0) {
textShow = "0";
}
// 給顯示器的末尾新增一個小數點
textShow.append('.');
ui->textBrowser->setText(textShow);
}
void CalculatorMine::SearchHistory() {
// 我們獲取的資料共有四個列,分別記錄了第一個運算元、操作符、第二個運算元、計算結果。
// 這裡我們設定最多取200條運算記錄,定義4個陣列儲存
double previousNum[200];
QString operatorChar[200];
double presentNum[200];
double resultNum[200];
// 連線資料庫,初始化資料庫操作物件
QSqlDatabase db = QSqlDatabase::database("sqliteMine");
QSqlQuery query(db);
// 定義一個SQL語句的字串,用於獲取資料庫中所有的記錄
// SELECT * FROM: 標準的SQL命令,用於從後面的表 history 中查詢所有的資料
QString selectDataString = "SELECT * FROM history";
// 執行查詢語句,如果不成功,在顯示屏上進行提示
bool success = query.exec(selectDataString);
if (!success) {
ui->textBrowser->setText("can't get history data");
}
// 將獲取的查詢結果放入我們事先準備好的陣列中。
int index = 0;
while (query.next()) {
previousNum[index] = query.value(0).toDouble();
operatorChar[index] = query.value(1).toString();
presentNum[index] = query.value(2).toDouble();
resultNum[index] = query.value(3).toDouble();
index++;
}
// 將歷史記錄一條一條的插入到我們的顯示器中,格式為 IDx:運算元1 操作符 運算元2 = 運算結果。
ui->textBrowser->clear();
for (int i = 0; i < index; i++) {
QString historyString = QString("ID%1: %2 %3 %4 = %5").arg(i + 1).arg(previousNum[i]).arg(operatorChar[i]).arg(presentNum[i]).arg(resultNum[i]);
ui->textBrowser->append(historyString);
}
}
void CalculatorMine::CloseHistory() {
// 直接清空顯示器就好了
ui->textBrowser->clear();
}
void CalculatorMine::ClearHistory() {
// 連線資料庫,建立資料庫操作物件
QSqlDatabase db = QSqlDatabase::database("sqliteMine");
QSqlQuery query(db);
// 定義一個SQL語句的字串,用來刪除表。
// DROP TABLE : 標準SQL語句,用於刪除後面的表 history 。
QString dropTableString = "DROP TABLE history";
// 執行刪除表的語句,如果未成功,在顯示器上提醒。
bool success = query.exec(dropTableString);
if (!success) {
ui->textBrowser->setText("can't clear history data");
}
else {
ui->textBrowser->clear();
}
}
不解釋啦,註釋好好看,肯定都看得懂,功能都是類似的。
main.cpp
#include <QtWidgets/QApplication>
#include "CalculatorMine.h"
int main(int argc, char* argv[]) {
// 開啟Qt應用環境
QApplication a(argc, argv);
// 建立一個計算器物件
CalculatorMine calculatorMine;
// 執行計算器物件的介面
calculatorMine.show();
// 結束Qt應用環境
return a.exec();
}
CalculatorMine.h
#pragma once
#include <QMainWindow>
#include <QButtonGroup>
#include <QtSql>
#include "ui_CalculatorMine.h"
class CalculatorMine : public QMainWindow
{
Q_OBJECT
public:
CalculatorMine(QWidget *parent = Q_NULLPTR);
~CalculatorMine();
// 定義一系列槽函數(與控制元件的訊號連線),當點選計算器的某個鍵時,執行。
private slots:
// 當數位按鈕組中的某個鍵被點選時呼叫,用來更新textBrowser等。
void NumButtonClicked(QAbstractButton*);
// 當操作符按鈕組中的某個鍵被點選時呼叫,用來更新textBrower和執行運算等。
void OperatorButtonClicked(QAbstractButton*);
// 當C按鈕被點選時呼叫,用來清空textBrowser和各種變數。
void PushButtonCClicked();
// 當Del按鈕被點選時呼叫,用來刪除一個輸入數位。
void PushButtonDelClicked();
// 當%按鈕被點選時呼叫,用來將運算元除100。
void PushButonPercentClicked();
// 當+/-按鈕被點選時呼叫,用來將運算元取負。
void PushButtonReverseClicked();
// 當=按鈕被點選時呼叫,用來計算和顯示結果。
void PushButtonEqualClicked();
// 當Point按鈕被點選時呼叫,用來增加小數點。
void PushButtonPointClicked();
// 當檢視歷史被點選時呼叫,用來和資料庫通訊,獲得歷史記錄並顯示。
void SearchHistory();
// 用來清空歷史顯示
void CloseHistory();
// 用來清空資料庫和顯示。
void ClearHistory();
private:
// 用來執行運算(在操作符被點選時呼叫),並與資料庫通訊,儲存資料
void CalculateResult();
private:
// 新增一個計算器UI的範例,UI介面的名稱空間為Ui
Ui::CalculatorMineWindow* ui;
// 定義數位按鈕組(numButtonGroup)和操作符按鈕組(operatorButtonGroup)。按鈕組內的按鈕都是相似的,可以共用一個槽函數
QButtonGroup* numButtonGroup;
QButtonGroup* operatorButtonGroup;
// 定義儲存操作符(operatorChar)和運算元(storedNum)以及運算結果(resultNum)的變數
QChar operatorChar;
double storedNum;
double resultNum;
// 定義標誌位,isOperatorClicked標記是否已經點選了操作符,isStoredNum標記是否有一個已經儲存了一個數位
bool isOperatorClicked;
bool isStoredNum;
// 定義輸入數位限制,輸入數位的數量不能超過numLimit
const int numLimit = 16;
};
CalculatorMine.cpp
#include "CalculatorMine.h"
CalculatorMine::CalculatorMine(QWidget *parent)
: QMainWindow(parent),
ui(new Ui::CalculatorMineWindow)
{
// 啟動我們的ui介面
ui->setupUi(this);
// 初始化儲存變數和標誌位變數
isOperatorClicked = false;
isStoredNum = false;
storedNum = 0;
resultNum = 0;
// 初始化按鈕組
numButtonGroup = new QButtonGroup;
operatorButtonGroup = new QButtonGroup;
// 為數位按鈕組新增按鈕: 0 1 2 3 4 5 6 7 8 9
numButtonGroup->addButton(ui->pushButton1, 1);
numButtonGroup->addButton(ui->pushButton2, 2);
numButtonGroup->addButton(ui->pushButton3, 3);
numButtonGroup->addButton(ui->pushButton4, 4);
numButtonGroup->addButton(ui->pushButton5, 5);
numButtonGroup->addButton(ui->pushButton6, 6);
numButtonGroup->addButton(ui->pushButton7, 7);
numButtonGroup->addButton(ui->pushButton8, 8);
numButtonGroup->addButton(ui->pushButton9, 9);
numButtonGroup->addButton(ui->pushButton0, 0);
// 為操作符按鈕組新增按鈕:+ - * / =
operatorButtonGroup->addButton(ui->pushButtonAdd, 1);
operatorButtonGroup->addButton(ui->pushButtonSub, 2);
operatorButtonGroup->addButton(ui->pushButtonMul, 3);
operatorButtonGroup->addButton(ui->pushButtonDiv, 4);
operatorButtonGroup->addButton(ui->pushButtonEqual, 5);
// 連線按鈕組和按鈕組的槽函數
numButtonGroup->connect(numButtonGroup, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(NumButtonClicked(QAbstractButton*)));
operatorButtonGroup->connect(operatorButtonGroup, SIGNAL(buttonClicked(QAbstractButton*)), this, SLOT(OperatorButtonClicked(QAbstractButton*)));
// 連線普通按鈕和他們對應的槽函數
connect(ui->pushButtonC, SIGNAL(clicked()), this, SLOT(PushButtonCClicked()));
connect(ui->pushButtonDel, SIGNAL(clicked()), this, SLOT(PushButtonDelClicked()));
connect(ui->pushButtonPercent, SIGNAL(clicked()), this, SLOT(PushButonPercentClicked()));
connect(ui->pushButtonReverse, SIGNAL(clicked()), this, SLOT(PushButtonReverseClicked()));
connect(ui->pushButtonEqual, SIGNAL(clicked()), this, SLOT(PushButtonEqualClicked()));
connect(ui->pushButtonPoint, SIGNAL(clicked()), this, SLOT(PushButtonPointClicked()));
// 設定與資料庫的連線。 資料庫的型別為「QSQLITE」,即SQLite; 連線的名稱為「sqliteMine」(隨意設定);
// 連線資料庫的主機地址為:127.0.0.1,即迴環地址(自己);要操作的資料庫名稱為 「HistoryData.db」(隨意設定);
// 連線的使用者為root使用者,使用預設密碼123456
QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE", "sqliteMine");
db.setHostName("127,0,0,1");
db.setDatabaseName("HistoryData.db");
db.setUserName("root");
db.setPassword("123456");
if (!db.open()) {
ui->textBrowser->setText("can't open database");
}
// 連線選單項和他們對應的槽函數
connect(ui->actionSearchHistory, SIGNAL(triggered()), this, SLOT(SearchHistory()));
connect(ui->actionCloseHistory, SIGNAL(triggered()), this, SLOT(CloseHistory()));
connect(ui->actionClearHistory, SIGNAL(triggered()), this, SLOT(ClearHistory()));
}
CalculatorMine::~CalculatorMine()
{
}
void CalculatorMine::NumButtonClicked(QAbstractButton* numButton) {
// 用textShow接收顯示器上的顯示文字,用來執行判斷等
QString textShow = ui->textBrowser->toPlainText();
// 如果上一個點選的是操作符按鈕,那麼就代表要輸入第二個運算元了,此時清空顯示,操作符標誌置否。
if (isOperatorClicked) {
textShow.clear();
isOperatorClicked = false;
}
// 如果輸入的數位數量過多,那麼就不能輸入了,直接返回
if (textShow.length() >= numLimit) {
return;
}
// 為textShow追加按鈕上的文字(0/1/2/3/4/5/6/7/8/9)
textShow.append(numButton->text());
// 將textShow的文字顯示到計算器顯示器上
ui->textBrowser->setText(textShow);
}
void CalculatorMine::OperatorButtonClicked(QAbstractButton* operatorButton) {
// 如果上一個點選的也是操作符,那麼替換掉,將操作符儲存在operatorChar中。
if (isOperatorClicked) {
operatorChar = operatorButton->text().at(0);
}
else {
// 如果已經儲存的有數位,那麼證明輸入了兩個數位,可以運算了,進行運算。
if (isStoredNum) {
CalculateResult();
}
else {
// 如果沒存數位,證明是第一次點選操作符,那麼把顯示屏上的數位存起來,這是第一個運算元
QString textShow = ui->textBrowser->toPlainText();
storedNum = textShow.toDouble();
isStoredNum = true;
}
// 操作符標誌位設定為真,儲存操作符
isOperatorClicked = true;
operatorChar = operatorButton->text().at(0);
}
}
void CalculatorMine::CalculateResult() {
// 用textShow記錄顯示器介面的數位。
QString textShow = ui->textBrowser->toPlainText();
// 第一個運算元是儲存的運算元,第二個操作時為顯示屏上顯示的數位。
double previousNum = storedNum;
double presentNum = textShow.toDouble();
// 根據操作符(+/-/*/÷)的不同,對兩個運算元執行不同的運算,儲存到resultNum中。
if (operatorChar == '+') {
resultNum = previousNum + presentNum;
}
else if (operatorChar == '-') {
resultNum = previousNum - presentNum;
}
else if (operatorChar == '*') {
resultNum = previousNum * presentNum;
}
else if (operatorChar == '/') {
resultNum = previousNum / presentNum;
}
// 計算的結果可以作為下一次計算的第一個運算元,便於連續運算。
storedNum = resultNum;
// 將數位轉為QString型別的字串,限制其串長度,賦值給texxtShow,顯示在顯示屏上。
textShow = QString::number(resultNum, 'g', numLimit);
ui->textBrowser->setText(textShow);
// 使用連線 "sqliteMine" 連入資料庫,這樣就可以存取我們建立的資料庫HistoryData了
QSqlDatabase db = QSqlDatabase::database("sqliteMine");
// 使用db初始化一個資料庫操作的物件,用來對資料庫進行管理
QSqlQuery query(db);
// 定義一個字串,字串內容是SQLite的命令,用於建立表。
// CREATE TABLE IF NOT EXISTS :標準的SQL命令,意味著如果後面這個表名 history 不存在的話,那麼建立一個新表。
// history是表的名字,previousNum是第一個列的列名,double是第一列的資料型別。很清楚,一共四列。
QString createTableString = "CREATE TABLE IF NOT EXISTS history(previousNum double, operator varchar, presentNum double, resultNum double)";
// 執行建立表的命令,如果建立不成功,在顯示器中進行提示。
bool success = query.exec(createTableString);
if (!success) {
ui->textBrowser->setText("can't create table");
}
// 再次定義一個SQLite命令的字串,用於插入一行資料。
// INSERT INTO :標準的SQL命令,向後面的表 history 中插入資料。
// (previousNum, operator, presentNum, resultNum) : 插入資料列的列名,可以用不一樣的順序,但是列名一定是表中的列。
// VALUES('%1', '%2', '%3', '%4') : VALUES裡面就是插入資料的值,這裡的‘%1’等是預留位置,真正的值是arg()裡面的資料。
QString insertDataString = QString("INSERT INTO history(previousNum, operator, presentNum, resultNum) VALUES('%1', '%2', '%3', '%4')").arg(previousNum).arg(operatorChar).arg(presentNum).arg(resultNum);
// 執行插入命令,如果插入不成功,那麼在顯示器中進行提示。
success = query.exec(insertDataString);
if (!success) {
ui->textBrowser->setText("can't insert data");
}
}
void CalculatorMine::PushButtonCClicked() {
// 清除顯示器和倆標誌位即可
ui->textBrowser->clear();
isOperatorClicked = false;
isStoredNum = false;
}
void CalculatorMine::PushButtonDelClicked() {
// 刪除顯示器上的最後一個數位後再顯示
QString textShow = ui->textBrowser->toPlainText();
if (textShow.length() == 0) {
return;
}
textShow.chop(1);
ui->textBrowser->setText(textShow);
}
void CalculatorMine::PushButonPercentClicked() {
// 把顯示器上的數位除以100之後再顯示
QString textShow = ui->textBrowser->toPlainText();
double percentResult = 0.01 * textShow.toDouble();
textShow = QString::number(percentResult, 'g', numLimit);
ui->textBrowser->setText(textShow);
}
void CalculatorMine::PushButtonReverseClicked() {
// 把顯示器上的數位取負之後再顯示
QString textShow = ui->textBrowser->toPlainText();
double reverseResult = -1 * textShow.toDouble();
textShow = QString::number(reverseResult, 'g', numLimit);
ui->textBrowser->setText(textShow);
}
void CalculatorMine::PushButtonEqualClicked() {
// 如果符合運算標準,執行運算操作,運算元標誌位置假
QString textShow = ui->textBrowser->toPlainText();
if (!isStoredNum || textShow.length() == 0 || isOperatorClicked) {
return;
}
CalculateResult();
isStoredNum = false;
}
void CalculatorMine::PushButtonPointClicked() {
// 如果顯示器上已經有了小數點,直接返回
QString textShow = ui->textBrowser->toPlainText();
if (textShow.length() >= (numLimit - 1) || textShow.contains('.', Qt::CaseSensitive)) {
return;
}
// 如果顯示器沒有數位,那麼那麼就是0-1之間的小數,先加入一個0
if (textShow.length() == 0) {
textShow = "0";
}
// 給顯示器的末尾新增一個小數點
textShow.append('.');
ui->textBrowser->setText(textShow);
}
void CalculatorMine::SearchHistory() {
// 我們獲取的資料共有四個列,分別記錄了第一個運算元、操作符、第二個運算元、計算結果。
// 這裡我們設定最多取200條運算記錄,定義4個陣列儲存
double previousNum[200];
QString operatorChar[200];
double presentNum[200];
double resultNum[200];
// 連線資料庫,初始化資料庫操作物件
QSqlDatabase db = QSqlDatabase::database("sqliteMine");
QSqlQuery query(db);
// 定義一個SQL語句的字串,用於獲取資料庫中所有的記錄
// SELECT * FROM: 標準的SQL命令,用於從後面的表 history 中查詢所有的資料
QString selectDataString = "SELECT * FROM history";
// 執行查詢語句,如果不成功,在顯示屏上進行提示
bool success = query.exec(selectDataString);
if (!success) {
ui->textBrowser->setText("can't get history data");
}
// 將獲取的查詢結果放入我們事先準備好的陣列中。
int index = 0;
while (query.next()) {
previousNum[index] = query.value(0).toDouble();
operatorChar[index] = query.value(1).toString();
presentNum[index] = query.value(2).toDouble();
resultNum[index] = query.value(3).toDouble();
index++;
}
// 將歷史記錄一條一條的插入到我們的顯示器中,格式為 IDx:運算元1 操作符 運算元2 = 運算結果。
ui->textBrowser->clear();
for (int i = 0; i < index; i++) {
QString historyString = QString("ID%1: %2 %3 %4 = %5").arg(i + 1).arg(previousNum[i]).arg(operatorChar[i]).arg(presentNum[i]).arg(resultNum[i]);
ui->textBrowser->append(historyString);
}
}
void CalculatorMine::CloseHistory() {
// 直接清空顯示器就好了
ui->textBrowser->clear();
}
void CalculatorMine::ClearHistory() {
// 連線資料庫,建立資料庫操作物件
QSqlDatabase db = QSqlDatabase::database("sqliteMine");
QSqlQuery query(db);
// 定義一個SQL語句的字串,用來刪除表。
// DROP TABLE : 標準SQL語句,用於刪除後面的表 history 。
QString dropTableString = "DROP TABLE history";
// 執行刪除表的語句,如果未成功,在顯示器上提醒。
bool success = query.exec(dropTableString);
if (!success) {
ui->textBrowser->setText("can't clear history data");
}
else {
ui->textBrowser->clear();
}
}
ui_CalculatorMine.h 這個是自動生成的檔案,放出來也沒啥用,截圖給大家認識以下:
我把編譯產生的附加檔案刪了之後,整個工程只有10k,設定好環境後就可以直接執行,下面是資源:
提取碼:qt6j