Unable to pass argument to GTK callback - c++

When the program is initially run, the print statements, print a valid hex code corresponding to the pointer. However, when I click on the screen, and the handleClick method is called through the 'clicked' callback, 0x0 is printed to the screen. What happened? Why has my board object suddenly become null?
#include <stdio.h>
#include <stdlib.h>
#include <gtk/gtk.h>
#include "board.h"
void printBoard(Board *board);
void handleClick(GtkWidget *widget, GdkEventButton *event, cairo_t *cr, gpointer data);
gboolean draw_cb(GtkWidget *widget, cairo_t *cr, gpointer data)
{
Board *temp = (Board *)data;
printf("%p\n",temp);
return TRUE;
}
void handleClick (GtkWidget *widget, GdkEventButton *event, cairo_t *cr, gpointer data)
{
Board *temp = (Board *)data;
printf("%p\n",temp);
}
void start(Board *newBoard)
{
GtkWidget *window;
GtkWidget *da;
GtkWidget *frame;
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
char string[40];
snprintf(string, sizeof(string), "Connect %d-%d",newBoard->k,newBoard->n);
gtk_window_set_default_size (GTK_WINDOW(window), 400, 400);
gtk_window_move(GTK_WINDOW(window), 100, 100);
g_signal_connect (GTK_WINDOW(window), "destroy", G_CALLBACK (gtk_main_quit), NULL);
da = gtk_drawing_area_new ();
gtk_widget_set_size_request (da, 500, 500);
frame = gtk_frame_new (NULL);
gtk_frame_set_shadow_type (GTK_FRAME (frame), GTK_SHADOW_IN);
gtk_container_add (GTK_CONTAINER (window), frame);
gtk_container_add (GTK_CONTAINER (frame), da);
gtk_widget_set_events (da, gtk_widget_get_events (da)
| GDK_BUTTON_PRESS_MASK);
g_signal_connect (da, "draw",
G_CALLBACK (draw_cb), newBoard);
g_signal_connect (da, "button-press-event",
G_CALLBACK (handleClick), newBoard);
gtk_widget_show_all (window);
}

The prototype of the button-press-event signal handler is the following according to the reference manual.
gboolean user_function (GtkWidget *widget,
GdkEvent *event,
gpointer user_data)
If you remove cairo_t *cr parameter from the handleClick function data will be ok.

As Szilard said, you're using the wrong prototype for the handleClick function.
The button-press-event signal handler has the following prototype:
gboolean user_function (GtkWidget *widget,
GdkEvent *event,
gpointer user_data)
To fix your mistakes, you need to:
remove thecairo_t *cr parameter. It was receiving the value of data.
fix event type which is GdkEvent *, not GdkEventButton *
fix the return value, which is a boolean, not void, and which is important to determine if the event is propagated or not.

Related

Proper way of building Gtk system tray in c++

