2009-12-24 3 views
40

Angenommen, mein Modell hat Elemente mit der folgenden Zeichenfolge für Qt :: DisplayRoleWie Punkt Ansicht reich render (html) Text in Qt

<span>blah-blah <b>some text</b> other blah</span> 

Ich möchte QTreeView (eigentlich jedes Element Ansicht) machen es zu machen wie ein reicher Text. Stattdessen werden sie in Elementansichten standardmäßig als reiner Text dargestellt. Wie erreiche ich das gewünschte Rendering?


Eigentlich ist dies ein Suchergebnismodell. Der Benutzer gibt einen Text ein, ein Dokument wird mit diesem Text durchsucht, und dem Benutzer werden Suchergebnisse angezeigt, wobei die Wörter, die durchsucht werden, mutiger sein sollten als der umgebende Text.

Antwort

19

Meine Antwort ist meist von @ serge_gubenko's inspiriert. Es wurden jedoch einige Verbesserungen vorgenommen, so dass der Code schließlich in meiner Anwendung nützlich ist.

class HtmlDelegate : public QStyledItemDelegate 
{ 
protected: 
    void paint (QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const; 
    QSize sizeHint (const QStyleOptionViewItem & option, const QModelIndex & index) const; 
}; 

void HtmlDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const 
{ 
    QStyleOptionViewItemV4 optionV4 = option; 
    initStyleOption(&optionV4, index); 

    QStyle *style = optionV4.widget? optionV4.widget->style() : QApplication::style(); 

    QTextDocument doc; 
    doc.setHtml(optionV4.text); 

    /// Painting item without text 
    optionV4.text = QString(); 
    style->drawControl(QStyle::CE_ItemViewItem, &optionV4, painter); 

    QAbstractTextDocumentLayout::PaintContext ctx; 

    // Highlighting text if item is selected 
    if (optionV4.state & QStyle::State_Selected) 
     ctx.palette.setColor(QPalette::Text, optionV4.palette.color(QPalette::Active, QPalette::HighlightedText)); 

    QRect textRect = style->subElementRect(QStyle::SE_ItemViewItemText, &optionV4); 
    painter->save(); 
    painter->translate(textRect.topLeft()); 
    painter->setClipRect(textRect.translated(-textRect.topLeft())); 
    doc.documentLayout()->draw(painter, ctx); 
    painter->restore(); 
} 

QSize HtmlDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const 
{ 
    QStyleOptionViewItemV4 optionV4 = option; 
    initStyleOption(&optionV4, index); 

    QTextDocument doc; 
    doc.setHtml(optionV4.text); 
    doc.setTextWidth(optionV4.rect.width()); 
    return QSize(doc.idealWidth(), doc.size().height()); 
} 
+0

Beachten Sie, dass der Abschnitt ctx.palette.setcolor eine zusätzliche verschachtelte benötigt, wenn das Konto für OptionV4.state inaktiv ist.Andernfalls wird der Text fast unlesbar, wenn Sie in ein anderes Fenster wechseln. Funktioniert ansonsten super. Danke – mmccoo

+3

Text Farbhinweis: Verwenden Sie 'else ctx.palette.setColor (QPalette :: Text, optionV4.palette.color (QPalette :: Aktiv, QPalette :: Text));' um sicherzustellen, dass Textfarbe richtig eingestellt ist. Wird benötigt, wenn nicht standardmäßige Textfarben über Stylesheet verwendet werden. –

+2

QTextDocument Setup: Wenn Sie 'doc.setDocumentMargin (0); doc.setDefaultFont (optionV4.font); '(fügen Sie beides in paint & sizeHint hinzu), dann sind die Schriftarten korrekt, wenn Sie sie über Stylesheet ändern. Auch der Aufruf 'doc.setTextWidth' in der sizeHint-Routine scheint nichts zu tun. Wenn Sie es sowohl in die 'sizeHint'- als auch die' paint'-Methode schreiben, können Sie Wörter verschwinden lassen, anstatt abgeschnitten zu werden, wenn die Spalte des Artikels schrumpft. –

34

Ich denke, Sie können setItemDelegate Methode der Treeview verwenden, um benutzerdefinierte Maler für Ihre Treeview-Elemente einzurichten. In der paint-Methode des Delegate können Sie QTextDocument verwenden, um den Text des Elements als HTML zu laden und zu rendern. Bitte überprüfen Sie, ob ein Beispiel unten für Sie arbeiten würde:

treeview Initialisierung:

... 
    // create simple model for a tree view 
    QStandardItemModel *model = new QStandardItemModel(); 
    QModelIndex parentItem; 
    for (int i = 0; i < 4; ++i) 
    { 
     parentItem = model->index(0, 0, parentItem); 
     model->insertRows(0, 1, parentItem); 
     model->insertColumns(0, 1, parentItem); 
     QModelIndex index = model->index(0, 0, parentItem); 
     model->setData(index, "<span>blah-blah <b>some text</b> other blah</span>"); 
    } 
    // create custom delegate 
    HTMLDelegate* delegate = new HTMLDelegate(); 
    // set model and delegate to the treeview object 
    ui->treeView->setModel(model); 
    ui->treeView->setItemDelegate(delegate); 
... 

individuelle Delegat Implementierung

class HTMLDelegate : public QStyledItemDelegate 
{ 
protected: 
    void paint (QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const; 
    QSize sizeHint (const QStyleOptionViewItem & option, const QModelIndex & index) const; 
}; 

void HTMLDelegate::paint(QPainter* painter, const QStyleOptionViewItem & option, const QModelIndex &index) const 
{ 
    QStyleOptionViewItemV4 options = option; 
    initStyleOption(&options, index); 

    painter->save(); 

    QTextDocument doc; 
    doc.setHtml(options.text); 

    options.text = ""; 
    options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter); 

