GTK3 and multithreading, replacing deprecated functions
Asked Answered
S

4

12

I would like to replace deprecated functions gdk_threads_enter()/leave() in my application that uses threads. The application as it is now, works perfect (although i am not sure if this is the right way to do it).

My main loop, runs the gtk_main and the signal handlers. When i receive a start button, i start a thread, that runs in the background along the main. How can i update the GUI from that thread. I know per the Documentation of GTK3 and GDK3, they say avoid it by using

gdk_threads_add_idle() 

or

gdk_threads_add_timeout() 

But how do I do this if I want the updating to be done only when I click start? is there any example. I am not asking how to use gdk_threads_add_idle(), I am asking how to run worker function in the main without a thread after clicking start.

Button clicked --> start the worker function "in thread previously" --> update large amount of GUI elements in the GUI window.

Siu answered 2/6, 2015 at 22:5 Comment(0)
O
15

You have 3 ways to do it:

  1. make computation in the button callback and use gtk_event_pending()/gtk_main_iteration()

  2. use g_idle_add() or others, and gtk_event_pending()/gtk_main_iteration()

  3. use a thread, eventually a mutex, and g_idle_add() or others. Normally, a mutex isn't needed but it may solve some bugs or Heisenbugs.

The third solution seems to be the best, because with the first two methods, I experienced some problems when exiting the application while a computation was running. The application didn't exit and was printing a lot of "Gtk Critical" warnings. (I tried it on Windows and mingw32).


1. button callback:

If you want to run the worker thread in the main gtk loop, you can directly make the computation in the button callback, updating the GUI and treating events from it with gtk_event_pending() and gtk_main_iteration(), as in the following sample code:

void on_button_clicked(GtkButton * button, gpointer data) {

  // do some computation...

  // modify the GUI:
  gtk_label_set_text(label,"text");

  // run the main iteration to update the GUI,
  // you need to call these functions even if the GUI wasn't modified,
  // in order to get it responsive and treat events from it:
  while(gtk_events_pending()) gtk_main_iteration();

  // do some other computation...

  // huge computation in a loop:
  while(1) {
    // do some computation...

    // update the GUI and treat events from it:
    while(gtk_events_pending()) gtk_main_iteration();
  }
}

2. g_idle_add():

You can also use, instead of g_thread_new(), gdk_thread_add_idle()(in the case some libraries not under your control may use gdk_threads_enter()/leave()) or g_idle_add() or g_main_context_invoke():

gboolean compute_func(gpointer data) {

  // do some computation...

  // modify the GUI:
  gtk_label_set_text(label,"text");
  // run the main loop to update the GUI and get it responsive:
  while(gtk_events_pending()) gtk_main_iteration();

  // do some other computation...

  // huge computation in a loop:
  while(1) {
    // do some computation...

    // update GUI and treat events from it:
    while(gtk_events_pending()) gtk_main_iteration();
  }

  return FALSE;
}

void on_button_clicked(GtkButton * button, gpointer data) {

    g_idle_add(compute_func,data);
}

3. thread and mutex:

In some cases using a thread make the computation to be faster, so when using a worker thread NOT in the main gtk loop, and when updating the GUI in function added to the main loop with gdk_threads_add_idle() or g_idle_add() from the worker thread, you may have to lock the access to the GUI using a mutex, because there may be a conflict between the functions accessing the GUI. The mutex have to be initialized with g_mutex_init(&mutex_interface); before beeing used by the application. For example:

GMutex mutex_interface;

gboolean update_gui(gpointer data) {
  g_mutex_lock(&mutex_interface);
  // update the GUI here:
  gtk_button_set_label(button,"label");
  // And read the GUI also here, before the mutex to be unlocked:
  gchar * text = gtk_entry_get_text(GTK_ENTRY(entry));
  g_mutex_unlock(&mutex_interface);

  return FALSE;
}

gpointer threadcompute(gpointer data) {
  int count = 0;

  while(count <= 10000) {
    printf("\ntest %d",count);
    // sometimes update the GUI:
    gdk_threads_add_idle(update_gui,data);
    // or:
    g_idle_add(update_gui,data);

    count++;
  }

  return NULL;
}

void on_button_clicked(GtkButton * button, gpointer data) {

    g_thread_new("thread",threadcompute,data);
}

