You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Gtk4中序列化含格式标签的Gtk.TextView缓冲区

Gtk4 TextBuffer 自定义序列化/反序列化(含格式标签)Python示例

Gtk4的Gtk.TextBuffer确实移除了内置序列化方法,需要通过Gdk.content_register_serializer和Gdk.content_register_deserializer自定义处理逻辑。下面是一个最小化示例,实现将斜体格式序列化到纯文本(用*标记斜体内容),并能反序列化还原格式。

核心实现思路

  1. 序列化:遍历TextBuffer的文本片段,检查每个片段是否带有斜体标签,用*包裹斜体文本后写入输出流。
  2. 反序列化:读取纯文本内容,解析*标记,创建对应的斜体标签并应用到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)

关键部分说明

  1. 序列化函数_serialize_text:

    • 通过forward_to_tag_toggle定位每个格式变化的边界,分割文本片段
    • 检查片段是否带有斜体标签,添加对应的*标记
    • 将拼接后的字符串编码为UTF-8写入输出流
  2. 反序列化函数_deserialize_text:

    • 读取输入流的纯文本,解析*标记区分斜体与普通文本
    • 逐段插入TextBuffer,并给对应文本应用斜体标签
  3. 注册与调用:

    • 用Gdk.content_register_serializer/deserializer注册自定义逻辑
    • 通过Gdk.content_serialize/deserialize触发序列化/反序列化流程

你可以基于这个示例扩展更多格式(比如粗体、颜色),只需在序列化时添加对应标记,反序列化时解析并创建对应标签即可。

内容的提问来源于stack exchange,提问作者Kripto

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 23:28:10