如何遍历QTreeView并为所有匹配的单元格着色?

时间:2019-06-09 18:55:46

标签: python pyqt pyqt5

当按下搜索按钮时,我想在QTreeView的所有项目(也称为单元格)中进行搜索,并通过CSS样式为与搜索到的文本单元格匹配的所有单元格着色。

这可能吗?

当前代码(完整的工作示例):

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
from PyQt5 import QtCore, QtGui, QtWidgets


class App(QtWidgets.QWidget):
    MAIL_RANGE = 4
    ID, FROM, SUBJECT, DATE = range(MAIL_RANGE)

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setGeometry(10, 10, 640, 240)

        self.dataGroupBox = QtWidgets.QGroupBox("Inbox")
        self.dataView = QtWidgets.QTreeView(
            rootIsDecorated=False,
            alternatingRowColors=True,
            selectionMode=QtWidgets.QAbstractItemView.ExtendedSelection,
            editTriggers=QtWidgets.QAbstractItemView.NoEditTriggers,
            selectionBehavior=QtWidgets.QAbstractItemView.SelectRows,
        )

        dataLayout = QtWidgets.QHBoxLayout()
        dataLayout.addWidget(self.dataView)
        self.dataGroupBox.setLayout(dataLayout)

        model = App.createMailModel(self)
        self.dataView.setModel(model)

        for i in range(0, 2):
            self.dataView.resizeColumnToContents(i)

        self.addMail(model, 1, 'service@github.com', 'Your Github Donation','03/25/2017 02:05 PM')
        self.addMail(model, 2, 'support@github.com', 'Github Projects','02/02/2017 03:05 PM')
        self.addMail(model, 3, 'service@phone.com', 'Your Phone Bill','01/01/2017 04:05 PM')
        self.addMail(model, 4, 'service@abc.com', 'aaaYour Github Donation','03/25/2017 02:05 PM')
        self.addMail(model, 5, 'support@def.com', 'bbbGithub Projects','02/02/2017 03:05 PM')
        self.addMail(model, 6, 'service@xyz.com', 'cccYour Phone Bill','01/01/2017 04:05 PM')

        self.dataView.setColumnHidden(0, True)

        self.leSearch = QtWidgets.QLineEdit()
        self.pbSearch = QtWidgets.QPushButton(
            "Search", clicked=self.on_pbSearch_clicked
        )

        hlay = QtWidgets.QHBoxLayout()
        hlay.addWidget(self.leSearch)
        hlay.addWidget(self.pbSearch)

        mainLayout = QtWidgets.QVBoxLayout(self)
        mainLayout.addLayout(hlay)
        mainLayout.addWidget(self.dataGroupBox)

    @staticmethod
    def createMailModel(parent):
        model = QtGui.QStandardItemModel(0, App.MAIL_RANGE, parent)
        for c, text in zip(
            (App.ID, App.FROM, App.SUBJECT, App.DATE),
            ("ID", "From", "Subject", "Date"),
        ):
            model.setHeaderData(c, QtCore.Qt.Horizontal, text)
        return model

    def addMail(self, model, mailID, mailFrom, subject, date):
        model.insertRow(0)
        for c, text in zip(
            (App.ID, App.FROM, App.SUBJECT, App.DATE),
            (mailID, mailFrom, subject, date),
        ):
            model.setData(model.index(0, c), text)

    @QtCore.pyqtSlot()
    def on_pbSearch_clicked(self):
        text = self.leSearch.text()
        self.leSearch.clear()
        if text:
            start = self.dataView.model().index(0, 2)
            # find index
            ixs = self.dataView.model().match(
                start,
                QtCore.Qt.DisplayRole,
                text,
                flags=QtCore.Qt.MatchStartsWith,
            )
            if ixs:
                ix = ixs[0]
                # scroll to index
                self.dataView.scrollTo(ix)
                ix_from = ix.sibling(ix.row(), 0)
                ix_to = ix.sibling(
                    ix.row(), self.dataView.model().columnCount() - 1
                )
                # select row
                self.dataView.selectionModel().select(
                    QtCore.QItemSelection(ix_from, ix_to),
                    QtCore.QItemSelectionModel.SelectCurrent,
                )
        else:
            self.dataView.clearSelection()


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    ex = App()
    ex.show()
    sys.exit(app.exec_())

请参见方法on_pbSearch_clicked->在此,所有匹配的行都标记为已选择。相反,我想通过CSS样式为单元格上色。

1 个答案:

答案 0 :(得分:1)

您可以使用委托来设置背景和文本的颜色,并可以使用角色来指示应该绘制哪个单元格。

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import sys
from PyQt5 import QtCore, QtGui, QtWidgets


MatchRole = QtCore.Qt.UserRole + 1000


