0

我想我修复了这个错误,但我想确定我做对了。我也不确定为什么会这样。

修复前的代码:

private Gee.ArrayList<Gtk.Widget> menuButtons;

// Some other code here

public override void remove (Gtk.Widget widget) {
    if (widget in menuButtons) {
        widget.unparent ();
        menuButtons.remove ( widget ); // Look at this method call
        if (this.get_visible () && widget.get_visible ()) {
            this.queue_resize_no_redraw ();
        }
    }
}

代码导致:

ERROR:arraylist.c:957:gee_array_list_real_get: assertion failed: (index < _size)
./run: line 3: 11054 Aborted                 (core dumped) ./bin

修复后的代码:

    private Gee.ArrayList<Gtk.Widget> menuButtons;

    // Some other code here

    public override void remove (Gtk.Widget widget) {
        if (widget in menuButtons) {
            widget.unparent ();
            if (this.get_visible () && widget.get_visible ()) {
                this.queue_resize_no_redraw ();
                menuButtons.remove ( widget ); // Moved method call here
            }
        }
    }

现在它可以工作了,我不确定,但这可能与异步调用 remove 方法有关,是吗?

有什么好的解释吗?它是对问题的正确解决方案吗?

@再次检查代码后,我确定这不是我的问题的正确解决方法,因为 menuButtons.remove ( widget ); 在我的情况下永远不会被调用。小部件保留在列表中,这是不需要的行为。

MVCE:

MyContainer.vala

public class MyContainer : Gtk.Container {
  // ArrayList for storing menu buttons
  private Gee.ArrayList<Gtk.Widget> menuButtons;

  public MyContainer () {
    base.set_has_window (false);
    menuButtons = new Gee.ArrayList<Gtk.Widget> ();
  }

  public override void add (Gtk.Widget widget) {
    widget.set_parent (this);
    menuButtons.add (widget);
  }

  public override void remove (Gtk.Widget widget) {
    if (widget in menuButtons) {
      widget.unparent ();
      menuButtons.remove ( widget ); // After removing the widget from the List I get "assertion failed error"
      if (this.get_visible () && widget.get_visible ()) {
        this.queue_resize_no_redraw ();
      }
    }
  }

  public override void forall_internal (bool include_internals, Gtk.Callback callback) {
    foreach (var widget in menuButtons) {
      callback (widget);
    }
  }
}

SimpleGtkApplication.vala

public class SimpleGtkApplication : Gtk.Application {

  public SimpleGtkApplication () {
    Object (application_id: "simple.gtk.application", flags: ApplicationFlags.FLAGS_NONE);
  }

  protected override void activate () {
    Gtk.ApplicationWindow window = new Gtk.ApplicationWindow (this);
    window.set_default_size (800, 600);
    window.title = "SimpleGtkApplication";

    Gtk.Container container = new MyContainer ();
    container.add ( new Gtk.Button.with_label ("Button 1") );
    container.add ( new Gtk.Button.with_label ("Button 2") );

    window.add ( container );
    window.show_all ();
  }

  public static int main (string[] args) {
    SimpleGtkApplication app = new SimpleGtkApplication ();
    return app.run (args);
  }

}

编译:--pkg=gtk+-3.0 --pkg=gee-0.8

4

2 回答 2

1

几点:

  1. 您正在覆盖Gtk.Container::remove,但从不通过调用链接到父类的实现base.remove(),从长远来看,这会给您带来问题。
  2. MyContainer::remove你打电话widget.unparent()时,这可能会导致某种二次调用MyContainer::remove. 如果是这样,则两次widget in menuButtons测试都评估为真,但是当原始调用尝试从列表中删除小部件时,它已经消失了,因此断言失败。

TL;DR:将调用替换widget.unparent()base.remove(widget).

PS:如果您也需要显式调用,我会感到非常惊讶this.queue_resize_no_redraw(),GTK+ 真的应该为您管理。

于 2017-03-26T01:44:12.903 回答
1

正如迈克尔所写,您正在做很多 Gtk 可以为您自己做的事情。您也没有在覆盖中调用基本方法。

您直接来自Gtk.Container,我已将您的 MVCE 调整为使用 a Gtk.Box,并且使用此代码不会收到警告和断言:

public class MyContainer : Gtk.Box {
  private Gee.ArrayList<Gtk.Widget> menuButtons;

  public MyContainer () {
    menuButtons = new Gee.ArrayList<Gtk.Widget> ();
  }

  public override void add (Gtk.Widget widget) {
    base.add (widget);
    menuButtons.add (widget);
  }

  public override void remove (Gtk.Widget widget) {
    if (widget in menuButtons) {
      menuButtons.remove (widget);
    }
    base.remove (widget);
  }
}
于 2017-03-26T18:15:36.290 回答