If you need the functions updating the GUI to be executed in a specific order, you need to add two counters and to assign a number to each function called with g_idle_add() or gdk_threads_add_ilde():

GMutex mutex_interface;

typedef struct _data DATA;
struct _data {
  gchar label[1000];
  GtkWidget * w;
  int num;
};


int counter = 0;
int counter2 = 0;

gboolean update_gui(gpointer data) {
  DATA * d = (DATA *)data;

  debutloop:
  g_mutex_lock(&mutex_interface);
  if(d->num != counter2) {
    g_mutex_unlock(&mutex_interface);
    goto debutloop;
  }
  counter2++;
  // update the GUI here:
  gtk_button_set_label(GTK_BUTTON(d->w),d->label);
  // And read the GUI also here, before the mutex to be unlocked:
  gchar * text = gtk_entry_get_text(GTK_ENTRY(entry));
  g_mutex_unlock(&mutex_interface);

  free(d);

  return FALSE;
}

gpointer threadcompute(gpointer data) {
  int count = 0;

  while(count <= 10000) {
    printf("\ntest %d",count);

    DATA * d = (DATA*)malloc(sizeof(DATA));
    sprintf(d->label,"%d",count);
    d->w = (GtkWidget*)data;
    d->num = counter;
    counter++;
    // update the GUI:
    g_idle_add(update_gui,d);

    count++;
  }
  return NULL;
}

void on_button_clicked(GtkButton * button, gpointer data) {

    g_thread_new("thread",threadcompute,button);
}

I have also tested the case of locking individual widgets instead of the whole GUI, and it seems to work.

Outfit answered 3/6, 2015 at 7:21 Comment(8)
Thank you very very much...Bertrand125Siu
I tried the third option, but it is slow , so much so that it affects the timing of receiving messages and the link drops when timing requirements aren't meant. Do i need a mutex for the shared data between threads , i thought gdk_threads_add_idle grabs the global mutex from the main_gtk . i think i am doing it right, but maybe there is a race condition....i am unaware ofSiu
Normally a mutex isn't needed, but in the past i had a mingw system that required the use of a mutex inside the fuctions added to the main gtk loop with gdk_threads_add_idle(), without a mutex my program crashed. Since I have updated some libraries, a mutex isn't needed anymore. But in my answer, in the version with the two counters, you need a mutex, because the main thread and the other thread are accessing the counters and there is a race condition.Outfit
There is a race condition on the counters. But this version may be useless if gtk execute the whole functions in the right order.Outfit
To improve the speed, you can try to use gdk_threads_add_idle_full(), which allow to set the priority of the function added to the main gtk loop. For example: gdk_threads_add_idle_full(G_PRIORITY_HIGH_IDLE,function1,data,NULL). If no libraries not under your control use gdk_threads_enter()/leave(), you can try g_idle_add_full(), may be it is faster than gdk_threads_add_idle_full().Outfit
The priority goes from 300 (lower) to -100 (higher), the defined constants are: G_PRIORITY_HIGH (-100) G_PRIORITY_DEFAULT (0) G_PRIORITY_HIGH_IDLE (100) G_PRIORITY_DEFAULT_IDLE (200) and G_PRIORITY_LOW (300)Outfit
thank you very much man, i also made an attempt at the first method using solely while(gtk_events_pending()) gtk_main_iteration();Siu
thank you very much man, i also made an attempt at the first method using solely while(gtk_events_pending()) gtk_main_iteration() from the start button...with the amount of tasks i have to handle, i used so many of them....The result: it works from what it appears, However, there is a something wrong and i think you hinted to this problem. I get this running error when i close the main window: Gtk-CRITICAL **: gtk_widget_get_parent: assertion 'GTK_IS_WIDGET (widget)' failed . How can i get rid of this , tried to isolate the problem ...but this is a problem i couldn't locate thus farSiu
S
11

What the documentation says is that you can still run your worker function in a thread, you just can't use GTK and GDK functions from that thread. So, you can still start the thread when you click start. But instead of updating GUI elements from the thread, you have to schedule them to be updated from the main thread by using gdk_threads_add_idle().

So your diagram should look something like this:

Main thread     Worker thread
    |
