x01.DiamondIDE
雖然一直在用 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 PyQt5.QtGui import QColor, QFont, QPaintEvent, QPainter, QSyntaxHighlighter, QTextBlock, QTextCharFormat, QTextDocument, QTextFormat
from PyQt5.QtCore import QRect, Qt, QSize
from PyQt5.QtWidgets import QMainWindow, QTextEdit, QPlainTextEdit, QApplication, QWidget
from functools import namedtuple
import re
def get_span(match, key=None):
if key is not None:
start, end = match.span(key)
else:
start, end = match.span()
start = len(match.string[:start])
end = len(match.string[:end])
return start, end
class Highlighter(QSyntaxHighlighter):
HighlightingRule = namedtuple('HighlightingRule', ['pattern', 'format'])
def __init__(self, parent: QTextDocument=None):
super().__init__(parent)
self.keywordFormat = QTextCharFormat()
self.keywordFormat.setForeground(Qt.red)
self.keywordFormat.setFontWeight(QFont.Bold)
self.keywords = r'\b' + '(?P<keyword>' + '|'.join("class int char".split()) + ')' + r'\b'
def highlightBlock(self, text:str):
patterns = re.compile(self.keywords, re.S)
match = patterns.search(text)
index = 0
while match:
for key, value in list(match.groupdict().items()):
if value:
start, end = get_span(match, key)
index += end - start
self.setFormat(start, end-start, self.keywordFormat)
match = patterns.search(text, match.end())
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()
self.highlighting = Highlighter(self.document())
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!現在已經可以顯示行號和語法高亮了,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/247059.html
標籤:Python
下一篇:20200110-正則運算式
