From: John Darrington Date: Wed, 11 Nov 2015 18:09:15 +0000 (+0100) Subject: New top level menu for Graphs. X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=612b51515e356bc4dd625a3fb18d0a4f827a1e2c;p=pspp New top level menu for Graphs. Added a new top level menu to assist with the generation of graphics. Currently it has only scatterplot, histogram and barchart. --- diff --git a/NEWS b/NEWS index 14000fe958..d7d0a4102b 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,11 @@ Changes from 0.8.5 to 0.9.0: * A Russian localisation has been contributed. + * The graphic user interface now has a Graphs menu to access the GRAPH + command. + + * The GRAPH command now has a /BAR subcommand to draw barcharts. + * The graphical user interface uses Gtk+ version 3 instead of version 2. Accordingly, it has a somewhat different look and feel. diff --git a/doc/statistics.texi b/doc/statistics.texi index 85217cd636..a9adb2afc6 100644 --- a/doc/statistics.texi +++ b/doc/statistics.texi @@ -411,8 +411,9 @@ large quantity of output. @display GRAPH - /HISTOGRAM = @var{var} - /SCATTERPLOT [(BIVARIATE)] = @var{var1} WITH @var{var2} [BY @var{var3}] + /HISTOGRAM [(NORMAL)]= @var{var} + /SCATTERPLOT [(BIVARIATE)] = @var{var1} WITH @var{var2} [BY @var{var3}] + /BAR = @{@var{summary-function}(@var{var1}) | @var{count-function}@} BY @var{var2} [BY @var{var3}] [ /MISSING=@{LISTWISE, VARIABLE@} [@{EXCLUDE, INCLUDE@}] ] [@{NOREPORT,REPORT@}] @@ -441,6 +442,8 @@ this plot it is possible to analyze gender differences for @var{height} vs.@: @v The subcommand @subcmd{HISTOGRAM} produces a histogram. Only one variable is allowed for the histogram plot. +The keyword @subcmd{NORMAL} may be specified in parentheses, to indicate that the ideal normal curve +should be superimposed over the histogram. For an alternative method to produce histograms @pxref{EXAMINE}. The following example produces a histogram plot for the variable @var{weight}. @@ -449,6 +452,57 @@ GRAPH /HISTOGRAM = @var{weight}. @end example +@cindex bar chart +The subcommand @subcmd{BAR} produces a bar chart. +This subcommand requires that a @var{count-function} be specified (with no arguments) or a @var{summary-function} with a variable @var{var1} in parentheses. +Following the summary or count function, the keyword @subcmd{BY} should be specified and then a catagorical variable, @var{var2}. +The values of the variable @var{var2} determine the labels of the bars to be plotted. +Optionally a second categorical variable @var{var3} may be specified in which case a clustered (grouped) bar chart is produced. + +Valid count functions are +@table @subcmd +@item COUNT +The weighted counts of the cases in each category. +@item PCT +The weighted counts of the cases in each category expressed as a percentage of the total weights of the cases. +@item CUFREQ +The cumulative weighted counts of the cases in each category. +@item CUPCT +The cumulative weighted counts of the cases in each category expressed as a percentage of the total weights of the cases. +@end table + +The summary function is applied to @var{var1} across all cases in each category. +The recognised summary functions are: +@table @subcmd +@item SUM +The sum. +@item MEAN +The arithmetic mean. +@item MAXIMUM +The maximum value. +@item MINIMUM +The minimum value. +@end table + +The following examples assume a dataset which is the results of a survey. +Each respondent has indicated annual income, their sex and city of residence. +One could create a bar chart showing how the mean income varies between of residents of different cities, thus: +@example +GRAPH /BAR = MEAN(@var{income}) BY @var{city}. +@end example + +This can be extended to also indicate how income in each city differs between the sexes. +@example +GRAPH /BAR = MEAN(@var{income}) BY @var{city} BY @var{sex}. +@end example + +One might also want to see how many respondents there are from each city. This can be achieved as follows: +@example +GRAPH /BAR = COUNT BY @var{city}. +@end example + +Bar charts can also be produced using the @ref{FREQUENCIES} and @ref{CROSSTABS} commands. + @node CORRELATIONS @section CORRELATIONS diff --git a/src/language/stats/automake.mk b/src/language/stats/automake.mk index bfe379e345..6388e148d6 100644 --- a/src/language/stats/automake.mk +++ b/src/language/stats/automake.mk @@ -11,6 +11,7 @@ language_stats_sources = \ src/language/stats/autorecode.c \ src/language/stats/binomial.c \ src/language/stats/binomial.h \ + src/language/stats/chart-category.h \ src/language/stats/chisquare.c \ src/language/stats/chisquare.h \ src/language/stats/cochran.c \ diff --git a/src/language/stats/chart-category.h b/src/language/stats/chart-category.h new file mode 100644 index 0000000000..afe88b9ac5 --- /dev/null +++ b/src/language/stats/chart-category.h @@ -0,0 +1,21 @@ +#ifndef BARCHART_DEF_H +#define BARCHART_DEF_H 1 + +struct ag_func +{ + const char *name; + const char *description; + + int arity; + bool cumulative; + double (*pre) (void); + double (*calc) (double acc, double x, double w); + double (*post) (double acc, double cc); + double (*ppost) (double acc, double ccc); +}; + +extern const struct ag_func ag_func[]; + +extern const int N_AG_FUNCS; + +#endif diff --git a/src/language/stats/graph.c b/src/language/stats/graph.c index a0dffbd3d1..8af6a1515f 100644 --- a/src/language/stats/graph.c +++ b/src/language/stats/graph.c @@ -23,6 +23,7 @@ #include #include +#include "gl/xalloc.h" #include #include "libpspp/assertion.h" @@ -48,11 +49,14 @@ #include "math/order-stats.h" #include "output/charts/plot-hist.h" #include "output/charts/scatterplot.h" +#include "output/charts/barchart.h" #include "language/command.h" #include "language/lexer/lexer.h" #include "language/lexer/value-parser.h" #include "language/lexer/variable-parser.h" +#include "language/stats/freq.h" +#include "language/stats/chart-category.h" #include "output/tab.h" @@ -81,6 +85,15 @@ enum scatter_type ST_XYZ }; +enum bar_type + { + CBT_SIMPLE, + CBT_GROUPED, + CBT_STACKED, + CBT_RANGE + }; + + /* Variable index for histogram case */ enum { @@ -122,14 +135,152 @@ struct graph bool missing_pw; /* ------------ Graph ---------------- */ + bool normal; /* For histograms, draw the normal curve */ + enum chart_type chart_type; enum scatter_type scatter_type; - const struct variable *byvar; + enum bar_type bar_type; + const struct variable *by_var[2]; + size_t n_by_vars; + + struct subcase ordering; /* Ordering for aggregation */ + int agr; /* Index into ag_func */ + /* A caseproto that contains the plot data */ struct caseproto *gr_proto; }; + + +static double +calc_mom1 (double acc, double x, double w) +{ + return acc + x * w; +} + +static double +calc_mom0 (double acc, double x UNUSED, double w) +{ + return acc + w; +} + +static double +pre_low_extreme (void) +{ + return -DBL_MAX; +} + +static double +calc_max (double acc, double x, double w UNUSED) +{ + return (acc > x) ? acc : x; +} + +static double +pre_high_extreme (void) +{ + return DBL_MAX; +} + +static double +calc_min (double acc, double x, double w UNUSED) +{ + return (acc < x) ? acc : x; +} + +static double +post_normalise (double acc, double cc) +{ + return acc / cc; +} + +static double +post_percentage (double acc, double ccc) +{ + return acc / ccc * 100.0; +} + + +const struct ag_func ag_func[] = + { + {"COUNT", N_("Count"), 0, 0, NULL, calc_mom0, 0, 0}, + {"PCT", N_("Percentage"), 0, 0, NULL, calc_mom0, 0, post_percentage}, + {"CUFREQ", N_("Cumulative Count"), 0, 1, NULL, calc_mom0, 0, 0}, + {"CUPCT", N_("Cumulative Percent"), 0, 1, NULL, calc_mom0, 0, post_percentage}, + + {"MEAN", N_("Mean"), 1, 0, NULL, calc_mom1, post_normalise, 0}, + {"SUM", N_("Sum"), 1, 0, NULL, calc_mom1, 0, 0}, + {"MAXIMUM", N_("Maximum"), 1, 0, pre_low_extreme, calc_max, 0, 0}, + {"MINIMUM", N_("Minimum"), 1, 0, pre_high_extreme, calc_min, 0, 0}, + }; + +const int N_AG_FUNCS = sizeof (ag_func) / sizeof (ag_func[0]); + +static bool +parse_function (struct lexer *lexer, struct graph *graph) +{ + int i; + for (i = 0 ; i < N_AG_FUNCS; ++i) + { + if (lex_match_id (lexer, ag_func[i].name)) + { + graph->agr = i; + break; + } + } + if (i == N_AG_FUNCS) + { + goto error; + } + + graph->n_dep_vars = ag_func[i].arity; + if (ag_func[i].arity > 0) + { + int v; + if (!lex_force_match (lexer, T_LPAREN)) + goto error; + + graph->dep_vars = xzalloc (sizeof (graph->dep_vars) * graph->n_dep_vars); + for (v = 0; v < ag_func[i].arity; ++v) + { + graph->dep_vars[v] = parse_variable (lexer, graph->dict); + } + + if (!lex_force_match (lexer, T_RPAREN)) + goto error; + } + + if (!lex_force_match (lexer, T_BY)) + goto error; + + graph->by_var[0] = parse_variable (lexer, graph->dict); + if (!graph->by_var[0]) + { + goto error; + } + subcase_add_var (&graph->ordering, graph->by_var[0], SC_ASCEND); + graph->n_by_vars++; + + if (lex_match (lexer, T_BY)) + { + graph->by_var[1] = parse_variable (lexer, graph->dict); + if (!graph->by_var[1]) + { + goto error; + } + subcase_add_var (&graph->ordering, graph->by_var[1], SC_ASCEND); + graph->n_by_vars++; + } + + return true; + + error: + lex_error (lexer, NULL); + return false; +} + + static void show_scatterplot (const struct graph *cmd, struct casereader *input) { @@ -139,12 +290,12 @@ show_scatterplot (const struct graph *cmd, struct casereader *input) ds_init_empty (&title); - if (cmd->byvar) + if (cmd->n_by_vars > 0) { ds_put_format (&title, _("%s vs. %s by %s"), var_to_string (cmd->dep_vars[1]), var_to_string (cmd->dep_vars[0]), - var_to_string (cmd->byvar)); + var_to_string (cmd->by_var[0])); } else { @@ -152,12 +303,11 @@ show_scatterplot (const struct graph *cmd, struct casereader *input) var_to_string (cmd->dep_vars[1]), var_to_string (cmd->dep_vars[0])); } - scatterplot = scatterplot_create (input, var_to_string(cmd->dep_vars[0]), var_to_string(cmd->dep_vars[1]), - cmd->byvar, + (cmd->n_by_vars > 0) ? cmd->by_var[0] : NULL, &byvar_overflow, ds_cstr (&title), cmd->es[0].minimum, cmd->es[0].maximum, @@ -218,7 +368,7 @@ show_histogr (const struct graph *cmd, struct casereader *input) chart_item_submit ( histogram_chart_create (histogram->gsl_hist, ds_cstr (&label), n, mean, - sqrt (var), false)); + sqrt (var), cmd->normal)); statistic_destroy (&histogram->parent); ds_destroy (&label); @@ -237,6 +387,122 @@ cleanup_exploratory_stats (struct graph *cmd) } +static void +run_barchart (struct graph *cmd, struct casereader *input) +{ + struct casegrouper *grouper; + struct casereader *group; + double ccc = 0.0; + + if ( cmd->missing_pw == false) + input = casereader_create_filter_missing (input, + cmd->dep_vars, + cmd->n_dep_vars, + cmd->dep_excl, + NULL, + NULL); + + + input = sort_execute (input, &cmd->ordering); + + struct freq **freqs = NULL; + int n_freqs = 0; + + for (grouper = casegrouper_create_vars (input, cmd->by_var, + cmd->n_by_vars); + casegrouper_get_next_group (grouper, &group); + casereader_destroy (group)) + { + int v; + struct ccase *c = casereader_peek (group, 0); + + /* Deal with missing values in the categorical variables */ + for (v = 0; v < cmd->n_by_vars; ++v) + { + if (var_is_value_missing (cmd->by_var[v], case_data (c, cmd->by_var[v]), cmd->fctr_excl) ) + break; + } + + if (v < cmd->n_by_vars) + { + case_unref (c); + continue; + } + + freqs = xrealloc (freqs, sizeof (*freqs) * ++n_freqs); + freqs[n_freqs - 1] = xzalloc (sizeof (**freqs) + + sizeof (union value) * (cmd->n_by_vars - 1) ); + + if (ag_func[cmd->agr].cumulative && n_freqs >= 2) + freqs[n_freqs - 1]->count = freqs[n_freqs - 2]->count; + else + freqs[n_freqs - 1]->count = 0; + if (ag_func[cmd->agr].pre) + freqs[n_freqs - 1]->count = ag_func[cmd->agr].pre(); + + + for (v = 0; v < cmd->n_by_vars; ++v) + { + value_clone (&freqs[n_freqs - 1]->values[v], case_data (c, cmd->by_var[v]), + var_get_width (cmd->by_var[v]) + ); + } + case_unref (c); + + double cc = 0; + for (;(c = casereader_read (group)) != NULL; case_unref (c)) + { + const double weight = dict_get_case_weight (cmd->dict,c,NULL); + const double x = (cmd->n_dep_vars > 0) ? case_data (c, cmd->dep_vars[0])->f : SYSMIS; + + cc += weight; + + freqs[n_freqs - 1]->count + = ag_func[cmd->agr].calc (freqs[n_freqs - 1]->count, x, weight); + } + + if (ag_func[cmd->agr].post) + freqs[n_freqs - 1]->count + = ag_func[cmd->agr].post (freqs[n_freqs - 1]->count, cc); + + ccc += cc; + } + + casegrouper_destroy (grouper); + + for (int i = 0; i < n_freqs; ++i) + { + if (ag_func[cmd->agr].ppost) + freqs[i]->count = ag_func[cmd->agr].ppost (freqs[i]->count, ccc); + } + + + { + struct string label; + ds_init_empty (&label); + + if (cmd->n_dep_vars > 0) + ds_put_format (&label, _("%s of %s"), + ag_func[cmd->agr].description, + var_get_name (cmd->dep_vars[0])); + else + ds_put_cstr (&label, + ag_func[cmd->agr].description); + + chart_item_submit (barchart_create (cmd->by_var, cmd->n_by_vars, + ds_cstr (&label), + freqs, n_freqs)); + + ds_destroy (&label); + } + + for (int i = 0; i < n_freqs; ++i) + free (freqs[i]); + + free (freqs); +} + + static void run_graph (struct graph *cmd, struct casereader *input) { @@ -278,10 +544,10 @@ run_graph (struct graph *cmd, struct casereader *input) const double weight = dict_get_case_weight (cmd->dict,c,NULL); if (cmd->chart_type == CT_HISTOGRAM) case_data_rw_idx (outcase, HG_IDX_WT)->f = weight; - if (cmd->chart_type == CT_SCATTERPLOT && cmd->byvar) + if (cmd->chart_type == CT_SCATTERPLOT && cmd->n_by_vars > 0) value_copy (case_data_rw_idx (outcase, SP_IDX_BY), - case_data (c, cmd->byvar), - var_get_width (cmd->byvar)); + case_data (c, cmd->by_var[0]), + var_get_width (cmd->by_var[0])); for(int v=0;vn_dep_vars;v++) { const struct variable *var = cmd->dep_vars[v]; @@ -347,14 +613,14 @@ cmd_graph (struct lexer *lexer, struct dataset *ds) graph.dict = dataset_dict (ds); - - /* ---------------- graph ------------------ */ graph.dep_vars = NULL; graph.chart_type = CT_NONE; graph.scatter_type = ST_BIVARIATE; - graph.byvar = NULL; + graph.n_by_vars = 0; graph.gr_proto = caseproto_create (); + subcase_init_empty (&graph.ordering); + while (lex_token (lexer) != T_ENDCMD) { lex_match (lexer, T_SLASH); @@ -366,6 +632,17 @@ cmd_graph (struct lexer *lexer, struct dataset *ds) lex_error (lexer, _("Only one chart type is allowed.")); goto error; } + graph.normal = false; + if (lex_match (lexer, T_LPAREN)) + { + if (!lex_force_match_id (lexer, "NORMAL")) + goto error; + + if (!lex_force_match (lexer, T_RPAREN)) + goto error; + + graph.normal = true; + } if (!lex_force_match (lexer, T_EQUALS)) goto error; graph.chart_type = CT_HISTOGRAM; @@ -379,6 +656,54 @@ cmd_graph (struct lexer *lexer, struct dataset *ds) goto error; } } + else if (lex_match_id (lexer, "BAR")) + { + if (graph.chart_type != CT_NONE) + { + lex_error (lexer, _("Only one chart type is allowed.")); + goto error; + } + graph.chart_type = CT_BAR; + graph.bar_type = CBT_SIMPLE; + + if (lex_match (lexer, T_LPAREN)) + { + if (lex_match_id (lexer, "SIMPLE")) + { + /* This is the default anyway */ + } + else if (lex_match_id (lexer, "GROUPED")) + { + graph.bar_type = CBT_GROUPED; + goto error; + } + else if (lex_match_id (lexer, "STACKED")) + { + graph.bar_type = CBT_STACKED; + lex_error (lexer, _("%s is not yet implemented."), "STACKED"); + goto error; + } + else if (lex_match_id (lexer, "RANGE")) + { + graph.bar_type = CBT_RANGE; + lex_error (lexer, _("%s is not yet implemented."), "RANGE"); + goto error; + } + else + { + lex_error (lexer, NULL); + goto error; + } + if (!lex_force_match (lexer, T_RPAREN)) + goto error; + } + + if (!lex_force_match (lexer, T_EQUALS)) + goto error; + + if (! parse_function (lexer, &graph)) + goto error; + } else if (lex_match_id (lexer, "SCATTERPLOT")) { if (graph.chart_type != CT_NONE) @@ -452,14 +777,9 @@ cmd_graph (struct lexer *lexer, struct dataset *ds) lex_error (lexer, _("Variable expected")); goto error; } - graph.byvar = v; + graph.by_var[0] = v; } } - else if (lex_match_id (lexer, "BAR")) - { - lex_error (lexer, _("%s is not yet implemented."),"BAR"); - goto error; - } else if (lex_match_id (lexer, "LINE")) { lex_error (lexer, _("%s is not yet implemented."),"LINE"); @@ -547,16 +867,18 @@ cmd_graph (struct lexer *lexer, struct dataset *ds) /* See scatterplot.h for the setup of the case prototype */ graph.gr_proto = caseproto_add_width (graph.gr_proto, 0); /* x value - SP_IDX_X*/ graph.gr_proto = caseproto_add_width (graph.gr_proto, 0); /* y value - SP_IDX_Y*/ - /* The byvar contains the plot categories for the different xy plot colors */ - if (graph.byvar) /* SP_IDX_BY */ - graph.gr_proto = caseproto_add_width (graph.gr_proto, var_get_width(graph.byvar)); + /* The by_var contains the plot categories for the different xy plot colors */ + if (graph.n_by_vars > 0) /* SP_IDX_BY */ + graph.gr_proto = caseproto_add_width (graph.gr_proto, var_get_width(graph.by_var[0])); break; case CT_HISTOGRAM: graph.gr_proto = caseproto_add_width (graph.gr_proto, 0); /* x value */ graph.gr_proto = caseproto_add_width (graph.gr_proto, 0); /* weight value */ break; + case CT_BAR: + break; case CT_NONE: - lex_error_expecting (lexer,"HISTOGRAM","SCATTERPLOT",NULL); + lex_error_expecting (lexer, "HISTOGRAM", "SCATTERPLOT", "BAR", NULL); goto error; default: NOT_REACHED (); @@ -570,11 +892,17 @@ cmd_graph (struct lexer *lexer, struct dataset *ds) grouper = casegrouper_create_splits (proc_open (ds), graph.dict); while (casegrouper_get_next_group (grouper, &group)) - run_graph (&graph, group); + { + if (graph.chart_type == CT_BAR) + run_barchart (&graph, group); + else + run_graph (&graph, group); + } ok = casegrouper_destroy (grouper); ok = proc_commit (ds) && ok; } + subcase_destroy (&graph.ordering); free (graph.dep_vars); pool_destroy (graph.pool); caseproto_unref (graph.gr_proto); @@ -582,6 +910,7 @@ cmd_graph (struct lexer *lexer, struct dataset *ds) return CMD_SUCCESS; error: + subcase_destroy (&graph.ordering); caseproto_unref (graph.gr_proto); free (graph.dep_vars); pool_destroy (graph.pool); diff --git a/src/ui/gui/automake.mk b/src/ui/gui/automake.mk index 59200b09a7..ad581e8b03 100644 --- a/src/ui/gui/automake.mk +++ b/src/ui/gui/automake.mk @@ -5,6 +5,7 @@ UI_FILES = \ src/ui/gui/autorecode.ui \ src/ui/gui/binomial.ui \ src/ui/gui/compute.ui \ + src/ui/gui/barchart.ui \ src/ui/gui/correlation.ui \ src/ui/gui/count.ui \ src/ui/gui/crosstabs.ui \ @@ -17,6 +18,7 @@ UI_FILES = \ src/ui/gui/factor.ui \ src/ui/gui/find.ui \ src/ui/gui/frequencies.ui \ + src/ui/gui/histogram.ui \ src/ui/gui/indep-samples.ui \ src/ui/gui/k-means.ui \ src/ui/gui/k-related.ui \ @@ -35,6 +37,7 @@ UI_FILES = \ src/ui/gui/regression.ui \ src/ui/gui/reliability.ui \ src/ui/gui/roc.ui \ + src/ui/gui/scatterplot.ui \ src/ui/gui/select-cases.ui \ src/ui/gui/t-test.ui \ src/ui/gui/text-data-import.ui \ @@ -189,6 +192,8 @@ src_ui_gui_psppire_SOURCES = \ src/ui/gui/psppire-dialog-action.h \ src/ui/gui/psppire-dialog-action-1sks.c \ src/ui/gui/psppire-dialog-action-1sks.h \ + src/ui/gui/psppire-dialog-action-barchart.c \ + src/ui/gui/psppire-dialog-action-barchart.h \ src/ui/gui/psppire-dialog-action-binomial.c \ src/ui/gui/psppire-dialog-action-binomial.h \ src/ui/gui/psppire-dialog-action-chisquare.c \ @@ -211,6 +216,8 @@ src_ui_gui_psppire_SOURCES = \ src/ui/gui/psppire-dialog-action-flip.h \ src/ui/gui/psppire-dialog-action-frequencies.c \ src/ui/gui/psppire-dialog-action-frequencies.h \ + src/ui/gui/psppire-dialog-action-histogram.c \ + src/ui/gui/psppire-dialog-action-histogram.h \ src/ui/gui/psppire-dialog-action-indep-samps.c \ src/ui/gui/psppire-dialog-action-indep-samps.h \ src/ui/gui/psppire-dialog-action-kmeans.c \ @@ -235,6 +242,8 @@ src_ui_gui_psppire_SOURCES = \ src/ui/gui/psppire-dialog-action-roc.h \ src/ui/gui/psppire-dialog-action-runs.c \ src/ui/gui/psppire-dialog-action-runs.h \ + src/ui/gui/psppire-dialog-action-scatterplot.c \ + src/ui/gui/psppire-dialog-action-scatterplot.h \ src/ui/gui/psppire-dialog-action-sort.c \ src/ui/gui/psppire-dialog-action-sort.h \ src/ui/gui/psppire-dialog-action-tt1s.c \ diff --git a/src/ui/gui/barchart.ui b/src/ui/gui/barchart.ui new file mode 100644 index 0000000000..e27e6c35d1 --- /dev/null +++ b/src/ui/gui/barchart.ui @@ -0,0 +1,388 @@ + + + + + + + False + Barchart + True + GRAPH + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 2 + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + True + + + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + False + False + 5 + True + dict-view + entry1 + + + 1 + 1 + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + never + etched-in + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + False + + + + + + + + 0 + 0 + 3 + + + + + True + False + True + 0 + none + + + True + False + 12 + + + True + True + • + False + False + + + + + + + True + False + Category A_xis: + True + True + entry1 + + + + + 2 + 1 + + + + + True + False + True + True + 0 + + + True + False + 5 + + + _N of cases + True + True + False + True + True + True + 0 + True + True + + + 0 + 0 + + + + + _Cum. n of cases + True + True + False + True + 0 + True + radiobutton-count + + + 0 + 1 + + + + + Other _summary function + True + True + False + True + True + 0 + True + radiobutton-count + + + 0 + 2 + + + + + % of c_ases + True + True + False + True + 0 + True + radiobutton-count + + + 1 + 0 + + + + + C_um. % of cases + True + True + False + True + True + 0 + True + radiobutton-count + + + 1 + 1 + + + + + True + False + + + True + False + 0.5 + none + 9.9999997473787516e-05 + + + True + True + True + 5 + dict-view + entry2 + + + + + False + False + 0 + + + + + True + False + 0 + none + + + True + False + 12 + + + True + True + • + False + False + + + + + + + True + False + _Variable: + True + True + entry1 + + + + + True + True + 1 + + + + + 0 + 3 + 2 + + + + + True + False + center + False + + + 1 + 2 + + + + + + + True + False + Bars Represent + True + + + + + 1 + 0 + 2 + + + + + True + False + True + 0 + none + + + True + False + 12 + + + True + True + • + False + False + + + + + + + True + False + Category C_luster: + True + True + entry1 + + + + + 2 + 2 + + + + + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + False + False + 5 + dict-view + entry3 + + + 1 + 2 + + + + + True + True + 0 + + + + + True + False + 5 + vertical + + + False + False + end + 1 + + + + + + diff --git a/src/ui/gui/data-editor.ui b/src/ui/gui/data-editor.ui index 10f0f5f8c4..ae4ba2b4ef 100644 --- a/src/ui/gui/data-editor.ui +++ b/src/ui/gui/data-editor.ui @@ -479,6 +479,36 @@ analyze-roc-curve + + + graphs + _Graphs + + + + + uimanager1 + graphs_scatterplot + _Scatterplot + graphs-scatterplot + + + + + uimanager1 + graphs_histogram + _Histogram + graphs-histogram + + + + + uimanager1 + graphs_barchart + _Barchart + graphs-barchart + + utilities @@ -612,6 +642,14 @@ + + + + + + + + diff --git a/src/ui/gui/histogram.ui b/src/ui/gui/histogram.ui new file mode 100644 index 0000000000..35f5ea2370 --- /dev/null +++ b/src/ui/gui/histogram.ui @@ -0,0 +1,177 @@ + + + + + + + Histogram + True + GRAPH + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 2 + + + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 3 + 3 + + + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + True + 5 + dict-view + entry1 + + + 1 + 2 + 1 + 2 + + + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + never + automatic + etched-in + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False + + + + + 3 + + + + + True + 0 + none + + + True + 12 + + + True + True + + + + + + + + True + _Variable: + True + True + entry1 + + + + + 2 + 3 + 1 + 2 + GTK_FILL + + + + + True + vertical + + + True + 0 + + + True + 12 + + + True + vertical + + + _Display normal curve + True + True + False + True + True + + + False + False + 0 + + + + + + + + + True + Display + True + + + + + 1 + + + + + 1 + 3 + 2 + 3 + GTK_FILL + 5 + 2 + + + + + 0 + + + + + True + 5 + vertical + + + False + False + end + 1 + + + + + + diff --git a/src/ui/gui/psppire-dialog-action-barchart.c b/src/ui/gui/psppire-dialog-action-barchart.c new file mode 100644 index 0000000000..87b8fa1318 --- /dev/null +++ b/src/ui/gui/psppire-dialog-action-barchart.c @@ -0,0 +1,238 @@ +/* PSPPIRE - a graphical user interface for PSPP. + Copyright (C) 2015 Free Software Foundation + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + 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 . */ + + +#include + +#include "psppire-dialog-action-barchart.h" +#include "psppire-value-entry.h" + +#include "dialog-common.h" +#include +#include "psppire-var-view.h" + +#include "psppire-dialog.h" +#include "builder-wrapper.h" + +#include "psppire-dict.h" +#include "libpspp/str.h" + +#include "language/stats/chart-category.h" + +static void +psppire_dialog_action_barchart_class_init (PsppireDialogActionBarchartClass *class); + +G_DEFINE_TYPE (PsppireDialogActionBarchart, psppire_dialog_action_barchart, PSPPIRE_TYPE_DIALOG_ACTION); + +static gboolean +dialog_state_valid (gpointer rd_) +{ + PsppireDialogActionBarchart *rd = PSPPIRE_DIALOG_ACTION_BARCHART (rd_); + + if ( gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->button_summary_func)) ) + { + if (0 == g_strcmp0 ("", gtk_entry_get_text (GTK_ENTRY (rd->var)))) + return FALSE; + } + + if (0 == g_strcmp0 ("", gtk_entry_get_text (GTK_ENTRY (rd->variable_xaxis)))) + return FALSE; + + return TRUE; +} + +static void +refresh (PsppireDialogAction *rd_) +{ + PsppireDialogActionBarchart *rd = PSPPIRE_DIALOG_ACTION_BARCHART (rd_); + + gtk_entry_set_text (GTK_ENTRY (rd->var), ""); + gtk_entry_set_text (GTK_ENTRY (rd->variable_xaxis), ""); + gtk_entry_set_text (GTK_ENTRY (rd->variable_cluster), ""); + + /* Set summary_func to true, then let it get unset again. + This ensures that the signal handler gets called. */ + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rd->button_summary_func), TRUE); + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rd->button_freq_func[0]), TRUE); + + gtk_widget_set_sensitive (rd->combobox, FALSE); + + gtk_combo_box_set_active (GTK_COMBO_BOX (rd->combobox), 0); +} + +static void +on_summary_toggle (PsppireDialogActionBarchart *act) +{ + gboolean status = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (act->button_summary_func)); + + gtk_widget_set_sensitive (act->summary_variables, status); + gtk_widget_set_sensitive (act->combobox, status); +} + +static void +populate_combo_model (GtkComboBox *cb) +{ + int i; + GtkListStore *list = gtk_list_store_new (4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT, G_TYPE_INT); + GtkTreeIter iter; + GtkCellRenderer *renderer ; + + for (i = 0; i < N_AG_FUNCS; ++i) + { + const struct ag_func *af = ag_func + i; + + if (af->arity == 0) + continue; + + gtk_list_store_append (list, &iter); + gtk_list_store_set (list, &iter, + 0, af->description, + 1, af->name, + -1); + } + + renderer = gtk_cell_renderer_text_new (); + gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cb), renderer, FALSE); + + gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (cb), renderer, "text", 0); + + gtk_combo_box_set_model (GTK_COMBO_BOX (cb), GTK_TREE_MODEL (list)); + g_object_unref (list); +} + + +static void +psppire_dialog_action_barchart_activate (GtkAction *a) +{ + PsppireDialogActionBarchart *act = PSPPIRE_DIALOG_ACTION_BARCHART (a); + PsppireDialogAction *pda = PSPPIRE_DIALOG_ACTION (a); + + GtkBuilder *xml = builder_new ("barchart.ui"); + pda->dialog = get_widget_assert (xml, "barchart-dialog"); + pda->source = get_widget_assert (xml, "dict-view"); + + act->variable_xaxis = get_widget_assert (xml, "entry1"); + act->variable_cluster = get_widget_assert (xml, "entry3"); + act->var = get_widget_assert (xml, "entry2"); + act->button_freq_func[0] = get_widget_assert (xml, "radiobutton-count"); + act->button_freq_func[1] = get_widget_assert (xml, "radiobutton-percent"); + act->button_freq_func[2] = get_widget_assert (xml, "radiobutton-cum-count"); + act->button_freq_func[3] = get_widget_assert (xml, "radiobutton-cum-percent"); + + act->button_summary_func = get_widget_assert (xml, "radiobutton3"); + act->summary_variables = get_widget_assert (xml, "hbox1"); + act->combobox = get_widget_assert (xml, "combobox1"); + + populate_combo_model (GTK_COMBO_BOX(act->combobox)); + + g_object_unref (xml); + + g_signal_connect_swapped (act->button_summary_func, "toggled", + G_CALLBACK (on_summary_toggle), act); + + psppire_dialog_action_set_refresh (pda, refresh); + + psppire_dialog_action_set_valid_predicate (pda, + dialog_state_valid); + + if (PSPPIRE_DIALOG_ACTION_CLASS (psppire_dialog_action_barchart_parent_class)->activate) + PSPPIRE_DIALOG_ACTION_CLASS (psppire_dialog_action_barchart_parent_class)->activate (pda); +} + +static char * +generate_syntax (PsppireDialogAction *a) +{ + PsppireDialogActionBarchart *rd = PSPPIRE_DIALOG_ACTION_BARCHART (a); + gchar *text; + const gchar *var_name_xaxis = gtk_entry_get_text (GTK_ENTRY (rd->variable_xaxis)); + const gchar *var_name_cluster = gtk_entry_get_text (GTK_ENTRY (rd->variable_cluster)); + + GString *string = g_string_new ("GRAPH /BAR = "); + + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->button_summary_func))) + { + GtkTreeIter iter; + if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (rd->combobox), &iter)) + { + GValue value = {0}; + GtkTreeModel *model = gtk_combo_box_get_model (GTK_COMBO_BOX (rd->combobox)); + gtk_tree_model_get_value (model, &iter, 1, &value); + g_string_append (string, g_value_get_string (&value)); + g_value_unset (&value); + } + g_string_append (string, " ("); + g_string_append (string, gtk_entry_get_text (GTK_ENTRY (rd->var))); + g_string_append (string, ")"); + } + else + { + int b; + for (b = 0; b < 4; ++b) + { + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->button_freq_func[b]))) + break; + } + switch (b) + { + case 0: + g_string_append (string, "COUNT"); + break; + case 1: + g_string_append (string, "PCT"); + break; + case 2: + g_string_append (string, "CUFREQ"); + break; + case 3: + g_string_append (string, "CUPCT"); + break; + default: + g_assert_not_reached (); + break; + } + } + + g_string_append (string, " BY "); + g_string_append (string, var_name_xaxis); + + if (g_strcmp0 (var_name_cluster, "")) + { + g_string_append (string, " BY "); + g_string_append (string, var_name_cluster); + } + + g_string_append (string, ".\n"); + + text = string->str; + + g_string_free (string, FALSE); + + return text; +} + +static void +psppire_dialog_action_barchart_class_init (PsppireDialogActionBarchartClass *class) +{ + psppire_dialog_action_set_activation (class, psppire_dialog_action_barchart_activate); + + PSPPIRE_DIALOG_ACTION_CLASS (class)->generate_syntax = generate_syntax; +} + + +static void +psppire_dialog_action_barchart_init (PsppireDialogActionBarchart *act) +{ +} diff --git a/src/ui/gui/psppire-dialog-action-barchart.h b/src/ui/gui/psppire-dialog-action-barchart.h new file mode 100644 index 0000000000..4ebc8e9867 --- /dev/null +++ b/src/ui/gui/psppire-dialog-action-barchart.h @@ -0,0 +1,82 @@ +/* PSPPIRE - a graphical user interface for PSPP. + Copyright (C) 2015 Free Software Foundation + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + 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 . */ + + +#include +#include + +#include "psppire-dialog-action.h" + +#ifndef __PSPPIRE_DIALOG_ACTION_BARCHART_H__ +#define __PSPPIRE_DIALOG_ACTION_BARCHART_H__ + +G_BEGIN_DECLS + + +#define PSPPIRE_TYPE_DIALOG_ACTION_BARCHART (psppire_dialog_action_barchart_get_type ()) + +#define PSPPIRE_DIALOG_ACTION_BARCHART(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + PSPPIRE_TYPE_DIALOG_ACTION_BARCHART, PsppireDialogActionBarchart)) + +#define PSPPIRE_DIALOG_ACTION_BARCHART_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + PSPPIRE_TYPE_DIALOG_ACTION_BARCHART, \ + PsppireDialogActionBarchartClass)) + + +#define PSPPIRE_IS_DIALOG_ACTION_BARCHART(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PSPPIRE_TYPE_DIALOG_ACTION_BARCHART)) + +#define PSPPIRE_IS_DIALOG_ACTION_BARCHART_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), PSPPIRE_TYPE_DIALOG_ACTION_BARCHART)) + + +#define PSPPIRE_DIALOG_ACTION_BARCHART_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + PSPPIRE_TYPE_DIALOG_ACTION_BARCHART, \ + PsppireDialogActionBarchartClass)) + +typedef struct _PsppireDialogActionBarchart PsppireDialogActionBarchart; +typedef struct _PsppireDialogActionBarchartClass PsppireDialogActionBarchartClass; + + +struct _PsppireDialogActionBarchart +{ + PsppireDialogAction parent; + + /*< private >*/ + + GtkWidget *variable_xaxis; + GtkWidget *variable_cluster; + GtkWidget *var; + GtkWidget *button_freq_func[4]; + GtkWidget *button_summary_func; + GtkWidget *summary_variables; + GtkWidget *combobox; +}; + + +struct _PsppireDialogActionBarchartClass +{ + PsppireDialogActionClass parent_class; +}; + + +GType psppire_dialog_action_barchart_get_type (void) ; + +G_END_DECLS + +#endif /* __PSPPIRE_DIALOG_ACTION_BARCHART_H__ */ diff --git a/src/ui/gui/psppire-dialog-action-histogram.c b/src/ui/gui/psppire-dialog-action-histogram.c new file mode 100644 index 0000000000..5a57c2aece --- /dev/null +++ b/src/ui/gui/psppire-dialog-action-histogram.c @@ -0,0 +1,128 @@ +/* PSPPIRE - a graphical user interface for PSPP. + Copyright (C) 2015 Free Software Foundation + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + 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 . */ + + +#include + +#include "psppire-dialog-action-histogram.h" +#include "psppire-value-entry.h" + +#include "dialog-common.h" +#include +#include "psppire-var-view.h" + +#include "psppire-dialog.h" +#include "builder-wrapper.h" + +#include "psppire-dict.h" +#include "libpspp/str.h" + +static void +psppire_dialog_action_histogram_class_init (PsppireDialogActionHistogramClass *class); + +G_DEFINE_TYPE (PsppireDialogActionHistogram, psppire_dialog_action_histogram, PSPPIRE_TYPE_DIALOG_ACTION); + +static gboolean +dialog_state_valid (gpointer data) +{ + PsppireDialogActionHistogram *rd = data; + + const gchar *var_name = gtk_entry_get_text (GTK_ENTRY (rd->variable)); + const struct variable *var = psppire_dict_lookup_var (PSPPIRE_DIALOG_ACTION (rd)->dict, var_name); + + if ( var == NULL) + return FALSE; + + + return TRUE; +} + +static void +refresh (PsppireDialogAction *rd_) +{ + PsppireDialogActionHistogram *rd = PSPPIRE_DIALOG_ACTION_HISTOGRAM (rd_); + + gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (rd->curve), FALSE); + gtk_entry_set_text (GTK_ENTRY (rd->variable), ""); +} + +static void +psppire_dialog_action_histogram_activate (GtkAction *a) +{ + PsppireDialogActionHistogram *act = PSPPIRE_DIALOG_ACTION_HISTOGRAM (a); + PsppireDialogAction *pda = PSPPIRE_DIALOG_ACTION (a); + + GtkBuilder *xml = builder_new ("histogram.ui"); + pda->dialog = get_widget_assert (xml, "histogram-dialog"); + pda->source = get_widget_assert (xml, "dict-view"); + + act->variable = get_widget_assert (xml, "entry1"); + act->curve = get_widget_assert (xml, "curve"); + + g_object_unref (xml); + + psppire_dialog_action_set_refresh (pda, refresh); + + psppire_dialog_action_set_valid_predicate (pda, + dialog_state_valid); + + if (PSPPIRE_DIALOG_ACTION_CLASS (psppire_dialog_action_histogram_parent_class)->activate) + PSPPIRE_DIALOG_ACTION_CLASS (psppire_dialog_action_histogram_parent_class)->activate (pda); +} + + + +static char * +generate_syntax (PsppireDialogAction *a) +{ + PsppireDialogActionHistogram *rd = PSPPIRE_DIALOG_ACTION_HISTOGRAM (a); + gchar *text; + const gchar *var_name = gtk_entry_get_text (GTK_ENTRY (rd->variable)); + GString *string = g_string_new ("GRAPH /HISTOGRAM "); + + if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rd->curve))) + { + g_string_append (string, "(NORMAL)"); + } + + g_string_append (string, " = "); + g_string_append (string, var_name); + + g_string_append (string, ".\n"); + + text = string->str; + + g_string_free (string, FALSE); + + return text; +} + +static void +psppire_dialog_action_histogram_class_init (PsppireDialogActionHistogramClass *class) +{ + GtkActionClass *action_class = GTK_ACTION_CLASS (class); + + action_class->activate = psppire_dialog_action_histogram_activate; + + PSPPIRE_DIALOG_ACTION_CLASS (class)->generate_syntax = generate_syntax; +} + + +static void +psppire_dialog_action_histogram_init (PsppireDialogActionHistogram *act) +{ +} + diff --git a/src/ui/gui/psppire-dialog-action-histogram.h b/src/ui/gui/psppire-dialog-action-histogram.h new file mode 100644 index 0000000000..a80910b826 --- /dev/null +++ b/src/ui/gui/psppire-dialog-action-histogram.h @@ -0,0 +1,78 @@ +/* PSPPIRE - a graphical user interface for PSPP. + Copyright (C) 2015 Free Software Foundation + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + 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 . */ + + +#include +#include + +#include "psppire-dialog-action.h" + +#ifndef __PSPPIRE_DIALOG_ACTION_HISTOGRAM_H__ +#define __PSPPIRE_DIALOG_ACTION_HISTOGRAM_H__ + +G_BEGIN_DECLS + + +#define PSPPIRE_TYPE_DIALOG_ACTION_HISTOGRAM (psppire_dialog_action_histogram_get_type ()) + +#define PSPPIRE_DIALOG_ACTION_HISTOGRAM(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + PSPPIRE_TYPE_DIALOG_ACTION_HISTOGRAM, PsppireDialogActionHistogram)) + +#define PSPPIRE_DIALOG_ACTION_HISTOGRAM_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + PSPPIRE_TYPE_DIALOG_ACTION_HISTOGRAM, \ + PsppireDialogActionHistogramClass)) + + +#define PSPPIRE_IS_DIALOG_ACTION_HISTOGRAM(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PSPPIRE_TYPE_DIALOG_ACTION_HISTOGRAM)) + +#define PSPPIRE_IS_DIALOG_ACTION_HISTOGRAM_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), PSPPIRE_TYPE_DIALOG_ACTION_HISTOGRAM)) + + +#define PSPPIRE_DIALOG_ACTION_HISTOGRAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + PSPPIRE_TYPE_DIALOG_ACTION_HISTOGRAM, \ + PsppireDialogActionHistogramClass)) + +typedef struct _PsppireDialogActionHistogram PsppireDialogActionHistogram; +typedef struct _PsppireDialogActionHistogramClass PsppireDialogActionHistogramClass; + + +struct _PsppireDialogActionHistogram +{ + PsppireDialogAction parent; + + /*< private >*/ + gboolean dispose_has_run ; + + GtkWidget *variable; + GtkWidget *curve; +}; + + +struct _PsppireDialogActionHistogramClass +{ + PsppireDialogActionClass parent_class; +}; + + +GType psppire_dialog_action_histogram_get_type (void) ; + +G_END_DECLS + +#endif /* __PSPPIRE_DIALOG_ACTION_HISTOGRAM_H__ */ diff --git a/src/ui/gui/psppire-dialog-action-scatterplot.c b/src/ui/gui/psppire-dialog-action-scatterplot.c new file mode 100644 index 0000000000..64017df7c9 --- /dev/null +++ b/src/ui/gui/psppire-dialog-action-scatterplot.c @@ -0,0 +1,129 @@ +/* PSPPIRE - a graphical user interface for PSPP. + Copyright (C) 2015 Free Software Foundation + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + 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 . */ + + +#include + +#include "psppire-dialog-action-scatterplot.h" + +#include "psppire-var-view.h" + +#include "psppire-dialog.h" +#include "builder-wrapper.h" +#include "helper.h" + + +#include "gettext.h" +#define _(msgid) gettext (msgid) +#define N_(msgid) msgid + + +static void psppire_dialog_action_scatterplot_init (PsppireDialogActionScatterplot *act); +static void psppire_dialog_action_scatterplot_class_init (PsppireDialogActionScatterplotClass *class); + +G_DEFINE_TYPE (PsppireDialogActionScatterplot, psppire_dialog_action_scatterplot, PSPPIRE_TYPE_DIALOG_ACTION); + + +static char * +generate_syntax (PsppireDialogAction *act) +{ + PsppireDialogActionScatterplot *ow = PSPPIRE_DIALOG_ACTION_SCATTERPLOT (act); + gchar *text; + struct string dss; + + ds_init_cstr (&dss, "GRAPH SCATTERPLOT(BIVARIATE) = "); + + ds_put_cstr (&dss, gtk_entry_get_text (GTK_ENTRY (ow->y_axis))); + + ds_put_cstr (&dss, " WITH "); + + ds_put_cstr (&dss, gtk_entry_get_text (GTK_ENTRY (ow->x_axis))); + + ds_put_cstr (&dss, ".\n"); + + text = ds_steal_cstr (&dss); + ds_destroy (&dss); + + return text; +} + + +static gboolean +dialog_state_valid (gpointer data) +{ + PsppireDialogActionScatterplot *ow = PSPPIRE_DIALOG_ACTION_SCATTERPLOT (data); + + const char *xvar = gtk_entry_get_text (GTK_ENTRY (ow->x_axis)); + const char *yvar = gtk_entry_get_text (GTK_ENTRY (ow->y_axis)); + + if ( 0 == strcmp ("", xvar)) + return FALSE; + + if ( 0 == strcmp ("", yvar)) + return FALSE; + + + return TRUE; +} + +static void +refresh (PsppireDialogAction *rd_) +{ + PsppireDialogActionScatterplot *ow = PSPPIRE_DIALOG_ACTION_SCATTERPLOT (rd_); + + gtk_entry_set_text (GTK_ENTRY (ow->x_axis), ""); + gtk_entry_set_text (GTK_ENTRY (ow->y_axis), ""); +} + + + +static void +psppire_dialog_action_scatterplot_activate (GtkAction *a) +{ + PsppireDialogAction *pda = PSPPIRE_DIALOG_ACTION (a); + PsppireDialogActionScatterplot *act = PSPPIRE_DIALOG_ACTION_SCATTERPLOT (a); + + GtkBuilder *xml = builder_new ("scatterplot.ui"); + + pda->dialog = get_widget_assert (xml, "scatterplot-dialog"); + pda->source = get_widget_assert (xml, "scatterplot-treeview1"); + + act->y_axis = get_widget_assert (xml, "scatterplot-y-axis"); + act->x_axis = get_widget_assert (xml, "scatterplot-x-axis"); + + psppire_dialog_action_set_valid_predicate (pda, dialog_state_valid); + psppire_dialog_action_set_refresh (pda, refresh); + + g_object_unref (xml); + + if (PSPPIRE_DIALOG_ACTION_CLASS (psppire_dialog_action_scatterplot_parent_class)->activate) + PSPPIRE_DIALOG_ACTION_CLASS (psppire_dialog_action_scatterplot_parent_class)->activate (pda); +} + +static void +psppire_dialog_action_scatterplot_class_init (PsppireDialogActionScatterplotClass *class) +{ + GtkActionClass *action_class = GTK_ACTION_CLASS (class); + + action_class->activate = psppire_dialog_action_scatterplot_activate; + PSPPIRE_DIALOG_ACTION_CLASS (class)->generate_syntax = generate_syntax; +} + + +static void +psppire_dialog_action_scatterplot_init (PsppireDialogActionScatterplot *act) +{ +} diff --git a/src/ui/gui/psppire-dialog-action-scatterplot.h b/src/ui/gui/psppire-dialog-action-scatterplot.h new file mode 100644 index 0000000000..948ec3b283 --- /dev/null +++ b/src/ui/gui/psppire-dialog-action-scatterplot.h @@ -0,0 +1,78 @@ +/* PSPPIRE - a graphical user interface for PSPP. + Copyright (C) 2015 Free Software Foundation + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + 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 . */ + + +#include +#include + +#include "psppire-dialog-action.h" + +#ifndef __PSPPIRE_DIALOG_ACTION_SCATTERPLOT_H__ +#define __PSPPIRE_DIALOG_ACTION_SCATTERPLOT_H__ + +G_BEGIN_DECLS + + +#define PSPPIRE_TYPE_DIALOG_ACTION_SCATTERPLOT (psppire_dialog_action_scatterplot_get_type ()) + +#define PSPPIRE_DIALOG_ACTION_SCATTERPLOT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + PSPPIRE_TYPE_DIALOG_ACTION_SCATTERPLOT, PsppireDialogActionScatterplot)) + +#define PSPPIRE_DIALOG_ACTION_SCATTERPLOT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), \ + PSPPIRE_TYPE_DIALOG_ACTION_SCATTERPLOT, \ + PsppireDialogActionScatterplotClass)) + + +#define PSPPIRE_IS_DIALOG_ACTION_SCATTERPLOT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), PSPPIRE_TYPE_DIALOG_ACTION_SCATTERPLOT)) + +#define PSPPIRE_IS_DIALOG_ACTION_SCATTERPLOT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), PSPPIRE_TYPE_DIALOG_ACTION_SCATTERPLOT)) + + +#define PSPPIRE_DIALOG_ACTION_SCATTERPLOT_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + PSPPIRE_TYPE_DIALOG_ACTION_SCATTERPLOT, \ + PsppireDialogActionScatterplotClass)) + +typedef struct _PsppireDialogActionScatterplot PsppireDialogActionScatterplot; +typedef struct _PsppireDialogActionScatterplotClass PsppireDialogActionScatterplotClass; + + +struct _PsppireDialogActionScatterplot +{ + PsppireDialogAction parent; + + /*< private >*/ + gboolean dispose_has_run ; + + GtkWidget *x_axis; + GtkWidget *y_axis; +}; + + +struct _PsppireDialogActionScatterplotClass +{ + PsppireDialogActionClass parent_class; +}; + + +GType psppire_dialog_action_scatterplot_get_type (void) ; + +G_END_DECLS + +#endif /* __PSPPIRE_DIALOG_ACTION_SCATTERPLOT_H__ */ diff --git a/src/ui/gui/scatterplot.ui b/src/ui/gui/scatterplot.ui new file mode 100644 index 0000000000..bada0a3790 --- /dev/null +++ b/src/ui/gui/scatterplot.ui @@ -0,0 +1,223 @@ + + + + + + + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + Scatterplot + True + GRAPH + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 2 + + + True + False + 5 + 5 + 5 + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 2 + 3 + 5 + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + _X Axis: + True + scatterplot-x-axis + + + False + False + 0 + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + ● + False + False + True + True + + + True + True + 1 + + + + + 2 + 3 + 1 + 2 + + + + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 0 + _Y Axis: + True + scatterplot-y-axis + + + False + False + 0 + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + ● + False + False + True + True + + + True + True + 2 + + + + + 2 + 3 + GTK_EXPAND + + + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + never + automatic + etched-in + + + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + False + False + + + + + 2 + + + + + True + False + 0 + 0 + + + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + scatterplot-treeview1 + scatterplot-x-axis + + + + + 1 + 2 + 1 + 2 + GTK_FILL + + + + + True + False + 0 + 0 + + + True + True + True + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + scatterplot-treeview1 + scatterplot-y-axis + + + + + 1 + 2 + GTK_FILL + + + + + + + True + True + 0 + + + + + True + False + GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK + 5 + + + False + False + end + 1 + + + + + + diff --git a/src/ui/gui/widgets.c b/src/ui/gui/widgets.c index c8b21e69a9..71c472d548 100644 --- a/src/ui/gui/widgets.c +++ b/src/ui/gui/widgets.c @@ -16,6 +16,7 @@ #include "psppire-val-chooser.h" #include "psppire-checkbox-treeview.h" +#include "psppire-dialog-action-barchart.h" #include "psppire-dialog-action-binomial.h" #include "psppire-dialog-action-chisquare.h" #include "psppire-dialog-action-compute.h" @@ -27,6 +28,7 @@ #include "psppire-dialog-action-flip.h" #include "psppire-dialog-action-factor.h" #include "psppire-dialog-action-frequencies.h" +#include "psppire-dialog-action-histogram.h" #include "psppire-dialog-action-indep-samps.h" #include "psppire-dialog-action-k-related.h" #include "psppire-dialog-action-1sks.h" @@ -41,6 +43,7 @@ #include "psppire-dialog-action-reliability.h" #include "psppire-dialog-action-roc.h" #include "psppire-dialog-action-runs.h" +#include "psppire-dialog-action-scatterplot.h" #include "psppire-dialog-action-sort.h" #include "psppire-dialog-action-tt1s.h" #include "psppire-dialog-action-two-sample.h" @@ -70,6 +73,7 @@ preregister_widgets (void) psppire_dialog_action_1sks_get_type (); psppire_dialog_action_binomial_get_type (); + psppire_dialog_action_barchart_get_type (); psppire_dialog_action_chisquare_get_type (); psppire_dialog_action_compute_get_type (); psppire_dialog_action_correlation_get_type (); @@ -80,6 +84,7 @@ preregister_widgets (void) psppire_dialog_action_factor_get_type (); psppire_dialog_action_flip_get_type (); psppire_dialog_action_frequencies_get_type (); + psppire_dialog_action_histogram_get_type (); psppire_dialog_action_logistic_get_type (); psppire_dialog_action_kmeans_get_type (); psppire_dialog_action_k_related_get_type (); @@ -94,6 +99,7 @@ preregister_widgets (void) psppire_dialog_action_regression_get_type (); psppire_dialog_action_roc_get_type (); psppire_dialog_action_runs_get_type (); + psppire_dialog_action_scatterplot_get_type (); psppire_dialog_action_sort_get_type (); psppire_dialog_action_tt1s_get_type (); psppire_dialog_action_two_sample_get_type (); diff --git a/tests/language/dictionary/split-file.at b/tests/language/dictionary/split-file.at index f1fc33d8a3..83055bbf21 100644 --- a/tests/language/dictionary/split-file.at +++ b/tests/language/dictionary/split-file.at @@ -93,6 +93,8 @@ FREQUENCIES b. GLM c BY b. GRAPH /HISTOGRAM = b . GRAPH /SCATTERPLOT(BIVARIATE) = b with c by e . +GRAPH /BAR (GROUPED) = MEAN(b) by c by e. +GRAPH /BAR = COUNT BY b. LIST. LOGISTIC REGRESSION q WITH b. MEANS c b. diff --git a/tests/language/stats/graph.at b/tests/language/stats/graph.at index ae716a3b8d..e3dce9d538 100644 --- a/tests/language/stats/graph.at +++ b/tests/language/stats/graph.at @@ -163,3 +163,131 @@ graph AT_CHECK([pspp -o pspp.pdf null-hist.sps], [0], [ignore]) dnl Ignore output -- this is just a no-crash check. AT_CLEANUP + + + + +AT_SETUP([GRAPH barcharts]) +AT_CHECK([ln -s $top_srcdir/examples/physiology.sav .], [0]) +AT_CHECK([ln -s $top_srcdir/examples/repairs.sav .], [0]) + +AT_DATA([barchart.sps], [dnl +GET FILE="physiology.sav". + +GRAPH /BAR = COUNT BY SEX. + +GRAPH /BAR = MEAN(height) BY SEX. + +NEW FILE. + +GET FILE="repairs.sav". + +GRAPH /BAR = MEAN (mtbf) BY factory. + +COMPUTE R = TRUNC(RV.UNIFORM(1,5)). + +GRAPH /BAR = MEAN (mtbf) BY factory BY R. +]) + +AT_CHECK([pspp -o pspp.pdf barchart.sps], [0], [ignore]) +dnl Ignore output -- this is just a no-crash check. + +AT_CLEANUP + + + +AT_SETUP([GRAPH barchart arity]) + +AT_DATA([barchart.sps], [dnl +data list notable list /x y z*. +begin data +1 1 3 +2 1 4 +3 1 3 +4 1 4 +5 . 3 +6 2 4 +7 2 3 +8 2 4 +9 2 3 +10 2 4 +end data. + +* This line is invalid +GRAPH /BAR = COUNT(x) BY y. +]) + +AT_CHECK([pspp -o pspp.pdf barchart.sps], [1], [ignore]) +dnl Ignore output -- this is just a no-crash check. + +AT_CLEANUP + + + + +AT_SETUP([GRAPH barchart bad syntax]) + +AT_DATA([barchart.sps], [dnl +data list notable list /x y z*. +begin data +1 1 3 +2 1 4 +3 1 3 +4 1 4 +5 . 3 +6 2 4 +7 2 3 +8 2 4 +9 2 3 +10 2 4 +end data. + +* This line is invalid +GRAPH /BAR = SCROD BY y. +]) + +AT_CHECK([pspp -o pspp.pdf barchart.sps], [1], [ignore]) +dnl Ignore output -- this is just a no-crash check. + +AT_CLEANUP + + + +AT_SETUP([GRAPH barchart full]) + +AT_DATA([barchart.sps], [dnl +data list notable list /x y z*. +begin data +1 1 3 +2 1 4 +3 1 3 +4 1 4 +5 . 3 +6 2 4 +7 2 3 +8 2 4 +9 2 3 +10 2 4 +end data. + +* This line is invalid +GRAPH /BAR = COUNT by z. +GRAPH /BAR = CUFREQ by z. +GRAPH /BAR = PCT by z. +GRAPH /BAR = CUPCT by z. + +GRAPH /BAR = MEAN(y) BY z. +GRAPH /BAR = SUM(y) BY z. +GRAPH /BAR = MAXIMUM(y) BY z. +GRAPH /BAR = MINIMUM(y) BY z. + +GRAPH /BAR = MEAN(y) BY z BY y. +GRAPH /BAR = SUM(y) BY z BY y. +GRAPH /BAR = MAXIMUM(y) BY z BY y. +GRAPH /BAR = MINIMUM(y) BY z BY y. +]) + +AT_CHECK([pspp -o pspp.pdf barchart.sps], [0], [ignore]) +dnl Ignore output -- this is just a no-crash check. + +AT_CLEANUP \ No newline at end of file