一 Qt初識
暫時不寫了
我的理解是類似于c#,是一個組件庫,不局限是一個組件框架。
二 Qt Core??
Qt Core 是 Qt 框架的基礎模塊,提供非 GUI 的核心功能:
核心類:
QObject
(信號槽機制)、QEvent
(事件處理)、QThread
(線程)數據序列化:
QDataStream
、QSettings
文件處理:
QFile
、QDir
、QFileInfo
時間日期:
QDateTime
、QTimer
容器類:
QList
、QVector
、QMap
?等字符串處理:
QString
(Unicode)、QByteArray
(二進制數據)資源管理:
QResource
(嵌入資源文件)插件系統:
QPluginLoader
1. QObject
所有 Qt 對象的基類
支持信號槽、事件處理、對象樹管理
所有的對象都繼承于QObject(與java挺類似)
class MyObject : public QObject {Q_OBJECT // 啟用元對象系統
signals:void mySignal();
};
2. QString
屬于core,創建對象可以使用構造函數。或者直接Qstring str= “”;
特點:
存儲?UTF-16?編碼的 Unicode 字符串
隱式共享(Copy-on-Write)優化內存
自動處理內存分配
?2.1 常用操作
QString str = "Hello, 世界!"; // 自動處理多語言// 拼接
str.append(" 😊");
str = QString("%1 %2").arg("Qt", "6.5");// 分割
QStringList list = str.split(",");// 轉換
std::string s = str.toStdString();
const char* cstr = str.toUtf8().constData();// 查找/替換
if (str.contains("世界")) str.replace("世界", "World");
2.2 常用函數?
toInt(), toDouble():類型轉換trimmed():移除空白toUpper()/toLower():大小寫轉換sprintf():C 風格格式化(已棄用,推薦 arg())
append:字符串拼接preappend:在前面拼接arg: 參數 Qstring("xxx%1yyy%2").arg(1).arg(2);contains:包含isNull: 判NullisEmpty:判EmptystartWith:以什么開始endWith:以什么結束indexOf:在那個位置replace:替換split:拆分
3.?QByteArray
用途:
存儲原始字節(
char*
)處理二進制數據(如圖片、網絡數據)
8-bit 文本(ASCII/Latin-1)
和Qstring的相互轉換
new Qstirng(QByteArray)
Qstirng.toUtf8();
QByteArray data("\x48\x65\x6C\x6C\x6F", 5); // "Hello"// 編碼轉換
QByteArray base64 = data.toBase64(); // "SGVsbG8="
QByteArray hex = data.toHex(); // "48656c6c6f"// 與 QString 互轉
QString str = QString::fromUtf8(data);
QByteArray utf8 = str.toUtf8();// 直接訪問內存
char* rawData = data.data();
4. 集合
Qt 提供類似 STL 但更易用的容器,支持?隱式共享。
(1) 順序容器
類型 特點 示例 QList 動態數組(最優通用容器) QList<int> list = {1,2}
QVector 連續內存(Qt6 中同 QList) QVector<QString> vec;
QStack LIFO(繼承自 QVector) stack.push(10);
QQueue FIFO(繼承自 QList) queue.enqueue("A");
QList<QString> fruits = {"Apple", "Banana"};
fruits.prepend("Mango"); // 頭部添加
fruits.removeLast(); // 刪除尾部
(2) 關聯容器
類型 特點 示例 QMap 有序鍵值對(紅黑樹) QMap<QString, int> map;
QHash 哈希表(更快查找) QHash<int, QString> hash;
QSet 無序唯一值集合 QSet<QString> set;
QMap<QString, int> ages;
ages["Alice"] = 30;
ages.insert("Bob", 25);if (ages.contains("Alice")) qDebug() << ages.value("Alice"); // 30
(3) 迭代器
// Java 風格迭代器
QListIterator<QString> it(list);
while (it.hasNext()) qDebug() << it.next();// STL 風格迭代器(更快)
QList<QString>::const_iterator stlIt;
for (stlIt = list.begin(); stlIt != list.end(); ++stlIt)qDebug() << *stlIt;
?5. 其他核心類
QVariant:萬能數據類型容器
QVariant v1 = 42; // int
QVariant v2 = "Qt"; // QString
int i = v1.toInt(); // 42
QObject:所有 Qt 對象的基類
支持信號槽、事件處理、對象樹管理
class MyObject : public QObject {Q_OBJECT // 啟用元對象系統
signals:void mySignal();
};
QFile:文件 I/O
QFile file("data.txt");
if (file.open(QIODevice::ReadOnly)) {QByteArray data = file.readAll();
}
5.? 日期,定時器,線程等
5.1 日期
類名 描述 示例 QDate 處理日期(年月日) QDate(2023, 12, 31)
QTime 處理時間(時分秒毫秒) QTime(14, 30, 45, 500)
QDateTime 組合日期和時間(含時區支持) QDateTime::currentDateTime()
(2) 關鍵操作?
// 獲取當前時間
QDateTime now = QDateTime::currentDateTime();// 日期運算
QDateTime tomorrow = now.addDays(1);
QDateTime nextHour = now.addSecs(3600);// 格式化輸出
qDebug() << "ISO格式: " << now.toString(Qt::ISODate); // 2023-12-31T14:30:45
qDebug() << "自定義格式: " << now.toString("yyyy-MM-dd hh:mm:ss.zzz");// 時間差計算
QDateTime deadline(QDate(2024, 1, 1), QTime(0, 0));
qint64 secsRemaining = QDateTime::currentSecsTo(deadline);// 時區轉換
QDateTime utcTime = now.toUTC();
QDateTime localTime = utcTime.toLocalTime();
?(3) 實用技巧
// 計算程序耗時
QElapsedTimer timer;
timer.start();
// ... 執行操作 ...
qDebug() << "耗時:" << timer.elapsed() << "毫秒";// 周/月計算
QDate today = QDate::currentDate();
qDebug() << "本周第一天:" << today.addDays(1 - today.dayOfWeek());
qDebug() << "本月天數:" << today.daysInMonth();
5.2 定時器
(1) 定時器類型
類型 描述 觸發方式 單次定時器 觸發一次后自動停止 setSingleShot(true)
間隔定時器 按固定間隔重復觸發 start(interval)
精確定時器 高精度定時(Qt.PreciseTimer) setTimerType()
(2) 基本用法
// 創建定時器
QTimer *timer = new QTimer(this);// 連接信號槽
connect(timer, &QTimer::timeout, []() {qDebug() << "定時觸發!";
});// 啟動定時器(1秒間隔)
timer->start(1000); // 停止定時器
timer->stop();
(3) 高級用法
// 單次延時執行
QTimer::singleShot(2000, []() {qDebug() << "2秒后執行";
});// 多定時器管理
QTimer *timer1 = new QTimer(this);
QTimer *timer2 = new QTimer(this);// 使用不同精度
timer1->setTimerType(Qt::PreciseTimer); // 毫秒級精度
timer2->setTimerType(Qt::CoarseTimer); // 5%誤差容忍// 定時器ID管理
int timerId = startTimer(500); // 需重寫timerEvent()
5.3 線程
(1) 線程創建方式
方式 適用場景 特點 繼承QThread 簡單任務 重寫run()方法 moveToThread 復雜對象(推薦) 利用事件循環 QtConcurrent 并行計算 無需顯式管理線程
(2) 繼承QThread示例
class WorkerThread : public QThread {Q_OBJECT
protected:void run() override {// 線程執行體for(int i=0; i<10; i++) {qDebug() << "Working..." << i;sleep(1);emit progress(i*10);}}
signals:void progress(int percent);
};// 使用線程
WorkerThread *thread = new WorkerThread;
connect(thread, &WorkerThread::progress, this, &MainWindow::updateProgress);
connect(thread, &WorkerThread::finished, thread, &QObject::deleteLater);
thread->start();
(3) moveToThread 示例(推薦)
class Worker : public QObject {Q_OBJECT
public slots:void doWork() {// 耗時任務emit resultReady(42);}
signals:void resultReady(int result);
};// 在主線程創建
Worker *worker = new Worker;
QThread *thread = new QThread;// 移動對象到新線程
worker->moveToThread(thread);// 連接信號
connect(thread, &QThread::started, worker, &Worker::doWork);
connect(worker, &Worker::resultReady, this, &MainWindow::handleResult);
connect(worker, &Worker::resultReady, thread, &QThread::quit);
connect(thread, &QThread::finished, worker, &QObject::deleteLater);
connect(thread, &QThread::finished, thread, &QObject::deleteLater);thread->start();
(4) 線程同步機制
類名 用途 示例 QMutex 互斥鎖 QMutexLocker locker(&mutex)
QReadWriteLock 讀寫鎖 locker.lockForRead()
QSemaphore 信號量 semaphore.acquire(3)
QWaitCondition 條件變量 condition.wait(&mutex)
// 互斥鎖示例
QMutex mutex;
void ThreadSafeFunction() {QMutexLocker locker(&mutex); // 自動加鎖/解鎖// 臨界區操作
}// 條件變量示例
QWaitCondition condition;
QMutex mutex;// 等待線程
mutex.lock();
condition.wait(&mutex); // 釋放mutex并等待
mutex.unlock();// 喚醒線程
condition.wakeOne(); // 喚醒一個線程
condition.wakeAll(); // 喚醒所有線程
(5) 線程間通信
// 跨線程信號槽(自動排隊)
connect(worker, &Worker::dataReady, guiObject, &GUI::updateDisplay,Qt::QueuedConnection); // 顯式指定排隊連接// 使用事件傳遞復雜數據
class CustomEvent : public QEvent {
public:CustomEvent(const Data &d) : QEvent(User), data(d) {}Data data;
};// 發送事件
QCoreApplication::postEvent(receiver, new CustomEvent(data));
5.4 并發框架(QtConcurrent)
(1) 并行處理模式
(2) 線程池管理
三?槽(Slots)&?信號(Signals)& 連接(connect)
即為,連接(信號發送方,發送的信號,信號的接收者,信號的處理)
連接:即為鏈接這些個操作的函數
信號發送方:即,要執行操作的初始方(button)
發送的信號:即,要執行的初始操作(click,release)
信號的接收者:即接收信號并處理信號的函數(click后執行的,release和后的操作)
信號的處理(處理的槽函數):系統預置
// 關閉窗口
? ? ?鏈接? 發送者按鈕? ?要執行的事件? ? 接受? ? ? ? 處理方式(槽的處理)? ? ?
? ? connect(btn, &QPushButton::clicked, this, &MainWindow::close);
1. 槽的本質與特性
本質:普通 C++ 成員函數,但需在類聲明中使用?
slots
?關鍵字標識核心特性:
可被信號觸發執行
可具有返回值(但通常被忽略)
支持任意參數類型(需與連接信號匹配)
可聲明為虛函數
支持訪問控制(public/protected/private)
class MyObject : public QObject {Q_OBJECT
public:explicit MyObject(QObject *parent = nullptr);public slots: // 公共槽(可被外部連接)void processData(const QByteArray &data);private slots: // 私有槽(僅限類內部連接)void internalCleanup();protected slots: // 受保護槽(子類可訪問)virtual void updateState();
};
2. 槽的聲明與定義
(1) 聲明位置
class MyClass : public QObject {Q_OBJECT // 必須包含此宏public slots:void slot1(); // 無參數槽QString slot2(int param); // 帶參數槽signals:void dataProcessed(); // 信號聲明
};
?(2) 定義實現
void MyClass::slot1() {qDebug() << "Slot1 called";
}QString MyClass::slot2(int param) {return QString("Received: %1").arg(param);
}
3. 信號與槽的連接
(1) 基本連接方式
// 語法:connect(發送者, 信號, 接收者, 槽)
connect(button, &QPushButton::clicked, processor, &DataProcessor::startProcessing);
(2) 連接類型
連接類型 | 行為描述 | 適用場景 |
---|---|---|
Qt::AutoConnection | 自動選擇(同線程=Direct,跨線程=Queued) | 默認選項(推薦) |
Qt::DirectConnection | 立即在發送者線程調用槽 | 單線程應用 |
Qt::QueuedConnection | 將調用加入接收者線程事件隊列 | 跨線程通信(最安全) |
Qt::BlockingQueuedConnection | 阻塞發送者線程直到槽執行完畢 | 線程同步(需謹慎) |
Qt::UniqueConnection | 確保相同對象間不重復連接 | 防止重復連接 |
// 顯式指定連接類型
connect(sender, &Sender::signal, receiver, &Receiver::slot,Qt::QueuedConnection);
?(3) Lambda 表達式作為槽
connect(button, &QPushButton::clicked, [=]() {qDebug() << "Button clicked at" << QTime::currentTime();// 可捕獲局部變量 [=]或[&]
});
4. 槽的高級用法
(1) 重載槽的處理
// 使用靜態轉型解決重載歧義
connect(comboBox, QOverload<int>::of(&QComboBox::currentIndexChanged),this, &MyClass::onIndexChanged);// C++14 風格
connect(comboBox, qOverload<int>(&QComboBox::currentIndexChanged),this, &MyClass::onIndexChanged);
(2) 槽的參數處理
// 槽可接收比信號更少的參數(多余參數被忽略)
connect(networkManager, &NetworkManager::dataReceived, // 信號:dataReceived(QByteArray, QDateTime)logger, &Logger::logData); // 槽:logData(QByteArray)// 使用 QSignalMapper(舊版)或 Lambda 處理參數轉換
connect(buttonGroup, QOverload<QAbstractButton*>::of(&QButtonGroup::buttonClicked),[=](QAbstractButton *btn) {handleButtonClick(btn->text());});
?(3) 槽的自動連接(UI 文件)
在 Qt Designer 中命名槽為?
on_對象名_信號名
?可實現自動連接:
// 自動連接條件:
// 槽名:on_buttonSubmit_clicked()
// 對象名:buttonSubmit
// 信號:clicked()
5. 槽的生命周期管理
(1) 連接斷開
// 手動斷開特定連接
QMetaObject::Connection conn = connect(...);
disconnect(conn);// 斷開對象所有連接
disconnect(sender, nullptr, receiver, nullptr);
disconnect(receiver); // 斷開接收者所有槽
(2) 自動清理
// 接收者銷毀時自動斷開連接
connect(sender, &Sender::signal, receiver, &Receiver::slot);
// 當receiver被delete時連接自動斷開// 使用上下文對象管理Lambda生命周期
connect(sender, &Sender::signal, contextObject, [=](){ /* ... */ });
6. 槽與線程安全
(1) 跨線程通信模式
(2) 最佳實踐
// Worker對象在工作線程
Worker *worker = new Worker;
worker->moveToThread(workerThread);// 主線程到工作線程的連接
connect(this, &Controller::startWork, // 主線程信號worker, &Worker::doWork, // 工作線程槽Qt::QueuedConnection); // 必須指定排隊連接// 工作線程到主線程的連接
connect(worker, &Worker::resultReady, // 工作線程信號this, &Controller::handleResult, // 主線程槽Qt::QueuedConnection); // 自動排隊
7. 槽的性能優化
-
減少連接數量:
-
使用單個槽處理多個信號
-
使用?
QSignalMapper
(Qt5)或 Lambda(推薦)
-
-
避免阻塞槽:
void MainWindow::onDataReceived() {// 錯誤:阻塞事件循環processHugeData(); // 耗時操作// 正確:移入工作線程QtConcurrent::run(this, &MainWindow::processHugeData);
}
-
使用直接連接(同線程):
connect(model, &DataModel::dataChanged,view, &DataView::refresh,Qt::DirectConnection); // 減少事件隊列開銷
8. 常見問題與解決方案
問題1:槽未觸發
檢查點:
1. 類聲明是否包含 Q_OBJECT 宏?
2. 是否調用了 QCoreApplication::exec()?
3. 連接是否成功建立?(connect 返回 QMetaObject::Connection)
4. 發送者/接收者是否已被銷毀?
問題2:跨線程崩潰
// 錯誤:跨線程訪問GUI
void WorkerThread::run() {label->setText("Done"); // 崩潰!
}// 正確:通過信號更新
emit updateGUI("Done"); // 連接至主線程槽
問題3:內存泄漏
// Lambda捕獲this導致泄漏
connect(button, &QPushButton::clicked, [this]() {this->process(); // 若this先于button銷毀...
});// 解決方案:使用弱引用
connect(button, &QPushButton::clicked, [weakThis = QPointer(this)]() {if (weakThis) weakThis->process();
});
9. 槽的最佳實踐總結
-
命名規范:
-
使用?
on_ObjectName_SignalName()
?實現自動連接 -
常規槽使用動詞描述行為(如?
processData()
)
-
-
訪問控制:
-
公有槽:供外部對象連接
-
私有槽:內部實現細節
-
-
線程規則:
-
GUI操作只在主線程槽中執行
-
耗時操作在工作線程槽中執行
-
-
資源管理:
-
使用?
QPointer
?或上下文對象管理接收者生命周期 -
及時斷開不再需要的連接
-
-
性能關鍵路徑:
-
避免在頻繁觸發的槽中執行復雜操作
-
使用?
QElapsedTimer
?監控槽執行時間
-
總結:槽的核心價值
松耦合通信:對象間無需相互引用
類型安全:編譯時檢查參數匹配
線程安全:跨線程通信內置支持
靈活組合:一個信號可連接多個槽,一個槽可響應多個信號
元對象集成:支持動態連接/斷開
6. 官方文檔?
Signals & Slots | Qt Core | Qt 6.9.1
四 事件(Events)
1. 事件系統核心概念
事件(Event)是Qt框架中處理用戶輸入、系統通知和應用程序內部通信的基礎機制。與信號槽機制不同,事件系統提供了更底層的控制能力。
事件系統關鍵特性:
-
事件對象:所有事件繼承自
QEvent
,包含事件類型和相關信息 -
事件傳遞:通過事件隊列(event loop)分發
-
事件處理:對象通過重寫事件處理器(event handler)響應事件
-
事件過濾:支持在事件到達目標對象前進行攔截
五 核心組件關系圖
┌────────────┐ ┌────────────┐ ┌──────────────┐ │ QTimer │───> │ QThread │<───>│ QtConcurrent │ └────────────┘ └────────────┘ └──────────────┘│ │ │▼ ▼ ▼ ┌────────────┐ ┌────────────┐ ┌──────────────┐ │ QDateTime ├────>│ 信號槽系統 │<────│ 線程池管理 │ └────────────┘ └────────────┘ └──────────────┘│▼┌──────────────┐│ 同步原語 ││ (QMutex等) │└──────────────┘