#!/usr/bin/env python """Text Widget/Automatic scrolling This example demonstrates how to use the gravity of GtkTextMarks to keep a text view scrolled to the bottom while appending text.""" import gobject import gtk class AutomaticScrollingDemo(gtk.Window): def __init__(self, parent=None): # Create the toplevel window gtk.Window.__init__(self) try: self.set_screen(parent.get_screen()) except AttributeError: self.connect('destroy', lambda *w: gtk.main_quit()) self.set_title(self.__class__.__name__) self.set_default_size(600, 400) self.set_border_width(0) hbox = gtk.HBox(True, 6) self.add(hbox) self.create_text_view(hbox, True) self.create_text_view(hbox, False) self.count_sb = 0 self.count_se = 0 self.show_all() def create_text_view(self, hbox, scroll_to_end): swindow = gtk.ScrolledWindow() hbox.pack_start(swindow) textview = gtk.TextView() swindow.add(textview) timeout = self.setup_scroll(textview, scroll_to_end) # Remove the timeout in destroy handler, so we don't try to # scroll destroyed widget. textview.connect("destroy", lambda widget: gobject.source_remove(timeout)) def setup_scroll(self, textview, scroll_to_end): buf = textview.get_buffer() itr = buf.get_end_iter() if scroll_to_end: # If we want to scroll to the end, including horizontal scrolling, # then we just create a mark with right gravity at the end of the # buffer. It will stay at the end unless explicitely moved with # gtk_text_buffer_move_mark. buf.create_mark("end", itr, False) # Add scrolling timeout. return gobject.timeout_add(50, self.scroll_to_end, textview) else: # If we want to scroll to the bottom, but not scroll horizontally, # then an end mark won't do the job. Just create a mark so we can # use it with gtk_text_view_scroll_mark_onscreen, we'll position it # explicitely when needed. Use left gravity so the mark stays where # we put it after inserting new text. buf.create_mark("scroll", itr, True) # Add scrolling timeout. return gobject.timeout_add(100, self.scroll_to_bottom, textview) """ Scroll to the end of the buffer. """ def scroll_to_end(self, textview): buf = textview.get_buffer() # Get the "end" mark. It's located at the end of buffer because # of right gravity mark = buf.get_mark("end") itr = buf.get_iter_at_mark(mark) # and insert some text at its position, the iter will be # revalidated after insertion to point to the end of inserted text buf.insert(itr, "\n") buf.insert(itr, " " * self.count_se) buf.insert(itr, "Scroll to end scroll to end scroll to end scroll to end ") # Now scroll the end mark onscreen. textview.scroll_mark_onscreen(mark) # Emulate typewriter behavior, shift to the left if we # are far enough to the right. self.count_se += 1 if self.count_se > 150: self.count_se = 0 return True """ Scroll to the bottom of the buffer. """ def scroll_to_bottom(self, textview): buf = textview.get_buffer() # Get the end iterator itr = buf.get_end_iter() # and insert some text at it, the iter will be revalidated # after insertion to point to the end of inserted text buf.insert(itr, "\n") buf.insert(itr, " " * self.count_sb) buf.insert(itr, "Scroll to bottom scroll to bottom scroll to bottom scroll to bottom") # Move the iterator to the beginning of line, so we don't scroll # in horizontal direction itr.set_line_offset(0) # and place the mark at iter. the mark will stay there after we # insert some text at the end because it has right gravity. mark = buf.get_mark("scroll") buf.move_mark(mark, itr) # Scroll the mark onscreen. textview.scroll_mark_onscreen(mark) # Shift text back if we got enough to the right. self.count_sb += 1 if self.count_sb > 40: self.count_sb = 0 return True def main(): AutomaticScrollingDemo() gtk.main() if __name__ == '__main__': main()