Report 2: Pie Chart

only for RuBoard - do not distribute or recompile

Report 2: Pie Chart

This second report will be a pie chart display of sales by division. Obviously, this type of display best lends itself to displaying a limited number of values, normally between 2 and 15. A pie chart that attempts to show more than that becomes too fragmented to show anything but the largest slices, if the data breaks out as such. Figure 12.3 shows Glade highlighting the DrawingArea widget, which will be used extensively in the next three reports . Listing 12.6 is the code for the pie chart report.

Figure 12.3. The pie chart project is under construction with Glade; the DrawingArea widget is highlighted in the palette.
graphics/12fig03.gif
Listing 12.6 piechart_utils.c
 #include <gtk/gtk.h>  #include <mysql.h>  /*  time.h  is needed for the calls to find the current   * month and year.   *  /  #include <time.h>  /*  stdlib.h  is needed for the  atoi/atof  call. */  #include <stdlib.h>  #include "support.h"  #define SERVER  "einstein"  #define LOGIN   "kbi_user"  #define PASS    "dengissa"  #define DB      "kbi"  #define max_div 10  /* The maximum number of business   * divisions, or "departments,"   * this application can handle.   */  GtkWidget *frm_piechart;  MYSQL           *conx;  MYSQL_RES       *result_set;  MYSQL_ROW       row;  gchar           *sql;  gint            number_of_divisions = 0;  gint            total_sales = 0;  /* This assumes there will never be more than 10 divisions. */  gchar       *division_names[max_div];  gint        division_sales[max_div];  GdkColor    color[max_div];  gchar  *  color_choices[max_div] = {"Yellow", "Blue", "Red", "Gray", "Black",                               "White", "Green", "Purple", "Orange", "Gold"};  void draw_legend(gint posit_x, gint posit_y,                   gchar* legend_text, GdkColor my_color);  void draw_pie_wedge(gint start_deg, gint end_deg, GdkColor my_color)  {  /* This function will draw one pie wedge. It is called repeatedly with a   * different value each time until the entire pie shape is drawn.   */  GdkGC *gc;      gc = gdk_gc_new(GTK_WIDGET(lookup_widget(frm_piechart,                      "drawingarea1"))->window);      if (!gdk_colormap_alloc_color(gdk_colormap_get_system(),             &my_color, FALSE, TRUE))        {        g_print("unable to allocate colormap\n");         gtk_statusbar_push(GTK_STATUSBAR(lookup_widget(frm_piechart,             "statusbar1")), 1, "Unable to allocate colormap");        }      gdk_gc_set_foreground(gc, &my_color);      //g_print("Drawing Arc.\n");      gdk_draw_arc(GTK_WIDGET(lookup_widget(frm_piechart,             "drawingarea1"))->window,             gc, TRUE, 10, 10, 350, 350, start_deg*64, end_deg*64);      //g_print("exiting draw_pie_wedge\n");      gtk_statusbar_push(GTK_STATUSBAR(lookup_widget(frm_piechart,             "statusbar1")), 1, "Done with draw_pie_wedge");        }      void parse_colors()      {  /* As part of the startup procedure for this application,   * you need to parse each of the colors in  color_choices[]  .   */  gint i;         for (i=0; i<10; i++)             {            if (!gdk_color_parse(color_choices[i], &color[i]))               {                g_print("unable to parse color %d.\n", i);                 gtk_statusbar_push(GTK_STATUSBAR(lookup_widget(frm_piechart,                      "statusbar1")), 1, "Unable to parse colors.");               }             }      }      void draw_pie_chart()      {  /* This function draws the entire pie shape by calculating the   * values for each wedge and calling  draw_pie_wedge().  */  gint  i;         gint  current_pct;         gint  current_degree = 0; // The degree for starting, for GTK+                                  // it is the "3 o'clock" position.         gint  legend_vertical = 405; // An arbitrary number of pixels                                     // down from the top to start drawing                                     // the legend.         //g_print("Number of divisions is %d\n", number_of_divisions);         for (i=1; i<=number_of_divisions; i++)           {  /* First calculate what percentage the current pie   * slice is compared to the total.   */  //g_print("div sales i - 1 is %d\n", division_sales[i-1]);              //g_print("total sales is %d\n", total_sales);  /* This is truncating. So it is necessary to compensate.   * In the following call, the "+ .5" compensates   * for the fact that  current_pct  is an integer,   * and so the division calculation is truncated.   * One half is a good "fudge factor" because the   * truncation will always be between .001 and .999,   * and .5 is right in the middle of those two.   * On a pie graph, +/.5 of a percent does not   * show to the human eye.   */  current_pct = ((gdouble) division_sales[i - 1] /                             (gdouble) total_sales) * 100 + .5;              //g_print("current percentage is %d\n", current_pct);  /* If this is the final pie wedge, you might as well   * close the circle rather than leave a sliver of open   * space that may be caused by rounding.   */   /* In the  draw_pie_wedge()  function, the second   * parameter is degrees of arc, not ending degrees. */  if (i == number_of_divisions)                  {  /* This is the final pie wedge; you might as well   * go ahead and close the circle, rather than   * showing a small slice that's left because of rounding   * errors.   */  draw_pie_wedge(current_degree,                           360 - current_degree, color[i - 1]);                  }              else                  {  /* The 3.6 conversion factor is due to the   * ratio of percentage to degreesthat is,that is,   * 100:360.   */  draw_pie_wedge(current_degree,                           current_pct * 3.6, color[i - 1]);                  }              current_degree += current_pct * 3.6;              draw_legend(legend_vertical, 0,                          g_strconcat(division_names[i - 1], " - ",                          g_strdup_printf("%d", division_sales[i - 1]),                          " (", g_strdup_printf("%d", current_pct),                          "%)", 0L),                          color[i - 1]);  /* Put 20 pixels between the legend line that was just   * drawn and the next one that will be drawn.   */  legend_vertical += 20;              gtk_statusbar_push(GTK_STATUSBAR(lookup_widget(frm_piechart,                       "statusbar1")), 1, "Done drawing pie chart.");       }  }  void draw_legend(gint posit_x, gint posit_y,                   gchar* legend_text, GdkColor my_color)  {  /* This function draws one line in the legend. Thus, the   * legend is created by repeated calls to this function,   * once for each line in the legend.   */  GdkGC *gc;     GdkColor black;     gc = gdk_gc_new(GTK_WIDGET(lookup_widget(frm_piechart,                     "drawingarea1"))->window);     if (!gdk_colormap_alloc_color(gdk_colormap_get_system(),            &my_color, FALSE, TRUE))         {             gtk_statusbar_push(GTK_STATUSBAR(lookup_widget(frm_piechart,                   "statusbar1")), 1, "Unable to allocate colormap.");         }     gdk_gc_set_foreground(gc, &my_color);     gdk_draw_rectangle(GTK_WIDGET(lookup_widget(frm_piechart,          "drawingarea1"))->window, gc, TRUE, 20, posit_x, 10, 10);     if (!gdk_color_parse("Black", &black))          {             g_print("unable to parse black.\n");              gtk_statusbar_push(GTK_STATUSBAR(lookup_widget(frm_piechart,                   "statusbar1")), 1, "Unable to parse Black.");          }     if (!gdk_colormap_alloc_color(gdk_colormap_get_system(),            &black, FALSE, TRUE))            {             g_print("unable to allocate colormap\n");              gtk_statusbar_push(GTK_STATUSBAR(lookup_widget(frm_piechart,                      "statusbar1")), 1, "Unable to allocate colormap.");            }        gdk_gc_set_foreground(gc, &black);        gdk_draw_string(GTK_WIDGET(lookup_widget(frm_piechart, "drawingarea1"))->window,             GTK_WIDGET(lookup_widget(frm_piechart, "drawingarea1"))->style->font,             gc, 35, posit_x + 10, legend_text);  }  gboolean load_data()  {  /* Connect to the database and return the data to be used   * for the pie chart.   */  gchar *str_year, *str_month;     struct tm *ptr;     time_t now;     conx = mysql_init((MYSQL *)0L);     if (conx == 0L)       {         gtk_statusbar_push(GTK_STATUSBAR(lookup_widget(frm_piechart,                        "statusbar1")), 1,"mysql_init problem");          return FALSE;       }     gtk_statusbar_push(GTK_STATUSBAR(lookup_widget(frm_piechart,                        "statusbar1")), 1, "mysql_init ok");     conx = mysql_real_connect(conx, SERVER, LOGIN, PASS, DB, 0, 0L, 0);     if (conx == 0L)       {         gtk_statusbar_push(GTK_STATUSBAR(lookup_widget(frm_piechart,                        "statusbar1")), 1,"mysql_real_connect problem");          return FALSE;       }     gtk_statusbar_push(GTK_STATUSBAR(lookup_widget(frm_piechart,                        "statusbar1")), 1, "mysql_real_connect ok");  /* Figure out the current month and year. Then extract the   * previous month's data.   */  time(&now);      ptr = localtime(&now);  /*  tm_year  is the number of years since 1900. So add 1900   * to get the current year as an integer.   */  str_year = g_strdup_printf("%d", ptr->tm_year + 1900);  /*  tm_mon  returns a number between 0 and 11 for the month.   * So adding 1 would make it more understandableso that   * 1 = January instead of 0 and so on. However, because the desired   * data is for the last completed calendar month, in effect,   * you let the  tm_mon  call return the current month as a   * zero-based array, which translates to the current   * month minus one.   */  str_month = g_strdup_printf("%d", ptr->tm_mon);  /* What if it is January? In that case, the month/year information   * needs to reflect the last month of the previous year.   */  if (g_strcasecmp(str_month, "0") == 0)         {  /* For clarity, use the same function call as before,   * subtracting one "year."   */  str_year = g_strdup_printf("%d", ptr->tm_year + 1900 - 1);             str_month = "12";         }      g_print("Year and month for query are %s and %s\n",               str_year, str_month);      sql = g_strconcat("select * from tbl_sales_by_division ",                        "where sales_month = ", str_month,                        " and sales__year = ", str_year, 0L);      g_print("sql is %s\n", sql);      if (mysql_query (conx, sql) != 0)         {          gtk_statusbar_push(GTK_STATUSBAR(lookup_widget(frm_piechart,                         "statusbar1")), 1,"mysql_query problem");           g_print("mysql_query problem in load_data.\n");           mysql_close(conx);           return FALSE;         }      gtk_statusbar_push(GTK_STATUSBAR(lookup_widget(frm_piechart,                         "statusbar1")), 1, "mysql_query ok");      result_set = mysql_store_result(conx);  /* The result set with the data has been returned and is   * contained in the variable  result_set  . Next, take   * that data and fill in the arrays that will be used   * to present the pie chart.   */  while ((row = mysql_fetch_row (result_set)) != 0)        {          static gint i = 0;           division_names[i] = row[2];           division_sales[i] = atoi(row[3]);           //g_print("data is %s and %d\n",           //           division_names[i], division_sales[i]);           total_sales += division_sales[i];           //g_print("Total sales is %d.\n", total_sales);           i++;           //g_print("Number of divisions is %d\n", i);           number_of_divisions = i;        }      mysql_close(conx);      return TRUE;  } 

