Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Learn more about Collectives
Teams
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Learn more about Teams
I got the following code, which I slightly modified, as an answer on
how to use HTML formatted text in a QListWidget
.
main.ui
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>Dialog</class>
<widget class="QDialog" name="Dialog">
<property name="geometry">
<x>0</x>
<y>0</y>
<width>341</width>
<height>161</height>
</rect>
</property>
<property name="windowTitle">
<string>Dialog</string>
</property>
<widget class="QWidget" name="verticalLayoutWidget">
<property name="geometry">
<x>10</x>
<y>10</y>
<width>321</width>
<height>141</height>
</rect>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<widget class="QListWidget" name="lwOptions"/>
</item>
</layout>
</widget>
</widget>
<resources/>
<connections/>
test.py
import os
import html
from PyQt5 import uic
from PyQt5.Qt import (QApplication, QDialog, QListWidget,
QListWidgetItem, QStyleOptionViewItem,
QStyledItemDelegate, QStyle)
from PyQt5.QtGui import QPalette, QAbstractTextDocumentLayout, QTextDocument
from PyQt5.QtCore import QSize
class HTMLDelegate(QStyledItemDelegate):
def __init__(self, parent=None):
super(HTMLDelegate, self).__init__(parent)
self.doc = QTextDocument(self)
def paint(self, painter, option, index):
painter.save()
options = QStyleOptionViewItem(option)
self.initStyleOption(options, index)
self.doc.setHtml(options.text)
options.text = ""
style = QApplication.style() if options.widget is None \
else options.widget.style()
style.drawControl(QStyle.CE_ItemViewItem, options, painter)
ctx = QAbstractTextDocumentLayout.PaintContext()
if option.state & QStyle.State_Selected:
ctx.palette.setColor(QPalette.Text, option.palette.color(
QPalette.Active, QPalette.HighlightedText))
else:
ctx.palette.setColor(QPalette.Text, option.palette.color(
QPalette.Active, QPalette.Text))
textRect = style.subElementRect(QStyle.SE_ItemViewItemText, options, None)
if index.column() != 0:
textRect.adjust(5, 0, 0, 0)
constant = 4
margin = (option.rect.height() - options.fontMetrics.height()) // 2
margin = margin - constant
textRect.setTop(textRect.top() + margin)
painter.translate(textRect.topLeft())
painter.setClipRect(textRect.translated(-textRect.topLeft()))
self.doc.documentLayout().draw(painter, ctx)
painter.restore()
def sizeHint(self, option, index):
return QSize(self.doc.idealWidth(), self.doc.size().height())
class GUI(QDialog):
def __init__(self):
super(GUI, self).__init__()
dirname = os.path.dirname(os.path.abspath(__file__))
uic.loadUi(os.path.join(dirname,'main.ui'), self)
delegate = HTMLDelegate(self.lwOptions)
self.lwOptions.setItemDelegate(delegate)
for ordinal in ['first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'seventh', 'eighth', 'ninth']:
item = QListWidgetItem('This is the <b>{}</b> word. This is the <b>{}</b> word. This is the <b>{}</b> word.'.format(ordinal, ordinal, ordinal))
self.lwOptions.addItem(item)
if __name__ == '__main__':
import sys
app = QApplication(sys.argv)
window = GUI()
window.show()
sys.exit(app.exec_())
The code works fine, however, text that is longer than the width of the QListWidget
is truncated and no horizontal scroll bar is displayed.
(The vertical scroll bar is automatically added when needed.)
I tried forcing the display via ScrollBarAlwaysOn, which only displayed a non-working horizontal scroll bar.
How do I need to change the code to automatically add a horizontal scroll bar when needed?
The calculation I made in my previous answer was incorrect since the size that returns sizeHint()
is relative to the previous item. A simple solution is to use the Qt::SizeHintRole
role to set the size.
import os
from PyQt5 import QtCore, QtGui, QtWidgets, uic
class HTMLDelegate(QtWidgets.QStyledItemDelegate):
def __init__(self, parent=None):
super(HTMLDelegate, self).__init__(parent)
self.doc = QtGui.QTextDocument(self)
def paint(self, painter, option, index):
painter.save()
options = QtWidgets.QStyleOptionViewItem(option)
self.initStyleOption(options, index)
self.doc.setHtml(options.text)
options.text = ""
style = QApplication.style() if options.widget is None \
else options.widget.style()
style.drawControl(QtWidgets.QStyle.CE_ItemViewItem, options, painter)
ctx = QtGui.QAbstractTextDocumentLayout.PaintContext()
if option.state & QtWidgets.QStyle.State_Selected:
ctx.palette.setColor(QtGui.QPalette.Text, option.palette.color(
QtGui.QPalette.Active, QtGui.QPalette.HighlightedText))
else:
ctx.palette.setColor(QtGui.QPalette.Text, option.palette.color(
QtGui.QPalette.Active, QtGui.QPalette.Text))
textRect = style.subElementRect(QtWidgets.QStyle.SE_ItemViewItemText, options, None)
if index.column() != 0:
textRect.adjust(5, 0, 0, 0)
constant = 4
margin = (option.rect.height() - options.fontMetrics.height()) // 2
margin = margin - constant
textRect.setTop(textRect.top() + margin)
painter.translate(textRect.topLeft())
painter.setClipRect(textRect.translated(-textRect.topLeft()))
self.doc.documentLayout().draw(painter, ctx)
painter.restore()
s = QtCore.QSize(self.doc.idealWidth(), self.doc.size().height())
index.model().setData(index, s, QtCore.Qt.SizeHintRole)
class GUI(QtWidgets.QDialog):
def __init__(self):
super(GUI, self).__init__()
dirname = os.path.dirname(os.path.abspath(__file__))
uic.loadUi(os.path.join(dirname,'main.ui'), self)
delegate = HTMLDelegate(self.lwOptions)
self.lwOptions.setItemDelegate(delegate)
for ordinal in ['first', 'second', 'third', 'fourth', 'fifth', 'sixth', 'seventh', 'eighth', 'ninth']:
item = QtWidgets.QListWidgetItem('This is the <b>{}</b> word. This is the <b>{}</b> word. This is the <b>{}</b> word.'.format(ordinal, ordinal, ordinal))
self.lwOptions.addItem(item)
if __name__ == '__main__':
import sys
app = QtWidgets.QApplication(sys.argv)
window = GUI()
window.show()
sys.exit(app.exec_())
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.