gtk_widget_add_tick_callback() и gtk_main_iteration()

У меня два окна GTK

  1. Нормальное (основное) окно, которое запускает анимацию, рисует материал в обратном вызове, зарегистрированный gtk_widget_add_tick_callback().

  2. В какой-то момент создается вторичное окно, которое запускает модальный цикл:

    void show_modal() 
    {
       GtkWindow* gw = gtkwindow(this);
    
       if( parent() )
         gtk_window_set_transient_for(gw, gtkwindow( parent() ));
    
    
       gtk_widget_show(GTK_WIDGET(gw));
       gtk_window_set_modal(gw,TRUE);
       gtk_window_set_keep_above(gw,TRUE);
       this->update_window_state(gool::WINDOW_SHOWN);
    
       while( this->is_valid_window() )
       {
          if(this->_window_state == WINDOW_HIDDEN) break;
          if(this->_window_state == WINDOW_STATE_NA) break;
          gtk_main_iteration(); // gtk_main_iteration_do(true);
       }
    }
    

Проблема. Анимация в главном окне работает нормально до show_modal(). Он выглядит как gtk_main_iteration(); блокирует тики, добавленные функцией gtk_widget_add_tick_callback(). Как только я закрываю вторичное окно, и поэтому while() {gtk_main_iteration();} цикл завершается, анимации в главном окне снова запускаются.

Любая идея о том, как сделать "анимацию дружественных" модальных петель в GTK?

UPDATE: оно выглядит как gtk_main_iteration(); блокирует не только тики, но и любые обновления любых окон, кроме "текущего" - они просто застывают. В чем же причина такого поведения GTK?

ОБНОВЛЕНИЕ # 2:

gtk_dialog_run(); ведет себя точно так же, как gtk_main_iteration(); - блокирует любые обновления в любом окне, кроме активного окна.

Ответ 1

Кажется, по определению: link

gboolean gtk_main_iteration (void); Выполняет одиночную итерацию mainloop. Если никакие события не ожидают обработки GTK+ будет блокироваться до тех пор, пока не будет замечено следующее событие. Если вы не захотите заблокировать просмотр gtk_main_iteration_do() или проверьте, не gtk_events_pending() ли какие-либо события с gtk_events_pending().

Объяснение предлагает использовать gtk_main_iteration_do(FALSE) если вы не хотите блокировать:

gboolean gtk_main_iteration_do (gboolean blocking); Выполняет одиночную итерацию mainloop. Если никакие события не доступны или возвращаются или блокируются в зависимости от значения blocking: TRUE если вы хотите GTK+ блокировать, если события не ожидаются

Что касается gtk_dialog_run: он также блокирует по дизайну ссылку

gint gtk_dialog_run (GtkDialog *dialog); Блоки в рекурсивном основном цикле до тех пор, пока диалог не испустит "ответный" сигнал или не будет уничтожен. [...]

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

Ответ 2

Я предполагаю, что show_modal вызывается из обратного вызова или другого действия в основном контексте. Вы можете попробовать добавить свое модальное окно в основной контекст, используя invoke или signal_idle.

Таким образом show_modal выполнения show_modal закончится.

#include <gtkmm.h>
#include <string>

int main()
{
    auto Application = Gtk::Application::create();
    Gtk::Window window;
    Gtk::Window* window2;
    Gtk::Button button;
    window.add(button);

    //I hope timeout behaves similar to ticks. I have no idea how animations in GTK work
    int i=0;
    Glib::MainContext::get_default()->signal_timeout().connect([&]()->bool{
            button.set_label(std::to_string(i++));
            return true;
        }, 1000);

    button.signal_clicked().connect([&]{
            Glib::MainContext::get_default()->invoke([&]()->bool{
                    window2 = new Gtk::Window;
                    window2->set_modal(true);
                    window2->set_keep_above(true);
                    window2->signal_delete_event().connect([&](GdkEventAny* any_event)->bool{
                            delete window2;
                            return false;
                        });
                    window2->show_all();
                    return false;
                });
        });

    window.show_all();

    return Application->run(window);
}