Gtk+ Callback functions and signals help - c

I have the following example from here, it shows this under the "Increase - Decrease" title.
#include <gtk/gtk.h>
gint count = 0;
char buf[5];
void increase(GtkWidget *widget, gpointer label)
{
count++;
sprintf(buf, "%d", count);
gtk_label_set_text(label, buf);
}
void decrease(GtkWidget *widget, gpointer label)
{
count--;
sprintf(buf, "%d", count);
gtk_label_set_text(label, buf);
}
int main(int argc, char** argv) {
GtkWidget *label;
GtkWidget *window;
GtkWidget *frame;
GtkWidget *plus;
GtkWidget *minus;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 250, 180);
gtk_window_set_title(GTK_WINDOW(window), "+-");
frame = gtk_fixed_new();
gtk_container_add(GTK_CONTAINER(window), frame);
plus = gtk_button_new_with_label("+");
gtk_widget_set_size_request(plus, 80, 35);
gtk_fixed_put(GTK_FIXED(frame), plus, 50, 20);
minus = gtk_button_new_with_label("-");
gtk_widget_set_size_request(minus, 80, 35);
gtk_fixed_put(GTK_FIXED(frame), minus, 50, 80);
label = gtk_label_new("0");
gtk_fixed_put(GTK_FIXED(frame), label, 190, 58);
gtk_widget_show_all(window);
g_signal_connect(window, "destroy",
G_CALLBACK (gtk_main_quit), NULL);
g_signal_connect(plus, "clicked",
G_CALLBACK(increase), label);
g_signal_connect(minus, "clicked",
G_CALLBACK(decrease), label);
gtk_main();
return 0;
}
what I'm wondering is, the g_signal_connect(plus, "clicked",G_CALLBACK(increase), label); function sends the "label" to the function increase, where its arguments are void increase(GtkWidget *widget, gpointer label). Now in the increase function the gtk_label_set_text() function requires a data type of GtkLabel as its first argument but I only see a GtkWidget variable and a void pointer label as the arguments to the increase function. If that is so how does the gtk_label_set_text() work?.

In C (but not C++), you can implicitly cast a void* to a pointer to any other type. It's very commonly seen when allocating memory with malloc, which returns a void*:
int *myIntArray = malloc(10 * sizeof(int)); // allocate array of 10 ints
Your code is doing the same thing, just with parameter passing:
void gtk_label_set_text(GtkLabel *label, const char *text);
void *label = ...;
gtk_label_set_text(label, "some string"); // label is implicitly cast from
// void* to GtkLabel*

Related

Passing an integer stored with mmap to GTK

