Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Adding clickable widgets into a qplaintextedit



  • So basically let's say you have followed this guide: https://doc.qt.io/qt-5/qtwidgets-widgets-codeeditor-example.html

    And you modify the width to be a bit bigger: 0_1566302222616_12afce80-1ace-4e6e-b0e3-6325682fadef-Screenshot 2019-08-20 at 14.56.46.png Screenshot 2019-08-20 at 14.56.46

    I want to add a little arrow next to line number 1, like QtCreator has, to be clickable so I could fold the text that is on line 2
    But I have no idea where to start, how can I add a little widget next to the line number



  • I'm currently trying to find out how QtCreator does it, if anyone knows, please let me know!


  • Lifetime Qt Champion

    Hi,

    I would start by looking at the TextEditor plugin and the CppEditor plugin.



  • Alright so I think the best way to proceed is to have each of the numbers on the number bar clickable
    so instead of painting them, they should be widgets

    Here's a little Number class:

    class Number(QFrame):
        def __init__(self, number):
            super().__init__()
            self.num = QLabel(str(number), self)
            self.layout = QVBoxLayout()
            self.layout.addWidget(self.num)
            self.layout.addStretch()
            self.setLayout(self.layout)
            self.setStyleSheet("background-color: red; color: black")
            self.setContentsMargins(0,0,0,0)
    

    But there is still some annoying spacing around the widget:0_1566316340588_26f80f26-ce8c-4994-a4e1-05c0b3eca274-Screenshot 2019-08-20 at 18.52.05.png
    How do I remove that?



  • Well after digging, and I mean A LOT of digging I found I could use QStyle to draw those little arrows
    and since QStyleOptionViewItem has states, I can choose how the arrow is pointing.

    This is a little example of code:

        def paintEvent(self, event: QPaintEvent):
    
            if self.isVisible():
                block: QTextBlock = self.editor.firstVisibleBlock()
                height: int = self.fontMetrics().height()
                number: int = block.blockNumber()
    
                painter = QPainter(self)
                painter.fillRect(event.rect(), QColor(53, 53, 53))
                # painter.drawRect(0, 0, event.rect().width() - 1, event.rect().height() - 1)
                font = painter.font()
                font.setPointSize(15)
                currentBlockNumber: int = self.editor.textCursor().block().blockNumber() + 1
    
                while block.isValid():
                    blockGeometry: QRectF = self.editor.blockBoundingGeometry(block)
                    offset: QPointF = self.editor.contentOffset()
                    blockTop: int = int(blockGeometry.translated(offset).top() + 1)
                    number += 1
    
                    if number == currentBlockNumber:
                        font.setBold(True)
                    else:
                        font.setBold(False)
                    painter.setFont(font)
                    rect: QRect = QRect(0, blockTop, self.width() - 5, height)
                    painter.drawText(rect, Qt.AlignCenter, str(number))
                    if number in self.lines:
                        options = QStyleOptionViewItem()
                        options.rect = QRect(0, blockTop, self.width() + 45, height)
                        options.state = (QStyle.State_Active |
                                     QStyle.State_Item |
                                     QStyle.State_Children |
                                     QStyle.State_Open)
                        self.style().drawPrimitive(QStyle.PE_IndicatorBranch, options,
                                                   painter, self)
                        print("drawn")
                    if blockTop > event.rect().bottom():
                        break
    
                    block = block.next()
    
                painter.end()
    

    That is the whole paintEvent function and the magic happens here:

                    if number in self.lines:
                        options = QStyleOptionViewItem()
                        options.rect = QRect(0, blockTop, self.width() + 45, height)
                        options.state = (QStyle.State_Active |
                                     QStyle.State_Item |
                                     QStyle.State_Children |
                                     QStyle.State_Open)
                        self.style().drawPrimitive(QStyle.PE_IndicatorBranch, options,
                                                   painter, self)
                        print("drawn")
    

    when painting numbers I will check that if the current number is in a list of numbers where the user can fold at (this is done parsing a syntax tree, if anyone is interested in that please let me know!) and if it is in there then it will create a rect and draw a little arrow that points down (this is because QStyle.State_Open) is in the options.state variable
    if you remove that, the arrow points right. That will be useful when after folding

    Here's a little preview:
    0_1566332422283_ee3f8570-58e3-45a6-97df-4084ce47bc8e-Screenshot 2019-08-20 at 23.19.53.png


Log in to reply