[PyQt] [PYQT5]Highlight search results in qtablewidget(select and highlight that text or character not all of the row or column)

Maziar Parsijani maziar.parsijani at gmail.com
Tue Aug 21 06:38:21 BST 2018


Well of course I am not OK with the code that I sent.And as I said the font
and alignment are important to me.

On Mon, Aug 20, 2018 at 10:59 PM, Maurizio Berti <maurizio.berti at gmail.com>
wrote:

> In my opinion, if I may, it seems a bit too complex and elaborate than it
> could be, and seems to be even more prone to error; for instance, it's not
> clear why you init a new QStyleOptionViewItem, while you can just
> initStyleOption the one from the paint argument, and the use of
> PaintContext is missing important state cases (focus/active, enabled,
> hover, etc). But, if it works for you, that's fine :-)
>
> About the size, since you are not using character distinction
> (bold/italic), you could use QFontMetrics (that will not take kerning into
> account, obviously) with its width or boundingRect methods.
> If you are interested in width only, I suppose QTextDocument's textWidth
> should be fine also, but I actually never used for this kind of situations.
>
> If alignment is an issue, you should be able to fix those data with
> QStyleOption's displayAlignment, along with QWidget's layoutDirection and
> QLocale settings, but I suppose you already know that.
>
> Maurizio
>
> Il giorno lun 20 ago 2018 alle ore 18:58 Maziar Parsijani <
> maziar.parsijani at gmail.com> ha scritto:
>
>> Here I think Maurizio code will functioning like this :
>>
>> from PyQt5 import QtCore, QtGui, QtWidgets
>> import random
>> import html
>>
>> words = ["Hello", "world", "Stack", "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("orange")
>>         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)
>>
>>         thefuckyourshitup_constant = 4
>>         margin = (option.rect.height() - options.fontMetrics.height()) // 2
>>         margin = margin - thefuckyourshitup_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 sizeHint(self, option, index):
>>         return QSize(self.doc.idealWidth(), self.doc.size().height())
>>
>> class Widget(QtWidgets.QWidget):
>>     def __init__(self, parent=None):
>>         super(Widget, self).__init__(parent)
>>         hlay = QtWidgets.QHBoxLayout()
>>         lay = QtWidgets.QVBoxLayout(self)
>>
>>         self.le = QtWidgets.QLineEdit()
>>         self.button = QtWidgets.QPushButton("filter")
>>         self.table = QtWidgets.QTableWidget(5, 5)
>>         hlay.addWidget(self.le)
>>         hlay.addWidget(self.button)
>>         lay.addLayout(hlay)
>>         lay.addWidget(self.table)
>>         self.button.clicked.connect(self.find_items)
>>         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))
>>                 self.table.setItem(i, j, it)
>>
>>     def find_items(self):
>>         text = self.le.text()
>>         # clear
>>         allitems = self.table.findItems("", QtCore.Qt.MatchContains)
>>         selected_items = self.table.findItems(self.le.text(), QtCore.Qt.MatchContains)
>>         for item in allitems:
>>             item.setData(QtCore.Qt.UserRole, text 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_())
>>
>> But the main problem of this method as you said is changing in font size and aligning because my data's are Arabic and English.
>>
>>
>> On Mon, Aug 20, 2018 at 7:15 PM, Maurizio Berti <maurizio.berti at gmail.com
>> > wrote:
>>
>>> I'm not an expert on regular expressions, maybe there's a better and
>>> faster approach than the one I used in the example.
>>> I initially tried using re.sub, but works correctly only if you want to
>>> match case, as the substitution string is fixed.
>>> Anyway, it seems fast enough to me, as the viewport update() takes care
>>> of repainting only visible elements.
>>>
>>> Another (derivate) approach could be subclassing the model too (the
>>> data() method), adding a custom user role for the html filtered text that
>>> can be read by the delegate, maybe with some sort of caching in the model
>>> to speed up reading. It may be slightly faster and more "elegant" (the
>>> model takes care of the string match, not the delegate), and could be
>>> useful for further implementation.
>>>
>>> I forgot to say: my example obviously doesn't consider spacings, grid
>>> lines pen width and icon decoration.
>>>
>>> Maurizio
>>>
>>> Il giorno lun 20 ago 2018 alle ore 16:01 Maziar Parsijani <
>>> maziar.parsijani at gmail.com> ha scritto:
>>>
>>>> Really thanks to Maurizio
>>>> I will try this.I agree with you and was thinking if someone had a
>>>> solution with re library and regix could be better for me .
>>>>
>>>>
>>>> On Mon, Aug 20, 2018 at 6:09 PM, Maurizio Berti <
>>>> maurizio.berti at gmail.com> wrote:
>>>>
>>>>> I know you already found a solution, but I decided to give it a try
>>>>> anyway, just out of curiosity.
>>>>> I didn't like the idea of using another QWidget, which might involve
>>>>> QStyle issues, then I found out that it's possible to use QTextDocument.
>>>>> This is a simple implementation that just matches the text in all
>>>>> items (no actual search in the model).
>>>>>
>>>>>
>>>>> class MatchDelegate(QtWidgets.QStyledItemDelegate):
>>>>>     regex = re.compile('')
>>>>>     fakeIndex = QtCore.QModelIndex()
>>>>>
>>>>>     def paint(self, qp, option, index):
>>>>>         self.initStyleOption(option, index)
>>>>>         td = QtGui.QTextDocument()
>>>>>         if self.regex.pattern:
>>>>>             splitted = self.regex.split(option.text)
>>>>>             matches = iter(self.regex.findall(option.text))
>>>>>             text = ''
>>>>>             for words in splitted[:-1]:
>>>>>                 text += words + '<b>{}</b>'.format(matches.next())
>>>>>             text += splitted[-1]
>>>>>         else:
>>>>>             text = option.text
>>>>>         td.setHtml(text)
>>>>>         option.text = ''
>>>>>         # Using an invalid index avoids painting of the text
>>>>>         QtWidgets.QStyledItemDelegate.paint(self, qp, option,
>>>>> self.fakeIndex)
>>>>>         qp.save()
>>>>>         qp.translate(option.rect.topLeft())
>>>>>         td.drawContents(qp, QtCore.QRectF(0, 0, option.rect.width(),
>>>>> option.rect.height()))
>>>>>         qp.restore()
>>>>>
>>>>>
>>>>> class Widget(QtWidgets.QWidget):
>>>>>     def __init__(self):
>>>>>         [...]
>>>>>         self.delegate = MatchDelegate()
>>>>>         self.table.setItemDelegate(self.delegate)
>>>>>         self.search = QtWidgets.QLineEdit()
>>>>>         self.search.textChanged.connect(self.findText)
>>>>>         [...]
>>>>>
>>>>>     def findText(self, text):
>>>>>         self.delegate.regex = re.compile(text, re.I)
>>>>>         self.table.viewport().update()
>>>>>
>>>>>
>>>>> Of course it doesn't take the text size change into account whenever
>>>>> bold characters are in place, so the sizeHint is not perfect. Also, it
>>>>> might be possible to implement QFontMetrics' elidedText, again with some
>>>>> small issues about font size changes.
>>>>>
>>>>> Maurizio
>>>>>
>>>>>
>>>>> Il giorno lun 20 ago 2018 alle ore 11:31 Maziar Parsijani <
>>>>> maziar.parsijani at gmail.com> ha scritto:
>>>>>
>>>>>> Hi Denis Rouzaud
>>>>>>
>>>>>> This question was for 19days ago.But you are correct the best method
>>>>>> is html delegate and I did it but I couldn't fix that with my table and the
>>>>>> table was changed but it worked nice.
>>>>>>
>>>>>> On Mon, Aug 20, 2018 at 12:28 PM, Denis Rouzaud <
>>>>>> denis.rouzaud at gmail.com> wrote:
>>>>>>
>>>>>>> This is not easily done.
>>>>>>> You'd have to create a custom delegate using a QLabbel and use html
>>>>>>> in there.
>>>>>>>
>>>>>>> I have been creating a search tool for tables and ending up
>>>>>>> highlighting the whole cell.
>>>>>>> The effort and the risk of bad results is just not worth the effort
>>>>>>> IMHO.
>>>>>>>
>>>>>>> Denis
>>>>>>>
>>>>>>> Le mar. 31 juil. 2018 à 23:05, Maziar Parsijani <
>>>>>>> maziar.parsijani at gmail.com> a écrit :
>>>>>>>
>>>>>>>> I use method1 to find some text in qtablewidget rows.
>>>>>>>>
>>>>>>>> method1 :
>>>>>>>>
>>>>>>>> def FindItem(self):
>>>>>>>>     items = self.SuraBRS.findItems(
>>>>>>>>         self.SearchTbox.text(), QtCore.Qt.MatchContains)
>>>>>>>>     if items:
>>>>>>>>         results = '\n'.join(
>>>>>>>>             'row %d column %d' % (item.row() + 1, item.column() + 1)
>>>>>>>>             for item in items)
>>>>>>>>     else:
>>>>>>>>         results = 'Found Nothing'
>>>>>>>>     print(results)
>>>>>>>>
>>>>>>>> Now I want to know how to highlight results or change their color.*I
>>>>>>>> want to select and highlight that text or character not all of the row or
>>>>>>>> column*.
>>>>>>>> _______________________________________________
>>>>>>>> PyQt mailing list    PyQt at riverbankcomputing.com
>>>>>>>> https://www.riverbankcomputing.com/mailman/listinfo/pyqt
>>>>>>>
>>>>>>> --
>>>>>>>
>>>>>>> Denis Rouzaud
>>>>>>> denis at opengis.ch  <denis at opengis.ch>
>>>>>>> +41 76 370 21 22
>>>>>>>
>>>>>>>
>>>>>>>
>>>>>> _______________________________________________
>>>>>> PyQt mailing list    PyQt at riverbankcomputing.com
>>>>>> https://www.riverbankcomputing.com/mailman/listinfo/pyqt
>>>>>
>>>>>
>>>>>
>>>>> --
>>>>> È difficile avere una convinzione precisa quando si parla delle
>>>>> ragioni del cuore. - "Sostiene Pereira", Antonio Tabucchi
>>>>> http://www.jidesk.net
>>>>>
>>>>
>>>>
>>>
>>> --
>>> È difficile avere una convinzione precisa quando si parla delle ragioni
>>> del cuore. - "Sostiene Pereira", Antonio Tabucchi
>>> http://www.jidesk.net
>>>
>>
>>
>
> --
> È difficile avere una convinzione precisa quando si parla delle ragioni
> del cuore. - "Sostiene Pereira", Antonio Tabucchi
> http://www.jidesk.net
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://www.riverbankcomputing.com/pipermail/pyqt/attachments/20180821/21c2447d/attachment-0001.html>


More information about the PyQt mailing list