Button clicked
    |      \________
    |               \
    |           Start worker function
    |                |
    |           Computation
    |                |
    |           Want to update GUI
    |                |
    |           gdk_threads_add_idle(function1, data1)
    | ______________/|
    |/               |
    v           More computation
function1 runs       |
    |           Want to update GUI
GUI updated          |
    |           gdk_threads_add_idle(function2, data2)
    | ______________/|
    |/               |
    v           More computation
function2 runs       |
    |      
  etc...

If this is too complicated for your use case, and you have a computation in your worker thread that returns control to your worker thread often enough (say, you are calculating something in a loop), then you can run the calculation entirely in the main thread without locking up the GUI by briefly returning control to the GUI main loop, like so:

for (lots of items) {
    result = do_short_calculation_on(one_item);

    update_gui(result);

    while (gtk_events_pending())
        gtk_main_iteration();
}
Strick answered 3/6, 2015 at 4:34 Comment(2)
Can you elaborate on the last example you gave. What do you mean by gtk_events_pending() and gtk_main_iteration() ? The first illustration you made, that's what i'm trying to do right now, but it causes Segmentation fault...because in the function1 or function2, that's where i do the updating in many GUI window elementsSiu
By gtk_events_pending() and gtk_main_iteration() I mean this.Strick
S
1

This is an old question but I thought I'd go ahead and add a better way of doing this:

Firstly, the problem with Bertrand's Method 1 & 2 is that it's not advisable to do long running threads on the UI thread, even if one calls gtk_main_iteration() to service pending events as can be seen one is then roped into corner cases involving close and delete events, and further it can possibly cause stack overflow if one does this from too many widgets which all do it with long running work to do, and it just seems like a fragile band-aid solution to be calling gtk_events_pending() and gtk_main_iteration(), it may work for shorter operations where one needs to keep the UI alive while doing something real quick, but for like a long running network operation this doesn't seem like a good design pattern, it'd be much better to put that into it's own thread separate from the UI completely.

Now, if one wishes to update the UI from such a long running thread, such as doing several network transfers and reporting the status then one could use pipes for interthread communication. The problem with using mutexes like in Bertrand's method 3 is that acquiring the lock can be slow and can block if the long running thread already has the lock acquired, especially the way Bertrand loops back to debutLoop, this causes the UI thread to stall waiting for the compute thread, which is unacceptable.

However, using pipes, one can communicate with the UI thread in a non-blocking manner.

Essentially one creates a non-blocking pipe from a FIFO file at the beginning of the program and then one can use gdk_threads_add_idle to create a sentinel thread for receiving messages from a thread in the background, this sentinel function can even exist the entire lifetime of the program if for instance one has timer threads which check a URL often to update a UI element with it's result from an HTTP transaction.

For example:

/* MyRealTimeIP, an example of GTK UI update thread interthread communication
 *
 * Copyright (C) 2023 Michael Motes
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to
 * deal in the Software without restriction, including without limitation the
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
 * sell copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 * IN THE SOFTWARE.
 */

#include <gtk/gtk.h>
#include <fcntl.h>
#include <pthread.h>
#include <curl/curl.h>
#include <sys/stat.h>
#include <stdlib.h>

#define APP_NAME            "MyRealTimeIP"
#define MY_IP_LABEL         "My IP: "
#define UPDATE_TIME_LABEL   "Updated at: "


#define FIFO_FILE "/tmp/" APP_NAME "_Pipe"
#define IP_CHECK_URL "https://domains.google.com/checkip"
#define NETWORK_ERROR_TIMEOUT 1 //one second cooldown in case of curl error
#define PIPE_TIMEOUT 50000000 //50ms timeout between pipe checks to lower CPU usage

#define IP_CHECK_URL "https://domains.google.com/checkip"
#define UNEXPECTED_ERROR (-1)
#define MEMORY_ERROR "\nMemory allocation failed.\n"
#define MEMCHECK(x)                        \
        if((x)== NULL){                    \
            fprintf(stderr, MEMORY_ERROR); \
            exit(UNEXPECTED_ERROR);        \
        }


