text: 
  from PyQt5 import QtCore, QtGui, QtWidgets
  
  
  class QLineNumberArea(QtWidgets.QWidget):
      def __init__(self, editor):
          super().__init__(editor)
          self.codeEditor = editor
          
  
      def sizeHint(self):
          return QtCore.QSize(self.editor.lineNumberAreaWidth(), 0)
  
      def paintEvent(self, event):
          self.codeEditor.lineNumberAreaPaintEvent(event)
  
  
  class QCodeEditor(QtWidgets.QPlainTextEdit):
      def __init__(self, parent=None):
          super().__init__(parent)
          self.lineNumberArea = QLineNumberArea(self)
          self.blockCountChanged.connect(self.updateLineNumberAreaWidth)
          self.updateRequest.connect(self.updateLineNumberArea)
          self.cursorPositionChanged.connect(self.highlightCurrentLine)
          self.updateLineNumberAreaWidth(0)
  
      def lineNumberAreaWidth(self):
          digits = 1
          max_value = max(1, self.blockCount())
          while max_value >= 10:
              max_value /= 10
              digits += 1
          space = 3 + self.fontMetrics().width('9') * digits
          return space
  
      def updateLineNumberAreaWidth(self, _):
          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(QtCore.QRect(cr.left(), cr.top(), self.lineNumberAreaWidth(), cr.height()))
  
      def highlightCurrentLine(self):
          extraSelections = []
          if not self.isReadOnly():
              selection = QtWidgets.QTextEdit.ExtraSelection()
              lineColor = QtGui.QColor(QtCore.Qt.green).lighter(160)
              selection.format.setBackground(lineColor)
              selection.format.setProperty(QtGui.QTextFormat.FullWidthSelection, True)
              selection.cursor = self.textCursor()
              selection.cursor.clearSelection()
              extraSelections.append(selection)
          self.setExtraSelections(extraSelections)
  
      def lineNumberAreaPaintEvent(self, event):
          painter = QtGui.QPainter(self.lineNumberArea)
  
          painter.fillRect(event.rect(), QtCore.Qt.lightGray)
  
          block = self.firstVisibleBlock()
          blockNumber = block.blockNumber()
          top = self.blockBoundingGeometry(block).translated(self.contentOffset()).top()
          bottom = top + self.blockBoundingRect(block).height()
  
          # Just to make sure I use the right font
          height = self.fontMetrics().height()
          while block.isValid() and (top <= event.rect().bottom()):
              if block.isVisible() and (bottom >= event.rect().top()):
                  number = str(blockNumber + 1)
                  painter.setPen(QtCore.Qt.black)
                  painter.drawText(0, top, self.lineNumberArea.width(), height, QtCore.Qt.AlignRight, number)
  
              block = block.next()
              top = bottom
              bottom = top + self.blockBoundingRect(block).height()
              blockNumber += 1