2015-11-30 25 views
6

Breve versioneQuali sono i meccanismi del delegato predefinito per le visualizzazioni di elementi in Qt?

Qual è il delegato di default usata da QTreeView? In particolare sto cercando di trovare il suo metodo paint()?

Longer versione

Sono un utente Python (PySide/PyQt), e sto usando un delegato personalizzato per ricreare alcune delle funzionalità di QTreeView. Quindi, sto cercando di trovare il delegato predefinito e il metodo paint, utilizzato in un QTreeView. Ancora meglio sarebbe una spiegazione di come funziona.

postale Croce

ho postato la stessa domanda a Qt Centre (http://www.qtcentre.org/threads/64458-Finding-default-delegate-for-QTreeView?).

risposta

8

tl; dr

Il delegato di default per tutte le viste voce è il QStyledItemDelegate. Il suo metodo paint() richiama drawControl(), definito in qcommonstyle.cpp, per disegnare ogni elemento. Quindi, esaminare qcommonstyle.cpp per i dettagli nitty su come viene disegnato ogni elemento.


lungo modulo di risposta

Chi preferisce brevità dovrebbe leggere il tl; dr sopra e la documentation on Styles in Item Views. Se sei ancora bloccato (e probabilmente molti utenti di Python lo saranno), il resto di questa risposta dovrebbe aiutare.

I. Il delegato di default è QStyledItemDelegate

Il QStyledItemDelegate è il delegato di default per gli elementi in vista. Ciò viene affermato chiaramente nella Qt's Model/View Programming overview:

Dal Qt 4.4, l'implementazione predefinita delegato è fornito da QStyledItemDelegate, e questo viene utilizzato come delegato predefinito viste standard di Qt.

Il docs for QStyledItemDelegate fornire un po 'più in dettaglio:

Quando si visualizzano i dati provenienti da modelli in Qt vista voce, per esempio, un QTableView, le singole voci sono disegnati da un delegato. Inoltre, quando un articolo è modificato , fornisce un widget dell'editor, che viene collocato in cima alla vista dell'oggetto durante la modifica. QStyledItemDelegate è il delegato predefinito per tutte le viste delle voci Qt e viene installato su di loro quando vengono creati.

In sintesi, se si vuole comprendere i meccanismi alla base di una qualsiasi delle viste voce (non solo una vista ad albero, ma anche tabelle ed elenchi), studiare QStyledItemDelegate.

Il metodo paint() in qstyleditemdleegate.cpp è definito on line 419 of the doxygenated code base. Diamo un'occhiata alle ultime due righe:

QStyle *style = widget ? widget->style() : QApplication::style(); 
    style->drawControl(QStyle::CE_ItemViewItem, &opt, painter, widget); 

Qui stanno succedendo due cose. Innanzitutto, viene impostato lo stile, in genere su QApplication.style(). In secondo luogo, viene richiamato il metodo drawControl() di questo stile per disegnare l'elemento da dipingere. E questo è tutto. Questa è letteralmente la riga finale di QStyledItemDelegate.paint()!

Quindi, se si vuole davvero capire come il delegato predefinito sta dipingendo le cose, dobbiamo effettivamente studiare lo stile, che sta facendo tutto il vero lavoro. Questo è ciò che faremo nel resto di questo documento.

II. QStyle: cosa dà al delegato il suo stile?

Quando qualcosa viene visualizzato con Qt, viene disegnato in base a uno stile scelto, in modo specifico per il sistema, quando è stato creato il numero QApplication. Da the docs for QStyle:

La classe QStyle è una classe base astratta che incapsula l'aspetto e la sensazione di una GUI. Qt contiene un set di sottoclassi QStyle che emulano lo stile degli stili delle diverse piattaforme supportate da Qt (QWindowsStyle, QMacStyle, QMotifStyle, ecc.). Per impostazione predefinita, questi stili sono incorporati nella libreria QtGui .

In Qt, il codice sorgente per lo stile N è src/gui/styles/N.cpp.

Ogni stile contiene l'implementazione delle operazioni elementari utilizzate per disegnare tutto in una GUI, dalle viste ad albero ai menu a discesa. Gli stili standard, ad esempio QWindowsStyle, ereditano la maggior parte dei loro metodi da QCommonStyle. Ogni particolare stile include in genere solo piccole deviazioni da quella base comune. Quindi, uno studio ravvicinato di qcommonstyle.cpp rivelerà la funzionalità di base che gli sviluppatori di Qt hanno trovato utile per dipingere tutte le parti di una GUI. La sua importanza è difficile da sopravvalutare.

In seguito, esamineremo le parti rilevanti per disegnare gli elementi della vista.

III. QStyle.drawControl(): esegue endoscopia sulla delegato

Come menzionato sopra, la comprensione dei meccanismi fondamentali di disegno una vista richiede esaminare l'attuazione di drawControl() in qcommonstyle.cpp, un'implementazione che inizia sulla linea 1197. Nota nel seguito, quando mi riferisco a un numero di linea senza menzionare un nome di file, per convenzione mi riferisco a qcommonstyle.cpp in the doxygenated code base.

Il documentation for QStyle.drawControl() è istruttivo:

QStyle.drawControl (elemento, l'opzione, pittore)

Parametri:

  • elemento - QStyle.ControlElement

  • opzione - QtGui.QStyleOption

  • pittore - PySide.QtGui.QPainter

Disegna l'elemento data con il pittore fornito con lo stile opzioni specificate dall'opzione .... Il parametro di opzione è un puntatore a un oggetto QStyleOption e contiene tutte le informazioni richieste a disegnare l'elemento desiderato.

Il chiamante dice drawControl() che tipo di elemento che sta cercando di attirare l'facendolo passare una bandiera QStyle.ControlElement. Gli elementi di controllo sono componenti di livello superiore di una finestra che visualizza le informazioni all'utente: cose come checkbox, pulsanti e voci di menu. Tutti gli elementi di comando sono elencate qui:

http://pyqt.sourceforge.net/Docs/PyQt4/qstyle.html#ControlElement-enum

richiamo dell'elemento di comando inviato nella chiamata a QStyledItemDelegate.paint() era CE_ItemViewItem, che è semplicemente un elemento da visualizzare all'interno di una vista elementi. All'interno QCommonStyle.drawControl(), il caso CE_ItemViewItem inizia on line 2153. scaviamo nel

A. subElementRect():. Le dimensioni contano

E 'la chiave per ottenere la dimensione e la disposizione di ogni oggetto giusto. Questa è la prima cosa che calcola drawControl(). Per ottenere queste informazioni, invoca subElementRect() (definito nella riga 2313 e chiamato per primo nella riga 2158). Ad esempio, abbiamo:

QRect textRect = subElementRect(SE_ItemViewItemText, vopt, widget); 

Il primo argomento è una bandiera QStyle.SubElement, in questo caso SE_ItemViewItemText. I sottoelementi dello stile rappresentano parti costitutive degli elementi di controllo. Ogni elemento in una vista ha tre possibili sottoelementi: la casella di controllo, l'icona e il testo; ovviamente, il sottoelemento SE_ItemViewItemText rappresenta il testo. Tutti gli elementi secondari possibili sono elencate qui:

http://pyqt.sourceforge.net/Docs/PyQt4/qstyle.html#SubElement-enum

Il metodo subElementRect() contiene tutti i casi nella enumerazione sottoelemento: il nostro caso SE_ItemViewItemText inizia on line 3015.

Nota che subElementRect() restituisce un QRect, che definisce un rettangolo nel piano usando la precisione di interi e può essere costruito con quattro numeri interi (a sinistra, in alto, in larghezza, in altezza). Ad esempio r1 = QRect(100, 200, 11, 16). Questo specifica, per un sottoelemento, la sua dimensione e la posizione x, y dove verrà dipinto nella finestra.

subElementRect() chiama effettivamente viewItemLayout() (definito nella riga 999) per eseguire il lavoro reale, che è un processo in due passaggi. Innanzitutto, viewItemLayout() calcola l'altezza e la larghezza dei sottoelementi utilizzando viewItemSize(). Secondo, calcola la posizione xey del sottoelemento. Consideriamo ognuna di queste operazioni a turno.

1.viewItemLayout(): calcolare la larghezza e l'altezza di sottoelementi

partenza sulla linea 1003, viewItemLayout() chiama viewItemSize() (definito sulla linea 838), che calcola l'altezza e la larghezza necessaria per un sottoelemento.

Dove viewItemSize() ottiene i numeri predefiniti per cose come l'altezza della barra del titolo? Questa è la provincia della metrica dei pixel. Una metrica di pixel è una dimensione dipendente dallo stile rappresentata da un singolo valore di pixel. Ad esempio, Style.PM_IndicatorWidth restituisce la larghezza di un indicatore casella di controllo e QStyle.PM_TitleBarHeight restituisce l'altezza della barra del titolo per lo stile dell'applicazione. Tutti i diversi QStyle.PixelMetric s sono elencati qui:

http://pyqt.sourceforge.net/Docs/PyQt4/qstyle.html#PixelMetric-enum

è possibile recuperare il valore di un dato pixel metriche utilizzando QStyle.pixelMetric(), che viene utilizzato molto in viewItemSize(). Il primo input di pixelMetric() è uno dei numeri QStyle.PixelMetric nell'enumerazione. In qcommonstyle.cpp l'attuazione di pixelMetric() inizia sulla linea 4367.

Ad esempio, viewItemSize() calcola la larghezza e l'altezza della casella (se necessario) usando la seguente:

return QSize(proxyStyle->pixelMetric(QStyle::PM_IndicatorWidth, option, widget), 
    proxyStyle->pixelMetric(QStyle::PM_IndicatorHeight, option, widget)); 

noti che pixelMetric() non è solo usato in viewItemSize(), ma è onnipresente. Viene utilizzato per calcolare le proprietà delle metriche di molti elementi della GUI, dai bordi delle finestre alle icone ed è disseminato in tutto qcommonstyle.cpp. Fondamentalmente, ogni volta che devi sapere quanti pixel il tuo stile usa per un elemento grafico le cui dimensioni non cambiano (come una casella di controllo), lo stile chiamerà le metriche dei pixel.

2. viewItemLayout(): Cubo di Rubik sottoelementi per ottenere x/y posizioni

La seconda parte del viewItemLayout() è dedicata ad organizzare il layout dei sottoelementi la cui larghezza e altezza sono stati appena calcolato. Cioè, ha bisogno di trovare le loro posizioni xey per completare il riempimento dei valori in QRect s come textRect. I sottoelementi saranno organizzati in modo diverso a seconda dell'impostazione generale della vista (ad es., Se è orientata a destra-sinistra o sinistra-destra). Quindi, viewItemLayout() calcola la posizione finale di riposo di ciascun sottoelemento in base a tali fattori.

Se avete bisogno di indicazioni su come reimplementare sizeHint() in un delegato personalizzato, subElementRect() potrebbe essere un'utile fonte di suggerimenti e trucchi. In particolare, è probabile che viewItemSize() contenga utili informazioni utili e metriche relative ai pixel che potresti voler interrogare quando vuoi che una vista personalizzata corrisponda al valore predefinito.

Una volta che i numeri QRect vengono calcolati per il testo, icona e sottoelementi della casella di controllo, drawControl() si sposta e infine inizia a disegnare.

B.Colorare lo sfondo: ottenere primitiva

Prima lo sfondo viene compilato per l'elemento (linea 2163):

drawPrimitive(PE_PanelItemViewItem, option, painter); 

Questo è un po 'come la chiamata a QStyle.drawControl(), ma il primo argomento non è un controllo elemento, ma un elemento primitivo (PE). Proprio come drawControl() è un grande metodo tentacolare che controlla il disegno di tutti gli elementi di controllo di livello superiore, QStyle.drawPrimitive() disegna la maggior parte degli elementi grafici primitivi di livello inferiore in una GUI (come lo sfondo rettangolare in una vista elemento). Queste unità di livello inferiore, gli elementi primitivi, sono elencate qui:

http://pyqt.sourceforge.net/Docs/PyQt4/qstyle.html#PrimitiveElement-enum

I documenti dicono, "Un elemento primitivo è un elemento comune GUI, quali un indicatore casella di controllo o pulsante smussatura". Ad esempio, l'elemento primitivo PE_PanelItemViewItem è "[T] lo sfondo per un elemento in una vista oggetto."

Ogni stile deve avere un'implementazione di drawPrimitive() e il nostro inizia sulla linea 140. In questo modo è possibile scoprire in modo molto dettagliato come esegue le operazioni di pittura primitive. Questa è una fonte utile di suggerimenti su come utilizzare il comando paint() in pratica nei delegati personalizzati.

La reimplementazione di QStyle.drawPrimitive() per il caso PE_PanelItemViewItem inizia nella riga 773. Seleziona prima il colore di sfondo appropriato in base allo stato dell'elemento. Lo fa interrogando l'articolo QStyle.StateFlag. Lo option.state contiene i flag di stato che descrivono lo stato dell'elemento al momento (è abilitato, selezionato, in corso di modifica, ecc.?). Questi stati non sono solo utilizzati nel back-end, ma è probabile che sarà necessario utilizzarlo per reimplementare QStyledItemDelegate.paint() nei delegati personalizzati. È possibile trovare un'enumerazione delle QStyle.StateFlag s qui:

http://pyqt.sourceforge.net/Docs/PyQt4/qstyle.html#StateFlag-enum

Dopo aver scelto il colore giusto, drawPrimitive() quindi utilizza QPainter.fillRect() a riempire la regione appropriata con quel colore (linea 786):

p->fillRect(vopt->rect, vopt->backgroundBrush); 

QPainter.fillRect() è un metodo molto utile per l'implementazione personalizzata dei delegati.

Dopo aver preso cura dello sfondo, drawControl() procede quindi a completare il disegno dell'elemento, a partire dalla casella di controllo (riga 2165), quindi dall'icona (riga 2185) e infine dal testo (riga 2194). Non approfondiremo questo argomento in dettaglio, ma finirò brevemente discutendo su come disegna il testo.

C. Disegno del testo: va rinnegato

In primo luogo, lo stato della voce è utilizzata per specificare il colore del testo. Questo utilizza lo QStyle.StateFlags appena discusso. Poi responsabilità drawControl() calci disegno testo su di un metodo personalizzato viewItemDrawText() (definiti alla riga 921):

viewItemDrawText(painter, vopt, textRect); 

Questo metodo richiede nel pittore, opzione, e il rettangolo di testo sopra descritto (parte A) come parametri.Nota che il parametro option è molto importante: è una classe QStyleOption con cui il contenuto viene passato all'interno di uno stile (include icona, checkstate e proprietà testuali).

Dopo che il testo è stato estratto dall'opzione, viene incorporato in uno QtGui.QTextLine che viene aggiunto a QtGui.QTextLayout. Il testo finale, eliso (vale a dire, con ellissi, a seconda delle impostazioni di ritorno a capo) viene disegnato da QPainter.drawText() (vedere la riga 983), che è una delle operazioni di pittura primitiva di Qt.

Sinceramente una buona parte di viewItemDrawText() viene utilizzata per il "word wrapping". Questo è il punto in cui iniziamo a entrare in alcuni degli aspetti di Qt che nessun utente di Python avrebbe mai voluto vedere, e ancor meno armeggiare. Ad esempio, utilizza la classe QStackTextEngine. Vi incoraggio a "qstacktextengine pyqt" di Google per vedere quanto raramente sia venuto fuori per gli utenti Python. Se questo ti interessa, abbi!

IV. Riassunto

Se si desidera accedere alla meccanica di pittura di fondo per il delegato di default, si finirà per studiare l'attuazione di QStyle.drawControl() in qcommonstyle.cpp, una bestia 6000-riga di un file. Questo esercizio può essere molto utile per capire le esatte procedure utilizzate per calcolare le dimensioni e disegnare gli elementi grafici primitivi che gli elementi contengono. A volte, tuttavia, questa bestia può essere decisamente terrificante e inutile, come quando si tratta di trattare con le parole. In questi casi, probabilmente dovrai calcolare un'implementazione personalizzata della funzionalità desiderata per il tuo delegato.

Infine, ora che abbiamo visto una visione d'insieme di come funzionano le cose, possiamo apprezzare meglio l'utilità della documentazione per QStyle, in particolare la sezione Styles in Item Views. Lì, troviamo la seguente pepita d'amore rivelatrice:

La pittura di elementi in viste è eseguita da un delegato. delegato default di Qt, QStyledItemDelegate, viene utilizzato anche per calcolare delimitazione rettangoli di elementi (e le loro sotto-elementi) ... Quando QStyledItemDelegate dipinge i suoi articoli, si richiama CE_ItemViewItems ... Quando attuazione di uno stile per personalizzare il disegno di vista voce, è necessario a controllare l'implementazione di QCommonStyle (e qualsiasi altra sottoclasse da cui il tuo stile eredita). In questo modo, puoi scoprire quali e come gli elementi di stile sono già stati dipinti, e puoi quindi reimplementare la pittura di elementi che dovrebbero essere disegnati in modo diverso.

Quindi in sostanza la risposta al post originale è "Cosa hanno detto".