when pressing the search button, I would like to search in all items (aka cells) of a QTreeView and color all cells matching the searched text cells via a CSS style.
Is this possible?
Code currently (full working example):
#!/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_())
See method on_pbSearch_clicked
-> here all the matched rows are flagged as selected. Instead I would like to color the cells via a CSS style.
Answer
Yes, it's possible to color the cells matching the search query using CSS styles in PyQt. To do this, you can use the QTreeView
's setStyleSheet
method in combination with a search function that will find all matching cells, then apply a CSS style to those matching cells.
The QTreeView
uses the model-view architecture, so to apply the CSS styles to individual cells, you can modify the QStandardItem
's setData
method to include custom styles for the cells that match the search query.
Steps to achieve the desired behavior:
-
Search all cells in the model:
- Use the same method you are using (
match
) to identify cells that match the search text.
- Use the same method you are using (
-
Apply custom style to matching cells:
- Once you have the matching cells, you can apply a CSS style (for example, a background color) to those cells.
-
Use
setData
withQtCore.Qt.BackgroundRole
to apply the CSS style.
Here's the updated code to highlight matching cells with a CSS style instead of selecting them:
Updated Code with CSS Styling for Search Results
#!/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()
# Reset background color of all cells before searching
model = self.dataView.model()
for row in range(model.rowCount()):
for col in range(model.columnCount()):
item = model.item(row, col)
item.setData(QtCore.Qt.GlobalColor.white, QtCore.Qt.BackgroundRole)
if text:
# Iterate over all rows and columns
for row in range(model.rowCount()):
for col in range(model.columnCount()):
index = model.index(row, col)
item = model.itemFromIndex(index)
# If the item contains the search text, highlight it
if text.lower() in str(item.text()).lower():
item.setData(QtGui.QColor(255, 255, 0), QtCore.Qt.BackgroundRole) # Yellow background
else:
self.dataView.clearSelection()
if __name__ == "__main__":
app = QtWidgets.QApplication(sys.argv)
ex = App()
ex.show()
sys.exit(app.exec_())
Key Changes:
-
Reset Background Color: Before searching, the background color of all cells is reset to white to ensure that any previously highlighted cells are cleared.
# Reset background color of all cells before searching model = self.dataView.model() for row in range(model.rowCount()): for col in range(model.columnCount()): item = model.item(row, col) item.setData(QtCore.Qt.GlobalColor.white, QtCore.Qt.BackgroundRole)
-
Search and Highlight Cells: For each cell in the model, the search text is checked. If it matches, the background color of that cell is set to yellow (
QColor(255, 255, 0)
).if text: # Iterate over all rows and columns for row in range(model.rowCount()): for col in range(model.columnCount()): index = model.index(row, col) item = model.itemFromIndex(index) # If the item contains the search text, highlight it if text.lower() in str(item.text()).lower(): item.setData(QtGui.QColor(255, 255, 0), QtCore.Qt.BackgroundRole) # Yellow background
-
CSS Styling for Highlighting: This is done using the
setData
method withQtCore.Qt.BackgroundRole
, which allows you to apply custom background color to cells that match the search text.
Result:
- All cells containing the search text will be highlighted with a yellow background.
- You can easily change the background color to any other color by adjusting the
QColor
values.
This solution should give you the behavior you want: highlighting cells with a CSS-like style instead of selecting the rows.