Ошибка утверждения в методе: 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); никогда не вызывается в моем случае. Виджет остается в списке, и это нежелательное поведение.

МВЦЭ:

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


person ProNOOB    schedule 24.03.2017    source источник
comment
Расширьте свой код до MVCE, который мы можем скомпилировать и протестировать.   -  person Jens Mühlenhoff    schedule 25.03.2017
comment
Вот репозиторий github: github.com/andros705/   -  person ProNOOB    schedule 25.03.2017
comment
Я удалил код, не относящийся к MVCE. Если вы запустите программу сейчас и нажмете кнопку закрытия, она все равно вызовет утверждение.   -  person Jens Mühlenhoff    schedule 26.03.2017
comment
Это хорошая идея, чтобы получить непосредственно от Gtk.Container? Что вы пытаетесь сделать в первую очередь? Возможно, вы захотите добавить это к вопросу.   -  person Jens Mühlenhoff    schedule 26.03.2017


Ответы (2)


Несколько моментов:

  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+ действительно должен управлять этим за вас.

person Michael Gratton    schedule 26.03.2017
comment
Я обновил код на GitHub в соответствии с вашими замечаниями, но я все еще получаю эту ужасную ошибку утверждения + одно новое предупреждение Gtk-WARNING **: GtkContainerClass::remove не реализован для ссылки GitHub «MyContainer»: github.com/andros705/ - person ProNOOB; 26.03.2017
comment
Это предупреждение может быть связано с некоторыми отсутствующими функциями. Если вы произведете от Gtk.Box вместо Gtk.Container, он исчезнет. - person Jens Mühlenhoff; 26.03.2017

Как писал Майкл, вы сами делаете многое из того, что 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);
  }
}
person Jens Mühlenhoff    schedule 26.03.2017
comment
Ладно, на самом деле я следил за этой документацией: valadoc.org/gtk+-3.0/ Gtk.Container.html, спасибо, попробую ваш способ - person ProNOOB; 27.03.2017
comment
Вы должны были упомянуть об этом раньше. Если вы хотите, чтобы люди были как можно более полезными, вы должны добавить всю имеющуюся у вас справочную информацию. Кстати, я ни в коем случае не эксперт по Gtk+. - person Jens Mühlenhoff; 27.03.2017
comment
О, я не знал, что это очень важная информация. Кажется, что Gtk.Box содержит много кода, который мне просто не нужен. И это не дает необходимой гибкости, ломая весь дизайн приложения. - person ProNOOB; 28.03.2017
comment
Тогда было бы полезно знать, чего вы хотите достичь. Хороший вопрос часто имеет вступительный абзац с справочной информацией. Конечно, можно напрямую вывести из Gtk.Container. Хотя лично я никогда этого не делал. Я думаю, что сейчас это перерастает в другую тему. - person Jens Mühlenhoff; 28.03.2017
comment
Я попытаюсь добиться своего с помощью Gtk.Box, но я также хотел бы знать, как исправить версию Gtk.Container. - person ProNOOB; 30.03.2017