#define TIME_FMT_LEN 45
#define CURRENT_TIME_STR(timeStr){                  \
    struct timespec rt_clock = {};                  \
    clock_gettime(CLOCK_REALTIME,&rt_clock);        \
    time_t raw_time;                                \
    struct tm *time_info;                           \
    time(&raw_time);                                \
    time_info = localtime(&raw_time);               \
    /*If this is ever not true it means the
      hour changed between clock_gettime call
      and localtime call, so I update the values
      unless it would roll back the day, in that case
      I just roll forward nanoseconds to 0.*/       \
    if(time_info->tm_hour - (daylight ? 1 :  0)     \
            + timezone/3600 !=                      \
                      (int)((rt_clock.tv_sec / 3600)\
                                              % 24))\
    {                                               \
        if(time_info->tm_hour == 0)  {              \
            rt_clock.tv_nsec = 0;                   \
        }else{                                      \
            time_info->tm_hour =                    \
                      (int)((rt_clock.tv_sec / 3600)\
                                              % 24);\
            time_info->tm_sec =                     \
                        (int)(rt_clock.tv_sec % 60);\
            time_info->tm_min =                     \
                        (int)((rt_clock.tv_sec / 60)\
                                              % 60);\
             }                                      \
    } else {                                        \
        time_info->tm_sec =                         \
                    (int)(rt_clock.tv_sec % 60);    \
        time_info->tm_min =                         \
                    (int)((rt_clock.tv_sec / 60)    \
                                          % 60);    \
    }                                               \
                                                    \
    timeStr = malloc(TIME_FMT_LEN);                 \
    snprintf(timeStr,TIME_FMT_LEN,                  \
            "%04d-%02d-%02d %02d:%02d:%02d.%03d",   \
            time_info->tm_year + 1900,              \
            time_info->tm_mon + 1,                  \
            time_info->tm_mday,                     \
            time_info->tm_hour,                     \
            time_info->tm_min,                      \
            time_info->tm_sec,                      \
            (int)(rt_clock.tv_nsec/1000000));       \
}

#pragma region IO_Macros
#define READ_BUF_SET_BYTES(fd, buffer, numb, bytesRead){  \
    ssize_t rb = bytesRead;                               \
    ssize_t nb;                                           \
    while (rb < numb) {                                   \
        nb = read(fd,(char*)&buffer + rb,numb - rb);      \
        if(nb<=0)                                         \
            break;                                        \
        rb += nb;                                         \
    }                                                     \
    bytesRead = rb;                                       \
}
#define READ_BUF(fd, buffer, numb) {                   \
        ssize_t bytesRead = 0;                         \
        READ_BUF_SET_BYTES(fd, buffer, numb, bytesRead)\
}

#define WRITE_BUF(fd, buf, sz){                \
    size_t nb = 0;                             \
    size_t wb = 0;                             \
    while (nb < sz){                           \
        wb = write(fd, &buf + nb, sz-nb);      \
        if(wb == EOF) break;                   \
        nb += wb;                              \
    }                                          \
}
#pragma  endregion


GtkWidget *my_IP_Label;
GtkWidget *updatedTimeLabel;
static int interthread_pipe;

enum pipeCmd {
    SET_IP_LABEL,
    SET_UPDATED_TIME_LABEL,
    IDLE
};

typedef struct {
    size_t size;
    char *str;
} curl_ret_data;


static void fifo_write(enum pipeCmd newUIcmd);

static void fifo_write_ip(char *newIP_Str);

static void fifo_write_update_time(char *newUpdateTimeStr);

static gboolean ui_update_thread(gpointer unused);

static void *ui_update_restart_thread(void *);

static size_t curl_write_data(void *in, size_t size, size_t nmemb, curl_ret_data *data_out);

static void *checkIP_thread(void *);

