1

Is there a way to draw control characters with or in a QTextEdit, similar to the way Sublime Text 3 and Notepad++ do.

Example Control Character Display

I am not sure if they are using a font that has these control characters in them or if they are drawing a rectangle with text in place of these characters.

eyllanesc
  • 235,170
  • 19
  • 170
  • 241
Fallrisk
  • 11
  • 2

1 Answers1

0

I had a slightly different problem a few weeks ago, i had to create a QLineEdit with a QCompleter and a View which displays the current match. Example QLineEdit with QListView and QStyledItemDelegate.

The simplest solution is to use a QStyledItemDelegate. There you have a custom QFontMetric which holds information about the Textstyle. You simply draw the text at the given coordinates with the given style information.

I think there is not the "right" way to draw it like Sublime Text 3, but with QFont, QFontMetrics and the right QRectF its a good start.

Some helpful links:

Get bounding boxes for each character

Example of QStyledItemDelegate

Example paint function of subclassed QStyleItemDelegate class:

    def paint(self, painter: QPainter, option: 'QStyleOptionViewItem', index: QModelIndex) -> None:
        """
        paint the text
        :param painter: painter to draw
        :param option: style options
        :param index: index with data
        :return: None
        """
        if not index.isValid():
            super(CompleterItemDelegate, self).paint(painter, option, index)
            return
        # self.initStyleOption(option, index) # we do not init the options because this draws the text

        painter.setFont(QApplication.font())

        # draw correct background
        style: QStyle = option.widget.style() if option.widget else QApplication.style()
        style.drawControl(QStyle.CE_ItemViewItem, option, painter, option.widget)

        # get current string
        txt = str(index.data(Qt.DisplayRole))

        if len(txt) == 0 or len(self._search) == 0:
            painter.drawText(option.rect, Qt.AlignVCenter, txt)
            painter.restore()
            return

        equal: int = 0
        for j in range(len(self._search)):
            c_s = self._search[j]
            for i in range(len(txt)):
                c = txt.lower()[i]
                if c == c_s and i == j:
                    equal += 1

        if self._color is None:
            self._color = option.palette.color(QPalette.ColorGroup.Active, QPalette.ColorRole.HighlightedText)

        # calculate and draw the selected text
        left_text = txt[:equal]
        right_text = txt[equal:]
        w1 = option.fontMetrics.horizontalAdvance(left_text)
        w2 = option.fontMetrics.horizontalAdvance(right_text)

        rect = option.rect
        h = option.fontMetrics.height()
        margins = option.widget.contentsMargins()

        p1 = rect.topLeft() + QPointF(margins.left(), (rect.height() - h) / 2)
        p2 = p1 + QPointF(w1, 0)
        rect1 = QRectF(p1, QSizeF(w1, h))
        rect2 = QRectF(p2, QSizeF(w2, h))

        painter.setPen(self._color)
        painter.drawText(rect1, left_text)
        painter.setPen(option.palette.color(QPalette.Text))
        painter.drawText(rect2, Qt.AlignVCenter, txt[equal:])
desertnaut
  • 57,590
  • 26
  • 140
  • 166
AdamGe09
  • 1
  • 1