0

我一直致力于编写故事的文本编辑器。我正在使用 Python、GTK+ 3 和 GtkSourceView 3。编辑器的要点是折叠某些区域。虽然没有 - 还没有?- 在 GTK TextView / SourceView 中内置对折叠的支持,我一直在使用带有 invisible=True 的标签和 SourceView 的源标记来实现该功能。

此处提供源代码:https ://github.com/mkoskim/mawe

核心编辑器(SourceBuffer 和 SourceView)位于:https ://github.com/mkoskim/mawe/blob/master/gui/gtk/SceneView.py https://github.com/mkoskim/mawe/blob/master /gui/gtk/SceneBuffer.py

出于测试目的,您可以克隆存储库,并使用以下命令运行应用程序:

mawe$ ./mawe.py test/test.txt

现在,应用程序不断频繁地随机崩溃,出现如下错误:

(mawe.py:10556): Gtk-WARNING **: /build/gtk+3.0-2Ut_nl/gtk+3.0-3.18.9/./gtk/gtktextbtree.c:4034: byte index off the end of the line
(mawe.py:10556): Gtk-ERROR **: Byte index 1362 is off the end of the line
Trace/breakpoint trap

没有其他错误或警告日志。我一直在谷歌搜索错误没有任何成功。

其他症状似乎是:

  • 即使编辑器处于空闲状态,也会发生崩溃

  • 今天我发现奇怪的是,我可以通过将鼠标移到隐藏部分上来非常快地崩溃:o

我不能 100% 确定,但我认为这与不可见区域有关。

问题:有没有人碰巧知道这是否是一些已知的错误?

问题:有没有人有任何想法可以让我寻找可能的解决方案?有什么想法会导致崩溃,以及我可以更深入地探索什么?


更新:我用标签做了一些更广泛的测试。似乎没有其他属性对鼠标移动做出反应,但是当打开不可见性时,鼠标在区域上移动会使应用程序崩溃。我一直在搜索报告鼠标事件崩溃 gtktextbtree,但到目前为止没有成功。看起来这适用于几个 v3.x GTK 版本。


更新:我想我几乎找到了解决方法:从 GtkSource.View 过滤掉运动通知事件似乎有效,如下所示:

def filter_event(widget, event, *args):
    # Allow these
    if event.type == Gdk.EventType.KEY_PRESS: return False
    if event.type == Gdk.EventType.KEY_RELEASE: return False

    # Block these
    if event.type == Gdk.EventType.LEAVE_NOTIFY: return True
    if event.type == Gdk.EventType.MOTION_NOTIFY: return True

    # Print & allow the rest
    print(event)
    return False

self.text.connect("event", filter_event)

如果您在隐藏线附近按下鼠标按钮,应用程序仍然会崩溃,但似乎它不再因鼠标移动而崩溃。


更新:更多调查。虽然阻止鼠标事件可以防止崩溃,但它也会导致怪癖,例如无法使用鼠标放置光标、选择区域、DnD、... 此外,鼠标光标可能会消失,因为它不是每次都正确更新。我很确定将鼠标/窗口坐标转换为缓冲区位置的算法存在错误(当文本中有较大的隐藏块时),因此任何鼠标事件都可能使应用程序崩溃。


更新:我一直在尝试为该主题创建简单的测试用例。好事:隐藏似乎奏效了。坏事:还不能重现问题。测试脚本可以在这里找到:

https://github.com/mkoskim/mawe/blob/master/gui/gtk/test/hidecrash/hidecrash.py


更新:试图弄清楚 - 测试用例有效,编辑器不起作用。测试用例的区别至少在于编辑器将隐藏标签放入事件循环(*)中。试图为此做一个测试用例......

(*) 使用当前的 Gtk SourceView/TextView 实现折叠肯定有很多不同的解决方案。我选择了使用“标记”语言并在编辑时应用折叠的方法,因为它适用于撤消/重做。我还尝试了其他解决方案,例如:

  1. 剪切折叠场景并将小部件插入到文本缓冲区,包含文本本身。想法:“文本[选择用于折叠的部分]文本”->“文本[锚+带有剪切文本的小部件]文本”-遗憾的是,它不适用于撤消/重做。

  2. 剪切文本,给它一个 ID,然后在缓冲区中放置一个包含 ID 的特殊标记部分。想法“文本[选择用于折叠的部分]文本”->“文本[ID w/隐藏+受保护标签]文本”-不起作用,因为剪切'n'粘贴或撤消/重做不应用标签,所以用户可以销毁标记。

  3. 普通标记:尝试使用折叠指示器保持标记非常困难。您需要带有受保护标签的“[char][mark][char]”之类的东西,以确保您不会在某处丢失标记。

无论如何,我会继续调查。


更新:仍然无法在我的测试脚本中重现该问题,但发现了一些可能有趣的东西:折叠最后一个场景不会导致崩溃 - 仅在折叠跟随另一个场景(折叠或未折叠)的场景时。

4

2 回答 2

0

我有解决这个问题的办法。我只是不明白它为什么起作用,以及为什么我不能用测试脚本重现问题。这是执行折叠的片段:

fold_start = at.copy()
fold_start.forward_to_line_end()
fold_start.forward_char() # Comment this line -> crash
fold_end = self.scene_end_iter(end)
self.apply_tag(self.tag_fold_hide, fold_start, fold_end)

at在插入文本或删除范围回调处派生并指向行开始。fold_end是 TextIter 到下一个场景标记或文件结尾。如果我们查看缓冲区内容,它是这样的:

<mark 1><at>Scene 1 heading<eol>
Line
Line
<mark 2>Scene 2 heading

应用隐藏标签 from <eol>to<mark 2>会导致崩溃。应用标签 from<eol + 1>可以<mark 2>按预期工作。如果折叠到文件末尾 ( <mark 2> == buffer.get_end_iter()),则折叠有效。在某些情况下,如果只隐藏换行符,它也可以工作,但并非在所有情况下都有效。

如前所述,我不明白为什么这有效,为什么我不能用更简单的脚本重现问题,但我一直在调查更多,尽管现在有修复时,它并不那么紧急。

于 2019-12-07T12:37:05.497 回答
0

好的,所以昨天我这样做了,可见部分和不可见部分都使用相似的字体描述进行格式化。我仍然无法用简单的测试脚本重现这个问题,但似乎如果可见部分和隐藏部分离它们太远,偏移计算就会出现问题。到目前为止,我知道:

  • 错误消息与Byte index 1362 is off the end of the line Trace/breakpoint trap隐藏部分的长度相差约 5-7 个字节。

  • 当可见部分和不可见部分在字体大小、填充和粗细方面彼此“足够接近”时,不会出现错误。在我的工作示例中,我使用完全相同的字体、粗细、大小等格式化不可见部分,并且程序不再崩溃。

于 2019-12-13T00:16:24.453 回答