PyQt5桌面应用系列
- PyQt5桌面应用开发(0/22):总结再出发
- PyQt5桌面应用开发(1):需求分析
- PyQt5桌面应用开发(2):事件循环
- PyQt5桌面应用开发(3):并行设计
- PyQt5桌面应用开发(4):界面设计
- PyQt5桌面应用开发(5):对话框
- PyQt5桌面应用开发(6):文件对话框
- PyQt5桌面应用开发(7):文本编辑+语法高亮与行号
- PyQt5桌面应用开发(8):从QInputDialog转进到函数参数传递
- PyQt5桌面应用开发(9):经典布局QMainWindow
- PyQt5桌面应用开发(10):界面布局基本支持
- PyQt5桌面应用开发(11):摸鱼也要讲基本法,两个字,16
- PyQt5桌面应用开发(12):QFile与线程安全
- PyQt5桌面应用开发(13):QGraphicsView框架
- PyQt5桌面应用开发(14):数据库+ModelView+QCharts
- PyQt5桌面应用开发(15):界面动画
- PyQt5桌面应用开发(16):定制化控件-QPainter绘图
- PyQt5桌面应用开发(17):类结构+QWebEngineView
- PyQt5桌面应用开发(18):自定义控件界面设计与实现
- PyQt5桌面应用开发(19):事件过滤器
- PyQt5桌面应用开发(20):界面设计结果自动测试(一)
- PyQt5桌面应用开发(21):界面设计结果自动测试(二)
代码编辑和语法高亮的亿点点细节
接着上回的文件对话框,我们来看看代码编辑器和语法高亮的实现。文件打开和显示、文件编辑这是跟文本相关的用户界面的两个核心的功能,在严肃的桌面应用开发中,这是必不可少的。
- 用户报表,例如:显示Log文件;
- 用户交互,例如:编辑配置文件。
PyQt5提供了三个控件,继承关系如下图。
QTextEdit:支持HTML语法的控件;QTextBrowser:只读,支持链接和跳转。
QPlainTextEdit:基于纯文本的控件。
这两个控件的主要不同在与文本布局计算的方式,实际上QPlainTextEdit实现了基于行的文本布局,其文本滚动则是基于段落的。

