x01.DiamondIDE: hello ide
虽然一直在用 vscode,但自己写一个也不错。通过比较,选择 Spyder 来学习。代码: x01.DiamondIDE
1 Edit
1.1 hello DiamondIDE
使用 pip 安装所需模块 pyqt 等,自不待言。hello.py 代码如下:
from qtpy.QtWidgets import QApplication, QPlainTextEdit
app = QApplication(["x01.DiamondIDE"])
edit = QPlainTextEdit("hello DiamondIDE")
edit.show()
app.exec_()
终端输入: python3 hello.py
运行一下,OK!
1.2 添加测试
删除 hello.py, 添加 widgets/edit.py 如下:
# widgets/edit.py (c) 2021 by x01
from qtpy.QtWidgets import QPlainTextEdit, QApplication
class Edit(QPlainTextEdit):
def __init__(self, parent=None):
super().__init__(parent=parent)
def test_edit():
app = QApplication(["x01.DiamondIDE"])
edit = Edit()
edit.setPlainText("Hello IDE!")
edit.show()
app.exec()
if __name__ == "__main__":
test_edit()
添加 tests/test_edit.py 如下:
import os, sys
RootDir = os.path.dirname(os.path.dirname(__file__))
sys.path.append(RootDir)
import widgets.edit as edit
def test_edit():
edit.test_edit()
先安装 pytest: python3 -m pip install -U pytest
, 然后在终端运行测试: pytest
, OK!
顺便添加 main.py,代码如下:
import os, sys
CurrDir = os.path.dirname(__file__)
sys.path.append(CurrDir)
from widgets.edit import test_edit
def main():
test_edit()
if __name__ == "__main__":
main()
运行一下,OK!
注释 tests/test_edit.py 的 RootDir,添加 test.py 以在测试时统一添加路径,代码如下:
import os, sys
RootDir = os.path.dirname(__file__)
sys.path.append(RootDir)
import pytest
if __name__ == "__main__":
pytest.main()
运行一下,OK!
1.3 切入点
在 Python 的 site-packages 目录下新建 mypath.pth 文件,添加 x01.DiamondIDE 所在路径,以便导入。
Spyder 太大,还是以 CodeEditor作为切入点。
widgets/edit.py 更改如下:
# widgets/edit.py (c) 2021 by x01
from qtpy.QtWidgets import QPlainTextEdit, QApplication, QWidget
from qtpy.QtCore import QSize
from PyQt5.QtGui import QColor, QPaintEvent, QPainter, QTextBlock, QTextFormat
from PyQt5.QtCore import QRect, Qt
from PyQt5.QtWidgets import QMainWindow, QTextEdit
class LineNumberArea(QWidget):
def __init__(self, editor=None):
super().__init__(editor)
self.editor = editor
self.left_padding = 3
self.right_padding = 6
# override
def sizeHint(self):
return QSize(self.editor.LineNumberAreaWidth(), 0)
def paintEvent(self, event):
self.editor.LineNumberAreaPaintEvent(event)
class CodeEditor(QPlainTextEdit):
def __init__(self, parent=None):
super().__init__(parent=parent)
self.line_number_area = LineNumberArea(self)
self.line_number_enabled = True
#event
self.blockCountChanged.connect(self.UpdateLineNumberAreaWidth)
self.updateRequest.connect(self.UpdateLineNumberArea)
self.cursorPositionChanged.connect(self.HighlightCurrentLine)
self.UpdateLineNumberAreaWidth(0)
self.HighlightCurrentLine()
def resizeEvent(self, event):
super().resizeEvent(event)
cr:QRect = self.contentsRect()
self.line_number_area.setGeometry(QRect(cr.left(), cr.top(), self.LineNumberAreaWidth(), cr.height()))
def LineNumberAreaWidth(self):
width = 0
if self.line_number_enabled:
digits = 1
count = max(1, self.blockCount())
while count >= 10:
count /= 10
digits += 1
fm = self.fontMetrics()
width = fm.width("9") * digits + self.line_number_area.left_padding + self.line_number_area.right_padding
return width
def LineNumberAreaPaintEvent(self, event:QPaintEvent):
if self.line_number_enabled:
painter = QPainter(self.line_number_area)
painter.fillRect(event.rect(), Qt.lightGray)
block:QTextBlock = self.firstVisibleBlock()
block_number = block.blockNumber()
top = round(self.blockBoundingGeometry(block).translated(self.contentOffset()).top())
bottom = top + round(self.blockBoundingRect(block).height())
while block.isValid() and top <= event.rect().bottom():
if block.isVisible() and bottom >= event.rect().top():
number = block_number + 1
painter.setPen(Qt.black)
painter.drawText(0, top, self.line_number_area.width() - self.line_number_area.right_padding,
self.fontMetrics().height(), Qt.AlignRight, str(number))
block = block.next()
top = bottom
bottom = top + round(self.blockBoundingRect(block).height())
block_number += 1
def UpdateLineNumberAreaWidth(self, new_block_count=None):
self.setViewportMargins(self.LineNumberAreaWidth(),0,0,0)
def UpdateLineNumberArea(self, rect, dy):
if self.line_number_enabled:
if dy:
self.line_number_area.scroll(0, dy)
else:
self.line_number_area.update(0, rect.y(), self.line_number_area.width(), rect.height())
if rect.contains(self.viewport().rect()):
self.UpdateLineNumberAreaWidth(0)
def HighlightCurrentLine(self):
extra = []
if not self.isReadOnly():
lineColor = QColor(Qt.yellow).lighter(160)
selection = QTextEdit.ExtraSelection()
selection.format.setBackground(lineColor)
selection.format.setProperty(QTextFormat.FullWidthSelection, True)
selection.cursor = self.textCursor()
selection.cursor.clearSelection()
extra.append(selection)
self.setExtraSelections(extra)
def test_edit():
app = QApplication(["x01.DiamondIDE"])
ed = CodeEditor()
ed.setPlainText("Hello IDE!")
ed.show()
app.exec_()
if __name__ == "__main__":
test_edit()
运行一下,OK!现在已经可以显示行号和高亮当前行了。