为Gtk.TextView添加斜体标签时Python程序崩溃问题排查
问题:Gtk.TextView Markdown斜体格式化程序崩溃
我正在编写一个基于Gtk.TextView的Markdown格式化工具,目标是读取文本并为Markdown标记分配对应格式标签,但每当添加斜体标记* *对时,程序就会崩溃,且输入任何内容都会触发崩溃。以下是测试代码:
# Imports import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk, Pango class MarkdownFormatter: def __init__(self, textview): self.textview = textview self.text_buffer = self.textview.get_buffer() self.text_buffer.connect('insert-text', self.on_key_insert) self.tag_bold = self.text_buffer.create_tag('bold',weight=Pango.Weight.BOLD) self.tag_italic = self.text_buffer.create_tag('italic',style=Pango.Style.ITALIC, ) self.tag_underline = self.text_buffer.create_tag('underline',underline=Pango.Underline.SINGLE) self.bold= False self.italic = False self.italic_start_iter = Gtk.TextIter() self.italic_end_iter = Gtk.TextIter() self.underline = False def on_key_insert(self, buffer, inter, text, iteg): end= self.text_buffer.get_end_iter() start= self.text_buffer.get_start_iter() all_text = self.text_buffer.get_text(end, start, True) self.text_buffer.remove_all_tags(start, end) count = 0 self.italic = False for d in all_text: count+= 1 if '*' in d: if self.italic == False: self.italic = True self.italic_start_iter.set_offset(count) elif self.italic: self.italic = False self.italic_end_iter.set_offset(count) self.text_buffer.apply_tag(self.tag_italic, self.italic_start_iter, self.italic_end_iter) text = ''' *I is good.* she is smart. "Hi" he said. ''' if __name__ == '__main__': def quit(*args): Gtk.main_quit() window = Gtk.Window.new(Gtk.WindowType.TOPLEVEL) window.set_title('Markdown formatter') view = Gtk.TextView() view.get_buffer().set_text(text) formater = MarkdownFormatter(view) window.set_default_size(600, 400) window.add(view) window.show_all() window.connect('delete-event', quit) Gtk.main()
问题分析与修复
核心错误点
- 文本获取顺序错误:
get_text(start, end)的参数顺序是起始迭代器在前、结束迭代器在后,原代码写反后获取的是反向文本,后续处理全部失效。 - 迭代器使用不当:复用全局迭代器对象,文本内容变化后旧迭代器会失效,直接引发崩溃。
- Offset计数错误:TextIter的offset从0开始,但代码中count从1开始,导致标签应用位置偏移。
- 无限递归触发:在
insert-text信号处理中修改缓冲区,会再次触发该信号,形成无限递归最终导致程序崩溃。
修复后的代码
import gi gi.require_version('Gtk', '3.0') from gi.repository import Gtk, Pango class MarkdownFormatter: def __init__(self, textview): self.textview = textview self.text_buffer = self.textview.get_buffer() self.text_buffer.connect('insert-text', self.on_key_insert) self.text_buffer.connect('delete-range', self.on_text_delete) self.tag_bold = self.text_buffer.create_tag('bold', weight=Pango.Weight.BOLD) self.tag_italic = self.text_buffer.create_tag('italic', style=Pango.Style.ITALIC) self.tag_underline = self.text_buffer.create_tag('underline', underline=Pango.Underline.SINGLE) def format_text(self): # 先断开信号避免递归触发 self.text_buffer.handler_block_by_func(self.on_key_insert) self.text_buffer.handler_block_by_func(self.on_text_delete) start = self.text_buffer.get_start_iter() end = self.text_buffer.get_end_iter() all_text = self.text_buffer.get_text(start, end, True) self.text_buffer.remove_all_tags(start, end) # 收集所有*的位置,成对处理 asterisk_positions = [i for i, char in enumerate(all_text) if char == '*'] for i in range(0, len(asterisk_positions), 2): if i + 1 >= len(asterisk_positions): break # 忽略未闭合的* start_idx = asterisk_positions[i] end_idx = asterisk_positions[i+1] # 获取当前有效的迭代器 start_iter = self.text_buffer.get_iter_at_offset(start_idx) end_iter = self.text_buffer.get_iter_at_offset(end_idx) # 跳过*本身,只格式化中间内容 start_iter.forward_char() self.text_buffer.apply_tag(self.tag_italic, start_iter, end_iter) # 重新连接信号 self.text_buffer.handler_unblock_by_func(self.on_key_insert) self.text_buffer.handler_unblock_by_func(self.on_text_delete) def on_key_insert(self, buffer, iter, text, length): # 延迟格式化,避免插入过程中修改缓冲区 Gtk.idle_add(self.format_text) def on_text_delete(self, buffer, start, end): # 删除文本时也触发格式化 Gtk.idle_add(self.format_text) text = ''' *I is good.* she is smart. "Hi" he said. ''' if __name__ == '__main__': def quit(*args): Gtk.main_quit() window = Gtk.Window.new(Gtk.WindowType.TOPLEVEL) window.set_title('Markdown formatter') view = Gtk.TextView() view.get_buffer().set_text(text) formatter = MarkdownFormatter(view) # 初始化格式化现有文本 formatter.format_text() window.set_default_size(600, 400) window.add(view) window.show_all() window.connect('delete-event', quit) Gtk.main()
关键修复说明
- 使用
Gtk.idle_add()延迟格式化操作,避免在信号处理中直接修改缓冲区引发递归。 - 处理文本时先断开信号,完成后重新连接,彻底杜绝无限递归。
- 先收集所有
*的位置,成对处理并忽略未闭合标记,避免无效操作。 - 使用
get_iter_at_offset()获取最新有效迭代器,不再复用旧迭代器。 - 修正文本获取顺序,确保获取正向文本。
内容的提问来源于stack exchange,提问作者ShadowOfHassen
相关产品推荐
相关产品推荐

