在 Qt5 視窗的 QLabel 上顯示 OpenCV 攝影機畫面。
若註解掉 cvNamedWindow/cvShowImage/cvDestroyWindow,程式進入 while(1) 迴圈後 **Qt 介面完全凍結,按鈕無反應**,必須強制關閉。
while(1) 迴圈跑在 **Qt 主執行緒(GUI 執行緒)**,持續佔用 CPU,不讓 Qt 事件迴圈 (QApplication::exec()) 處理重繪、滑鼠、鍵盤事件。cvWaitKey(330) 雖有延遲,但屬於 OpenCV 高層 API,內部仍在同一執行緒忙等,不會釋放 Qt 事件迴圈控制權。cvNamedWindow 時,OpenCV 內部可能啟動獨立視窗執行緒處理 cvShowImage,間接讓 Qt 主執行緒獲得喘息機會(非標準行為,不可依賴)。class VideoThread : public QThread {
Q_OBJECT
public:
void run() override {
cv::VideoCapture cap(0);
cv::Mat frame;
while (!isInterruptionRequested()) {
if (cap.read(frame)) {
cv::cvtColor(frame, frame, cv::COLOR_BGR2RGB);
QImage img(frame.data, frame.cols, frame.rows, frame.step, QImage::Format_RGB888);
emit frameReady(img.copy()); // 傳遞深拷貝,避免跨執行緒資料競爭
}
msleep(30); // 約 30 FPS
}
}
signals:
void frameReady(const QImage &img);
};
// 主視窗
MainWindow::MainWindow() {
auto *thread = new VideoThread(this);
connect(thread, &VideoThread::frameReady, this, [this](const QImage &img){
ui.label_JK->setPixmap(QPixmap::fromImage(img));
});
thread->start();
}
run():執行緒進入點,迴圈中檢查 isInterruptionRequested() 優雅退出。QImage:Qt 自動排隊到主執行緒執行槽函數,安全更新 UI。img.copy() 深拷貝:cv::Mat 資料在迴圈中會被覆寫,必須複製一份給 QImage,否則顯示花屏或崩潰。thread->requestInterruption(); thread->wait(); 確保執行緒乾淨結束。cv::VideoCapture + cv::Mat,棄用 IplImage/CvCapture 舊 C 介面。QVideoSink / QMediaDevices (Qt 6 Multimedia) 完全替代 OpenCV 擷取,效能更好、原生支援硬體加速。QImage 給 UI。結論:**任何耗時迴圈(視訊、串口、網路)絕不能寫在 Qt 主執行緒**,必須移至 QThread/QRunnable + 訊號槽,才能保持介面流暢回應。