如何为PySide6的CodeEditor示例添加跨行大括号对代码折叠区域?
实现PySide6 CodeEditor的大括号代码折叠功能
1. 新增折叠状态存储
在原CodeEditor类中添加字典用于记录可折叠块的信息,键为大括号起始行号,值为(结束行号, 是否折叠)的元组:
class CodeEditor(QPlainTextEdit): def __init__(self, parent=None): super().__init__(parent) # 初始化折叠块存储 self.fold_blocks = {} # 其他原有初始化代码...
2. 解析代码中的大括号配对
实现代码解析函数,通过栈结构匹配{和}的对应关系,在文本变化时更新折叠块数据:
def update_fold_blocks(self): self.fold_blocks.clear() stack = [] lines = self.toPlainText().split('\n') for line_num, line in enumerate(lines): stripped_line = line.strip() if stripped_line.endswith('{'): stack.append(line_num) elif stripped_line.startswith('}') and stack: start_line = stack.pop() self.fold_blocks[start_line] = (line_num, False) # 在初始化中连接文本变化信号 self.textChanged.connect(self.update_fold_blocks)
3. 扩展行号区域的绘制逻辑
修改LineNumberArea的paintEvent方法,在可折叠块的起始行右侧绘制折叠/展开图标(-表示展开,+表示折叠):
class LineNumberArea(QWidget): # 原有初始化代码... def paintEvent(self, event): painter = QPainter(self) painter.fillRect(event.rect(), Qt.lightGray) block = self.editor.firstVisibleBlock() block_number = block.blockNumber() top = self.editor.blockBoundingGeometry(block).translated(self.editor.contentOffset()).top() bottom = top + self.editor.blockBoundingRect(block).height() while block.isValid() and top <= event.rect().bottom(): if block.isVisible() and bottom >= event.rect().top(): # 绘制行号 number = str(block_number + 1) painter.setPen(Qt.black) painter.drawText(0, top, self.width() - 15, self.editor.fontMetrics().height(), Qt.AlignRight, number) # 绘制折叠图标 if block_number in self.editor.fold_blocks: is_folded = self.editor.fold_blocks[block_number][1] icon_text = '-' if not is_folded else '+' painter.drawText(self.width() - 12, top, 10, self.editor.fontMetrics().height(), Qt.AlignCenter, icon_text) block = block.next() block_number += 1 top = bottom bottom = top + self.editor.blockBoundingRect(block).height()
同时调整行号区域宽度,给图标留出空间,修改CodeEditor的resizeEvent:
def resizeEvent(self, event): super().resizeEvent(event) cr = self.contentsRect() self.line_number_area.setGeometry(QRect(cr.left(), cr.top(), self.fontMetrics().horizontalAdvance('999') + 15, cr.height()))
4. 添加折叠/展开的交互逻辑
在LineNumberArea中重写mousePressEvent,处理图标点击事件,切换折叠状态并控制对应行的显示:
def mousePressEvent(self, event): block = self.editor.cursorForPosition(event.pos()).block() block_number = block.blockNumber() if block_number in self.editor.fold_blocks: end_line, is_folded = self.editor.fold_blocks[block_number] # 切换折叠状态 self.editor.fold_blocks[block_number] = (end_line, not is_folded) # 控制中间行的显示/隐藏 for line_num in range(block_number + 1, end_line): target_block = self.editor.document().findBlockByNumber(line_num) target_block.setVisible(not is_folded) # 更新视图 self.editor.viewport().update() self.update()
注意事项
- 上述解析逻辑仅处理单行末尾的
{和单行开头的},若需支持更复杂的语法场景(比如{在行中间),需要优化解析规则。 - 折叠后滚动视图时,可根据需求添加自动调整可见区域的逻辑。
内容的提问来源于stack exchange,提问作者garawaa garawaa
相关产品推荐
相关产品推荐

