使用 TextBuffer pyGtk 正确显示标签

Displaying tags correctly using TextBuffer pyGtk

我正在玩 pygtk 来构建我的小项目,它使用 gtk,基本上有一个 window,在这个 window 我有一个树视图,在树视图里面有一个 gtk.liststore.

Here is a image with the window and its values

一切都很顺利,直到我意识到我需要一些标签,看到了很多使用 pango 的示例,并且它起作用了,至少在更改所选行之前是这样。

我用不太优雅的东西解决了这个问题,并且 here a link to the complete code:

    def on_toolbar_button_clicked(self, widget, tag):
        bounds_front    = self.text_buffer_front.get_selection_bounds()
        bounds_back     = self.text_buffer_back.get_selection_bounds()

        if len(bounds_front) != 0:

            (start, end)        = bounds_front
            selection_front      = self.text_buffer_front.get_text(start, end, True)
            get_insert_front     = self.text_buffer_front.get_insert()

            self.text_buffer_front.delete(start, end)

            iter_front           = self.text_buffer_front.get_iter_at_mark(get_insert_front)
            self.text_buffer_front.insert(iter_front, tag[0] + selection_front + tag[1])

基本上,当我单击工具栏下划线按钮时,此方法会将 放在单词周围,它的值将放在列表存储中,并使用 textview 显示该值。如果至少 set_text 检测到这些语法,这将是完美的。

所以,我想要实现的是在 textview 中显示标记的单词,当我更改行并返回到前一个标记行时,它仍然显示标记的单词,比如,如果我在 a 下划线单词,当我回来时它仍然强调,如果解决方案涉及使用 pango,我如何从中获取值以供以后使用。

到目前为止我尝试的是搞乱 textbuffer.serialize 和 textbuffer.deserialized,但它没有像我想要的那样工作。

编辑

像这里一样,我在 'paper' 上应用了下划线标记,序列化了文本缓冲区,将它放在一个变量中,但是我怎样才能将它传递回缓冲区?

exported = self.text_buffer_front.serialize( self.text_buffer_front, format, start_iter_front, end_iter_front )

打印变量 'exported' 我得到一个字节值:

b'GTKTEXTBUFFERCONTENTS-0001\x00\x00\x00w <text_view_markup>\n <tags>\n </tags>\n<text>A
At the first comes rock!  Rock, <apply_tag name="underline">paper</apply_tag>, scissors!

编辑 2

这可能是显而易见的,但对我来说不是,如果我有一个序列化的东西,我接下来需要做的就是 'deserialize' 它,为此有 gtk.TextBuffer.deserialize

语法应该是这样的:

        self.dict_any_tags = {str(key): value[1] for key, value in enumerate(self.sub_list_store)}

    def item_selected(self, *args):
        try:
            iter_start_front        = self.text_buffer_front.get_start_iter()
            iter_end_front          = self.text_buffer_front.get_end_iter()
            path                    = self.selected_row.get_selected_rows()[1][0]

            try:
                self.text_buffer_front.deserialize(self.text_buffer_front, self.text_buffer_front.register_deserialize_tagset(), self.text_buffer_front.get_start_iter(), self.dict_any_tags[str(path)])
            except:
                self.text_buffer_front.set_text(self.sub_list_store[path][1])
        except IndexError:
            pass

    def on_toolbar_button_clicked(self, widget, tag):
        bounds_front    = self.text_buffer_front.get_selection_bounds()
        bounds_back     = self.text_buffer_back.get_selection_bounds()
        path            = self.selected_row.get_selected_rows()[1][0]

        if len(bounds_front) != 0:

            (start, end)        = bounds_front
            selection_front     = self.text_buffer_front.get_text(start, end, True)
            get_insert_front    = self.text_buffer_front.get_insert()

            self.text_buffer_front.apply_tag(tag, start, end)

            start_iter_front    = self.text_buffer_front.get_start_iter()
            end_iter_front      = self.text_buffer_front.get_end_iter()
            format              = self.text_buffer_front.register_serialize_tagset()
            exported            = self.text_buffer_front.serialize( self.text_buffer_front,
                                                                    format,
                                                                    start_iter_front,
                                                                    end_iter_front)

            self.dict_any_tags[str(path)] = exported

问题是,当我之前尝试它时,我可能将反序列化放在错误的位置并且它什么也没做。现在我可以更轻松地跟踪标签在哪里等等。我只需要 运行 一些更多的测试。

关键是创建另一个迭代器(我使用字典)来跟踪序列化文本,然后当我连续单击时,如果值为字节,它会尝试使用反序列化函数,因为它不会简单地设置一个通常使用 set_text.

的文本

在反序列化之前将文本设置为空 set_text('') 也很重要,否则缓冲区的先前值将放在缓冲区当前值的前面。