int main(int argc, char *argv[]) {
    mkfifo(FIFO_FILE, 0777);
    interthread_pipe = open(FIFO_FILE, O_RDWR | O_NONBLOCK);


    gtk_init(&argc, &argv);

    GtkWidget *appWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title(GTK_WINDOW (appWindow), APP_NAME);
    gtk_widget_set_size_request(appWindow, 333, 206);

    GtkBox *vbox = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 0));

    my_IP_Label = gtk_label_new(MY_IP_LABEL "Not updated yet.");
    updatedTimeLabel = gtk_label_new(UPDATE_TIME_LABEL "Not updated yet.");

    gtk_box_pack_start(vbox, my_IP_Label, TRUE, FALSE, 0);
    gtk_box_pack_start(vbox, updatedTimeLabel, TRUE, FALSE, 0);

    gtk_container_add(GTK_CONTAINER (appWindow), GTK_WIDGET(vbox));

    gtk_widget_show_all(appWindow);
    g_signal_connect (G_OBJECT(appWindow), "destroy", G_CALLBACK(gtk_main_quit), NULL);


    pthread_t checkIP_thread_pid;
    if (pthread_create(&checkIP_thread_pid, NULL, &checkIP_thread, NULL) != 0)
        return UNEXPECTED_ERROR;

    gdk_threads_add_idle(ui_update_thread, NULL);

    gtk_main();

    pthread_cancel(checkIP_thread_pid);
    pthread_join(checkIP_thread_pid, NULL);

    return 0;
}

size_t curl_write_data(void *in, size_t size, size_t nmemb, curl_ret_data *data_out) {
    size_t index = data_out->size;
    size_t n = (size * nmemb);
    char *temp;

    data_out->size += (size * nmemb);

    temp = realloc(data_out->str, data_out->size + 1);
    MEMCHECK(temp)
    data_out->str = temp;
    memcpy((data_out->str + index), in, n);
    data_out->str[data_out->size] = '\0';

    return size * nmemb;
}

_Noreturn void *checkIP_thread(void *unused) {
    sleep(2); //not needed, just for example purposes to show initial screen first
    while (1) {
        CURL *curl;
        CURLcode res;
        curl_ret_data data = {};
        while (data.str == NULL) {
            curl = curl_easy_init();
            if (curl) {
                curl_easy_setopt(curl, CURLOPT_URL, IP_CHECK_URL);
                curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
                curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write_data);
                curl_easy_setopt(curl, CURLOPT_WRITEDATA, &data);
                res = curl_easy_perform(curl);
                if (res != CURLE_OK) {
                    fprintf(stderr, "curl_easy_perform() failed: %s\n",
                            curl_easy_strerror(res));
                    if (data.str != NULL) {
                        free(data.str);
                        data.str = NULL;
                    }
                    sleep(NETWORK_ERROR_TIMEOUT);
                }
                curl_easy_cleanup(curl);
            }
        }
        int newIP_StrSz = strlen(MY_IP_LABEL) + data.size + 1;
        char *newIP_Str = calloc(1, newIP_StrSz);
        snprintf(newIP_Str, newIP_StrSz, MY_IP_LABEL " %s", data.str);
        fifo_write_ip(newIP_Str);

        char *timeStr;
        CURRENT_TIME_STR(timeStr)
        int newUpdateTimeStrSz = strlen(UPDATE_TIME_LABEL) + TIME_FMT_LEN + 1;
        char *newUpdateTimeStr = calloc(1, newUpdateTimeStrSz);
        snprintf(newUpdateTimeStr, newUpdateTimeStrSz, UPDATE_TIME_LABEL " %s", timeStr);
        free(timeStr);
        fifo_write_update_time(newUpdateTimeStr);

        sleep(5);
    }
}

static void fifo_write(enum pipeCmd newUIcmd) {
    WRITE_BUF(interthread_pipe, newUIcmd, sizeof(newUIcmd))
}

static void fifo_write_ip(char *newIP_Str) {
    fifo_write(SET_IP_LABEL);
    WRITE_BUF(interthread_pipe, newIP_Str, sizeof(newIP_Str))
}

static void fifo_write_update_time(char *newUpdateTimeStr) {
    fifo_write(SET_UPDATED_TIME_LABEL);
    WRITE_BUF(interthread_pipe, newUpdateTimeStr, sizeof(newUpdateTimeStr))
}

