2015-02-01 29 views
5

Piszę prosty program Qt do przechwytywania wideo z kamery (przy użyciu OpenCV). Używam obiektu QThread, który wykonuje pętle, przechwytuje obrazy i przesyła je do obiektu MainWindow. Działa to tak, jak powinno.Aplikacja wielowątkowa Qt nie kończy się po zamknięciu

Problem polega na tym, że po zamknięciu aplikacja (tj. Naciśnięcie przycisku "X") zatrzymuje przechwytywanie kamery i gui znika. Ale program nadal działa w tle. Otrzymuję również ostrzeżenie w danych wyjściowych aplikacji:

QThread: Zniszczone, gdy wątek nadal działa.

Jak całkowicie zatrzymać aplikację po jej zamknięciu?

main.cpp

#include <QApplication> 
#include "application.h" 

using namespace cv; 

int main(int argc, char *argv[]) 
{ 
    QApplication a(argc, argv); 

    Application app; 
    app.init(); 

    return a.exec(); 
} 

application.h

#include "mainwindow.h" 
#include "camerathread.h" 
#include "mathandler.h" 
#include "tools.h" 
#include "opencv2/core/core.hpp" 

#ifndef APPLICATION 
#define APPLICATION 

class Application : public MatHandler{ 
    MainWindow w; 
    CameraThread ct; 
public: 
    Application() { 
     w.setFixedSize(800,600); 
    } 

    void init() { 
     ct.setMatHandler(this); 
     ct.start(); 
     w.show(); 
    } 

    void handleMat(cv::Mat mat) { 
     QImage qImage = toQImage(mat); 
     w.setImage(qImage); 
    } 
}; 

#endif // APPLICATION 

camerathread

#include <QThread> 
#include "mathandler.h" 
#include "opencv2/highgui/highgui.hpp" 

#ifndef CAMERATHREAD 
#define CAMERATHREAD 

class CameraThread : public QThread { 
    MatHandler *matHandler; 
public: 
    ~CameraThread() { 
    } 

    void setMatHandler(MatHandler *h) { 
     matHandler = h; 
    } 

private: void run() { 
     cv::VideoCapture vc(0); 

     if (vc.isOpened()) { 
      for(;;) { 
       cv::Mat img; 
       vc >> img; 
       matHandler->handleMat(img); 
      } 
     } 
    } 
}; 

#endif // CAMERATHREAD 

programu C. Onsists więcej kodu niż to, ale tylko uwzględnione kod Myślę, że jest istotna dla pytania. W razie potrzeby opublikuję resztę.

+0

Sprawdź, czy oprócz interfejsu i wątku kamery istnieją inne wątki. Możliwe, że używane API odradzają inne wątki. –

+0

Nie jestem pewien, jak to sprawdzić ... Czy mógłbyś wyjaśnić? – gromit190

+0

Sprawdzanie uruchomionych wątków zależy od używanego IDE (jeśli warta jest jego soli). Na przykład to wyszukiwanie: https://www.google.com/webhp?sourceid=chrome-instant&ion=1&espv=2&ie=UTF-8#q=how%20to%20see%20running%20threads%20in%20eclipse przyniosło ten wynik : http://help.eclipse.org/indigo/index.jsp?topic=%2Forg.eclipse.mat.ui.help%2Ftasks%2Fanalyzingthreads.html Ale to też zależy od twojego IDE. Właśnie zademonstrowałem przykładowy widok/w Eclipse. –

Odpowiedz

3

Występują poważne problemy z kodem. Przede wszystkim w biegu funkcji niekończące się pętli spowoduje swój wątek, aby nigdy nie przestać, chyba że uda Ci zakończenie wątku przez siebie jak:

while(!finishThread) 
{ 
    cv::Mat img; 
    vc >> img; 
    matHandler->handleMat(img); 
} 

Należy ustawić finishThread do true gdy aplikacja będzie się zamykać. Po prostu podaj miejsce, w którym ustawisz wartość dla finishThread. Gdy chcesz zakończyć wątek, wyemituj sygnał, który jest podłączony do tego gniazda z wartością true. Potem czekać na gwint do końca prawidłowo przez kilka sekund i zmusić go do wypowiedzenia, jeśli nie ukończył:

emit setThreadFinished(true); //Tell the thread to finish 
if(!ct->wait(3000)) //Wait until it actually has terminated (max. 3 sec) 
{ 
    ct->terminate(); //Thread didn't exit in time, probably deadlocked, terminate it! 
    ct->wait(); //We have to wait again here! 
} 

Również nie należy bezpośrednio wywołać handleMat funkcji z innego wątku. Może to spowodować awarię aplikacji lub doprowadzić do niezdefiniowanego zachowania. Do tego należy użyć mechanizmu sygnału/szczeliny. Podłącz sygnał z wątku do tego gniazda i emituj sygnał wzdłuż argumentu za każdym razem, gdy chcesz go wywołać.

Inną kwestią jest to, że lepiej jest wyprowadzić klasę z QObject i użyć moveToThread. Można to zrobić w konstruktorze klasy:

th = new QThread(); 

this->setParent(0); 
this->moveToThread(th); 

QObject::connect(th,SIGNAL(started()),this,SLOT(OnStarted())); 
QObject::connect(th,SIGNAL(finished()),this,SLOT(OnFinished())); 

th->start(); 

Twoje inicjalizacji i zakończenia zadania powinny być wykonane w OnStarted() i OnFinished() szczelin odpowiednio. Możesz mieć funkcję pracującą, w której uruchamiasz powtarzalną operację.

Również w destruktorze klasy Application zakończyć wątek w sposób podobny do podanego.

+0

Dzięki za opinie, jest to bardzo potrzebne, ponieważ jestem (jak zapewne już wiesz) nowicjuszem w programowaniu w C++ i Qt. Ale jedna rzecz pozostaje dla mnie niejasna. Dlaczego warto korzystać z publicznego automatu w aplikacji CameraThread, kiedy mogę użyć publicznej metody unieważniania, aby zatrzymać aplikację? Jakie są zalety/wady? – gromit190

+1

Jeśli używasz metody, aby zatrzymać aplikację, musisz bezpośrednio wywołać ją jako 'ct-> setFinished (true);' z 'Application' która jest naprawdę niebezpieczna. Dzieje się tak, ponieważ są one w dwóch różnych wątkach, a dostęp do zmiennej z dwóch wątków prowadzi jednocześnie do nieokreślonego zachowania. Kiedy używasz mechanizmu sygnału/szczeliny do zatrzymania wątku, upewnij się, że zmienna jest dostępna tylko z jednego wątku (tutaj "CameraThread"). Nigdy nie wywołaj bezpośrednio funkcji obiektu, który znajduje się w innym wątku. – Nejat

+0

Noted, dzięki. W głównej sprawie; Dodałem kod stopu ct-> do dekonstruktora aplikacji, tak jak sugerowałeś. Dodano też QThread :: exit() w dekonstruującym wątku. Aplikacja nadal działa po zamknięciu głównego okna ... – gromit190