VS+QT+SQLite實現簡單的計算器

2020-10-21 03:00:32

0. 計算器功能說明

0.1 計算器介面

計算器介面及其模組介紹如下:

在這裡插入圖片描述

0.2 歷史記錄模組

點開設定按鈕,有三個選項,功能如下:
在這裡插入圖片描述

我們點選檢視歷史,可以看到效果:

在這裡插入圖片描述

0.3 總體說明

計算器整體功能簡單,就是基本的四則運算,加上儲存歷史記錄的功能,可以用來學習QT和SQLite。

我們會使用三個軟體,分別為VS+QT(提供介面UI)+SQLite(提供資料庫)。

不會QT和SQLite沒關係。這裡不涉及很深入的知識,手把手教學,包教包會。

下面就從設定開發環境開始。

1. 安裝VS

1.1 下載VS_Community

這裡有VS的下載連結,自行取用:VS_Community下載

1.2 安裝VS

雙擊VS_Community.exe,傻瓜式安裝(我們只需要C++開發環境),不再詳述。

2. 安裝Qt

2.1 下載Qt

這裡有Qt的離線安裝包網址,自行取用:Qt離線安裝包下載
在這裡插入圖片描述

2.2 安裝Qt

雙擊開啟qt-opensource-windows-x86-5.12-8.exe,出現歡迎介面,點 next:
在這裡插入圖片描述
登入Qt賬戶:
在這裡插入圖片描述
直接下一步,然後選擇資料夾,這個自由選擇,只要知道自己裝在哪就好了。

選擇安裝的元件,如下:
在這裡插入圖片描述
一直下一步,直至安裝完成。

3. 為VS設定Qt VS Tools

3.1 下載安裝Qt VS Tools

開啟VS,找到標題列的 擴充套件,點選子選項 管理擴充套件:
在這裡插入圖片描述
在管理擴充套件中安裝Qt VS Tools:
在這裡插入圖片描述
下載安裝完成後,關閉VS的所有視窗,會自動應用更改,你只需要最後點選 「modify」 按鈕確認即可。

注意: 再次開啟VS有可能會出現下圖錯誤,這時兩種嘗試方法:

  1. 開啟VS_Community升級VS至最新版本。
  2. 解除安裝Qt VS Tools,重新安裝,我是採用的第二種方案,解決了。
    在這裡插入圖片描述

3.2 設定環境變數

之後設定環境變數,WIN+Q組合鍵開啟搜尋,鍵入 「編輯系統環境變數」 ,選中開啟:

在這裡插入圖片描述
在這裡插入圖片描述
在這裡插入圖片描述

3.3 特殊問題解決

開啟VS,找到依次點選:擴充套件 -> Qt VS Tools -> Qt Options,VS會自動為你選中QT的msvc,如果不報錯,設定完成。

注意:如果出現報錯,資訊顯示如:The following error occured:These Qt version are inaccessible,那麼需要以下操作:

在這裡插入圖片描述
在這裡插入圖片描述

重新開啟 Qt Options,進行下面操作:
在這裡插入圖片描述
刪除原先的Qt Version,只保留我們新新增的,然後將其設為預設:
在這裡插入圖片描述
不報錯,就設定完成了。

4. 安裝SQLite

4.1 下載SQLite

這裡提供SQLite的元件下載地址,大家自行取用:SQLite下載
在這裡插入圖片描述

4.2 安裝SQLite

將兩個壓縮包放到合適的位置,分別解壓即可。
在這裡插入圖片描述
設定環境變數,仍舊是在Path裡面,新增SQLite的路徑:
在這裡插入圖片描述

4.3 測試SQLite

按下 WIN+Q 組合鍵開啟搜尋,輸出 「cmd」 ,然後回車,開啟命令列,輸入sqlite3,回車:
在這裡插入圖片描述

5. 建立和設定VS+QT的工程

5.1 建立VS工程

開啟VS,點選建立新專案,找到 Qt Empty Application 專案,點選之後進入下一步:
在這裡插入圖片描述

設定專案名稱和位置大家自選就好了,如下:

在這裡插入圖片描述
設定QT工程,一路預設就好了:
在這裡插入圖片描述
在這裡插入圖片描述

