Статус кво:
У меня есть собственный виджет (MyWidget) с окном событий.
Проблема: если я создаю, показываю, а затем, позже, скрываю и уничтожаю виджет, я получаю следующее сообщение из приложения:
Gdk-WARNING **: losing last reference to undestroyed window
Что я узнал: Я посмотрел в gdkwindow.c
файл и это сообщение сообщается, когда GDK_WINDOW_DESTROYED(window) == FALSE
, Поэтому я не понимаю, как правильно разрушить окно, чтобы в конечном итоге gdk_window_destroy()
функция называется. Я думал, что лучшее место, чтобы назвать это было Gdk::~Window()
деструктор. Но это пусто. И более того gdk_window_destroy()
отсутствует в gdkwindow.cc
файл на всех.
on_realize()
а также on_unrealize()
обратные вызовы ниже.
class MyWidget : public Gtk::Widget
{
...
private:
Glib::RefPtr<Gdk::Window> _event_window;
...
};
void Gtk::MyWidget::on_realize()
{
GdkWindowAttr attributes;
const Allocation & allocation = get_allocation();
attributes.event_mask = GDK_BUTTON_PRESS_MASK;
attributes.x = allocation.get_x();
attributes.y = allocation.get_y();
attributes.width = allocation.get_width();
attributes.height = allocation.get_height();
attributes.wclass = GDK_INPUT_ONLY;
attributes.window_type = GDK_WINDOW_CHILD;
_event_window = Gdk::Window::create(get_parent_window(), &attributes, GDK_WA_X | GDK_WA_Y);
_event_window->set_user_data(Widget::gobj());
set_window(get_parent_window());
set_realized();
}
void Gtk::MyWidget::on_unrealize()
{
_event_window->set_user_data(NULL);
_event_window.reset();
set_realized(false);
}
Оказалось, что самый правильный способ уничтожить созданное вами окно GDK Gdk::Window::create()
это … угадай что? звонить Gtk::Widget::unrealize()
в on_unrealize()
метод вашего пользовательского виджета, потому что помимо прочего этот базовый метод вызывает gdk_window_destroy()
для окна GDK виджета. И для этого ваш виджет имеет быть «оконным» (то есть вы должны позвонить set_has_window(true);
в конструкторе и set_window(<your allocated GDK window>);
в on_realize()
Перезвоните. Очень очевидный подход, не правда ли?
Я должен также сказать кое-что о Gtk::Widget::realize()
, В отличие от Gtk::Widget::unrealize()
ты должен позвонить Gtk::Widget::realize()
только если ваш виджет не имеет окно GDK (метод предполагает это как утверждение).
К сожалению, у меня не было времени и желания докопаться до сути и постараться понять, почему это было сделано, и какие причины и последствия имеет этот подход. В противном случае я бы дал более подробное объяснение.
Вы можете найти официальный пример из учебника GTK по пользовательским виджетам
Вот.
Также код моего виджета теперь выглядит так:
class MyWidget : public Gtk::Widget
{
...
private:
Glib::RefPtr<Gdk::Window> _event_window;
...
};
void Gtk::MyWidget::on_realize()
{
GdkWindowAttr attributes;
const Allocation & allocation = get_allocation();
attributes.event_mask = GDK_BUTTON_PRESS_MASK | GDK_EXPOSURE;
attributes.x = allocation.get_x();
attributes.y = allocation.get_y();
attributes.width = allocation.get_width();
attributes.height = allocation.get_height();
attributes.wclass = GDK_INPUT_OUTPUT;
attributes.window_type = GDK_WINDOW_CHILD;
_event_window = Gdk::Window::create(get_parent_window(), &attributes, GDK_WA_X | GDK_WA_Y);
_event_window->set_user_data(Widget::gobj());
set_window(_event_window);
set_realized();
}
void Gtk::MyWidget::on_unrealize()
{
_event_window->set_user_data(NULL);
_event_window.reset();
Widget::unrealize();
// it will call gdk_destroy_window() and
// set_realized(false);
}
Других решений пока нет …