Main L oop
Для понимания GTK необходимо понимать 2 понятия.
Все современные графические интерфейсы являются однопоточными. У них есть поток, который обрабатывает события из оконной системы (например, кнопки, события мыши). Такой поток называется main event l oop или main l oop. GTK также однопоточный и не MT-безопасный. Это означает, что вы не должны вызывать какие-либо функции GTK из других потоков, так как это приведет к неопределенному поведению.
Как указано в документации Gtk,
Like Во всех GUI наборах инструментов GTK + использует модель программирования, управляемую событиями. Когда пользователь ничего не делает, GTK + сидит в «основном цикле» и ждет ввода. Если пользователь выполняет какое-либо действие, скажем, щелчком мыши, то основное l oop «просыпается» и доставляет событие в GTK +. GTK + пересылает событие одному или нескольким виджетам.
Gtk основан на событиях и асинхронен. Он реагирует на нажатия кнопок не в точный момент нажатия, а чуть позже.
Это может быть очень грубо написано так (не пытайтесь сделать это дома):
static list *pollable;
int main_loop (void)
{
while (run)
{
lock_mutex()
event_list = poll (pollable); // check whether there are some events to react to
unlock_mutex()
dispatch (event_list); // react to events.
}
}
void schedule (gpointer function)
{
lock_mutex()
add_to_list (pollable, something);
unlock_mutex()
}
Я хочу отложенное действие в моем приложении
Например, скрыть всплывающую подсказку за несколько секунд или изменить текст кнопки. Предполагая, что ваше приложение однопоточное, если вы вызовете sleep()
, оно будет выполнено в основном l oop. sleep()
означает, что этот конкретный поток будет приостановлен на указанное количество секунд. Никакой работы не будет сделано. И если этот поток является основным потоком, GTK не сможет перерисовывать или реагировать на взаимодействие с пользователем. Приложение зависает.
Что вам нужно сделать, это расписание вызов функции. Это можно сделать с помощью g_timeout_add
или g_idle_add
В первом случае наш poll()
из приведенного выше фрагмента вернет это событие через несколько секунд. В последнем случае он будет возвращен, когда нет событий с более высоким приоритетом.
static int count;
gboolean change_label (gpointer data)
{
GtkButton *button = data;
gchar *text = g_strdup_printf ("%i seconds left", --count);
if (count == 0)
return G_SOURCE_REMOVE;
return G_SOURCE_CONTINUE;
}
void button_clicked (GtkButton *button)
{
gtk_button_set_label (button, "clicked");
count = 5;
g_timeout_add (1 * G_TIME_SPAN_SECOND, change_label, button);
}
Возвращение значения из функции очень важно . Если вы этого не сделаете, функция будет вызываться снова и снова.
У меня есть дополнительные темы, и мое приложение вылетает
Как уже упоминалось, GTK не является MT-безопасным. Вы не должны вызывать функции Gtk из других потоков. Вы должны запланировать выполнение. g_timeout_add
и g_idle_add
являются MT-безопасными, в отличие от других функций GTK. Это обратные вызовы будут выполняться в основном l oop. Если у вас есть общие ресурсы между обратным вызовом и потоком, вы должны читать / записывать их атомарно или использовать мьютекс.
static int data;
static GMutex mutex;
gboolean change_label (gpointer data)
{
GtkButton *button = data;
int value;
gchar *text;
// retrieve data
g_mutex_lock (&mutex);
value = data;
g_mutex_unlock (&mutex);
// update widget
text = g_strdup_printf ("%i seconds left", --count);
return G_SOURCE_REMOVE;
}
gpointer thread_func (gpointer data)
{
GtkButton *button = data;
while (TRUE)
{
sleep (rand_time);
g_mutex_lock (&mutex);
++data;
g_mutex_unlock (&mutex);
g_idle_add (change_label, button);
}
}
Follow up: но python является однопоточным и GIL и так далее?
Вы можете себе представить, что python - это многопоточное приложение, работающее на одноядерном компьютере. Вы никогда не знаете, когда потоки будут переключены. Вы вызываете функцию GTK, но не знаете, в каком состоянии находится главный l oop. Может быть, это бесплатные ресурсы за мгновение до этого. Всегда график.
Что не обсуждается и что дальше читается
- Подробную документацию по glib main l oop можно найти здесь
GSource
как более низкоуровневый примитив. GTask