Changing background color of GtkEntry - c

I have written a C language program with many GtkEntry's for data input. I have created the UI using Glade and the GtkEntrys emit an on_entry#_changed() signal when modified. My program checks the validity of the input, which has certain requirements. e.g., must be valid hexadecimal.
I would like the background of a GtkEntry to go red while it is invalid, and turn back to the original color when acceptable. The original color is dependent upon the desktop style set by the user. For example, on Ubuntu, I'm using a "Dark" style, so the box is dark gray.
What is the best way to implement this background color switching so that it applies to an individual GtkEntry and renders the user's chosen style color when the data is ok? I see a lot of discussion out there but it is often using deprecated functions.

You can use the error style class to mark the entry as having error. The following is a minimal example that checks if an entry has a valid hex digit and updates the entry style:
/* main.c
*
* Compile: cc -ggdb main.c -o main $(pkg-config --cflags --libs gtk+-3.0) -o main
* Run: ./main
*
* Author: Mohammed Sadiq <www.sadiqpk.org>
*
* SPDX-License-Identifier: LGPL-2.1-or-later OR CC0-1.0
*/
#include <gtk/gtk.h>
static void
entry_changed_cb (GtkEntry *entry)
{
GtkStyleContext *style;
const char *text;
gboolean empty;
g_assert (GTK_IS_ENTRY (entry));
style = gtk_widget_get_style_context (GTK_WIDGET (entry));
text = gtk_entry_get_text (entry);
empty = !*text;
/* Loop until we reach an invalid hex digit or the end of the string */
while (g_ascii_isxdigit (*text))
text++;
if (empty || *text)
gtk_style_context_add_class (style, "error");
else
gtk_style_context_remove_class (style, "error");
}
static void
app_activated_cb (GtkApplication *app)
{
GtkWindow *window;
GtkWidget *entry;
window = GTK_WINDOW (gtk_application_window_new (app));
entry = gtk_entry_new ();
gtk_widget_set_halign (entry, GTK_ALIGN_CENTER);
gtk_widget_set_valign (entry, GTK_ALIGN_CENTER);
gtk_widget_show (entry);
gtk_container_add (GTK_CONTAINER (window), entry);
g_signal_connect_object (entry, "changed",
G_CALLBACK (entry_changed_cb),
app, G_CONNECT_AFTER);
entry_changed_cb (GTK_ENTRY (entry));
gtk_window_present (window);
}
int
main (int argc,
char *argv[])
{
g_autoptr(GtkApplication) app = gtk_application_new (NULL, 0);
g_signal_connect (app, "activate", G_CALLBACK (app_activated_cb), NULL);
return g_application_run (G_APPLICATION (app), argc, argv);
}

Related

C, gtk: ‘window’ undeclared (first use in this function)