int main(int argc, char *argv[])
{
gtk_init (&argc, &argv);
GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "GtkStatusIcon Example");
gtk_widget_set_size_request (window, 200, -1);
set try icon file
GtkStatusIcon *trayIcon = gtk_status_icon_new_from_file ("/root/Desktop/icon.png");
set popup menu for tray icon
GtkWidget *menu, *menuItemView, *menuItemExit;
menu = gtk_menu_new();
menuItemView = gtk_menu_item_new_with_label ("View");
menuItemExit = gtk_menu_item_new_with_label ("Exit");
g_signal_connect (G_OBJECT (menuItemView), "activate", G_CALLBACK (trayView), window);
g_signal_connect (G_OBJECT (menuItemExit), "activate", G_CALLBACK (trayExit), NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuItemView);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuItemExit);
gtk_widget_show_all (menu);
}
//gcc trayicon.c -o trayicon pkg-config --cflags --libs gtk+-2.0.
#include <gtk/gtk.h>
static void trayView(GtkMenuItem *item, gpointer user_data);
static void trayExit(GtkMenuItem *item, gpointer user_data);
static void trayIconActivated(GObject *trayIcon, gpointer data);
static void trayIconPopup(GtkStatusIcon *status_icon, guint button, guint32 activate_time, gpointer popUpMenu);
static void destroy (GtkWidget*, gpointer);
static gboolean delete_event (GtkWidget*, GdkEvent*, gpointer);
static gboolean window_state_event (GtkWidget *widget, gdkEventWindowState *event, gpointer user_data);
int main(int argc, char *argv[])
{
gtk_init (&argc, &argv);
GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_window_set_title (GTK_WINDOW (window), "GtkStatusIcon Example");
gtk_widget_set_size_request (window, 200, -1);
//set try icon file
GtkStatusIcon *trayIcon = gtk_status_icon_new_from_file ("/root/Desktop/icon.png");
//set popup menu for tray icon
GtkWidget *menu, *menuItemView, *menuItemExit;
menu = gtk_menu_new();
menuItemView = gtk_menu_item_new_with_label ("View");
menuItemExit = gtk_menu_item_new_with_label ("Exit");
g_signal_connect (G_OBJECT (menuItemView), "activate", G_CALLBACK (trayView), window);
g_signal_connect (G_OBJECT (menuItemExit), "activate", G_CALLBACK (trayExit), NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuItemView);
gtk_menu_shell_append (GTK_MENU_SHELL (menu), menuItemExit);
gtk_widget_show_all (menu);
//set tooltip
gtk_status_icon_set_tooltip (trayIcon, "MsgWatcherGTK");
//connect handlers for mouse events
g_signal_connect(GTK_STATUS_ICON (trayIcon), "activate", GTK_SIGNAL_FUNC (trayIconActivated), window);
g_signal_connect(GTK_STATUS_ICON (trayIcon), "popup-menu", GTK_SIGNAL_FUNC (trayIconPopup), menu);
gtk_status_icon_set_visible(trayIcon, FALSE); //set icon initially invisible
GtkWidget *menuBar, *menuItemTopLvl, *mainMenu, *mainMenuItemExit;
menuBar = gtk_menu_bar_new ();
menuItemTopLvl = gtk_menu_item_new_with_label ("Menu");
gtk_menu_shell_append (GTK_MENU_SHELL (menuBar), menuItemTopLvl);
mainMenu = gtk_menu_new ();
gtk_menu_item_set_submenu (GTK_MENU_ITEM (menuItemTopLvl), mainMenu);
mainMenuItemExit = gtk_menu_item_new_with_label ("Quit");
g_signal_connect (G_OBJECT (mainMenuItemExit), "activate", G_CALLBACK (trayExit), NULL);
gtk_menu_shell_append (GTK_MENU_SHELL (mainMenu), mainMenuItemExit);
g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (destroy), NULL);
g_signal_connect (G_OBJECT (window), "delete_event", G_CALLBACK (delete_event), trayIcon);
g_signal_connect (G_OBJECT (window), "window-state-event", G_CALLBACK (window_state_event), trayIcon);
gtk_container_add (GTK_CONTAINER (window), menuBar);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
static void trayView(GtkMenuItem *item, gpointer window)
{
gtk_widget_show(GTK_WIDGET(window));
gtk_window_deiconify(GTK_WINDOW(window));
}
static void trayExit(GtkMenuItem *item, gpointer user_data)
{
printf("exit");
gtk_main_quit();
}
static void trayIconActivated(GObject *trayIcon, gpointer window)
{
gtk_widget_show(GTK_WIDGET(window));
gtk_window_deiconify(GTK_WINDOW(window));
}
static void trayIconPopup(GtkStatusIcon *status_icon, guint button, guint32 activate_time, gpointer popUpMenu)
{
gtk_menu_popup(GTK_MENU(popUpMenu), NULL, NULL, gtk_status_icon_position_menu, status_icon, button, activate_time);
}
static void destroy (GtkWidget *window, gpointer data)
{
gtk_main_quit ();
}
static gboolean delete_event (GtkWidget *window, GdkEvent *event, gpointer data)
{
return FALSE;
}
static gboolean window_state_event (GtkWidget *widget, GdkEventWindowState *event, gpointer trayIcon)
{
if(event->changed_mask == GDK_WINDOW_STATE_ICONIFIED && (event->new_window_state == GDK_WINDOW_STATE_ICONIFIED || event->new_window_state == (GDK_WINDOW_STATE_ICONIFIED | GDK_WINDOW_STATE_MAXIMIZED)))
{
gtk_widget_hide (GTK_WIDGET(widget));
gtk_status_icon_set_visible(GTK_STATUS_ICON(trayIcon), TRUE);
}
else if(event->changed_mask == GDK_WINDOW_STATE_WITHDRAWN && (event->new_window_state == GDK_WINDOW_STATE_ICONIFIED || event->new_window_state == (GDK_WINDOW_STATE_ICONIFIED | GDK_WINDOW_STATE_MAXIMIZED)))
{
gtk_status_icon_set_visible(GTK_STATUS_ICON(trayIcon), FALSE);
}
return TRUE;
}

