【问题标题】:Create text area (textEdit) with line number in PyQt在 PyQt 中使用行号创建文本区域(textEdit)
【发布时间】:2017-03-16 03:13:17
【问题描述】:

我想在 PyQt 的左侧创建带有行号的 textEdit,例如 Notepad++。我试过这个添加另一个 textEdit 但滚动被卡住了。我搜索并找到了这个question,但没有好的解决方案。

【问题讨论】:

标签: python pyqt pyqt4 pyqt5 qtextedit


【解决方案1】:

这是我结合@Dan-Dev 和@Axel Schneider 的PyQt5 和python3 代码。可以直接运行,也可以直接导入使用代码from QCodeEditor import QCodeEditor

#!/usr/bin/python3
# QcodeEditor.py by acbetter.
# -*- coding: utf-8 -*-

from PyQt5.QtCore import Qt, QRect, QSize
from PyQt5.QtWidgets import QWidget, QPlainTextEdit, QTextEdit
from PyQt5.QtGui import QColor, QPainter, QTextFormat


class QLineNumberArea(QWidget):
    def __init__(self, editor):
        super().__init__(editor)
        self.codeEditor = editor

    def sizeHint(self):
        return QSize(self.editor.lineNumberAreaWidth(), 0)

    def paintEvent(self, event):
        self.codeEditor.lineNumberAreaPaintEvent(event)


