2010-09-22 15 views
7

Utilizzo una QTableView con una sottoclasse di QItemDelegate per controllare l'aspetto delle celle di Tableview.Aggiornamento efficiente di una QTableView ad alta velocità

Ogni cella visualizza il nome e lo stato di un dispositivo collegato esternamente e possono essere collegati contemporaneamente fino a 100 dispositivi.

Il nome e il tipo di ogni dispositivo è essenzialmente statico, con un aggiornamento molto raro (forse una volta all'ora), ma ogni cella deve visualizzare un valore in tempo reale dell'input del dispositivo, che attualmente prelevo ogni 50 millisecondi. Questo valore viene visualizzato come un grafico a barre di base disegnato dal pittore fornito al metodo Delegate :: paint() dal TableView.

Il problema con l'aggiornamento del mio modello 20 volte al secondo è che l'intera tabella viene ridisegnata ogni volta, il che è altamente inefficiente. Limitare il metodo paint al solo disegno del grafico a barre mostra che la maggior parte del tempo di CPU è dedicato a disegnare il nome, lo stato e l'immagine associata su ogni cella, piuttosto che il grafico.

Quello che devo trovare è un modo per aggiornare il grafico per ogni cella regolarmente senza ridisegnare la cella, ma non riesco a capire come farlo.

Qual è il modo più efficiente per raggiungere questo obiettivo?

Modifica: immagine allegata per aiutare.

L'immagine rappresenta 10 sensori in un QTableView. Il numero, il nome e lo stato sono praticamente statici, quasi mai in fase di aggiornamento. Il grafico a barre accanto al testo "Valore sensore" viene aggiornato ogni 50 ms. Voglio solo dipingere questa barra, piuttosto che il testo, lo stato e lo sfondo della cella. Le spie di stato e lo sfondo sono immagini complesse, quindi è necessario molto più tempo della CPU rispetto al semplice disegno e riempimento di un rect.

alt text

+0

Lo stato deve essere nello stesso widget di tutto il resto? Il mio primo pensiero sarebbe quello di incollare un ListView dallo stesso modello accanto ad esso. –

+0

Sì, purtroppo lo fa. Ogni dispositivo ha un numero di parametri che devono essere accanto al grafico.Ho pensato di avere due punti di vista, forse sovrapposti, ma mi sembra un modo molto disordinato per ottenere ciò che voglio, e rende più difficile cambiare modelli, editing e così via. – Dani

risposta

6

dal tuo QTableView eredita QWidget, è possibile chiamare il seguente su di esso:

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

Quando setUpdatesEnabled è falso, qualsiasi traccia di vernice() o update() chiamata su di esso non ha alcun effetto. Quindi, potresti smettere di aggiornarlo, cambiare tutti i tuoi dati e quindi riabilitarli manualmente, probabilmente manualmente manualmente su paint() o update(), non sono sicuro di questa parte.

Questo è il documento per il metodo setUpdatesEnabled.

QWidget updatesEnabled

Spero che questo aiuti.

EDIT dopo il commento da utente:

Si potrebbe implementare il proprio setUpdatesEnabled (bool) per la sottoclasse QItemDelegate (dal momento che non erediterà QWidget e non ha uno) testando una bandiera prima di eseguire l'originale paint() o update(). Successivamente, è possibile specificare per ogni cella (o riga o colonna) di QTableView se devono essere aggiornati o ridipinti.

In questo modo, è possibile impedire alle altre celle (delegate) di ridipingere, a meno che non si modifichi il flag setUpdatesEnabled creato manualmente, ma si mantengano gli aggiornamenti sulle celle contenenti un grafico.

Devo dire che non ho mai provato questo o qualcosa del genere, quindi spero che funzioni come penso.

Buona fortuna

EDIT dopo modificare dall'utente:

Dopo il mio commento precedente, invece di impostare un flag per ogni cella (ho pensato che il grafico era in una cella separata), si potrebbe imposta una bandiera per ogni delegato per dipingere solo il tuo grafico o l'intera immagine.

Spero che questo aiuti,

EDIT:

sono incappato in una nuova funzionalità in Qt 4.7 (non so se è possibile per voi di usarlo, ma potrebbe risolvere alcuni dei vostri problemi). La funzione è QStaticText. È una classe che ti permette di mettere in cache il testo (font ed effetti) e dipingerli più velocemente. Vedi il link here.

Spero che possa risolvere il tuo problema.

+1

In realtà sto già facendo questo, quindi sto solo aggiornando il modello 20 volte al secondo, piuttosto che 20 x numberOfDevices, ma non risolve il problema che, oltre ai grafici, sto disegnando un immagine di sfondo, una stringa di nome, una stringa di stato e vari altri display che si aggiornano solo a una velocità molto più bassa. – Dani

+0

Il grafico che vuoi scrivere da solo in una cella? – Live

+0

No, è incluso con altre informazioni. Ho allegato un'immagine alla domanda originale per aiutare a visualizzare la GUI. – Dani

1

Cache l'immagine di sfondo (immagine di sfondo della cella, stato e nome) sul modello come QPixmap. Ridisegna quella pixmap solo quando lo stato o il nome sono cambiati. Nel caso comune dovrai solo disegnare la QPixmap memorizzata nella cache e il valore del sensore in cima a quello.

Edit:

Aggiungi bandiera fullRepaintNeeded alla classe di dati. Quando lo stato o il nome sono cambiati, fullRepaintNeeded è impostato su true.

Quando il delegato sta pitturando l'oggetto, il delegato controlla prima il flag fullRepaintNeeded dell'elemento. Se fullRepaintNeeded è true, viene creata una nuova QPixmap e tutto viene dipinto su quel QPixmap che viene infine dipinto su tableview. QPixmap viene quindi memorizzato nella cache del modello (che significa per la classe di dati) con la funzione setData del modello (ma non viene chiamato dataChanged). fullRepaintNeeded ora è impostato su false.

Ma se fullRepaintNeeded è falso nella funzione paint del delegato, viene richiesto dal modello una QPixmap precedentemente memorizzata nella cache, disegnata in tableview e il valore finale del sensore viene disegnato sopra.

+0

Anche la bella idea non ci aveva pensato. – Live

+0

Questo è quello che voglio fare, ma come faccio a farlo? C'è solo una routine paint e solo uno slot dataChanged(). Idealmente ho bisogno di due, ma quale oggetto QPainter devo usare per quello aggiuntivo? Ho iniziato a implementarlo usando il metodo flag di Live, vedrò come va. – Dani

+0

Ho appena provato questo. La routine di pittura funziona esattamente come descrivi, il risultato: quando si imposta la bandiera, la cella viene cancellata e il grafico viene dipinto. Quando si aggiornano gli altri valori (o si modifica la selezione) sono solo 50ms al massimo fino al successivo aggiornamento del grafico, che quindi cancella le celle e dipinge nuovamente solo la barra. – Dani

2

È raro che suggerisca questo percorso piuttosto che delegare, ma sembra nella tua situazione che potrebbe valerne la pena. Potrei prendere in considerazione la mia vista, che è abbastanza consapevole per aggiornare solo le porzioni dello schermo che devono essere aggiornate. Un widget di visualizzazione come questo è ovviamente un po 'più speciale di quello che sarebbe normalmente il caso, ma se hai davvero bisogno dell'efficienza, è un modo per andare.

Altre cose da considerare se hai bisogno di una piccola spinta in termini di efficienza, assicurati di segnare solo le righe modificate che effettivamente cambiano (se i valori dei sensori non cambiano spesso, e vengono solo sottoposti a polling spesso) o considerare l'aggiunta un valore di isteresi tra il quale non è effettivamente ridisegnato (se i valori del sensore non cambiano abbastanza rapidamente per negarlo).

+0

Questa è la strada per cui sono andato; creando il mio punto di vista. Ho preso il consiglio di Roku e ho usato QGLWidget per gestire il rendering, e di conseguenza sto vedendo il 3% di utilizzo della CPU per centinaia di dispositivi che si aggiornano anche a intervalli più brevi di prima. – Dani

+0

@Dani Un po 'tardi per la festa, ma questa domanda potrebbe trarre grandi vantaggi da ciò che hai imparato seguendo questo approccio. – UmNyobe

Problemi correlati