Re: What is the minimum number of lines to update a gui window without user clicking a button



dispatcher() causes the function blocking_operation_finished() to be
called, which in turn calls Glib::Threads::Thread::join(). join() will
block the GTK+ main loop until the thread has completely finished
(i.e. blocking_operation() has returned).

Calling dispatcher() more than once means that
blocking_operation_finished() will be called more than once. I hope
you can understand why that is wrong.

Here is the next step in our example series. I have chosen to abstract
the ability to send parameters a function on the main thread through
the use of the CallbackDispatcher class. Its usage is simple: create a
functor that accepts zero arguments and pass it to send(). That
functor will be executed on the main loop.

A zero argument functor may be created from any function by using sigc::bind.

I have also chosen to use a Gtk::Label for this example, so as to
sidestep the issue of bug 495762.

Using a custom wrapper around Glib::Dispatcher to teach a newbie is,
perhaps, bad etiquette. But the utility of this wrapper in sending
progress back to the main loop has been so great in my experience that
I can't resist.
//========== Example 4 Begin ============//
#include <gtkmm.h>
#include <unistd.h> /* for sleep() */
#include <queue>

class CallbackDispatcher {
public:
    CallbackDispatcher() {
        dispatcher.connect(sigc::mem_fun(this,
&CallbackDispatcher::on_dispatch));
    }

    typedef sigc::slot<void> Message;
    void send(Message msg) {
        Glib::Threads::Mutex::Lock lock(mutex);
        queue.push(msg);
        dispatcher();
    }

private:
    /* CallbackDispatcher may not be copied, so we must hide these
     * constructors */
    CallbackDispatcher(const CallbackDispatcher&);
    CallbackDispatcher& operator=(const CallbackDispatcher&);

    Glib::Threads::Mutex mutex;
    std::queue<Message> queue;
    Glib::Dispatcher dispatcher;

    void on_dispatch() {
        Glib::Threads::Mutex::Lock lock(mutex);
        while (!queue.empty()) {
            queue.front()();
            queue.pop();
        }
    }
};

class Example {
private:
    Glib::RefPtr<Gtk::Application> app;
    Gtk::Window                    win;
    Gtk::Label                     label;
    CallbackDispatcher             callback_dispatcher;
    Glib::Threads::Thread         *thread;

    void report_progress(const Glib::ustring& str) {
        callback_dispatcher.send(sigc::bind(sigc::mem_fun(label,
&Gtk::Label::set_text), str));
    }

   /* This function is called on a separate thread so as to avoid
    * blocking GTK+'s main loop.
    */
    void blocking_operation() {
        /* This simulates a "blocking" process */
        sleep(5);
        report_progress("5% complete.");

        sleep(5);
        report_progress("15% complete.");

        sleep(5);
        report_progress("35% complete.");

        sleep(5);
        report_progress("55% complete.");

        sleep(5);
        report_progress("75% complete.");

        sleep(5);
        report_progress("95% complete.");

        sleep(5);
        report_progress("100% complete.");

        /* When the process is finished, we notify the GTK+ Main Loop by
         * using the dispatcher */
        callback_dispatcher.send(sigc::mem_fun(this,
&Example::blocking_operation_finished));
    };

    /* This function is called on GTK+'s main loop via a
     * Glib::Dispatcher */
    void blocking_operation_finished() {
        cleanup_thread();
        label.set_text("Operation finished.");
    };

    void cleanup_thread() {
        /* We must call Glib::Threads::Thread::join() in order to
         * correctly clean up the resource. */
        if (thread) {
            thread->join();
            thread = NULL;
        }
    }

public:
    ~Example() {
        /* This will prevent the Window from being closed while
         * the blocking operation is ongoing. */
        cleanup_thread();
    }

    Example(int argc, char *argv[]) :
        app(Gtk::Application::create(argc, argv, "com.example")),
        label(),
        thread(NULL)
    {
        win.add(label);
        win.show_all();
    }

    void run() {
        /* Create a slot to the blocking_operation() member function
         * to pass to Glib::Threads::Thread::create().
         */
        sigc::slot<void> op_functor = sigc::mem_fun(this,
&Example::blocking_operation);

        /* Create a worker thread to perform the blocking_operation
         * function.
         */
        thread = Glib::Threads::Thread::create(op_functor);

        /* Now set the text in the buffer.
         */
        label.set_text("Operation started.");

        app->run(win);
    }
};

int main(int argc, char *argv[]) {
    Example example(argc, argv);

    example.run();
    return 0;
}
//========== Example 4 End ============//