I'm very new in C, i've started learning this language about 1 week ago, coming from Python. So,I have difficult understanding the C syntax, and the solution online. I'm trying building a simple graphic interface. In this example project created for this post, if the user types "create_window", the program creates a window, and if after it the user types "create_button" the program should create a button.. but it doesn't work. Below there is the error, but first the code:
#include <stdio.h>
#include <string.h>
#include <gtk/gtk.h>
void main(int argc, char* argv[]){
char row[50];
for(int i=0;i<=1;i++){
scanf("%s",row);
if(strstr(row,"create_window")!=NULL){
GtkWidget* window;
GtkWidget* button;
gtk_init(&argc,&argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "New window");
g_signal_connect(window,"destroy",G_CALLBACK(gtk_main_quit),NULL);
gtk_window_set_default_size(GTK_WINDOW(window),200,200);
gtk_widget_show_all(window);
gtk_main();
}
if(strstr(row,"create_button")!=NULL){
button = gtk_button_new_with_label("Button text");
gtk_container_add(GTK_CONTAINER(window), button);
gtk_widget_show_all(window);
gtk_main();
}
}
}
this code create successfully the window, but not the button. After creating the window, i type "create_button" and i receive this error:
error: ‘window’ undeclared (first use in this function)
80 | gtk_container_add(GTK_CONTAINER(window), button);
the complete error refers to my """complete""" project, if you need it write in a comment.
Your window variable is scoped exclusively to that first if.
Variables only exist within the scope in which they've been declared.
#include <stdio.h>
#include <string.h>
#include <gtk/gtk.h>
void main(int argc, char* argv[]) {
char row[50];
for(int i = 0; i <= 1; i++) {
scanf("%s", row);
GtkWidget* window;
GtkWidget* button;
if (strstr(row, "create_window") != NULL) {
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "New window");
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_window_set_default_size(GTK_WINDOW(window), 200, 200);
gtk_widget_show_all(window);
gtk_main();
}
if (strstr(row, "create_button") != NULL) {
button = gtk_button_new_with_label("Button text");
gtk_container_add(GTK_CONTAINER(window), button);
gtk_widget_show_all(window);
gtk_main();
}
}
}
Also, I suggest using a width specifier with scanf to avoid overflows.
scanf("%49s", row);
Your program doesn't make sense. You cannot create a button till you have a window to place it on (even if you fix the syntax error of making the variable available in the 2nd if statement). Also, gtk_main() runs an event loop and doesn't return. Perhaps you want to optionally create a button?
#include <gtk/gtk.h>
#include <stdio.h>
#include <string.h>
void main(int argc, char* argv[]){
gtk_init(&argc, &argv);
GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "New window");
g_signal_connect(window,"destroy", G_CALLBACK(gtk_main_quit),NULL);
gtk_window_set_default_size(GTK_WINDOW(window),200,200);
char row[50];
fgets(row, 50, stdin);
if(!strcmp(row, "create_button\n")) {
GtkWidget *button = gtk_button_new_with_label("Button text");
gtk_container_add(GTK_CONTAINER(window), button);
}
gtk_widget_show_all(window);
gtk_main();
}
I build the program and executed it like this:
gcc 1.c $(pkg-config --cflags atk) $(pkg-config --cflags gdk-3.0) $(pkg-config gtk-3.0) $(pkg-config --libs gtk+-3.0) $(pkg-config --libs gdk-3.0) && ./a.out
If you enter create_button\n it will create a window with a button, and if you enter any other line it will just create the window.
Otherwise, you need to tell us what should happen for "create_button"? Do we automatically create a window and then the button? Do we just remember that we need to create the button when user asks to "create_window"? If you want to show the window, then regain control, you need to use gtk_main_iteration_do(FALSE) instead of gtk_main(), and then you can add the button.

Create and save a file in GTK2 using C

