The Gtk.TextView widget can be used to display and edit large amounts
of formatted text. Like the Gtk.TreeView, it has a model/view design.
In this case the Gtk.TextBuffer is the model which represents the text
being edited. This allows two or more Gtk.TextView widgets to share the
same Gtk.TextBuffer, and allows those text buffers to be displayed
slightly differently. Or you could maintain several text buffers and choose to
display each one at different times in the same Gtk.TextView widget.

Another default setting of the Gtk.TextView widget is long lines of
text will continue horizontally until a break is entered. To wrap the text and
prevent it going off the edges of the screen call Gtk.TextView.set_wrap_mode().

The Gtk.TextBuffer is the core of the Gtk.TextView widget, and
is used to hold whatever text is being displayed in the Gtk.TextView.
Setting and retrieving the contents is possible with Gtk.TextBuffer.set_text()
and Gtk.TextBuffer.get_text().
However, most text manipulation is accomplished with iterators, represented by
a Gtk.TextIter. An iterator represents a position between two characters
in the text buffer. Iterators are not valid indefinitely; whenever the buffer is
modified in a way that affects the contents of the buffer, all outstanding
iterators become invalid.

Text in a buffer can be marked with tags. A tag is an attribute that can be
applied to some range of text. For example, a tag might be called “bold” and make
the text inside the tag bold. However, the tag concept is more general than that;
tags don’t have to affect appearance. They can instead affect the behaviour of
mouse and key presses, “lock” a range of text so the user can’t edit it, or
countless other things. A tag is represented by a Gtk.TextTag object.
One Gtk.TextTag can be applied to any number of text ranges in any number
of buffers.

Each tag is stored in a Gtk.TextTagTable. A tag table defines a set of
tags that can be used together. Each buffer has one tag table associated with it;
only tags from that tag table can be used with the buffer. A single tag table can
be shared between multiple buffers, however.

