在開發 Qt GUI 應用程式時,常會遇到一個棘手的問題:當使用者點擊視窗關閉按鈕時,某些阻塞型的背景執行緒或子程序可能仍在運行。若不妥善處理,這會導致資源洩漏、資料損壞,甚至程序殘留在系統程序中無法完全退出。
解決之道在於重寫 QMainWindow 的虛擬函式,攔截關閉信號,在視窗實際銷毀前執行清理邏輯。主要涉及兩個關鍵事件處理函式:
closeEvent(QCloseEvent *event):處理視窗關閉事件(點擊右上角 ×、呼叫 close() 等)keyPressEvent(QKeyEvent *event):處理鍵盤事件,例如攔截 Esc 鍵觸發關閉#include <QKeyEvent>
#include <QEvent>
#include <QCloseEvent>
class MainWindow : public QMainWindow {
Q_OBJECT
protected:
void closeEvent(QCloseEvent *event) override;
void keyPressEvent(QKeyEvent *event) override;
private:
// 假設有一個需要優雅關閉的背景工作物件
WorkerThread *m_workerThread;
};
void MainWindow::closeEvent(QCloseEvent *event)
{
// 1. 先通知背景執行緒停止工作
if (m_workerThread && m_workerThread->isRunning()) {
m_workerThread->requestInterruption(); // Qt 5.15+ 建議用法
m_workerThread->quit();
// 等待執行緒實際結束,設定超時避免主介面卡死
if (!m_workerThread->wait(3000)) { // 最多等 3 秒
m_workerThread->terminate(); // 強制終止(最後手段)
m_workerThread->wait();
}
}
// 2. 儲存視窗幾何資訊、使用者設定等
saveSettings();
// 3. 接受關閉事件,讓視窗正常銷毀
event->accept();
// 若有不可恢復的錯誤,可呼叫 event->ignore() 取消關閉
}
void MainWindow::keyPressEvent(QKeyEvent *event)
{
if (event->key() == Qt::Key_Escape) {
// 按下 Esc 鍵時觸發關閉流程,會自動呼叫 closeEvent()
this->close();
event->accept();
} else {
// 其他按鍵交給父類別處理(如快捷鍵、焦點導航等)
QMainWindow::keyPressEvent(event);
}
}
requestInterruption() + quit() + wait() 的組合,避免直接 terminate() 造成資源未釋放。wait() 必須設定超時時間,防止背景執行緒死結導致主介面無法關閉。closeEvent 中集中處理所有收尾工作(存檔、斷開連線、釋放硬體資源等)。keyPressEvent 中未處理的按鍵務必呼叫父類別實作,維持標準行為。同樣的模式適用於:
掌握事件攔截機制,是編寫健壯、專業級 Qt 應用程式的基礎功夫之一。