I'm following the instructions of the official Raspberry Pi book called An introduction to C & GUI Programming (link).
It uses GTK2 to create a gui in C.
I encountered some problems trying the code that should save a file. Here the code of the book (same I used):
#include <gtk/gtk.h>
static void save_file (GtkWidget *btn, gpointer ptr)
{
GtkWidget *sch = gtk_file_chooser_dialog_new ("Save file", GTK_WINDOW (ptr), GTK_FILE_CHOOSER_ACTION_SAVE, "Cancel", 0, "OK", 1, NULL);
if (gtk_dialog_run (GTK_DIALOG (sch)) == 1)
{
printf ("%s selected\n", gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (sch)));
}
gtk_widget_destroy (sch);
}
void end_program (GtkWidget *wid, gpointer ptr)
{
gtk_main_quit ();
}
int main (int argc, char * argv[])
{
gtk_init (&argc, &argv);
GtkWidget *win = gtk_window_new (GTK_WINDOW_TOPLEVEL);
GtkWidget *btn = gtk_button_new_with_label ("Close window");
g_signal_connect (btn, "clicked", G_CALLBACK (end_program), NULL);
g_signal_connect (win, "delete_event", G_CALLBACK (end_program), NULL);
GtkWidget *vbox = gtk_vbox_new (FALSE, 5);
gtk_container_add (GTK_CONTAINER (win), box);
GtkWidget *fc_btn = gtk_button_new_with_label ("Save file");
g_signal_connect (fc_btn, "clicked", G_CALLBACK (save_file), win);
gtk_box_pack_start (GTK_BOX (vbox), fc_btn, TRUE, TRUE, 0);
gtk_box_pack_start (GTK_BOX (vbox), btn, TRUE, TRUE, 0);
gtk_widget_show_all (win);
gtk_main ();
return 0;
}
The books says that this program should open a window with a button that, if clicked, opens a new window where I can insert the name of the file and with OK I should be able to save it.
The resulting file path is printed correctly inside the terminal.
If I enter inside the path where I saved the file, the file doesn't exist! It's not hidded neither saved with a different name.
Is there something missing in this piece of code?
I left you a comment above. As noted, I reviewed the sample code in the PDF version of the book in your link. The purpose of that code is to present a file chooser dialog widget and confirm the name of the file you either select or type in by printing the full path in the console. The program, as is, has no mechanism for actually writing a file out to your storage medium. So that is why you do not see a file when you view the folder on your system. If you want as a minimum, a file actually written, a little more code needs to be added. Just to give you one really simple example of what that might look like, I made a revision to the "save_file" function in your sample program as noted in the following code snippet.
static void save_file (GtkWidget *btn, gpointer ptr)
{
GtkWidget *sch = gtk_file_chooser_dialog_new ("Save file", GTK_WINDOW (ptr), GTK_FILE_CHOOSER_ACTION_SAVE, "Cancel", 0, "OK", 1, NULL);
if (gtk_dialog_run (GTK_DIALOG (sch)) == 1)
{
printf ("%s selected\n", gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (sch)));
char cmd[1024];
strcpy(cmd, "touch ");
strcat(cmd, gtk_file_chooser_get_filename(GTK_FILE_CHOOSER (sch)));
system(cmd);
}
gtk_widget_destroy (sch);
}
FYI, I built this revised program on a Linux system using the "touch" command which will either update an existing file's timestamp or create a new empty file and the "system" command which allows one to execute a program as though one was using a terminal. You mentioned a Raspberry Pi system, which I believe uses Linux or a Linux like operating system, so these commands should work.
For further examples and tutorials, you might want to check out some videos out on the web. The following link is not a specific recommendation, but it was one of the first videos I found out on the web that walks through the various steps of C coding with GTK including references to the GTK file chooser dialog.
"https://www.youtube.com/watch?v=EdJVkr87LSk&list=PLMkSWKN9VsZH562FmV8sMvMu_sVZsYAt6"
You might want to review those videos to see if they might help you.
I hope that clarifies things for you.
Regards.

GTK get multiple range value segmentation error

So, I'm trying to create an rgb selector with gtk in C.
I follow the documentation to learn how to put in place a glade project and use it with gtk, so I made an UI in glade, but I have some trouble with the GTK part. I need to have access to multiple slider in the change-value signal because I want to get their value to print them, so I have created a struct to stock them, and I send this struct in the signal. But my problem is when I try to get the value of the range which his stock in the struct I have a segmentation error and I don't know why. I tried without the struct and I can correctly get the slider value, but it didn't work with the struct. How can I fix it?
Ask me if you need the glade file too.
//Compil : gcc -rdynamic -o ColorPicker colorPicker.c `pkg-config --cflags --libs gtk+-3.0`
#include <gtk/gtk.h>
#include <glib/gstdio.h>
#include <gdk/gdk.h>
typedef struct
{
GObject *redSlider;
GObject *blueSlider;
GObject *greenSlider;
GObject *label;
}Data;
static void changeLabel (GtkWidget *range, Data *data)
{
gdouble red = gtk_range_get_value(GTK_RANGE(data->redSlider));
g_print("%.0lf\n",red);
//g_print("R : %d, G : %d, B : %d",red,green,blue);
}
static void activate(GtkApplication *app, gpointer user_data)
{
Data *data;
data = malloc(sizeof(*data));
GtkBuilder *builder = gtk_builder_new();
gtk_builder_add_from_file(builder, "colorPicker.glade",NULL);
GObject *window = gtk_builder_get_object (builder, "window");
gtk_window_set_application (GTK_WINDOW (window), app);
data->redSlider = gtk_builder_get_object (builder, "redSlider");
g_signal_connect (data->redSlider, "change-value", G_CALLBACK (changeLabel), (gpointer) data);
data->blueSlider = gtk_builder_get_object (builder, "blueSlider");
g_signal_connect (data->blueSlider, "change-value", G_CALLBACK (changeLabel), (gpointer) data);
data->greenSlider = gtk_builder_get_object (builder, "greenSlider");
g_signal_connect (data->greenSlider, "change-value", G_CALLBACK (changeLabel), (gpointer) data);
data->label = gtk_builder_get_object (builder, "cpChooseLabel");
/*GObject *colorBtn = gtk_builder_get_object (builder, "colorBtn");
g_signal_connect (colorBtn, "clicked", G_CALLBACK (getColor), (gpointer) data);*/
gtk_widget_show (GTK_WIDGET (window));
/* We do not need the builder any more */
g_object_unref (builder);
free(data);
}
int main(int argc, char **argv)
{
#ifdef GTK_SRCDIR
g_chdir(GTK_SRCDIR);
#endif
GtkApplication *app = gtk_application_new("com.github.XXXXXX.rgbSelector", G_APPLICATION_FLAGS_NONE);
g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
int status = g_application_run(G_APPLICATION(app), argc, argv);
g_object_unref(app);
return status;
}
You end the structure's lifetime by freeing it at the end of activate. When the callback changeLabel is invoked at some time later, it tries to access the no longer existing structure. You could just drop the free(data); if you don't care that the space occupied by the four pointers can't be freed then; another possibility is to simply use a static structure and pass its address.

