2015-11-30 5 views
6

KurzfassungWas sind die Mechanismen des Standarddelegaten für Artikelansichten in Qt?

Was ist die Standard-Delegierter von QTreeView verwendet? Insbesondere versuche ich seine paint() Methode zu finden?

Längere Version

Ich bin ein Python-Benutzer (pyside/PyQt) und bin mit einem benutzerdefinierten Delegierten einen Teil der Funktionalität von QTreeView neu zu erstellen. Daher versuche ich, den Standarddelegaten und die Malmethode zu finden, die in einem QTreeView verwendet wird. Noch besser wäre eine Erklärung dafür, wie es funktioniert.

Kreuzes

Ich stellte die gleiche Frage an Qt-Center (http://www.qtcentre.org/threads/64458-Finding-default-delegate-for-QTreeView?).

Antwort

8

tl; dr

Der Standard Delegierten für alle Elementansichten ist die QStyledItemDelegate. Seine paint() Methode ruft drawControl(), definiert in qcommonstyle.cpp auf, um jedes Element zu zeichnen. Also, lesen Sie qcommonstyle.cpp für die wesentlichen Details darüber, wie jedes Element gezeichnet wird.


Langform Antwort

Wer Kürze vorziehen sollte die tl lesen; dr oben und die documentation on Styles in Item Views. Wenn Sie immer noch feststecken (und viele Python-Benutzer werden wahrscheinlich sein), sollte der Rest dieser Antwort helfen.

I. Der Standard Delegat ist QStyledItemDelegate

Die QStyledItemDelegate die für Elemente in Ansichten Standard Delegat ist. Dies wird eindeutig erklärt in Qt's Model/View Programming overview:

Da Qt 4.4, die Standard-Delegaten Implementierung von QStyledItemDelegate vorgesehen ist, und dies wird als Standard-Delegat von Qt Standardansichten verwendet.

The docs for QStyledItemDelegate bieten ein wenig mehr Detail:

Wenn Daten von Modellen in Qt Elementansichten angezeigt wird, beispielsweise ein QTableView, die einzelnen Elemente durch einen Delegierten gezogen werden. Wenn ein Element bearbeitet wird, bietet es außerdem ein Editor-Widget, das während der Bearbeitung über die Elementansicht platziert wird. QStyledItemDelegate ist der Standarddelegate für alle Qt-Artikelansichten und wird auf ihnen installiert, wenn sie erstellt werden.

In Summe, wenn Sie die zugrundeliegenden Mechanismen des einer der Artikelansichten (nicht nur eine Baumansicht, aber Tabellen und Listen zu) verstehen wollen, studieren QStyledItemDelegate.

Die paint() Methode in qstyleditemdleegate.cpp ist on line 419 of the doxygenated code base definiert. Lassen Sie uns die letzten zwei Zeilen betrachten:

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

Zwei Dinge sind hier passiert. Zuerst wird der Stil festgelegt - normalerweise QApplication.style(). Zweitens wird die Methode drawControl() dieses Styles aufgerufen, um das zu bemalende Objekt zu zeichnen. Und das ist es. Das ist buchstäblich die letzte Zeile von QStyledItemDelegate.paint()!

Wenn Sie also wirklich herausfinden wollen, wie der Standard-Delegierte die Dinge malt, müssen wir den Stil studieren, der die ganze Arbeit erledigt. Das werden wir im Rest dieses Dokuments tun.

II. QStyle: Was gibt dem Delegierten seinen Stil?

Wenn irgendetwas mit Qt angezeigt wird, wird es nach einem bestimmten Stil gezeichnet, der systemspezifisch gewählt wurde, als Sie QApplication instanziiert haben. Von the docs for QStyle:

Die QStyle Klasse ist eine abstrakte Basisklasse, die das Aussehen und das Gefühl eines GUI kapselt. Qt enthält eine Reihe von QStyle-Unterklassen, die die Stile der verschiedenen von Qt unterstützten Plattformen emulieren (QWindowsStyle, QMacStyle, QMotifStyle usw.). Standardmäßig sind diese Stile in die QtGui-Bibliothek integriert.

In Qt finden Sie den Quellcode für Stil N in src/gui/styles/N.cpp.

Jeder Stil enthält die Implementierung der elementaren Operationen, die zum Zeichnen aller Elemente in einer GUI verwendet werden, von Baumansichten bis zu Dropdownmenüs. Die Standardstile wie QWindowsStyle erben die meisten ihrer Methoden von QCommonStyle. Jeder bestimmte Stil enthält typischerweise nur geringfügige Abweichungen von dieser gemeinsamen Basis. Daher wird eine genaue Studie von qcommonstyle.cpp die Basisfunktionalität enthüllen, die Qt-Entwickler als nützlich zum Malen aller Teile einer GUI empfanden. Seine Bedeutung ist schwer zu übertreiben.

Im Folgenden werden die für das Zeichnen von Ansichtselementen relevanten Teile untersucht.

III. QStyle.drawControl(): Durchführung der Endoskopie auf der Delegierten

Wie oben erwähnt, das Verständnis der grundlegenden Mechanismen einer Ansicht Zeichnung erfordert die Implementierung von drawControl() in qcommonstyle.cpp Prüfung eine Implementierung, die in der Linie 1197. Hinweis beginnt, was folgt, wenn ich mich auf eine Zeilennummer beziehe, ohne einen Dateinamen zu erwähnen, beziehe ich mich per Konvention auf qcommonstyle.cpp in the doxygenated code base.

Die documentation for QStyle.drawControl() ist lehrreich:

QStyle.drawControl (Element, Option, Maler)

Parameter:

  • Element - QStyle.ControlElement

  • Option - QtGui.QStyleOption

  • Maler - PySide.QtGui.QPainter

Zeichnet das angegebene Element mit dem mitgelieferten Maler mit den Stil Optionen durch Option angegeben .... Der Optionsparameter ist ein Zeiger auf ein Objekt QStyleOption und enthält alle Informationen, die erforderlich sind, um das gewünschte Element zu zeichnen .

Der Anrufer sagt drawControl(), welche Art von Element es, indem es eine QStyle.ControlElement Flagge zu ziehen versucht. Steuerelemente sind übergeordnete Komponenten eines Fensters, die Informationen für den Benutzer anzeigen: z. B. Kontrollkästchen, Drucktasten und Menüelemente. war CE_ItemViewItem, die ein Element ist einfach angezeigt werden innerhalb eines Artikels Ansicht

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

Recall das Steuerelement in dem Aufruf von QStyledItemDelegate.paint() gesendet: Alle Kontrollelemente hier aufgezählt werden. Innerhalb QCommonStyle.drawControl(), die CE_ItemViewItem Fall beginnt auf der Leitung 2153. Lassen Sie uns in graben

A. subElementRect (.): Größe zählt

Es Schlüssel ist, um die Größe und das Layout der einzelnen Elemente richtig zu machen. Dies ist das erste, was drawControl() berechnet. Um diese Information zu erhalten, ruft sie subElementRect() auf (definiert in Zeile 2313 und zuerst in Zeile 2158 aufgerufen). Zum Beispiel haben wir:

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

Das erste Argument ist eine QStyle.SubElement Flagge, in diesem Fall SE_ItemViewItemText. Style-Subelemente sind Bestandteile von Control-Elementen. Jedes Element in einer Ansicht hat drei mögliche Unterelemente: das Kontrollkästchen, das Symbol und den Text; Offensichtlich repräsentiert das SE_ItemViewItemText Unterelement den Text. Alle möglichen Unterelemente sind hier aufgezählt:

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

Die subElementRect() Verfahren die Fälle alle in der Subelement Aufzählung enthält: unsere SE_ItemViewItemText Fall beginnt auf der Leitung 3015.

Beachten Sie, dass subElementRect() eine QRect zurückgibt, die eine definiert Rechteck in der Ebene mit ganzzahliger Genauigkeit und kann mit vier ganzen Zahlen (links, oben, Breite, Höhe) konstruiert werden. Zum Beispiel r1 = QRect(100, 200, 11, 16). Dies gibt für ein Unterelement seine Größe sowie die x, y-Position an, an der es im Ansichtsfenster dargestellt wird.

subElementRect() ruft tatsächlich viewItemLayout() (definiert in Zeile 999), um die eigentliche Arbeit zu tun, die ein zweistufiger Prozess ist. Zuerst berechnet viewItemLayout() die Höhe und Breite von Unterelementen mit viewItemSize(). Zweitens berechnet es die x- und y-Position des Unterelements. Betrachten wir nacheinander jede dieser Operationen.

1.viewItemLayout(): Berechnen der Breite und der Höhe von Subelementen

Beginnend auf der Leitung 1003 viewItemLayout() aufruft viewItemSize() (auf der Leitung 838 definiert), die die Höhe und Breite für ein Subelement benötigt berechnet.

Woher erhält viewItemSize() die Standardnummern für Dinge wie die Höhe der Titelleiste? Dies ist die Provinz der Pixelmetrik. Eine Pixelmetrik ist eine stilabhängige Größe, die durch einen einzelnen Pixelwert dargestellt wird. Beispielsweise gibt Style.PM_IndicatorWidth die Breite eines Kontrollkästchens zurück, und QStyle.PM_TitleBarHeight gibt die Höhe der Titelleiste für den Stil Ihrer Anwendung zurück. All die verschiedenen QStyle.PixelMetric s sind hier aufgezählt:

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

Sie können den Wert eines gegebenen Pixels abzurufen Metrik QStyle.pixelMetric(), die viel in viewItemSize() verwendet wird. Der erste Eingang von pixelMetric() ist einer der QStyle.PixelMetric s in der Aufzählung. In qcommonstyle.cpp beginnt die Umsetzung des pixelMetric() auf der Leitung 4367.

Zum Beispiel viewItemSize() die Breite und Höhe der Checkbox (falls erforderlich) mit dem folgenden berechnet:

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

Beachten Sie, dass pixelMetric() wird nicht nur verwendet, in viewItemSize(), aber ist allgegenwärtig. Es wird verwendet, um Metrikeigenschaften vieler GUI-Elemente zu berechnen, von Fensterrändern bis zu Symbolen, und ist durchgängig in qcommonstyle.cpp gespickt. Wenn Sie wissen möchten, wie viele Pixel Ihr Stil für ein grafisches Element verwendet, dessen Größe sich nicht ändert (wie ein Kontrollkästchen), ruft der Stil Pixelmetriken auf.

2. viewItemLayout(): Rubiks Würfel die Subelemente x/y-Positionen

Der zweite Teil des viewItemLayout() widmet bekommen das Layout der Teilelemente, deren Breite und Höhe berechnet wurden nur zu organisieren. Das heißt, sie muss ihre x- und y-Positionen finden, um das Füllen der Werte in QRect s wie textRect zu beenden. Die Unterelemente werden je nach dem allgemeinen Aufbau der Ansicht (z. B. ob sie rechts-links oder links-rechts-orientiert ist) unterschiedlich organisiert. Daher berechnet viewItemLayout() die endgültige Ruheposition jedes Unterelements in Abhängigkeit von solchen Faktoren.

Wenn Sie jemals Hinweise benötigen, wie sizeHint() in einem benutzerdefinierten Delegaten neu zu implementieren, könnte subElementRect() eine nützliche Quelle von Tipps und Tricks sein. Insbesondere enthält viewItemSize() wahrscheinlich nützliche Informationen zu Tidbits und relevanten Pixeln, die Sie möglicherweise abfragen möchten, wenn eine benutzerdefinierte Ansicht dem Standard weitgehend entsprechen soll.

Sobald die QRect s für die Text-, Symbol- und Kontrollkästchen-Unterelemente berechnet wurden, wird drawControl() weitergeführt und schließlich mit dem Zeichnen begonnen.

B.Farbe der Hintergrund: getting primitive

zuerst den Hintergrund wird gefüllt in für das Element (Linie 2163):

drawPrimitive(PE_PanelItemViewItem, option, painter); 

Das ist viel wie der Ruf nach QStyle.drawControl(), aber das erste Argument ist keine Kontrolle Element, aber ein primitives Element (PE). Genau wie drawControl() eine große ausufernde Methode ist, die die Zeichnung aller übergeordneten Steuerelementelemente steuert, zeichnet QStyle.drawPrimitive() die meisten elementaren grafischen Elemente der unteren Ebene in einer GUI (z. B. den rechteckigen Hintergrund in einer Elementansicht). Diese untergeordneten Einheiten, die primitiven Elemente werden hier aufgezählt:

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

Die docs sagen: „Ein primitives Element ist ein gemeinsames GUI-Element, wie eine Checkbox Anzeige Taste oder Fase.“ Zum Beispiel ist das PE_PanelItemViewItem Grundelement "der Hintergrund für ein Element in einer Elementansicht".

Jeder Stil muss eine Implementierung drawPrimitive() haben, und unsere beginnt in Zeile 140. Darin können Sie sehr detailliert entdecken, wie es seine primitiven Malvorgänge ausführt. Dies ist eine nützliche Quelle für Hinweise zur Verwendung des Befehls paint() in der Praxis in Ihren benutzerdefinierten Delegaten.

Die Reimplementierung von QStyle.drawPrimitive() für den PE_PanelItemViewItem Fall beginnt in Zeile 773. Es wählt zuerst die entsprechende Hintergrundfarbe basierend auf dem Status des Elements aus. Dies geschieht durch Abfragen des Artikels QStyle.StateFlag. Die option.state enthält die Statusflags, die den Status des Objekts zu der Zeit beschreiben (ist es aktiviert, ausgewählt, wird bearbeitet usw.?). Diese Status werden nicht nur im Backend verwendet, sondern Sie müssen sie wahrscheinlich auch verwenden, wenn Sie in Ihren benutzerdefinierten Delegaten QStyledItemDelegate.paint() neu implementieren. Sie können eine Aufzählung der QStyle.StateFlag s finden Sie hier:

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

Nach der richtigen Farbe Kommissionierung, drawPrimitive() verwendet dann QPainter.fillRect() die entsprechende Region mit dieser Farbe (Linie 786) zu füllen:

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

QPainter.fillRect() ist eine sehr nützliche Methode beim Implementieren von benutzerdefinierten Delegaten für Sie.

Nachdem Sie sich um den Hintergrund gekümmert haben, begibt sich drawControl() dann zum Zeichnen des Elements, beginnend mit dem Kontrollkästchen (Zeile 2165), dann das Symbol (Zeile 2185) und schließlich den Text (Zeile 2194). Wir werden das nicht im Detail durchgehen, aber ich werde abschließend kurz besprechen, wie es den Text zeichnet.

C. den Text Zeichnung: going abtrünnigen

Zuerst wird der Zustand des Elements verwendet, der Text der Farbe zu spezifizieren. Dies verwendet die eben besprochene QStyle.StateFlags. Dann drawControl() Tritte Textzeichenaufgaben, um eine benutzerdefinierte Methode viewItemDrawText() (bei der Linie 921 definiert):

viewItemDrawText(painter, vopt, textRect); 

Diese Methode nimmt in der Maler, Option und das Textrechteck oben (Teil A) als Parameter beschrieben.Beachten Sie, dass der Optionsparameter sehr wichtig ist: Es handelt sich um eine Klasse QStyleOption, mit der der Inhalt innerhalb eines Styles übergeben wird (einschließlich der Eigenschaften icon, checkstate und text).

Nachdem der Text aus der Option gezogen wurde, wird er in eine QtGui.QTextLine eingefügt, die zu einer QtGui.QTextLayout hinzugefügt wird. Der letzte, elide (d. H. Mit Ellipsen, abhängig von Wortumbrucheinstellungen) Text wird von QPainter.drawText() gezeichnet (siehe Zeile 983), was eine der primitiven Maloperationen von Qt ist.

Ehrlich gesagt ist eine Menge von viewItemDrawText() beschäftigt mit Wortumbruch. Das ist der Punkt, an dem wir anfangen, einige der Eingebungen von Qt kennenzulernen, die kein Python-Benutzer je sehen sollte, geschweige denn, daran herumzubasteln. Zum Beispiel verwendet es die Klasse QStackTextEngine. Ich ermutige Sie zu Google "qstacktextengine pyqt", um zu sehen, wie selten dies für Python-Benutzer aufgekommen ist. Wenn dich das interessiert, dann mach es!

IV. Zusammenfassung

Wenn Sie die zugrunde liegende Malerei Mechanik für den Standard-Delegat zugreifen möchten, werden Sie die Implementierung von QStyle.drawControl() in qcommonstyle.cpp, eine 6000-line Tier einer Datei am Ende des Studiums. Diese Übung kann sehr hilfreich sein, um die genauen Prozeduren zu ermitteln, die zum Berechnen von Größen und zum Zeichnen der primitiven grafischen Elemente verwendet werden, die Elemente enthalten. Manchmal jedoch kann dieses Biest geradezu beängstigend und wenig hilfreich sein, etwa wenn es darum geht, mit Word-Wrap umzugehen. In diesen Fällen müssen Sie wahrscheinlich nur eine benutzerdefinierte Implementierung der gewünschten Funktionalität für Ihren Stellvertreter herausfinden.

Endlich, nachdem wir einen Gesamtüberblick über die Dinge gesehen haben, können wir besser verstehen, wie hilfreich die Dokumentation für QStyle ist, insbesondere der Abschnitt Styles in Item Views. Dort finden wir das folgende offensichtliche Liebesnugget:

Das Malen von Elementen in Ansichten wird von einem Delegaten durchgeführt. Qt Standard delegieren, QStyledItemDelegate, auch für die Berechnung der Hüllrechtecke der Elemente verwendet wird (und deren Unterelemente) ... Wenn QStyledItemDelegate seine Artikel malt, zieht CE_ItemViewItems ... Wenn einen Stil Umsetzung Zeichnung Artikel Ansichten anpassen, müssen Sie zu überprüfen Sie die Implementierung von QCommonStyle (und alle anderen Unterklassen , von denen Ihr Stil erbt). Auf diese Weise finden Sie heraus, welche und wie andere Stilelemente bereits gemalt sind, und Sie können dann das Malen von Elementen, die anders gezeichnet werden sollen, reimplementieren.

Also im Grunde ist die Antwort auf den ursprünglichen Beitrag: "Was sie gesagt haben."