eric
eric

Reputation: 8108

Vertically center item's text using a delegate (PySide/Qt/PyQt)

I have a custom delegate for a QTableView that allows for the display/editing of html strings (using a QTextDocument). There is a SSCCE below.

Unfortunately, when I display the text using paint(), it is not vertically centered, but seems to top align. For instance, if I apply the delegate to the second column, but not the first, the table looks like this:

the problem

My search hasn't revealed anything on how to fix this from within the delegate in a principled way. Manually adding 5 to option.rect.y() works on my computer, but I don't consider that principled.

Is there some way to center the text vertically?

SSCCE

from PySide import QtGui
import sys

class HtmlTable(QtGui.QTableView):
    def __init__(self, parent = None):    
        QtGui.QTableView.__init__(self)
        model = QtGui.QStandardItemModel()
        model.setHorizontalHeaderLabels(['Title', 'Summary'])
        item0 = [QtGui.QStandardItem('Infinite Jest'), QtGui.QStandardItem('Hello, <i>Hal</i>')]
        item00 = [QtGui.QStandardItem('Hamlet'), QtGui.QStandardItem('Best served <b>cold</b>')]
        model.appendRow(item0)
        model.appendRow(item00)          
        self.setModel(model)
        self.setItemDelegate(HtmlPainter(self))

class HtmlPainter(QtGui.QStyledItemDelegate):
    def __init__(self, parent=None):
        QtGui.QStyledItemDelegate.__init__(self, parent)
    def paint(self, painter, option, index):
        if index.column() == 1: 
            text = index.model().data(index) #default role is display
            palette = QtGui.QApplication.palette()
            document = QtGui.QTextDocument()
            document.setDefaultFont(option.font)
            #Set text (color depends on whether selected)
            if option.state & QtGui.QStyle.State_Selected:  
                displayString = "<font color={0}>{1}</font>".format(palette.highlightedText().color().name(), text) 
                document.setHtml(displayString)
            else:
                document.setHtml(text)
            #Set background color
            bgColor = palette.highlight().color() if (option.state & QtGui.QStyle.State_Selected)\
                     else palette.base().color()
            painter.save()
            painter.fillRect(option.rect, bgColor)
            painter.translate(option.rect.x(), option.rect.y())  #If I add +5 it works
            document.drawContents(painter)
            painter.restore()
        else:
            QtGui.QStyledItemDelegate.paint(self, painter, option, index)          


def main():
    app = QtGui.QApplication(sys.argv)
    myTable = HtmlTable()
    myTable.show()
    sys.exit(app.exec_())

if __name__ == "__main__":
    main()

Upvotes: 3

Views: 970

Answers (1)

Pavel Strakhov
Pavel Strakhov

Reputation: 40522

You should set document's width using QTextDocument::setTextWidth. This allows you to determine text height and use it to calculate offset:

document.setTextWidth(option.rect.width())
offset_y = (option.rect.height() - document.size().height())/2
painter.translate(option.rect.x(), option.rect.y() + offset_y) 
document.drawContents(painter) 

Setting text width is also necessary because otherwise the text would not be wrapped when column is not wide enough.

You might want to reimplement sizeHint to calculate preferred width and height based on document size.

Upvotes: 5

Related Questions