Cannot reduce size of gtk window programatically

I seem to be facing a problem when resizing a gtk window programatically. The problem is that once I have increased the width and height of the window to 800x600, I cannot seem to reduce it back to its original size of 400x200.
Below is the sample code. Has anyone faced such a problem?
#include <gtk/gtk.h>
static gboolean is_clicked = FALSE;
static void Child_window_resize( GtkWidget *widget,
GtkWidget *window)
{
if(!is_clicked)
{
g_print("Inside If block increase bool value %d\n",is_clicked);
gtk_widget_set_size_request(window,800,600);
is_clicked = TRUE;
}
else
{
g_print("Inside Else block decrease bool value %d\n",is_clicked);
gtk_widget_set_size_request(window,400,200);
is_clicked = FALSE;
}
}
int main(int argc, char* argv[])
{
GtkWidget *window;
GtkWidget *fixed;
GtkWidget *resizebutton;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_widget_set_size_request(window,400,200);
gtk_window_set_resizable(GTK_WINDOW(window),TRUE);
gtk_window_set_title(GTK_WINDOW(window), "Demo Resize");
gtk_window_set_decorated(GTK_WINDOW(window),FALSE);
gtk_signal_connect(GTK_OBJECT(window), "destroy",
GTK_SIGNAL_FUNC(gtk_exit), NULL);
gtk_container_set_border_width(GTK_CONTAINER(window), 10);
// creating a fixed GTK_CONTAINER
fixed = gtk_fixed_new();
gtk_container_add(GTK_CONTAINER(window),fixed);
gtk_widget_show(fixed);
resizebutton = gtk_button_new_with_label("Resize");
gtk_widget_set_size_request(resizebutton, 80, 60);
gtk_fixed_put(GTK_FIXED(fixed), resizebutton, 0, 0);
gtk_signal_connect(GTK_OBJECT(resizebutton), "clicked",
GTK_SIGNAL_FUNC(Child_window_resize), window);
gtk_widget_show(resizebutton);
gtk_widget_show(window);
gtk_main();
return 0;
}
Complied using ...
gcc -Wall -Werror -g resize.c -o resize -export-dynamic `pkg-config --cflags --libs gtk+-2.0 gthread-2.0`
Any help is much appreciated.
Instead of gtk_widget_set_size_request(), you need gtk_window_resize().
From the linked manual:
void
gtk_window_resize (GtkWindow *window,
gint width,
gint height);
Resizes the window as if the user had done so, obeying geometry
constraints. The default geometry constraint is that windows may not
be smaller than their size request; to override this constraint, call
gtk_widget_set_size_request() to set the window's request to a smaller
value.
If gtk_window_resize() is called before showing a window for the first
time, it overrides any default size set with
gtk_window_set_default_size().
Windows may not be resized smaller than 1 by 1 pixels.

