views:

2653

answers:

3

I'm writing my first proper useful piece of software. Part of it will involve the user viewing an image, and choosing to accept or reject it. Doing this will cause the image to be saved to an accepted or rejected folder, and possibly rotated and/or resized.

At the moment, my rotate/resize/save operation is pausing execution of my program, but I'd like it to happen in the background so the next image is displayed instantly.

Is the only way to do this in Qt to process the image in a separate thread, or is there another way? I'm still getting my head round C++ and Qt, so I don't want to confuse myself by diving into a new field!

+8  A: 

Qt has thread support. You might find this example application interesting since it's somewhat similar to what you describe.

Also, here is the full Qt 4.5 thread documentation.

Naaff
So would you recommend just diving in and learning the basics?
Skilldrick
Threads are the right answer and Qt's thread support is pretty good. If it were me I'd just learn by example. The linked application is similar in that it performs a task in the background and allows you to be notified when it's done. Take an example that is interesting or applicable to you and try it out yourself. Look up things you don't understand in the docs until the whole process is more clear. Usually that'll get you where you need to go.
Naaff
Watch out, there is a memory leak in the example! The thread object is never deleted. And why do they protect the write opertion of m_abort and not the read operation?! Why protect it anyway, the worst thing that can happen is that there is one extra loop processed.
TimW
+2  A: 

these kind of tasks are perfectly suited for threads. still, you should first do a 'normal' function that does it, and when it works add a thread that reads a queue and calls the same processing function.

Qt has a lot of tools to help you on this, mainly the fact that most of the containers are thread-safe, and also a couple of threading algorithms (like map-reduce). still, first try it synchronously.

Javier
+2  A: 

Edited

Sorry guys, I have a very hard time linking the "Queued Custom Type Example" to the requirements.

Is far as i can tell from the question, ones the users has accepted or rejected the image it must be optional rotated and/or scaled and always saved to a specific directory and go on to the next image. (-> no interaction anymore with the user)
Even if the user leaves the current dialog the image still has to be saved.

The "Queued Custom Type Example" handles only one image, is always linked to the gui and when the users exit the dialog the thread operation is stopped.
So if he starts his program from the Queued example he will probably start writing an mutex protected image queue so he can add new images to the list if there are pending save operations. Otherwise the user still has to wait on pending operations.
The second problem is that he probably doesn't want to wait on pending save operations when the dialog closes.

What I would do to meet the requirements is working with the thread pool. Feed the thread pool the save operations you want and use a decorator pattern based on QRunnable if it also needs to be rotated/scaled. All the queueing is handled correctly by the library and pending operations are executed even if the user leaves the current dialog. At the end I would possibly use the Queued example code to load new images and give the user a wait indication for the loading operation.

My runnables and decorator would probably look like this ... (maybe some additional constructors to replace the set functions) so I can very easly add new operation like this QThreadPool::globalInstance()->start(saver ); without using any low level synchronisation object.

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 );
TimW