Thanks for this code, the better I found on the web.
I use your code in my project but I need to use qss style sheet and your code doesn't work.
I replace QItemDelegate by QStyledItemDelegate and modify your code (vertical alignment on html link, may be you can find another simpler workaroud), and make computations only when string begins with '
class LinkItemDelegate(QStyledItemDelegate):
linkActivated = pyqtSignal(str)
linkHovered = pyqtSignal(str)
def __init__(self, parentView):
super(LinkItemDelegate, self).__init__(parentView)
assert isinstance(parentView, QAbstractItemView), \
"The first argument must be the view"
parentView.setMouseTracking(True)
parentView.viewportEntered.connect(parentView.unsetCursor)
self.documents = []
for i in range(2):
self.documents.append(QTextDocument(self))
self.documents[i].setDocumentMargin(0)
self.lastTextPos = QPoint(0,0)
def drawDisplay(self, painter, option, rect, text):
mouseOver = option.state & QStyle.State_MouseOver \
and rect.contains(self.parent().viewport() \
.mapFromGlobal(QCursor.pos())) \
and option.state & QStyle.State_Enabled
fontMetrics = QFontMetrics(option.font)
rect.moveTop(rect.y() + rect.height() / 2 - fontMetrics.height() / 2)
if mouseOver:
doc = self.documents[0]
self.lastTextPos = rect.topLeft()
doc.setDefaultStyleSheet("")
else:
doc = self.documents[1]
doc.setDefaultStyleSheet("a {text-decoration: none; }")
doc.setDefaultFont(option.font)
doc.setHtml(text)
painter.save()
painter.translate(rect.topLeft())
ctx = QAbstractTextDocumentLayout.PaintContext()
ctx.palette = option.palette
doc.documentLayout().draw(painter, ctx)
painter.restore()
def editorEvent(self, event, model, option, index):
if event.type() not in [QEvent.MouseMove, QEvent.MouseButtonRelease] \
or not (option.state & QStyle.State_Enabled):
return False
pos = QPointF(event.pos() - self.lastTextPos)
anchor = self.documents[0].documentLayout().anchorAt(pos)
if anchor == "":
self.parent().unsetCursor()
else:
self.parent().setCursor(Qt.PointingHandCursor)
if event.type() == QEvent.MouseButtonRelease:
self.linkActivated.emit(anchor)
return True
else:
self.linkHovered.emit(anchor)
return False
def sizeHint(self, option, index):
size = super(LinkItemDelegate, self).sizeHint(option, index)
if option.text.startswith('<a'):
doc = self.documents[1]
html = index.data()
doc.setHtml(html)
plainText = doc.toPlainText()
fontMetrics = QFontMetrics(option.font)
diff = fontMetrics.width(html) - fontMetrics.width(plainText)
size = size - QSize(diff, 0)
return size
def paint(self, painter, option, index):
if (index.isValid()):
text = None
options = QStyleOptionViewItem(option)
self.initStyleOption(options,index)
if options.text.startswith('<a'):
text = options.text
options.text = ""
style = options.widget.style() if options.widget.style() else QApplication.style()
style.drawControl(QStyle.CE_ItemViewItem, options, painter, options.widget)
if text:
textRect = style.subElementRect(QStyle.SE_ItemViewItemText, options, options.widget)
self.drawDisplay(painter, option, textRect, text)
Don't forget to connect item delegate :
linkItemDelegate = LinkItemDelegate(self.my_treeView)
linkItemDelegate.linkActivated.connect(self.onClicLink)
self.my_treeView.setItemDelegate(linkItemDelegate) # Create custom delegate and set model and delegate to the treeview object
And it's work great !