【问题标题】:How to highlight a words in QTableWidget from a Searchlist如何从搜索列表中突出显示 QTableWidget 中的单词
【发布时间】:2019-04-20 13:30:03
【问题描述】:

我想通过单词列表搜索 QTableWidget-Table,如果找到它们,我希望它们被突出显示。

我试图从here 修改代码,因此该表正在按单词列表进行搜索,而不仅仅是一个单词。不幸的是,我的结果不断被覆盖。我总是只得到列表中最后一个单词的结果。

有谁知道如何修改代码以显示整个单词列表的结果?

代码如下:

from PyQt5 import QtCore, QtGui, QtWidgets
import random
import html

words_1 = ["Hello dseerfd", "world sdfsdf sdfgsdf sdfsdf", "Stack dasdf", "Overflow", "Hello world", """<font color="red">Hello world</font>"""]

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):
        substring = index.data(QtCore.Qt.UserRole)
        painter.save()
        options = QtWidgets.QStyleOptionViewItem(option)
        self.initStyleOption(options, index)
        res = ""
        color = QtGui.QColor("red")
        if substring:
            substrings = options.text.split(substring)
            res = """<font color="{}">{}</font>""".format(color.name(QtGui.QColor.HexRgb), substring).join(list(map(html.escape, substrings)))
        else:
            res = html.escape(options.text)
        self.doc.setHtml(res)

        options.text = ""
        style = QtWidgets.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)

        if index.column() != 0:
            textRect.adjust(5, 0, 0, 0)

        the_constant = 4
        margin = (option.rect.height() - options.fontMetrics.height()) // 2
        margin = margin - the_constant
        textRect.setTop(textRect.top() + margin)

        painter.translate(textRect.topLeft())
        painter.setClipRect(textRect.translated(-textRect.topLeft()))
        self.doc.documentLayout().draw(painter, ctx)

        painter.restore()


class Widget(QtWidgets.QWidget):
    def __init__(self, parent=None):
        super(Widget, self).__init__(parent)
        hlay = QtWidgets.QHBoxLayout()
        lay = QtWidgets.QVBoxLayout(self)
        self.table = QtWidgets.QTableWidget(5, 5)
        lay.addLayout(hlay)
        lay.addWidget(self.table)



        self.table.setItemDelegate(HTMLDelegate(self.table))

        for i in range(self.table.rowCount()):
            for j in range(self.table.columnCount()):
                it = QtWidgets.QTableWidgetItem(random.choice(words_1))
                self.table.setItem(i, j, it)


        text_list = ['ello', 'ack']
        # clear
        allitems = self.table.findItems("", QtCore.Qt.MatchContains)

        selected_items =[]
        for words in text_list:
            for item in allitems:
                selected_items = self.table.findItems(words, QtCore.Qt.MatchContains)
                selected_items.append(self.table.findItems(words, QtCore.Qt.MatchContains)) ## i tried to make a list which is beeing appened but using this list it returns only the same as the input

                item.setData(QtCore.Qt.UserRole, words if item in selected_items else None)




if __name__ == '__main__':
    import sys
    app = QtWidgets.QApplication(sys.argv)
    w = Widget()
    w.show()
    sys.exit(app.exec_())

【问题讨论】:

    标签: python pyqt pyqt5 qtablewidget


    【解决方案1】:

    在前一种情况下,我想过滤这些情况,以免不必要地进行绘制,但在这种情况下,因为它更复杂,我决定使用 QTextCharFormat 而不是 HTML 来实现高亮逻辑,如下所示:

    from PyQt5 import QtCore, QtGui, QtWidgets
    import random
    
    words = ["Hello dseerfd", "world sdfsdf sdfgsdf sdfsdf", "Stack dasdf", "Overflow", "Hello world", """<font color="red">Hello world</font>"""]
    
    class HighlightDelegate(QtWidgets.QStyledItemDelegate):
        def __init__(self, parent=None):
            super(HighlightDelegate, self).__init__(parent)
            self.doc = QtGui.QTextDocument(self)
            self._filters = []
    
        def paint(self, painter, option, index):
            painter.save()
            options = QtWidgets.QStyleOptionViewItem(option)
            self.initStyleOption(options, index)
            self.doc.setPlainText(options.text)
            self.apply_highlight()
            options.text = ""
            style = QtWidgets.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)
    
            if index.column() != 0:
                textRect.adjust(5, 0, 0, 0)
    
            the_constant = 4
            margin = (option.rect.height() - options.fontMetrics.height()) // 2
            margin = margin - the_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 apply_highlight(self):
            cursor = QtGui.QTextCursor(self.doc)
            cursor.beginEditBlock()
            fmt = QtGui.QTextCharFormat()
            fmt.setForeground(QtCore.Qt.red)
            for f in self.filters():
                highlightCursor = QtGui.QTextCursor(self.doc)
                while not highlightCursor.isNull() and not highlightCursor.atEnd():
                    highlightCursor = self.doc.find(f, highlightCursor)
                    if not highlightCursor.isNull():
                        highlightCursor.mergeCharFormat(fmt)
            cursor.endEditBlock()
    
        @QtCore.pyqtSlot(list)
        def setFilters(self, filters):
            if self._filters == filters: return
            self._filters = filters
    
        def filters(self):
            return self._filters
    
    class Widget(QtWidgets.QWidget):
        def __init__(self, parent=None):
            super(Widget, self).__init__(parent)
    
            self.table = QtWidgets.QTableWidget(30, 6)
            self._delegate = HighlightDelegate(self.table)
            self.table.setItemDelegate(self._delegate)
            for i in range(self.table.rowCount()):
                for j in range(self.table.columnCount()):
                    it = QtWidgets.QTableWidgetItem(random.choice(words))
                    self.table.setItem(i, j, it)
            self.table.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents)
    
            le = QtWidgets.QLineEdit()
            le.textChanged.connect(self.on_textChanged)
            lay = QtWidgets.QVBoxLayout(self)
            lay.addWidget(le)
            lay.addWidget(self.table)
    
            le.setText("ello ack")
    
        @QtCore.pyqtSlot(str)
        def on_textChanged(self, text):
            self._delegate.setFilters(list(set(text.split())))
            self.table.viewport().update()
    
    if __name__ == '__main__':
        import sys
        app = QtWidgets.QApplication(sys.argv)
        w = Widget()
        w.showMaximized()
        sys.exit(app.exec_())
    

    【讨论】:

    • 你拯救了我的一天!但是有一个问题:我已经通过自定义 QStyledItemDelegate 将您的代码应用于我的 QTableView,但是样式表(我通过 QApplication.instance().setStyleSheet(...) 设置)不适用于表格项目(即颜色、背景颜色、边框...... . 都被忽略了)。是否可以告诉自定义委托仅突出显示匹配项,而不触及应用样式表中指定的任何样式选项?