clutter stage in gtk can't not handle stage mouse press event

I have tried to use the clutter-gtk. I wrote a small piece of code that create a gtk window with a clutter stage in it. I try to get mouse button press event on the stage but there is nothing.
Here is the code:
#include <clutter/clutter.h>
#include <clutter-gtk/clutter-gtk.h>
#include <glib.h>
#include <stdlib.h>
#include <stdio.h>
/*gcc 3_clutter_app_clickable_text_in_gtk.c -o 3_clutter_app_clickable_text_in_gtk `pkg-config clutter-1.0 clutter-gtk-1.0 glib --cflags --libs`*/
/*mouse clic handler*/
void on_stage_button_press( ClutterStage *stage, ClutterEvent *event, gpointer data)
{
printf("ok\n");
}
int main(int argc, char *argv[])
{
if (gtk_clutter_init(&argc, &argv) != CLUTTER_INIT_SUCCESS)
return EXIT_FAILURE;
/*create the window*/
GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_default_size (GTK_WINDOW (window), 640, 480);
/*destroy from window close all*/
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
/*vertical box, 0 spacing*/
GtkWidget * box = gtk_box_new(GTK_ORIENTATION_VERTICAL,0);
/*add box in the window*/
gtk_container_add(GTK_CONTAINER(window), box);
/*create the cutter widget*/
GtkWidget *clutter_widget = gtk_clutter_embed_new ();
gtk_widget_set_size_request (clutter_widget, 200, 200);
ClutterActor *stage = gtk_clutter_embed_get_stage (GTK_CLUTTER_EMBED(clutter_widget));
clutter_stage_set_use_alpha(CLUTTER_STAGE(stage), TRUE);
ClutterColor stage_color ;
GString * bg_color = g_string_new("#555753ff");
clutter_color_from_string(&stage_color,bg_color->str);
clutter_actor_set_background_color(stage, &stage_color);
/*add the cutter widget in the box expand and fill are TRUE and spacing is 0*/
gtk_box_pack_start (GTK_BOX (box), clutter_widget, TRUE, TRUE, 0);
/*create line text*/
ClutterColor text_color;
GString * fg_color = g_string_new("#00000055");
clutter_color_from_string(&text_color, fg_color->str);
ClutterActor *some_text = clutter_text_new_full("Sans 24", "Hello, world", &text_color);
clutter_actor_set_size(some_text, 256, 128);
clutter_actor_set_position(some_text, 128, 128);
clutter_actor_add_child(CLUTTER_ACTOR(stage), some_text);
clutter_text_set_editable(CLUTTER_TEXT(some_text), TRUE);
/*define clic event*/
g_signal_connect(stage, "button-press-event", G_CALLBACK(on_stage_button_press), NULL);
/* Show the window and all its widgets: */
gtk_widget_show_all (GTK_WIDGET (window));
/* Start the main loop, so we can respond to events: */
gtk_main ();
return EXIT_SUCCESS;
}
Nothing happens when I click on the stage. I have made some tests and I can handle click event on the main windows, on the clutter_widget but not directly on the clutter stage.
This code is modified one from http://www.openismus.com/documents/clutter_tutorial/0.9/docs/tutorial/html/sec-stage-widget.html. In this one the author connect the signal directly on the stage but this example is for clutter 0.9 and don't compile anymore with clutter v > 1.0.
Any ideas on what I am doing wrong?
Edit
I have made some test with "key-press-event" which are handled. So the problem seems to come from the events mask of the stage.
Does anyone know how to change the event mask of the stage in order to force the stage to react on mouse events?
I close this question because :
clutter-gtk is just a proof of concept
clutter-gtk will not be used in the future
see https://mail.gnome.org/archives/clutter-list/2013-February/msg00059.html
so I don't want to loose more time with this.
For information,the same example using juste clutter works as expected.
CLUTTER_BACKEND=gdk ./yourexecutable

Resources