GTK3 Detect user resizing window

This is some example code from the Gnome website:
#include <gtk/gtk.h>
static void resizechange() {
printf("User resized window!\n");
}
static void
activate (GApplication *app,
gpointer user_data)
{
GtkWidget *widget;
widget = gtk_application_window_new (GTK_APPLICATION (app));
gtk_widget_show (widget);
}
int
main (int argc, char **argv)
{
GtkApplication *app;
int status;
app = gtk_application_new ("org.gnome.example", G_APPLICATION_FLAGS_NONE);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
status = g_application_run (G_APPLICATION (app), argc, argv);
g_object_unref (app);
return status;
}
Is there a way that the resizechange() function could be run when the user resizes the program window, so I can act upon this?
Can this be done with events or callbacks?
You can do that by connecting to the configure-event signal of your widget.
However, your callback must respect the function signature stated in the documentation, so instead of:
static void resizechange()
you will have:
static gboolean resizechange (GtkWidget *widget, GdkEvent *event, gpointer user_data)

Set Cell in GtkTreeView to edit mode programmatically

I currently write an application that has a GtkTreeView with a GtkCellRendererText which property editable is set to true. When double clicking an item, I can edit it. Now I want to be able to add an empty row and immediately start editing it. I tried using gtk_tree_view_set_cursor_on_cell with start_editing set to true. It selects the row, but it does not start editing. I put together a small example (please not that I do not have any error checking in place as its just a small example).
#include <gtk/gtk.h>
GtkListStore *store;
GtkWidget *window;
GtkWidget *view;
GtkCellRenderer *renderer;
GtkWidget *button;
GtkWidget *vbox;
void sig_inserted(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter)
{
// Set cursor on cell with start_editing = TRUE
gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(view), path, gtk_tree_view_get_column(GTK_TREE_VIEW(view), 0), renderer, TRUE);
}
void button_clicked(GtkButton *button, GdkEvent *event, gpointer user_data)
{
// Add empty row to liststore
GtkTreeIter iter;
gtk_list_store_append(store, &iter);
gtk_list_store_set(store, &iter, 0, "", -1);
}
int main (int argc, char **argv)
{
gtk_init (&argc, &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
g_signal_connect (window, "delete_event", gtk_main_quit, NULL);
view = gtk_tree_view_new ();
// Create renderer and set editable to TRUE
renderer = gtk_cell_renderer_text_new ();
GValue val = G_VALUE_INIT;
g_value_init(&val, G_TYPE_BOOLEAN);
g_value_set_boolean(&val, TRUE);
g_object_set_property(G_OBJECT(renderer), "editable", &val);
g_value_unset(&val);
// Insert Text column
gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (view),
-1,
"Name",
renderer,
"text", 0,
NULL);
store = gtk_list_store_new (1, G_TYPE_STRING);
// Add test item
GtkTreeIter iter;
gtk_list_store_append (store, &iter);
gtk_list_store_set (store, &iter,
0, "Test",
-1);
gtk_tree_view_set_model (GTK_TREE_VIEW (view), GTK_TREE_MODEL(store));
vbox = gtk_vbox_new(0,0);
button = gtk_button_new_with_label("Add edit item");
gtk_box_pack_start(GTK_BOX(vbox), view, TRUE, TRUE, 0);
gtk_box_pack_start(GTK_BOX(vbox), button, TRUE, TRUE, 0);
gtk_container_add (GTK_CONTAINER (window), vbox);
gtk_widget_show_all (window);
g_signal_connect(G_OBJECT(store), "row-inserted", G_CALLBACK(sig_inserted), NULL);
g_signal_connect(G_OBJECT(button), "button-release-event", G_CALLBACK(button_clicked), NULL);
gtk_main ();
return 0;
}
Compile it with g++ (as I do not comply with all C needs) using
g++ -o tree tree.cc `pkg-config --libs --cflags gtk+-2.0`
It would also work with Gtk3 I think as nothing seems to have changed in the things I use.
Can somebody point me out why it does not start editing?
gtk_list_store_set cancels editing of a cell. Commenting out this line makes everything work.
It can be found this with a little modification to code:
void sig_inserted(GtkTreeModel *model, GtkTreePath *path, GtkTreeIter *iter)
{
GtkTreeViewColumn * col = gtk_tree_view_get_column(GTK_TREE_VIEW(view), 0);
// Set cursor on cell with start_editing = TRUE
g_message ("sig_inserted", __LINE__);
gtk_tree_view_set_cursor_on_cell(GTK_TREE_VIEW(view), path, col, renderer, TRUE);
}
void button_clicked(GtkButton *button, GdkEvent *event, gpointer user_data)
{
// Add empty row to liststore
GtkTreeIter iter;
g_message ("Clicked");
gtk_list_store_append(store, &iter);
g_message ("Appended");
gtk_list_store_set(store, &iter, 0, "x", -1);
g_message ("Set");
}
void
estart (GtkCellRenderer *renderer,
GtkCellEditable *editable,
gchar *path,
gpointer user_data)
{
g_message ("renderer: start edit");
}
void
ecancel (GtkCellRenderer *renderer,
gpointer user_data)
{
g_message ("renderer: cancel edit");
}
And connecting to renderers signals:
g_signal_connect(G_OBJECT(renderer), "editing-started", G_CALLBACK(estart), NULL);
g_signal_connect(G_OBJECT(renderer), "editing-canceled", G_CALLBACK(ecancel), NULL);
Give the following output:
** Message: Clicked
** Message: sig_inserted
** Message: renderer: start edit
** Message: Appended
** Message: renderer: cancel edit
** Message: Set

