2009-05-28 13 views
8

Piszę mój pierwszy prawidłowy użyteczny kawałek oprogramowania. Częścią tego będzie przeglądanie obrazu przez użytkownika i wybór jego akceptacji lub odrzucenia. Spowoduje to zapisanie obrazu w zaakceptowanym lub odrzuconym folderze i ewentualnie obrócenie i/lub zmianę rozmiaru.Uruchamianie oddzielnego procesu lub wątku w Qt

W tej chwili moja operacja rotate/resize/save wstrzymuje wykonywanie mojego programu, ale chciałbym, aby to się stało w tle, aby następny obraz był wyświetlany natychmiast.

Czy to jedyny sposób, aby to zrobić w Qt, aby przetworzyć obraz w osobnym wątku, czy jest inny sposób? Wciąż mam głowę wokół C++ i Qt, więc nie chcę się mylić, nurkując w nowym polu!

+8

Jedno słowo ostrzeżenia. Nie można używać QPixmaps poza wątkiem GUI. Ostatnio zostałem ukąszony przez to, że jest to podobna aplikacja do renderowania obrazów gwintowanych. Zamiast tego użyj QImage. Jeśli naprawdę potrzebujesz QPixmap (co zrobiłem), będziesz musiał zwrócić QImage z wątku i wykonać konwersję (która jest dość droga) w głównym wątku GUI. –

Odpowiedz

14

Qt obsługuje wątki. Może Cię zainteresować this example application, ponieważ jest nieco podobny do tego, co opisujesz.

Również, here is the full Qt thread documentation.

+0

Czy poleciłbyś tylko nurkowanie i naukę podstaw? – Skilldrick

+4

Wątki są właściwą odpowiedzią, a obsługa wątków Qt jest całkiem niezła. Gdybym to był ja, uczyłbym się przez przykład. Połączona aplikacja jest podobna pod tym względem, że wykonuje zadanie w tle i umożliwia otrzymanie powiadomienia o zakończeniu. Weź przykład, który jest dla ciebie interesujący lub przydatny i wypróbuj go sam. Sprawdzaj rzeczy, których nie rozumiesz w dokumentach, dopóki cały proces nie będzie bardziej przejrzysty. Zwykle doprowadzi cię tam, gdzie musisz iść. – Naaff

+0

Uważaj, w tym przykładzie występuje przeciek pamięci! Obiekt wątku nigdy nie jest usuwany. I dlaczego chronią funkcję zapisu m_abort, a nie operację odczytu ?! Po co go chronić, najgorszą rzeczą, jaka może się stać, jest to, że przetwarzana jest jedna dodatkowa pętla. – TimW

3

tego rodzaju zadania doskonale nadają się do wątków. mimo to powinieneś najpierw wykonać funkcję "normalną", która to robi, a gdy działa, dodaj wątek, który odczytuje kolejkę i wywołuje tę samą funkcję przetwarzania.

Qt ma wiele narzędzi, które mogą ci w tym pomóc, głównie fakt, że większość kontenerów jest bezpieczna dla wątków, a także kilka algorytmów gwintowania (takich jak map-reduce). jeszcze, najpierw spróbuj to synchronicznie.

2

redakcją

Sorry chłopaki, mam bardzo ciężko łączącą „kolejce” Typ niestandardowy Przykład z wymaganiami.

O ile mogę stwierdzić na podstawie pytania, po zaakceptowaniu lub odrzuceniu obrazu przez użytkownika musi on być opcjonalnie obrócony i/lub skalowany i zawsze zapisywany w określonym katalogu i przechodzić do następnego obrazu. (-> brak interakcji z użytkownikiem)
Nawet jeśli użytkownik opuści bieżące okno dialogowe, zdjęcie nadal musi zostać zapisane.
Przykład "Queued Custom Type" obsługuje tylko jeden obraz, jest zawsze połączony z gui, a gdy użytkownik opuszcza okno dialogowe, operacja wątku jest zatrzymana.
Tak więc jeśli uruchamia swój program z przykładu Queue, prawdopodobnie zacznie pisać kolejkę obrazów chronionych muteksem, aby mógł dodawać nowe obrazy do listy, jeśli są oczekujące operacje zapisu. W przeciwnym razie użytkownik nadal musi czekać na operacje oczekujące.
Drugi problem polega na tym, że prawdopodobnie nie chce czekać na oczekujące operacje składowania po zamknięciu okna dialogowego.