Listing 12.7 is the callbacks.c listing for the pie chart report. Most of the calls in it are simple and make use of the functions in piechart_utils.c (refer to Listing 12.6).

Listing 12.7 callbacks.c for the Pie Chart Application
 #ifdef HAVE_CONFIG_H  #  include <config.h>  #endif  #include <gtk/gtk.h>  #include "callbacks.h"    #include "interface.h"  #include "support.h"  #include "piechart_utils.h"  void  on_frm_piechart_show                   (GtkWidget       *widget,                                          gpointer         user_data)  {   if (load_data() = FALSE)        {           g_print("Unable to load data for pie chart.\n");            return;        }    parse_colors();    draw_pie_chart();    g_print("on_frm_piechart_show\n");  }  gboolean  on_frm_piechart_delete_event           (GtkWidget       *widget,                                          GdkEvent        *event,                                          gpointer         user_data)  {   g_print("on_frm_piechart_delete_event\n");    gtk_main_quit();    return FALSE;  }  /* The following functions are included so the reader can see the   * event sequence for the DrawingArea widget. Strictly speaking,   * they are not absolutely needed for the application.   */  gboolean  on_drawingarea1_delete_event           (GtkWidget       *widget,                                          GdkEvent        *event,                                          gpointer         user_data)  {   //g_print("on_drawingarea1_delete_event\n");    return FALSE;  }  void  on_drawingarea1_show                   (GtkWidget       *widget,                                          gpointer         user_data)  {   //g_print("on_drawingarea1_show\n");  }  void  on_drawingarea1_draw                   (GtkWidget       *widget,                                          GdkRectangle    *area,                                          gpointer         user_data)  {   //g_print("on_drawingarea1_draw\n");  }  gboolean  on_drawingarea1_event                  (GtkWidget       *widget,                                          GdkEvent        *event,                                          gpointer         user_data)  {  /* Putting the  draw_pie_chart()  function call in the  event  event   * of the GTK+ DrawingArea widget ensures that the pie graph   * redraws itself when something happens to it, for example, if another   * window or tooltip was drawn over the pie chart.   *   * This works fine for simple graphics like this one, but it can   * measurably affect performance in a complicated application,   * such as the scatter plot later in this chapter, which plots   * nearly 4,000 points (vice ~6 for the pie chart).   */  draw_pie_chart();    //g_print("on_drawingarea1_event\n");    return FALSE;  } 

Figure 12.4 shows the Pie Chart Report running. Note that it will not redraw itself to a different size if its parent window is resized.

Figure 12.4. The pie chart applications.
graphics/12fig04.gif
only for RuBoard - do not distribute or recompile


MySQL Building User Interfaces
MySQL: Building User Interfaces (Landmark)
ISBN: 073571049X
EAN: 2147483647
Year: 2001
Pages: 119

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net