On Fri, Jun 14, 2013 at 8:28 AM, Kip Warner <k...@thevertigo.com> wrote:
> On Thu, 2013-06-13 at 08:59 +0100, jcup...@gmail.com wrote:
>> Hi Kip,
>
> Hey John,
>
>> There are two easy ways to do a long operation in Python.
>>
>> First, with idle_add(). Your callback should run for no more than 50ms
>> or so before returning. If you need to do more work than that, just
>> wait to be called again. Do not process events, you can leave that up
>> to the main loop. This style is handy for non-blocking, background
>> tasks that don't need interaction from the user.
>
> Ok, fair enough. I didn't know idle callbacks were intended to be used
> only for fast non-blocking tasks.
>
>> Secondly, with a regular loop that takes control for a long period.
>> You can keep the GUI alive by processing events, as you say above.
>> This style is better for modal actions that either block the GUI or
>> may require interaction.
>
> So as I mentioned I tried abandoning the idle_add() approach and instead
> relied on calling the worker function directly.
>
>> It sounds like you have done both at the same time, which seems
>> confusing to me. I'd make a pure 2) one. If the GUI doesn't refresh,
>> you probably have a bug in your code somewhere.
>
> I do this by calling the long job function directly from within the
> GtkAssistant's "prepare" signal callback immediately. The problem is
> that the GUI doesn't refresh throughout the duration of the long job,
> even though I do explicitly pump the message queue by calling
> _updateGUI() method regularly:

Kip,
   Let me try to illustrate what you need to do to make this work.

What you currently have is:
~~~~~~~~~~~~~~~~~~~~~

prepare_signal_callback ()
{
    /* Update the GUI and flush the event queue, only once, at the
beginning of your operation */
    while (gtk_events_pending())
        gtk_main_iteration_do ();


    /* Process some data that takes a long time, without ever again
updating the GUI */
    while (there_is_data)
        process_data();
}

What you need to do instead is:
~~~~~~~~~~~~~~~~~~~~~~~

prepare_signal_callback ()
{
    /* Do your huge intensive loop that takes a long time */
    while (there_is_data)
    {
        /* Process a chunk of your long data operation */
        process_only_a_little_bit_of_data();

        /* Every once and a while, during your huge task, be friendly
to the user and show
         * some feedback, i.e. update the GUI often enough so that
things appear to run smoothly.
         */
        while (gtk_events_pending())
            gtk_main_iteration_do ();

    }
}

What you seem to be missing, is that GTK+ doesnt update itself in a
separate thread, your program
by default is single threaded and can only be processing data or
updating the GUI at a given time,
but not both.

If you have to call a function in a proprietary library which you
cannot modify, and that function
atomically takes a long time, the only way to keep the GUI responsive
during that time is to
add your own worker thread to call the function, and then signal the
main thread when the
operation completes (usually by queuing a g_idle_add() at the end of
your worker thread
so that the completion notification callback is queued to execute in
the main GUI thread).

Cheers,
    -Tristan

>
>     # Update the GUI...
>     def _updateGUI(self):
>
>         # Update GUI...
>         (...)
>
>         # Flush the event queue so we don't block...
>         while Gtk.events_pending():
>             Gtk.main_iteration()
>
> The GUI just appears to hang on the previously displayed page in the
> assistant and doesn't advance to the one that actually should be visible
> following the "prepare" signal callback.
_______________________________________________
gtk-app-devel-list mailing list
gtk-app-devel-list@gnome.org
https://mail.gnome.org/mailman/listinfo/gtk-app-devel-list

Reply via email to