On Sat, Aug 10, 2013 at 5:39 AM, L. D. James <ljames apollo3 com> wrote:
I may be able to eventually figure out how to do it from your example, but
at present it doesn't appear to have the ability to append text, except for
an initial text and one addition.

I'm trying to be able to arbitrarily update the text numerous times (just
like you would with "cout").

I thought the dispatcher() function would work reusable like the gprint()
function I'm trying to create.  It appears to work correctly the first time.

The other updates doesn't occur when the dispatcher() function is called.
The gui window becomes non responsive and dimmed until all the functions are
completed, then everything is showed at once.  The window at the end of the
operation becomes a normal gui.

Kjell's example works exactly as intended, except that it has many widgets
and I can't figure out how to remove the widgets without breaking the code.
Also it requires a clicking of a button to start.

I realize that, everything that a person needs is included in his example.
My problem is being able to isolate a basic screen and an update.

I know that I'll eventually understand it, and I'd be able to break it down
for anyone else that might have problems with it, and reuse the many
components in various applications.

I realize how easy it would be for you and other experts to use his example.
But I can't figure out where to put my function and output a single line to
the gui, then perform a followup after some operations.

I also appreciate the your reference to how complicated this task is.
Hopefully I can contribute to making something complicated, easy for the
next person.

I added my example, using your code to demonstrate the problem that I'm
having.  I'm sure I'm misusing the dispatcher() function.  But I don't see
any other way to update the gui from your example.

Thanks again for your contribution and input!

// Code begin
//
--------------------------------------------------------------------------------------

#include <gtkmm.h>
#include <unistd.h> /* for sleep() */
#include <iostream>

using namespace std;

class Example {

string textdata = "uninitilzed";


private:
    Glib::RefPtr<Gtk::Application> app;
    Gtk::Window                    win;
    Gtk::TextView                  tv;
    Glib::RefPtr<Gtk::TextBuffer>  tb;
    Glib::Dispatcher               dispatcher;
    Glib::Threads::Thread         *thread;

   /* This function is called on a separate thread so as to avoid
    * blocking GTK+'s main loop.
    */
   void blocking_operation() {
        /* This simulates a "blocking" process */
        sleep(5);

        textdata += "First function is complete.\n";


        /* When the process is finished, we "notify" the GTK+ Main Loop by
         * using the dispatcher */
        dispatcher();

        sleep(5);
        textdata += "Second function is completed.\n";
        dispatcher();

        sleep(5);
        textdata += "Third function is completed.\n";
        dispatcher();

        sleep(5);
        textdata += "Fourth function is completed.\n";
        textdata += "This next funtion may take up to three hours to
complete.\n";
        dispatcher();

        sleep(60);
        textdata += "Fifth function is completed.\n";

        dispatcher();
   };

    /* This function is called on GTK+'s main loop via a
     * Glib::Dispatcher */
    void blocking_operation_finished() {
        cleanup_thread();
        tb->set_text(textdata);

    };

    void cleanup_thread() {
        /* We must call Glib::Threads::Thread::join() in order to
         * correctly clean up the resource. */
        if (thread) {
            thread->join();
            thread = NULL;
        }
    }

public:
    ~Example() {
        /* This will prevent the Window from being closed while
         * the blocking operation is ongoing. */
        cleanup_thread();
    }

    Example(int argc, char *argv[]) : app(Gtk::Application::create(argc,
argv, "com.example")), tb(tv.get_buffer()), thread(NULL)
    {
        /* Create a slot (a.k.a. functor) to the
         * blocking_operation_finished() member function for the
         * dispatcher to execute.
         */
        sigc::slot<void> op_finished_functor = sigc::mem_fun(this,
&Example::blocking_operation_finished);

        /* The dispatcher will be used when the operation has finished.
         * Here we set the function the dispatcher will call on the "main
         * thread" -- a.k.a. GTK+'s Main Loop.
         */
        dispatcher.connect(op_finished_functor);

        /* Now set the text in the buffer.
         */
        textdata = "Starting application.\n";
        tb->set_text(textdata);


        win.add(tv);
        win.show_all();
    }

    void run() {
        /* Create a slot to the blocking_operation() member function
         * to pass to Glib::Threads::Thread::create().
         */
        sigc::slot<void> op_functor = sigc::mem_fun(this,
&Example::blocking_operation);

        /* Create a worker thread to perform the blocking_operation
         * function.
         */
        thread = Glib::Threads::Thread::create(op_functor);

        app->run(win);
    }
};

int main(int argc, char *argv[]) {
    Example example(argc, argv);

    example.run();
    return 0;
}
//
--------------------------------------------------------------------------------------
// code end




-- L. James

--
L. D. James
ljames apollo3 com
www.apollo3.com/~ljames


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