至于文本的变化,我使用connect方法连接'changed'信号并将变化序列化并将序列化值传递给字典。这就是我得到的:

        # dictionary to track the tags
        self.dict_any_change_front  = {str(key): value[1] for key, value in enumerate(self.sub_list_store)}
        self.dict_any_change_back   = {str(key): value[1] for key, value in enumerate(self.sub_list_store_back)}

    def deserialize(self, text_buffer, exported):
        text_buffer.set_text('')
        text_buffer.deserialize( text_buffer,
                                 text_buffer.register_deserialize_tagset(),
                                 text_buffer.get_start_iter(),
                                 exported )

    def item_selected(self, *args):
        #   Need this try/except to silent a indexerror that will occur case the second window close and if opened again,
        # merely cosmetic as it will always occur, just select any row and all good.
        #   The get_selected_rows()[1] will return a empty list at first try when reopening the second window, I just don't know why

        try:
            path                    = self.selected_row.get_selected_rows()[1][0]

            exported_front          = self.dict_any_change_front[str(path)]
            exported_back           = self.dict_any_change_back[str(path)]

            try:
                if isinstance(exported_front, bytes):
                    self.deserialize(self.text_buffer_front, exported_front)
                else:
                    self.text_buffer_front.set_text(self.sub_list_store[path][1])
                if isinstance(exported_back, bytes):
                    self.deserialize(self.text_buffer_back, exported_back)
                else:
                    self.text_buffer_back.set_text(self.sub_list_store_back[path][1])
            except:
                self.text_buffer_front.set_text(self.sub_list_store[path][1])
                self.text_buffer_back.set_text(self.sub_list_store_back[path][1])

            self.text_buffer_front.connect('changed', self.editingCard)
            self.text_buffer_back.connect('changed', self.editingCardBack)

        except IndexError:
            pass

    def editingCard(self, text_buffer):
        path                                = self.selected_row.get_selected_rows()[1][0]
        start_iter_front                    = text_buffer.get_start_iter()
        end_iter_front                      = text_buffer.get_end_iter() 

        self.sub_list_store[path][1]        = text_buffer.get_text(start_iter_front, end_iter_front, True)

        format                              = text_buffer.register_serialize_tagset()
        exported                            = text_buffer.serialize(    text_buffer,
                                                                        format,
                                                                        start_iter_front,
                                                                        end_iter_front )
        
        self.dict_any_change_front[str(path)] = exported

    def editingCardBack(self, text_buffer):
        path                                = self.selected_row.get_selected_rows()[1][0]
        start_iter_back                     = text_buffer.get_start_iter()
        end_iter_back                       = text_buffer.get_end_iter() 

        self.sub_list_store_back[path][1]   = text_buffer.get_text(start_iter_back, end_iter_back, True)

        format              = text_buffer.register_serialize_tagset()
        exported            = text_buffer.serialize(    text_buffer,
                                                        format,
                                                        start_iter_back,
                                                        end_iter_back   )
        self.dict_any_change_back[str(path)] = exported

    def on_toolbar_button_clicked(self, widget, tag_front, tag_back):
        bounds_front    = self.text_buffer_front.get_selection_bounds()
        bounds_back     = self.text_buffer_back.get_selection_bounds()
        path            = self.selected_row.get_selected_rows()[1][0]

        ##### FRONT
        if len(bounds_front) != 0:
            (start, end)        = bounds_front
            selection_front     = self.text_buffer_front.get_text(start, end, True)
            get_insert_front    = self.text_buffer_front.get_insert()

            self.text_buffer_front.apply_tag(tag_front, start, end)

            start_iter_front    = self.text_buffer_front.get_start_iter()
            end_iter_front      = self.text_buffer_front.get_end_iter()
            format              = self.text_buffer_front.register_serialize_tagset()
            exported            = self.text_buffer_front.serialize( self.text_buffer_front,
                                                                    format,
                                                                    start_iter_front,
                                                                    end_iter_front )
            self.dict_any_change_front[str(path)] = exported


        ###### BACK
        if len(bounds_back) != 0:
            (start, end)        = bounds_back
            selection_back      = self.text_buffer_back.get_text(start, end, True)
            get_insert_back     = self.text_buffer_back.get_insert()

            self.text_buffer_back.apply_tag(tag_back, start, end)

            start_iter_back     = self.text_buffer_back.get_start_iter()
            end_iter_back       = self.text_buffer_back.get_end_iter()
            format              = self.text_buffer_back.register_serialize_tagset()
            exported            = self.text_buffer_back.serialize(  self.text_buffer_back,
                                                                    format,
                                                                    start_iter_back,
                                                                    end_iter_back )
            self.dict_any_change_back[str(path)] = exported

随心所欲地工作:)。

编辑

