Results 1 to 4 of 4

Thread: Apply the outline of a QTextCharFormat only on the outside of the text

  1. #1
    Join Date
    Apr 2021
    Posts
    4
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default Apply the outline of a QTextCharFormat only on the outside of the text

    Hello everyone,

    I have been using Qt for a personal project and I am so far pleasantly surprised how powerful it is.

    One issue I am still struggling with despite several hours of digging is the following. I want to write text inside a QTextEdit with an outline around the text. I manage to do it with the following code, however there is an apparent problem (see the picture below): the outline is drawn not only outside the text, but also inside.

    Qt Code:
    1. from PyQt5.QtWidgets import *
    2. import sys
    3. from PyQt5.QtCore import Qt
    4. from PyQt5.QtGui import QPen, QFont
    5. from PyQt5.QtGui import QTextCursor
    6.  
    7. class MyTextEdit(QTextEdit):
    8. def __init__(self):
    9. super().__init__()
    10.  
    11. self.resize(800, 400)
    12.  
    13. font = self.currentFont()
    14. font.setPointSize(80)
    15. font.setStyleStrategy(QFont.PreferAntialias)
    16. self.setFont(font)
    17.  
    18. my_cursor = self.textCursor()
    19. my_cursor.movePosition(QTextCursor.Start)
    20.  
    21. my_char_format = my_cursor.charFormat()
    22. my_cursor.setCharFormat(my_char_format)
    23.  
    24. my_cursor.insertText("Text example", my_char_format)
    25.  
    26. outline_format = my_char_format
    27. outline_format.setTextOutline(QPen(Qt.red, 5))
    28.  
    29. my_cursor.setCharFormat(outline_format)
    30. my_cursor.insertBlock()
    31. my_cursor.insertText("Text example")
    32.  
    33.  
    34. if __name__ == '__main__':
    35. app = QApplication([sys.argv])
    36. win = MyTextEdit()
    37.  
    38. win.show()
    39.  
    40. app.exec_()
    To copy to clipboard, switch view to plain text mode 

    Result:


    This problem has already been encountered, for example here: https://stackoverflow.com/questions/...-thinning-font and here https://stackoverflow.com/questions/...inside-outside .

    However, I do not understand the answer given using the print() method, which is not available for QTextEdit, although it is available for QGraphicsTextItem.

    I have a hard constraint that the text should still be selectable, and nicely selectable. I do not need the text to be editable.

    I wonder if, for example, overriding the paintEvent function would allow me to do more advanced text painting (for example, writing first the text with outline, and then overriding with the text without outline but keeping the first painting under it), but still having the text selectable.

    Thanks a lot for your help, I will still be thinking meanwhile and post if I find a solution :)

    Edit 2:

    I think I got it. I should implement a class inheriting from QTextDocument, and overwrite its print() method as detailed in the Stackoverflow answer.

    Then, for my custom QTextEdit in my init, I should use setDocument() with my custom QTextDocument class where print() is overwritten.

    See https://doc.qt.io/qt-5/qtextedit.html#document-prop

    I will try that later and let you know if this works.

    Edit:

    Here is an example where the outline gets indeed only outside the text, however the text is not selectable anymore:
    Qt Code:
    1. from PyQt5.QtWidgets import *
    2. import sys
    3. from PyQt5 import QtGui
    4. from PyQt5.QtCore import Qt
    5. from PyQt5.QtGui import QColor, QPainter, QPainterPath, QBrush, QPen, QPalette, QFont
    6.  
    7. class MyTextEdit(QTextEdit):
    8. def __init__(self, parent = None):
    9. super(MyTextEdit, self).__init__(parent)
    10.  
    11. self.resize(800, 400)
    12.  
    13. def paintEvent(self, event):
    14. painter = QPainter(self.viewport())
    15. painter.setRenderHint(QPainter.Antialiasing);
    16.  
    17. painter.drawLine(10, 10, 200, 10) # just for testing purposes
    18.  
    19. #font = self.font()
    20. font = QFont("Arial", 72, 50, False);
    21.  
    22. text = "Text example"
    23. text_path = QPainterPath()
    24.  
    25. text_path.addText(0, 100, font, text)
    26.  
    27. painter.setFont(font)
    28.  
    29. # draw outline
    30. painter.setPen(outline_pen)
    31. painter.drawPath(text_path)
    32.  
    33. # draw text
    34. color = self.palette().color(QPalette.Text)
    35. painter.setPen(color)
    36. painter.drawText(0, 100, text)
    37.  
    38. super(MyTextEdit, self).paintEvent(event)
    39.  
    40. if __name__ == '__main__':
    41. app = QApplication([sys.argv])
    42.  
    43. outline_color = QColor(200, 0, 0, 180)
    44. outline_brush = QBrush(outline_color, Qt.SolidPattern)
    45. outline_pen = QPen(outline_brush, 15, Qt.SolidLine, Qt.RoundCap, Qt.RoundJoin)
    46.  
    47. win = MyTextEdit()
    48.  
    49. win.show()
    50.  
    51. app.exec_()
    To copy to clipboard, switch view to plain text mode 
    Last edited by Sylve; 4th April 2021 at 22:09.

  2. #2
    Join Date
    Apr 2021
    Posts
    4
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default Re: Apply the outline of a QTextCharFormat only on the outside of the text

    Unfortunately I think my second edit can not work, I have mistaken print() and paint() functions. QTextDocument has no such paint().
    Last edited by Sylve; 5th April 2021 at 14:18.

  3. #3
    Join Date
    Apr 2021
    Posts
    4
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default Re: Apply the outline of a QTextCharFormat only on the outside of the text

    I post my solution (in the end heavily based on the Stackoverflow one), just in case it helps anybody. Note I used Qt 6 intead of 5, but it should not matter.

    Qt Code:
    1. from PyQt6.QtWidgets import *
    2. import sys
    3. from PyQt6 import QtGui
    4. from PyQt6.QtCore import Qt
    5. from PyQt6.QtGui import QTextDocument, QTextCursor
    6. from PyQt6.QtGui import QColor, QPainter, QPainterPath, QBrush, QPen, QPalette, QFont
    7.  
    8. class MyTextDocument(QTextDocument):
    9. def __init__(self, parent, font):
    10. super().__init__()
    11.  
    12. self.parent = parent
    13.  
    14. self.setDefaultFont(font)
    15.  
    16. def drawContents(self, painter):
    17.  
    18. super().drawContents(painter)
    19.  
    20. my_cursor = self.parent.textCursor()
    21. my_char_format = my_cursor.charFormat()
    22.  
    23. my_char_format.setTextOutline(QPen(Qt.GlobalColor.red, 5))
    24. my_cursor.select(QTextCursor.SelectionType.Document)
    25. my_cursor.mergeCharFormat(my_char_format)
    26.  
    27. super().drawContents(painter)
    28.  
    29. my_char_format.setTextOutline(QPen(Qt.GlobalColor.transparent))
    30. my_cursor.mergeCharFormat(my_char_format)
    31.  
    32. super().drawContents(painter)
    33.  
    34.  
    35. class MyTextEdit(QTextEdit):
    36. def __init__(self):
    37. super().__init__()
    38.  
    39. font = self.currentFont()
    40. font.setPointSize(80)
    41. font.setStyleStrategy(QFont.StyleStrategy.PreferAntialias)
    42.  
    43. self.setDocument(MyTextDocument(self, font))
    44.  
    45. self.setText("Does it work?")
    46. self.resize(800, 400)
    47.  
    48. def paintEvent(self, event):
    49. painter = QPainter(self.viewport())
    50.  
    51. super(MyTextEdit, self).paintEvent(event)
    52.  
    53. self.document().drawContents(painter)
    54.  
    55.  
    56. if __name__ == '__main__':
    57. app = QApplication(sys.argv)
    58.  
    59. win = MyTextEdit()
    60. win.show()
    61.  
    62. app.exec_()
    To copy to clipboard, switch view to plain text mode 

    Result:



    Feel free to post if you have something better in mind / any comment!

  4. #4
    Join Date
    Apr 2021
    Posts
    4
    Qt products
    Qt5
    Platforms
    Unix/X11

    Default Re: Apply the outline of a QTextCharFormat only on the outside of the text

    I found a very concerning issue with this solution: the line

    Qt Code:
    1. my_cursor.select(QTextCursor.SelectionType.Document)
    To copy to clipboard, switch view to plain text mode 

    calls recursively paintEvent in a infinite loop. I should find a way to disable recursive calls in paintEvent...

    Try out this code to see the issue:

    Qt Code:
    1. from PyQt5.QtWidgets import *
    2. import sys
    3. from PyQt5.QtCore import Qt
    4. from PyQt5.QtGui import QTextDocument, QTextCursor
    5. from PyQt5.QtGui import QPainter, QPen, QFont
    6.  
    7. style_subs = '''
    8. /* looks of subtitles */
    9. QFrame {
    10. background: green;
    11. color: white;
    12. font-family: "Noto Serif CJK JP Light";
    13. border: 0;
    14. }
    15. '''
    16.  
    17. class MyTextEdit(QTextEdit):
    18. def __init__(self):
    19. super().__init__()
    20.  
    21. self.setStyleSheet(style_subs)
    22.  
    23. font = self.currentFont()
    24. font.setPointSize(80)
    25. font.setStyleStrategy(QFont.PreferAntialias)
    26. self.setFont(font)
    27.  
    28. self.setText("Does it work?")
    29.  
    30. self.resize(800, 400)
    31.  
    32. self.n = 0
    33.  
    34. def paintEvent(self, event):
    35. print("painting", self.n, "...")
    36. self.n += 1
    37. painter = QPainter(self.viewport())
    38.  
    39. my_cursor = self.textCursor()
    40. my_char_format = my_cursor.charFormat()
    41.  
    42. my_char_format.setTextOutline(QPen(Qt.red, 10))
    43. my_cursor.select(QTextCursor.SelectionType.Document)
    44. my_cursor.mergeCharFormat(my_char_format)
    45.  
    46. self.document().drawContents(painter)
    47.  
    48. my_char_format.setTextOutline(QPen(Qt.transparent))
    49. my_cursor.mergeCharFormat(my_char_format)
    50.  
    51.  
    52. super(MyTextEdit, self).paintEvent(event)
    53.  
    54.  
    55. if __name__ == '__main__':
    56. app = QApplication(sys.argv)
    57.  
    58. win = MyTextEdit()
    59. win.show()
    60.  
    61. app.exec_()
    To copy to clipboard, switch view to plain text mode 
    Last edited by Sylve; 5th April 2021 at 19:35.

Similar Threads

  1. QTextCharFormat question
    By moez in forum KDE Forum
    Replies: 1
    Last Post: 1st February 2012, 10:14
  2. Replies: 4
    Last Post: 5th August 2011, 21:04
  3. QPushButton: Outline white text in black on transparent background
    By Tito in forum Qt for Embedded and Mobile
    Replies: 3
    Last Post: 20th July 2011, 08:16
  4. QTextCharFormat and Hyperlinks
    By Rayven in forum Qt Programming
    Replies: 0
    Last Post: 8th January 2010, 22:12
  5. Replies: 1
    Last Post: 27th November 2007, 11:10

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  
Digia, Qt and their respective logos are trademarks of Digia Plc in Finland and/or other countries worldwide.