Currently trying to create a custom widget which is based directly on GtkWidget, marks it as drawable and draws content with cairo. So much for the context.
As soon as I try to handle events (so I can implement zoom) - especially the scroll event - just does not work and I am not sure as of why.
The callback does get executed on focus-in/focus out (good old print statement proves that), but I do never ever get any scroll wheel activity detected by that callback (button clicks are key press/release do not work either).
I tried to hook up to the event signal by
using the function pointer and assigning callback function (which I think is the right thing to do)
using g_signal_connect (mywidget, "event",..) from foo_new or foo_init
Neither did work.
The Foo init:
foo_init (Foo *self)
{
GtkWidget *widget = GTK_WIDGET (self);
gtk_widget_set_has_window (widget, FALSE);
self->priv = FOO_GET_PRIVATE (self);
gtk_widget_add_events (widget, GDK_ALL_EVENTS_MASK);
g_assert ((gtk_widget_get_events (widget) & GDK_SCROLL_MASK) != 0); //just fine
/* added some stuff I also tried but did not work */
gtk_widget_set_sensitive (widget, TRUE);
gtk_widget_set_can_focus (widget, TRUE);
gtk_widget_grab_focus (widget);
...
How can I get all the events of my widget?
Assigning widget_class->key_press_event = my_handler_callback; actually works as expected and I get the keys I press, but the very same widget_class->button_press_event = my_handler_callback; or widget_class->scroll_event = my_handler_callback; assignments do not work!
widget_class->key_press_event = my_handler_callback; // works
widget_class->key_release_event = my_handler_callback; // works
widget_class->button_press_event = my_handler_callback; // NOT
widget_class->button_release_event = my_handler_callback; // NOT
widget_class->scroll_event = my_handler_callback; // NOT
This made me suspicious.
To receive this signal, the GdkWindow associated to the widget needs
to enable the GDK_BUTTON_PRESS_MASK mask.
Is it necessary to realize the widget before gtk_widget_add_events "works"...?
Update: Tried to call gtk_widget_add_events after gtk_widget_show_all. No change.
Update: fully compileable example
#ifndef __FOO_H__
#define __FOO_H__
#include <gtk/gtk.h>
G_BEGIN_DECLS
#define FOO_TYPE (foo_get_type ())
#define FOO(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE, Foo))
#define FOO_CONST(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), FOO_TYPE, Foo const))
#define FOO_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), FOO_TYPE, FooClass))
#define FOO_IS(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), FOO_TYPE))
#define FOO_IS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), FOO_TYPE))
#define FOO_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), FOO_TYPE, FooClass))
typedef struct _Foo Foo;
typedef struct _FooClass FooClass;
typedef struct _FooPrivate FooPrivate;
struct _Foo
{
GtkWidget parent;
FooPrivate *priv;
};
struct _FooClass
{
GtkWidgetClass parent_class;
};
GType foo_get_type (void) G_GNUC_CONST;
Foo *foo_new (void);
G_END_DECLS
#endif /* __FOO_H__ */
#include "foo.h"
gboolean
scroll_hook (GtkWidget *widget, GdkEventScroll *event)
{
g_print ("%p registered a scroll event\n");
return TRUE;
}
#define FOO_GET_PRIVATE(object)(G_TYPE_INSTANCE_GET_PRIVATE((object), FOO_TYPE, FooPrivate))
struct _FooPrivate
{
char to_silence_warning;
};
G_DEFINE_TYPE (Foo, foo, GTK_TYPE_WIDGET)
static void
foo_finalize (GObject *object)
{
G_OBJECT_CLASS (foo_parent_class)->finalize (object);
}
static void
foo_class_init (FooClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->finalize = foo_finalize;
GtkWidgetClass *widget_class = GTK_WIDGET_CLASS(klass);
widget_class->scroll_event = scroll_hook;
g_type_class_add_private (object_class, sizeof (FooPrivate));
}
static void
foo_init (Foo *self)
{
self->priv = FOO_GET_PRIVATE (self);
gtk_widget_set_has_window(GTK_WIDGET(self), FALSE);
}
Foo *
foo_new ()
{
return g_object_new (FOO_TYPE, NULL);
}
#include <gtk/gtk.h>
#include "foo.h"
#include <stdlib.h>
gboolean
chicken_out (GtkWidget *widget, GdkEvent *event, gpointer user_data)
{
g_print ("bye");
gtk_main_quit();
return TRUE;
}
int
main (int argc, char *argv[])
{
GtkWidget *window;
Foo *my;
int i;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
my = foo_new ();
gtk_container_add (GTK_CONTAINER (window), GTK_WIDGET (my));
gtk_widget_show_all (window);
gtk_widget_add_events (GTK_WIDGET (my), GDK_ALL_EVENTS_MASK);
g_assert ((gtk_widget_get_events (GTK_WIDGET (my)) & GDK_SCROLL_MASK) != 0);
g_signal_connect (window, "delete-event", G_CALLBACK(chicken_out), NULL);
gtk_main ();
return EXIT_SUCCESS;
}
Use
gcc `pkg-config --cflags --libs gtk+-3.0` -I. ./foo.c ./foo_test.c -o foo.bin
to compile (granted, all files are in your cwd)
Moving from GtkWidget to GtkDrawingArea was required, as without it would crash. The root cause was that the ->window private variable of widget is not populated without a proper realize default callback - which I did not implement.
Related
I have an application in which, on a button click, I want to display a tooltip, and then hide it after some time delay. The time delay is implemented using https://stackoverflow.com/a/3930477/6286575.
Here's what I have tried.
#include <gtk/gtk.h>
#include <stdio.h>
#include <time.h>
// https://stackoverflow.com/a/3930477/6286575
void wait(int unsigned delay)
{
int unsigned end_time = time(0) + delay;
while(time(0) < end_time);
}
void show_tooltip(GtkWidget *widget, gpointer data)
{
GtkWidget **window = data;
printf("Showing tooltip.\n");
gtk_widget_set_tooltip_text(*window, "Clicked!");
wait(5);
// printf("Hiding tooltip.\n");
// gtk_widget_set_has_tooltip(*window, FALSE);
}
int main(void)
{
gtk_init(0, NULL);
GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
GtkWidget *grid = gtk_grid_new();
gtk_container_add(GTK_CONTAINER(window), grid);
GtkWidget *button = gtk_button_new_with_label("Click Me");
gtk_grid_attach(GTK_GRID(grid), button, 0, 0, 1, 1);
g_signal_connect(GTK_BUTTON(button), "clicked", G_CALLBACK(show_tooltip), &window);
gtk_widget_show_all(window);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_main();
}
Expectation
The tooltip should displayed immediately after Showing tooltip. appears on the terminal.
Observation
The tooltip is displayed after approximately 5 seconds after Showing tooltip. appears on the terminal.
How can I ensure that the tooltip is displayed at the same time as Showing tooltip. is printed? After the wait(5); delay, I would like to hide the tooltip, so that, effectively, the tooltip is visible only for a short amount of time.
To run the code, GTK3 is required.
sudo apt install libgtk-3-dev
gcc $(pkg-config --libs --cflags gtk+-3.0) main.c
./a.out
GUI is drawn in mainloop. When you call sleep() you freeze it for some time and it does nothing: no redrawing, no feedback for user input etc. You must show the tooltip and schedule a function which will hide the tooltip later.
gboolean hide_tooltip (gpointer data)
{
GtkWidget *widget = data;
gtk_widget_set_has_tooltip (widget, FALSE);
return G_SOURCE_REMOVE; // same as FALSE. otherwise this function will be called again;
}
void show_tooltip (GtkWidget *widget, gpointer data)
{
GtkWidget **window = data;
printf("Showing tooltip.\n");
gtk_widget_set_tooltip_text(*window, "Clicked!");
// Schedule
g_timeout_add (5 * G_TIME_SPAN_MILLISECOND, hide_tooltip, *window);
}
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.
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`
I've tried a few things and may be nearing a solution but here goes ...
on a (possibly realised) widget, has anyone got a good example of say an
GtkEntry widget receiving a ALT+ Down or CTRL + n key press which will raise
a "new" signal e.g. SIG-NEW on the widget. which I know how to catch with g_signal_connect.
I appended a minimal working example.
The important point is to connect the key-press-event of an GtkEntry-instance to a suitable callback. In that callback you have to extract the GdkEventKey struct that is a member of the GdkEvent struct which is a parameter of the callback function.
GdkEventKey contains all information you need to check which key+modifier was pressed.
Since i tested the code with gtk+-3.0 you may have to modify the GdkEventMask of the widget to receive key-press-event's. For gtk+-3.0 it seems that is already the default setting. If the callback isn't invoked you should enable it with gdk_window_set_events () (of course you have to invoke it upon the gdk window of the widget).
#include <gtk/gtk.h>
#include <glib.h>
gboolean
key_press_event_cb (GtkWidget * widget, GdkEvent * event,
gpointer data)
{
GdkEventKey key = event->key;
/* check modifier key */
/* on most keyboards GDK_MOD1_MASK is the Alt key */
if (key.state == GDK_MOD1_MASK) {
/* check for key that was pressed */
switch (key.keyval) {
case GDK_KEY_d:
g_print
("`Alt-d` deletes the content in the entry box\n");
gtk_entry_set_text (GTK_ENTRY (widget), "");
break;
}
}
/* check for unmodified key presses */
switch (key.keyval) {
case GDK_KEY_x:
g_print
("`x` deletes the content in the entry box\n");
gtk_entry_set_text (GTK_ENTRY (widget), "");
return TRUE;
break;
}
return FALSE;
}
int
main (int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *entry;
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
entry = gtk_entry_new ();
gtk_container_add (GTK_CONTAINER (window), entry);
gtk_widget_show_all (window);
g_signal_connect (window, "destroy",
G_CALLBACK (gtk_main_quit), NULL);
g_signal_connect (entry, "key-press-event",
G_CALLBACK (key_press_event_cb), NULL);
gtk_main ();
return 0;
}
I have a GTK+ application written in C that loads a matrix of animated GIF files. These GtkImages automatically run the animation when they are loaded and then stop when the animation is completed. How would I restart the animation of each GtkImage containing the GIF and are signals generated when the animation is complete?
Thank you.
EDIT :
Would the use of gdk_pixbuf_animation_get_iter() described here make this possible?
Full code is provided below.
/*
* Compile me with:
* gcc -o reels reels.c $(pkg-config --cflags --libs gtk+-2.0 gmodule-2.0)
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <time.h>
/* GTK */
#include <gdk/gdkx.h>
#include <gtk/gtk.h>
/**** prototypes ****/
static void destroy (GtkWidget*, gpointer);
GdkPixbuf *create_pixbuf(const gchar * filename);
GtkWidget *SetupWindow(gchar *data, const gchar *filename);
static void destroy (GtkWidget *window, gpointer data);
void btnSpin_clicked(GtkWidget *button, gpointer data);
void btnExit_clicked(GtkWidget *button, gpointer data);
/********************/
GtkWidget *images[3][5];
static void destroy (GtkWidget *window, gpointer data)
{
gtk_main_quit ();
}
void btnSpin_clicked(GtkWidget *button, gpointer data)
{
printf("Spin Button pressed.\n");
return;
}
void btnExit_clicked(GtkWidget *button, gpointer data)
{
gtk_main_quit();
return;
}
GtkWidget *SetupWindow(gchar *data, const gchar *filename)
{
GdkPixmap *background;
GdkPixbuf *pixbuf;
GdkScreen *ourscreen;
GdkColormap *colormap;
GtkStyle *style;
GdkColor fg;
GdkColor bg;
GError *error = NULL;
GdkRectangle *rect;
GtkWidget *window;
pixbuf = gdk_pixbuf_new_from_file (filename,&error);
if (error != NULL) {
if (error->domain == GDK_PIXBUF_ERROR) {
g_print ("Pixbuf Related Error:\n");
}
if (error->domain == G_FILE_ERROR) {
g_print ("File Error: Check file permissions and state:\n");
}
g_printerr ("%s\n", error[0].message);
}
gdk_pixbuf_render_pixmap_and_mask (pixbuf, &background, NULL, 0);
style = gtk_style_new ();
style->bg_pixmap[0] = background;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), data);
// gtk_window_maximize(GTK_WINDOW(window));
gtk_window_set_modal (GTK_WINDOW (window),TRUE);
gtk_window_set_default_size(GTK_WINDOW(window),628,530);
gtk_widget_set_style (GTK_WIDGET(window), GTK_STYLE(style));
gtk_window_set_position(GTK_WINDOW(window),GTK_WIN_POS_CENTER_ALWAYS);
gtk_container_set_border_width(GTK_CONTAINER(window), 0);
//gtk_window_set_resizable(GTK_WINDOW(window), (gboolean) FALSE);
gtk_window_set_decorated( GTK_WINDOW(window), FALSE );
return(window);
}
int main (int argc, char *argv[])
{
GdkPixbufAnimation *animation;
GtkWidget *image;
int x,y;
GdkPixbuf *pixBuf;
GdkPixmap *pixMap;
gchar filename[20];
GtkWidget *btnSpin, *btnExit;
GtkWidget *frame; /* for absolute positionining of widgets */
GtkWidget *window;
int posx, posy;
gtk_init (&argc, &argv);
window = SetupWindow("Demo", "background.gif");
g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
frame = gtk_fixed_new();
gtk_container_add(GTK_CONTAINER(window), frame);
btnSpin = gtk_button_new_with_label("Spin");
gtk_widget_set_size_request(btnSpin, 80, 35);
gtk_fixed_put(GTK_FIXED(frame), btnSpin, 229, 485);
g_signal_connect(G_OBJECT( btnSpin ), "clicked", G_CALLBACK(btnSpin_clicked), NULL );
btnExit = gtk_button_new_with_label("Exit");
gtk_widget_set_size_request(btnExit, 80, 35);
gtk_fixed_put(GTK_FIXED(frame), btnExit, 320, 485);
g_signal_connect(G_OBJECT( btnExit ), "clicked", G_CALLBACK(btnExit_clicked), NULL );
/* setup animated gifs */
for( y = 0; y < 3; y++ )
{
posy = (y*120) + 20;
for( x = 0; x < 5; x++ )
{
posx = (x*120) + 20;
/* set each Image widget to spin GIF */
sprintf( filename,"%d-%d.gif", y+1,x+1 );
images[y][x] = gtk_image_new_from_file(filename);
gtk_fixed_put(GTK_FIXED(frame), images[y][x], posx, posy);
}
}
gtk_widget_show_all(window);
gtk_main ();
return 0;
}
After taking a look at the GtkImage source code, I am afraid there is no signal that is generated when the animation is completed. However, you should be able to restart the animation by calling gtk_image_set_from_file() or gtk_image_set_from_animation().
To completely solve your initial issue, I propose you create a subclass of GtkImage. It should behave exactly like GtkImage, except that animation_timeout should send a signal if delay < 0 around line 1315.
Information about creating a subclass of a GObject (note that GtkImage is a GObject) can be found here.
Based on user1202136's answer the following code snippets show the solution. I post this in case others find it useful.
The basic idea is to use GdkPixbufAnimation, gdk_pixbuf_animation_new_from_file and gtk_image_set_from_animation
The following code snippet(s) show how to do it.
GtkWidget *images[3][5];
GdkPixbufAnimation *animations[3][5];
/* Initial setup of animated gifs */
for( y = 0; y < 3; y++ )
{
for( x = 0; x < 5; x++ )
{
/* set each Image widget to spin GIF */
sprintf( filename,"%d-%d.gif", y+1,x+1 );
images[y][x] = gtk_image_new();
animations[y][x] = gdk_pixbuf_animation_new_from_file ( filename , &error);
gtk_image_set_from_animation (GTK_IMAGE(images[y][x]), animations[y][x]);
gtk_fixed_put(GTK_FIXED(frame), images[y][x], (x*120) + 20, (y*120) + 20);
}
}
/* now to restart the animations use the images and animations array already stored in memory,
no need to re-read the animations from disk so this happens quickly
*/
/* restart animated gifs */
for( y = 0; y < 3; y++ )
{
for( x = 0; x < 5; x++ )
{
gtk_image_set_from_animation(GTK_IMAGE(images[y][x]), animations[y][x]);
}
}