class HighlightDelegate(QtWidgets.QStyledItemDelegate):
    @property
    def background(self):
        if not hasattr(self, "_background"):
            self._background = QtGui.QBrush()
        return self._background

    @background.setter
    def background(self, brush):
        self._background = QtGui.QBrush(brush)

    @property
    def foreground(self):
        if not hasattr(self, "_foreground"):
            self._foreground = QtGui.QBrush()
        return self._foreground

    @foreground.setter
    def foreground(self, brush):
        self._foreground = QtGui.QBrush(brush)

    def initStyleOption(self, option, index):
        super(HighlightDelegate, self).initStyleOption(option, index)
        if index.data(MatchRole):
            if self.background != QtGui.QBrush():
                option.backgroundBrush = self.background
            if self.foreground != QtGui.QBrush():
                option.palette.setBrush(QtGui.QPalette.Text, self.foreground)


class App(QtWidgets.QWidget):
    MAIL_RANGE = 4
    ID, FROM, SUBJECT, DATE = range(MAIL_RANGE)

    def __init__(self):
        super().__init__()
        self.initUI()

    def initUI(self):
        self.setGeometry(10, 10, 640, 240)

        self.dataGroupBox = QtWidgets.QGroupBox("Inbox")
        self.dataView = QtWidgets.QTreeView(
            rootIsDecorated=False,
            alternatingRowColors=True,
            selectionMode=QtWidgets.QAbstractItemView.ExtendedSelection,
            editTriggers=QtWidgets.QAbstractItemView.NoEditTriggers,
            selectionBehavior=QtWidgets.QAbstractItemView.SelectRows,
        )

        delegate = HighlightDelegate(self.dataView)
        self.dataView.setItemDelegate(delegate)
        delegate.background = QtGui.QColor("gray")
        delegate.foreground = QtGui.QColor("salmon")
        self.dataView.viewport().update()

        dataLayout = QtWidgets.QHBoxLayout()
        dataLayout.addWidget(self.dataView)
        self.dataGroupBox.setLayout(dataLayout)

        model = App.createMailModel(self)
        self.dataView.setModel(model)

        for i in range(0, 2):
            self.dataView.resizeColumnToContents(i)

        self.addMail(model, 1, 'service@github.com', 'Your Github Donation','03/25/2017 02:05 PM')
        self.addMail(model, 2, 'support@github.com', 'Github Projects','02/02/2017 03:05 PM')
        self.addMail(model, 3, 'service@phone.com', 'Your Phone Bill','01/01/2017 04:05 PM')
        self.addMail(model, 4, 'service@abc.com', 'aaaYour Github Donation','03/25/2017 02:05 PM')
        self.addMail(model, 5, 'support@def.com', 'bbbGithub Projects','02/02/2017 03:05 PM')
        self.addMail(model, 6, 'service@xyz.com', 'cccYour Phone Bill','01/01/2017 04:05 PM')

        self.dataView.setColumnHidden(0, True)

        for i in range(self.dataView.model().columnCount()):
            self.dataView.header().setSectionResizeMode(i, QtWidgets.QHeaderView.Stretch)

        self.leSearch = QtWidgets.QLineEdit()
        self.pbSearch = QtWidgets.QPushButton(
            "Search", clicked=self.on_pbSearch_clicked
        )

        hlay = QtWidgets.QHBoxLayout()
        hlay.addWidget(self.leSearch)
        hlay.addWidget(self.pbSearch)

        mainLayout = QtWidgets.QVBoxLayout(self)
        mainLayout.addLayout(hlay)
        mainLayout.addWidget(self.dataGroupBox)

    @staticmethod
    def createMailModel(parent):
        model = QtGui.QStandardItemModel(0, App.MAIL_RANGE, parent)
        for c, text in zip(
            (App.ID, App.FROM, App.SUBJECT, App.DATE),
            ("ID", "From", "Subject", "Date"),
        ):
            model.setHeaderData(c, QtCore.Qt.Horizontal, text)
        return model

    def addMail(self, model, mailID, mailFrom, subject, date):
        model.insertRow(0)
        for c, text in zip(
            (App.ID, App.FROM, App.SUBJECT, App.DATE),
            (mailID, mailFrom, subject, date),
        ):
            model.setData(model.index(0, c), text)

    @QtCore.pyqtSlot()
    def on_pbSearch_clicked(self):
        text = self.leSearch.text()
        # self.leSearch.clear()
        model = self.dataView.model()
        # clear
        for column in range(model.columnCount()):
            for row in range(model.rowCount()):
                ix = model.index(row, column)
                model.setData(ix, False, MatchRole)

        if text:
            for column in range(model.columnCount()):
                start = self.dataView.model().index(0, column)
                ixs = self.dataView.model().match(
                    start,
                    QtCore.Qt.DisplayRole,
                    text,
                    hits=-1,
                    flags=QtCore.Qt.MatchContains,
                )
                for ix in ixs:
                    model.setData(ix, True, MatchRole)


if __name__ == "__main__":
    app = QtWidgets.QApplication(sys.argv)
    ex = App()
    ex.show()
    sys.exit(app.exec_())

enter image description here