2010-09-22 7 views
7

J'utilise un QTableView avec une sous-classe de QItemDelegate pour contrôler l'aspect et la convivialité des cellules de tableview. Chaque cellule affiche le nom et l'état d'un périphérique connecté en externe, et jusqu'à 100 périphériques peuvent être connectés en même temps. Le nom et le type de chaque périphérique sont essentiellement statiques, mis à jour très rarement (peut-être une fois par heure), mais chaque cellule doit afficher une valeur en temps réel de l'entrée du périphérique, que je scrute toutes les 50 millisecondes. Cette valeur est affichée sous la forme d'un graphique à barres basique dessiné par le peintre fourni à la méthode Delegate :: paint() par TableView.Mise à jour efficace d'un QTableView à haute vitesse

Le problème avec la mise à jour de mon modèle 20 fois par seconde est que la table entière est redessinée à chaque fois, ce qui est très inefficace. Limiter la méthode de peinture à dessiner uniquement le graphique à barres montre que la plus grande partie du temps processeur est dédiée au dessin du nom, de l'état et de l'image associée sur chaque cellule, plutôt que sur le graphique. Ce que j'ai besoin de trouver est un moyen de mettre à jour le graphique pour chaque cellule régulièrement sans redessiner la cellule, mais je ne peux pas savoir comment le faire.

Quel est le moyen le plus efficace d'y parvenir?

Éditer: Image jointe pour aider.

L'image représente 10 capteurs dans un QTableView. Le numéro, le nom et le statut sont pratiquement statiques, ne se mettant presque jamais à jour. Le graphique à barres à côté du texte "Valeur du capteur" est mis à jour toutes les 50ms. Je veux seulement peindre cette barre, plutôt que le texte, le statut et l'arrière-plan de la cellule. Les voyants d'état et l'arrière-plan sont des images complexes, il faut donc beaucoup plus de temps processeur que de simplement dessiner et remplir un rect.

alt text

+0

Le statut doit-il être dans le même widget que tout le reste? Ma première pensée serait de coller un ListView du même modèle à côté de lui. –

+0

Oui, malheureusement, c'est le cas. Chaque appareil a un certain nombre de paramètres qui doivent être à côté du graphique.J'ai pensé à deux points de vue, peut-être superposés, mais cela semble être une manière très désordonnée de réaliser ce que je veux, et cela rend plus difficile la modification de modèles, l'édition, etc. – Dani

Répondre

6

depuis votre QTableView hérite QWidget, vous pouvez appeler ce qui suit sur ce:

setUpdatesEnabled(false); 
changeAllYourData(); 
setUpdatesEnabled(true); 

Lorsque setUpdatesEnabled est faux, la peinture() ou mise à jour() faire appel n'a pas d'effet. Donc, vous pouvez l'empêcher de mettre à jour, modifier toutes vos données et ensuite le réactiver, éventuellement en appelant manuellement paint() ou update() manuellement, je ne suis pas sûr de cette partie.

Voici le doc pour la méthode setUpdatesEnabled.

QWidget updatesEnabled

Hope this helps.

EDIT après un commentaire de l'utilisateur:

