如果 Python GTK 中的内容发生变化,则滚动到 Scrollable 的底部
Scroll to bottom of Scrollable if content changes in Python GTK
我正在构建一个 GUI,它有一个可滚动的元素,其中包含一个 Textview。我正在使用 Textbuffer 将文本添加到文本视图;它运行良好,但是当内容变大时,滚动条会停留在原位。我用 glade 创建了我的 GUI。
我阅读了文档,但找不到 scrollToBottom()
或其他方法。
那么如何确保输出新文本时滚动条始终在底部?
要在 GtkTextView 中滚动,您必须在 GtkTextBuffer 中使用标记。 GtkTextMark 对象是自动更新的,因此在创建 TextBuffer 时只需创建一次标记。
- 创建 GtkTextBuffer 对象和 GtkTextMark
- 创建 GtkTextView
- 任何时候你追加文本你都可以调用gtk_text_view_scroll_to_mark在最后滚动
操作方法如下:
C
GtkTextView *text_view;
GtkTextMark *text_mark_end;
void create_text_view () {
text_view = gtk_text_view_new ();
/* get the text buffer */
GtkTextBuffer *text_buffer;
text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
/* create an auto-updating 'always at end' marker to scroll */
GtkTextIter text_iter_end;
gtk_text_buffer_get_end_iter (text_buffer, &text_iter_end);
text_mark_end = gtk_text_buffer_create_mark (text_buffer,
NULL,
&text_iter_end,
FALSE);
}
void append_text(const gchar *text) {
if (text) {
/* get the text buffer */
GtkTextBuffer *text_buffer;
text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
/* get an end iter */
GtkTextIter text_iter_end;
gtk_text_buffer_get_end_iter (text_buffer, &text_iter_end);
/* append text */
gtk_text_buffer_insert (text_buffer, &text_iter_end);
/* now scroll to the end using marker */
gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (text_view),
text_mark_end,
0., FALSE, 0., 0.);
}
}
void destroy_text_view () {
g_object_unref (text_mark_end);
gtk_widget_destroy (text_view);
}
Python
#!/usr/bin/env python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class TextView(Gtk.TextView):
def __init__(self):
Gtk.TextView.__init__(self)
# create mark for scrolling
text_buffer = self.get_buffer()
text_iter_end = text_buffer.get_end_iter()
self.text_mark_end = text_buffer.create_mark("", text_iter_end, False)
def append_text(self, text):
# append text
text_buffer = self.get_buffer()
text_iter_end = text_buffer.get_end_iter()
text_buffer.insert(text_iter_end, text)
# now scroll using mark
self.scroll_to_mark(self.text_mark_end, 0, False, 0, 0)
class Window(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self)
self.set_title('Test GtkTextView scrolling')
self.set_default_size(400, 300)
self.grid = Gtk.Grid()
self.scrolled_win = Gtk.ScrolledWindow()
self.text_view = TextView()
self.button = Gtk.Button(label='Append text')
self.scrolled_win.set_hexpand(True)
self.scrolled_win.set_vexpand(True)
self.scrolled_win.add(self.text_view)
self.grid.add(self.scrolled_win)
self.grid.add(self.button)
self.add(self.grid)
self.connect('destroy', Gtk.main_quit)
self.button.connect('clicked', self.on_button_clicked)
self.show_all()
def on_button_clicked(self, widget):
self.text_view.append_text('Hello\n' * 5);
if __name__ == '__main__':
win = Window()
Gtk.main()
注:
还有一个gtk_text_view_scroll_to_iter函数。这并不是很有用,因为 GtkTextView 使用动画滚动(动画滚动可以在 gedit 和 PAGE up/down 中看到)。与基于标记的滚动不同,基于迭代器的滚动不能很好地与 GtkTextView 的动画滚动功能配合使用:
https://developer.gnome.org/gtk3/stable/GtkTextView.html#gtk-text-view-scroll-to-iter
Note that this function uses the currently-computed height of the
lines in the text buffer. Line heights are computed in an idle
handler; so this function may not have the desired effect if it’s
called before the height computations. To avoid oddness, consider
using gtk_text_view_scroll_to_mark() which saves a point to be
scrolled to after line validation.
我正在构建一个 GUI,它有一个可滚动的元素,其中包含一个 Textview。我正在使用 Textbuffer 将文本添加到文本视图;它运行良好,但是当内容变大时,滚动条会停留在原位。我用 glade 创建了我的 GUI。
我阅读了文档,但找不到 scrollToBottom()
或其他方法。
那么如何确保输出新文本时滚动条始终在底部?
要在 GtkTextView 中滚动,您必须在 GtkTextBuffer 中使用标记。 GtkTextMark 对象是自动更新的,因此在创建 TextBuffer 时只需创建一次标记。
- 创建 GtkTextBuffer 对象和 GtkTextMark
- 创建 GtkTextView
- 任何时候你追加文本你都可以调用gtk_text_view_scroll_to_mark在最后滚动
操作方法如下:
C
GtkTextView *text_view;
GtkTextMark *text_mark_end;
void create_text_view () {
text_view = gtk_text_view_new ();
/* get the text buffer */
GtkTextBuffer *text_buffer;
text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
/* create an auto-updating 'always at end' marker to scroll */
GtkTextIter text_iter_end;
gtk_text_buffer_get_end_iter (text_buffer, &text_iter_end);
text_mark_end = gtk_text_buffer_create_mark (text_buffer,
NULL,
&text_iter_end,
FALSE);
}
void append_text(const gchar *text) {
if (text) {
/* get the text buffer */
GtkTextBuffer *text_buffer;
text_buffer = gtk_text_view_get_buffer (GTK_TEXT_VIEW (text_view));
/* get an end iter */
GtkTextIter text_iter_end;
gtk_text_buffer_get_end_iter (text_buffer, &text_iter_end);
/* append text */
gtk_text_buffer_insert (text_buffer, &text_iter_end);
/* now scroll to the end using marker */
gtk_text_view_scroll_to_mark (GTK_TEXT_VIEW (text_view),
text_mark_end,
0., FALSE, 0., 0.);
}
}
void destroy_text_view () {
g_object_unref (text_mark_end);
gtk_widget_destroy (text_view);
}
Python
#!/usr/bin/env python3
import gi
gi.require_version('Gtk', '3.0')
from gi.repository import Gtk
class TextView(Gtk.TextView):
def __init__(self):
Gtk.TextView.__init__(self)
# create mark for scrolling
text_buffer = self.get_buffer()
text_iter_end = text_buffer.get_end_iter()
self.text_mark_end = text_buffer.create_mark("", text_iter_end, False)
def append_text(self, text):
# append text
text_buffer = self.get_buffer()
text_iter_end = text_buffer.get_end_iter()
text_buffer.insert(text_iter_end, text)
# now scroll using mark
self.scroll_to_mark(self.text_mark_end, 0, False, 0, 0)
class Window(Gtk.Window):
def __init__(self):
Gtk.Window.__init__(self)
self.set_title('Test GtkTextView scrolling')
self.set_default_size(400, 300)
self.grid = Gtk.Grid()
self.scrolled_win = Gtk.ScrolledWindow()
self.text_view = TextView()
self.button = Gtk.Button(label='Append text')
self.scrolled_win.set_hexpand(True)
self.scrolled_win.set_vexpand(True)
self.scrolled_win.add(self.text_view)
self.grid.add(self.scrolled_win)
self.grid.add(self.button)
self.add(self.grid)
self.connect('destroy', Gtk.main_quit)
self.button.connect('clicked', self.on_button_clicked)
self.show_all()
def on_button_clicked(self, widget):
self.text_view.append_text('Hello\n' * 5);
if __name__ == '__main__':
win = Window()
Gtk.main()
注:
还有一个gtk_text_view_scroll_to_iter函数。这并不是很有用,因为 GtkTextView 使用动画滚动(动画滚动可以在 gedit 和 PAGE up/down 中看到)。与基于标记的滚动不同,基于迭代器的滚动不能很好地与 GtkTextView 的动画滚动功能配合使用:
https://developer.gnome.org/gtk3/stable/GtkTextView.html#gtk-text-view-scroll-to-iter
Note that this function uses the currently-computed height of the lines in the text buffer. Line heights are computed in an idle handler; so this function may not have the desired effect if it’s called before the height computations. To avoid oddness, consider using gtk_text_view_scroll_to_mark() which saves a point to be scrolled to after line validation.