如何在Gtk4中序列化含格式标签的Gtk.TextView缓冲区
Gtk4 TextBuffer 自定义序列化/反序列化(含格式标签)Python示例
Gtk4的Gtk.TextBuffer确实移除了内置序列化方法,需要通过Gdk.content_register_serializer和Gdk.content_register_deserializer自定义处理逻辑。下面是一个最小化示例,实现将斜体格式序列化到纯文本(用*标记斜体内容),并能反序列化还原格式。
核心实现思路
- 序列化:遍历TextBuffer的文本片段,检查每个片段是否带有斜体标签,用
*包裹斜体文本后写入输出流。 - 反序列化:读取纯文本内容,解析
*标记,创建对应的斜体标签并应用到TextBuffer的对应位置。
完整代码示例
import gi gi.require_version('Gtk', '4.0') gi.require_version('Gdk', '4.0') from gi.repository import Gtk, Gdk, Gio class TextEditorWindow(Gtk.ApplicationWindow): def __init__(self, app): super().__init__(application=app, title="Format Serializer Demo") self.set_default_size(600, 400) # 创建TextView和TextBuffer self.text_view = Gtk.TextView() self.buffer = self.text_view.get_buffer() # 添加斜体标签 self.italic_tag = self.buffer.create_tag("italic", style=Gtk.Style.ITALIC) # 注册序列化和反序列化器 self._register_serializers() # 布局 scrolled_window = Gtk.ScrolledWindow() scrolled_window.set_child(self.text_view) self.set_child(scrolled_window) # 测试按钮组 save_btn = Gtk.Button(label="Save to File") save_btn.connect("clicked", self._save_to_file) load_btn = Gtk.Button(label="Load from File") load_btn.connect("clicked", self._load_from_file) box = Gtk.Box(orientation=Gtk.Orientation.HORIZONTAL, spacing=10) box.append(save_btn) box.append(load_btn) box.set_margin_top(10) box.set_margin_start(10) box.set_margin_end(10) self.set_titlebar(box) def _register_serializers(self): # 注册text/plain格式的序列化器 Gdk.content_register_serializer( Gtk.TextBuffer, "text/plain;charset=utf-8", self._serialize_text, None, None ) # 注册对应的反序列化器 Gdk.content_register_deserializer( "text/plain;charset=utf-8", Gtk.TextBuffer, self._deserialize_text, None, None ) def _serialize_text(self, buffer, mime_type, stream, user_data, cancellable): """GdkContentSerializeFunc实现:将斜体内容序列化为带*标记的纯文本""" start_iter = buffer.get_start_iter() end_iter = buffer.get_end_iter() current_iter = start_iter.copy() output = [] while not current_iter.is_end(): # 获取当前片段的标签集合 tags = current_iter.get_tags() is_italic = any(tag == self.italic_tag for tag in tags) # 定位到下一个格式变化的位置 next_iter = current_iter.copy() if not next_iter.forward_to_tag_toggle(None): next_iter = end_iter.copy() # 提取文本并添加格式标记 text = buffer.get_text(current_iter, next_iter, False) output.append(f"*{text}*" if is_italic else text) current_iter = next_iter # 将结果写入输出流 data = "".join(output).encode("utf-8") stream.write_all(data, cancellable) return True def _deserialize_text(self, mime_type, stream, buffer, user_data, cancellable): """GdkContentDeserializeFunc实现:解析*标记还原斜体格式""" # 读取流中数据 data = stream.read_bytes(-1, cancellable).get_data().decode("utf-8") buffer.set_text("") in_italic = False current_pos = 0 # 遍历解析*标记 for i, char in enumerate(data): if char == "*": # 写入当前区间的文本 text = data[current_pos:i] if text: end_iter = buffer.get_end_iter() buffer.insert(end_iter, text) # 给对应文本应用斜体标签 if in_italic: start = buffer.get_iter_at_offset(buffer.get_char_count() - len(text)) buffer.apply_tag(self.italic_tag, start, end_iter) # 切换斜体状态 in_italic = not in_italic current_pos = i + 1 # 处理最后一段未闭合的文本 if current_pos < len(data): text = data[current_pos:] end_iter = buffer.get_end_iter() buffer.insert(end_iter, text) if in_italic: start = buffer.get_iter_at_offset(buffer.get_char_count() - len(text)) buffer.apply_tag(self.italic_tag, start, end_iter) return True def _save_to_file(self, btn): """测试序列化:保存到本地文件""" file_chooser = Gtk.FileChooserNative( title="Save File", parent=self, action=Gtk.FileChooserAction.SAVE ) if file_chooser.run() == Gtk.ResponseType.ACCEPT: file = file_chooser.get_file() stream = file.create_write_stream(Gio.FileCreateFlags.NONE, None) # 触发自定义序列化逻辑 success = Gdk.content_serialize( stream, "text/plain;charset=utf-8", self.buffer, None ) print("保存成功" if success else "保存失败") stream.close(None) file_chooser.destroy() def _load_from_file(self, btn): """测试反序列化:从本地文件加载""" file_chooser = Gtk.FileChooserNative( title="Load File", parent=self, action=Gtk.FileChooserAction.OPEN ) if file_chooser.run() == Gtk.ResponseType.ACCEPT: file = file_chooser.get_file() stream = file.read(None) # 触发自定义反序列化逻辑 success = Gdk.content_deserialize( stream, "text/plain;charset=utf-8", self.buffer, None ) print("加载成功" if success else "加载失败") stream.close(None) file_chooser.destroy() class TextEditorApp(Gtk.Application): def __init__(self): super().__init__(application_id="com.example.TextEditor") def do_activate(self): win = TextEditorWindow(self) win.present() if __name__ == "__main__": app = TextEditorApp() app.run(None)
关键部分说明
序列化函数
_serialize_text:- 通过
forward_to_tag_toggle定位每个格式变化的边界,分割文本片段 - 检查片段是否带有斜体标签,添加对应的
*标记 - 将拼接后的字符串编码为UTF-8写入输出流
- 通过
反序列化函数
_deserialize_text:- 读取输入流的纯文本,解析
*标记区分斜体与普通文本 - 逐段插入TextBuffer,并给对应文本应用斜体标签
- 读取输入流的纯文本,解析
注册与调用:
- 用
Gdk.content_register_serializer/deserializer注册自定义逻辑 - 通过
Gdk.content_serialize/deserialize触发序列化/反序列化流程
- 用
你可以基于这个示例扩展更多格式(比如粗体、颜色),只需在序列化时添加对应标记,反序列化时解析并创建对应标签即可。
内容的提问来源于stack exchange,提问作者Kripto
相关产品推荐
相关产品推荐

