Skip to content

Instantly share code, notes, and snippets.

@trin94
Last active December 1, 2018 11:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save trin94/0b61a7476f276c1f7648172d950041a5 to your computer and use it in GitHub Desktop.
Save trin94/0b61a7476f276c1f7648172d950041a5 to your computer and use it in GitHub Desktop.
Qt HTML Delegate using PyQt5
from PyQt5.QtCore import QModelIndex, QPoint, QSize, Qt
from PyQt5.QtGui import QTextDocument, QAbstractTextDocumentLayout, QPainter, QLinearGradient, QColor, QPalette
from PyQt5.QtWidgets import QStyledItemDelegate, QStyle, QStyleOptionViewItem
def find_colors(option: QStyleOptionViewItem, even_row: bool):
if option.state & QStyle.State_Active and option.state & QStyle.State_Selected:
text_color = option.palette.color(QPalette.Active, QPalette.HighlightedText)
bg_color = option.palette.color(QPalette.Active, QPalette.Highlight)
elif option.state & QStyle.State_Selected:
text_color = option.palette.color(QPalette.Inactive, QPalette.Text)
bg_color = option.palette.color(QPalette.Inactive, QPalette.Highlight)
elif even_row and option.state & QStyle.State_Active:
text_color = option.palette.color(QPalette.Inactive, QPalette.Text)
bg_color = option.palette.color(QPalette.Active, QPalette.Base)
elif even_row:
text_color = option.palette.color(QPalette.Inactive, QPalette.Text)
bg_color = option.palette.color(QPalette.Inactive, QPalette.Base)
elif option.state & QStyle.State_Active:
text_color = option.palette.color(QPalette.Inactive, QPalette.Text)
bg_color = option.palette.color(QPalette.Active, QPalette.AlternateBase)
else:
text_color = option.palette.color(QPalette.Inactive, QPalette.Text)
bg_color = option.palette.color(QPalette.Inactive, QPalette.AlternateBase)
return text_color, bg_color
class HtmlDelegate(QStyledItemDelegate):
def __init__(self, parent=None):
super().__init__(parent)
self.__text_doc = QTextDocument()
self.__text_doc.setDocumentMargin(0.0)
def paint(self, painter: QPainter, option: QStyleOptionViewItem, index: QModelIndex):
new_option = QStyleOptionViewItem(option)
self.initStyleOption(new_option, index)
self.__text_doc.setHtml(new_option.text)
new_option.text = ""
style = new_option.widget.style()
style.drawControl(QStyle.CE_ItemViewItem, new_option, painter, new_option.widget)
text_rect = style.subElementRect(QStyle.SE_ItemViewItemText, new_option, new_option.widget)
pc = QAbstractTextDocumentLayout.PaintContext()
# Colors
tc, bg = find_colors(new_option, index.row() % 2 == 0)
pc.palette.setColor(QPalette.Text, tc)
# Points
text_point = QPoint(text_rect.topLeft().x() + 2,
text_rect.topLeft().y() + round(
0.5 * (option.rect.height() - self.__text_doc.size().height())))
grad_point = QPoint(text_rect.topRight().x() - 43, option.rect.topRight().y())
# Gradient
gradient = QLinearGradient(grad_point.x(), option.rect.height(), grad_point.x() + 35, option.rect.height())
gradient.setColorAt(0, QColor(Qt.transparent))
gradient.setColorAt(1, bg)
# Paint action
painter.save()
painter.translate(text_point)
self.__text_doc.documentLayout().draw(painter, pc)
painter.translate(-text_point)
painter.fillRect(option.rect, gradient)
painter.restore()
def sizeHint(self, option: QStyleOptionViewItem, index: QModelIndex):
return QSize(int(self.__text_doc.idealWidth()), int(self.__text_doc.size().height()))
from PyQt5 import QtCore, QtGui, QtWidgets
class Ui_MainWindow(object):
def setupUi(self, MainWindow):
MainWindow.setObjectName("MainWindow")
MainWindow.resize(550, 300)
MainWindow.setWindowTitle("HTML Delegate")
self.centralwidget = QtWidgets.QWidget(MainWindow)
self.centralwidget.setObjectName("centralwidget")
self.horizontalLayout = QtWidgets.QHBoxLayout(self.centralwidget)
self.horizontalLayout.setObjectName("horizontalLayout")
self.tableView = QtWidgets.QTableView(self.centralwidget)
self.tableView.setObjectName("tableView")
self.horizontalLayout.addWidget(self.tableView)
MainWindow.setCentralWidget(self.centralwidget)
self.menubar = QtWidgets.QMenuBar(MainWindow)
self.menubar.setObjectName("menubar")
MainWindow.setMenuBar(self.menubar)
self.statusbar = QtWidgets.QStatusBar(MainWindow)
self.statusbar.setObjectName("statusbar")
MainWindow.setStatusBar(self.statusbar)
self.retranslateUi(MainWindow)
QtCore.QMetaObject.connectSlotsByName(MainWindow)
def retranslateUi(self, MainWindow):
pass
from PyQt5 import QtWidgets
from PyQt5.QtCore import QItemSelectionModel
from PyQt5.QtGui import QStandardItemModel, QStandardItem
from PyQt5.QtWidgets import QAbstractItemView
from delegate import HtmlDelegate
from gui import Ui_MainWindow
# Supported subset: http://doc.qt.io/qt-5/richtext-html-subset.html
CONTENT = [
'<b>Hello</b>',
'This is <span style="background-color: #FFBF00">highlighted</span> text using <i>HTML</i>. '
'It is also possible using a SyntaxHighlighter in the delegate.',
'<tt>More stuff supported ... </tt>'
]
if __name__ == "__main__":
import sys
app = QtWidgets.QApplication(sys.argv)
MainWindow = QtWidgets.QMainWindow()
ui = Ui_MainWindow()
ui.setupUi(MainWindow)
tv = ui.tableView
# Model
mo = QStandardItemModel(tv)
tv.setModel(mo)
# Headers
tv.horizontalHeader().setStretchLastSection(True)
tv.horizontalHeader().hide()
tv.verticalHeader().hide()
# Delegate
tv.setItemDelegateForColumn(0, HtmlDelegate(parent=tv))
# Selection
tv.setSelectionMode(QAbstractItemView.SingleSelection)
tv.setSelectionBehavior(QAbstractItemView.SelectRows)
# Misc
tv.setAlternatingRowColors(True)
tv.setShowGrid(False)
for item in map(QStandardItem, CONTENT):
mo.appendRow(item)
tv.selectionModel().setCurrentIndex(mo.index(0, 0), QItemSelectionModel.ClearAndSelect | QItemSelectionModel.Rows)
MainWindow.show()
sys.exit(app.exec_())
@trin94
Copy link
Author

trin94 commented Nov 30, 2018

image

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment