python - Gtk3 TextBuffer.serialize() 返回带有格式标签的文本,即使视觉上没有

标签 python formatting gtk gtk3

我在我的项目中使用 Gtk TextView/TextBuffer,用户可以通过选择正确的切换按钮来输入富文本(粗体/斜体/下划线)。

问题是,如果我将下划线或斜体 Pango 标志应用于 TextView 中的文本,然后关闭斜体/下划线并输入更多内容,然后通过 TextBuffer.serialize() 获取带有这些标志的文本,返回未格式化的文本(在 TextView 中明显未格式化),并在其周围带有下划线/斜体标签。

您可以在此处看到:(注意,为了便于阅读,我使用 BeautifulSoup 将标签简化为对应的 HTML,但根本没有编辑实际位置/类型。)

这是代码(需要安装 Gtk3 和 BS4 才能安装 Python3):

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk, Gdk, Pango

import smtplib, mimetypes
from bs4 import BeautifulSoup

class Handler():

    def __init__(self):
        global html
        self.state = 0

    def onDeleteWindow(self, *args):
        Gtk.main_quit(*args)

    def onSendClicked(self, button):
        start, end = textBodyBuffer.get_bounds()
        self.content = textBodyBuffer.get_text(start, end, True)

        # Below is the serialization code for exporting with format tags
        format = textBodyBuffer.register_serialize_tagset() 
        exported = textBodyBuffer.serialize(textBodyBuffer, format, start, end)

        exported = exported.decode("latin-1")

        exported = exported.split('<text_view_markup>', 1)
        del exported[0]
        exported[0] = '<text_view_markup>' + str(exported[0])

        exported = exported[0].split('</tags>', 1)
        del exported[0]

        exported = exported[0].split('</text_view_markup>', 1)
        exported = str(exported[0]).replace('\n', ' ')

        soup = BeautifulSoup(exported)

        soupTags = soup.find_all('apply_tag')

        for tag in soupTags:

            if tag['name'] == 'bold':
                tag.name = 'b'
                del tag['name']
            elif tag['name'] == 'italic':
                tag.name = 'em'
                del tag['name']
            elif tag['name'] == 'underline':
                tag.name = 'u'
                del tag['name']

        print (soup)

    def bold(self, button):
        global tags_on
        name = button.get_name()
        if button.get_active():             # Button is "down"/enabled
            tags_on.append('bold')
        elif button.get_active() != True:   # Button is "up"/disabled
            del tags_on[tags_on.index('bold')]

    def italic(self, button):
        global tags_on
        name = button.get_name()
        if button.get_active():             # Button is "down"/enabled
            tags_on.append('italic')
        elif button.get_active() != True:   # Button is "up"/disabled
            del tags_on[tags_on.index('italic')]

    def underline(self, button):
        global tags_on
        name = button.get_name()
        if button.get_active():             # Button is "down"/enabled
            tags_on.append('underline')
        elif button.get_active() != True:   # Button is "up"/disabled
            del tags_on[tags_on.index('underline')]

    def alignToggled(self, radiobutton):
        pass

    def undo(self, button):
        pass

    def redo(self, button):
        pass

    def keyHandler(self, widget, event):
        global html
        if Gdk.ModifierType.CONTROL_MASK & event.state:
            if Gdk.keyval_name(event.keyval) == 'q':    # Quit the program
                w.destroy()
                Gtk.main_quit()

def get_iter_position(buffer):
    return buffer.get_iter_at_mark(buffer.get_insert())

def text_inserted(buffer, iter, char, length):

    global tags_on

    if len(tags_on) >= 0:
        iter.backward_chars(length)

        for tag in tags_on:
            w.queue_draw()
            if tag == 'bold':
                buffer.apply_tag(tag_bold, get_iter_position(buffer), iter)
            elif tag == 'italic':
                buffer.apply_tag(tag_italic, get_iter_position(buffer), iter)
            elif tag == 'underline':
                buffer.apply_tag(tag_underline, get_iter_position(buffer), iter)


if __name__ == '__main__':

    global text, html
    # Gtk tag globals
    global tag_bold, tag_italic, tag_underline, tags_on

    tags_on = []

    text = ''
    html = '<html><body><p>'

    builder = Gtk.Builder()
    builder.add_from_file('editor.glade')
    builder.connect_signals(Handler())

    buttonSend = builder.get_object('buttonSend')

    textBody = builder.get_object('textviewBody')
    textBodyBuffer = textBody.get_buffer()

    textBodyBuffer.connect_after('insert-text', text_inserted)
    tag_bold = textBodyBuffer.create_tag("bold", weight=Pango.Weight.BOLD)
    tag_italic = textBodyBuffer.create_tag("italic", style=Pango.Style.ITALIC)
    tag_underline = textBodyBuffer.create_tag("underline", underline=Pango.Underline.SINGLE)

    w = builder.get_object('window1')
    w.show_all()

    Gtk.main()

这是 editor.glade 文件:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.16.1 -->
<interface>
  <requires lib="gtk+" version="3.10"/>
  <object class="GtkImage" id="image1">
    <property name="visible">True</property>
    <property name="can_focus">False</property>
    <property name="icon_name">mail-send</property>
  </object>
  <object class="GtkWindow" id="window1">
    <property name="can_focus">False</property>
    <property name="title" translatable="yes">Keyboard Mail - Edit Message</property>
    <property name="modal">True</property>
    <property name="type_hint">dialog</property>
    <signal name="delete-event" handler="onDeleteWindow" swapped="no"/>
    <signal name="key-press-event" handler="keyHandler" swapped="no"/>
    <child>
      <object class="GtkBox" id="box2">
        <property name="visible">True</property>
        <property name="can_focus">False</property>
        <property name="orientation">vertical</property>
        <child>
          <object class="GtkBox" id="box1">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="orientation">vertical</property>
            <child>
              <object class="GtkButton" id="buttonSend">
                <property name="label" translatable="yes">Send</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="receives_default">True</property>
                <property name="image">image1</property>
                <property name="relief">none</property>
                <property name="image_position">top</property>
                <property name="always_show_image">True</property>
                <signal name="clicked" handler="onSendClicked" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="fill">False</property>
                <property name="position">1</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">False</property>
            <property name="position">1</property>
          </packing>
        </child>
        <child>
          <object class="GtkToolbar" id="toolbar1">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="toolbar_style">icons</property>
            <property name="show_arrow">False</property>
            <child>
              <object class="GtkToggleToolButton" id="buttonBold">
                <property name="name">bold</property>
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="tooltip_text" translatable="yes">Make the selected text bold</property>
                <property name="icon_name">format-text-bold</property>
                <signal name="toggled" handler="bold" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="homogeneous">True</property>
              </packing>
            </child>
            <child>
              <object class="GtkToggleToolButton" id="buttonItalic">
                <property name="name">italic</property>
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="icon_name">format-text-italic</property>
                <signal name="toggled" handler="italic" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="homogeneous">True</property>
              </packing>
            </child>
            <child>
              <object class="GtkToggleToolButton" id="buttonUnderline">
                <property name="name">underline</property>
                <property name="visible">True</property>
                <property name="can_focus">False</property>
                <property name="icon_name">format-text-underline</property>
                <signal name="toggled" handler="underline" swapped="no"/>
              </object>
              <packing>
                <property name="expand">False</property>
                <property name="homogeneous">True</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">3</property>
          </packing>
        </child>
        <child>
          <object class="GtkBox" id="box5">
            <property name="visible">True</property>
            <property name="can_focus">False</property>
            <property name="orientation">vertical</property>
            <child>
              <object class="GtkTextView" id="textviewBody">
                <property name="width_request">500</property>
                <property name="height_request">100</property>
                <property name="visible">True</property>
                <property name="can_focus">True</property>
                <property name="hexpand">True</property>
                <property name="vexpand">True</property>
                <property name="wrap_mode">word</property>
                <property name="left_margin">5</property>
                <property name="right_margin">5</property>
              </object>
              <packing>
                <property name="expand">True</property>
                <property name="fill">True</property>
                <property name="position">0</property>
              </packing>
            </child>
          </object>
          <packing>
            <property name="expand">False</property>
            <property name="fill">True</property>
            <property name="position">3</property>
          </packing>
        </child>
      </object>
    </child>
  </object>
</interface>

有谁知道为什么 TextBuffer.serialize() 语句(第 23 行)在下划线/斜体标签中返回可见的非格式化字符?

发生这种情况时我似乎找不到任何模式,它似乎随机决定是否返回带有标签的文本。

编辑:我已经使用 pdb(Python 调试器)逐步检查了代码,但仍然没有看到任何会导致此问题的内容。

编辑:我发现了一个有趣的模式 - 如果你同时打开斜体和下划线,然后输入一些,然后同时关闭它们,然后输入 serialize () 调用返回正确的字符串。

但是,如果您一次应用一个,为每个新标签键入一点,则返回错误。

最佳答案

我已经能够用 minimal example 重现问题。在 C 中。因此,我强烈怀疑这是 GTK 中的错误。提问者因此opened a bugreport upstream .

作为一种解决方法,我建议您尝试自己实现序列化。 forward_to_tag_toggle应该对此有所帮助。请注意,您不能使用使用 register_serialize_format 的明显路径。注册您自己的序列化程序,然后调用序列化,因为虽然 GtkTextBufferSerializeFunc应该返回一个字符串,在 GObject 存储库中 it is apparently recorded as a function returning a single integer . (由于另一个错误。)相反,完全从您的代码中进行序列化,即获取一个开始迭代器,然后遍历文本缓冲区。

关于python - Gtk3 TextBuffer.serialize() 返回带有格式标签的文本,即使视觉上没有,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30387247/

相关文章:

python - 如何解决 "azure.common.AzureHttpError: This request is not authorized to perform this operation. ErrorCode: AuthorizationFailure"

python - 您如何保护自己免受可能无限期挂起的不稳定的库调用的影响?

c# - 特殊小数格式

c# - LINQ to SQL - 保存前格式化字符串?

c - 如何使用 C 中的 GStreamer 连接到 UDP 视频广播

css - 如何在 GtkTreeView 中交替亮/暗行?

c - 除非光标位于滚动条上方,否则 GtkScrolledWindow 不响应鼠标滚轮

python - 为什么看不到回归线?

python - 如何从数据框中找到句子中单词和字符的最大数量?

python - 从 lxml.objectify.ObjectifiedElement 中剥离 python 命名空间属性