Displaying tags correctly using TextBuffer pyGtk

145 Views Asked by At

I was playing around with pygtk to build my little project, that uses gtk, basically there is a window, in this window I have a treeview, inside the treeview a gtk.liststore.

Here is a image with the window and its values

Everything was cool until I realized that I was needing some tags, saw a lot of examples using pango, and it's work, at least until the row selected is changed.

I got around the problem with something not much elegant, and 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])

Basically this method will put <u></u> around a word when I click at the toolbar underline button, and it's value will be placed at liststore, and also display that value with textview. This would be perfect if at least set_text detected these syntaxes.

So, what I'm trying to achieve is something that display the word tagged at textview and when I change the row and get back at the previous tagged row that it still display words tagged, like, if I underline a word, that it still underline when I get back, and in case the solution involves using pango, how can I get the values from it to use later.

What I tried so far was messing around textbuffer.serialize and textbuffer.deserialized, but it didn't worked as I want.

Edit

Like here I had applied underline tag to 'paper', serialized the textbuffer, put it inside a variable, but how can I pass it back to the buffer?

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

Printing the variable 'exported' I get a byte value:

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!

Edit 2

This was probably obvious but not for me, if I have a serialized something all that I'll need to do next is just 'deserialize' it, and for that there is gtk.TextBuffer.deserialize.

The syntaxes should be something like this:

        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

The thing is that when I tried it before I was probably putting the deserialize at wrong place and with that it did nothing. Now I can track more easily where are the tags, etc. I just need run some more tests.

1

There are 1 best solutions below

0
ltsdw On BEST ANSWER

The key was create another iterator (I used a dictionary) to track the serialized text, then when I click in a row it tries to use deserialize function if the value is byte, caso is not it will simply set a text normally with set_text.

Also is important set the text to nothing set_text('') before deserialize, otherwise the previous value of the buffer will be placed in front of the current value at the buffer.

As for the changes on text, I used the method connect to connect the 'changed' signal and serialized the changes and passed the serialized value to the dictionary. And this is what I got:

        # 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

Working as I wanted :).

Edit

I adjusted my code to serialize everything at start and putting at the dictionary, instead of putting strings in the dictionary and as was editing the texts serializing the text and putting it the dictionary, with this was possible remove some if/else's and try/except's.

Also I created functions to serialize and deserialize thing, and put these functions in another file, I think this way is better.

  • 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 )
...