    painter->translate(options.rect.left(), options.rect.top()); 
    QRect clip(0, 0, options.rect.width(), options.rect.height()); 
    doc.drawContents(painter, clip); 

    painter->restore(); 
} 

QSize HTMLDelegate::sizeHint (const QStyleOptionViewItem & option, const QModelIndex & index) const 
{ 
    QStyleOptionViewItemV4 options = option; 
    initStyleOption(&options, index); 

    QTextDocument doc; 
    doc.setHtml(options.text); 
    doc.setTextWidth(options.rect.width()); 
    return QSize(doc.idealWidth(), doc.size().height()); 
} 

hoffe, das hilft, sieht

UPDATE0: Änderungen HTMLDelegate, um Symbole sichtbar zu machen und verschiedene Stiftfarben für ausgewählte Objekte

void HTMLDelegate::paint(QPainter* painter, const QStyleOptionViewItem & option, const QModelIndex &index) const 
{ 
    QStyleOptionViewItemV4 options = option; 
    initStyleOption(&options, index); 

    painter->save(); 

    QTextDocument doc; 
    doc.setHtml(options.text); 

    options.text = ""; 
    options.widget->style()->drawControl(QStyle::CE_ItemViewItem, &options, painter); 

    // shift text right to make icon visible 
    QSize iconSize = options.icon.actualSize(options.rect.size()); 
    painter->translate(options.rect.left()+iconSize.width(), options.rect.top()); 
    QRect clip(0, 0, options.rect.width()+iconSize.width(), options.rect.height()); 

    //doc.drawContents(painter, clip); 

    painter->setClipRect(clip); 
    QAbstractTextDocumentLayout::PaintContext ctx; 
    // set text color to red for selected item 
    if (option.state & QStyle::State_Selected) 
     ctx.palette.setColor(QPalette::Text, QColor("red")); 
    ctx.clip = clip; 
    doc.documentLayout()->draw(painter, ctx); 

    painter->restore(); 
} 
+0

Vielen Dank für Ihre Antwort. Eigentlich habe ich mit dem übergeordneten Delegaten und QTextDocument gespielt. Es gab jedoch ein Problem mit der Größe der Elemente. Ihre Antwort hat mich auf 'initStyleOption' und' widget-> style() -> drawControl' hingewiesen. Ihre Lösung ist ausgezeichnet, außer zwei Probleme. 1. Der Text wird über das Elementsymbol gezeichnet. 2. Das ausgewählte Element sollte eine andere Textfarbe haben. Versuchen Sie herauszufinden, wie Sie sie beheben können. –

+0

pls überprüfen Sie die update0 für den ursprünglichen Beitrag; Änderungen sind in der HTMLDelegate :: paint-Methode. Um Icons sichtbar zu machen, habe ich den Text einfach nach rechts verschoben. Wie für die Textfarbe musste ich die Paletteneinstellungen für die Textfarbe des Malkontextobjekts ändern. Hoffe, das ist, was Sie suchen, Grüße –

+0

@Anton haben Sie herausgefunden, wie Sie die ausgewählte Textfarbe ändern? – To1ne

15

Hier ist die PyQt Umwandlung der Kombination der obigen Antworten, die für mich funktionierte. Ich würde erwarten, dass dies auch für PySide praktisch identisch funktioniert.

from PyQt4 import QtCore, QtGui 

class HTMLDelegate(QtGui.QStyledItemDelegate): 
    def paint(self, painter, option, index): 
     options = QtGui.QStyleOptionViewItemV4(option) 
     self.initStyleOption(options,index) 

