Home > front end >  Usage of QTableView and QStyledItemDelegate classes (PyQt5)
Usage of QTableView and QStyledItemDelegate classes (PyQt5)

Time:12-29

I found a PyQt5 code from here (https://stackoverflow.com/a/44365155/12875212) including two classes CustomTableView(QtWidgets.QTableView) and CustomDelegate(QtWidgets.QStyledItemDelegate).

How can I use them for a QTableView in a QMainWindow with my data?

Code:

from PyQt5 import QtCore, QtWidgets, QtGui
from PyQt5.QtWidgets import QApplication, QMainWindow, QTableView
import sys


class CustomTableView(QtWidgets.QTableView):
    link_activated = QtCore.pyqtSignal(str)

    def __init__(self, parent=None):
        self.parent = parent
        super().__init__(parent)

        self.setMouseTracking(True)
        self._mousePressAnchor = ''
        self._lastHoveredAnchor = ''

    def mousePressEvent(self, event):
        anchor = self.anchorAt(event.pos())
        self._mousePressAnchor = anchor

    def mouseMoveEvent(self, event):
        anchor = self.anchorAt(event.pos())
        if self._mousePressAnchor != anchor:
            self._mousePressAnchor = ''

        if self._lastHoveredAnchor != anchor:
            self._lastHoveredAnchor = anchor
            if self._lastHoveredAnchor:
                QtWidgets.QApplication.setOverrideCursor(QtGui.QCursor(QtCore.Qt.PointingHandCursor))
            else:
                QtWidgets.QApplication.restoreOverrideCursor()

    def mouseReleaseEvent(self, event):
        if self._mousePressAnchor:
            anchor = self.anchorAt(event.pos())
            if anchor == self._mousePressAnchor:
                self.link_activated.emit(anchor)
            self._mousePressAnchor = ''

    def anchorAt(self, pos):
        index = self.indexAt(pos)
        if index.isValid():
            delegate = self.itemDelegate(index)
            if delegate:
                itemRect = self.visualRect(index)
                relativeClickPosition = pos - itemRect.topLeft()
                html = self.model().data(index, QtCore.Qt.ItemDataRole.DisplayRole)
                return delegate.anchorAt(html, relativeClickPosition)
        return ''


class CustomDelegate(QtWidgets.QStyledItemDelegate):

    def anchorAt(self, html, point):
        doc = QtGui.QTextDocument()
        doc.setHtml(html)
        textLayout = doc.documentLayout()
        return textLayout.anchorAt(point)

    def paint(self, painter, option, index):
        options = QtWidgets.QStyleOptionViewItem(option)
        self.initStyleOption(options, index)

        if options.widget:
            style = options.widget.style()
        else:
            style = QtWidgets.QApplication.style()

        doc = QtGui.QTextDocument()
        doc.setHtml(options.text)
        options.text = ''

        style.drawControl(QtWidgets.QStyle.CE_ItemViewItem, options, painter)
        ctx = QtGui.QAbstractTextDocumentLayout.PaintContext()

        textRect = style.subElementRect(QtWidgets.QStyle.SE_ItemViewItemText, options)

        painter.save()

        painter.translate(textRect.topLeft())
        painter.setClipRect(textRect.translated(-textRect.topLeft()))
        painter.translate(0, 0.5 * (options.rect.height() - doc.size().height()))
        doc.documentLayout().draw(painter, ctx)

        painter.restore()

    def sizeHint(self, option, index):
        options = QtWidgets.QStyleOptionViewItem(option)
        self.initStyleOption(options, index)

        doc = QtGui.QTextDocument()
        doc.setHtml(options.text)
        doc.setTextWidth(options.rect.width())

        return QtCore.QSize(doc.idealWidth(), doc.size().height())

Data and QMainWindow:

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()

        self.table = CustomTableView

        data = [
            ['<p style="color: #ffffff; background-color: #ff0000">test</p>', 9, 2],
            [1, '<b>Hello</b>', -1],
            [3, '<a href="https://stackoverflow.com/">Stackoverflow</a>', 2],
            ['<a href="https://www.google.com/">Google</a>', 3, 2],
            [5, 8, 9],
        ]

        #self.table.setItemDelegate(CustomDelegate()) # not working


app = QApplication(sys.argv)
window = MainWindow()
window.show()
app.exec_()

CodePudding user response:

Thank you musicamante, as suggested I solved it by adding a QAbstractTableModel:

class PandasModel(QAbstractTableModel):
    def __init__(self, data):
        super().__init__()
        self._data = data

    def rowCount(self, index):
        # The length of the outer list.
        return len(self._data)

    def columnCount(self, index):
        # The following takes the first sub-list, and returns
        # the length (only works if all rows are an equal length)
        return len(self._data[0])

    def data(self, index, role=QtCore.Qt.ItemDataRole.DisplayRole):
        if index.isValid():
            if role == QtCore.Qt.ItemDataRole.DisplayRole or role == QtCore.Qt.ItemDataRole.EditRole:
                value = self._data[index.row()][index.column()]
                return str(value)
                # return str(value)

    def setData(self, index, value, role):
        if role == QtCore.Qt.ItemDataRole.EditRole:
            self._data[index.row()][index.column()] = value
            return True
        return False

    def flags(self, index):
        return QtCore.Qt.ItemFlag.ItemIsSelectable | QtCore.Qt.ItemFlag.ItemIsEnabled | QtCore.Qt.ItemFlag.ItemIsEditable

And call it by:

        self.model = PandasModel(data)
        self.table.setModel(self.model)

        self.table.setItemDelegate(CustomDelegate())
        self.setCentralWidget(self.table)
  • Related