5.2 重定向目標工具集

現在編譯程式會產生如下的報錯,因為我們用的MSVC2017,版本落後,需要升級:
在這裡插入圖片描述
我們需要按照提示,重定向目標工具集,首先右擊解決方案,找到重定向目標解決方案,單擊即可:
在這裡插入圖片描述
出現重定向專案視窗,確定即可:
在這裡插入圖片描述
再次生成解決方案即可成功。

5.3 新增main函數

在Source Files裡面新增main.cpp:
在這裡插入圖片描述

輸入以下程式碼:

//引入Qt視窗應用程式
#include <QtWidgets/QApplication>

int main(int argc, char* argv[]) {
	// 開啟Qt應用環境
	QApplication a(argc, argv);
	// 結束Qt應用環境
	return a.exec();
}

這裡只有Qt的基本應用環境的開啟和關閉,沒有生成任何介面。

5.3 設定QT模組

我們需要為專案新增QT的模組,否則連線會出錯,如下:
在這裡插入圖片描述
首先右鍵單擊專案,找到 屬性,單擊,進入屬性設定介面:
在這裡插入圖片描述

然後在屬性設定介面進行如下操作,進入模組選擇介面:

在這裡插入圖片描述
選擇 Core GUI SQL Widget 四個模組,點選Finish即可:
在這裡插入圖片描述

在這裡插入圖片描述
點選確定,再次編譯執行,通過。

6. 搭建程式框架

6.1 建立計算器的QT類

右鍵單擊專案,遊標移到新增,點選Add Qt Class,如圖:
在這裡插入圖片描述
新增一個名為CalculatorMine的類,如下:
在這裡插入圖片描述
設定類的屬性:
在這裡插入圖片描述

為建構函式新增預設引數 Q_NULLPTR,否則編譯會報錯:

在這裡插入圖片描述
在這裡插入圖片描述

6.2 在Main中範例化計算器類

我們已經有了計算器類,那麼就可以在main函數中新增它的物件。在main中新增以下程式碼:

	// 建立一個計算器物件
	CalculatorMine calculatorMine;
	// 執行計算器物件的介面
	calculatorMine.show();

新增之後的main函數如下:

在這裡插入圖片描述

執行程式,得到基本的視窗,效果如下:

在這裡插入圖片描述

6.3 新建UI介面

在資原始檔夾中新增UI介面,首先右擊 Resource Files,遊標移到 新增,單擊 新建項,如下:

在這裡插入圖片描述

然後根據提示進行設定:

在這裡插入圖片描述
Qt設計師介面會自動開啟,或者你雙擊Resource Files下的CalculatorMine.ui也可以開啟,如下:
在這裡插入圖片描述

6.4 編譯UI介面

這個時候,你還查詢不到ui介面的標頭檔案,需要將ui檔案編譯之後得到,如下操作:

在這裡插入圖片描述
編譯完成之後,在外部依賴項中有可能可以找到 ui_CalculatorMine.h 檔案,找不到沒事,不影響。

6.5 更換表單的名稱和標題

QT預設的視窗名稱和標題為mianWindow,我們可以改為自己想要的:

在這裡插入圖片描述
重新編譯CalculatorMine.ui檔案。一般來說,UI介面的更改在VS中重新編譯.ui檔案即可生效,如果不行,重新啟動VS即可。

6.6 建立計算器UI物件

有了UI類,我們就可以範例化計算器的UI了,在CalculatorMine.h中新增的程式碼如下:
在這裡插入圖片描述

下一步我們需要初始化物件,並展示其UI。更改CalculatorMine.cpp檔案:

在這裡插入圖片描述

我們執行程式,可以看到效果:

在這裡插入圖片描述

7. 設計UI介面

7.1 Qt設計師面板介紹

面板功能介紹如下:

在這裡插入圖片描述

7.2 新增一個水平佈局

滑鼠左鍵點選Vertical Layout不放,將其拖放到UI介面效果欄中,如下:

在這裡插入圖片描述

調整UI介面和水平佈局,使其大小合適(調整方式就是拖動邊緣),效果如下:

在這裡插入圖片描述

7.3 新增Text Browser和水平佈局

