Ошибка подтверждения в методе: gee_array_list_real_get

Я думаю, что исправил эту ошибку, но хочу быть уверен, что сделал все правильно. Также я не уверен, почему так происходит.

Код перед исправлением:

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
            }
        }
    }

И теперь это работает, я не уверен, но это может быть связано с асинхронным вызовом метода удаления, не так ли?

Любое хорошее объяснение? Это правильное решение проблемы?

@ После повторной проверки кода я уверен, что это не является правильным решением моей проблемы, потому что 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

2 ответа

Как писал Майкл, вы делаете много вещей, которые Gtk может сделать для вас самостоятельно. Вы также не вызываете базовые методы в своих переопределениях.

Вы напрямую выводите из Gtk.ContainerЯ адаптировал ваш MVCE для использования 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);
  }
}

Пара моментов:

  1. Вы переопределяете Gtk.Container::remove, но никогда не соединяясь в цепочку с реализацией родительского класса, вызывая base.remove(), что вызовет у вас проблемы в долгосрочной перспективе.
  2. В MyContainer::remove ты звонишь widget.unparent()что может быть причиной какого-то вторичного вызова MyContainer::remove, Если так, то оба раза widget in menuButtons test оценивается как true, но когда исходный вызов пытается удалить виджет из списка, он уже исчез, следовательно, ошибка подтверждения.

TL; DR: заменить вызов на widget.unparent() с base.remove(widget),

PS: я был бы очень удивлен, если вам нужно явное this.queue_resize_no_redraw() позвоните либо, GTK+ действительно должен управлять этим для вас.

Другие вопросы по тегам