x01.DiamondIDE:  hello ide[Python基础]

虽然一直在用 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!现在已经可以显示行号和高亮当前行了。

hmoban主题是根据ripro二开的主题,极致后台体验,无插件,集成会员系统
自学咖网 » x01.DiamondIDE: hello ide