[gtkmm-documentation] Add a multi-threaded example program.



commit 98c676fbeff0f0d5a339acac28cebebccdadcd35
Author: Kjell Ahlstedt <kjell ahlstedt bredband net>
Date:   Wed Aug 7 09:55:05 2013 +0200

    Add a multi-threaded example program.
    
    * docs/tutorial/C/gtkmm-tutorial-in.xml: Add an Example section in the
    Multi-Threaded Programs chapter.
    * docs/tutorial/C/figures/multithread.png: New file.
    * docs/tutorial/Makefile.am: Add multithread.png.
    * examples/Makefile.am: Add the book/multithread example program.
    * examples/book/multithread/examplewindow.[h|cc]:
    * examples/book/multithread/exampleworker.[h|cc]:
    * examples/book/multithread/main.cc: New files.

 docs/tutorial/C/figures/multithread.png    |  Bin 0 -> 18549 bytes
 docs/tutorial/C/gtkmm-tutorial-in.xml      |   29 +++++
 docs/tutorial/Makefile.am                  |    1 +
 examples/Makefile.am                       |    8 ++
 examples/book/multithread/examplewindow.cc |  171 ++++++++++++++++++++++++++++
 examples/book/multithread/examplewindow.h  |   57 +++++++++
 examples/book/multithread/exampleworker.cc |   99 ++++++++++++++++
 examples/book/multithread/exampleworker.h  |   46 ++++++++
 examples/book/multithread/main.cc          |   27 +++++
 9 files changed, 438 insertions(+), 0 deletions(-)
---
diff --git a/docs/tutorial/C/figures/multithread.png b/docs/tutorial/C/figures/multithread.png
new file mode 100644
index 0000000..3825d22
Binary files /dev/null and b/docs/tutorial/C/figures/multithread.png differ
diff --git a/docs/tutorial/C/gtkmm-tutorial-in.xml b/docs/tutorial/C/gtkmm-tutorial-in.xml
index 8c8927d..d06af01 100644
--- a/docs/tutorial/C/gtkmm-tutorial-in.xml
+++ b/docs/tutorial/C/gtkmm-tutorial-in.xml
@@ -7469,6 +7469,35 @@ object could of course be used instead.
 
 </sect1>
 
+<sect1 id="sec-multithread-example">
+<title>Example</title>
+<para>
+This is an example program with two threads, one GUI thread, like in all
+&gtkmm; programs, and one worker thread. The worker thread is created when you
+press the <literal>Start work</literal> button. It is deleted when the work is
+finished, when you press the <literal>Stop work</literal> button, or when you
+press the <literal>Quit</literal> button.
+</para>
+
+<para>
+A <classname>Glib::Dispatcher</classname> is used for sending notifications
+from the worker thread to the GUI thread. The <classname>ExampleWorker</classname>
+class contains data which is accessed by both threads. This data is protected
+by a <classname>Glib::Threads::Mutex</classname>.
+Only the GUI thread updates the GUI.
+</para>
+
+<figure id="figure-multithread">
+  <title>Multi-Threaded Program</title>
+  <screenshot>
+    <graphic format="PNG" fileref="&url_figures_base;multithread.png"/>
+  </screenshot>
+</figure>
+
+<para><ulink url="&url_examples_base;multithread">Source Code</ulink></para>
+
+</sect1>
+
 </chapter>
 
 <chapter id="chapter-recommended-techniques">
diff --git a/docs/tutorial/Makefile.am b/docs/tutorial/Makefile.am
index 972d0e5..eac11ae 100644
--- a/docs/tutorial/Makefile.am
+++ b/docs/tutorial/Makefile.am
@@ -75,6 +75,7 @@ DOC_FIGURES =                                 \
        figures/menus_and_toolbars.png          \
        figures/menus_menu.png                  \
        figures/menus_menubar.png               \
+       figures/multithread.png         \
        figures/notebook.png                    \
        figures/paned.png                       \
        figures/printing.png                    \
diff --git a/examples/Makefile.am b/examples/Makefile.am
index d82731a..a1ad851 100644
--- a/examples/Makefile.am
+++ b/examples/Makefile.am
@@ -74,6 +74,7 @@ check_PROGRAMS =                                      \
        book/menus/main_menu/main_menu                  \
        book/menus/popup/popup                          \
        book/menus_and_toolbars/example                 \
