我在 GTK textview 中发现了奇怪的异常,希望有人能解释一下。
有一个简单的功能可以滚动到窗口底部:
richTextBox.ScrollToIter(richTextBox.Buffer.EndIter, 0, true, 0, 0);
这可能应该在大多数情况下都有效。但有时它会因 MemoryAccessViolation(Linux 中的 SIGSEGV)而导致整个应用程序崩溃
每次我向 TreeView 插入文本时,我的应用程序都会调用此函数,以便我可以始终处于关闭状态(您正在向某个窗口插入文本,并且想要查看最新的文本,例如在聊天中)
文本仅从主线程插入,并且该函数也仅在主线程中调用,因此这与我自己的应用程序中的多线程无关,但是我发现 TextView 与应用程序的其余部分异步渲染文本。就像负责绘制和处理文本的线程实际上不是主线程一样。例如,如果我将大量文本加载到 TextView 中,即使 TextView 以某种方式加载文本,我的应用程序也会响应。
因此,我相信 TextView 实际上正在使用自己的单独线程,并且当我尝试滚动窗口时,该线程正在更改文本。更改的文本会使 iter 无效,因此在将
richTextBox.Buffer.EndIter
传递给 TextView ScrollToBottom 函数的 IL 代码中的某处,文本可能被此外部线程更改并且 iter 无效,这就是我收到此内存访问异常的原因。
这甚至可能是 GTK 中的一个错误,但我使用的是非常稳定的版本(2.1.20),该版本随所有 Mono 版本一起提供,直到 Mono3,甚至适用于所有 Windows gtk# 版本。
有没有其他方法可以“安全”地向下滚动
这似乎有效:
连接滚动到
SizeAllocated
事件的函数:
this.tv.SizeAllocated += new SizeAllocatedHandler(Scroll2);
创建滚动功能
public void Scroll2(object sender, Gtk.SizeAllocatedArgs e)
{
tv.ScrollToIter(tv.Buffer.EndIter, 0, false, 0, 0);
}
我不知道它是否正确,但到目前为止它对我来说还没有崩溃,而且渲染完成后它总是滚动到底部
在 GtkAda 中,这也适用于 C(可能还有所有其他语言),重要的是使用
Add
函数,而不是 Add_With_Viewport
函数,因为 Add_With_Viewport
将添加一个视口,其中(看来)Gtk.Text_View 不需要,并且在任何情况下,使用视口,滚动命令将不起作用。我的初始化代码(用例看起来像这样(注意这是 Ada):
终端类型声明(在包规范中):
type Gtk_Terminal_Buffer_Record is new
Gtk.Text_Buffer.Gtk_Text_Buffer_Record
with record
master_fd : Interfaces.C.int;
-- <further record elements in here>
parent : Gtk.Text_View.Gtk_Text_View;
end record;
type Gtk_Terminal_Buffer is access all Gtk_Terminal_Buffer_Record'Class;
-- Other declarations in here as required
type Gtk_Terminal_Record is new
Gtk.Scrolled_Window.Gtk_Scrolled_Window_Record
with record
terminal : Gtk.Text_View.Gtk_Text_View;
buffer : Gtk_Terminal_Buffer;
-- <other record elements for it go in here>
end record;
其初始化代码(在包体中):
procedure Initialize (the_terminal : access Gtk_Terminal_Record'Class) is
begin
Gtk.Scrolled_Window.Initialize(Gtk_Scrolled_Window(the_terminal));
Gtk.Text_View.Gtk_New(view => the_terminal.terminal);
Gtk_New(the_terminal.buffer);
Gtk.Text_View.Set_Buffer(view => the_terminal.terminal,
buffer => the_terminal.buffer);
the_terminal.buffer.parent := the_terminal.terminal;
-- Other code in here
Add(the_terminal, the_terminal.terminal);
end Initialize;
就我而言,我发现这个特定的应用程序最好在有传入字符时调用“滚动到底部”操作,命令如下:
procedure Process(the_input : in UTF8_String;
for_buffer : Gtk_Terminal_Buffer) is
cursor_mark: Gtk.Text_Mark.Gtk_Text_Mark;
begin
-- <other commands in here>
-- Scroll if required to make it visible
cursor_mark := Get_Insert(for_buffer);
Scroll_Mark_Onscreen(for_buffer.parent, cursor_mark);
end Process;