GTK Blinks when drawing

I'm really new with GTK, and im trying to learn how to handle it for a project i have in mind.
The thing is, i could manage to make an app for placing "Generators" on a window ... Each time i click on the screen, the app place a gen in the window, and prepare another one ready to be placed ...
The problem is that the program blinks when is drawing all the generators ...
Each gen has to redraw itself all the time, and this is i think the problem... Here is the code ... how can i make it faster? ... Thanks in advance!!
// gcc main.c -o main `pkg-config gtk+-3.0 --cflags --libs`
#include <gtk/gtk.h>
#include <stdlib.h>
#include <iostream>
#include <vector>
using namespace std;
class Gen{
public:
int x;
int y;
GdkPixbuf *pix;
GtkWidget *canvas;
bool placed;
Gen(GtkWidget *canvas){
this->canvas=canvas;
GError *err = NULL;
pix = gdk_pixbuf_new_from_file("./Img/gen.png", &err);
pix= gdk_pixbuf_scale_simple(pix,50,50, GDK_INTERP_BILINEAR);
x=10;y=10;
placed=0;
}
void draw(cairo_t *cr){
gdk_cairo_set_source_pixbuf(cr, pix, x, y);
cairo_paint(cr);
}
void updatePosition(int a, int b){
if(placed==0){
x=a-25;
y=b-25;
}
}
void place(){
placed=1;
}
};
class Map{
public:
vector<Gen *> Gens;
GtkWidget *window;
GtkWidget *canvas;
int xPointer,yPointer;
Map(GtkWidget *_window, GtkWidget *_canvas){
window=_window;
canvas=_canvas;
}
void draw(){
cairo_t *cr;
cr = gdk_cairo_create (gtk_widget_get_window(canvas));
cairo_set_source_rgb(cr, 1, 1, 1);
cairo_rectangle(cr, xPointer-35, yPointer-35, 70, 70);
cairo_paint(cr);
for(vector<Gen *>::const_iterator i=Gens.begin();i!=Gens.end();i++){
(*i)->draw(cr);
}
cairo_destroy (cr);
}
void place(){
Gen *aux=Gens.back();
aux->place();
//Gens.push_back(new Gen(canvas));
}
void moving(int x,int y){
xPointer=x;yPointer=y;
if(Gens.size()==0){
Gens.push_back(new Gen(canvas));
}
else if (Gens.back()->placed==1){
Gens.push_back(new Gen(canvas));
}
Gen *aux=Gens.back();
aux->updatePosition(x,y);
this->draw();
cout<<"Elementos -> "<<Gens.size()<<"\n";
}
};
static gboolean
moving(GtkWidget *da, GdkEvent *event, gpointer data)
{
int x, y;
GdkModifierType state;
gdk_window_get_device_position (gdk_event_get_window ((GdkEvent *) event),
gdk_event_get_device ((GdkEvent *) event),
&x, &y, &state);
/*
(void)event; (void)data;
((Gen *)da)->draw();*/
Map *g=(Map *)data;
g->moving(x,y);
}
static gboolean
placing (GtkWidget *da, GdkEvent *event, gpointer data)
{
Map *g=(Map *)data;
g->place();
}
int main ( int argc, char **argv) {
GtkWidget *window;
GtkWidget *canvas;
gtk_init (&argc , &argv);
window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
gtk_widget_set_size_request (window,
500, 500);
g_signal_connect (window, "destroy",
G_CALLBACK (gtk_main_quit) , NULL);
canvas = gtk_drawing_area_new ();
Map *g=new Map(window,canvas);
gtk_container_add (GTK_CONTAINER (window), canvas);
gtk_widget_set_events(window, GDK_POINTER_MOTION_MASK);
//g_signal_connect (canvas, "draw", G_CALLBACK (drawing), (gpointer *)g);
g_signal_connect (window, "motion-notify-event", G_CALLBACK (moving), (gpointer *)g);
g_signal_connect (window, "button-press-event", G_CALLBACK (placing), (gpointer *)g);
//g_signal_connect (canvas, "motion-notify-event", (GCallback) on_window_draw, NULL);
gtk_widget_set_app_paintable(canvas, TRUE);
gtk_widget_show_all (window);
gtk_main ();
return 0;
}
Several remarks here:
Gen::pix is allocated each time a Gen object is created. It's the same pixbuf, but you create one, then another one when using the scaling function (meaning you're leaking memory of the original pixbuf), and this for each Gen object. This is really unefficient, so using a static pix member, loading then scaling the pixbuf and fixing the memory leak would allow you to do this only once.
Then: you're calling gdk_cairo_create in the draw handler, but since GTK 3, you're supposed to get the cairo context as an input parameter in the draw signal callback. I see you're calling a custom draw method through the motion events, that's not how the GTK+ drawing stack works!
To do it right:
in the main, connect to the draw signal of the GtkDrawingArea
in your motion callbacks, just change positions of the Gen objects, and call gtk_widget_queue_draw for the drawing area. This will fire the draw signal for you.
in the callback connected to the draw signal, you then redraw your Gen objects in the cairo context you're given.
to improve performance, you can use the cairo clipping functions, or call gtk_widget_queue_draw_area or gtk_widget_queue_draw_region instead of gtk_widget_queue_draw. You'll get then a pre-computed clipping region in the cairo context you'll receive in your draw callback. With those hints, you can determine exactly what part of the image needs to be redrawn, and avoid unnecessary work.
Please read The GTK+ Drawing Model in the official documentation.

