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: 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 -
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 widgetsHere'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:
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 foldingHere's a little preview: