gtkmm Beispiel Programm Pt. 3 ( Multithreaded )



  • mywindow.hpp

    #ifndef GUARD_MY_WINDOW_HPP_INCLUDED
    #define GUARD_MY_WINDOW_HPP_INCLUDED 1
    
    #include <boost/noncopyable.hpp>
    #include <gtkmm.h>
    
    class MyWindow : public Gtk::Window, boost::noncopyable
    {
      public:
        MyWindow();
        virtual ~MyWindow();
    
        // on_button_click wird ausgeführt wenn der User den Button auf unserem 
        // Fenster anklickt
        void on_button_click();
    
        // Diese Methode wird als seperater Thread gestartet
        // Sie sorgt dann für die Animation des ProgressBar ohne die ganze Anwendung
        // zu blockieren
        void thread_worker(); 
    
      /* Das machen wir mit boost::noncopyable 
      private: // Nicht kopierbar! 
        MyWindow( MyWindow const & );
        MyWindow const & operator=( MyWindow const & );
      */
      private: // Deklaration der Variablen
    
        // Diese Variable brauchen wir um das Ende des zweiten Threads 
        // zu veranlassen
        bool             m_end_thread;
    
        // Ich denke das ich mir zu diesen Widgets die Kommentare sparen kann
        // da diese bereits in den Examples I und II bereits benutzt worden ist
        Gtk::VBox        m_vbox;
        Gtk::Button      m_button;
        Gtk::Label       m_label;
    
        // ProgressBar zeigt an wie weit ein Prozess bereits ist
        // Ausserdem hat sie die pulse eigenschaft, die anzeigt das etwas getan
        // wird. Diese Pulse eigenschaft werden wir jetz einmal benutzen da
        // sie imho die interessantere ist ;) 
        Gtk::ProgressBar m_pbar;
    
        // Da wir eine Multi Threaded Anwendung schreiben müssen wird eine
        // Möglichkeit finden wie wir problemlos auf Daten/Funktionen/Methoden
        // in einem anderen Thread zugreifen kann. Dies kann man dann via
        // einem Glib::Dispatcher realisieren.
        Glib::Dispatcher m_dispatcher;
    
        // Speichert den Zeiger auf den Thread ( zum beenden ) 
        Glib::Thread *   m_thread;
    };
    
    #endif //GUARD_MY_WINDOW_HPP_INCLUDED
    

    mywindow.cpp

    #include <boost/lexical_cast.hpp>
    #include "mywindow.hpp"
    
    Glib::StaticMutex mutex = GLIBMM_STATIC_MUTEX_INIT;
    
    MyWindow::MyWindow()
    : m_end_thread(false), // Der Thread soll laufen
      m_vbox(false,10),    // Gtk::VBox soll die Elemente nicht homogen anlegen
                           // und 10 Pixel platz zwischen den einzelnen Elementen lassen
      m_button(Glib::locale_to_utf8("Click mich")) // Button text setzen
    {
      add(m_vbox); // Hier verbinden wir das Gtk::VBox mit dem Fenster
    
      m_vbox.set_border_width(10); // Randbreite des Gtk::VBox auf 10 Pixel setzen
    
      m_vbox.pack_start(m_label);  // Verbinden des Gtk::Label mit dem Gtk::VBox
      m_vbox.pack_start(m_pbar);   // Verbinden des Gtk::ProgressBar mit dem Gtk::VBox                
      m_vbox.pack_start(m_button); // Verbinden des Gtk::Button mit dem Gtk::VBox                 
    
      m_pbar.set_pulse_step(0.05f); // Schrittgröße für pulse() setzen / 0.0 - 1.0
    
      // Dem Button die Methode, die er beim clicked Signal ausführen soll, als 
      // Funktor übergeben
      m_button.signal_clicked().connect(sigc::mem_fun(*this,&MyWindow::on_button_click));
    
      // Hier wird die Methode die der Dispatcher beim aufruf ausführen soll
      // als Funktor übergeben
      m_dispatcher.connect( sigc::mem_fun( m_pbar , &Gtk::ProgressBar::pulse ));
    
      // Hier starten wir den 2. Thread (der erste ist der Hauptthread!) der für 
      // uns die ProgressBar - Arbeit erledigen wird.
      // Hierzu übergeben wir die thread_worker Methode als Funktor.
      // das 'true' sagt dem Thread das er joinable sein soll!
      // Was auch anhand des Pointers der auf den Thread zeigt und zurückgegeben 
      // wird überprüft werden. 
      m_thread = Glib::Thread::create( sigc::mem_fun(*this,&MyWindow::thread_worker),true);
    
      // Nun lassen wir alle Elemente des Fensters anzeigen.
      show_all(); 
    }
    
    MyWindow::~MyWindow()
    {
      // Ein Anonymouer Scope innerhalb einer Funktion/Methode hat den
      // Zweck das darin erstellte Objekte beim verlassen zerstört werden
      // wir haben hier ein Mutex::Lock objekt erstellt das bei erstellung
      // das lock setzt und bei der Zerstörung wieder unlockt
      {
        Glib::Mutex::Lock lock(mutex);                    
        if(m_end_thread)       // Wenn der Thread noch läuft
           m_end_thread = true; // sagen wir dem Thread das er beenden soll
      }
      if(m_thread->joinable())
        m_thread->join();
    }
    
    void MyWindow::on_button_click()
    {
       static unsigned long count_clicked = 0;
       // Hier konvertieren wir den Wert von count_clicked und incrementieren in sogleich
       // und übergeben dann den Wert als Glib::ustring an unser Label weiter
       // Somit haben wir dann einen Click counter ;)
       m_label.set_text(boost::lexical_cast<Glib::ustring>(count_clicked++)); 
    }
    
    void MyWindow::thread_worker()
    {
      while(1)
      {
        Glib::usleep(50000);  // 50.000 Mikrosekunden warten
    
        m_dispatcher();       // Threadsafer Aufruf der pulse()-Methode 
    
        // Dies ist ein anonymer scope [Erklärung siehe Destruktor Defintion]
        {
          Glib::Mutex::Lock lock(mutex);
          if(m_end_thread)      // Müssen wir beenden?
            return;
        }// Ende des anonymen scopes, das den effekt hat das das mutex wir unlocked wird
      }
    }
    

    Dies mal hat sich etwas an der Eintritts-Funktion geändert:

    main.cpp

    int main(int argc, char**argv)
    {
      // Die einzige Änderung hier ist dieser Aufruf:
      Glib::thread_init();
      // Mit dem Glib::thread_init aufruf wird dafür gesorgt das 
      // das Gtk+/Glib Subsystem darauf vorbereitet wird das wir
      // mehrere Threads benutzen.
    
      Gtk::Main main_obj(argc,argv);
    
      MyWindow window_obj;
    
      main_obj.run(window_obj);
    
      return EXIT_SUCCESS;  
    }
    


  • #include <boost/noncopyable.hpp>
    #include <boost/lexical_cast.hpp>
    #include <gtkmm.h>
    
    class MyWindow : public Gtk::Window, boost::noncopyable
    {
      public:
        MyWindow();
        virtual ~MyWindow();
    
        // on_button_click wird ausgeführt wenn der User den Button auf unserem 
        // Fenster anklickt
        void on_button_click();
    
        // Diese Methode wird als seperater Thread gestartet
        // Sie sorgt dann für die Animation des ProgressBar ohne die ganze Anwendung
        // zu blockieren
        void thread_worker(); 
    
      /* Das machen wir mit boost::noncopyable 
      private: // Nicht kopierbar! 
        MyWindow( MyWindow const & );
        MyWindow const & operator=( MyWindow const & );
      */
      private: // Deklaration der Variablen
    
        // Diese Variable brauchen wir um das Ende des zweiten Threads 
        // zu veranlassen
        bool             m_end_thread;
    
        // Ich denke das ich mir zu diesen Widgets die Kommentare sparen kann
        // da diese bereits in den Examples I und II bereits benutzt worden ist
        Gtk::VBox        m_vbox;
        Gtk::Button      m_button;
        Gtk::Label       m_label;
    
        // ProgressBar zeigt an wie weit ein Prozess bereits ist
        // Ausserdem hat sie die pulse eigenschaft, die anzeigt das etwas getan
        // wird. Diese Pulse eigenschaft werden wir jetz einmal benutzen da
        // sie imho die interessantere ist ;) 
        Gtk::ProgressBar m_pbar;
    
        // Da wir eine Multi Threaded Anwendung schreiben müssen wird eine
        // Möglichkeit finden wie wir problemlos auf Daten/Funktionen/Methoden
        // in einem anderen Thread zugreifen kann. Dies kann man dann via
        // einem Glib::Dispatcher realisieren.
        Glib::Dispatcher m_dispatcher;
    
        // Speichert den Zeiger auf den Thread ( zum beenden )
        Glib::Thread * m_thread;
    
    };
    
    Glib::StaticMutex mutex = GLIBMM_STATIC_MUTEX_INIT;
    
    MyWindow::MyWindow()
    : m_end_thread(false), // Der Thread soll laufen
      m_vbox(false,10),    // Gtk::VBox soll die Elemente nicht homogen anlegen
                           // und 10 Pixel platz zwischen den einzelnen Elementen lassen
      m_button(Glib::locale_to_utf8("Click mich")) // Button text setzen
    {
      add(m_vbox); // Hier verbinden wir das Gtk::VBox mit dem Fenster
    
      m_vbox.set_border_width(10); // Randbreite des Gtk::VBox auf 10 Pixel setzen
    
      m_vbox.pack_start(m_label);  // Verbinden des Gtk::Label mit dem Gtk::VBox
      m_vbox.pack_start(m_pbar);   // Verbinden des Gtk::ProgressBar mit dem Gtk::VBox                
      m_vbox.pack_start(m_button); // Verbinden des Gtk::Button mit dem Gtk::VBox                 
    
      m_pbar.set_pulse_step(0.05f); // Schrittgröße für pulse() setzen / 0.0 - 1.0
    
      // Dem Button die Methode, die er beim clicked Signal ausführen soll, als 
      // Funktor übergeben
      m_button.signal_clicked().connect(sigc::mem_fun(*this,&MyWindow::on_button_click));
    
      // Hier wird die Methode die der Dispatcher beim aufruf ausführen soll
      // als Funktor übergeben
      m_dispatcher.connect( sigc::mem_fun( m_pbar , &Gtk::ProgressBar::pulse ));
    
    // Hier starten wir den 2. Thread (der erste ist der Hauptthread!) der für
      // uns die ProgressBar - Arbeit erledigen wird.
      // Hierzu übergeben wir die thread_worker Methode als Funktor.
      // das 'true' sagt dem Thread das er joinable sein soll!
      // Was auch anhand des Pointers der auf den Thread zeigt und zurückgegeben
      // wird überprüft werden.
      m_thread = Glib::Thread::create( sigc::mem_fun(*this,&MyWindow::thread_worker),true); 
    
      // Nun lassen wir alle Elemente des Fensters anzeigen.
      show_all(); 
    }
    
    MyWindow::~MyWindow()
    {
      // Ein Anonymouer Scope innerhalb einer Funktion/Methode hat den
      // Zweck das darin erstellte Objekte beim verlassen zerstört werden
      // wir haben hier ein Mutex::Lock objekt erstellt das bei erstellung
      // das lock setzt und bei der Zerstörung wieder unlockt
      {
        Glib::Mutex::Lock lock(mutex);                    
        if(m_end_thread)       // Wenn der Thread noch läuft
           m_end_thread = true; // sagen wir dem Thread das er beenden soll
      }
      if(m_thread->joinable())
        m_thread->join();
    }
    
    void MyWindow::on_button_click()
    {
       static unsigned long count_clicked = 0;
       // Hier konvertieren wir den Wert von count_clicked und incrementieren in sogleich
       // und übergeben dann den Wert als Glib::ustring an unser Label weiter
       // Somit haben wir dann einen Click counter ;)
       m_label.set_text(boost::lexical_cast<Glib::ustring>(count_clicked++)); 
    }
    
    void MyWindow::thread_worker()
    {
      while(1)
      {
        Glib::usleep(50000);  // 50.000 Mikrosekunden warten
    
        m_dispatcher();       // Threadsafer Aufruf der pulse()-Methode 
    
        // Dies ist ein anonymer scope [Erklärung siehe Destruktor Defintion]
        {
          Glib::Mutex::Lock lock(mutex);
          if(m_end_thread)      // Müssen wir beenden?
            return;
        }// Ende des anonymen scopes, das den effekt hat das das mutex wir unlocked wird
      }
    }
    
    int main(int argc, char**argv)
    {
      // Die einzige Änderung hier ist dieser Aufruf:
      Glib::thread_init();
      // Mit dem Glib::thread_init aufruf wird dafür gesorgt das 
      // das Gtk+/Glib Subsystem darauf vorbereitet wird das wir
      // mehrere Threads benutzen.
    
      Gtk::Main main_obj(argc,argv);
    
      MyWindow window_obj;
    
      main_obj.run(window_obj);
    
      return EXIT_SUCCESS;  
    }
    


  • /*
     * Glib::Dispatcher example -- cross thread signalling
     * by Daniel Elstner 
     *
     * Copyright (c) 2005  Free Software Foundation
     */
    
    #include <sigc++/class_slot.h>
    #include <glibmm.h>
    #include <gtkmm/box.h>
    #include <gtkmm/button.h>
    #include <gtkmm/buttonbox.h>
    #include <gtkmm/main.h>
    #include <gtkmm/progressbar.h>
    #include <gtkmm/stock.h>
    #include <gtkmm/window.h>
    
    #include <algorithm>
    #include <functional>
    #include <list>
    
    namespace
    {
    
    class ThreadProgress : public Gtk::ProgressBar
    {
    public:
      ThreadProgress();
      virtual ~ThreadProgress();
    
      void launch();
      SigC::Signal0<void>& signal_finished();
    
    private:
      unsigned int        progress_;
      Glib::Dispatcher    signal_increment_;
      SigC::Signal0<void> signal_finished_;
    
      void progress_increment();
      void thread_function();
    };
    
    class MainWindow : public Gtk::Window
    {
    public:
      MainWindow();
      virtual ~MainWindow();
    
      void launch_threads();
    
    protected:
      virtual bool on_delete_event(GdkEventAny* event);
    
    private:
      std::list<ThreadProgress*>  progress_bars_;
      Gtk::Button*                close_button_;
    
      void on_progress_finished(ThreadProgress* progress);
    };
    
    ThreadProgress::ThreadProgress()
    :
      progress_ (0)
    {
      // Connect to the cross-thread signal.
      signal_increment_.connect(SigC::slot(*this, &ThreadProgress::progress_increment));
    }
    
    ThreadProgress::~ThreadProgress()
    {}
    
    void ThreadProgress::launch()
    {
      // Create a non-joinable thread -- it's deleted automatically on thread exit.
      Glib::Thread::create(SigC::slot_class(*this, &ThreadProgress::thread_function), false);
    }
    
    SigC::Signal0<void>& ThreadProgress::signal_finished()
    {
      return signal_finished_;
    }
    
    void ThreadProgress::progress_increment()
    {
      // Use an integer because floating point arithmetic is inaccurate --
      // we want to finish *exactly* after the 1000th increment.
      ++progress_;
    
      const double fraction = double(progress_) / 1000.0;
      set_fraction(std::min(fraction, 1.0));
    
      if(progress_ >= 1000)
        signal_finished_();
    }
    
    void ThreadProgress::thread_function()
    {
      Glib::Rand rand;
      int usecs = 5000;
    
      for(int i = 0; i < 1000; ++i)
      {
        usecs = rand.get_int_range(std::max(0, usecs - 1000 - i), std::min(20000, usecs + 1000 + i));
        Glib::usleep(usecs);
    
        // Tell the GUI thread to increment the progress bar value.
        signal_increment_();
      }
    }
    
    MainWindow::MainWindow()
    :
      close_button_ (0)
    {
      set_title("Thread Dispatcher Example");
    
      Gtk::VBox *const vbox = new Gtk::VBox(false, 10);
      add(*Gtk::manage(vbox));
      vbox->set_border_width(10);
    
      for(int i = 0; i < 5; ++i)
      {
        ThreadProgress *const progress = new ThreadProgress();
        vbox->pack_start(*Gtk::manage(progress), Gtk::PACK_SHRINK);
        progress_bars_.push_back(progress);
    
        progress->signal_finished().connect(
            SigC::bind(SigC::slot(*this, &MainWindow::on_progress_finished), progress));
      }
    
      Gtk::ButtonBox *const button_box = new Gtk::HButtonBox();
      vbox->pack_end(*Gtk::manage(button_box), Gtk::PACK_SHRINK);
    
      close_button_ = new Gtk::Button(Gtk::Stock::CLOSE);
      button_box->pack_start(*Gtk::manage(close_button_), Gtk::PACK_SHRINK);
      close_button_->set_flags(Gtk::CAN_DEFAULT);
      close_button_->grab_default();
      close_button_->set_sensitive(false);
      close_button_->signal_clicked().connect(SigC::slot(*this, &Gtk::Widget::hide));
    
      show_all_children();
      set_default_size(300, -1);
    }
    
    MainWindow::~MainWindow()
    {}
    
    void MainWindow::launch_threads()
    {
      std::for_each(
          progress_bars_.begin(), progress_bars_.end(),
          std::mem_fun(&ThreadProgress::launch));
    }
    
    bool MainWindow::on_delete_event(GdkEventAny*)
    {
      // Don't allow closing the window before all threads finished.
      return !progress_bars_.empty();
    }
    
    void MainWindow::on_progress_finished(ThreadProgress* progress)
    {
      progress_bars_.remove(progress);
    
      // Enable the close button when all threads finished.
      if(progress_bars_.empty())
        close_button_->set_sensitive(true);
    }
    
    } // anonymous namespace
    
    int main(int argc, char** argv)
    {
      Glib::thread_init();
      Gtk::Main main_instance (&argc, &argv);
    
      MainWindow window;
    
      // Install a one-shot idle handler to launch the threads
      // right after the main window has been displayed.
      Glib::signal_idle().connect(
          SigC::bind_return(SigC::slot(window, &MainWindow::launch_threads), false));
    
      Gtk::Main::run(window);
    
      return 0;
    }
    


  • @nachtrag:

    Das Beispiel ist nicht von mir. Es ist von Daniel Elstner und etwas anders als meines.

    Ich hab es bewusst ähnlich gemacht, da ich es für ein gutes Beispiel gehalten habe.
    In den aktuellen Versionen haben die in den glibmm verzeichnissen aber nur noch non-gtkmm Beispiele, d.h. reine glibmm sources.

    Ausserdem hab ich meine Version stark vereinfacht. Und ausreichend kommentiert (hoffe ich) Sollten noch fragen auftreten meldet euch einfach. Und postet nicht irgendwas.

    MfG


Anmelden zum Antworten