+       book/multithread/example                        \
        book/notebook/example                           \
        book/paned/example                              \
        book/printing/advanced/example                  \
@@ -476,6 +477,13 @@ book_menus_and_toolbars_example_SOURCES =  \
        book/menus_and_toolbars/examplewindow.h \
        book/menus_and_toolbars/main.cc
 
+book_multithread_example_SOURCES =     \
+       book/multithread/examplewindow.cc\
+       book/multithread/examplewindow.h        \
+       book/multithread/exampleworker.cc\
+       book/multithread/exampleworker.h        \
+       book/multithread/main.cc
+
 book_notebook_example_SOURCES =                \
        book/notebook/examplewindow.cc  \
        book/notebook/examplewindow.h   \
diff --git a/examples/book/multithread/examplewindow.cc b/examples/book/multithread/examplewindow.cc
new file mode 100644
index 0000000..fe50dc4
--- /dev/null
+++ b/examples/book/multithread/examplewindow.cc
@@ -0,0 +1,171 @@
+/* gtkmm example Copyright (C) 2013 gtkmm development team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "examplewindow.h"
+#include <iostream>
+
+ExampleWindow::ExampleWindow() :
+  m_VBox(Gtk::ORIENTATION_VERTICAL, 5),
+  m_ButtonBox(Gtk::ORIENTATION_HORIZONTAL),
+  m_ButtonStart("Start work"),
+  m_ButtonStop("Stop work"),
+  m_ButtonQuit("_Quit", /* mnemonic= */ true),
+  m_ProgressBar(),
+  m_ScrolledWindow(),
+  m_TextView(),
+  m_Dispatcher(),
+  m_Worker(),
+  m_WorkerThread(0)
+{
+  set_title("Multi-threaded example");
+  set_border_width(5);
+  set_default_size(300, 300);
+
+  add(m_VBox);
+
+  // Add the ProgressBar.
+  m_VBox.pack_start(m_ProgressBar, Gtk::PACK_SHRINK);
+
+  m_ProgressBar.set_text("Fraction done");
+  m_ProgressBar.set_show_text();
+
+  // Add the TextView, inside a ScrolledWindow.
+  m_ScrolledWindow.add(m_TextView);
+
+  // Only show the scrollbars when they are necessary.
+  m_ScrolledWindow.set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
+
+  m_VBox.pack_start(m_ScrolledWindow);
+
+  m_TextView.set_editable(false);
+
+  // Add the buttons to the ButtonBox.
+  m_VBox.pack_start(m_ButtonBox, Gtk::PACK_SHRINK);
+
+  m_ButtonBox.pack_start(m_ButtonStart, Gtk::PACK_SHRINK);
+  m_ButtonBox.pack_start(m_ButtonStop, Gtk::PACK_SHRINK);
+  m_ButtonBox.pack_start(m_ButtonQuit, Gtk::PACK_SHRINK);
+  m_ButtonBox.set_border_width(5);
+  m_ButtonBox.set_spacing(5);
+  m_ButtonBox.set_layout(Gtk::BUTTONBOX_END);
+
+  // Connect the signal handlers to the buttons.
+  m_ButtonStart.signal_clicked().connect(sigc::mem_fun(*this, &ExampleWindow::on_start_button_clicked));
+  m_ButtonStop.signal_clicked().connect(sigc::mem_fun(*this, &ExampleWindow::on_stop_button_clicked));
+  m_ButtonQuit.signal_clicked().connect(sigc::mem_fun(*this, &ExampleWindow::on_quit_button_clicked));
+
+  // Connect the handler to the dispatcher.
+  m_Dispatcher.connect(sigc::mem_fun(*this, &ExampleWindow::on_notification_from_worker_thread));
+
+  // Create a text buffer mark for use in update_widgets().
+  Glib::RefPtr<Gtk::TextBuffer> buffer = m_TextView.get_buffer();
+  buffer->create_mark("last_line", buffer->end(), /* left_gravity= */ true);
+
+  update_start_stop_buttons();
+
+  show_all_children();
+}
+
+void ExampleWindow::on_start_button_clicked()
+{
+  if (m_WorkerThread)
+  {
+    std::cout << "Can't start a worker thread while another one is running." << std::endl;
+  }
+  else
+  {
+    // Start a new worker thread.
+    m_WorkerThread = Glib::Threads::Thread::create(
+      sigc::bind(sigc::mem_fun(m_Worker, &ExampleWorker::do_work), this));
+  }
+  update_start_stop_buttons();
+}
+
+void ExampleWindow::on_stop_button_clicked()
+{
+  if (!m_WorkerThread)
+  {
+    std::cout << "Can't stop a worker thread. None is running." << std::endl;
+  }
+  else
+  {
+   // Order the worker thread to stop.
+    m_Worker.stop_work();
+    m_ButtonStop.set_sensitive(false);
+  }
+}
+
+void ExampleWindow::update_start_stop_buttons()
+{
+  const bool thread_is_running = m_WorkerThread != 0;
+
+  m_ButtonStart.set_sensitive(!thread_is_running);
+  m_ButtonStop.set_sensitive(thread_is_running);
+}
+
+void ExampleWindow::update_widgets()
+{
+  double fraction_done;
+  Glib::ustring message_from_worker_thread;
+  m_Worker.get_data(&fraction_done, &message_from_worker_thread);
+
+  m_ProgressBar.set_fraction(fraction_done);
+
+  if (message_from_worker_thread != m_TextView.get_buffer()->get_text())
+  {
+    Glib::RefPtr<Gtk::TextBuffer> buffer = m_TextView.get_buffer();
+    buffer->set_text(message_from_worker_thread);
+
+    // Scroll the last inserted line into view. That's somewhat complicated.
+    Gtk::TextIter iter = buffer->end();
+    iter.set_line_offset(0); // Beginning of last line
+    Glib::RefPtr<Gtk::TextMark> mark = buffer->get_mark("last_line");
+    buffer->move_mark(mark, iter);
+    m_TextView.scroll_to(mark);
+    // TextView::scroll_to(iter) is not perfect.
+    // We do need a TextMark to always get the last line into view.
+  }
+}
+
+void ExampleWindow::on_quit_button_clicked()
+{
+  if (m_WorkerThread)
+  {
+    // Order the worker thread to stop and wait for it to stop.
+    m_Worker.stop_work();
+    m_WorkerThread->join();
+  }
+  hide();
+}
+
+// notify() is called from ExampleWorker::do_work(). It is executed in the worker
+// thread. It triggers a call to on_notification_from_worker_thread(), which is
+// executed in the GUI thread.
+void ExampleWindow::notify()
+{
+  m_Dispatcher.emit();
+}
+
+void ExampleWindow::on_notification_from_worker_thread()
+{
+  if (m_WorkerThread && m_Worker.has_stopped())
+  {
+    // Work is done.
+    m_WorkerThread->join();
+    m_WorkerThread = 0;
+    update_start_stop_buttons();
+  }
+  update_widgets();
+}
diff --git a/examples/book/multithread/examplewindow.h b/examples/book/multithread/examplewindow.h
new file mode 100644
index 0000000..520be0a
--- /dev/null
+++ b/examples/book/multithread/examplewindow.h
@@ -0,0 +1,57 @@
+/* gtkmm example Copyright (C) 2013 gtkmm development team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTKMM_EXAMPLEWINDOW_H
+#define GTKMM_EXAMPLEWINDOW_H
+
+#include <gtkmm.h>
+#include "exampleworker.h"
+
+class ExampleWindow : public Gtk::Window
+{
+public:
+  ExampleWindow();
+
+  // Called from the worker thread.
+  void notify();
+
+private:
+  // Signal handlers.
+  void on_start_button_clicked();
+  void on_stop_button_clicked();
+  void on_quit_button_clicked();
+
+  void update_start_stop_buttons();
+  void update_widgets();
+
+  // Dispatcher handler.
+  void on_notification_from_worker_thread();
+
+  // Member data.
+  Gtk::Box m_VBox;
+  Gtk::ButtonBox m_ButtonBox;
+  Gtk::Button m_ButtonStart;
+  Gtk::Button m_ButtonStop;
+  Gtk::Button m_ButtonQuit;
+  Gtk::ProgressBar m_ProgressBar;
+  Gtk::ScrolledWindow m_ScrolledWindow;
+  Gtk::TextView m_TextView;
+
+  Glib::Dispatcher m_Dispatcher;
+  ExampleWorker m_Worker;
+  Glib::Threads::Thread* m_WorkerThread;
+};
+
+#endif // GTKMM_EXAMPLEWINDOW_H
diff --git a/examples/book/multithread/exampleworker.cc b/examples/book/multithread/exampleworker.cc
new file mode 100644
index 0000000..e5cf2f7
--- /dev/null
+++ b/examples/book/multithread/exampleworker.cc
@@ -0,0 +1,99 @@
+/* gtkmm example Copyright (C) 2013 gtkmm development team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "exampleworker.h"
+#include "examplewindow.h"
+#include <sstream>
+
+ExampleWorker::ExampleWorker() :
+  m_Mutex(),
+  m_shall_stop(false),
+  m_has_stopped(false),
+  m_fraction_done(0.0),
+  m_message()
+{
+}
+
+// Accesses to these data are synchronized by a mutex.
+// Some microseconds can be saved by getting all data at once, instead of having
+// separate get_fraction_done() and get_message() methods.
+void ExampleWorker::get_data(double* fraction_done, Glib::ustring* message) const
+{
+  Glib::Threads::Mutex::Lock lock(m_Mutex);
+
+  if (fraction_done)
+    *fraction_done = m_fraction_done;
+
+  if (message)
+    *message = m_message;
+}
+
+void ExampleWorker::stop_work()
+{
+  Glib::Threads::Mutex::Lock lock(m_Mutex);
+  m_shall_stop = true;
+}
+
+bool ExampleWorker::has_stopped() const
+{
+  Glib::Threads::Mutex::Lock lock(m_Mutex);
+  return m_has_stopped;
+}
+
+void ExampleWorker::do_work(ExampleWindow* caller)
+{
+  {
+    Glib::Threads::Mutex::Lock lock(m_Mutex);
+    m_has_stopped = false;
+    m_fraction_done = 0.0;
+    m_message = "";
+  } // The mutex is unlocked here by lock's destructor.
+
+  // Simulate a long calculation.
+  for (int i = 0; ; ++i) // do until break
+  {
+    Glib::usleep(250000); // microseconds
+
+    Glib::Threads::Mutex::Lock lock(m_Mutex);
+
+    m_fraction_done += 0.01;
+
+    if (i % 4 == 3)
+    {
+      std::ostringstream ostr;
+      ostr << (m_fraction_done * 100.0) << "% done\n";
+      m_message += ostr.str();
+    }
+
+    if (m_fraction_done >= 1.0)
+    {
+      m_message += "Finished";
+      break;
+    }
+    if (m_shall_stop)
+    {
+      m_message += "Stopped";
+      break;
+    }
+    lock.release();
+    caller->notify();
+  }
+
+  Glib::Threads::Mutex::Lock lock(m_Mutex);
+  m_shall_stop = false;
+  m_has_stopped = true;
+  lock.release();
+  caller->notify();
+}
diff --git a/examples/book/multithread/exampleworker.h b/examples/book/multithread/exampleworker.h
new file mode 100644
index 0000000..27ac005
--- /dev/null
+++ b/examples/book/multithread/exampleworker.h
@@ -0,0 +1,46 @@
+/* gtkmm example Copyright (C) 2013 gtkmm development team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef GTKMM_EXAMPLEWORKER_H
+#define GTKMM_EXAMPLEWORKER_H
+
+#include <gtkmm.h>
+
+class ExampleWindow;
+
+class ExampleWorker
+{
+public:
+  ExampleWorker();
+
+  // Thread function.
+  void do_work(ExampleWindow* caller);
+
+  void get_data(double* fraction_done, Glib::ustring* message) const;
+  void stop_work();
+  bool has_stopped() const;
+
+private:
+  // Synchronizes access to member data.
+  mutable Glib::Threads::Mutex m_Mutex;
+
+  // Data used by both GUI thread and worker thread.
+  bool m_shall_stop;
+  bool m_has_stopped;
+  double m_fraction_done;
+  Glib::ustring m_message;
+};
+
+#endif // GTKMM_EXAMPLEWORKER_H
diff --git a/examples/book/multithread/main.cc b/examples/book/multithread/main.cc
new file mode 100644
index 0000000..3d7a9e6
--- /dev/null
+++ b/examples/book/multithread/main.cc
@@ -0,0 +1,27 @@
+/* gtkmm example Copyright (C) 2013 gtkmm development team
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "examplewindow.h"
+#include <gtkmm/application.h>
+
+int main (int argc, char* argv[])
+{
+  Glib::RefPtr<Gtk::Application> app = Gtk::Application::create(argc, argv, "org.gtkmm.example");
+
+  ExampleWindow window;
+
+  //Shows the window and returns when it is closed.
+  return app->run(window);
+}


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