importgigi.require_version('Gtk','3.0')fromgi.repositoryimportGtk,PangoclassSearchDialog(Gtk.Dialog):def__init__(self,parent):Gtk.Dialog.__init__(self,"Search",parent,Gtk.DialogFlags.MODAL,buttons=(Gtk.STOCK_FIND,Gtk.ResponseType.OK,Gtk.STOCK_CANCEL,Gtk.ResponseType.CANCEL))box=self.get_content_area()label=Gtk.Label("Insert text you want to search for:")box.add(label)self.entry=Gtk.Entry()box.add(self.entry)self.show_all()classTextViewWindow(Gtk.Window):def__init__(self):Gtk.Window.__init__(self,title="TextView Example")self.set_default_size(-1,350)self.grid=Gtk.Grid()self.add(self.grid)self.create_textview()self.create_toolbar()self.create_buttons()defcreate_toolbar(self):toolbar=Gtk.Toolbar()self.grid.attach(toolbar,0,0,3,1)button_bold=Gtk.ToolButton()button_bold.set_icon_name("format-text-bold-symbolic")toolbar.insert(button_bold,0)button_italic=Gtk.ToolButton()button_italic.set_icon_name("format-text-italic-symbolic")toolbar.insert(button_italic,1)button_underline=Gtk.ToolButton()button_underline.set_icon_name("format-text-underline-symbolic")toolbar.insert(button_underline,2)button_bold.connect("clicked",self.on_button_clicked,self.tag_bold)button_italic.connect("clicked",self.on_button_clicked,self.tag_italic)button_underline.connect("clicked",self.on_button_clicked,self.tag_underline)toolbar.insert(Gtk.SeparatorToolItem(),3)radio_justifyleft=Gtk.RadioToolButton()radio_justifyleft.set_icon_name("format-justify-left-symbolic")toolbar.insert(radio_justifyleft,4)radio_justifycenter=Gtk.RadioToolButton.new_from_widget(radio_justifyleft)radio_justifycenter.set_icon_name("format-justify-center-symbolic")toolbar.insert(radio_justifycenter,5)radio_justifyright=Gtk.RadioToolButton.new_from_widget(radio_justifyleft)radio_justifyright.set_icon_name("format-justify-right-symbolic")toolbar.insert(radio_justifyright,6)radio_justifyfill=Gtk.RadioToolButton.new_from_widget(radio_justifyleft)radio_justifyfill.set_icon_name("format-justify-fill-symbolic")toolbar.insert(radio_justifyfill,7)radio_justifyleft.connect("toggled",self.on_justify_toggled,Gtk.Justification.LEFT)radio_justifycenter.connect("toggled",self.on_justify_toggled,Gtk.Justification.CENTER)radio_justifyright.connect("toggled",self.on_justify_toggled,Gtk.Justification.RIGHT)radio_justifyfill.connect("toggled",self.on_justify_toggled,Gtk.Justification.FILL)toolbar.insert(Gtk.SeparatorToolItem(),8)button_clear=Gtk.ToolButton()button_clear.set_icon_name("edit-clear-symbolic")button_clear.connect("clicked",self.on_clear_clicked)toolbar.insert(button_clear,9)toolbar.insert(Gtk.SeparatorToolItem(),10)button_search=Gtk.ToolButton()button_search.set_icon_name("system-search-symbolic")button_search.connect("clicked",self.on_search_clicked)toolbar.insert(button_search,11)defcreate_textview(self):scrolledwindow=Gtk.ScrolledWindow()scrolledwindow.set_hexpand(True)scrolledwindow.set_vexpand(True)self.grid.attach(scrolledwindow,0,1,3,1)self.textview=Gtk.TextView()self.textbuffer=self.textview.get_buffer()self.textbuffer.set_text("This is some text inside of a Gtk.TextView. "+"Select text and click one of the buttons 'bold', 'italic', "+"or 'underline' to modify the text accordingly.")scrolledwindow.add(self.textview)self.tag_bold=self.textbuffer.create_tag("bold",weight=Pango.Weight.BOLD)self.tag_italic=self.textbuffer.create_tag("italic",style=Pango.Style.ITALIC)self.tag_underline=self.textbuffer.create_tag("underline",underline=Pango.Underline.SINGLE)self.tag_found=self.textbuffer.create_tag("found",background="yellow")defcreate_buttons(self):check_editable=Gtk.CheckButton("Editable")check_editable.set_active(True)check_editable.connect("toggled",self.on_editable_toggled)self.grid.attach(check_editable,0,2,1,1)check_cursor=Gtk.CheckButton("Cursor Visible")check_cursor.set_active(True)check_editable.connect("toggled",self.on_cursor_toggled)self.grid.attach_next_to(check_cursor,check_editable,Gtk.PositionType.RIGHT,1,1)radio_wrapnone=Gtk.RadioButton.new_with_label_from_widget(None,"No Wrapping")self.grid.attach(radio_wrapnone,0,3,1,1)radio_wrapchar=Gtk.RadioButton.new_with_label_from_widget(radio_wrapnone,"Character Wrapping")self.grid.attach_next_to(radio_wrapchar,radio_wrapnone,Gtk.PositionType.RIGHT,1,1)radio_wrapword=Gtk.RadioButton.new_with_label_from_widget(radio_wrapnone,"Word Wrapping")self.grid.attach_next_to(radio_wrapword,radio_wrapchar,Gtk.PositionType.RIGHT,1,1)radio_wrapnone.connect("toggled",self.on_wrap_toggled,Gtk.WrapMode.NONE)radio_wrapchar.connect("toggled",self.on_wrap_toggled,Gtk.WrapMode.CHAR)radio_wrapword.connect("toggled",self.on_wrap_toggled,Gtk.WrapMode.WORD)defon_button_clicked(self,widget,tag):bounds=self.textbuffer.get_selection_bounds()iflen(bounds)!=0:start,end=boundsself.textbuffer.apply_tag(tag,start,end)defon_clear_clicked(self,widget):start=self.textbuffer.get_start_iter()end=self.textbuffer.get_end_iter()self.textbuffer.remove_all_tags(start,end)defon_editable_toggled(self,widget):self.textview.set_editable(widget.get_active())defon_cursor_toggled(self,widget):self.textview.set_cursor_visible(widget.get_active())defon_wrap_toggled(self,widget,mode):self.textview.set_wrap_mode(mode)defon_justify_toggled(self,widget,justification):self.textview.set_justification(justification)defon_search_clicked(self,widget):dialog=SearchDialog(self)response=dialog.run()ifresponse==Gtk.ResponseType.OK:cursor_mark=self.textbuffer.get_insert()start=self.textbuffer.get_iter_at_mark(cursor_mark)ifstart.get_offset()==self.textbuffer.get_char_count():start=self.textbuffer.get_start_iter()self.search_and_mark(dialog.entry.get_text(),start)dialog.destroy()defsearch_and_mark(self,text,start):end=self.textbuffer.get_end_iter()match=start.forward_search(text,0,end)ifmatch!=None:match_start,match_end=matchself.textbuffer.apply_tag(self.tag_found,match_start,match_end)self.search_and_mark(text,match_end)win=TextViewWindow()win.connect("delete-event",Gtk.main_quit)win.show_all()Gtk.main()