我们想要实现代码编辑器,那就必须不考虑采用HTML语法来格式化显示的文本,就算是HTML代码编辑器,也要用纯文本!所以我们选择QPlainTextEdit。
QPlainTextEdit是一个高级的查看器/编辑器,支持纯文本。它被优化用于处理大型文档,并快速响应用户输入。 这个控件使用了和QTextEdit相同的技术和概念,但是它是为了纯文本处理而优化的。控件的文档是由段落组成的,段落是格式化的字符串,它会自动换行以适应控件的宽度。默认情况下,一个换行符表示一个段落。一个文档由零个或多个段落组成。段落由硬换行符分隔。段落中的每个字符都有自己的属性,例如字体和颜色。
鼠标光标的形状默认是Qt::IBeamCursor。可以通过viewport()的cursor属性来改变。
作为用户报表的文本控件
文本采用setPlainText()设置或替换,该函数删除现有文本并用传递给setPlainText()的文本替换它。
文本可以使用QTextCursor类或使用insertPlainText(),appendPlainText()或paste()的便利函数插入。
默认情况下,文本编辑器在空格处换行以适应文本编辑器窗体。 setLineWrapMode()函数用于指定所需的换行方式,如果不需要任何换行,则为WidgetWidth或NoWrap。如果使用单词换行到窗体宽度WidgetWidth,则可以使用setWordWrapMode()指定是否在空格处或任何位置中断。
find函数可以用于查找和选择文本中的字符串。
如果要限制QPlainTextEdit中段落的总数,例如在日志查看器中非常有用,则可以使用maximumBlockCount属性。 setMaximumBlockCount()和appendPlainText()的组合将QPlainTextEdit变为日志文本的高效查看器。 可以使用centerOnScroll()属性减少滚动,从而使日志查看器更快。 可以以有限的方式格式化文本,要么使用语法突出显示器,要么使用appendHtml()附加html格式的文本。 虽然QPlainTextEdit不支持具有表格和浮动的复杂富文本呈现,但它支持您可能需要的日志查看器中的有限基于段落的格式。
作为编辑器的文本控件
编辑器首先也是一个展示文本的控件,所以上述的内容也适用于编辑器。
选择文本由QTextCursor类处理,该类提供了创建选择,检索文本内容或删除选择的功能。 您可以使用textCursor()方法检索与用户可见光标对应的对象。 如果要在QPlainTextEdit中设置选择,只需在QTextCursor对象上创建一个选择,然后使用setCursor()将该光标设置为可见光标。 可以使用copy()将选择复制到剪贴板,或使用cut()将其剪切到剪贴板。 可以使用selectAll()选择整个文本。
QPlainTextEdit组合了一个QTextDocument对象,可以使用document()方法检索该对象。 您还可以使用setDocument()设置自己的文档对象。 如果文本更改,则QTextDocument发出textChanged()信号,它还提供了一个isModified()函数,如果自加载以来文本已被修改或自上次调用setModified(),则返回true,参数为false。 此外,它提供了撤消和重做的方法。
代码编辑器的需求
QPlainTextEdit是一个纯文本编辑/查看器;- 它提供了很强大的编辑和查看功能;
- 编辑对象由
QTextDocument类提供。
实现代码编辑器,则主要有两个方面的内容:
- 显示行号,高亮当前行;
- 语法高亮。
这都有现成的参考例子,例如我们正在编的代码,用的是IDEA,它的代码编辑器就是这样的:

代码编辑1
这个地方,就是简单的实现行号显示和高亮当前行。从图中可以看出,编辑器在编辑区域左侧的区域中显示行号。 编辑器将突出显示包含光标的行。
参考官方代码,我们实现继承自QPlainTextEdit的CodeEditor类;增加LineNumberArea类,用于显示行号。LineNumberArea类继承自QWidget,并与CodeEditor类形成组合关系,也就是作为一个成员变量。
下面就是LineNumberArea的代码,这个类与一个Editor联系在一起,当Editor的块计数变化、更新的时候,就调用这里的两个方法来计算宽度、行数,更新外观。 这里重载了QWidget.paintEvent方法,来设置相应的字体、背景,显示行数,可以看到,这里的行数从1开始计数。
painter.drawText(paint_rect, Qt.AlignRight, str(block_number + 1))
from PyQt5.QtCore import Qt
from PyQt5.QtCore import QRect
from PyQt5.QtGui import QFont, QColor, QPainter
from PyQt5.QtWidgets import QWidget
class LineNumberArea(QWidget):
def __init__(self, editor):
QWidget.__init__(self, editor)
self.editor = editor
self.editor.blockCountChanged.connect(self.update_width)
self.editor.updateRequest.connect(self.update_contents)
self.font = QFont()
self.numberBarColor = QColor("#e8e8e8")
def paintEvent(self, event):
# Override paintEvent to draw the line numbers
painter = QPainter(self)
painter.fillRect(event.rect(), self.numberBarColor)
block = self.editor.firstVisibleBlock()
# Iterate over all visible text blocks in the document.
while block.isValid():
block_number = block.blockNumber()
block_top = self.editor.blockBoundingGeometry(block).translated(self.editor.contentOffset()).top()
# Check if the position of the block is outside the visible area.
if not block.isVisible() or block_top >= event.rect().bottom():
break
# We want the line number for the selected line to be bold.
if block_number == self.editor.textCursor().blockNumber():
self.font.setBold(True)
painter.setPen(QColor("#000000"))
else:
self.font.setBold(False)
painter.setPen(QColor(

最低0.47元/天 解锁文章
1536