     style = QtGui.QApplication.style() if options.widget is None else options.widget.style() 

     doc = QtGui.QTextDocument() 
     doc.setHtml(options.text) 

     options.text = "" 
     style.drawControl(QtGui.QStyle.CE_ItemViewItem, options, painter); 

     ctx = QtGui.QAbstractTextDocumentLayout.PaintContext() 

     # Highlighting text if item is selected 
     #if (optionV4.state & QStyle::State_Selected) 
      #ctx.palette.setColor(QPalette::Text, optionV4.palette.color(QPalette::Active, QPalette::HighlightedText)); 

     textRect = style.subElementRect(QtGui.QStyle.SE_ItemViewItemText, options) 
     painter.save() 
     painter.translate(textRect.topLeft()) 
     painter.setClipRect(textRect.translated(-textRect.topLeft())) 
     doc.documentLayout().draw(painter, ctx) 

     painter.restore() 

    def sizeHint(self, option, index): 
     options = QtGui.QStyleOptionViewItemV4(option) 
     self.initStyleOption(options,index) 

     doc = QtGui.QTextDocument() 
     doc.setHtml(options.text) 
     doc.setTextWidth(options.rect.width()) 
     return QtCore.QSize(doc.idealWidth(), doc.size().height()) 
+1

Was für ein Hack! Ach, aber danke. Hervorhebung: if options.state & QtGui.QStyle.State_Selected: ctx.palette.setColor (QtGui.QPalette.Text, Optionen.palette.Farbe (QtGui.QPalette.Active, QtGui.QPalette.HighlightedText)) – Pepijn

+3

Nach Zeile: ' doc.setHtml (options.text) ', müssen Sie auch' doc.setTextWidth (option.rect.width()) '' setzen, sonst wird der Delegat den Inhalt in Bezug auf den Zielzeichenbereich nicht mehr korrekt darstellen. Zum Beispiel werden Wörter in QListView nicht umgebrochen. – Timo

4

Dieser ist in PySide. Anstatt viel benutzerdefiniertes Zeichnen zu machen, gebe ich den QPainter an QLabel und lasse ihn zeichnen. Hervorhebung von Code aus anderen Antworten.

from PySide import QtGui 

class TaskDelegate(QtGui.QItemDelegate): 
    #http://doc.qt.nokia.com/4.7/qitemdelegate.html#drawDisplay 
    #http://doc.qt.nokia.com/4.7/qwidget.html#render 
    def drawDisplay(self, painter, option, rect, text): 
     label = QtGui.QLabel(text) 

     if option.state & QtGui.QStyle.State_Selected: 
      p = option.palette 
      p.setColor(QtGui.QPalette.WindowText, p.color(QtGui.QPalette.Active, QtGui.QPalette.HighlightedText)) 

      label.setPalette(p) 

     label.render(painter, rect.topLeft(), renderFlags=QtGui.QWidget.DrawChildren) 
+1

Funktionierte nicht für mich, ich sehe nur einen sehr kleinen Teil des Textes, zufällig, und einige Einträge. – WhyNotHugo

+0

Für diejenigen, die es brauchen: Ich modifizierte @Pepijn Antwort ein wenig, um auch mehrzeilige Etiketten in http://stackoverflow.com/a/38028318/1504082 zu decken – maggie