Vous pouvez mettre en œuvre votre propre setUpdatesEnabled (bool) pour votre sous-classe de QItemDelegate (car il ne hérite pas QWidget et n'a pas) en testant un drapeau avant d'exécuter original paint() ou update(). Après cela, vous pouvez spécifier pour chaque cellule (ou ligne ou colonne) de votre QTableView si elles doivent être mises à jour ou repeintes. En faisant cela, vous pouvez empêcher vos autres cellules (délégués) de repeindre, sauf si vous modifiez le drapeau setUpdatesEnabled que vous avez créé manuellement, mais gardez les mises à jour sur vos cellules contenant un graphique.Je dois dire que je n'ai jamais testé ceci ou quelque chose comme ça, alors j'espère que ça fonctionne comme je le pense.

Bonne chance

EDIT après modification de l'utilisateur:

Suite à mon commentaire précédent, au lieu de mettre un drapeau pour chaque cellule (je pensais que votre graphique était dans une cellule séparée), vous pourriez définir un indicateur pour chaque délégué pour peindre uniquement votre graphique ou l'image entière.

Hope this helps,

EDIT:

Je suis tombé sur une nouvelle fonctionnalité dans Qt 4.7 (je ne sais pas s'il est possible pour vous de l'utiliser, mais il pourrait résoudre une partie de votre problèmes.) La fonctionnalité est QStaticText. C'est une classe qui vous permet de mettre en cache du texte (police et effets) et de les peindre plus rapidement. Voir le lien here.

Espérons que cela pourrait résoudre votre problème.

+1

Je suis déjà en train de faire cela, de sorte que je ne fais que mettre à jour le modèle 20 fois par seconde, plutôt que 20 x numberOfDevices, mais cela ne résout pas le problème que les graphiques, je dessine un image de fond, une chaîne de nom, une chaîne de statut et divers autres affichages qui eux-mêmes mettent à jour à un taux beaucoup plus faible. – Dani

+0

Le graphique que vous voulez écrire seul dans une cellule? – Live

+0

Non, il est inclus avec d'autres informations. J'ai attaché une image à la question originale pour aider à visualiser l'interface graphique. – Dani

1

Mettre en cache l'image d'arrière-plan (image d'arrière-plan de la cellule, statut et nom) dans le modèle en tant que QPixmap. Redessiner ce pixmap uniquement lorsque le statut ou le nom sont modifiés. Dans le cas courant, vous n'aurez besoin que de dessiner le QPixmap en cache et la valeur du capteur.

Edit:

Ajouter drapeau fullRepaintNeeded à votre classe de données. Lorsque l'état ou le nom est modifié, fullRepaintNeeded est défini sur true. Lorsque le délégué peint l'élément, le délégué vérifie d'abord l'indicateur fullRepaintNeeded de l'élément. Si fullRepaintNeeded a la valeur true, une nouvelle QPixmap est créée et tout est peint dans cette QPixmap qui est finalement peinte dans la tableview. Le QPixmap est ensuite mis en cache dans le modèle (ce qui signifie pour votre classe de données) avec la fonction setData du modèle (mais dataChanged n'est pas appelé). fullRepaintNeeded est maintenant défini sur false.

Toutefois, si fullRepaintNeeded est défini sur false dans la fonction de peinture du délégué, un modèle QPixmap précédemment mis en cache est demandé par le modèle, dessiné sur la tableview et la valeur finale du capteur est dessinée en plus.

+0

Belle idée aussi, n'avait pas pensé à ça. – Live

+0

C'est ce que je veux faire, mais comment vais-je le faire? Il n'y a qu'une seule routine de peinture et un seul slot dataChanged(). Idéalement, j'en ai besoin de deux, mais quel objet QPainter dois-je utiliser pour l'objet supplémentaire? J'ai commencé à l'implémenter en utilisant la méthode du flag de Live, je vais voir comment ça se passe. – Dani

+0

Je viens d'essayer ça. La routine de peinture fonctionne exactement comme vous le décrivez, le résultat: Lorsque le drapeau est défini, la cellule est effacée puis le graphique est peint. Lors de la mise à jour des autres valeurs (ou de la modification de la sélection), il ne reste que 50 ms maximum avant la prochaine mise à jour du graphique, qui efface alors les cellules et ne peint plus que la barre. – Dani

2

Il est rare que je suggère ce chemin plutôt que des délégués, mais il apparaît dans votre situation, il pourrait en valoir la peine. Je considérerais faire ma propre vue, qui est assez consciente pour mettre à jour seulement les parties de l'écran qui ont besoin d'être mises à jour. Un widget de vue comme celui-ci est évidemment un peu plus spécial que ce qui serait normalement le cas, mais si vous avez vraiment besoin de l'efficacité, c'est une façon de faire. D'autres choses à considérer si vous avez besoin d'un petit coup de pouce en matière d'efficacité, assurez-vous de ne marquer que les lignes qui changent réellement (si les valeurs des capteurs ne changent pas souvent, et ne sont souvent interrogées) ou d'ajouter une valeur d'hystérésis entre laquelle il n'est pas réellement redessiné (si les valeurs du capteur ne changent pas assez rapidement pour annuler cela).

+0

C'est la route que je suis allé pour; créer ma propre vue. J'ai pris le conseil de Roku et utilisé QGLWidget pour gérer le rendu, et par conséquent, je vois une utilisation du processeur de 3% pour des centaines d'appareils rafraîchissant à des intervalles encore plus courts qu'auparavant. – Dani

+0

@Dani Un peu en retard à la fête mais cette question pourrait grandement bénéficier de ce que vous avez appris suite à cette approche. – UmNyobe