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



Thanks, Andrew.  I'll study and compile your example and comment on if it's the gist of what I'm trying to accomplish.

Looking at it, it appears like it will work.  However, I'm not trying to send progress statistics, I'm just trying to send general information.  Some of my programs sorts and index, and archives email messages.  It may send a message to the console that there are currently 1000 emails about to be sorted.  Then it's cross reference the headers with messages addresses in the users' address book.  It'll output to the console that there are 500 email addresses that are not in the address book and sort them into a separate area.  These are all messages to a black console.

Many of my customers are used to seeing the black screen.  I'm just want to take my 10's of programs and have many of them output the text to a graphics screen rather than the console screen.

I will find it convenient to create a function call gprint().  Where I have "cout" ether replace the "cout" with "gprint" or use both, but the "gprint()" will just update a gui with a message that currently goes to a black screen.

I apologize for repeating this so much.  I have read numerous times the "code of conduct" for this forum.  I'm trying to comply and not to aggravate the membership with repetition and verbose messages.  But most of the comments I get gives me the impression that the members are misunderstanding what I'm looking for.

Just like gtkmm is a convenient wrapper for gtk++, I hope to make a gprint() routine to be a wapper for however many lines it takes to create a gui window and be able to run a one or two liner to please a new message to that gui window.

At a glance it appears that the code you have below might have the gist, even though it's referencing percentages of progress instead of regular text output informing the user what the application is currently doing, and what it might do next, or informing the user of any errors or problems it might be encountering.

I really apologize for the many words to try to be clear.  I just posted an example of one of my C++ applications.  I have done it with Java and might post a Java application that perfectly fits the bill (as for as having a window and sending updates to that window).  The problem with the Java, is that you can't set the suid bit for some of the routines that needs to be run as root.

I was doing that with a C++ wrapper to call the Java program.  But now, I'm trying to do it all in C++ with gtkmm.

Once I develop the "gprint()" function, I'm sure this will totally make things clear.  I'm sure that many will find it a convenient resource.

-- L. James

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

On Sat, 2013-08-10 at 12:23 -0700, Andrew Potter wrote:
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]