I have a C GTK3 program that has a notebook with two images. I want to be able to grab the corner of the window and adjust the size of the image currently displayed. What I currently have is a program that once started, the window keeps growing until I kill it from the terminal using ctrl-c. I put a sleep call in the callback to slow it down, but it still grows. How do I stop the window from growing unless I "grab" a corner of the window and adjust it myself?
#include <stdio.h>
#include <gtk/gtk.h>
#include <gdk-pixbuf/gdk-pixbuf.h>
GtkWidget *notebook;
gboolean resize_image(GtkWidget *widget, GdkRectangle *allocation,
gpointer user_data)
{
int w,h, pagenum;
GdkPixbuf *pxbscaled;
GtkWidget *image;
GdkPixbuf *pixbuf;
pagenum = gtk_notebook_get_current_page (GTK_NOTEBOOK(notebook));
image = gtk_notebook_get_nth_page (GTK_NOTEBOOK(notebook), pagenum);
// GtkImageType image_type = gtk_image_get_storage_type
// (GTK_IMAGE(image));
pixbuf = gtk_image_get_pixbuf(GTK_IMAGE(image));
h = allocation->height;
w = (gdk_pixbuf_get_width(pixbuf) * h) / gdk_pixbuf_get_height(pixbuf);
pxbscaled = gdk_pixbuf_scale_simple(pixbuf, w, h, GDK_INTERP_BILINEAR);
printf("Allocation height %d width %d.\n", h, w);
gtk_image_set_from_pixbuf(GTK_IMAGE(image), pxbscaled);
g_object_unref (pxbscaled);
sleep(2);
return FALSE;
}
static gboolean delete( GtkWidget *widget,
GtkWidget *event,
gpointer data )
{
gtk_main_quit ();
return FALSE;
}
int main( int argc,
char *argv[] )
{
GtkWidget *window;
GtkWidget *button;
GtkWidget *table;
GtkWidget *label;
GtkWidget *image;
int i;
char bufferl[32];
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
// gtk_widget_set_size_request (GTK_WIDGET(window), 800, 480);
g_signal_connect (window, "delete-event",
G_CALLBACK (delete), NULL);
gtk_container_set_border_width (GTK_CONTAINER (window), 10);
table = gtk_grid_new ();
gtk_container_add (GTK_CONTAINER (window), table);
/* Create notebook, place position of tabs */
notebook = gtk_notebook_new ();
gtk_notebook_set_tab_pos (GTK_NOTEBOOK (notebook), GTK_POS_TOP);
gtk_grid_attach (GTK_GRID (table), notebook, 0, 6, 3, 3);
gtk_widget_show (notebook);
/* Append pages to the notebook */
for (i = 0; i < 2; i++) {
sprintf(bufferl, "Page %d", i + 1);
if (i == 0) {
image = gtk_image_new_from_file("image1.jpg");
} else {
image = gtk_image_new_from_file("image2.jpg");
}
gtk_widget_set_halign(image, GTK_ALIGN_START);
gtk_widget_set_valign(image, GTK_ALIGN_START);
g_signal_connect(window, "size-allocate",
G_CALLBACK(resize_image), NULL);
label = gtk_label_new (bufferl);
gtk_notebook_append_page (GTK_NOTEBOOK(notebook),
image, label);
}
/* Create a close button */
button = gtk_button_new_with_label ("close");
g_signal_connect (button, "clicked",
G_CALLBACK (delete), NULL);
gtk_grid_attach (GTK_GRID (table), button, 0, 10, 1, 1);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
A user will need to provide image1.jpg and image2.jpg. Edit out the sleep call will result in the program filling the screen extremely quickly.
EDIT: I have also asked this question on the gtk mailing list.
The window with the image was growing because I was applying the size of the window to the image. Hence the image got larger and thus made the window get larger. Which continued in an endless progression, the "size-allocate" signal was constantly being called.
I fixed it by limiting the allocation height in the call back, by multiplying it by 0.75.
Now I can expand and contract the window with ease and it does not grow out of control.
The image does get ugly quite quickly, but that is another problem.
Related
I am trying to make UI using GTK in c for raspberry pi 4. I want to change the visibility of different widgets based on button click just to simulate a new page. I have tried everything available on the internet but as I am not that good at coding I cant figure out what is wrong.
can someone please help ?
This program compiles but when I press the button it gives error " assertion failed on gtk_widget_show " and also on widget hide. Also a segmentation fault occurs and the program crashes.
I am using cmake to compile my code. I have attached the error screen shot.
#include <gtk/gtk.h>
typedef struct AppData
{
GtkWidget *label1;
GtkWidget *label2;
} AppData;
static void button1 (gpointer data)
{
AppData *data2 = (AppData*)data;
gtk_widget_hide(data2->label1);
gtk_widget_show(data2->label2);
}
static void button2 ( gpointer data)
{
AppData *data2 = (AppData*)data;
gtk_widget_show(data2->label1);
gtk_widget_hide(data2->label2);
}
int main(int argc, char *argv[])
{
GtkWidget *window;
GtkWidget *fixed;
GtkWidget *btn1;
GtkWidget *btn2;
GtkWidget *box1;
GtkWidget *box2;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_title(GTK_WINDOW(window), "ethercat test 1");
gtk_window_set_default_size(GTK_WINDOW(window), 1000,500);
fixed = gtk_fixed_new();
gtk_container_add(GTK_CONTAINER(window), fixed);
box1 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 1);
gtk_fixed_put(GTK_FIXED(fixed), box1, 0,0);
box2 = gtk_box_new(GTK_ORIENTATION_VERTICAL, 1);
gtk_fixed_put(GTK_FIXED(fixed), box2, 100,100);
AppData *app_data = g_new0 (AppData, 2);
app_data->label1 = gtk_label_new("label1");
gtk_box_pack_start(GTK_BOX(box1),app_data->label1, TRUE,TRUE,0);
app_data->label2 = gtk_label_new("label2");
gtk_box_pack_start(GTK_BOX(box2),app_data->label2, TRUE,TRUE,0);
btn1 = gtk_button_new_with_label("ethercat 1");
gtk_fixed_put(GTK_FIXED(fixed), btn1, 10, 450);
gtk_widget_set_size_request(btn1, 80,30);
btn2 = gtk_button_new_with_label("ethercat 2");
gtk_fixed_put(GTK_FIXED(fixed), btn2, 110, 450);
gtk_widget_set_size_request(btn2, 80,30);
gtk_widget_show_all(window);
g_signal_connect(G_OBJECT(btn1), "clicked", G_CALLBACK(button1), app_data);
g_signal_connect(G_OBJECT(btn2), "clicked", G_CALLBACK(button2), app_data);
g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
gtk_main();
printf("program end\n");
return (0);
}
enter image description here
The function signature of your "clicked" callbacks is wrong. It should be of the form as described in the documentation:
void on_clicked(
GtkButton* self,
gpointer user_data
)
So for example, your button2() function becomes
static void button2 (GtkButton *btn2, gpointer data)
{
AppData *data2 = (AppData*)data;
gtk_widget_show(data2->label1);
gtk_widget_hide(data2->label2);
}
I am receiving this error and not sure why...Please take a look at my buttons array, maybe I messed smth there, I am not sure...
The error i get is: Xlib: extension "RANDR" missing on display ":24.0".
and nothing happens after that, meaning my program doesnt run at all....
#include <gtk/gtk.h>
/* Our new improved callback. The data passed to this function
* is printed to stdout. */
static void callback (GtkWidget *widget, gpointer data)
{
system ((gchar *) data);
}
/* another callback */
static gboolean delete_event (GtkWidget *widget, GdkEvent *event, gpointer data)
{
gtk_main_quit ();
return FALSE;
}
int main (int argc, char *argv[])
{
/* GtkWidget is the storage type for widgets */
GtkWidget *window;
GtkWidget *box1;
/* This is called in all GTK applications. Arguments are parsed
* from the command line and are returned to the application. */
gtk_init (&argc, &argv);
/* Create a new window */
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
/* This is a new call, which just sets the title of our
* new window to "My Assignments" */
gtk_window_set_title (GTK_WINDOW (window), "My Assignments");
/* Here we just set a handler for delete_event that immediately
* exits GTK. */
g_signal_connect (window, "delete-event",
G_CALLBACK (delete_event), NULL);
/* Sets the border width of the window. */
gtk_container_set_border_width (GTK_CONTAINER (window), 50);
/* We create a box to pack widgets into. This is described in detail
* in the "packing" section. The box is not really visible, it
* is just used as a tool to arrange widgets. */
box1 = gtk_vbox_new (FALSE,0);
/* Put the box into the main window. */
gtk_container_add (GTK_CONTAINER (window), box1);
/*array is here*/
GtkWidget *button[2];
int i;
for (i=0; i<2; i++)
{
button[i]=gtk_button_new();
}
button[0] = gtk_button_new_with_label ("Run shellscript");
g_signal_connect (button[0], "clicked",
G_CALLBACK (callback), "shellscript");
gtk_box_pack_start (GTK_BOX (box1), button[0], TRUE, TRUE, 0);
gtk_widget_show(button[0]);
button[1] = gtk_button_new_with_label ("Run program2 ");
g_signal_connect (button[1], "clicked",
G_CALLBACK (callback), "program");
gtk_box_pack_start (GTK_BOX (box1), button[1], TRUE, TRUE, 0);
gtk_widget_show(button[1]);
gtk_widget_show (box1);
/* Rest in gtk_main and wait for the fun to begin! */
gtk_main ();
return 0;
}
I have a problem.
I need to draw on the widget type GtkDrawingArea using functions Xlib (XDrawLine etc).
Why?
I use the library, which draws with Xlib. And I need to pass any arguments (Display, Window, GC) in the rendering function drawSome (...). All is well. I obtain these arguments (via gdk_x11_... (), GdkDrawable, GdkGC) and call drawSome (...) with obtained parameters.
But there are problems - drawing is not always done. The image is not displayed when maximizing windows, dragging, resizing DrawingArea etc.. The image is displayed only under unusual manipulation of the top window .
Then I tested the function XDrawPoint/Line/Rectangle - the same problem. If we use gdk_draw_rectangle (...) - all is normal.
Here's the code below:
...
GtkDrawingArea* area;
...
int main (int argc, char *argv[])
{
...
area=GTK_DRAWING_AREA(gtk_builder_get_object(builder,"area"));
gtk_widget_realize (GTK_WIDGET(area));
...
g_signal_connect (G_OBJECT(area), "expose_event", G_CALLBACK(expose_event_callback), NULL);
...
}
...
gboolean expose_event_callback (GtkWidget *widget, GdkEventExpose *event, gpointer data)
{
Display *dpy = gdk_x11_drawable_get_xdisplay(widget->window);
Window win =gdk_x11_drawable_get_xid(widget->window);
GC gc = DefaultGC(dpy, DefaultScreen(dpy));
//draw image on (0,0) in widget DrawingArea and a small black rectangle over image
drawSome(dpy, win, gc, ...);
XFillRectangle(dpy, win, gc, 0, 0, 10, 10);
return FALSE;
}
...
Image and a small black rectangle displayed only in one case: if the window move beyond the desktop and return back to the desktop - the image appears. In other cases, it is not displayed.
The impression is that another function erases DrawingArea.
Who can tell me what's the problem?
I would be grateful!
And... sorry to so bad English!
I think all you need to do is add XFlush(dpy); after the XFillRectangle command. I wrote a short routine and it seems to work.
#include <X11/Xlib.h> //-lX11
#include <gtk/gtk.h> //$$(pkg-config --cflags --libs gtk+-3.0)
#include <gdk/gdkx.h>
void DrawOnWidget(GtkWidget *widget)
{
GdkDisplay *gdk_dis = gdk_display_get_default();
Display *dis = gdk_x11_display_get_xdisplay (gdk_dis);
GC gc = DefaultGC(dis, DefaultScreen(dis));
GdkWindow *gdk_window = gtk_widget_get_window(widget);
Window win = gdk_x11_window_get_xid(gdk_window);
unsigned long valuemask = GCForeground;
XGCValues vColor;
vColor.foreground = 0x000000FF;
XChangeGC(dis, gc, valuemask, &vColor);
XFillRectangle(dis, win, gc, 0, 0, 100, 100);
XFlush(dis);
}
You'll need to use these functions X Window System Interaction but be warned but there may be other pitfalls. I think you'll also need to disable double-buffering for your GtkDrawingArea using gtk_widget_set_double_buffered
This is my second answer to this question... My first answer probably addresses the original question so I left it as unchanged. Although, the more general solution which is what I was looking for when I first found this thread was not addressed therefore, I decided to post a more general solution as well.
In short, I created a gtk image widget which can be displayed independently or attached to other widgets like buttons. Then I sent the image widget to the drawing function. In the drawing function all of the xlib parameters are queried in order to create a Pixmap as the xlib drawable which is where all xlib drawing is rendered. Then the GdkPixbuf is created and the Pixmap pixels are copied to the GdkPixbuf which is then set to the image widget...
There are a few commented lines which can be used to change the behavior the explanation will be left to the reader to determine.
It should be noted that while using xlib is possible; cairo appears to be a bit less cumbersome to implement.
#include <gtk/gtk.h> //$$(pkg-config --cflags --libs gtk+-3.0)
#include <gdk/gdkx.h>
#include <X11/Xlib.h> //-lX11
void DrawOnWidget(GtkWidget *widget, int width, int height)
{
GdkDisplay *gdk_dis = gdk_display_get_default();
Display *dis = gdk_x11_display_get_xdisplay (gdk_dis);
GC gc = DefaultGC(dis, DefaultScreen(dis));
GdkWindow *gdk_window = gtk_widget_get_window(widget);
Window win = gdk_x11_window_get_xid(gdk_window);
GdkPixbuf *pb = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, width, height);
//GdkPixbuf *pb = gtk_image_get_pixbuf((GtkImage *) widget);
char *data = (char *) gdk_pixbuf_read_pixels((const GdkPixbuf *) pb); //RGB(A)
//int width = gdk_pixbuf_get_width(pb);
//int height = gdk_pixbuf_get_height(pb);
int pb_depth = gdk_pixbuf_get_n_channels(pb);
int depth = DefaultDepth(dis, DefaultScreen(dis)) / 8;
Pixmap pm = XCreatePixmap(dis, win, width, height, depth * 8);
unsigned long valuemask = GCForeground;
XGCValues vColor;
vColor.foreground = 0x00FF0000;
XChangeGC(dis, gc, valuemask, &vColor);
XFillRectangle(dis, pm, gc, 0, 0, width, height);
XFlush(dis);
XImage *ximage = XGetImage(dis, pm, 0, 0, width, height, AllPlanes, ZPixmap); //BGRX
for(int i=0, j=0; i<width*height*pb_depth; i+=pb_depth, j+=4)
{
data[i+0] = ximage->data[j+2];
data[i+1] = ximage->data[j+1];
data[i+2] = ximage->data[j+0];
if(pb_depth == 4) data[i+3] = 255;
}
gtk_image_set_from_pixbuf((GtkImage *) widget, pb);
XFreePixmap(dis, pm);
g_object_unref(pb);
return;
}
static void destroy( GtkWidget *widget, gpointer data )
{
gtk_main_quit();
}
static void test( GtkWidget *widget, gpointer data )
{
DrawOnWidget((GtkWidget *) data, 200, 200);
}
void GTK_Win()
{
GtkWidget *window, *grid;
GtkWidget *button_Exit, *button_Test;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect (window, "destroy", G_CALLBACK (destroy), NULL);
grid = gtk_grid_new ();
gtk_container_add (GTK_CONTAINER (window), grid);
button_Exit = gtk_button_new_with_label ("x");
g_signal_connect (button_Exit, "clicked", G_CALLBACK (destroy), NULL);
//GdkPixbuf *pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, true, 8, 200, 200);
//GtkWidget *image = gtk_image_new_from_pixbuf(pixbuf);
GtkWidget *image = gtk_image_new();
button_Test = gtk_button_new_with_label ("Test");
g_signal_connect (button_Test, "clicked", G_CALLBACK (test), image);
//gtk_button_set_image((GtkButton *) button_Test, image);
gtk_grid_attach (GTK_GRID (grid), button_Exit, 0, 0, 1, 1);
gtk_grid_attach (GTK_GRID (grid), button_Test, 0, 1, 1, 1);
gtk_grid_attach (GTK_GRID (grid), image, 0, 2, 1, 1);
gtk_widget_show_all (window);
gtk_main();
}
int main(int argc, char** argv)
{
gtk_init (&argc, &argv);
GTK_Win();
return 0;
}
I would like to draw some lines (actually a sine wave) inside a gtk+ cairo drawing area but I can't figure out a way to do that.
So my code first defines the drawing area which has a callback to a function that draws a white background into the drawing area itself...at this point I would like to start drawing a piece of the sin wave after each time the START button is pressed (the button has a callback to the drawing function)....
So first of all..would it be possible to do that??Am I missing something?
Thanks.
#include <cairo.h>
#include <gtk/gtk.h>
int x = 0;
int x_old = 0;
float value = 0;
float value_old = 0;
GtkWidget *button = NULL;
GtkWidget *window = NULL;
GtkWidget *area = NULL;
GtkWidget *table = NULL;
static gboolean load_interface(GtkWidget *widget)
{
cairo_t *cr;
cr = gdk_cairo_create(area->window);
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_rectangle(cr, 0, 0, 900, 400);
cairo_fill(cr);
}
void draw(GtkWidget *widget)
{
cairo_t *cr;
cr = gdk_cairo_create(widget->window);
x_old = x;
x = x + 15;
value_old = value;
value = value + 0.25;
cairo_set_source_rgb(cr, 0, 0, 0);
cairo_set_line_width (cr, 1);
cairo_move_to(cr, x_old, 100 + (sin(value_old))*50);
cairo_line_to(cr, x, 100 + (sin(value))*50);
cairo_stroke(cr);
}
int main (int argc, char *argv[])
{
g_log_set_handler ("Gtk", G_LOG_LEVEL_WARNING, (GLogFunc) gtk_false, NULL);
gtk_init (&argc, &argv);
g_log_set_handler ("Gtk", G_LOG_LEVEL_WARNING, g_log_default_handler, NULL);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_container_set_border_width (GTK_CONTAINER (window), 3);
gtk_window_set_title (GTK_WINDOW (window), "Draw");
gtk_window_set_position (GTK_WINDOW (window), GTK_WIN_POS_CENTER);
gtk_widget_realize (window);
g_signal_connect (window, "destroy", gtk_main_quit, NULL);
table = gtk_table_new (10, 10, TRUE);
gtk_table_set_col_spacings(GTK_TABLE(table),10);
gtk_table_set_row_spacings(GTK_TABLE(table),5);
gtk_container_add (GTK_CONTAINER (window), table);
area = gtk_drawing_area_new();
gtk_signal_connect (GTK_OBJECT(area), "event", G_CALLBACK (load_interface), (gpointer)area);
gtk_table_attach_defaults (GTK_TABLE(table), area, 1, 10, 0, 10);
button = gtk_button_new_from_stock ("START");
g_signal_connect (G_OBJECT (button), "clicked", G_CALLBACK (draw), (gpointer) window);
gtk_table_attach_defaults (GTK_TABLE(table), button, 0, 1, 0, 1);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
All your drawing should happen in the drawing area drawing event, called expose-event for GTK2 and draw in GTK3. In response to the button being clicked, you just change some values that you will reuse to compute the whole scene drawn. Once you changed these values, you just invalidate the drawing area to force it being redrawn, calling gtk_widget_queue_draw on the drawing area. And that's it.
If the scene is expensive to draw, remember using cairo clipping functions to improve performance by drawing only what has changed. Use then gtk_widget_queue_draw_area instead of gtk_widget_queue_draw, to give a hint about the zone that has changed and needs to be redrawn.
See GtkDrawingArea documentation.
BTW, don't use casts to GTK_OBJECT (deprecated), use G_OBJECT instead. This makes porting to GTK3 easier.
g_signal_connect() the expose-event.
I am new to GTK+ programming.I wrote a simple GTK+ program where i display a label and a textbox in a window, the label should be to the left of the textbox and i should be able to specify the horizontal length of the textbox. Below is my code so far,the program runs fine but im unable to align the label to the left of the textbox and also set the textbox horizontal length.
#include <stdio.h>
#include <stdlib.h>
#include <gtk/gtk.h>
static void destroy(GtkWidget *widget,gpointer data)
{
gtk_main_quit ();
}
int main (int argc, char *argv[])
{
GtkWidget *window,*table,*label,*entry;
gtk_init(&argc, &argv);
void initialize_window(GtkWidget *);
//Create the main window
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
initialize_window(window);
gtk_widget_show(window);
/* Create a 1x2 table */
table = gtk_table_new (1, 2, TRUE);
gtk_container_add (GTK_CONTAINER (window), table);
gtk_widget_show (table);
/* create a new label. */
label = gtk_label_new ("Enter some text: ");
gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
gtk_table_attach_defaults (GTK_TABLE (table),label, 0, 1, 0, 1);
gtk_widget_show (label);
//create a text box
entry = gtk_entry_new ();
gtk_entry_set_max_length (GTK_ENTRY (entry),0);
gtk_table_attach_defaults (GTK_TABLE (table),entry, 0, 1, 0, 1);
gtk_widget_show (entry);
gtk_main ();
return 0;
}
void initialize_window(GtkWidget *window)
{
gtk_window_set_title(GTK_WINDOW(window),"My Window"); //Set window title
gtk_window_set_default_size (GTK_WINDOW (window), 400, 200); //Set default size for the window
g_signal_connect (window, "destroy",G_CALLBACK (destroy), NULL); //End application when close button clicked
}
How can i fix this problem ?
Please help
Thank You.
You messed with the table position and don't set aligin if you do not know what it does, it may be a bit missleading.
Here is working code (I think this is what you wanted):
#include <stdio.h>
#include <stdlib.h>
#include <gtk/gtk.h>
static void destroy(GtkWidget *widget, gpointer data)
{
gtk_main_quit ();
}
static void initialize_window(GtkWidget* window)
{
gtk_window_set_title(GTK_WINDOW(window),"My Window"); //Set window title
gtk_window_set_default_size (GTK_WINDOW (window), 400, 200); //Set default size for the window
g_signal_connect (window, "destroy", G_CALLBACK (destroy), NULL); //End application when close button clicked
}
int main (int argc, char *argv[])
{
GtkWidget *window,*table,*label,*entry;
gtk_init(&argc, &argv);
//Create the main window
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
initialize_window(window);
/* Create a 1x2 table */
table = gtk_table_new (1, 2, TRUE);
gtk_container_add (GTK_CONTAINER (window), table);
/* create a new label. */
label = gtk_label_new ("Enter some text:" );
//gtk_misc_set_alignment (GTK_MISC (label), 0, 0);
gtk_table_set_homogeneous(GTK_TABLE (table), TRUE);
gtk_table_attach_defaults (GTK_TABLE (table), label, 1, 2, 0, 1);
//create a text box
entry = gtk_entry_new ();
gtk_entry_set_max_length (GTK_ENTRY (entry),0);
gtk_table_attach_defaults (GTK_TABLE (table), entry, 0, 1, 0, 1);
gtk_widget_show_all(window);
gtk_main ();
return 0;
}
For alignment one can use hbox for horizontal arrangement or vbox for vertical arrangement.