class QCodeEditor(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(QRect(cr.left(), cr.top(), self.lineNumberAreaWidth(), cr.height()))

    def highlightCurrentLine(self):
        extraSelections = []
        if not self.isReadOnly():
            selection = QTextEdit.ExtraSelection()
            lineColor = QColor(Qt.yellow).lighter(160)
            selection.format.setBackground(lineColor)
            selection.format.setProperty(QTextFormat.FullWidthSelection, True)
            selection.cursor = self.textCursor()
            selection.cursor.clearSelection()
            extraSelections.append(selection)
        self.setExtraSelections(extraSelections)

    def lineNumberAreaPaintEvent(self, event):
        painter = QPainter(self.lineNumberArea)

        painter.fillRect(event.rect(), 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(Qt.black)
                painter.drawText(0, top, self.lineNumberArea.width(), height, Qt.AlignRight, number)

            block = block.next()
            top = bottom
            bottom = top + self.blockBoundingRect(block).height()
            blockNumber += 1


if __name__ == '__main__':
    import sys
    from PyQt5.QtWidgets import QApplication

    app = QApplication(sys.argv)
    codeEditor = QCodeEditor()
    codeEditor.show()
    sys.exit(app.exec_())

它是这样运行的。

顺便说一句,如果你想在你的 Qt Designer 中使用它,你应该这样做,并将 *.ui 文件放在 QCodeEditor.py 文件路径的同一目录中,除非你设置了环境变量。而且,您需要通过命令pyuic5 -x *.ui -o *.py 将您的*.ui 文件转换为*.py 文件。希望有帮助~

【讨论】:

    【解决方案2】:

    这里是 PyQt5 的版本(带有菜单等...)

    #!/usr/bin/python3
    # -- coding: utf-8 --
    
    from PyQt5.QtWidgets import QPlainTextEdit, QWidget, QVBoxLayout, QApplication, QFileDialog, QMessageBox, QHBoxLayout, \
                             QFrame, QTextEdit, QToolBar, QComboBox, QLabel, QAction, QLineEdit, QToolButton, QMenu, QMainWindow
    from PyQt5.QtGui import QIcon, QPainter, QTextFormat, QColor, QTextCursor, QKeySequence, QClipboard, QTextCharFormat, QPalette
    from PyQt5.QtCore import Qt, QVariant, QRect, QDir, QFile, QFileInfo, QTextStream, QRegExp, QSettings
    import sys, os
    
    lineBarColor = QColor("#ACDED5")
    lineHighlightColor  = QColor("#ACDED5")
    
    class NumberBar(QWidget):
        def __init__(self, parent = None):
            super(NumberBar, self).__init__(parent)
            self.editor = parent
            layout = QVBoxLayout()
            self.setLayout(layout)
            self.editor.blockCountChanged.connect(self.update_width)
            self.editor.updateRequest.connect(self.update_on_scroll)
            self.update_width('1')
    
        def update_on_scroll(self, rect, scroll):
            if self.isVisible():
                if scroll:
                    self.scroll(0, scroll)
                else:
                    self.update()
    
        def update_width(self, string):
            width = self.fontMetrics().width(str(string)) + 10
            if self.width() != width:
                self.setFixedWidth(width)
    
        def paintEvent(self, event):
            if self.isVisible():
                block = self.editor.firstVisibleBlock()
                height = self.fontMetrics().height()
                number = block.blockNumber()
                painter = QPainter(self)
                painter.fillRect(event.rect(), lineBarColor)
                painter.drawRect(0, 0, event.rect().width() - 1, event.rect().height() - 1)
                font = painter.font()
    
                current_block = self.editor.textCursor().block().blockNumber() + 1
    
                condition = True
                while block.isValid() and condition:
                    block_geometry = self.editor.blockBoundingGeometry(block)
                    offset = self.editor.contentOffset()
                    block_top = block_geometry.translated(offset).top()
                    number += 1
    
                    rect = QRect(0, block_top, self.width() - 5, height)
    
                    if number == current_block:
                        font.setBold(True)
                    else:
                        font.setBold(False)
    
                    painter.setFont(font)
                    painter.drawText(rect, Qt.AlignRight, '%i'%number)
    
                    if block_top > event.rect().bottom():
                        condition = False
    
                    block = block.next()
    
                painter.end()
    
    class myEditor(QMainWindow):
        def __init__(self, parent = None):
            super(myEditor, self).__init__(parent)
            self.MaxRecentFiles = 5
            self.windowList = []
            self.recentFileActs = []
            self.setAttribute(Qt.WA_DeleteOnClose)
            # Editor Widget ...
            QIcon.setThemeName('Faenza-Dark')
            self.editor = QPlainTextEdit() 
            self.editor.setStyleSheet(stylesheet2(self))
            self.editor.setFrameStyle(QFrame.NoFrame)
            self.editor.setTabStopWidth(14)
            self.extra_selections = []
            self.fname = ""
            self.filename = ""
            # Line Numbers ...
            self.numbers = NumberBar(self.editor)
    
            self.createActions()
            # Laying out...
            layoutH = QHBoxLayout()
            layoutH.setSpacing(1.5)
            layoutH.addWidget(self.numbers)
            layoutH.addWidget(self.editor)
    
            ### begin toolbar
            tb = QToolBar(self)
            tb.setWindowTitle("File Toolbar")        
    
            self.newAct = QAction("&New", self, shortcut=QKeySequence.New,
                    statusTip="Create a new file", triggered=self.newFile)
            self.newAct.setIcon(QIcon.fromTheme("document-new"))
    
            self.openAct = QAction("&Open", self, shortcut=QKeySequence.Open,
                    statusTip="open file", triggered=self.openFile)
            self.openAct.setIcon(QIcon.fromTheme("document-open"))
    
            self.saveAct = QAction("&Save", self, shortcut=QKeySequence.Save,
                    statusTip="save file", triggered=self.fileSave)
            self.saveAct.setIcon(QIcon.fromTheme("document-save"))
    
            self.saveAsAct = QAction("&Save as ...", self, shortcut=QKeySequence.SaveAs,
                    statusTip="save file as ...", triggered=self.fileSaveAs)
            self.saveAsAct.setIcon(QIcon.fromTheme("document-save-as"))
    
            self.exitAct = QAction("Exit", self, shortcut=QKeySequence.Quit,
                    toolTip="Exit", triggered=self.handleQuit)
            self.exitAct.setIcon(QIcon.fromTheme("application-exit"))
    
            ### find / replace toolbar
            self.tbf = QToolBar(self)
            self.tbf.setWindowTitle("Find Toolbar")   
            self.findfield = QLineEdit()
            self.findfield.addAction(QIcon.fromTheme("edit-find"), QLineEdit.LeadingPosition)
            self.findfield.setClearButtonEnabled(True)
            self.findfield.setFixedWidth(150)
            self.findfield.setPlaceholderText("find")
            self.findfield.setToolTip("press RETURN to find")
            self.findfield.setText("")
            ft = self.findfield.text()
            self.findfield.returnPressed.connect(self.findText)
            self.tbf.addWidget(self.findfield)
            self.replacefield = QLineEdit()
            self.replacefield.addAction(QIcon.fromTheme("edit-find-and-replace"), QLineEdit.LeadingPosition)
            self.replacefield.setClearButtonEnabled(True)
            self.replacefield.setFixedWidth(150)
            self.replacefield.setPlaceholderText("replace with")
            self.replacefield.setToolTip("press RETURN to replace the first")
            self.replacefield.returnPressed.connect(self.replaceOne)
            self.tbf.addSeparator() 
            self.tbf.addWidget(self.replacefield)
            self.tbf.addSeparator()
    
            self.tbf.addAction("replace all", self.replaceAll)
            self.tbf.addSeparator()
    
            layoutV = QVBoxLayout()
    
            bar=self.menuBar()
    
            self.filemenu=bar.addMenu("File")
            self.separatorAct = self.filemenu.addSeparator()
            self.filemenu.addAction(self.newAct)
            self.filemenu.addAction(self.openAct)
            self.filemenu.addAction(self.saveAct)
            self.filemenu.addAction(self.saveAsAct)
            self.filemenu.addSeparator()
            for i in range(self.MaxRecentFiles):
                self.filemenu.addAction(self.recentFileActs[i])
            self.updateRecentFileActions()
            self.filemenu.addSeparator()
            self.filemenu.addAction(self.exitAct)
            bar.setStyleSheet(stylesheet2(self))
            editmenu = bar.addMenu("Edit")
            editmenu.addAction(QAction(QIcon.fromTheme('edit-copy'), "Copy", self, triggered = self.editor.copy, shortcut = QKeySequence.Copy))
            editmenu.addAction(QAction(QIcon.fromTheme('edit-cut'), "Cut", self, triggered = self.editor.cut, shortcut = QKeySequence.Cut))
            editmenu.addAction(QAction(QIcon.fromTheme('edit-paste'), "Paste", self, triggered = self.editor.paste, shortcut = QKeySequence.Paste))
            editmenu.addAction(QAction(QIcon.fromTheme('edit-delete'), "Delete", self, triggered = self.editor.cut, shortcut = QKeySequence.Delete))
            editmenu.addSeparator()
            editmenu.addAction(QAction(QIcon.fromTheme('edit-select-all'), "Select All", self, triggered = self.editor.selectAll, shortcut = QKeySequence.SelectAll))
    
            layoutV.addWidget(bar)      
            layoutV.addWidget(self.tbf)
            layoutV.addLayout(layoutH)
    
            ### main window
            mq = QWidget(self)
            mq.setLayout(layoutV)
            self.setCentralWidget(mq)
    
            # Event Filter ...
            self.installEventFilter(self)
            self.editor.setFocus()
            self.cursor = QTextCursor()
            self.editor.setPlainText("hello")
            self.editor.moveCursor(self.cursor.End)
            self.editor.document().modificationChanged.connect(self.setWindowModified)
    
            # Brackets ExtraSelection ...
            self.left_selected_bracket  = QTextEdit.ExtraSelection()
            self.right_selected_bracket = QTextEdit.ExtraSelection()
    
        def createActions(self):
            for i in range(self.MaxRecentFiles):
                self.recentFileActs.append(
                       QAction(self, visible=False,
                                triggered=self.openRecentFile))
    
    
        def openRecentFile(self):
            action = self.sender()
            if action:
                if (self.maybeSave()):
                    self.openFileOnStart(action.data())
    
            ### New File
        def newFile(self):
            if self.maybeSave():
                self.editor.clear()
                self.editor.setPlainText("")
                self.filename = ""
                self.setModified(False)
                self.editor.moveCursor(self.cursor.End)
    
           ### open File
        def openFileOnStart(self, path=None):
            if path:
                inFile = QFile(path)
                if inFile.open(QFile.ReadWrite | QFile.Text):
                    text = inFile.readAll()
    
                    try:
                            # Python v3.
                        text = str(text, encoding = 'utf8')
                    except TypeError:
                            # Python v2.
                        text = str(text)
                    self.editor.setPlainText(text)
                    self.filename = path
                    self.setModified(False)
                    self.fname = QFileInfo(path).fileName() 
                    self.setWindowTitle(self.fname + "[*]")
                    self.document = self.editor.document()
                    self.setCurrentFile(self.filename)
    
            ### open File
        def openFile(self, path=None):
            if self.maybeSave():
                if not path:
                    path, _ = QFileDialog.getOpenFileName(self, "Open File", QDir.homePath() + "/Documents/",
                        "Text Files (*.txt *.csv *.py);;All Files (*.*)")
    
                if path:
                    inFile = QFile(path)
                    if inFile.open(QFile.ReadWrite | QFile.Text):
                        text = inFile.readAll()
    
                        try:
                            # Python v3.
                            text = str(text, encoding = 'utf8')
                        except TypeError:
                            # Python v2.
                            text = str(text)
                        self.editor.setPlainText(text)
                        self.filename = path
                        self.setModified(False)
                        self.fname = QFileInfo(path).fileName() 
                        self.setWindowTitle(self.fname + "[*]")
                        self.document = self.editor.document()
                        self.setCurrentFile(self.filename)
    
        def fileSave(self):
            if (self.filename != ""):
                file = QFile(self.filename)
                print(self.filename)
                if not file.open( QFile.WriteOnly | QFile.Text):
                    QMessageBox.warning(self, "Error",
                            "Cannot write file %s:\n%s." % (self.filename, file.errorString()))
                    return
    
                outstr = QTextStream(file)
                QApplication.setOverrideCursor(Qt.WaitCursor)
                outstr << self.editor.toPlainText()
                QApplication.restoreOverrideCursor()                
                self.setModified(False)
                self.fname = QFileInfo(self.filename).fileName() 
                self.setWindowTitle(self.fname + "[*]")
                self.setCurrentFile(self.filename)
    
    
            else:
                self.fileSaveAs()
    
                ### save File
        def fileSaveAs(self):
            fn, _ = QFileDialog.getSaveFileName(self, "Save as...", self.filename,
                    "Python files (*.py)")
    
            if not fn:
                print("Error saving")
                return False
    
            lfn = fn.lower()
            if not lfn.endswith('.py'):
                fn += '.py'
    
            self.filename = fn
            self.fname = os.path.splitext(str(fn))[0].split("/")[-1]
            return self.fileSave()
    
        def closeEvent(self, e):
            if self.maybeSave():
                e.accept()
            else:
                e.ignore()
    
            ### ask to save
        def maybeSave(self):
            if not self.isModified():
                return True
    
            if self.filename.startswith(':/'):
                return True
    
            ret = QMessageBox.question(self, "Message",
                    "<h4><p>The document was modified.</p>\n" \
                    "<p>Do you want to save changes?</p></h4>",
                    QMessageBox.Yes | QMessageBox.No | QMessageBox.Cancel)
    
            if ret == QMessageBox.Yes:
                if self.filename == "":
                    self.fileSaveAs()
                    return False
                else:
                    self.fileSave()
                    return True
    
            if ret == QMessageBox.Cancel:
                return False
    
            return True   
    
        def findText(self):
            ft = self.findfield.text()
            if self.editor.find(ft):
                return
            else:
                self.editor.moveCursor(1)
                if self.editor.find(ft):
                    self.editor.moveCursor(QTextCursor.Start, QTextCursor.MoveAnchor)
    
        def handleQuit(self):
            print("Goodbye ...")
            app.quit()
    
        def set_numbers_visible(self, value = True):
            self.numbers.setVisible(False)
    
        def match_left(self, block, character, start, found):
            map = {'{': '}', '(': ')', '[': ']'}
    
            while block.isValid():
                data = block.userData()
                if data is not None:
                    braces = data.braces
                    N = len(braces)
    
                    for k in range(start, N):
                        if braces[k].character == character:
                            found += 1
    
                        if braces[k].character == map[character]:
                            if not found:
                                return braces[k].position + block.position()
                            else:
                                found -= 1
    
                    block = block.next()
                    start = 0
    
        def match_right(self, block, character, start, found):
            map = {'}': '{', ')': '(', ']': '['}
    
            while block.isValid():
                data = block.userData()
    
                if data is not None:
                    braces = data.braces
    
                    if start is None:
                        start = len(braces)
                    for k in range(start - 1, -1, -1):
                        if braces[k].character == character:
                            found += 1
                        if braces[k].character == map[character]:
                            if found == 0:
                                return braces[k].position + block.position()
                            else:
                                found -= 1
                block = block.previous()
                start = None
    #    '''
    
            cursor = self.editor.textCursor()
            block = cursor.block()
            data = block.userData()
            previous, next = None, None
    
            if data is not None:
                position = cursor.position()
                block_position = cursor.block().position()
                braces = data.braces
                N = len(braces)
    
                for k in range(0, N):
                    if braces[k].position == position - block_position or braces[k].position == position - block_position - 1:
                        previous = braces[k].position + block_position
                        if braces[k].character in ['{', '(', '[']:
                            next = self.match_left(block,
                                                   braces[k].character,
                                                   k + 1, 0)
                        elif braces[k].character in ['}', ')', ']']:
                            next = self.match_right(block,
                                                    braces[k].character,
                                                    k, 0)
                        if next is None:
                            next = -1
    
            if next is not None and next > 0:
                if next == 0 and next >= 0:
                    format = QTextCharFormat()
    
                cursor.setPosition(previous)
                cursor.movePosition(QTextCursor.NextCharacter,
                                    QTextCursor.KeepAnchor)
    
                format.setBackground(QColor('white'))
                self.left_selected_bracket.format = format
                self.left_selected_bracket.cursor = cursor
    
                cursor.setPosition(next)
                cursor.movePosition(QTextCursor.NextCharacter,
                                    QTextCursor.KeepAnchor)
    
                format.setBackground(QColor('white'))
                self.right_selected_bracket.format = format
                self.right_selected_bracket.cursor = cursor
    #            '''
        def paintEvent(self, event):
            highlighted_line = QTextEdit.ExtraSelection()
            highlighted_line.format.setBackground(lineHighlightColor)
            highlighted_line.format.setProperty(QTextFormat
                                                     .FullWidthSelection,
                                                      QVariant(True))
            highlighted_line.cursor = self.editor.textCursor()
            highlighted_line.cursor.clearSelection()
            self.editor.setExtraSelections([highlighted_line,
                                          self.left_selected_bracket,
                                          self.right_selected_bracket])
    
        def document(self):
            return self.editor.document
    
        def isModified(self):
            return self.editor.document().isModified()
    
        def setModified(self, modified):
            self.editor.document().setModified(modified)
    
        def setLineWrapMode(self, mode):
            self.editor.setLineWrapMode(mode)
    
        def clear(self):
            self.editor.clear()
    
        def setPlainText(self, *args, **kwargs):
            self.editor.setPlainText(*args, **kwargs)
    
        def setDocumentTitle(self, *args, **kwargs):
            self.editor.setDocumentTitle(*args, **kwargs)
    
        def set_number_bar_visible(self, value):
            self.numbers.setVisible(value)
    
        def replaceAll(self):
            print("replacing all")
            oldtext = self.editor.document().toPlainText()
            newtext = oldtext.replace(self.findfield.text(), self.replacefield.text())
            self.editor.setPlainText(newtext)
            self.setModified(True)
    
        def replaceOne(self):
            print("replacing all")
            oldtext = self.editor.document().toPlainText()
            newtext = oldtext.replace(self.findfield.text(), self.replacefield.text(), 1)
            self.editor.setPlainText(newtext)
            self.setModified(True)
    
        def setCurrentFile(self, fileName):
            self.curFile = fileName
            if self.curFile:
                self.setWindowTitle("%s - Recent Files" % self.strippedName(self.curFile))
            else:
                self.setWindowTitle("Recent Files")
    
            settings = QSettings('Axel Schneider', 'PTEdit')
            files = settings.value('recentFileList')
    
            try:
                files.remove(fileName)
            except ValueError:
                pass
    
            files.insert(0, fileName)
            del files[self.MaxRecentFiles:]
    
            settings.setValue('recentFileList', files)
    
            for widget in QApplication.topLevelWidgets():
                if isinstance(widget, myEditor):
                    widget.updateRecentFileActions()
    
        def updateRecentFileActions(self):
            mytext = ""
            settings = QSettings('Axel Schneider', 'PTEdit')
            files = settings.value('recentFileList')
    
            numRecentFiles = min(len(files), self.MaxRecentFiles)
    
            for i in range(numRecentFiles):
                text = "&%d %s" % (i + 1, self.strippedName(files[i]))
                self.recentFileActs[i].setText(text)
                self.recentFileActs[i].setData(files[i])
                self.recentFileActs[i].setVisible(True)
    
            for j in range(numRecentFiles, self.MaxRecentFiles):
                self.recentFileActs[j].setVisible(False)
    
            self.separatorAct.setVisible((numRecentFiles > 0))
    
        def clearRecentFileList(self, fileName):
            self.rmenu.clear()
    
    
        def strippedName(self, fullFileName):
            return QFileInfo(fullFileName).fileName()
    
    def stylesheet2(self):
        return """
    QPlainTextEdit
    {
    background: #ECECEC;
    color: #202020;
    border: 1px solid #1EAE3D;
    selection-background-color: #505050;
    selection-color: #ACDED5;
    }
    QMenu
    {
    background: #F2F2F2;
    color: #0E185F;
    border: 1px solid #1EAE3D;
    selection-background-color: #ACDED5;
    } 
        """       
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        win = myEditor()
        win.setWindowIcon(QIcon.fromTheme("application-text"))
        win.setWindowTitle("Plain Text Edit" + "[*]")
        win.setMinimumSize(640,250)
        win.showMaximized()
        if len(sys.argv) > 1:
            print(sys.argv[1])
            win.openFileOnStart(sys.argv[1])
        app.exec_()
    

    【讨论】:

      【解决方案3】:

      这就是你要找的基于c++的CodeEditor example in pyqthttp://doc.qt.io/qt-5/qtwidgets-widgets-codeeditor-example.html

      将它放在一起用于 python3(我使用的是 PyQt4 而不是 5,但我猜它是相似的)(并且使用 QPlainTextEdit 而不是 QTextEdit 参见QTextEdit vs QPlainTextEdit):

      import sys
      from PyQt4.QtGui import *
      from PyQt4.QtCore import *
      
      import numpy as np
      
      class LineNumberArea(QWidget):
      
      
          def __init__(self, editor):
              super().__init__(editor)
              self.myeditor = editor
      
      
          def sizeHint(self):
              return Qsize(self.editor.lineNumberAreaWidth(), 0)
      
      
          def paintEvent(self, event):
              self.myeditor.lineNumberAreaPaintEvent(event)
      
      
      class CodeEditor(QPlainTextEdit):
          def __init__(self):
              super().__init__()
              self.lineNumberArea = LineNumberArea(self)
      
              self.connect(self, SIGNAL('blockCountChanged(int)'), self.updateLineNumberAreaWidth)
              self.connect(self, SIGNAL('updateRequest(QRect,int)'), self.updateLineNumberArea)
              self.connect(self, SIGNAL('cursorPositionChanged()'), self.highlightCurrentLine)
      
              self.updateLineNumberAreaWidth(0)
      
      
          def lineNumberAreaWidth(self):
              digits = 1
              count = max(1, self.blockCount())
              while count >= 10:
                  count /= 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(QRect(cr.left(), cr.top(),
                          self.lineNumberAreaWidth(), cr.height()))
      
      
          def lineNumberAreaPaintEvent(self, event):
              mypainter = QPainter(self.lineNumberArea)
      
              mypainter.fillRect(event.rect(), 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)
                      mypainter.setPen(Qt.black)
                      mypainter.drawText(0, top, self.lineNumberArea.width(), height,
                       Qt.AlignRight, number)
      
                  block = block.next()
                  top = bottom
                  bottom = top + self.blockBoundingRect(block).height()
                  blockNumber += 1
      
      
          def highlightCurrentLine(self):
              extraSelections = []
      
              if not self.isReadOnly():
                  selection = QTextEdit.ExtraSelection()
      
                  lineColor = QColor(Qt.yellow).lighter(160)
      
                  selection.format.setBackground(lineColor)
                  selection.format.setProperty(QTextFormat.FullWidthSelection, True)
                  selection.cursor = self.textCursor()
                  selection.cursor.clearSelection()
                  extraSelections.append(selection)
              self.setExtraSelections(extraSelections)
      
      
      if __name__ == "__main__":
          app = QApplication(sys.argv)
      
          txt = CodeEditor()
          txt.show()
      
          sys.exit(app.exec_())
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2019-03-22
        • 2023-04-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-02-29
        • 1970-01-01
        相关资源
        最近更新 更多