Re: gtk_widget_show() outside gtk_main()




Aaron Campbell <aaron@ug.cs.dal.ca> writes:

> I have a button widget in an application that is connected to a callback
> function which, among other things, starts a separate process via a
> standard fork() + execl() + waitpid() procedure. I want to display a note
> via a label notifying the user that files are transferring and to please
> wait.
> 
> The button is connected to this callback with the gtk_signal_connect()
> function. The first part of the function just does some checking and
> setup. Right after that I put in my label and issued a
> gtk_widget_show(label). Unfortunately it doesn't update the display until
> the separate process completes (ie after the waitpid). I also tried
> putting the labelling part in a separate function that is called first,
> then another function to start the transfer. That also didn't work.

You are correct in assumming that the update will not occur
until GTK+ enters its main loop. Drawing in GTK+
is done in an idler handler when nothing else needs to be done.

It is possible to get force all pending events and the
internal draw queue to be updated, by doing:

 while (gtk_events_pending())
   gtk_main_iteration();

If you are putting up a window, then you have pay attention
to something else - you need to wait for the window to
be mapped by the X server. The call gtk_widget_show_now()
shows a toplevel widget and waits for it to be mapped.

In that case, you would do:

 gtk_widget_show_now (window);
 while (gtk_events_pending())
   gtk_main_iteration();

However, you should realize that with the above system, your
program will not do _any_ updates while in the waitpid().
In particular, it won't handle expose events.

An alternative method is to actually do a double fork - 
the immediate child spawns the real process, waits
for it to terminate, then communciates back to the
main program via a pipe. The real main process uses
an input handler to be notified when the grandchild
terminates.

The following is some simplified code that does basically
this - it forks off wget to get a file asynchronously,
with notification when it terminates. I've modified
a bit from its source, so I won't guarantee that it
actually works as is. [ This is code from a modification
that I recently made to Electric Eyes ]

If you want to block the rest of your UI while the
child process is working, you can take the typical
approach of:

  /* put up a "please wait" dialog */
  [...]

  gtk_grab_add (dialog);

  /* fork the process */
  [...]

  gtk_main();

and add a call to gtk_main_quit() inside the input_func()
which is called when the subprocess terminates.

Regards,
                                        Owen

typedef struct {
  gint pid;
  gint tag;
} ChildData;

/* Called when the child quits */
static void
input_func(gpointer data, gint source, GdkInputCondition condition)
{
  char buf[2];
  ChildData *child_data = data;
  int status;

  if (read (source, buf, 2) != 2 ||
      (buf[0] != 'O') || (buf[1] != 'K')) /* failure */
    {
      g_warning ("Download failed!\n");
    }
  else
    {
      /* Success ! */      
    }

  gdk_input_remove (child_data->tag);
  waitpid (child_data->pid, &status, NULL);

  g_free (child_data->name);
  g_free (child_data);
  
  close(source);
}

static void 
grab_url(gchar *name)
{
  int pid;
  int fds[2];

  if (pipe(fds))
    {
      g_warning ("Could not create pipe: %s\n", g_strerror(errno));
      return;
    }
  
  if (!(pid = fork()))
    {
      /* Child */

      close(fds[0]);
      
      /* Fork off a wget */

      if (!(pid = fork()))
	{
	  execlp("wget", "wget", "-q", name, NULL);
	  g_warning("Could not run wget: %s\n", g_strerror(errno));
	  _exit(0);
	}
      else if (pid > 0)
	{
	  int status;
	  waitpid (pid, &status, 0);

	  if (!status)
	    write (fds[1], "OK", 2);

	  _exit(0);
	}
      else
	{
	  g_warning ("Could not fork!\n");
	  _exit(0);
	}
    }
  else if (pid > 0)
    {
      /* Parent */

      ChildData *child_data;

      close(fds[1]);

      child_data = g_new (ChildData, 1);

      child_data->pid = pid;
      child_data->tag = gdk_input_add (fds[0], 
				       GDK_INPUT_READ, 
				       input_func, 
	                               child_data);
    }
  else
    g_warning ("Could not fork\n");
}




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]