I. L'article original▲
Qt Labs est un site géré par les développeurs de Qt. Ils y publient des projets, des idées propres et des composants afin d'obtenir les retours d'informations sur les API, le code et les fonctionnalités ou simplement pour partager avec nous ce qui les intéresse. Le code que vous y trouverez peut fonctionner tel quel, mais c'est sans aucune garantie ni support. Voir les conditions d'utilisation pour plus d'informations.
Nokia, Qt, Qt Labs et leurs logos sont des marques déposées de Nokia Corporation en Finlande et/ou dans les autres pays. Les autres marques déposées sont détenues par leurs propriétaires respectifs.
Cet article est la traduction de l'article Qt Graphics and Performance - Generating Content in Threads de Gunnar paru dans Qt Labs.
II. Introduction▲
Dans cette série que nous avons faite, je voulais couvrir le threading, un sujet qui a été activement discuté au sein de certains trolls durant les derniers mois. Nous avons ajouté le support pour faire du rendu dans des QImage à partir de threads non-GUI depuis les premiers jours de Qt 4.0 mais c'est seulement dans les versions récentes de Qt, à partir de la 4.4 je crois, que nous avons ajouté le rendu du texte dans des images. Maintenant que cette fonction est supportée se pose la question de savoir comment l'utiliser correctement. Générer du contenu dans un thread est un cas d'utilisation, voici un exemple de cela.
Cela signifie que, plutôt que de dessiner tout le contenu d'une vue particulière dans les fonctions QWidget::paintEvent() ou dans le QGraphicsItem::paint(), nous utilisons un thread tournant en arrière-plan qui produit le cache. L'avantage est que même si le rendu du contenu réel peut être très coûteux, dessiner un pré-rendu d'une image est rapide, ce qui permet à l'interface utilisateur de rester 100% réactive tandis que le travail lourd se déroule en arrière-plan. Cela implique que le contenu n'est pas disponible en totalité à tout moment, mais dans de nombreux cas, ceci est parfaitement acceptable. Il n'y a rien de nouveau dans cette approche, je pense que c'est une belle façon de résoudre un problème qui revient souvent dans la pratique.
Cette approche est utilisée par Google Maps (en fait, je ne sais pas ce que le serveur fait, mais au moins il envoie individuellement chaque carreau dans le navigateur), les navigateurs internet de l'IPhone et du N900. Dans le passé, j'avais parlé à des clients qui utilisent cette approche fonctionnelle quand la génération du contenu est coûteuse, mais que l'interface utilisateur en a besoin pour rester souple. En fait, cette approche s'applique à pratiquement n'importe quel cas où il est autorisé que le contenu ne soit pas immédiatement disponible, comme les tableaux de données (par exemple un index de mp3 ou une liste de contacts), les images d'un dossier de données, etc.
III. Le traitement▲
Regardons d'abord le traitement. J'ai écrit une implémentation triviale qui regarde dans un répertoire et affiche toutes les images trouvées. Chaque image est un morceau de contenu distinct et j'ajoute un fond, un petit cadre autour d'elle et une ombre portée en dessous. Juste pour qu'il y ait un peu de travail actif en cours. Si vous voulez voir le code, vous pouvez le télécharger ici.
Les morceaux de contenu auraient pu être des tuiles dans une carte de Norvège ou des tuiles qui composent une page web, mais j'ai choisi les images parce que j'en ai déjà quelques-unes disponibles et j'ai pensé qu'elles étaient très bien comme exemples. La démo est exécutée sur un N900 avec le compositeur désactivé utilisant les lignes de commande suivantes:
- Version sans thread : ./threaded_tile_generation -no-thread -graphicssystem opengl MyImageFolder
- version avec thread : ./threaded_tile_generation -graphicssystem opengl MyImageFolder
Voilà à quoi cela ressemble lorsque le contenu est généré dans le thread de l'interface graphique :
L'interface utilisateur reste très fluide tant que je ne montre que le contenu qui a déjà été chargé. Au moment où il est nécessaire de charger des images, l'interface utilisateur se bloque et l'expérience utilisateur est vraiment mauvaise. Voici à quoi cela ressemble si nous passons le travail dans un thread en arrière-plan :
IV. L'algorithme▲
N'utilisez pas cet algorithme directement. Il est très basique et écrit pour montrer le concept. Tout d'abord, parce que j'ai été paresseux, j'ai utilisé des connexions mises en file d'attente plutôt que d'une file d'attente synchronisée à la liste des pièces devant être rendues. Cela signifie que la file d'attente est gérée par boucles d'événements de Qt, hors de mon contrôle. Donc, si je me déplace rapidement dans les images, je vais demander le rendu d'un grand nombre d'images, puis passer à d'autres images avant que le rendu soit fini. Dans une implémentation décente, je les sortirais de la file d'attente et m'assurerais que seules les pièces qui sont directement visibles sont en cours de traitement.
L'autre chose est qu'il n'y a pas de logique d'anticipation. Je demande que les images soient générées uniquement au moment où j'en ai besoin. Si au contraire je les demande à l'avance, en fonction de la direction courante de défilement des images, et si en plus, je ne demande pas de les supprimer trop rapidement, il en résulterait probablement une situation dans laquelle la plupart des images seraient affichées à l'avance.
V. QGraphicsView▲
Ce serait assez cool si cela pouvait être appliqué directement sur QGraphicsView. Vous définissez un drapeau dans la vue et au lieu de générer ses pixmaps en cache dans le thread de l'interface graphique, la vue décharge le travail dans un thread. Ce n'est pas quelque chose de direct car le thread de l'interface graphique peut, pour le moment au moins, continuer à modifier l'état de la vue pendant qu'elle réalise le rendu dans le thread de travail. La synchronisation de ces deux threads devient un peu du fouillis et résoudre ce problème, le cas échéant, n'est pas quelque chose que nous avons planifié. Cela n'empêche pas les gens de faire ce genre de travail dans leur propre fonction paint() bien sûr.
VI. Divers▲
Merci à ClaudeLELOUP pour sa relecture et ses conseils.
Au nom de toute l'équipe Qt, j'aimerais adresser le plus grand remerciement à Nokia pour nous avoir autorisé la traduction de cet article !