gboolean ui_update_thread(gpointer unused) {
    enum pipeCmd pipeBuffer = IDLE;

    READ_BUF(interthread_pipe, pipeBuffer, sizeof(pipeBuffer))
    switch (pipeBuffer) {
        case SET_IP_LABEL: {
            char *newIP_Str = NULL;
            int bytesRead = 0;
            while (bytesRead != sizeof(newIP_Str)) {
                READ_BUF_SET_BYTES(interthread_pipe, newIP_Str, sizeof(newIP_Str) - bytesRead, bytesRead)
            }
            gtk_label_set_text(GTK_LABEL(my_IP_Label), newIP_Str);
            free(newIP_Str);
            break;
        }
        case SET_UPDATED_TIME_LABEL: {
            char *newUpdateTimeStr = NULL;
            int bytesRead = 0;
            while (bytesRead != sizeof(newUpdateTimeStr)) {
                READ_BUF_SET_BYTES(interthread_pipe, newUpdateTimeStr, sizeof(newUpdateTimeStr) - bytesRead, bytesRead)
            }
            gtk_label_set_text(GTK_LABEL(updatedTimeLabel), newUpdateTimeStr);
            free(newUpdateTimeStr);
            break;
        }
        case IDLE:
            break;
    }
    

    //Return false to detach update ui thread, reattach it after a timeout so CPU doesn't spin unnecessarily.
    pthread_t _unused;
    if (pthread_create(&_unused, NULL, ui_update_restart_thread, NULL))
        exit(UNEXPECTED_ERROR);
    return FALSE;
}

static void *ui_update_restart_thread(void *unused) {
    struct timespec delay = {0, PIPE_TIMEOUT};
    nanosleep(&delay, NULL);
    gdk_threads_add_idle(ui_update_thread, NULL);
    return NULL;
}
Specialistic answered 10/6, 2023 at 14:7 Comment(0)
O
0

I get this running error when i close the main window: Gtk-CRITICAL **: gtk_widget_get_parent: assertion 'GTK_IS_WIDGET (widget)' failed

I think i have found a solution, using two global variables that indicate to the callback to stop and to call gtk_main_quit(), and having trapped the "destroy" signal for the main window into a self defined callback named gtk_main_quit2() in the following example:

int process_running = 0; // indicate if the "process" is running
int stopprocess = 0; // indicate to the callback to stop or not

void gtk_main_quit2(GtkWidget * window, gpointer data) {
  if(process_running == 0) gtk_main_quit(); // if the "process" isn't running
                                            // then quit

  stopprocess = 1; // indicate to the button callback to stop and quit
}

void on_button_clicked(GtkButton * button, gpointer data) {

  // indicate the "process" is running:
  process_running = 1;

  // do some computation...


  while(gtk_events_pending()) gtk_main_iteration();
  if(stopprocess == 1) {
    // if close button clicked then quit:
    gtk_main_quit();
    return;
  }

  // do some other computation...

  // huge computation in a loop:
  while(1) {
    // do some computation...


    while(gtk_events_pending()) gtk_main_iteration();
    if(stopprocess == 1) {
      // if close button clicked then quit:
      gtk_main_quit();
      return;
    }
  }

  while(gtk_events_pending()) gtk_main_iteration();
  // indicate the "process" is finished:
  process_running = 0;
  // in the case the user clicked close button just at the end of computation:
  if(stopprocess == 1) {
    gtk_main_quit();
    return;
  }
}

int main() {

  gtk_init();
  Gtkwidget * window = create_window();
  g_signal_connect ((gpointer) window, "destroy", G_CALLBACK(gtk_main_quit2), NULL);
  gtk_main();

}

If you still have some gtk warnings after having clicked the close button, you can try to trap the "delete-event" signal instead of the "destroy" signal on the main window.

Outfit answered 9/6, 2015 at 11:27 Comment(5)
thanks man. I found a workaround solution but i don't like it since it is brutal, i simply in the call back for "destroy" of the main window, i wrote just after gtk_main_qui(), an exit(1). It aborts the program. I mean that is what i want....but this isn't exiting properly. I am gonna use ur solution, but im not sure what you mean by button "which button", i was talking about closing the main window. Without the exit(1), i get that perpetual shell error mentioned earlierSiu
In the example code, when I say "callback" or "button callback" it refers to the callback where there is the computation, when the button to launch the computation is clicked. When I say "close button", I mean the button in the top-right corner of the window. The normal callback of this button is gtk_main_quit(), but in my example, I defined another callback: gtk_main_quit2().Outfit
Ahh i see ...now, i 'll try that solution.Siu
But i meant to ask, does that error come from the fact that gtk gets closed while the process still in a loop calling for gtk functions ? or is it due to some signal i am not taking care of properly...Siu
I think it comes from gtk that want to finish the button callback after having destroyed the window.Outfit

© 2022 - 2024 — McMap. All rights reserved.