Search code examples
pythonpyqt5qtableviewhtml-rendering

HTML renderer for QTableView - what's happening to the font size?


This answer (updated by me from other answers there) to a question about how to render HTML in a QTableView certainly seems to produce a marked-up text look.

But there's a problem with the font size. See this MCE: column 0 uses the standard paint and sizeHint methods. You can click on the right-hand cell to see that the print statement is saying this is a size 12 font. But it's not showing like that.

from PyQt5 import QtWidgets, QtCore, QtGui
import sys

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.resize(1000, 500)
        self.centralwidget = QtWidgets.QWidget(MainWindow)
        self.verticalLayoutWidget = QtWidgets.QWidget(self.centralwidget)
        self.verticalLayoutWidget.setGeometry(QtCore.QRect(20, 20, 800, 300))
        self.verticalLayout = QtWidgets.QVBoxLayout(self.verticalLayoutWidget)
        self.table_view = QtWidgets.QTableView(self.verticalLayoutWidget)
        self.table_view.horizontalHeader().setStretchLastSection(True)
        
        class HTMLDelegate( QtWidgets.QStyledItemDelegate ):
            def __init__( self ):
                super().__init__()
                self.doc = QtGui.QTextDocument()
        
            def paint(self, painter, option, index):
                # default paint op in col 0
                if index.column() == 0:
                    super().paint(painter, option, index)
                    return
                
                options = QtWidgets.QStyleOptionViewItem(option)
                print( f'options {options} font {options.font} size {options.font.pointSize()} F {options.font.pointSizeF()}')
                self.initStyleOption(options, index)
                painter.save()
                self.doc.setTextWidth(options.rect.width())                
                self.doc.setHtml(options.text)
                options.text = ''
                options.widget.style().drawControl(QtWidgets.QStyle.CE_ItemViewItem, options, painter)
                painter.translate(options.rect.left(), options.rect.top())
                clip = QtCore.QRectF(0, 0, options.rect.width(), options.rect.height())
                painter.setClipRect(clip)
                ctx = QtGui.QAbstractTextDocumentLayout.PaintContext()
                ctx.clip = clip
                self.doc.documentLayout().draw(painter, ctx)
                painter.restore()
        
            def sizeHint( self, option, index ):
                # default size hint in col 0
                if index.column() == 0:
                    return super().sizeHint(option, index)
                
                print( f'option {option}' ) 
                options = QtWidgets.QStyleOptionViewItem(option)
                print( f'options {options} font {options.font} size {options.font.pointSize()} F {options.font.pointSizeF()}')
        
                self.initStyleOption(option, index)
                self.doc.setHtml(option.text)
                self.doc.setTextWidth(option.rect.width())
                return QtCore.QSize(self.doc.idealWidth(), self.doc.size().height())
    
        self.table_view.setItemDelegate(HTMLDelegate())
        
        # nice big font
        font = QtGui.QFont()
        font.setPointSize(12)
        self.table_view.setFont(font)
        
        self.table_view.setGeometry(QtCore.QRect(20, 20, 800, 300))
        self.verticalLayout.addWidget(self.table_view)
        self.table_view.setModel(QtGui.QStandardItemModel() )
        self.table_view.model().appendRow([QtGui.QStandardItem('no markup'), 
            QtGui.QStandardItem('here is some <strong>marked up</strong> html <em>text</em>'),])
        MainWindow.setCentralWidget(self.centralwidget)

class MainWindow( QtWidgets.QMainWindow ):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

app = QtWidgets.QApplication(sys.argv)
application = MainWindow()
application.show()
sys.exit(app.exec())

Solution

  • Checking the options.font is pointless if you don't use it: you altered the font on the widget, but the QTextDocument cannot know anything about that.

    Just set the correct font using the option:

        def paint(self, painter, option, index):
            # ...
            self.doc.setDefaultFont(options.font)
            # ...
    

    Please, never modify pyuic files unless you really know what you're doing and why, it's considered bad practice and providing code that does that is not a good thing.