Co zrobić, aby spełnić wymagania, działa z pulą wątków. Nakarm pulę wątków w wybrane operacje składowania i użyj wzoru dekoratora opartego na QRunnable, jeśli trzeba go również obrócić/przeskalować. Całe kolejkowanie jest obsługiwane poprawnie przez bibliotekę, a oczekujące operacje są wykonywane, nawet jeśli użytkownik opuści bieżące okno dialogowe. Na koniec prawdopodobnie użyłbym kolejkowego kodu przykładowego, aby załadować nowe obrazy i dać użytkownikowi wskazanie oczekiwania na operację ładowania.
Moje runnable i dekorator prawdopodobnie wyglądałyby tak ... (może niektóre dodatkowe konstruktory, które zastąpiłyby ustawione funkcje), więc mogę bardzo łatwo dodać nową operację, taką jak ta QThreadPool::globalInstance()->start(saver);, bez użycia jakiegokolwiek obiektu synchronizacji niskiego poziomu.

class ImageDecorator : public QRunnable 
{ 
    NextStep nextStep; 
public: 
    typedef boost::shared_ptr<QRunnable> NextStep; 

    ImageDecorator(const NextStep& nextStep) : nextStep(nextStep) { 
    } 

    ImageDecorator() : nextStep() { 
    } 

    // set/get image functions.... 

protected: 
    void next() { 
     if(nextStep) 
      nextStep->run(); 
    } 
}; 


class RotateImage : public ImageDecorator 
{ 
public: 
    typedef boost::shared_ptr<Image> Image; 

    RotateImage(const NextStep& nextStep) : ImageDecorator(nextStep) { 
    } 

    RotateImage() : ImageDecorator() { 
    } 
    // set angle functions.... 

private: 
    void run() 
    { 
     // rotate the image 
     // ... 
     next(); 
    } 
}; 

class ResizeImage : public ImageDecorator 
{ 
public: 
    typedef boost::shared_ptr<Image> Image; 

    ResizeImage(const NextStep& nextStep) : ImageDecorator(nextStep) { 
    } 

    ResizeImage() : ImageDecorator() { 
    } 
    // set size functions.... 

private: 
    void run() 
    { 
     // resize the image 
     next(); 
    } 
}; 

class SaveImage : public ImageDecorator 
{ 
public: 
    typedef boost::shared_ptr<Image> Image; 

    SaveImage(const NextStep& nextStep) : ImageDecorator(nextStep) { 
    } 

    SaveImage() : ImageDecorator() { 
    } 
    // set fileName functions.... 

private: 
    void run() 
    { 
     // save the image 
     next(); 
    } 
}; 

// save the image 
SaveImage *const saver(new SaveImage()); 
saver->setImage(/*use shared pointer*/); 
saver->setFilename(...); 

QThreadPool::globalInstance()->start(saver); 

// rotate and save the image 
const ImageDecorator::NextStep saver(new SaveImage()); 
saver->setImage(/*use shared pointer*/); 
saver->setFilename(...); 
RotateImage *const rotateAndSave(new RotateImage(saver)); 
rotateAndSave->setImage(/*use shared pointer*/); 
rotateAndSave->setAngle(...); 

QThreadPool::globalInstance()->start(rotateAndSave); 


// resize rotate and save the image 
const ImageDecorator::NextStep saver(new SaveImage()); 
saver->setImage(/*use shared pointer*/); 
saver->setFilename(...); 
const ImageDecorator::NextStep rotateAndSave(new RotateImage(saver)); 
rotateAndSave->setImage(/*use shared pointer*/); 
rotateAndSave->setAngle(...); 
ResizeImage *const resizeRotateAndSave(new ResizeImage(rotateAndSave)); 
resizeRotateAndSave->setImage(/*use shared pointer*/); 
resizeRotateAndSave->setSize(...); 

QThreadPool::globalInstance()->start(resizeRotateAndSave); 
+0

Tylko jedno pytanie, dlaczego używasz boost :: shared_ptr? Istnieją specjalne klasy Qt, takie jak QSharedData, QSharedDataPointer w tym celu. – bkausbk

1

Albo utworzyć oddzielny wątek z QThread lub użyj pula wątków wątków roboczych z QRunnable lub spojrzeć na wysokim poziomie klasy QtConcurrent.Here to przykład skalowania obrazu.

1

Najprostszym sposobem na to jest użycie QtConcurrent :: uruchom

Powiązane problemy