|
@@ -1,103 +1,109 @@
|
|
|
from PySide2.QtGui import *
|
|
from PySide2.QtGui import *
|
|
|
-from PySide2.QtWidgets import QTextBrowser,QTextEdit
|
|
|
|
|
|
|
+from PySide2 import QtCore, QtGui, QtWidgets
|
|
|
from PySide2.QtCore import Qt
|
|
from PySide2.QtCore import Qt
|
|
|
|
|
|
|
|
|
|
|
|
|
-class LineNumberWidget(QTextBrowser):
|
|
|
|
|
- def __init__(self, widget):
|
|
|
|
|
- super().__init__()
|
|
|
|
|
- self.__initUi(widget)
|
|
|
|
|
-
|
|
|
|
|
- def __initUi(self, widget):
|
|
|
|
|
- self.__lineCount = widget.document().lineCount()
|
|
|
|
|
- self.__size = int(widget.font().pointSizeF())
|
|
|
|
|
- self.__styleInit()
|
|
|
|
|
- # 尝试解决行号换行问题
|
|
|
|
|
- self.setLineWrapMode(QTextEdit.LineWrapMode.NoWrap)
|
|
|
|
|
- self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
|
|
|
|
|
- self.setTextInteractionFlags(Qt.NoTextInteraction)
|
|
|
|
|
-
|
|
|
|
|
- self.verticalScrollBar().setEnabled(False)
|
|
|
|
|
-
|
|
|
|
|
- widget.verticalScrollBar().valueChanged.connect(self.__changeLineWidgetScrollAsTargetedWidgetScrollChanged)
|
|
|
|
|
-
|
|
|
|
|
- self.__initLineCount()
|
|
|
|
|
- self.befor_hightlight_line = None
|
|
|
|
|
-
|
|
|
|
|
- def __changeLineWidgetScrollAsTargetedWidgetScrollChanged(self, v):
|
|
|
|
|
- self.verticalScrollBar().setValue(v)
|
|
|
|
|
- self.setWidthByStr()
|
|
|
|
|
-
|
|
|
|
|
- def __initLineCount(self):
|
|
|
|
|
- for n in range(1, self.__lineCount+1):
|
|
|
|
|
- self.append(str(n))
|
|
|
|
|
-
|
|
|
|
|
- def changeLineCount(self, n):
|
|
|
|
|
- max_one = max(self.__lineCount, n)
|
|
|
|
|
- diff = n-self.__lineCount
|
|
|
|
|
- if max_one == self.__lineCount:
|
|
|
|
|
- first_v = self.verticalScrollBar().value()
|
|
|
|
|
- for i in range(self.__lineCount, self.__lineCount + diff, -1):
|
|
|
|
|
- self.moveCursor(QTextCursor.End, QTextCursor.MoveAnchor)
|
|
|
|
|
- self.moveCursor(QTextCursor.StartOfLine, QTextCursor.MoveAnchor)
|
|
|
|
|
- self.moveCursor(QTextCursor.End, QTextCursor.KeepAnchor)
|
|
|
|
|
- self.textCursor().removeSelectedText()
|
|
|
|
|
- self.textCursor().deletePreviousChar()
|
|
|
|
|
- last_v = self.verticalScrollBar().value()
|
|
|
|
|
- if abs(first_v-last_v) != 2:
|
|
|
|
|
- self.verticalScrollBar().setValue(first_v)
|
|
|
|
|
- else:
|
|
|
|
|
- for i in range(self.__lineCount, self.__lineCount + diff, 1):
|
|
|
|
|
- self.append(str(i + 1))
|
|
|
|
|
-
|
|
|
|
|
- self.__lineCount = n
|
|
|
|
|
-
|
|
|
|
|
- def selectLine(self, line):
|
|
|
|
|
- position = self.document().findBlockByLineNumber(line-1).position()
|
|
|
|
|
- tc = self.textCursor()
|
|
|
|
|
- tc.setPosition(position, QTextCursor.MoveAnchor) # 直接跳转到start_pos
|
|
|
|
|
- tc.movePosition(QTextCursor.EndOfLine, QTextCursor.KeepAnchor)
|
|
|
|
|
- return tc
|
|
|
|
|
-
|
|
|
|
|
- def highlightLineNumber(self, line):
|
|
|
|
|
- if self.befor_hightlight_line:
|
|
|
|
|
- self.clearHighlightLineNumber(self.befor_hightlight_line)
|
|
|
|
|
- fmt = QTextCharFormat()
|
|
|
|
|
- fmt.setFontWeight(QFont.Bold)
|
|
|
|
|
- fmt.setForeground(QBrush(QColor(0,0,0)))
|
|
|
|
|
- tc = self.selectLine(line)
|
|
|
|
|
- tc.mergeCharFormat(fmt) # 格式化选中文本
|
|
|
|
|
- tc.clearSelection() # 取消选中
|
|
|
|
|
- self.befor_hightlight_line = line
|
|
|
|
|
-
|
|
|
|
|
- def clearHighlightLineNumber(self, line):
|
|
|
|
|
- tc = self.selectLine(line)
|
|
|
|
|
- tc.setCharFormat(QTextCharFormat())
|
|
|
|
|
-
|
|
|
|
|
- def setValue(self, v):
|
|
|
|
|
- self.verticalScrollBar().setValue(v)
|
|
|
|
|
|
|
+class Editor(QtWidgets.QPlainTextEdit):
|
|
|
|
|
+ def __init__(self, parent=None):
|
|
|
|
|
+ super().__init__(parent)
|
|
|
|
|
+ self.lineNumberArea = LineNumberArea(self)
|
|
|
|
|
+
|
|
|
|
|
+ self.blockCountChanged.connect(self.updateLineNumberAreaWidth)
|
|
|
|
|
+ self.updateRequest.connect(self.updateLineNumberArea)
|
|
|
|
|
+ self.cursorPositionChanged.connect(self.highlightCurrentLine)
|
|
|
|
|
+
|
|
|
|
|
+ self.updateLineNumberAreaWidth(0)
|
|
|
|
|
+ self.setLineWrapMode(QtWidgets.QPlainTextEdit.LineWrapMode.NoWrap)
|
|
|
|
|
+ self.setReadOnly(True)
|
|
|
|
|
|
|
|
- def setFontSize(self, s: float):
|
|
|
|
|
- self.__size = int(s)
|
|
|
|
|
- self.__styleInit()
|
|
|
|
|
-
|
|
|
|
|
- def setWidthByStr(self):
|
|
|
|
|
- withd = (len(str(self.__lineCount)) + 2) * self.__size*0.7
|
|
|
|
|
- self.setFixedWidth(withd)
|
|
|
|
|
-
|
|
|
|
|
- def line_count(self):
|
|
|
|
|
- return self.__lineCount
|
|
|
|
|
|
|
+ def goToLine(self, center_line):
|
|
|
|
|
+ total_num_of_lines = self.document().lineCount()
|
|
|
|
|
+ # 计算每行的高度
|
|
|
|
|
+ line_height = self.fontMetrics().lineSpacing()
|
|
|
|
|
+ # 获取文本控件可视窗口的高度
|
|
|
|
|
+ viewport_height = self.viewport().height()
|
|
|
|
|
+ # 每行所需滚动条的值
|
|
|
|
|
+ scroll_per_line = (self.verticalScrollBar().pageStep() + self.verticalScrollBar().maximum())/total_num_of_lines
|
|
|
|
|
|
|
|
- def __styleInit(self):
|
|
|
|
|
- self.__style = f'''
|
|
|
|
|
- QTextBrowser
|
|
|
|
|
- {{
|
|
|
|
|
- background: transparent;
|
|
|
|
|
- border: none;
|
|
|
|
|
- color: #AAA;
|
|
|
|
|
- font: {self.__size}pt;
|
|
|
|
|
- }}
|
|
|
|
|
- '''
|
|
|
|
|
|
|
+ scroll_value = (center_line - viewport_height/2/line_height) * scroll_per_line
|
|
|
|
|
|
|
|
- self.setStyleSheet(self.__style)
|
|
|
|
|
- self.setWidthByStr()
|
|
|
|
|
|
|
+ # 移动光标
|
|
|
|
|
+ cursor = self.textCursor()
|
|
|
|
|
+ cursor.movePosition(QtGui.QTextCursor.Start)
|
|
|
|
|
+ for i in range(center_line - 1):
|
|
|
|
|
+ cursor.movePosition(QtGui.QTextCursor.Down, QtGui.QTextCursor.MoveAnchor)
|
|
|
|
|
+ self.setTextCursor(cursor)
|
|
|
|
|
+ self.verticalScrollBar().setValue(0)
|
|
|
|
|
+ self.verticalScrollBar().setValue(scroll_value)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ def lineNumberAreaWidth(self):
|
|
|
|
|
+ digits = 1
|
|
|
|
|
+ count = max(1, self.blockCount())
|
|
|
|
|
+ while count >= 10:
|
|
|
|
|
+ count /= 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(QtCore.QRect(cr.left(), cr.top(), self.lineNumberAreaWidth(), cr.height()))
|
|
|
|
|
+
|
|
|
|
|
+ def highlightCurrentLine(self):
|
|
|
|
|
+ extraSelections = []
|
|
|
|
|
+
|
|
|
|
|
+ selection = QtWidgets.QTextEdit.ExtraSelection()
|
|
|
|
|
+
|
|
|
|
|
+ lineColor = QtGui.QColor(232,232,255)
|
|
|
|
|
+
|
|
|
|
|
+ selection.format.setBackground(lineColor)
|
|
|
|
|
+ selection.format.setProperty(QtGui.QTextFormat.FullWidthSelection, True)
|
|
|
|
|
+ selection.cursor = self.textCursor()
|
|
|
|
|
+ selection.cursor.clearSelection()
|
|
|
|
|
+ extraSelections.append(selection)
|
|
|
|
|
+
|
|
|
|
|
+ self.setExtraSelections(extraSelections)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+class LineNumberArea(QtWidgets.QWidget):
|
|
|
|
|
+ def __init__(self, editor):
|
|
|
|
|
+ super().__init__(editor)
|
|
|
|
|
+ self.editor = editor
|
|
|
|
|
+
|
|
|
|
|
+ def sizeHint(self):
|
|
|
|
|
+ return QtCore.QSize(self.editor.lineNumberAreaWidth(), 0)
|
|
|
|
|
+
|
|
|
|
|
+ def paintEvent(self, event):
|
|
|
|
|
+ painter = QtGui.QPainter(self)
|
|
|
|
|
+ painter.fillRect(event.rect(), QtCore.Qt.lightGray)
|
|
|
|
|
+
|
|
|
|
|
+ block = self.editor.firstVisibleBlock()
|
|
|
|
|
+ blockNumber = block.blockNumber()
|
|
|
|
|
+ top = self.editor.blockBoundingGeometry(block).translated(self.editor.contentOffset()).top()
|
|
|
|
|
+ bottom = top + self.editor.blockBoundingRect(block).height()
|
|
|
|
|
+
|
|
|
|
|
+ height = self.editor.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.width(), height, QtCore.Qt.AlignRight, number)
|
|
|
|
|
+
|
|
|
|
|
+ block = block.next()
|
|
|
|
|
+ top = bottom
|
|
|
|
|
+ bottom = top + self.editor.blockBoundingRect(block).height()
|
|
|
|
|
+ blockNumber += 1
|