I missed the list in my last reply, so I'm copying it here for the record. In addition to using a cancelable I changed CallbackDispatcher to use a recursive mutex so gui_print can be called from the main thread as well.
While providing such an in depth example to L. James maybe goes against the learn-the-fundamentals-and-help-yourself culture, I'd just like to have a final answer and close the thread after a month and 100+ messages. ---------- Forwarded message ---------- From: "Andrew Potter" <agpot...@gmail.com> Date: Aug 20, 2013 11:22 AM Subject: Re: What is the minimum number of lines to update a gui window without user clicking a button To: "L. D. James" <lja...@apollo3.com> Cc: > On Tue, Aug 20, 2013 at 6:01 AM, L. D. James <lja...@apollo3.com> wrote: > > When clicking on the close button to cancel the operation the gui window > > dims ad become non-responsive... locked up. That is a problem, in that some > > of the programs might only take seconds or minutes to complete. > > I designed it that way because I didn't know if it was safe to have > your blocking operation terminated suddenly; instead it ensures that > your blocking operation runs until it fully completes. If it is safe > to stop at any point then you can remove thread_cleanup() from > Example::~Example(). Otherwise it is best to periodically check in the > blocking operation whether the window has been closed. > Gio::Cancellable can be used for this. > > // Example 6 > #include <gtkmm.h> > #include <unistd.h> > #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::RecMutex::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::RecMutex mutex; > std::queue<Message> queue; > Glib::Dispatcher dispatcher; > > void on_dispatch() { > Glib::Threads::RecMutex::Lock lock(mutex); > while (!queue.empty()) { > queue.front()(); > queue.pop(); > } > } > }; > > class Example { > private: > Glib::RefPtr<Gtk::Application> app; > Gtk::Window win; > Gtk::TextView tv; > Glib::RefPtr<Gtk::TextBuffer> tb; > CallbackDispatcher callback_dispatcher; > Glib::Threads::Thread *thread; > Glib::RefPtr<Gio::Cancellable> cancellable; > > /* Appends str to the textbuffer. Like all methods that use the > * Gtk namespace, it is not safe to call from another thread. */ > void gui_print_cb(const Glib::ustring& str) { > tb->insert(tb->end(), str); > } > > /* Provides threadsafe access to gui_print_cb() */ > void gui_print(const Glib::ustring& str) { > sigc::slot<void> slot = sigc::bind(sigc::mem_fun(this, > &Example::gui_print_cb), str); > callback_dispatcher.send(slot); > } > > /* 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); > gui_print("5% complete.\n"); > if (cancellable->is_cancelled()) return; > > sleep(5); > gui_print("15% complete.\n"); > if (cancellable->is_cancelled()) return; > > sleep(5); > gui_print("35% complete.\n"); > if (cancellable->is_cancelled()) return; > > sleep(5); > gui_print("55% complete.\n"); > if (cancellable->is_cancelled()) return; > > sleep(5); > gui_print("75% complete.\n"); > if (cancellable->is_cancelled()) return; > > sleep(5); > gui_print("95% complete.\n"); > if (cancellable->is_cancelled()) return; > > sleep(5); > gui_print("100% complete.\n"); > > /* 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(); > gui_print("Operation finished.\n"); > }; > > 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. */ > cancellable->cancel(); > cleanup_thread(); > } > > Example(int argc, char *argv[]) : > app(Gtk::Application::create(argc, argv, "com.example")), > tb(tv.get_buffer()), > thread(NULL), > cancellable(Gio::Cancellable::create()) > { > 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); > > /* Now set the text in the buffer. */ > gui_print("Operation started.\n"); > > app->run(win); > } > }; > > int main(int argc, char *argv[]) { > Example example(argc, argv); > > example.run(); > return 0; > } > // Example 6
_______________________________________________ gtkmm-list mailing list gtkmm-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtkmm-list