API 描述
函數原型
參數說明
push_back()
在 list 尾部
添加一個元素
void push_back(const T& value);
value :要添
加到尾部的元
素
這個示例演示了如何創建 std::list 容器,并對其進行插入、刪除和迭代操作。在實際應用中,
std::list 還有許多其他的功能和方法可以使用,比如 splice() 、 merge() 、 sort() 等,用于更復
雜的操作。
以下是 std::list 常用的?API 方法,包括參數說明,整理成表格形式:
#include <iostream>
#include <list>
int main() {
// 創建一個存儲整數的 list 容器
std::list<int> myList;
// 在 list 尾部插入元素
myList.push_back(10);
myList.push_back(20);
myList.push_back(30);
// 在 list 頭部插入元素
myList.push_front(5);
myList.push_front(15);
// 使用迭代器遍歷 list 并輸出元素
std::cout << "List elements: ";
for (auto it = myList.begin(); it != myList.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
// 刪除 list 中特定的元素值
myList.remove(20);
// 使用范圍-based for 循環遍歷 list 并輸出元素
std::cout << "List elements after removal: ";
for (int num : myList) {
std::cout << num << " ";
}
std::cout << std::endl;
// 檢查 list 是否為空
if (myList.empty()) {
std::cout << "List is empty." << std::endl;
} else {
std::cout << "List is not empty." << std::endl;
}
return 0;
}API
描述
函數原型
參數說明
push_front()
在 list 頭部
添加一個元素
void push_front(const T&
value);
value :要添
加到頭部的元
素
pop_back()
刪除 list 尾
部的一個元素
void pop_back();
無參數
pop_front()
刪除 list 頭
部的一個元素
void pop_front();
無參數
size()
返回 list 中
元素的數量
size_type size() const
noexcept;
無參數
empty()
檢查 list 是
否為空
bool empty() const noexcept;
無參數
clear()
清空 list 中
的所有元素
void clear() noexcept;
無參數
begin()
返回指向
list 第一個
元素的迭代器
iterator begin() noexcept;
const_iterator begin() const
noexcept;
無參數
end()
返回指向
list 末尾
(最后一個元
素的后面)的
迭代器
iterator end() noexcept;
const_iterator end() const
noexcept;
無參數
rbegin()
返回指向
list 最后一
個元素的逆向
迭代器(逆向
開始迭代)
reverse_iterator rbegin()
noexcept;
const_reverse_iterator rbegin()
const noexcept;
無參數
rend()
返回指向
list 第一個
元素之前的逆
向迭代器(逆
向結束迭代)
reverse_iterator rend()
noexcept;
const_reverse_iterator rend()
const noexcept;
無參數
insert()
在指定位置插
入一個或多個
元素
iterator insert(const_iterator
pos, const T& value);
void insert(const_iterator pos,
size_type count, const T&
value);
pos :插入位
置
value :要插
入的元素
count :要插
入的元素個數API
描述
函數原型
參數說明
erase()
刪除指定位置
或指定范圍內
的一個或多個
元素
iterator erase(const_iterator
pos);
iterator erase(const_iterator
first, const_iterator last);
pos :要刪除
的元素位置或
范圍的起始位
置
first 、
last :要刪
除的范圍
splice()
在指定位置插
入另一個
list 中的元
素
void splice(const_iterator pos,
list& other);
void splice(const_iterator pos,
list& other, const_iterator
it);
void splice(const_iterator pos,
list& other, const_iterator
first, const_iterator last);
pos :插入位
置
other :要插
入的另一個
list
it :要插入的
元素
first 、
last :要插
入的范圍
merge()
合并兩個已排
序的 list
void merge(list& other);
void merge(list&& other);
other :要合
并的另一個
list
unique()
移除 list 中
重復的元素
void unique();
void unique(BinaryPredicate p);
p :可選的謂
詞函數,用于
比較元素是否
相等
sort()
對 list 進行
排序
void sort();
void sort(Compare comp);
comp :可選
的比較函數,
用于元素排序
這些方法使得在 std::list 上進行插入、刪除、迭代和操作變得方便。每個方法都有不同的參數和作
用,可根據需要選擇合適的方法來操作 std::list 容器。
2.14.4 set
std::set 是?C++ 標準模板庫中的關聯容器,用于存儲唯一值的集合。它基于紅黑樹實現,保持了元素
的有序性,且不允許重復的元素存在。
以下是關于 std::set 的一些特點和說明:
唯一性: std::set 中的元素是唯一的,不允許有重復的元素存在。當嘗試向 set 中插入重復的
元素時,新元素將不會被插入。
有序性: std::set 中的元素是根據元素值進行排序的,這使得元素按照一定順序存儲,并且支持
對元素的快速搜索。
紅黑樹實現: std::set 的底層實現通常是基于紅黑樹的,這保證了插入、刪除和查找操作的時間
復雜度為對數時間(O(log n))。動態操作: 可以對 std::set 進行動態操作,如插入、刪除和查找元素。插入和刪除操作的性能較
好,不會影響其他元素的位置。
以下是 std::set 常用的一些方法:
insert() : 向 set 中插入一個元素。
erase() : 刪除 set 中指定值的元素。
find() : 查找指定值在 set 中的位置。
size() : 返回 set 中元素的數量。
empty() : 檢查 set 是否為空。
clear() : 清空 set 中的所有元素。
下面是一個簡單的示例,演示了如何使用 std::set :
#include <iostream>
#include <set>
int main() {
// 創建一個存儲 int 類型值的 set 容器
std::set<int> mySet;
// 向 set 中插入元素
mySet.insert(10);
mySet.insert(20);
mySet.insert(30);
// 嘗試插入重復元素
mySet.insert(20); // 不會插入重復的元素
// 使用迭代器遍歷 set 并輸出元素
std::cout << "Set elements: ";
for (auto it = mySet.begin(); it != mySet.end(); ++it) {
std::cout << *it << " ";
}
std::cout << std::endl;
// 查找特定值在 set 中的位置
int searchValue = 20;
auto found = mySet.find(searchValue);
if (found != mySet.end()) {
std::cout << "Found " << searchValue << " in the set." << std::endl;
} else {
std::cout << searchValue << " not found in the set." << std::endl;
}
// 刪除特定值的元素
mySet.erase(30);
// 檢查 set 是否為空
if (mySet.empty()) {
std::cout << "Set is empty." << std::endl;
} else {
std::cout << "Set is not empty." << std::endl;這個示例演示了如何創建 std::set 容器,并對其進行插入、刪除、查找等操作。 std::set 是一個非
常有用的容器,適用于需要存儲唯一值的場景。
2.14.5 map
std::map 是?C++ 標準模板庫中的關聯容器,用于存儲鍵值對。它基于紅黑樹實現,保持了元素的有序
性,其中每個元素都是一個鍵值對,鍵和值之間存在映射關系。
以下是關于 std::map 的一些特點和說明:
有序性: std::map 中的元素是根據鍵值排序的,這使得元素按照一定順序存儲,并且支持對元素
的快速搜索。
唯一鍵: std::map 中的鍵是唯一的,每個鍵對應一個值。如果嘗試使用相同的鍵向 map 中插入
值,則會更新鍵對應的值。
紅黑樹實現: std::map 的底層實現通常是基于紅黑樹的,這保證了插入、刪除和查找操作的時間
復雜度為對數時間(O(log n))。
動態操作: 可以對 std::map 進行動態操作,如插入、刪除和查找鍵值對。插入和刪除操作的性能
較好,不會影響其他元素的位置。
以下是 std::map 常用的一些方法:
insert() : 向 map 中插入一個鍵值對。
erase() : 刪除 map 中指定鍵的鍵值對。
find() : 查找指定鍵在 map 中的位置。
operator[] : 通過鍵訪問對應的值。
size() : 返回 map 中鍵值對的數量。
empty() : 檢查 map 是否為空。
clear() : 清空 map 中的所有鍵值對。
下面是一個簡單的示例,演示了如何使用 std::map :
}
return 0;
}
#include <iostream>
#include <map>
int main() {
// 創建一個存儲 string 類型鍵和 int 類型值的 map 容器
std::map<std::string, int> myMap;
// 向 map 中插入鍵值對
myMap["Alice"] = 25;
myMap["Bob"] = 30;
myMap["Charlie"] = 20;
// 使用迭代器遍歷 map 并輸出鍵值對這個示例演示了如何創建 std::map 容器,并對其進行插入、刪除、查找等操作。 std::map 是一個非
常有用的容器,適用于需要鍵值對存儲和檢索的場景。
2.15 異常
2.15.1 異常基本
在?C++ 中,異常處理是一種機制,用于處理程序在運行時發生的異常情況。異常是指程序執行期間發生
的意外事件,比如除以零、訪問無效的內存地址等。通過使用異常處理機制,可以使程序更健壯,并能
夠處理這些意外情況,避免程序崩潰或產生不可預測的結果。
在?C++ 中,異常處理通常包括以下關鍵詞和概念:
try-catch 塊: try 塊用于標識可能會引發異常的代碼塊,而 catch 塊用于捕獲和處理異常。
catch 塊可以針對不同類型的異常進行處理。
throw 關鍵詞: throw 用于在程序中顯式拋出異常。當發生異常情況時,可以使用 throw 來拋出
一個特定的異常類型。
異常類型:異常可以是任何類型的數據,但通常是標準庫中的異常類或自定義的異常類。標準庫提
供了一些常見的異常類,如 std::exception 及其派生類,用于表示不同類型的異常情況。
下面是一個簡單的示例,演示了異常處理的基本用法:
std::cout << "Map elements: " << std::endl;
for (auto it = myMap.begin(); it != myMap.end(); ++it) {
std::cout << it->first << ": " << it->second << std::endl;
}
// 查找特定鍵在 map 中的位置
std::string searchKey = "Bob";
auto found = myMap.find(searchKey);
if (found != myMap.end()) {
std::cout << "Found " << searchKey << " with value: " << found->second <<
std::endl;
} else {
std::cout << searchKey << " not found in the map." << std::endl;
}
// 刪除特定鍵的鍵值對
myMap.erase("Charlie");
// 檢查 map 是否為空
if (myMap.empty()) {
std::cout << "Map is empty." << std::endl;
} else {
std::cout << "Map is not empty." << std::endl;
}
return 0;
}
#include <iostream>
void divide(int numerator, int denominator) {
try {在這個示例中, divide() 函數嘗試對 numerator 除以 denominator 進行除法運算。如果
denominator 為零,就會拋出一個字符串類型的異常。在 main() 函數中調用 divide() 函數時,由
于 b 的值為零,因此會拋出異常,然后在 catch 塊中捕獲并處理異常,輸出錯誤消息。
在實際的程序開發中,可以根據具體情況設計和拋出自定義的異常類,以及使用多個 catch 塊來處理不
同類型的異常,使程序能夠更好地處理各種異常情況。
2.15.2 自定義異常
在?C++ 中,你可以通過繼承標準庫的 std::exception 類或其派生類來自定義異常類。自定義異常類通
常用于表示特定類型的異常情況,并允許你提供有關異常的額外信息。
以下是一個示例,演示了如何創建自定義的異常類:
if (denominator == 0) {
throw "Division by zero is not allowed!";
}
int result = numerator / denominator;
std::cout << "Result of division: " << result << std::endl;
} catch (const char* errorMessage) {
std::cout << "Exception caught: " << errorMessage << std::endl;
}
}
int main() {
int a = 10;
int b = 0;
divide(a, b);
return 0;
}
#include <iostream>
#include <exception>
// 自定義異常類,繼承自 std::exception
class MyException : public std::exception {
private:
const char* message; // 異常消息
public:
// 構造函數,接受異常消息作為參數
MyException(const char* msg) : message(msg) {}
// 覆蓋基類的 what() 方法,返回異常消息
virtual const char* what() const throw() {
return message;
}
};
// 一個函數,演示如何拋出自定義異常
void myFunction() {
// 在這個示例中,函數總是拋出自定義異常
throw MyException("This is a custom exception!");在這個示例中, MyException 類繼承自 std::exception 類,并重寫了基類的 what() 方法,以返回
異常消息。在 myFunction() 中,我們拋出了一個 MyException 類型的異常,并在 main() 函數中的
try-catch 塊中捕獲并處理該異常。
通過自定義異常類,你可以根據需要添加其他成員變量、方法或構造函數,以便更好地描述和處理特定
類型的異常情況。這種方式可以提高程序的可讀性和可維護性,并允許你更精確地控制異常處理。
P3 記事本項目
3.1 項目概述
3.1.1 功能介紹
支持文本創建,打開,保存,關閉的功能
UI樣式美化
添加打開快捷鍵,添加保存快捷
底部顯示行列號及文本字符編碼
Ctrl加鼠標滾輪支持字體放大縮小
}
int main() {
try {
myFunction();
} catch (const MyException& e) {
std::cout << "Caught MyException: " << e.what() << std::endl;
}
return 0;
}3.1.2 界面預覽
3.2.3 工程概述
MainWindows還是Widget
在Qt中,創建?"MainWindow" 與?"Widget" 項目的主要區別在于他們的用途和功能范圍:
1. MainWindow:這是一個包含完整菜單欄、工具欄和狀態欄的主窗口應用程序框架。它適合于更復
雜的應用程序,需要這些額外的用戶界面元素來提供豐富的功能和交互。
2. Widget:這通常是一個簡單的窗口,沒有內置的菜單欄、工具欄或狀態欄。它適合于更簡單或專用
的應用程序,不需要復雜的用戶界面組件。
簡而言之,選擇"MainWindow"或"Widget"取決于你的應用程序需要多少內置的用戶界面元素和復雜
性。?MainWindow提供了更全面的框架,而Widget則更適合簡單、專注的界面。
QApplication
#include "widget.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}功能
說明
事件循環
維護事件循環,負責接收和分發各種事件,如鼠標點擊、鍵盤輸入等。
全局設置
處理應用程序的全局設置,包括字體、顏色和其他用戶界面元素。
GUI的初始化
在沒有創建 QApplication 的情況下,無法使用Qt的任何GUI組件,因此它負責
初始化GUI環境。
命令行參數
處理
可以處理命令行參數,這對于需要命令行交互的應用程序來說是必要的。
在Qt應用程序中, QApplication a(argc, argv); 這行代碼的作用是創建一個 QApplication 類的
實例。這是幾乎每個Qt應用程序必須做的第一步,因為它負責管理應用程序的許多核心功能。
下表總結了 QApplication 類在Qt框架中的主要功能和職責:
QApplication 是Qt應用程序的核心,它為應用程序提供了必要的環境和框架,確保GUI組件能夠正常
工作并響應用戶的操作。
簡而言之, QApplication a(argc, argv); 用于初始化Qt應用程序的環境,設置事件循環,并準備應
用程序處理GUI事件。
GUI代表圖形用戶界面(Graphical User Interface)。它是一種用戶界面,允許用戶通過圖形圖標
和視覺指示器(如按鈕、標簽、窗口等)與電子設備交互,而不是僅僅使用文本命令。GUI使得軟
件應用程序更加直觀和易于使用,因為它提供了視覺導向的操作方式,用戶可以通過點擊、拖拽和
輸入來操作界面元素,而不需要記憶和輸入復雜的命令。GUI是現代計算機和移動應用程序的標準
用戶界面類型。
return a.exec()
在Qt應用程序中, QApplication::exec() 函數是用來啟動應用程序的事件循環的。當你調用這個函數
時,它會開始處理和分發事件,如用戶的點擊、鍵盤輸入等。這個函數會一直運行,直到事件循環結
束,通常是因為調用了 QApplication::quit() 函數或者關閉了應用程序的主窗口。簡而言之,
exec() 是Qt程序中的主循環,負責監聽和響應事件,保持應用程序運行直到用戶決定退出。
namespace Ui { class Widget; }
在Qt框架中, namespace Ui { class Widget; } 是一種常見的用法,通常出現在使用Qt Designer設
計GUI時自動生成的代碼中。這里的 Ui 是一個命名空間,而 class Widget 是一個前向聲明,它聲明
了一個名為 Widget 的類。這種做法允許你在 .cpp 源文件中引用由Qt Designer創建的UI界面,而不需
要在頭文件中包含完整的UI類定義。這種分離的方法有助于減少編譯依賴性并保持代碼的清晰和組織。
在你的源文件中,你會創建一個 Ui::Widget 類型的對象來訪問和操作UI組件。
QT_BEGIN_NAMESPACE
QT_BEGIN_NAMESPACE 是Qt框架中用于支持命名空間的宏定義。Qt使用這些宏來確保其庫中的類和函數
不會與其他庫中的同名類和函數沖突。 QT_BEGIN_NAMESPACE 宏在定義Qt類和函數之前使用,用來指定
接下來的代碼位于Qt的命名空間中。它通常與 QT_END_NAMESPACE 配對使用,后者標志著命名空間的結
束。這種機制對于在大型項目中維護代碼的清晰度和防止命名沖突非常重要。
Q_OBJECT
Q_OBJECT 宏是Qt框架中一個非常重要的宏,用于啟用Qt對象的元對象系統。當你在Qt中定義一個類
時,如果這個類繼承自 QObject 或其子類,并且你想使用Qt的信號和槽機制、國際化、屬性系統或其他
Qt元對象系統提供的功能,就必須在類定義中包含 Q_OBJECT 宏。這個宏允許Qt的元對象編譯器(moc)識別并處理這個類,生成額外的代碼,這些代碼是實現信號和槽
機制以及其他元對象功能所必需的。簡單地說, Q_OBJECT 宏為Qt類提供了額外的元數據,使得類能夠
完全利用Qt框架的功能。
Widget::Widget(QWidget *parent) : QWidget(parent),ui(new Ui::Widget)
代碼 : QWidget(parent) 是初始化列表,用于調用基類 QWidget 的構造函數,并將 parent 傳遞給
它。 ui(new Ui::Widget) 是初始化類內部的 ui 成員變量,這是通過 new 關鍵字動態分配的。
Ui::Widget 是由Qt Designer工具生成的,用于處理用戶界面。這種方式允許將用戶界面的設計與后端
邏輯代碼分離,有助于提高代碼的可維護性和可讀性。
3.2 UI設計師工具
3.2.1 按鍵?QPushButton
涉及操作居多,在視頻中演示
新建一個QPushButton
屬性頁面基本使用
stylesheet初步接觸,按鍵美化操作
資源文件
stylesheet使用資源文件
3.2.2 水平布局?QHBoxLayout
新建水平布局
常用屬性和功能特征
建立布局大小
布局和父控件的關聯支持窗口變化
彈簧控件
3.2.3 文本編輯器?TextEdit
新建文本編輯器
屬性設置
TextEdit的常用C++接口
如何讀取TextEdit上的內容
如何往TextEdit上寫如內容
3.2.4 垂直布局?QVBoxLayout
同水平布局
3.2.5 主窗體元素設計
圖標設計
應用程序名稱設計連接方式
描述
示例
使用
QObject::connect
最常用的方式,直接通過
QObject::connect 函數連接信號和
槽。
QObject::connect(sender,
SIGNAL(signal()),
receiver, SLOT(slot()));
使用C++11
Lambda表達式
利用C++11引入的Lambda表達式進行
信號與槽的連接。這種方式可以直接
在連接點使用匿名函數,使代碼更加
簡潔。
QObject::connect(sender,
&Sender::signal, [=]() {
/* lambda body */ });
使用函數指針
Qt 5中引入,允許使用函數指針直接
連接信號和槽,這種方式類型安全,
且可以利用IDE的代碼補全和錯誤檢
查。
QObject::connect(sender,
&Sender::signal, receiver,
&Receiver::slot);
自動連接(使用UI
文件)
在使用Qt Designer時,可以通過命名
約定自動連接信號和槽。當UI文件加
載時,以
on_<objectName>_<signalName> 命
名的槽會自動連接到相應的信號。
在Qt Designer中命名按鈕為
pushButton ,然后在代碼中定
義
on_pushButton_clicked() 。
3.3 按鍵響應-初識信號與槽
3.3.1 信號與槽基本介紹
提出疑問,界面上已經有按鍵了,怎么操作才能讓用戶按下按鍵后有操作上的反應呢?
在?Qt 中,信號和槽機制是一種非常強大的事件通信機制。這是一個重要的概念,特別是對于初學者來
說,理解它對于編寫?Qt 程序至關重要。
概要
1. 信號?(Signals):是由對象在特定事件發生時發出的消息。例如, QPushButton 有一個
clicked() 信號,當用戶點擊按鈕時發出。
2. 槽?(Slots):是用來響應信號的方法。一個槽可以是任何函數,當其關聯的信號被發出時,該槽函數
將被調用。
3. 連接信號和槽:使用 QObject::connect() 方法將信號連接到槽。當信號發出時,關聯的槽函數
會自動執行。
3.3.2 按鍵QPushButton設置信號與槽
在?Qt 中,有幾種不同的方式來設置按鍵信號與槽的連接,主要包括:
Qt的信號和槽機制是其事件處理系統的核心。這種機制允許對象之間的通信,而不需要它們知道對方的
具體實現。以下是Qt信號和槽的幾種常見連接方式的簡要概述,我將它們整理成表格形式以便于理解:
這些方式各有優劣,選擇哪種方式取決于具體的應用場景、代碼風格以及個人偏好。例如,直接使用
QObject::connect 是最通用的方式,而使用Lambda表達式可以在同一位置編寫信號處理邏輯,提高
代碼的可讀性。使用函數指針的方式則在編譯時提供更好的類型檢查。自動連接通常在使用Qt Designer
設計UI時比較方便。
#include "widget.h"3.3.3 自定義信號與槽
在Qt中,自定義信號與槽是實現對象間通信的一種機制。信號和槽是Qt對象通信的核心特性,使得一個
對象能夠在發生某種事件時通知其他對象。自定義信號與槽的實現步驟如下:
1. 定義信號:在Qt中,信號是由 signals 關鍵字聲明的類成員函數。它們不需要實現,只需聲明。例
如:
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
//在構造函數中進行信號與槽的綁定
//第二種方式:QObject::connect(sender, SIGNAL(signal()), receiver,
SLOT(slot()));
QObject::connect(ui->btnCon, SIGNAL(clicked()), this,
SLOT(on_btnCon_clickedMyself()));
//第三方式:lambda表達式:QObject::connect(sender, &Sender::signal, [=]() { /*
lambda body */ });
QObject::connect(ui->btnLambda, &QPushButton::clicked,[=](){
std::cout << "btnLambdaClicked" << std::endl;
});
//第四種方式:QObject::connect(sender, &Sender::signal, receiver,
&Receiver::slot);
QObject::connect(ui-
>btnFortch,&QPushButton::clicked,this,&Widget::on_fortch_clicked);
}
Widget::~Widget()
{
delete ui;
}
//第一種方式:通過uiDesigner
void Widget::on_btnui_clicked()
{
std::cout << "UIBtnClicked" << std::endl;
}
void Widget::on_btnCon_clickedMyself()
{
std::cout << "btnConClicked" << std::endl;
}
void Widget::on_fortch_clicked()
{
std::cout << "btnForthClicked" << std::endl;
}在上面的例子中, MyClass 有一個名為 mySignal 的信號,它帶有一個整型參數。
定義槽:槽可以是任何普通的成員函數,但通常在類定義中用 slots 關鍵字標識。槽可以有返回類型,
也可以接受參數,但它們的參數類型需要與發出信號的參數類型匹配。例如:
在這個例子中,我們定義了一個名為 mySlot 的槽,它接收一個整型參數。
連接信號與槽:使用 QObject::connect 函數將信號與槽連接起來。當信號被發射時,連接到這個信號
的槽將被調用。
這行代碼連接了 myObject 的 mySignal 信號到同一個對象的 mySlot 槽。
發射信號:使用 emit 關鍵字發射信號。當信號被發射時,所有連接到這個信號的槽都會被調用。
這將觸發所有連接到 mySignal 的槽。
自定義信號和槽是Qt編程中非常強大的特性,它們使得組件之間的通信變得靈活而松耦合。通過信和
槽,可以方便地實現各種復雜的事件驅動邏//輯。
class MyClass : public QObject {
Q_OBJECT
public:
MyClass();
signals:
void mySignal(int value);
};
class MyClass : public QObject {
Q_OBJECT
public slots:
void mySlot(int value);
};
MyClass *myObject = new MyClass();
connect(myObject, SIGNAL(mySignal(int)), myObject, SLOT(mySlot(int)));
emit mySignal(123);
//widget.h
#ifndef WIDGET_H
#define WIDGET_H
#include <QWidget>
#include <iostream>
#include <QDebug>
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACEclass Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
signals:
void mysignal();
void mysignalparams(int value);
private slots:
void myslot();
void myslotparams(int value);
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
//widget.cpp
#include "widget.h"
#include "ui_widget.h"
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
connect(this,SIGNAL(mysignal()),this,SLOT(myslot()));
connect(this,SIGNAL(mysignalparams(int)),this,SLOT(myslotparams(int)));
emit mysignal();
emit mysignalparams(100);
}
Widget::~Widget()
{
delete ui;
}
void Widget::myslot()
{
std::cout << "myslot" << std::endl;
}
void Widget::myslotparams(int value)
{
qDebug() << "myslotparams";
qDebug() << value ;
}QDebug()
QDebug 是?Qt 框架中用于輸出調試信息的一個類。它提供了一種方便的方式來輸出文本到標準輸出(通
常是控制臺),這對于調試?Qt 應用程序非常有用。 QDebug 類可以與?Qt 的信號和槽機制一起使用,使
得在響應各種事件時能夠輸出有用的調試信息。
使用 QDebug 的一個典型方式是通過 qDebug() 函數,它返回一個 QDebug 對象。然后,可以使用流操
作符 << 來輸出各種數據類型。例如:
當執行這些代碼時,它們會在應用程序的控制臺輸出相應的文本。這對于檢查程序的運行狀態、變量的
值或者跟蹤程序的執行流程非常有幫助。
還可以使用 qDebug() 來輸出自定義類型,只要為這些類型提供了適當的輸出操作符重載。此外,Qt 還
提供了 qInfo() , qWarning() , qCritical() 和 qFatal() 函數,用于輸出不同級別的信息,分別用
于普通信息、警告、關鍵錯誤和致命錯誤。這有助于對日志信息進行級別劃分,從而更好地控制輸出內
容。
3.3 文件操作類?QFile
QFile 是?Qt 框架中用于文件處理的一個類。它提供了讀取和寫入文件的功能,支持文本和二進制文
件。
QFile 繼承自 QIODevice ,因此它可以像其他IO設備一樣使用。
主要功能
1. 文件讀寫: QFile 支持打開文件進行讀取或寫入操作
2. 文件信息:可以檢索有關文件的信息,如大小、修改日期等。
3. 文件操作:提供了對文件進行重命名、移動、刪除等操作的能力。
4. 錯誤處理: QFile 在操作文件時提供了錯誤處理機制,可以通過相應的函數檢查和獲取錯誤信息。
常用方法
open() :打開一個文件。需要指定模式(如只讀、只寫、讀寫等)。
close() :關閉文件。
read() 和 write() :用于讀取和寫入數據。
exists() :檢查文件是否存在。
remove() :刪除文件。
copy() :復制文件。
示例代碼
以下是使用 QFile 的一個簡單例子:
qDebug() << "This is a debug message";
int value = 10;
qDebug() << "The value is" << value;
#include "widget.h"
#include "ui_widget.h"
#include <QFile>
#include <QDebug>特性類別
說明
字符編碼
支持?Unicode,可以處理如?UTF-8、UTF-16 等不同編碼。通過 setCodec() 方法設
置特定編碼。
讀寫文本
用于讀寫文件、字符串或任何繼承自 QIODevice 的對象。
格式化
提供文本格式化功能,如數字精度、基數(十進制、十六進制等)調整。
流操作符
支持使用 << 和 >> 操作符,類似于?C++ 中的?iostream。
3.3.3 QTextStream
QTextStream 的主要特性成一個表格。請看下表:
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::on_btnRead_clicked()
{
//1. 打開文件
//QFile file("D:/QT/test.txt");
QFile file;
file.setFileName("D:/QT/test.txt");
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
qDebug() << "file open error";
}
//2. 讀取文件
char context[100] = {'\0'};
if( file.read(context,100) == -1) return;
//3. 輸出文件內容
qDebug() << context;
file.close();
}
void Widget::on_btnWrite_clicked()
{
// 1.打開
QFile file("D:/QT/test2.txt");
file.open(QIODevice::WriteOnly | QIODevice::Text);
// 2. 寫入
file.write("Program 45-QFile001 write something to This File 我是老陳");
// 3. 關閉
file.close();
}特性類別
說明
換行處理
自動處理不同操作系統間的換行符差異(如?Unix 的 \n 和?Windows 的 \r\n )。
錯誤處理
能夠檢測和報告在讀寫過程中出現的錯誤。
緩沖機制
提供緩沖機制,提高讀寫效率。
字符串操
作
可以方便地處理和解析字符串數據。
QTextStream 是一個功能強大的類,用于處理文本數據,特別是在需要考慮字符編碼和文本格式化的情
況下。通過這些特性,它提供了一種靈活而強大的方式來讀寫和操作文本。
使用示例
以下是一個更詳細的示例,展示了如何使用 QTextStream 來讀寫文件:
void Widget::on_btnstrRead_clicked()
{
QFile file;
file.setFileName("D:/QT/test.txt");
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
qDebug() << "file open error";
}
QTextStream in(&file);
in.setCodec("UTF-8");
// QString context = in.read(file.size());
while(!in.atEnd()){
QString context = in.readLine();
qDebug() << context;
}
file.close();
}
void Widget::on_btnstreamWrite_clicked()
{
QFile file;
file.setFileName("D:/QT/test3.txt");
if(!file.open(QIODevice::WriteOnly | QIODevice::Text)){
qDebug() << "file open error";
}
QTextStream out(&file);
out.setCodec("UTF-8");
out << "I write stream char to File";
file.close();
}3.4 文件選擇對話框?QFileDialog
3.4.1 QFileDialog開發流程
使用 QFileDialog 的基本步驟通常如下:
實例化:首先,創建一個 QFileDialog 對象的實例。
設置模式:根據需要設置對話框的模式,如打開文件、保存文件等。
設置過濾器:如果需要,可以設置文件類型過濾器,以限制用戶可以選擇的文件類型。
顯示對話框:通過調用 exec() 方法顯示對話框,并在用戶作出選擇后執行相應的操作。
通過 selectedFiles 方法獲取用戶選擇的文件路徑列表,然后對這些文件進行相應的處理。
這是使用 QFileDialog 的基本模式。Qt 也允許使用靜態方法直接創建和顯示對話框,例如
QFileDialog::getOpenFileName() ,這些方法更簡單,但提供的自定義選項較少。
3.4.2 QFileDialog 打開開發案例
QFileDialog dialog;
dialog.setFileMode(QFileDialog::AnyFile);
dialog.setNameFilter(tr("Images (*.png *.xpm *.jpg);;Text files (*.txt);;XML
files (*.xml)"));
if (dialog.exec()) {
QStringList files = dialog.selectedFiles();
// 對用戶選定的文件進行操作
}
#include <QApplication>
#include <QFileDialog>
#include <QStringList>
#include <QString>
#include <QMessageBox>
/*
fileName = QFileDialog::getOpenFileName(this, tr("Open File"), "",
tr("Text Files (*.txt);;All Files
(*)"));
*/
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 創建一個 QFileDialog 實例
QFileDialog dialog;
// 設置對話框為打開文件模式
dialog.setFileMode(QFileDialog::ExistingFiles);
// 設置文件過濾器3.4.3 QFileDialog 保存開發案例
3.6 實現文件打開功能
3.6.1 開發流程
為QPushButton對應Open的控件設置槽函數
槽函數代碼開發
打開文件
讀取文件
把文件數據顯示在TextEdit控件上
dialog.setNameFilter("Text files (*.txt);;Images (*.png *.jpg);;All files
(*)");
// 顯示對話框
if (dialog.exec()) {
// 獲取用戶選中的文件列表
QStringList fileNames = dialog.selectedFiles();
// 遍歷列表并處理每個文件
for (const QString &fileName : fileNames) {
// 此處可以添加對 fileName 的處理代碼
QMessageBox::information(nullptr, "File Selected", fileName);
}
}
return app.exec();
}
void Widget::on_btnSave_clicked()
{
QString fileName = QFileDialog::getSaveFileName(this, tr("Save File"),
"D:/QT/untitled.txt",
tr("Text (*.txt *.doc)"));
qDebug()<<fileName;
QFile file;
file.setFileName(fileName);
if(!file.open(QIODevice::WriteOnly | QIODevice::Text)){
qDebug() << "file open error";
}
QTextStream out(&file);
out.setCodec("UTF-8");
out << "Qdialog Write Data to the Txt File";
file.close();
}編碼名稱
描述
UTF-8
用于表示?Unicode 文本的變長字符編碼,廣泛用于網絡和多語言文本
UTF-16
用于表示?Unicode 文本的定長字符編碼
ISO 8859-1
也稱為?Latin1,用于表示西歐語言字符
GBK
用于表示簡體中文字符,是?GB2312 的擴展
Big5
用于表示繁體中文字符,常用于臺灣和香港地區
Windows-
1252
用于表示西歐語言字符,是?ISO 8859-1 的超集
ANSI
在?Qt 中,"ANSI" 編碼并不是一個明確指定的編碼標準,因為?ANSI 編碼可以指代
不同的編碼標準,這取決于操作系統的語言和區域設置。例如,在中文?Windows
系統中,ANSI 編碼通常指的是?GBK 編碼;而在西歐語言的?Windows 系統中,
ANSI 編碼可能指的是?ISO 8859-1 或?Windows-1252。
3.6.2 代碼實現
3.6.3 打開功能優化
字符編碼相關問題解決
在?Qt 中, QTextStream 常用的字符編碼主要包括以下幾種:
這些編碼覆蓋了大部分常用的語言字符集,可以通過 QTextCodec::codecForName() 方法在
QTextStream 中進行設置。
void Widget::on_btnFileOpen_clicked()
{
QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),
"D:/QT/",
tr("Text (*.txt)"));
ui->textEdit->clear();
file.setFileName(fileName);
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
qDebug() << "file open error";
}
QTextStream in(&file);
in.setCodec("UTF-8");
while(!in.atEnd()){
QString context = in.readLine();
// qDebug() << qPrintable(context);
// ui->textEdit->setText(context);
ui->textEdit->append(context);
}
}功能
描述
API 方法
添加選項
向下拉列表添加單個或多個選項
addItem() , addItems()
獲取選項
獲取當前選中的文本或索引
currentText() , currentIndex()
設置選項
設置當前選中的項
setCurrentIndex(int)
移除選項
從下拉列表中移除項
removeItem(int)
信號
當選項改變時觸發的事件
currentIndexChanged(int)
可編輯性
設置下拉列表是否可編輯
setEditable(bool)
自定義數據
向下拉列表項關聯額外的數據
setItemData(int, const QVariant&)
清空列表
移除所有選項
clear()
檢測光標位置,并在右下角顯示光標位置
在程序左上方顯示當前打開的文件名稱
3.6.4 QComboBox
QComboBox 是?Qt 框架中用于創建下拉列表的一個控件。
它允許用戶從一組選項中選擇一個選項,并可以配置為可編輯,使用戶能夠在其中輸入文本。
QComboBox 提供了一系列方法來添加、刪除和修改列表中的項,支持通過索引或文本檢索項,并可以通
過信號和槽機制來響應用戶的選擇變化。該
控件廣泛應用于需要從多個選項中進行選擇的用戶界面場景,例如表單和設置界面。
示例代碼
#include <QComboBox>
#include <QVBoxLayout>
#include <QWidget>
class ComboBoxDemo : public QWidget {
Q_OBJECT
public:
ComboBoxDemo() {
QComboBox *comboBox = new QComboBox(this);
comboBox->addItems({"選項1", "選項2", "選項3"});
comboBox->setEditable(true);
connect(comboBox, SIGNAL(currentIndexChanged(int)), this,
SLOT(onSelectionChanged(int)));
QVBoxLayout *layout = new QVBoxLayout(this);
layout->addWidget(comboBox);
}
private slots:
void onSelectionChanged(int index) {
// 當選中的項改變時的處理邏輯這個示例展示了 QComboBox 的基本用法,包括添加選項、設置為可編輯以及連接信號和槽。您可以根據
需要調整和擴展這個示例。
3.6.5 記事本支持字符編碼
獲取用戶在QComboBox上選擇的字符編碼,用特定編碼打開文件,這里注意QComboBox返回QString
類型,
setCodec參數要求const char*型
QString先轉成C++的String,再轉換成const char *
支持打開文件后進行字符編碼的重新選擇和顯示加載
}
};
void Widget::on_btnFileOpen_clicked()
{
// 使用文件對話框獲取要打開的文件的路徑
QString fileName = QFileDialog::getOpenFileName(this, tr("Open File"),
"D:/QT/",
tr("Text (*.txt)"));
// 清空文本編輯器的內容
ui->textEdit->clear();
// 設置 QFile 對象的文件名
file.setFileName(fileName);
// 嘗試以只讀和文本模式打開文件
if(!file.open(QIODevice::ReadOnly | QIODevice::Text)){
// 如果文件打開失敗,輸出錯誤信息
qDebug() << "file open error";
}
// 創建 QTextStream 用于讀取文件內容
QTextStream in(&file);
// 從下拉框獲取當前選中的字符編碼
QString str = ui->comboBox->currentText();
// 將 QString 轉化為 char* 類型
const char* c_str = str.toStdString().c_str();
// 設置 QTextStream 的字符編碼
in.setCodec(c_str);
// 循環讀取文件直到結束
while(!in.atEnd()){
// 讀取文件的一行
QString context = in.readLine();
// 將讀取的內容追加到文本編輯器
ui->textEdit->append(context);
}
}
//1. 在Widget的構造函數中關聯信號與槽,檢測用戶選擇條目的信號。
connect(ui-
>comboBox,SIGNAL(currentIndexChanged(int)),this,SLOT(onCurrentIndexChanged(int)))
;3.6.6 添加行列顯示
使用QTextEdit的cursorPositionChanged信號,當光標發生移動時候刷新顯示
//2. 添加槽函數,當用戶選擇信號后被調用,判斷是否當前有打開的文件,如果有,則重新用新的編碼讀取文
件并重新顯示。
// onCurrentIndexChanged 方法:當 QComboBox 的選中項變化時執行
void Widget::onCurrentIndexChanged(int index)
{
// 輸出調試信息,表示此槽函數被觸發
qDebug() << "currentIndexChanged Signal";
// 清空文本編輯器的內容
ui->textEdit->clear();
// 檢查文件是否已經打開
if(file.isOpen()){
// 輸出調試信息,表示文件是打開狀態
qDebug() << "file is Open";
// 創建 QTextStream 用于讀取文件內容
QTextStream in(&file);
// 設置 QTextStream 的字符編碼為 QComboBox 當前選中的編碼
in.setCodec(ui->comboBox->currentText().toStdString().c_str());
// 將文件指針移動到文件開始位置
file.seek(0);
// 循環讀取文件直到文件結束
while(!in.atEnd()){
// 讀取文件的一行
QString context = in.readLine();
// 將讀取的內容追加到文本編輯器
ui->textEdit->append(context);
}
}
}
//1. 在構造函數中添加信號與槽
connect(ui-
>textEdit,SIGNAL(cursorPositionChanged()),this,SLOT(onCursorPositionChanged()));
//2. 槽函數獲取textEdit的行列并顯示到QLabel上
void Widget::onCursorPositionChanged()
{
QTextCursor cursor = ui->textEdit->textCursor();
//qDebug() << cursor.blockNumber()+1 <<","<< cursor.columnNumber() + 1;
QString blockNum = QString::number(cursor.blockNumber()+1);
QString columnNum = QString::number(cursor.columnNumber()+1);
const QString labelMes = "L:"+blockNum+",C:"+columnNum+" ";
//const QString labelMes = "行:"+blockNum+",列:"+columnNum+" ";
ui->labelPosition->setText(labelMes);
}3.6.7 添加文件打開提示
3.6.8 設置當前行高亮
實現策略:
獲取當前行的光標位置,使用的信號和獲取行列值是一樣的
通過ExtraSelection來配置相關屬性
在當前行設置該屬性
實現該功能,需要用到一個API,
3.6.8.1 QList
在?Qt 框架中, QList 是一個容器類,它在內部實現上類似于一個數組,但也提供了一些鏈表的特性。
QList 的設計旨在提供一個在多數情況下既高效又方便的通用列表容器。用于存儲元素列表。它提供了
豐富的功能,包括添加、移除、訪問元素等。
QList 的內部工作原理:
1. 數組式存儲: QList 在大多數情況下使用連續內存存儲其元素,類似于數組。這意味著它提供了快
速的索引訪問(通過下標操作符 [] ),以及相對高效的迭代性能。
2. 動態調整大小:與靜態數組不同, QList 可以動態增長和縮減,自動管理內存分配。
3. 鏈表特性:雖然 QList 主要基于數組,但它也提供了一些鏈表的操作,比如在列表的開始或結束
處添加和移除元素。這些操作通常比在數組中間插入或刪除元素更高效。
4. 復制時共享內存: QList 使用一種稱為“隱式共享”(implicit sharing)或“寫時復制”(copy-on
write)的技術。這意味著當你復制一個 QList 時,它不會立即復制所有元素,而是共享相同的數
據,直到你嘗試修改其中一個列表,此時才進行實際的復制。這使得復制 QList 變得非常高效。
使用場景:
當你需要快速的隨機訪問(如通過索引訪問元素)時, QList 是一個不錯的選擇。
如果你的主要操作是在列表的兩端添加或移除元素, QList 也表現得很好。
基本用法
包含頭文件:首先,你需要包含 QList 的頭文件。
創建?QList 實例:創建一個 QList 對象,并指定存儲的元素類型。
添加元素:使用 append 或 push_back 方法添加元素。
//功能簡單,在視頻課程代碼中體現
this->setWindowTitle(fileName + "- MyNoteBook");
QList<QTextEdit::ExtraSelection> extraSelections;
void setExtraSelections(const QList<QTextEdit::ExtraSelection> &extraSelections)
#include <QList>
QList<int> list;訪問元素:可以使用下標操作符或 at() 方法訪問元素。
遍歷列表:使用迭代器或范圍基的?for 循環遍歷列表。
移除元素:使用 removeAt 、 removeOne 或 clear 方法移除元素。
3.8.2 ExtraSelection 簡介
QTextEdit::ExtraSelection 是一個在 QTextEdit 中用來表示額外的文本選擇和高亮的結構。
如何工作
1. ExtraSelection 結構體: QTextEdit::ExtraSelection 是一個結構體,包含了兩個主要成員:
QTextCursor 和 QTextCharFormat 。 QTextCursor 表示在文本中的一個位置或者區間,而
QTextCharFormat 用于定義這個區間的格式,比如背景顏色、字體等。
2. 設置?ExtraSelection:你可以創建一個或多個 ExtraSelection 對象,為它們設置相應的光標位
置和格式,然后通過 QTextEdit 的 setExtraSelections 方法將這些對象應用到文本編輯器中。
這樣,你可以對文本的特定部分應用特定的格式,而不影響其他文本。
3. 高亮當前行:要高亮顯示當前行,你需要在 cursorPositionChanged() 信號的槽函數中創建一個
ExtraSelection 對象。使用當前的 QTextCursor 對象(通過 textCursor() 方法獲取)來確
定當前行的位置,并設置背景顏色為你選擇的高亮顏色。
QTextCharFormat 類是?Qt 框架中的一部分,用于描述文本字符的格式。這個類提供了豐富的接口來設
置和獲取文本字符的各種屬性,如字體、顏色、背景色等。 QTextCharFormat 通常用于富文本處理,可
以在像 QTextEdit 和 QTextDocument 這樣的類中使用
下面列出了 QTextCharFormat 的一些常用功能和方法:
1. 設置和獲取字體樣式:
使用 setFont() 方法設置字體。
通過 font() 方法獲取當前字體。
list.append(1);
list.append(2);
list.append(3);
int firstElement = list[0];
int secondElement = list.at(1);
for(int i = 0; i < list.size(); ++i) { // size = sizeof(arr)/sizeof(arr[0])
qDebug() << list[i];
}
// 或者使用范圍基的 for 循環
for(int item : list) {
qDebug() << item;
}
list.removeAt(1); // 移除索引為 1 的元素
list.removeOne(3); // 移除一個值為 3 的元素
list.clear(); // 清空整個列表2. 設置字體屬性:
setFontWeight() : 設置字體的粗細。
setFontItalic() : 設置字體是否傾斜。
setFontUnderline() : 設置是否有下劃線。
3. 設置文本顏色和背景色:
setForeground() : 設置文本的前景色(即字體顏色)。
setBackground() : 設置文本的背景色。
4. 其他文本屬性:
setToolTip() : 設置文本的工具提示。
setAnchor() : 設置文本是否為超鏈接。
setAnchorHref() : 設置超鏈接的目標?URL。
示例代碼
下面是一個簡單的示例,展示如何在 QTextEdit 中使用 QTextCharFormat 來設置特定文本的格式:
3.7 文件保存功能優化
3.7.1 開發流程
判斷當下是否有已經打開的文件,如果有打開的文件
讀取TextEdit的內容
寫入新文件
#include <QApplication>
#include <QTextEdit>
#include <QTextCharFormat>
int main(int argc, char *argv[]) {
QApplication a(argc, argv);
QTextEdit editor;
// 創建一個 QTextCharFormat 對象
QTextCharFormat format;
format.setFontWeight(QFont::Bold);
format.setForeground(Qt::blue);
format.setBackground(Qt::yellow);
// 將格式應用到編輯器中的特定文本
QTextCursor cursor = editor.textCursor();
cursor.movePosition(QTextCursor::Start);
cursor.insertText("Hello, World!", format);
editor.show();
return a.exec();
void Widget::on_btnSave_clicked()
{3.8 關閉優化
在上節課中關閉部分稍微優化了以下,但是還是不夠, 我們應該彈出窗口多一個詢問!
3.8.1 消息對話框?QMessageBox
QMessageBox 是?Qt 框架中用于顯示消息框的一個類,它常用于向用戶顯示信息、詢問問題或者報告錯
誤。以下是 QMessageBox 的一些主要用途:
1. 顯示信息:向用戶顯示一些信息性的消息。
2. 詢問用戶決策:詢問用戶一個問題,并根據其回答做出相應的操作。
3. 報告錯誤:向用戶報告程序運行中的錯誤。
代碼示例
以下是一個簡單的 QMessageBox 使用示例,展示了如何創建一個基本的消息框:
//如果當前沒有文件打開,就彈窗讓用戶選擇新文件,創建新文件,而不是原來那樣,都彈出新的
文件保存窗口
if(!file.isOpen()){
QString fileName = QFileDialog::getSaveFileName(this, tr("Save
File"),
"D:/QT/untitled.txt",
tr("Text (*.txt
*.doc)"));
file.setFileName(fileName);
if(!file.open(QIODevice::WriteOnly | QIODevice::Text)){
qDebug() << "file open error";
}
this->setWindowTitle(fileName + "- MyNoteBook");
}
//當保存被按下,不管是已有打開的文件還是上面if滿足后用戶選擇新文件,都要讀取TextEdit
內容并寫入文件中
QTextStream out(&file);
out.setCodec(ui->comboBox->currentText().toStdString().c_str());
QString context = ui->textEdit->toPlainText();
out << context;
}
void Widget::on_btnClose_clicked()
{
ui->textEdit->clear();
if(file.isOpen()){
file.close();
this->setWindowTitle("MyNoteBook");
}
}
#include <QApplication>在這個例子中,我們創建了一個 QMessageBox 對象,并設置了窗口標題、主要文本、附加信息文本和
圖標。還添加了兩個按鈕(OK 和?Cancel),并設置了默認按鈕。通過 exec() 方法顯示消息框,并根
據用戶的選擇執行不同的操作。
由于 QMessageBox 是為標準對話框設計的,其定制能力有限,但你可以通過添加自定義按鈕來實現一
定程度的定制。例如,如果你想要添加一個自定義的按鈕,可以這樣做:
#include <QMessageBox>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QMessageBox msgBox;
msgBox.setWindowTitle("Message Title");
msgBox.setText("This is the main message text.");
msgBox.setInformativeText("This is additional informative text.");
msgBox.setIcon(QMessageBox::Information);
msgBox.addButton(QMessageBox::Ok);
msgBox.addButton(QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Ok);
int ret = msgBox.exec();
if (ret == QMessageBox::Ok) {
// 用戶點擊了 OK
} else if (ret == QMessageBox::Cancel) {
// 用戶點擊了 Cancel
}
return app.exec();
}
#include <QApplication>
#include <QMessageBox>
#include <QPushButton>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
QMessageBox msgBox;
msgBox.setText("Custom message box with a custom button");
QPushButton *customButton = msgBox.addButton("自定義的名字",
QMessageBox::ActionRole);
msgBox.exec();
if (msgBox.clickedButton() == customButton) {
// 用戶點擊了自定義按鈕
}
return app.exec();
}在這個例子中,通過 addButton() 方法添加了一個自定義按鈕。按鈕的角色被設置為
QMessageBox::ActionRole ,這意味著它將被放置在對話框的底部,與其他標準按鈕一起。通過檢查
用戶點擊的按鈕來確定是否點擊了自定義按鈕。
3.7.3 代碼實現
3.9 實現快捷鍵功能
3.9.1 快捷鍵開發基礎
在?Qt 中實現快捷鍵功能通常涉及到 QShortcut 類的使用。下面是一個簡單的代碼示例,展示了如何在
Qt 應用程序中為特定功能設置快捷鍵:
void Widget::on_btnClose_clicked()
{
QMessageBox msgBox;
int ret = QMessageBox::warning(this, tr("MyNoteBook Notice:"),
tr("The document has been modified.\n"
"Do you want to save your changes?"),
QMessageBox::Save | QMessageBox::Discard
| QMessageBox::Cancel,
QMessageBox::Save);
switch (ret) {
case QMessageBox::Save:
// Save was clicked
on_btnSave_clicked();
qDebug() << "QMessageBox::Save:";
break;
case QMessageBox::Discard:
// Don't Save was clicked
ui->textEdit->clear();
if(file.isOpen()){
file.close();
this->setWindowTitle("MyNoteBook");
}
qDebug() << "QMessageBox::Discard:";
break;
case QMessageBox::Cancel:
// Cancel was clicked
qDebug() << "QMessageBox::Cancel:";
break;
default:
// should never be reached
break;
}
}在這個示例中,當用戶按下?Ctrl + N 時,程序將彈出一個消息框。這是通過創建一個 QShortcut 對象,
并將其快捷鍵序列設置為 "Ctrl+N" 來實現的。然后,將 activated 信號連接到一個?Lambda 函數,
該函數在快捷鍵被激活時執行。這種方法非常適用于為特定操作提供快速訪問路徑。
3.9.2 上官記事本添加快捷鍵
3.10 實現字體放大縮小功能
3.10.1 滾動調節字體大小的流程
為TextEdit添加事件過濾器
重寫窗口的eventFilter函數
eventFilter設置滾輪事件和目標對象
實現字體放大縮小的功能函數
3.10.2 本節筆記失誤
啥也沒有,嘿嘿嘿
3.10.3 檢測Ctrl鍵被按下
QGuiApplication::keyboardModifiers() 是?Qt 中一個靜態函數,用于返回當前按下的鍵盤修飾符
(如?Ctrl、Shift、Alt 等)。當與 Qt::ControlModifier 結合使用時,這個函數可以用來檢測是否按下
了?Control 鍵。
例如,以下代碼片段檢查?Control 鍵是否被按下:
// 創建一個快捷鍵 (Ctrl + N) 并關聯到窗口
QShortcut *shortcut = new QShortcut(QKeySequence("Ctrl+N"), &window);
// 當快捷鍵被按下時,顯示一個消息框
QObject::connect(shortcut, &QShortcut::activated, [&]() {
QMessageBox::information(&window, "Shortcut Activated", "Ctrl+N was
pressed");
});
// 制作兩個快捷鍵
QShortcut *shortcutOpen = new QShortcut(QKeySequence("Ctrl+O"), this);
QShortcut *shortcutSave = new QShortcut(QKeySequence("Ctrl+S"), this);
// 把Ctrl+O的信號添加槽,調用打開按鍵的槽函數
QObject::connect(shortcutOpen, &QShortcut::activated, [&]() {
Widget::on_pushButtonOpen_clicked();
});
// 把Ctrl+S的信號添加槽,調用保存按鍵的槽函數
QObject::connect(shortcutSave, &QShortcut::activated, [&]() {
Widget::on_pushButtonSave_clicked();
});這里, QGuiApplication::keyboardModifiers() 返回當前按下的修飾符,而
Qt::ControlModifier 是一個枚舉值,表示?Control 鍵。使用位與運算符 & 檢查?Control 鍵是否在當
前按下的修飾符中。
3.10.4 記事本添加字體放大縮小
if (QGuiApplication::keyboardModifiers() & Qt::ControlModifier) {
// Control 鍵當前被按下
}
QShortcut *shortcutZoomIn = new QShortcut(QKeySequence(tr("Ctrl+Shift+=",
"File|Save")),this);
QShortcut *shortcutZoomOut = new QShortcut(QKeySequence(tr("Ctrl+Shift+-",
"File|Save")),this);
connect(shortcutZoomIn,&QShortcut::activated,[=](){
zoomIn();
});
connect(shortcutZoomOut,&QShortcut::activated,[=](){
zoomOut();
});
void Widget::zoomIn()
{
//獲得TextEdit的當前字體信息
QFont font = ui->textEdit->font();
//獲得當前字體的大小
int fontSize = font.pointSize();
if(fontSize == -1) return;
//改變大小,并設置字體大小
int newFontSize = fontSize+1;
font.setPointSize(newFontSize);
ui->textEdit->setFont(font);
}
void Widget::zoomOut()
{
//獲得TextEdit的當前字體信息
QFont font = ui->textEdit->font();
//獲得當前字體的大小
int fontSize = font.pointSize();
if(fontSize == -1) return;
//改變大小,并設置字體大小
int newFontSize = fontSize-1;
font.setPointSize(newFontSize);
ui->textEdit->setFont(font);
}3.10.5 事件
事件處理過程
眾所周知Qt是一個基于C++的框架,主要用來開發帶窗口的應用程序(不帶窗口的也行,但不是主流)。
我們使用的基于窗口的應用程序都是基于事件,其目的主要是用來實現回調(因為只有這樣程序的效率
才是最高的)。所以在Qt框架內部為我們提供了一些列的事件處理機制,當窗口事件產生之后,事件會
經過: 事件派發 -> 事件過濾->事件分發->事件處理 幾個階段。Qt窗口中對于產生的一系列事件都有默認
的處理動作,如果我們有特殊需求就需要在合適的階段重寫事件的處理動作,比如信號與槽就是一種
事件(event)是由系統或者?Qt 本身在不同的場景下發出的。當用戶按下/移動鼠標、敲下鍵盤,或者是
窗口關閉/大小發生變化/隱藏或顯示都會發出一個相應的事件。一些事件在對用戶操作做出響應時發出,
如鼠標/鍵盤事件等;另一些事件則是由系統自動發出,如計時器事件。
每一個Qt應用程序都對應一個唯一的 QApplication 應用程序對象,然后調用這個對象的 exec() 函
數,這樣Qt框架內部的事件檢測就開始了( 程序將進入事件循環來監聽應用程序的事件 )。
事件在Qt中產生之后,的分發過程是這樣的:
1. 當事件產生之后,Qt使用用應用程序對象調用 notify() 函數將事件發送到指定的窗口:
2. 事件在發送過程中可以通過事件過濾器進行過濾,默認不對任何產生的事件進行過濾。
3. 當事件發送到指定窗口之后,窗口的事件分發器會對收到的事件進行分類:
4. 事件分發器會將分類之后的事件(鼠標事件、鍵盤事件、繪圖事件。。。)分發給對應的事件處理
器函數進行處理,每個事件處理器函數都有默認的處理動作(我們也可以重寫這些事件處理器函
數),比如:鼠標事件:
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow* w = new MainWindow;
w.show();
return a.exec();
}
[override virtual] bool QApplication::notify(QObject *receiver, QEvent *e);
// 需要先給窗口安裝過濾器, 該事件才會觸發
[virtual] bool QObject::eventFilter(QObject *watched, QEvent *event)
[override virtual protected] bool QWidget::event(QEvent *event);
// 鼠標按下
[virtual protected] void QWidget::mousePressEvent(QMouseEvent *event);
// 鼠標釋放
[virtual protected] void QWidget::mouseReleaseEvent(QMouseEvent *event);
// 鼠標移動
[virtual protected] void QWidget::mouseMoveEvent(QMouseEvent *event);重寫事件案例
程序關閉之前的詢問,鼠標進入,鼠標離開,窗口大小改變
#include "widget.h"
#include "ui_widget.h"
#include <QDebug>
#include <QMessageBox>
#include <QWheelEvent>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
}
Widget::~Widget()
{
delete ui;
}
void Widget::enterEvent(QEvent *event)
{
qDebug() << "mouse enter";
}
void Widget::leaveEvent(QEvent *event)
{
qDebug() << "mouse leave";
}
void Widget::wheelEvent(QWheelEvent *event)
{
qDebug() << event->angleDelta();
}
void Widget::closeEvent(QCloseEvent *event)
{
int ret = QMessageBox::warning(this, tr("My Application"),
tr("close the window\n"
"Do you want to close the window?"),
QMessageBox::Ok | QMessageBox::No
);
switch(ret){
case QMessageBox::Ok:
event->accept();
break;
case QMessageBox::No:
event->ignore();
break;
}
}
void Widget::resizeEvent(QResizeEvent *event)
{
qDebug() << "oldSize:" << event->oldSize()
<< "newSize:" << event->size();自定義按鍵
mybutton.h
mybutton.cpp
}
void Widget::on_pushButton_clicked()
{
}
#ifndef MYBUTTON_H
#define MYBUTTON_H
#include <QWidget>
class MyButton : public QWidget
{
Q_OBJECT
private:
QPixmap pic;
public:
explicit MyButton(QWidget *parent = nullptr);
protected:
void mousePressEvent(QMouseEvent *event) override;
void leaveEvent(QEvent *event) override;
void enterEvent(QEvent *event) override;
void paintEvent(QPaintEvent *event) override;
signals:
void clicked();
};
#endif // MYBUTTON_H
#include "mybutton.h"
#include <QPainter>
MyButton::MyButton(QWidget *parent) : QWidget(parent)
{
pic.load(":/o1.png");
setFixedSize(pic.size());
update();
}
void MyButton::mousePressEvent(QMouseEvent *event)
{
pic.load(":/o3.png");
update();widget.cpp
事件方式實現字體放大縮小
自定義控件MyTextEdit
頭文件
emit clicked();
}
void MyButton::leaveEvent(QEvent *event)
{
pic.load(":/o1.png");
update();
}
void MyButton::enterEvent(QEvent *event)
{
pic.load(":/o2.png");
update();
}
void MyButton::paintEvent(QPaintEvent *event)
{
QPainter painter(this);
painter.drawPixmap(rect(),pic);
}
#include "widget.h"
#include "ui_widget.h"
#include "mybutton.h"
#include <QDebug>
Widget::Widget(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Widget)
{
ui->setupUi(this);
connect(ui->mybtn,&MyButton::clicked,[=](){
qDebug() << "myButton is clicked !";
});
}
Widget::~Widget()
{
delete ui;
}
#ifndef MYTEXTEDIT_H
#define MYTEXTEDIT_H
#include <QTextEdit>實現文件
class MyTextEdit : public QTextEdit
{
public:
MyTextEdit(QWidget *parent);
protected:
void wheelEvent(QWheelEvent *e) override;
void keyPressEvent(QKeyEvent *e) override;
void keyReleaseEvent(QKeyEvent *e) override;
private:
bool ctrlKeyPressed = 0;
};
#endif // MYTEXTEDIT_H
#include "mytextedit.h"
#include <QWheelEvent>
#include <QDebug>
MyTextEdit::MyTextEdit(QWidget *parent) : QTextEdit(parent)
{
}
void MyTextEdit::wheelEvent(QWheelEvent *e)
{
//qDebug() << e->angleDelta().y();
if(ctrlKeyPressed == 1){
if(e->angleDelta().y() > 0){
zoomIn();
}else if(e->angleDelta().y() < 0){
zoomOut();
}
e->accept();
}else{
QTextEdit::wheelEvent(e);
}
}
void MyTextEdit::keyPressEvent(QKeyEvent *e)
{
if(e->key() == Qt::Key_Control){
// qDebug() << "ctrl Pressed";
ctrlKeyPressed = 1;
}
QTextEdit::keyPressEvent(e);
}
void MyTextEdit::keyReleaseEvent(QKeyEvent *e)
{事件過濾器
我們通過繼承QTextEdit來重寫事件實現Ctrl加滾輪的檢測,還有一種處理方式,叫做事件過濾器
在Qt的事件處理過程中,引入事件過濾器(Event Filter)可以讓你在事件達到目標對象之前進行攔截和
處理。這是一種強大的機制,允許你在不同對象間共享事件處理邏輯或在父對象中集中處理特定事件。
下面是加入事件過濾器的步驟:
1. 定義事件過濾器: 事件過濾器通常是一個重寫了 QObject::eventFilter() 方法的對象。這個方法
會在事件傳遞給目標對象之前被調用。
2. 安裝事件過濾器: 使用 QObject::installEventFilter() 方法安裝事件過濾器。這個方法告訴Qt
在將事件發送給特定對象之前先通過過濾器對象。例如,如果你想在父窗口中過濾子窗口的事件,
你需要在父窗口的對象上調用 installEventFilter() ,并將子窗口作為參數傳遞。
3. 事件過濾器邏輯: 在 eventFilter() 方法內部,你可以編寫自定義邏輯來決定如何處理或忽略事
件。如果此方法返回 true ,則表示事件已被處理,不應該繼續傳遞;如果返回 false ,則事件將
正常傳遞給目標對象。
4. 事件分發: 當事件發生時,Qt首先將事件發送到安裝了事件過濾器的對象。在這一步,
eventFilter() 方法被調用。
5. 決定是否傳遞事件: 根據 eventFilter() 方法的返回值,Qt決定是否繼續向目標對象傳遞事件。如
果過濾器返回 true ,事件處理到此結束;如果返回 false ,事件繼續傳遞到原始目標對象。
6. 目標對象處理事件: 如果事件過濾器允許事件繼續傳遞,目標對象將像沒有事件過濾器存在時那樣處
理事件。
事件過濾器特別適用于以下情況:
當你想在不修改子類代碼的情況下改變事件的行為。
當多個對象需要共享相同的事件處理邏輯。
當你需要在更高的層級上監控或修改應用程序的事件流。
通過使用事件過濾器,Qt應用程序可以獲得更大的靈活性和更細粒度的事件處理控制。
3.10.6 鼠標滾輪和字體大小
if(e->key() == Qt::Key_Control){
// qDebug() << "ctrl Release";
ctrlKeyPressed = 0;
}
QTextEdit::keyPressEvent(e);
}
ui->textEdit->installEventFilter(this); // 給textEdit安裝了事件過濾器,為滾輪字體做準備
bool Widget::eventFilter(QObject *watched, QEvent *event)
{
if (event->type() == QEvent::Wheel) {
QWheelEvent *wheelEvent = dynamic_cast<QWheelEvent *>(event);
//檢查鍵盤的CTRL是否被按下
if (QGuiApplication::keyboardModifiers() & Qt::ControlModifier){
if (wheelEvent->angleDelta().y() > 0) {
zoomInText(); // 滾輪向上滾動在?C++ 中,強制類型轉換(或類型轉換)是一種將變量從一種類型轉換為另一種類型的方法。C++ 提供
了四種強制轉換運算符,每種都有其特定的用途和適用場景:
1. static_cast
static_cast 是最常用的類型轉換運算符,用于無風險的轉換,如整數到浮點數,字符到整
數等。
它在編譯時執行,不執行運行時類型檢查(RTTI)。
示例: int x = static_cast<int>(y); 其中 y 可能是?float 類型。
2. dynamic_cast
專門用于處理對象的多態性,只能用于指針和引用,且涉及對象類必須有虛函數。
它在運行時檢查類型的安全性,如果轉換失敗,對于指針類型返回 nullptr ,對于引用類型
拋出異常。
示例: Derived *dp = dynamic_cast<Derived *>(bp); 其中 bp 是基類指針, Derived
是派生類。
3. const_cast
用于修改類型的?const 或?volatile 屬性。
通常用于去除對象的?const 性質,允許修改原本被聲明為?const 的變量。
示例: const int a = 10; int* b = const_cast<int*>(&a);
4. reinterpret_cast
用于進行低級別的重新解釋轉換,幾乎無限制,但也是最危險的。
它可以將一種完全不相關的類型轉換為另一種類型,比如將指針類型轉換為整數類型。
示例: long p = reinterpret_cast<long>(&object); 其中 object 是某個類的對象。
} else {
zoomOutText(); // 滾輪向下滾動
}
}
return true;//表示事件已被處理
}
return QWidget::eventFilter(watched, event);
}
void Widget::zoomInText()
{
// 放大字體
QFont font = ui->textEdit->font();
font.setPointSize(font.pointSize() + 1);
ui->textEdit->setFont(font);
}
void Widget::zoomOutText()
{
// 縮小字體
QFont font = ui->textEdit->font();
font.setPointSize(font.pointSize() - 1);
ui->textEdit->setFont(font);
}類別
功能
描述
UI設計師基本控件操作
Widget
基礎的用戶界面單元,用于構建復雜的用戶界
面。
QPushButton
用于創建按鈕。
QHBoxLayout
水平布局管理器,用于水平排列控件。
QVBoxLayout
垂直布局管理器,用于垂直排列控件。
TextEdit
多行文本編輯器控件。
Stylesheet
使用樣式表來定制控件的外觀。
文件操作類
QFile
用于讀取和寫入文件。
文件選擇對話框類
QFileDialog
提供了一個對話框,允許用戶選擇文件或目錄。
QT的信號與槽
-
用于對象之間的通信機制。
消息對話框
QMessageBox
用于顯示信息、警告、錯誤等對話框。
快捷鍵捕獲和處理
-
用于捕獲和處理鍵盤快捷鍵。
Ctrl按鍵信號捕獲和處理
-
專門處理Ctrl按鍵的信號。
鼠標滾輪信號捕獲和處
理
-
用于捕獲和處理鼠標滾輪動作。
事件處理
event
用于處理不同的事件。
文本字符編碼檢測
-
用于檢測和處理文本的字符編碼。
3.12 記事本項目總結
類別
功能
描述
字體放大縮小
-
用于調整字體大小。
QT程序開發流程
-
涉及從設計到部署的整個開發流程。
P4 串口調試助手項目
4.1 項目概述
項目功能描述
見下方界面,所見即所得!
4.2 串口通信核心代碼開發
代碼會放在網盤上全程高能力輸出-代碼開發和調試都在視頻里P5 網絡調試助手
5.1 TCP網絡調試助手
5.1.1 項目概述
網絡相關的一些基礎概念-面試用
學習QTcpServer
學習QTcpClient
學習TextEdit特定位置輸入文字顏色
學習網絡通信相關知識點
復習鞏固之前UI控件
程序運行如下圖所示5.1.2 開發流程
5.1.3 QTtcp服務器的關鍵流程
工程建立,需要在.pro加入網絡權限
創建一個基于 QTcpServer 的服務端涉及以下關鍵步驟:
1. 創建并初始化 QTcpServer 實例:
實例化 QTcpServer 。
調用 listen 方法在特定端口監聽傳入的連接。
2. 處理新連接:
為 newConnection 信號連接一個槽函數。
在槽函數中,使用 nextPendingConnection 獲取 QTcpSocket 以與客戶端通信。
3. 讀取和發送數據:
通過連接 QTcpSocket 的 readyRead 信號來讀取來自客戶端的數據。
使用 write 方法發送數據回客戶端。
4. 關閉連接:在適當的時候關閉 QTcpSocket 。
示例代碼可能如下:
確保在使用 QTcpServer 和 QTcpSocket 時妥善處理網絡錯誤和異常情況。
5.1.4 QTtcp客戶端的關鍵流程
工程建立,需要在.pro加入網絡權限
創建一個基于 QTcpSocket 的Qt客戶端涉及以下步驟:
1. 創建 QTcpSocket 實例:
實例化 QTcpSocket 。
2. 連接到服務器:
使用 connectToHost 方法連接到服務器的IP地址和端口。
3. 發送數據到服務器:
使用 write 方法發送數據。
4. 接收來自服務器的數據:
為 readyRead 信號連接一個槽函數來接收數據。
5. 關閉連接:
class MyServer : public QObject {
Q_OBJECT
public:
MyServer() {
QTcpServer *server = new QTcpServer(this);
connect(server, &QTcpServer::newConnection, this,
&MyServer::onNewConnection);
server->listen(QHostAddress::Any, 1234);
}
private slots:
void onNewConnection() {
QTcpSocket *clientSocket = server->nextPendingConnection();
connect(clientSocket, &QTcpSocket::readyRead, this,
&MyServer::onReadyRead);
// ...
}
void onReadyRead() {
QTcpSocket *clientSocket = qobject_cast<QTcpSocket *>(sender());
// 讀取數據
QByteArray data = clientSocket->readAll();
// 處理數據
// ...
}
};關閉 QTcpSocket 連接。
示例代碼如下:
這個客戶端嘗試連接到指定的服務器地址和端口,然后等待和處理來自服務器的數據。記得根據需要管
理和處理網絡錯誤和異常情況。
5.1.2 TCP協議
以下內容自省閱讀和消化,主要在面試之前類似八股文問答,實際編程我們不需要關系這么多,
QTcpSocket類底下的API已經做好所有的封裝。
TCP(傳輸控制協議)是一種廣泛使用的網絡通信協議,設計用于在網絡中的計算機之間可靠地傳輸數
據。它是互聯網協議套件的核心部分,通常與IP(互聯網協議)一起使用,合稱為TCP/IP。以下是TCP協
議的一些基本特點:
1. 面向連接:在數據傳輸之前,TCP 需要在發送方和接收方之間建立一個連接。這包括三次握手過
程,確保兩端都準備好進行數據傳輸。
2. 可靠傳輸:TCP 提供可靠的數據傳輸服務,這意味著它保證數據包準確無誤地到達目的地。如果發
生數據丟失或錯誤,TCP 會重新發送數據包。
3. 順序控制:TCP 保證數據包的傳輸順序。即使數據包在網絡中的傳輸順序被打亂,接收方也能按照
正確的順序重組這些數據。
4. 流量控制:TCP 使用窗口機制來控制發送方的數據傳輸速率,以防止網絡過載。這有助于防止接收
方被發送方發送的數據所淹沒。
5. 擁塞控制:TCP 還包括擁塞控制機制,用來檢測并防止網絡擁塞。當網絡擁塞發生時,TCP 會減少
其數據傳輸速率。
6. 數據分段:大塊的數據在發送前會被分割成更小的段,以便于傳輸。這些段會被獨立發送并在接收
端重新組裝。
7. 確認和重傳:接收方對成功接收的數據包發送確認(ACK)信號。如果發送方沒有收到確認,它會
重傳丟失的數據包。
8. 終止連接:數據傳輸完成后,TCP 連接需要被正常關閉,這通常涉及到四次揮手過程。
class MyClient : public QObject {
Q_OBJECT
public:
MyClient() {
QTcpSocket *socket = new QTcpSocket(this);
connect(socket, &QTcpSocket::readyRead, this, &MyClient::onReadyRead);
socket->connectToHost("server_address", 1234);
}
private slots:
void onReadyRead() {
QTcpSocket *socket = qobject_cast<QTcpSocket *>(sender());
QByteArray data = socket->readAll();
// 處理接收到的數據
// ...
}
};TCP 適用于需要高可靠性的應用,如網頁瀏覽、文件傳輸、電子郵件等。然而,由于它的這些特性,TCP
在處理速度上可能不如其他協議(如UDP)那么快速。
TCP協議中的三次握手和四次揮手是建立和終止連接的重要過程。下面是它們的簡要描述:
三次握手(建立連接)
三次握手的主要目的是在兩臺設備之間建立一個可靠的連接。它包括以下步驟:
1. SYN:客戶端向服務器發送一個SYN(同步序列編號)報文來開始一個新的連接。此時,客戶端進
入SYN-SENT狀態。
2. SYN-ACK:服務器接收到SYN報文后,回復一個SYN-ACK(同步和確認)報文。此時服務器進入
SYN-RECEIVED狀態。
3. ACK:客戶端接收到SYN-ACK后,發送一個ACK(確認)報文作為回應,并進入ESTABLISHED(已
建立)狀態。服務器在收到這個ACK報文后,也進入ESTABLISHED狀態。這標志著連接已經建立。
四次揮手(斷開連接)
四次揮手的目的是終止已經建立的連接。這個過程包括以下步驟:
1. FIN:當通信的一方完成數據發送任務后,它會發送一個FIN(結束)報文來關閉連接。發送完FIN
報文后,該方進入FIN-WAIT-1狀態。
2. ACK:另一方接收到FIN報文后,發送一個ACK報文作為回應,并進入CLOSE-WAIT狀態。發送FIN
報文的一方在收到ACK后,進入FIN-WAIT-2狀態。
3. FIN:在等待一段時間并完成所有數據的發送后,CLOSE-WAIT狀態的一方也發送一個FIN報文來請
求關閉連接。
4. ACK:最初發送FIN報文的一方在收到這個FIN報文后,發送一個ACK報文作為最后的確認,并進入
TIME-WAIT狀態。經過一段時間后,確保對方接收到了最后的ACK報文,該方最終關閉連接。在這兩個過程中,三次握手主要確保雙方都準備好進行通信,而四次揮手則確保雙方都已經完成通信并
同意關閉連接。
5.1.4 Socket
Socket 不是一個協議,而是一種編程接口(API)或機制,用于在網絡中實現通信。Socket 通常在應用
層和傳輸層之間提供一個端點,使得應用程序可以通過網絡發送和接收數據。它支持多種協議,主要是
TCP 和?UDP。
以下是?Socket 的一些基本特點:
類型:有兩種主要類型的?Sockets —— TCP Socket(面向連接,可靠)和?UDP Socket(無連接,
不可靠)。
應用:在各種網絡應用中廣泛使用,如網頁服務器、聊天應用、在線游戲等。
編程語言支持:大多數現代編程語言如?Python, Java, C++, 等都提供?Socket 編程的支持。
功能:提供了創建網絡連接、監聽傳入的連接、發送和接收數據等功能。
QT:?在QT組件中,QTcpSocket用來管理和實現TCP Socket通信,QUdpSocket用來管理和實現
UDP Socket通信
總之,Socket 是實現網絡通信的基礎工具之一,它抽象化了網絡層的復雜性,為開發者提供了一種相對
簡單的方式來建立和管理網絡連接。
5.2 UI設計
UI設計過程,教學視頻展示,都是大家熟悉的內容了5.3 網絡通信核心代碼
QTcpServer 是?Qt 網絡模塊的一部分,用于構建?TCP 服務器。它提供了一種機制來異步監聽來自客戶
端的連接。一旦接受了一個連接,服務器就可以與客戶端進行數據交換。
5.3.1 創建TCP服務端的核心代碼
主要步驟如下:
1. 創建 QTcpServer 實例:啟動服務器并開始監聽指定端口。
2. 監聽連接請求:調用 listen() 方法使服務器監聽特定的?IP 地址和端口。
3. 接受連接:當客戶端嘗試連接時, QTcpServer 產生一個信號。你需要實現一個槽(slot)來響應
這個信號,并接受連接。
4. 處理客戶端連接:每個連接的客戶端都關聯一個 QTcpSocket 對象,用于數據交換。
示例代碼
代碼解釋
#include <QTcpServer>
#include <QTcpSocket>
#include <QCoreApplication>
#include <QDebug>
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
QTcpServer server;
// 監聽端口
if (!server.listen(QHostAddress::Any, 12345)) {
qDebug() << "Server could not start";
return -1;
}
qDebug() << "Server started!";
// 當有新連接時,執行相應的操作
QObject::connect(&server, &QTcpServer::newConnection, [&]() {
QTcpSocket *client = server.nextPendingConnection();
QObject::connect(client, &QTcpSocket::readyRead, [client]() {
QByteArray data = client->readAll();
qDebug() << "Received data:" << data;
client->write("Hello, client!");
});
QObject::connect(client, &QTcpSocket::disconnected, client,
&QTcpSocket::deleteLater);
});
return a.exec();
}1. 創建 QTcpServer 對象:在主函數中,直接創建了一個 QTcpServer 對象。
2. 監聽端口:使用 listen() 方法監聽所有接口上的?12345 端口。
3. 處理新連接:通過連接 newConnection 信號,當有新客戶端連接時,會調用相應的槽函數。
4. 讀取數據:為每個連接的客戶端創建 QTcpSocket 對象,并連接 readyRead 信號以接收數據。
5. 發送數據:向客戶端發送響應消息。
6. 客戶端斷開連接時的處理:使用 disconnected 信號確保客戶端在斷開連接時被適當地清理。
這個代碼示例展示了如何使用 QTcpServer 創建一個基本的?TCP 服務器,而無需通過繼承來擴展類。這
種方式通常更簡單,適用于不需要復雜處理的基本應用場景。
5.3.2 創建TCP客戶端的核心代碼
為了使客戶端代碼更具模塊化和響應性,可以使用?Qt 的信號與槽機制。這種方法允許客戶端以事件驅動
的方式響應網絡事件,如連接建立、數據接收等。下面是一個使用信號與槽的?TCP 客戶端示例。
示例代碼
#include <QTcpSocket>
#include <QCoreApplication>
#include <QDebug>
class TcpClient : public QObject {
Q_OBJECT
public:
TcpClient(const QString &host, quint16 port) {
connect(&socket, &QTcpSocket::connected, this, &TcpClient::onConnected);
connect(&socket, &QTcpSocket::readyRead, this, &TcpClient::onReadyRead);
socket.connectToHost(host, port);
}
private slots:
void onConnected() {
qDebug() << "Connected to server!";
socket.write("Hello, server!");
}
void onReadyRead() {
QByteArray data = socket.readAll();
qDebug() << "Server said:" << data;
socket.disconnectFromHost();
}
private:
QTcpSocket socket;
};
int main(int argc, char *argv[]) {
QCoreApplication a(argc, argv);
TcpClient client("localhost", 12345);代碼解釋
1. 創建 TcpClient 類:這個類繼承自 QObject ,允許使用信號與槽機制。
2. 連接信號和槽:在構造函數中,將 QTcpSocket 的 connected 和 readyRead 信號分別連接到
onConnected 和 onReadyRead 槽。
3. 連接到服務器:使用 connectToHost() 方法開始連接過程。
4. 處理連接建立:一旦連接建立, onConnected 槽被觸發,客戶端向服務器發送一條消息。
5. 接收數據:當數據可讀時, onReadyRead 槽被觸發,客戶端讀取并打印來自服務器的數據。
6. 斷開連接:在接收數據后,客戶端斷開與服務器的連接。
這個客戶端示例展示了如何使用?Qt 的信號與槽機制來處理?TCP 連接。這種方式使得代碼更加清晰,易
于維護,并且能更好地處理異步事件。
5.4 TCP服務端項目開發
核心代碼
return a.exec();
}
#include "main.moc"
// 包含主窗口和用戶界面定義
#include "mainwindow.h"
#include "ui_mainwindow.h"
// 主窗口構造函數
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
// 初始化用戶界面
ui->setupUi(this);
// 設置主窗口中心部件的布局
ui->centralwidget->setLayout(ui->verticalLayout_2);
// 設置主窗口標題
this->setWindowTitle("網絡調試助手服務端-上官QT案例");
cursor = ui->textBrowserRev->textCursor(); // 獲取文本瀏覽器的文本光標
// 初始時禁用“停止監聽”按鈕
ui->pushButtonListenStop->setEnabled(false);
// 創建新的 TCP 服務器實例
tcpServer = new QTcpServer(this);
// 將新連接信號連接到處理新連接的槽函數
connect(tcpServer, SIGNAL(newConnection()), this,
SLOT(mnewConnectionHandler()));
// 獲取系統上所有網絡接口,并將 IPv4 地址添加到下拉列表中
QList<QNetworkInterface> interfaces = QNetworkInterface::allInterfaces();for (const QNetworkInterface &interface : interfaces) {
for (const QNetworkAddressEntry &entry : interface.addressEntries()) {
if (entry.ip().protocol() == QAbstractSocket::IPv4Protocol) {
ui->comboBoxIpAddr->addItem(entry.ip().toString());
}
}
}
}
// 主窗口析構函數
MainWindow::~MainWindow()
{
// 釋放用戶界面資源
delete ui;
}
// “開始監聽”按鈕的點擊事件處理函數
void MainWindow::on_pushButtonListen_clicked()
{
// 偵聽指定 IP 地址和端口
tcpServer->listen(QHostAddress(ui->comboBoxIpAddr->currentText()),
ui->lineEditPort->text().toInt());
// 更新按鈕狀態
ui->pushButtonListen->setEnabled(false);
ui->pushButtonListenStop->setEnabled(true);
}
// 新 TCP 連接的處理函數
void MainWindow::mnewConnectionHandler()
{
// 獲取下一個待處理的連接
QTcpSocket *tmpSocket = tcpServer->nextPendingConnection();
// 向文本瀏覽器中添加客戶端信息
ui->textBrowserRev->append("服務器: 客戶端IP地址是:"+ tmpSocket-
>peerAddress().toString()
+" 客戶端端口號是: "+QString::number(tmpSocket-
>peerPort())+"\n");
// 連接套接字的狀態變化和數據接收信號到相應槽函數
connect(tmpSocket, SIGNAL(stateChanged(QAbstractSocket::SocketState)),
this, SLOT(mstateChanged(QAbstractSocket::SocketState)));
connect(tmpSocket, SIGNAL(readyRead()), this, SLOT(mreadData()));
}
// 套接字狀態改變時的槽函數
void MainWindow::mstateChanged(QAbstractSocket::SocketState state)
{
// 獲取發送信號的套接字對象
QTcpSocket *tmp = (QTcpSocket *)sender();
// 根據套接字的不同狀態進行不同處理
switch(state){
case QAbstractSocket::UnconnectedState:
// 客戶端斷開連接ui->textBrowserRev->append("服務器:有客戶端斷開連接!");
tmp->deleteLater();
break;
case QAbstractSocket::ConnectedState:
// 客戶端連接
ui->textBrowserRev->append("服務器:有新客戶端接入!");
break;
default:
break;
}
}
// “停止監聽”按鈕的點擊事件處理函數
void MainWindow::on_pushButtonListenStop_clicked()
{
// 更新按鈕狀態
ui->pushButtonListen->setEnabled(true);
ui->pushButtonListenStop->setEnabled(true);
// 停止監聽端口
tcpServer->close();
}
// 接收到數據時的槽函數
void MainWindow::mreadData()
{
// 獲取發送信號的套接字對象
QTcpSocket *tmp = (QTcpSocket *)sender();
setTextColor(0,0,0); // 設置文本顏色為紅色
cursor.insertText("客戶端:"+ tmp->readAll()+"\n");
}
// “發送”按鈕的點擊事件處理函數
void MainWindow::on_pushButtonSend_clicked()
{
// 查找所有的子 QTcpSocket 對象
QList<QTcpSocket*> socketList = tcpServer->findChildren<QTcpSocket*>();
// 向每個連接的客戶端發送數據
foreach(QTcpSocket *tmp, socketList){
tmp->write(ui->textEditSnd->toPlainText().toUtf8());
setTextColor(255,0,0); // 設置文本顏色為紅色
cursor.insertText("服務端:"+ui->textEditSnd-
>toPlainText().toUtf8()+"\n");
};
}
// 設置文本顏色的函數
void MainWindow::setTextColor(int r, int g, int b)
{
QTextCharFormat textFormat;
textFormat.setForeground(QBrush(QColor(r, g, b))); // 根據提供的 RGB 值設置顏色
// 應用格式到光標
cursor.setCharFormat(textFormat); }