from PySide.QtWidgets import QWidget, QPlainTextEdit from PySide.QtGui import QPainter, QFont from PySide.QtCore import Qt, QRect, QSize class LineNumberArea(QWidget): def __init__(self, editor): super().__init__(editor) self.editor = editor def sizeHint(self): return QSize(self.editor.fontMetrics().horizontalAdvance("999") + 4, 0) def paintEvent(self, event): painter = QPainter(self) painter.fillRect(event.rect(), Qt.black) font = QFont() font.setFamily(self.editor.font().family()) font.setFixedPitch(self.editor.font().fixedPitch()) font.setPointSize(self.editor.font().pointSize()) painter.setFont(font) block = self.editor.firstVisibleBlock() blockNumber = block.blockNumber() top = self.editor.blockBoundingGeometry(block).translated(self.editor.contentOffset()).top() bottom = top + self.editor.blockBoundingRect(block).height() while block.isValid() and top <= event.rect().bottom(): if block.isVisible() and bottom >= event.rect().top(): number = str(blockNumber + 1) painter.setPen(Qt.gray) painter.drawText( -6, int(top), self.width(), self.editor.fontMetrics().height(), Qt.AlignRight, number, ) block = block.next() top = bottom bottom = top + self.editor.blockBoundingRect(block).height() blockNumber += 1 class CodeEditor(QPlainTextEdit): def __init__(self, parent=None): super().__init__(parent) self.lineNumberArea = LineNumberArea(self) self.blockCountChanged.connect(self.updateLineNumberAreaWidth) self.updateRequest.connect(self.updateLineNumberArea) self.verticalScrollBar().valueChanged.connect(self.lineNumberArea.update) # Simple update self.updateLineNumberAreaWidth(0) # Initial call def lineNumberAreaWidth(self): digits = 2 max_value = max(1, self.blockCount()) while max_value >= 10: max_value /= 10 digits += 1 space = 3 + self.fontMetrics().horizontalAdvance("9") * digits return space def updateLineNumberAreaWidth(self, newBlockCount): self.setViewportMargins(self.lineNumberAreaWidth(), 0, 0, 0) def updateLineNumberArea(self, rect, dy): if dy: self.lineNumberArea.scroll(0, dy) else: self.lineNumberArea.update(0, rect.y(), self.lineNumberArea.width(), rect.height()) if rect.contains(self.viewport().rect()): self.updateLineNumberAreaWidth(0) def resizeEvent(self, event): super().resizeEvent(event) cr = self.contentsRect() self.lineNumberArea.setGeometry( QRect(cr.left(), cr.top(), self.lineNumberAreaWidth(), cr.height()) ) def setText(self, text): """Backward compatibility method for setText() calls.""" self.setPlainText(text)