Python Forum
[PyQt] Determining the format attributes on text in a QTextEdit object
Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
[PyQt] Determining the format attributes on text in a QTextEdit object
#1
Hi,

The following function is used to highlight text for a spell checker. If it finds a misspelt word it will underline it in red.

    def highlightBlock(self, text: str) -> None:
        if not hasattr(self, "speller"):
            return

        # The character format of text in a document specifies the visual properties of the text, as well as information about its role in a hypertext document.
        self.misspelledFormat = QTextCharFormat()
        self.misspelledFormat.setUnderlineStyle(
            QTextCharFormat.SpellCheckUnderline)  # we can set its visual style
        self.misspelledFormat.setUnderlineColor(Qt.red)  # red and underlined

        # we iterate the text using the regular expression above which identifies word boundaries
        for word_object in self.wordRegEx.finditer(text):
            # we check to see if this is a recognised word
            if not self.speller.check(word_object.group()):
                self.setFormat(    # if it is not we underline it using the style shown above
                    word_object.start(),  # index of first letter of match
                    # index of last letter - index of first letter= length
                    word_object.end() - word_object.start(),
                    self.misspelledFormat,
                )
I am trying to figure out how I might do the opposite. For example:
Given a block of text in a QTextEdit object is there some way of determining the format attributes of that text. It is almost the opposite of QSyntaxHighlighter.setFormat() method. I want to be able to iterate through the block and find the underlined words or words highlighted in blue etc..
Reply
#2
Formatting text is done by inserting Markdown format tags in the text. You can get the text with the html tags using the QTextEdit.html attribute (assuming this is a QTextEdit object). This returns a str that you can parse to find all the formatting tags.
Reply
#3
If a "normal" selection would suffice, QTextEdit has a built-in find function.
Reply
#4
Thank you for your replies.

I had thought about the HTML approach, but I thought there had to be a more direct way of finding the format attributes. I found a post that suggested that using the HTML approach could be problematic because the format could be set outside the selection (i.e. stylesheets), and that the proper way to verify the format at a specified position is by getting the QTextCharFormat of the text cursor for a given character position.

    def check_formatting(self):
        # assume no format by default
        bold = italic = underline = False
        textCursor = self.textCursor()
        rangeStart = textCursor.selectionStart()
        if textCursor.hasSelection():
            rangeEnd = textCursor.selectionEnd() + 1
        else:
            rangeEnd = textCursor.selectionStart() + 1
        wordToCheck = textCursor.selectedText()
        for pos in range(rangeStart, rangeEnd):
            textCursor.setPosition(pos)
            fmt = textCursor.charFormat()

            underline = fmt.fontUnderline()
            colour = fmt.underlineColor()
            if fmt.fontWeight() >= QFont.Bold:
                bold = True
            if fmt.fontItalic():
                italic = True
            if fmt.fontUnderline():
                underline = True
            if all((bold, italic, underline)):
                # all formats are set, no need for further checking
                break
This code was an attempt to do that, but when I run it I find that

            underline = fmt.fontUnderline()
            colour = fmt.underlineColor()
underline is False and the Colour is #000000, even though the selected text, in this case, is underlined in red. I expected to see that reflected in underline and colour.
Reply
#5
This is a way to underline found text.

Press Return in the find field.

from PyQt5.QtCore import (QSize)
from PyQt5.QtGui import QIcon, QKeySequence, QTextCursor, QTextCharFormat
from PyQt5.QtWidgets import (QAction, QApplication, QMainWindow, 
                             QTextEdit, QLineEdit)
from sys import argv, exit


class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.setGeometry(50, 50, 800, 600)
        self.windowList = []
        self.myeditor = QTextEdit(""" Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.""")
        self.myeditor.setAcceptRichText(False)
        self.myeditor.setUndoRedoEnabled(True)

        self.createActions()
        self.createToolBars()

        self.setWindowIcon(QIcon.fromTheme("accessories-text-editor"))

        self.setCentralWidget(self.myeditor)
        self.findfield.setFocus()

    def createToolBars(self):
        self.findToolBar = self.addToolBar("Suchen")
        self.findToolBar.setIconSize(QSize(16, 16))
        self.findfield = QLineEdit()
        self.findfield.addAction(QIcon.fromTheme("edit-find"), 0)
        self.findfield.setClearButtonEnabled(True)
        self.findfield.setFixedWidth(200)
        self.findfield.setPlaceholderText("suchen")
        self.findfield.setStatusTip("press RETURN")
        self.findfield.setText("ipsum")
        self.findfield.returnPressed.connect(self.findText)
        self.findToolBar.addWidget(self.findfield)

    def createActions(self):        
        self.findAct = QAction(QIcon.fromTheme('edit-find'), "find", self,
                shortcut=QKeySequence.Find, statusTip="find",
                triggered=self.setFindText)
                        
        
    def setFindText(self):
        self.findfield.setText(self.myeditor.textCursor().selectedText())
        self.findText()

    def findText(self):
        fmt = QTextCharFormat()
        fmt.setFontUnderline(False)
        self.myeditor.selectAll()
        self.myeditor.textCursor().mergeCharFormat(fmt)
        self.myeditor.moveCursor (QTextCursor.Start)
        fmt.setFontUnderline(True)
        word = self.findfield.text()
        while self.myeditor.find(word):
            textcursor = self.myeditor.textCursor()
            textcursor.select(QTextCursor.WordUnderCursor)
            textcursor.mergeCharFormat(fmt)
            self.myeditor.setTextCursor(textcursor)

            
if __name__ == '__main__':
    app = QApplication(argv)
    mainWin = MainWindow()
    mainWin.show()
    exit(app.exec_())
Reply
#6
Thank you @Axel_Erfurt, but your code shows how one can find a particular word in a block of text and then underline it. I am trying to do the opposite. I am trying to scan through the existing text in a QTextEdit widget and find all words that have already been underlined.
Reply
#7
Quote:I had thought about the HTML approach, but I thought there had to be a more direct way of finding the format attributes. I found a post that suggested that using the HTML approach could be problematic because the format could be set outside the selection (i.e. stylesheets), and that the proper way to verify the format at a specified position is by getting the QTextCharFormat of the text cursor for a given character position
Does it matter what the formatting is? You are looking for isolated sections of text that has special formatting. This test will be surrounded by tags, in particular a tag to underline the text. Just search for tags that turn underlining on and off.
Reply
#8
Thanks @deanhystad
I have reflected on the approach I am taking and the problem I am trying to solve and believe there is probably a more elegant way of doing what I am trying to do. My original idea was to use the formating information to derive the significance of certain text in the editor, for example: if a word was underlined in red it would denote that it was misspelt, but if it was underlined in blue it would indicate that it was grammatically wrong.
I do not think I should be using the syntax characters attributes as the identifier of certain properties pertaining to a word or a piece of text. I think instead I should be using metadata in the document itself, perhaps with something like QTextBlockUserData. So, I have begun to look in that direction now.
I appreciate you taking the time to answer my question though; it was helpful in making me reflect on what I wanted to do.
Reply


Possibly Related Threads…
Thread Author Replies Views Last Post
  [PyQt] AttributeError: 'NoneType' object has no attribute 'text' speedev 9 11,236 Sep-25-2021, 06:14 PM
Last Post: Axel_Erfurt
  [Tkinter] Glow text of clickable object on hover with transition andy 6 5,913 May-11-2021, 07:39 AM
Last Post: andy

Forum Jump:

User Panel Messages

Announcements
Announcement #1 8/1/2020
Announcement #2 8/2/2020
Announcement #3 8/6/2020