QTableView 和 QStyledItemDelegate 类的使用 (PyQt5)

Posted

技术标签:

【中文标题】QTableView 和 QStyledItemDelegate 类的使用 (PyQt5)【英文标题】:Usage of QTableView and QStyledItemDelegate classes (PyQt5) 【发布时间】:2021-12-26 15:44:08 【问题描述】:

我从这里 (https://***.com/a/44365155/12875212) 找到了一个 PyQt5 代码,其中包括两个类 CustomTableView(QtWidgets.QTableView)CustomDelegate(QtWidgets.QStyledItemDelegate)

如何将它们用于 QMainWindow 中的 QTableView 与我的数据?

代码:

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())

数据和 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://***.com/">***</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_()

【问题讨论】:

错字:更改为self.table = CustomTableView()。要基于基本 python 数据对象设置模型,请参阅***.com/questions/63012839/… 【参考方案1】:

谢谢musicamante,正如我建议的那样,我通过添加一个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

并通过以下方式调用它:

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

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

【讨论】:

以上是关于QTableView 和 QStyledItemDelegate 类的使用 (PyQt5)的主要内容,如果未能解决你的问题,请参考以下文章

QTableView() 仅在选择时更新更改

从 QTableView 读取和写入文件

QTreeView 和 QTableView 的 Qt 模型

QTableView:如何设置搜索栏

QTableView 和 QStandardItemModel 的问题

QTableView和QTableWidget翻页功能实现