我调整了我的代码以在开始时序列化所有内容并放入字典,而不是将字符串放入字典,就像编辑文本序列化文本并将其放入字典一样,这样可以删除一些 if/else 和 try/except。

我还创建了序列化和反序列化函数,并将这些函数放在另一个文件中,我认为这种方式更好。

  • myhandlerfile.py:
...

from myfuncfile import serializeIt, deserializeIt

...
        # dictionary to track the tags
        self.dict_any_change_front  =   {str(key): serializeIt(text_buffer=self.text_buffer_front, tmp_string=value[1]) \
                                        for key, value in enumerate(self.sub_list_store)}

        self.dict_any_change_back   =   {str(key): serializeIt(text_buffer=self.text_buffer_back, tmp_string=value[1]) \
                                        for key, value in enumerate(self.sub_list_store_back)}

    def item_selected(self, *args):
        #   Silencing a indexerror that will occur in case the window was hided and rised again
        # it is not important, can be ignored

        try:
            path                    = self.selected_row.get_selected_rows()[1][0]

            exported_front          = self.dict_any_change_front[str(path)]
            exported_back           = self.dict_any_change_back[str(path)]

            deserializeIt(self.text_buffer_front, exported_front)
            deserializeIt(self.text_buffer_back, exported_back)

            self.text_buffer_front.connect('changed', self.editingCard)
            self.text_buffer_back.connect('changed', self.editingCardBack)

        except IndexError:
            pass

    def editingCard(self, text_buffer_front):
        #   Silencing a indexerror that will occur in case the window was hided and rised again
        # it is not important, can be ignored
        try:
            path                                    = self.selected_row.get_selected_rows()[1][0]
            start_iter_front                        = text_buffer_front.get_start_iter()
            end_iter_front                          = text_buffer_front.get_end_iter() 

            self.sub_list_store[path][1]            = text_buffer_front.get_text(start_iter_front, end_iter_front, True)

            exported                                = serializeIt(text_buffer=text_buffer_front)
            self.dict_any_change_front[str(path)]   = exported
        except IndexError:
            pass

    def editingCardBack(self, text_buffer_back):
        #   Silencing a indexerror that will occur in case the window was hided and rised again
        # it is not important, can be ignored
        try:
            path                                    = self.selected_row.get_selected_rows()[1][0]
            start_iter_back                         = text_buffer_back.get_start_iter()
            end_iter_back                           = text_buffer_back.get_end_iter() 

            self.sub_list_store_back[path][1]       = text_buffer_back.get_text(start_iter_back, end_iter_back, True)

            exported                                = serializeIt(text_buffer=text_buffer_back)
            self.dict_any_change_back[str(path)]    = exported

        except IndexError:
            pass

    def on_toolbar_button_clicked(self, widget, tag_front, tag_back):
        bounds_front    = self.text_buffer_front.get_selection_bounds()
        bounds_back     = self.text_buffer_back.get_selection_bounds()
        path            = self.selected_row.get_selected_rows()[1][0]

        ##### FRONT
        if len(bounds_front) != 0:
            (start, end)        = bounds_front
            selection_front     = self.text_buffer_front.get_text(start, end, True)
            get_insert_front    = self.text_buffer_front.get_insert()

            self.text_buffer_front.apply_tag(tag_front, start, end)

            exported                                = serializeIt(text_buffer=self.text_buffer_front)
            self.dict_any_change_front[str(path)]   = exported


        ###### BACK
        if len(bounds_back) != 0:
            (start, end)        = bounds_back
            selection_back      = self.text_buffer_back.get_text(start, end, True)
            get_insert_back     = self.text_buffer_back.get_insert()

            self.text_buffer_back.apply_tag(tag_back, start, end)

            exported                                = serializeIt(text_buffer=self.text_buffer_back)
            self.dict_any_change_back[str(path)]    = exported
...
  • myfuncfile.py:
...

def serializeIt(text_buffer, tmp_string=None):
    if tmp_string:
        text_buffer.set_text(tmp_string)
        tmp_start_iter  = text_buffer.get_start_iter()
        tmp_end_iter    = text_buffer.get_end_iter()
        tmp_format      = text_buffer.register_serialize_tagset()
        tmp_exported    = text_buffer.serialize( text_buffer,
                                                 tmp_format,
                                                 tmp_start_iter,
                                                 tmp_end_iter )
        return tmp_exported
    else:
        start_iter  = text_buffer.get_start_iter()
        end_iter    = text_buffer.get_end_iter()
        format      = text_buffer.register_serialize_tagset()
        exported    = text_buffer.serialize( text_buffer,
                                             format,
                                             start_iter,
                                             end_iter )
        return exported

def deserializeIt(text_buffer, exported):
    text_buffer.set_text('')
    text_buffer.deserialize(text_buffer,
                            text_buffer.register_deserialize_tagset(),
                            text_buffer.get_start_iter(),
                            exported )
...