2009-05-28 2 views
8

Ich schreibe meine erste geeignete nützliche Software. Ein Teil davon beinhaltet, dass der Benutzer ein Bild sieht und es akzeptiert oder ablehnt. Dadurch wird das Bild in einem akzeptierten oder abgelehnten Ordner gespeichert und möglicherweise gedreht und/oder in der Größe geändert.Ausführen eines separaten Prozesses oder Threads in Qt

Im Moment, meine Rotation/Resize/Save Operation ist die Ausführung meines Programms Pause, aber ich möchte es im Hintergrund passieren, so dass das nächste Bild sofort angezeigt wird.

Ist der einzige Weg, dies in Qt zu tun, um das Bild in einem separaten Thread zu bearbeiten, oder gibt es einen anderen Weg? Ich bin immer noch dabei, C++ und Qt zu verstehen, damit ich mich nicht in ein neues Feld stürzen will!

+8

Ein Wort der Warnung. Sie können QPixmaps nicht außerhalb des GUI-Threads verwenden. Ich wurde kürzlich von dieser Anwendung mit einem ähnlichen Bildrendering Typ gebissen. Verwenden Sie stattdessen QImage. Wenn Sie wirklich eine QPixmap benötigen (was ich getan habe), müssen Sie ein QImage aus dem Thread zurückgeben und die Konvertierung (die ziemlich teuer ist) im Haupt-GUI-Thread durchführen. –

Antwort

14

Qt hat Thread-Unterstützung. Vielleicht finden Sie this example application interessant, da es etwas ähnelt, was Sie beschreiben.

Auch here is the full Qt thread documentation.

+0

Also würden Sie empfehlen, einfach einzusteigen und die Grundlagen zu erlernen? – Skilldrick

+4

Threads sind die richtige Antwort und Qt's Thread-Unterstützung ist ziemlich gut. Wenn ich es wäre, würde ich nur mit gutem Beispiel lernen. Die verknüpfte Anwendung ist insofern ähnlich, als sie eine Aufgabe im Hintergrund ausführt und Ihnen ermöglicht, benachrichtigt zu werden, wenn sie fertig ist. Nehmen Sie ein Beispiel, das für Sie interessant oder anwendbar ist, und probieren Sie es selbst aus. Suchen Sie in den Dokumenten nach Dingen, die Sie nicht verstehen, bis der gesamte Prozess klarer ist. Normalerweise bringt dich das dahin, wo du hin musst. – Naaff

+0

Vorsicht, es gibt ein Speicherleck im Beispiel! Das Thread-Objekt wird niemals gelöscht. Und warum schützen sie den Schreibvorgang von m_abort und nicht die Leseoperation ?! Warum schützen Sie es trotzdem? Das Schlimmste, was passieren kann, ist, dass eine zusätzliche Schleife verarbeitet wird. – TimW

3

Diese Art von Aufgaben sind perfekt für Gewinde geeignet. Dennoch sollten Sie zuerst eine 'normale' Funktion ausführen, die das tut, und wenn es funktioniert, fügen Sie einen Thread hinzu, der eine Warteschlange liest und die gleiche Verarbeitungsfunktion aufruft.

Qt hat viele Tools, die Ihnen dabei helfen, vor allem die Tatsache, dass die meisten Container threadsicher sind, und auch einige Threading-Algorithmen (wie map-reduce). noch, versuche es zuerst synchron.

2

Herausgegeben

Sorry, Jungs, ich habe eine sehr harte Zeit verbindet die „Queued Benutzerdef Beispiel“ den Anforderungen.

Soweit ich aus der Frage sagen kann, sobald der Benutzer das Bild akzeptiert oder abgelehnt hat, muss es optional gedreht und/oder skaliert werden und immer in einem bestimmten Verzeichnis gespeichert werden und mit dem nächsten Bild fortfahren. (-> keine Interaktion mehr mit dem Benutzer)
Auch wenn der Benutzer den aktuellen Dialog verlässt, muss das Bild noch gespeichert werden.

Das Beispiel "In Warteschlange eingereihter benutzerdefinierter Typ" behandelt nur ein Bild, ist immer mit der GUI verbunden und wenn die Benutzer den Dialog verlassen, wird die Thread-Operation gestoppt.
Wenn er also sein Programm aus dem Queued-Beispiel startet, wird er wahrscheinlich mit dem Schreiben einer Mutex-geschützten Bildwarteschlange beginnen, damit er neue Bilder zur Liste hinzufügen kann, wenn Speicheroperationen ausstehen. Andernfalls muss der Benutzer noch auf ausstehende Vorgänge warten.
Das zweite Problem ist, dass er wahrscheinlich nicht auf ausstehende Speichervorgänge warten möchte, wenn der Dialog geschlossen wird.

Was ich tun würde, um die Anforderungen zu erfüllen, ist die Arbeit mit dem Thread-Pool. Füttern Sie den Thread-Pool mit den gewünschten Speicheroperationen und verwenden Sie ein Dekorator-Muster basierend auf QRunnable, wenn es auch gedreht/skaliert werden muss. Die gesamte Warteschlange wird von der Bibliothek korrekt verarbeitet und ausstehende Vorgänge werden ausgeführt, selbst wenn der Benutzer das aktuelle Dialogfeld verlässt. Am Ende würde ich möglicherweise den Warteschlangen-Beispielcode verwenden, um neue Bilder zu laden und dem Benutzer eine Warteanzeige für den Ladevorgang zu geben.

Meine Runnables und Decorator würde wahrscheinlich so aussehen ... (vielleicht einige zusätzliche Konstruktoren, um die Set-Funktionen zu ersetzen), so kann ich sehr leicht neue Operation wie diese hinzufügen QThreadPool::globalInstance()->start(saver); ohne Verwendung von Low-Level-Synchronisationsobjekt.

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

Nur eine Frage, warum benutzt du boost :: shared_ptr? Zu diesem Zweck gibt es Qt-spezifische Klassen wie QSharedData, QSharedDataPointer. – bkausbk

1

entweder ein separates Gewinde mit QThread erstellen oder Threadarbeitsthreads mit QRunnable oder einen Blick auf hohe Niveau QtConcurrent Klasse nehmen verwenden.Here ist ein Beispiel für die Bildskalierung.

1

Der einfachste Weg, dies zu tun, ist QtConcurrent :: run