Re: Update progress bar without returning to main loop?



Murray Cumming wrote:

I'd still like someone to create an example for gtkmm of updating a
progressbar while doing intensive processing.

The following code is a snippet from a class that does employee record setup. The class is derived from Gtk::Window and its declaration follows:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// code begins
#ifndef __FISCAL_EMPLOYEE_
#define __FISCAL_EMPLOYEE_
#include <gtkmm.h>
#include <gtkmm/window.h>
#include <gtkmm/button.h>
#include <gtkmm/table.h>
#include <gtkmm/liststore.h>
#include <gtkmm/treeview.h>
#include "libpq-fe.h"
#include "libpq/libpq-fs.h"
#include "maskedentry.h"
#include <syslog.h>
#include <iostream>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#include <gdk/gdk.h>

class MonthMarks
{
public:
   MonthMarks(gint y, gint m);
   gint year;
   gint month;
   gint marked_dates[31];
};

class Employee : public Gtk::Window
{
public:
   Employee(void);
   ~Employee(void);
gboolean connect_database(void); // function connects to the database AFTER
   bool appIsReady;
protected:
   struct tm tm;
   GPtrArray *marks;
   gint records;
   Glib::ustring menu;
   Glib::ustring oldmenu;
Glib::ustring daysoff; // ustring for xml of scheduled days off Glib::ustring hoursoff; // ustring for xml of hours off week template PGconn *empdb; // connection pointer for cashier database gint sortcolumn; gboolean new_record; gboolean browse_state; // TRUE = browsing, FALSE = editing gboolean deleting_row; // TRUE = deleting a row for a non-existant record // Column record class to create a column record class ModelColumns : public Gtk::TreeModel::ColumnRecord { // for use in our treeview object
   public:
       ModelColumns()
       { add(m_id_text); add(m_name_text); }
Gtk::TreeModelColumn<Glib::ustring> m_id_text; // column template for cashier ID Gtk::TreeModelColumn<Glib::ustring> m_name_text; // column template for cashier NAME
   };
ModelColumns m_columns; // columns for Gtk::ListStore object Glib::RefPtr<Gtk::ListStore> list; // our listore object for the treeview Glib::RefPtr<Gtk::TreeSelection> table_sel; // selection for the treeview GPtrArray *buttons; // array for commandline button Gtk::Table *table; // master format table object
   Gtk::Table *personal_table;
Gtk::Notebook notebook; // note book object for scheduling Gtk::TreeView *tree; // tree view to display list store Gtk::TreeView *hour_tree; // tree view to display hours off TreeStore Glib::RefPtr<Gtk::TreeStore> hlist; class HourColumns : public Gtk::TreeModel::ColumnRecord // for use in our hours off treeview object { public:
       HourColumns(){ add(m_day); add(m_check);add(m_day_text);}
Gtk::TreeModelColumn<Glib::ustring> m_day; // column for day text
       Gtk::TreeModelColumn<Glib::ustring> m_day_text;
Gtk::TreeModelColumn<bool> m_check; // column for check button
   };
HourColumns m_hcolumns; Gtk::Label employee_id;
   Gtk::Entry *search;
   Gtk::ComboBoxText *search_type;
   Gtk::Entry *lookup_entry;
   Gtk::Calendar calendar;
   MaskedEntry *name;
   MaskedEntry *firstname;
   MaskedEntry *ssn;
   MaskedEntry *addr1;
   MaskedEntry *addr2;
   MaskedEntry *city;
   MaskedEntry *state;
   MaskedEntry *zip;
   MaskedEntry *phone;
   MaskedEntry *dob;
   Gtk::ComboBoxText *profitcenter;
   Gtk::Label *cashierid;
   MaskedEntry *hiredate;
   Gtk::ComboBoxText *job;
   MaskedEntry *pass1;
   MaskedEntry *pass2;
   MaskedEntry *logdiv;
   Gtk::ComboBoxText *paymethod;
   Gtk::ComboBoxText *commtype;
   Gtk::ComboBoxText *trust;
   Gtk::ComboBoxText *weight;
   Gtk::Button *cashedit;
   Gtk::Button *menuedit;
   MaskedEntry *baserate;
   void setup_commandline(void);
   void setup_lookup_group(void);
   void setup_password(void);
   void setup_personal(void);
   void setup_job(void);
   void set_edits_data(Glib::ustring key);
   void on_button_clicked(gint i);
   void set_widget_sensitivity(gint toggle);
   void on_edit_cashier(void);
   void on_edit_menu(void);
   void on_column_clicked(gint column);
   void wait_on_system(void);
   void on_row_changed(void);
   int update_database(gint whichone);
   void delete_record(void);
   bool on_entry_key_event(GdkEventKey *key);
   gboolean add_record(void);
   void delete_row(void);
   void setup_scheduling(void);
   void populate_daysoff(void);
   void populate_hoursoff(void);
   void get_daysoff(void);
   void get_hoursoff(void);
   void SetupDaysoff(xmlNode *cur);
   void SetupHoursoff(xmlNode *cur);
   void on_calendar_double_clicked(void);
   void on_calendar_month_changed(void);
   void on_child_toggled(Glib::ustring str);
void check_all_children(Gtk::TreeModel::Children& children,bool condition);
   void on_search(void);
int on_F1_pressed(Gtk::Widget *widget, GdkEventKey *keyevent); // help display
};
#endif
// code ends
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////

Code that follows illustrates how I display my progress bar while populating a Gtk::ListStore widget from a data table in a PostgreSQL database:

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// code begins
void Employee::wait_on_system(void)
{
   while(Gtk::Main::instance()->events_pending())
       Gtk::Main::instance()->iteration();
}

#define NUM_QUERY "select employee_id, employee_name, employee_firstname from employee where substr(employee_id,0,%d)=\'%s\' order by employee_id" #define NAME_QUERY "select employee_id, employee_name, employee_firstname from employee where employee_name~*\'%s\' or employee_firstname~*\'%s\' order by employee_id"

// function retrieves all records from employee data table that match a search image entered by the user and insert them into a ListStore while displaying a progress bar in a popup window.
void Employee::on_search(void)
{
   gchar *buf;
   Gtk::MessageDialog *db;
   Gtk::Window *progress;
   Glib::ustring error_str;
   Glib::ustring ID;
   Glib::ustring NAME;
   Gtk::TreeModel::iterator iter;
   Gtk::TreeModel::Row row;
PGresult *table_rows; // row pointer for table queries
   gchar *test;
progress = new Gtk::Window(); progress->set_position(Gtk::WIN_POS_CENTER_ALWAYS); // set window position to center of the screen
   progress->set_border_width(5);
   progress->set_title(" Initialization Progress ");
Gtk::Label *label = manage(new Gtk::Label(""));
   label->set_text(Glib::ustring("Searchng, please wait. . ."));
   label->show();
   Gtk::ProgressBar *pb = manage(new Gtk::ProgressBar());
   Gtk::VBox *vbox = manage(new Gtk::VBox(FALSE,5));
   vbox->pack_start(*label);
   vbox->pack_start(*pb);
   progress->add(*vbox);
progress->show_all_children(); progress->show_now(); // allow the main loop to iterate
   wait_on_system();
browse_state = POPULATE_STATE; list->clear(); if(search_type->get_active_text() == "Number") buf = g_strdup_printf(NUM_QUERY, search->get_text().length()+1, (char *)search->get_text().c_str());
   else
buf = g_strdup_printf(NAME_QUERY, (char *)search->get_text().c_str(), (char *)search->get_text().c_str()); // this call is asynchronus and I cannot do anything here but wait for its return (tests show that even 10,000 records has // retrieval times in terms of milliseconds depending upon network traffic)
   table_rows = PQexec(empdb, buf);
g_free(buf); // if this query fails, the notify the user and go away.
   if( PQresultStatus(table_rows) != PGRES_TUPLES_OK )
   {
       progress->hide();
       delete progress;
       Glib::ustring buf = PQresultErrorMessage(table_rows);
       Glib::ustring error_str = "Database query failed.Reported error-> ";
       error_str += buf;
db = new Gtk::MessageDialog (
               *this,
               error_str,
               TRUE,
               Gtk::MESSAGE_ERROR,
               Gtk::BUTTONS_OK,
               TRUE
           );
Glib::RefPtr<Gdk::Pixbuf> fis_icon = Gdk::Pixbuf::create_from_inline(-1,fisicon_inline,FALSE);
       db->set_icon(fis_icon);
db->set_position(Gtk::WIN_POS_CENTER_ALWAYS); // set window position to center of the screen
       db->run();
       delete db;
       PQclear(table_rows);
       return;
   }
records = PQntuples(table_rows); // if no tuples then report it and go away
   if(!records)
   {
       Glib::ustring buf = PQresultErrorMessage(table_rows);
       Glib::ustring error_str = "No records found for ";
       error_str += search->get_text();
db = new Gtk::MessageDialog (
               *this,
               error_str,
               TRUE,
               Gtk::MESSAGE_ERROR,
               Gtk::BUTTONS_OK,
               TRUE
           );
Glib::RefPtr<Gdk::Pixbuf> fis_icon = Gdk::Pixbuf::create_from_inline(-1,fisicon_inline,FALSE);
       db->set_icon(fis_icon);
db->set_position(Gtk::WIN_POS_CENTER_ALWAYS); // set window position to center of the screen
       db->run();
       delete db;
       delete progress;
       PQclear(table_rows);
       search->grab_focus();
       return;
   }
gdouble progress_fraction = (gdouble)(1.00/(gdouble)records);

// loop to populate the ListStore. Unless there are literally thousands of records to // display, the progress bar barely displays enough to be seen before the loops terminates for (gint i = 0; i < records; i++)
   {
       // if the id data is NULL, then we don't want this
       // record.. even though the database is designed such
       // that this field cannot be null.
       if(!strlen(PQgetvalue(table_rows,i,0)))
           continue;
test = PQgetvalue(table_rows,i,0); // update the progress bar and wait for the main loop to display the change
       pb->set_fraction((gdouble)((gdouble)i*progress_fraction));
       wait_on_system();
ID = PQgetvalue(table_rows,i,0);
       NAME = PQgetvalue(table_rows,i,1);
       if(!PQgetisnull(table_rows,i,2))
       {
           NAME += ", ";
           NAME += PQgetvalue(table_rows,i,2);
       }
row = *(iter = list->append()); row[m_columns.m_id_text] = ID;
       row[m_columns.m_name_text] = NAME;
   }
PQclear(table_rows);
   progress->hide();
   delete progress;
   browse_state = BROWSE_STATE;
   Gtk::TreeModel::Path *path = new Gtk::TreeModel::Path("0");
   tree->set_cursor(*path);
   delete path;
   search->set_text("");
   lookup_entry->grab_focus();
}

// code ends
/////////////////////////////////////////////////////////////////////////////////////////////////////////

It may be clunky, but it works well.

Bob Caryl




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