Dereferencing a String Vector in C++ when passing to GTK callback

I have this code which is trying to pass a vector to a callback function:
static void displayvecchoices( GtkWidget *widget, gpointer data ) {
std::vector<std::string> vecp = *(std::vector<std::string> *)(data);
std::cout<<"vec: "<<vecp[0]<<std::endl;
}
int main(int argc, char *argv[] ) {
GtkWidget *window;
GtkWidget *display;
gtk_init(&argc, &argv);
window = gtk_dialog_new ();
g_signal_connect (window, "destroy", G_CALLBACK (destroy), NULL);
gtk_window_fullscreen (GTK_WINDOW(window));
std::vector<std::string> vec;
vec.push_back("1");
display = gtk_button_new_with_label ("Display");
g_signal_connect_swapped (display, "clicked", G_CALLBACK (displayvecchoices), &vec);
gtk_widget_set_can_default (display, TRUE);
gtk_box_pack_start (GTK_BOX (GTK_DIALOG (window)->action_area), display, TRUE, TRUE, 0);
gtk_widget_grab_default (display);
gtk_widget_show (display);
gtk_main();
return 0;
}
When the button display is clicked, I get an error of 'bad alloc' and the program crashes.
When I try this:
std::vector<std::string>* vecp = (std::vector<std::string> *)(data);
std::cout<<"vec: "<<(*vecp)[0]<<std::endl;
It prints out vec: but no element and the size is said to be 18446744073706420840 of the vector.
It looks, your callback arguments are swapped:
you should either use g_signal_connect_object instead of g_signal_connect_swapped or
change displayvecchoices declaration to
static void displayvecchoices(gpointer data, GtkWidget *widget)