EDIT
This question ended up being two problems packed into one. Yet, I cannot delete the question. The scope of the original question regarding pointers was solved by #David Ranieri. The mmap/fork/gtk problem will be the scope of a new question and will not be addressed here.
I want to print a value I have stored in memory in a GTK window. The integer must be stored using mmap to be retained during a fork elsewhere in the code. I cannot reference this memory mapped address from GTK without getting a SegFault. Am I doing something wrong here? Is there a better way?
My current strategy:
Reserve memory for an int with mmap at *VAL
Fork process, one half modifies VAL the other half runs GTK
Pass *VAL to app in userdata slot
The userdata now called localval in activate()
Print the value at address localval by converting from gpointer to int.
MWE (this causes a segfault, run at your own risk):
/*
* MMAP Variable SegFault
*/
#include <stdlib.h>
#include <unistd.h>
#include <sys/mman.h>
#include <gtk/gtk.h>
static void activate (GtkApplication *app, gpointer *localval) {
GtkWidget *window;
// Button Containers
GtkWidget *button_box_quit;
// Buttons
GtkWidget *exit_button;
// Text
GtkWidget *text_status;
// Define Window, dynamic size for screen.
window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "test");
gtk_window_set_default_size(GTK_WINDOW(window), 400, 300);
// Define Button Boxes.
button_box_quit = gtk_button_box_new(GTK_ORIENTATION_HORIZONTAL);
// Define Exit Button, put it in a box, put box in window
exit_button = gtk_button_new_with_label ("Exit");
gtk_container_add(GTK_CONTAINER (button_box_quit), exit_button);
gtk_container_add(GTK_CONTAINER (window), button_box_quit);
// Connect signals to buttons
g_signal_connect_swapped (exit_button, "clicked", G_CALLBACK (gtk_widget_destroy), window);
// Define text status
char msg[32]={0};
// The "print" line
g_snprintf(msg, sizeof msg, "val: %d\n", GPOINTER_TO_INT(*localval));
text_status = gtk_label_new(msg);
gtk_container_add(GTK_CONTAINER (button_box_quit), text_status);
//Activate!
gtk_widget_show_all (window);
}
int main (int argc, char **argv) {
GtkApplication *app;
int status;
int *VAL = mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
int *ABORT = mmap(NULL, sizeof(int), PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
int pid = fork();
if (pid == 0) {
while(!*ABORT) {
printf("%d\n", *VAL);
*VAL = *VAL + 1;
usleep(1000000);
}
exit(0);
} else {
app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
// The passing line
g_signal_connect (app, "activate", G_CALLBACK (activate), (gpointer *)*VAL);
status = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
*ABORT = 1;
}
*ABORT = 1;
return status;
}
In a non-working alternative to this code, I tried changing the print line to:
g_snprintf(msg, sizeof msg, "val: %d\n", &GPOINTER_TO_INT(*localval));
But the compiler thinks I want to use the & as a comparator.
You are passing a dereferenced pointer (a value) to a function expecting a pointer:
int *VAL = ...;
...
g_signal_connect (app, "activate", G_CALLBACK (activate), (gpointer *)*VAL);
switch to
g_signal_connect (app, "activate", G_CALLBACK (activate), VAL); // Do not use a wrong cast (void **)
also, gpointer is an alias of void *, using gpointer *data you get a void **data, not what you want, so
static void activate (GtkApplication *app, gpointer *localval) {
should be
static void activate (GtkApplication *app, gpointer localval) { // without *
finally, to print the value of the pointer use
g_snprintf(msg, sizeof msg, "val: %d\n", *(int *)localval);

GTK - How to pass an argument to a GtkEventBox

This code make a program that create a window with label, if you click the label, the program executes an fprintf of buffer passed as an argument of g_signal_connect(G_OBJECT(eventbox), "button_press_event", G_CALLBACK(on_event_clicked), buffer). Previously the program put in buffer the string "Hello Word" and then the program should print this message, but maybe the program print only garbled char. Where I'm wrong?
#include <gtk/gtk.h>
#include <stdlib.h>
#include <string.h>
void on_event_clicked (GtkWidget* widget, gpointer user_data);
int main (int argc, char **argv) {
GtkWidget *window;
GtkWidget *eventbox;
GtkWidget *label;
char* buffer = malloc(sizeof(char)*10);
strcpy(buffer, "Hello Word\0");
gtk_init (&argc,&argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
label = gtk_label_new ("Hello Word");
eventbox = gtk_event_box_new ();
gtk_container_add (GTK_CONTAINER(eventbox), label);
gtk_container_add (GTK_CONTAINER(window), eventbox);
gtk_widget_show_all (window);
g_signal_connect(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(G_OBJECT(eventbox), "button_press_event",
G_CALLBACK(on_event_clicked), buffer);
gtk_main();
return 0;
}
void on_event_clicked (GtkWidget *widget, gpointer user_data) {
char* pn = user_data;
fprintf(stderr, "%s\n", pn);
}
Your prototype for on_event_clicked() is wrong, it doesn't match what GTK+ expects.
It should be:
gboolean user_function (GtkWidget *widget, GdkEvent *event, gpointer user_data);
You need to add the missing argument to your function, and also deal with the requirement for a return value. Remember to read the signal documentation seriously.

Drawing lines with GTK+ and Cairo without removing what is already drawn

Currently I am writing a program in C, on a linux system (Raspberry Pi to be exact) which should draw to a GTK window using Cairo. I've been following the tutorial at: http://zetcode.com/gfx/cairo/ . But it is way to vague with it's explanations at certain points.
It does not explain two points that I really need:
I can't figure out a way to draw to the window with a proper function call.
It removes what is already drawn.
I need a piece of code that does some simple things, in a very Object-Oriented manner:
Draw lines to a GTK window with a function call, given X and Y for both starting and end point;
Do not remove what is previously drawn;
All initializations of variables and the window should be outside the main function.
So basically something similar to this:
#include <cairo.h>
#include <gtk/gtk.h>
void drawLine(int xStart, int yStart, int yEnd, int xEnd) {
//Drawing code here.
}
void initializeCairo() {
//Insert cairo initialization.
}
void initializeGTK() {
//Insert GTK initialization.
}
/*If needed a general initializer for both cairo and GTK*/
void initialize() {
//Insert general initialization.
}
int main (int argc, char *archv[]) {
intializeGTK();
initializeCairo();
if(doSomething) {
drawLine(10, 10, 20, 20);
}
}
If it could be explained what a method does (in proper English please, not a reference to the documentation), that'd be absolutely great.
Also please include the gcc build command used.
Thanks in advance!
The answers from andlabs are fine. Here is in addition a short (although not entirely elegant) example. It will "kind of remember" the last NUM lines - creation/resize/activation/deactivation of the window will trigger a "draw" of the content. A Next button click will add a new line to the output. Check also the command-line output for an update of
the array values that are drawn.
#include <gtk/gtk.h>
#include <glib/gprintf.h>
#include <cairo.h>
#include <math.h>
#include <stdio.h>
#include <string.h>
#define NUM 3
typedef struct {
GtkApplication *app;
GtkWidget *window;
GtkWidget *button;
GtkWidget *da;
cairo_t* cr;
gboolean redraw;
gint xsize;
gint ysize;
} appWidgets;
gboolean drawEvent (GSimpleAction *action, GVariant *parameter, gpointer data);
void nextCallback (GtkWidget *widget, gpointer data);
void nextCallback (GtkWidget *widget, gpointer data)
{
appWidgets *w = (appWidgets*) data;
static gint cnt = 0;
static gdouble x[NUM], y[NUM], u[NUM], v[NUM];
// determine the next coordinates for a line
if (w->redraw == FALSE) {
x[cnt] = g_random_double();
y[cnt] = g_random_double();
u[cnt] = g_random_double();
v[cnt] = g_random_double();
}
w->cr = gdk_cairo_create (gtk_widget_get_window (w->da));
// map (0,0)...(xsize,ysize) to (0,0)...(1,1)
cairo_translate (w->cr, 0, 0);
cairo_scale (w->cr, w->xsize, w->ysize);
// set linewidth
cairo_set_line_width (w->cr, 0.005);
// draw the lines
for (int k = 0; k < NUM; k++) {
cairo_move_to (w->cr, x[k], y[k]);
cairo_line_to (w->cr, u[k], v[k]);
cairo_stroke (w->cr);
g_print("k=%d:(%1.2lf,%1.2lf).(%1.2lf,%1.2lf) ",
k, x[k], y[k], u[k], v[k]);
}
g_print("\n");
cairo_destroy (w->cr);
if (w->redraw == FALSE) {
cnt++;
if (cnt == NUM)
cnt = 0;
}
}
gboolean drawEvent (GSimpleAction *action, GVariant *parameter, gpointer data)
{
appWidgets *w = (appWidgets*) data;
w->xsize = gtk_widget_get_allocated_width (w->da);
w->ysize = gtk_widget_get_allocated_height (w->da);
w->redraw = TRUE;
nextCallback (NULL, w);
w->redraw = FALSE;
return TRUE;
}
void activate (GtkApplication *app, gpointer data)
{
GtkWidget *box;
appWidgets *w = (appWidgets*) data;
w->window = gtk_application_window_new (w->app);
gtk_window_set_application (GTK_WINDOW (w->window), GTK_APPLICATION (w->app));
box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 0);
gtk_container_add (GTK_CONTAINER (w->window), box);
w->da = gtk_drawing_area_new();
gtk_widget_set_size_request (w->da, 400, 400);
gtk_box_pack_start (GTK_BOX (box), w->da, TRUE, TRUE, 0);
g_signal_connect (w->da, "draw", G_CALLBACK (drawEvent), (gpointer) w);
w->button = gtk_button_new_with_label ("Next");
g_signal_connect (G_OBJECT (w->button), "clicked", G_CALLBACK (nextCallback),
(gpointer) w);
gtk_box_pack_start (GTK_BOX (box), w->button, FALSE, TRUE, 0);
gtk_widget_show_all (GTK_WIDGET (w->window));
w->redraw = FALSE;
}
int main (int argc, char *argv[])
{
gint status;
appWidgets *w = g_malloc (sizeof (appWidgets));
w->app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
g_signal_connect (w->app, "activate", G_CALLBACK (activate), (gpointer) w);
status = g_application_run (G_APPLICATION (w->app), argc, argv);
g_object_unref (w->app);
g_free (w);
w = NULL;
return status;
}
Build the program as usual:
gcc example.c -o example `pkg-config --cflags --libs gtk+-3.0`

How to set variable value from gtk_entry?

I would like to set var pid from entry, but I got the following warning:
gtk.c:7:6: warning: assignment makes integer from pointer without a cast [enabled by default].
How I can make var pid == entry? It is very imporatant because this program is going to send signal to a process which id (pid) we are going to enter.
#include <gtk/gtk.h>
#include <stdlib.h>
pid_t pid;
void sendSighup(GtkWidget *widget,GtkWidget *entry, gpointer label) {
pid = gtk_entry_get_text(GTK_ENTRY(entry));
kill(pid,SIGHUP);
}
void sendSigint(GtkWidget *widget, gpointer label) {
kill(pid,SIGINT);
}
void sendSigkill(GtkWidget *widget, gpointer label) {
kill(pid,SIGKILL);
}
void sendSigterm(GtkWidget *widget, gpointer label) {
kill(pid,SIGTERM);
}
void sendSigstop(GtkWidget *widget, gpointer label) {
kill(pid,SIGSTOP);
}
accept_clicked (GtkButton *button, GObject *context_object)
{
GtkLabel *accept_lable1 = g_object_get_data (context_object, "label1");
GtkEntry *accept_entry = g_object_get_data (context_object, "entry");
const char *entry_in = gtk_entry_get_text (accept_entry);
gtk_label_set_text (accept_lable1, entry_in);
}
int main( int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *frame;
GtkWidget *table;
GtkWidget *button1;
GtkWidget *button2;
GtkWidget *button3;
GtkWidget *button4;
GtkWidget *button5;
GtkWidget *quit;
GtkWidget *set;
GtkWidget *vseparator;
GtkWidget *entry;
GtkWidget *label;
GtkWidget *label1;
GtkWidget *label2;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "Send Signal");
gtk_window_set_default_size(GTK_WINDOW(window), 300, 250);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
frame = gtk_fixed_new();
gtk_container_add(GTK_CONTAINER(window), frame);
button1 = gtk_button_new_with_label("Sighup");
gtk_fixed_put(GTK_FIXED(frame), button1, 8, 15);
gtk_widget_set_size_request(button1, 80, 35);
button2 = gtk_button_new_with_label("Sigint");
gtk_fixed_put(GTK_FIXED(frame), button2, 8, 60);
gtk_widget_set_size_request(button2, 80, 35);
button3 = gtk_button_new_with_label("Sigkill");
gtk_fixed_put(GTK_FIXED(frame), button3, 8, 105);
gtk_widget_set_size_request(button3, 80, 35);
button4 = gtk_button_new_with_label("Sigterm");
gtk_fixed_put(GTK_FIXED(frame), button4, 8, 150);
gtk_widget_set_size_request(button4, 80, 35);
button5 = gtk_button_new_with_label("Sigstop");
gtk_fixed_put(GTK_FIXED(frame), button5, 8, 195);
gtk_widget_set_size_request(button5, 80, 35);
set = gtk_button_new_with_label ("Set");
gtk_fixed_put(GTK_FIXED(frame), set, 200, 80);
gtk_widget_set_size_request(set, 80, 35);
quit = gtk_button_new_with_label("Quit");
gtk_fixed_put(GTK_FIXED(frame), quit, 200, 195);
gtk_widget_set_size_request(quit, 80, 35);
vseparator = gtk_vseparator_new();
gtk_widget_set_size_request(vseparator, 10, 240);
gtk_fixed_put(GTK_FIXED(frame), vseparator, 95, 5);
label = gtk_label_new("Enter PID:");
gtk_fixed_put(GTK_FIXED(frame), label, 165, 20);
label2 = gtk_label_new("PID:");
gtk_fixed_put(GTK_FIXED(frame), label2, 110, 89);
label1 = gtk_label_new("0000");
gtk_fixed_put(GTK_FIXED(frame), label1, 140, 89);
entry = gtk_entry_new();
gtk_fixed_put(GTK_FIXED(frame), entry, 120, 40);
gtk_widget_grab_focus(entry);
g_object_set_data(G_OBJECT(set), "label1", label1);
g_object_set_data(G_OBJECT(set), "entry", entry);
g_signal_connect(GTK_BUTTON (set), "clicked",
G_CALLBACK (accept_clicked), set);
g_signal_connect_swapped(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
g_signal_connect(button1, "clicked",
G_CALLBACK(sendSighup), NULL);
g_signal_connect(button2, "clicked",
G_CALLBACK(sendSigint), NULL);
g_signal_connect(button3, "clicked",
G_CALLBACK(sendSigkill), NULL);
g_signal_connect(button4, "clicked",
G_CALLBACK(sendSigterm), NULL);
g_signal_connect(button5, "clicked",
G_CALLBACK(sendSigstop), NULL);
g_signal_connect(G_OBJECT(quit), "clicked",
G_CALLBACK(gtk_main_quit), G_OBJECT(window));
gtk_widget_show_all(window);
gtk_widget_show(entry);
gtk_widget_show(label);
gtk_main();
return 0;
}
In that line you can either use the plain old atoi:
pid = atoi(gtk_entry_get_text(GTK_ENTRY(entry)));
or use a more complex function such as g-ascii-strtod.
gtk_entry_get_text returns char* - a pointer to char. You are assigning a pointer to char to int in your code. This is implementation-defined according to C standard and is not probably what you want, this is why your compiler gives you a warning:
6.3.2.3 p.6:
Any pointer type may be converted to an integer type. Except as
previously specified, the result is implementation-defined. If the
result cannot be represented in the integer type, the behavior is
undefined. The result need not be in the range of values of any
integer type.
For example, take a look at this code which is an exact representation of problem you have:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
char *p ="123456";
int i = p;
printf("%d\n", i);
printf("%p\n", p);
exit 0;
}
It prints:
134514096
0x80485b0
Pointer to char is assigned to int in line 7. This yields the same error as in your code:
main.c:7:17: warning: initialization makes integer from pointer
without a cast [enabled by default]
Now let's see what's the result of this assignment - do not forget that this an implementation-defined behavior. First printf prints value of i variable after assignment. Second printf prints an address that a pointer holds using %p specifier which prints a pointer value in an implementation-defined manner. These values turned out to be the same - 0x80485b0 is hexadecimal 134514096. What happened here is pointer value (that is an address) has been assigned to int. The same is happening in your code. C is a very simple language, it doesn't do anything behind your back. It will not automatically convert a series of digits written in a string to an int. To convert string to a number you need to use standard atoi function or a custom function provided by libraries you are using.

error : expected declaration specifiers or ‘...’ before string constant while declaring colorbutton in gtk+

I want to put some color with gtkButton in GTK+ program, but it shows some declaration error as i've specified above. This is the declaration, i've used in the gtk program.
static GdkColor colorRed;//error line 1
gdk_color_parse("red", &colorRed); //error line 2
button1 = gtk_button_new_with_label("button");
gtk_widget_modify_base (button1, GTK_STATE_NORMAL, &colorRed);
but it's displaying error
error: expected declaration specifiers or ‘...’ before string constant //line 1
error: expected declaration specifiers or ‘...’ before ‘&’ token //line 2
gtk_widget_modify_base has been deprecated since 3.0. Use gtk_widget_override_background_color instead. You won't need GdkColor, just GdkRGBA, which is more convenient for cairo anyway.
About your error: I think you're focusing on the wrong part. You snippet is here the compiler sees the error, but lines before are always welcome, and I think your problem is that you're not including the headers for GdkColor.
The declaration to color the button was correct but i had declared it as global i.e outside the main() function to use the color in multiple function. So, when i put the declaration in side the main() function and replace gtk_widget_modify_base() to gtk_widget_modify_bg() then it's working perfectly.
Here is corrected code
#include <gtk/gtk.h>
static void destroy (GtkWidget *window, gpointer data);
GtkWidget *window;
GtkWidget *table;
GtkWidget *button;
GtkWidget *button1[20][20];
GtkWidget *button2;
GtkWidget *button3;
GtkWidget *title;
GtkWidget *label;
static char *values[100] =
{ "127.0.0.1", "Idle",
"192.168.73.129", "Idle",
"192.168.73.130", "Idle",
"192.168.73.131", "Idle",
"192.168.73.132", "Idle",
"192.168.73.133", "Idle",
"192.168.73.134", "Idle",
};
int main(int argc, char *argv[])
{ gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL); //gtk_scrolled_window_new(NULL, NULL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_window_set_default_size(GTK_WINDOW(window), 550, 700);
gtk_window_set_title(GTK_WINDOW(window), "Client Activity Monitor");
gtk_container_set_border_width(GTK_CONTAINER(window), 25);
g_signal_connect (G_OBJECT (window), "destroy",G_CALLBACK (destroy), NULL);
table = gtk_table_new(4, 2, TRUE);
gtk_table_set_row_spacings(GTK_TABLE(table), 2);
gtk_table_set_col_spacings(GTK_TABLE(table), 2);
GdkColor colorRed;//color declaration
gdk_color_parse("red", &colorRed);//color assignment
int i = 0;
int j = 0;
int pos = 0;
title = gtk_frame_new("Client Logs");
label = gtk_label_new("server: waiting for connections...\n");
gtk_frame_set_shadow_type(GTK_FRAME(title), GTK_SHADOW_IN);
gtk_table_attach_defaults(GTK_TABLE(table), title, 0, 1, 0, 1);
button3 = gtk_button_new_with_label("Start Server");
gtk_widget_modify_fg(button3, GTK_STATE_NORMAL, &colorRed);//color use
gtk_table_attach_defaults(GTK_TABLE(table), button3, 1, 2, 0, 1);
gtk_container_add (GTK_CONTAINER (title), label);
gtk_widget_show_all(title);
for(i=0; i < 6; i++) {
for( j=0; j < 2; j++) {
button1[i][j] = gtk_button_new_with_label(values[pos]);
gtk_widget_modify_fg(button1[i][j], GTK_STATE_NORMAL, &colorRed);
gtk_table_attach_defaults(GTK_TABLE(table), button1[i][j], j, j+1, i+1, i+1+1 );
pos++;
}
}
gtk_container_add(GTK_CONTAINER(window), table);
g_signal_connect_swapped (G_OBJECT (button3), "clicked",G_CALLBACK (destroy),(gpointer) window);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
static void destroy (GtkWidget *window, gpointer data)
{
gtk_main_quit ();
}

Resources