先新增一個Text Browser進入上一個水平佈局中,可以看到其佔據了所有的介面:

在這裡插入圖片描述

我們應該調整其大小,令其佔據空間合適,這時候就要用到屬性編輯器,操作如下:

在這裡插入圖片描述

以同樣的方式編輯其顯示字型的大小,把裡面顯示的字型調大一點,看著舒服:

在這裡插入圖片描述

拖一個水平佈局進去,效果如下:

在這裡插入圖片描述

7.4 新增垂直佈局和按鈕

在第二個水平佈局中拖入5個垂直佈局,可以採用如下方式:

在這裡插入圖片描述

在每個垂直佈局中分別拖入4個按鈕(Push Button),效果如下:

在這裡插入圖片描述

然後我們修改按鈕大小和按鈕字型大小,ctrl+點選 逐個選中所有按鈕,用屬性編輯器:

在這裡插入圖片描述

雙擊按鈕可以編輯按鈕的顯示文字,編輯成如下的效果:

在這裡插入圖片描述

VS編輯控制元件都需要其名稱,為了方便使用,這裡給所有的按鈕都對應一個名字,雙擊其物件即可編輯,效果如下:

在這裡插入圖片描述

7.5 新增選單欄

新增選單,雙擊 「在這裡輸入」 ,然後輸入 「設定」 ,回車即可,如下:

在這裡插入圖片描述
點選設定,雙擊 「在這裡輸入」 ,然後輸入 「檢視歷史」 ,回車即可,如下:
在這裡插入圖片描述

重複新增 「關閉歷史」 ,「清除歷史」。最終效果如下:

在這裡插入圖片描述
**注意:**如果不能輸入漢字,可以從其他地方複製過來。

下一步仍舊是修改物件名稱,便於使用,修改後的結果如下:

在這裡插入圖片描述

7.6 儲存並執行

首先,使用Ctrl+S儲存對介面的修改,整個介面就算設計完成了。

然後返回VS,編譯CalculatorMine.ui。

最後編譯執行專案,效果如下:

在這裡插入圖片描述

8. 新增成員變數和函數框架

8.1 新增成員變數

這裡首先為計算器類新增我們用到的成員變數,在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中的效果如下:

在這裡插入圖片描述

8.2 新增成員函數框架

我們已經有了各種控制元件和所有需要的成員變數,下一步就是定義槽函數,執行按鈕功能。

在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中效果如下:

在這裡插入圖片描述

9. 設定與資料庫的連線

9.1 新增資料庫標頭檔案

這裡用到了資料庫的操作,因此要引入資料庫的庫。在CalculatorMine.h中新增以下程式碼:

#include <QtSql>

在VS中的效果如下:

在這裡插入圖片描述

9.2 設定與資料庫的連線

下面開始運算元據庫了,在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中的效果如下:

在這裡插入圖片描述

10. 實現槽函數功能

所有的準備工作均已完成,下面我們就要實現按鈕的功能了。

10.1 數位按鈕組槽函數

我們點選一個數位按鈕時,呼叫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中顯示其效果:

在這裡插入圖片描述
我們執行程式,在計算器的按鈕面板上點選數位按鈕,發現會出現相應的數位:

在這裡插入圖片描述

10.2 操作符按鈕組槽函數

在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);
	}

註釋挺清楚的,就不多解釋了。

10.3 結果計算與儲存函數

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");
	}

註釋清楚,不多解釋。

10.4 單個鍵與選單欄槽函數

剩下的槽函數更加簡單,所以直接放程式碼了(都有註釋):

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();
	}
}

不解釋啦,註釋好好看,肯定都看得懂,功能都是類似的。

10.5 完成效果展示

在這裡插入圖片描述

11. 程式碼總結

11.1 檔案版程式碼(純程式碼)

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 這個是自動生成的檔案,放出來也沒啥用,截圖給大家認識以下:

在這裡插入圖片描述

11.2 整個工程(百度網路硬碟)

我把編譯產生的附加檔案刪了之後,整個工程只有10k,設定好環境後就可以直接執行,下面是資源:

連結: CalculatorMine.zip

提取碼:qt6j

12. 多多點贊關注,常交流!