2009-05-28 7 views
8

J'écris mon premier logiciel utile. Une partie impliquera que l'utilisateur visualise une image et choisisse de l'accepter ou de la rejeter. Cela entraînera l'enregistrement de l'image dans un dossier accepté ou rejeté, éventuellement pivoté et/ou redimensionné.Exécution d'un processus ou d'un thread séparé dans Qt

Pour le moment, mon opération de rotation/redimensionnement/sauvegarde met en pause l'exécution de mon programme, mais j'aimerais que cela se produise en arrière-plan pour que l'image suivante s'affiche instantanément.

Est-ce la seule façon de faire cela dans Qt pour traiter l'image dans un fil séparé, ou y a-t-il un autre moyen? Je suis toujours en train de faire le tour du C++ et du Qt, donc je ne veux pas me perdre en plongeant dans un nouveau domaine!

+8

Un mot d'avertissement. Vous ne pouvez pas utiliser QPixmaps en dehors du thread graphique. J'ai été récemment mordu par ceci est une application de type de rendu d'image fileté similaire. Utilisez QImage à la place. Si vous avez vraiment besoin d'un QPixmap (ce que j'ai fait), vous devrez retourner une QImage du thread et faire la conversion (ce qui est assez cher) dans le thread graphique principal. –

Répondre

14

Qt possède un support de filetage. Vous pourriez trouver this example application intéressant, car il est quelque peu similaire à ce que vous décrivez.

En outre, here is the full Qt thread documentation.

+0

Donc, recommanderiez-vous simplement de plonger et d'apprendre les bases? – Skilldrick

+4

Les threads sont la bonne réponse et le support des threads de Qt est plutôt bon. Si c'était moi, j'apprendrais juste par l'exemple. L'application liée est similaire en ce sens qu'elle exécute une tâche en arrière-plan et vous permet d'être averti lorsque c'est fait. Prends un exemple intéressant ou applicable à toi et essaie-le toi-même. Recherchez des choses que vous ne comprenez pas dans les documents jusqu'à ce que tout le processus soit plus clair. Habituellement, cela vous mènera là où vous devez aller. – Naaff

+0

Attention, il y a une fuite de mémoire dans l'exemple! L'objet thread n'est jamais supprimé. Et pourquoi protègent-ils l'opération d'écriture de m_abort et non l'opération de lecture ?! Pourquoi le protéger de toute façon, la pire chose qui puisse arriver est qu'il y ait une boucle supplémentaire traitée. – TimW

3

ces types de tâches sont parfaitement adaptés aux threads. encore, vous devez d'abord faire une fonction "normale" qui le fait, et quand cela fonctionne, ajoutez un thread qui lit une file d'attente et appelle la même fonction de traitement.

Qt a beaucoup d'outils pour vous aider à ce sujet, principalement le fait que la plupart des conteneurs sont thread-safe, et aussi un couple d'algorithmes de threading (comme map-reduce). encore, d'abord l'essayer de manière synchrone.

2

Edité

Désolé les gars, j'ai beaucoup de mal à lier le « type personnalisé Exemple Queued » aux exigences. Dans la mesure où je peux dire à partir de la question, une fois que l'utilisateur a accepté ou rejeté l'image, il doit être facultatif tourné et/ou mis à l'échelle et toujours enregistré dans un répertoire spécifique et passer à l'image suivante. (-> plus d'interaction avec l'utilisateur)
Même si l'utilisateur quitte la boîte de dialogue en cours, l'image doit encore être enregistrée. L'exemple de type personnalisé mis en file d'attente ne gère qu'une seule image, est toujours lié à l'interface graphique et lorsque les utilisateurs quittent la boîte de dialogue, l'opération de thread est arrêtée.
Ainsi, s'il lance son programme à partir de l'exemple Queued, il commencera probablement à écrire une file d'attente d'images protégée par mutex afin qu'il puisse ajouter de nouvelles images à la liste s'il y a des opérations de sauvegarde en attente. Sinon, l'utilisateur doit toujours attendre les opérations en attente.
Le deuxième problème est qu'il ne souhaite probablement pas attendre les opérations de sauvegarde en attente lorsque la boîte de dialogue se ferme.

Ce que je ferais pour répondre aux exigences est de travailler avec le pool de threads. Donnez au pool de threads les opérations de sauvegarde souhaitées et utilisez un modèle de décorateur basé sur QRunnable s'il doit également être pivoté/mis à l'échelle. Toutes les files d'attente sont gérées correctement par la bibliothèque et les opérations en attente sont exécutées même si l'utilisateur quitte la boîte de dialogue en cours. À la fin, j'utiliserais éventuellement le code d'exemple Queued pour charger de nouvelles images et donner à l'utilisateur une indication d'attente pour l'opération de chargement. Mes runnables et décorateur ressembleraient probablement à ceci ... (peut-être quelques constructeurs supplémentaires pour remplacer les fonctions d'ensemble) ainsi je peux très facilement ajouter une nouvelle opération comme ceci QThreadPool::globalInstance()->start(saver); sans employer n'importe quel objet de synchronisation de bas niveau.

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

Juste une question, pourquoi utilisez-vous boost :: shared_ptr? Il existe des classes spécifiques à Qt telles que QSharedData, QSharedDataPointer à cette fin. – bkausbk

1

Soit créer un thread séparé avec QThread ou utiliser threadpool threads de travail avec QRunnable ou un coup d'oeil à la classe QtConcurrent de haut niveau.Here est un exemple de mise à l'échelle de l'image.

1

Le moyen le plus simple de le faire est d'utiliser QtConcurrent :: run

Questions connexes