How to refresh gtk widget (gtk_widget_queue_draw error) - c++

I have a simple gtk window with one image in it. After making some modification on that image (taken from OpenCV) I want to make window refresh by expose-event. I use gtk2 and it is not possible to change to gtk3. There is no errors but image is not redrawn, the old one still persist.
class TestApp {
public:
GtkWidget *frameWindow;
GInputStream *inStr;
GtkWidget *image;
GdkPixbuf *pixBuff;
cv::Mat *frame;
TestApp(int argc, char *argv[]) : frameWindow(NULL), image(NULL), pixBuff(NULL), inStr(NULL),frame(NULL){
gtk_init(&argc, &argv);
}
int refresh(cv::Mat *f){
frame=f;
int sz = f->dataend - f->datastart;
memcpy((uchar*)gdk_pixbuf_get_pixels(pixBuff),f->datastart,sz);
gtk_widget_queue_draw(frameWindow);
return 0;
}
void imshow(cv::Mat *im){
/* main window */
frame = im;
frameWindow = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_container_set_border_width(GTK_CONTAINER(frameWindow), 1);
gtk_window_set_title(GTK_WINDOW(frameWindow), "image");
gtk_window_set_default_size(GTK_WINDOW(frameWindow), 1280, 720);
gtk_window_set_position(GTK_WINDOW(frameWindow), GTK_WIN_POS_CENTER);
gtk_window_fullscreen(GTK_WINDOW(frameWindow));
pixBuff=gdk_pixbuf_new_from_data((guchar*)im->datastart,GDK_COLORSPACE_RGB,FALSE,8,im->size().width,im->size().height,(im->channels()*im->cols), NULL,NULL);
image = gtk_image_new_from_pixbuf(pixBuff);
gtk_container_add(GTK_CONTAINER(frameWindow), image);
g_object_ref_sink(G_OBJECT(frameWindow));
g_signal_connect(G_OBJECT(frameWindow), "expose_event", G_CALLBACK((void*)exposeCb), (gpointer)this);
g_signal_connect(G_OBJECT(frameWindow), "key_press_event", G_CALLBACK((void*)buttonCb), (gpointer)this);
gtk_widget_realize(frameWindow);
gtk_widget_show_all(frameWindow);
gtk_main();
}
static gboolean buttonCb(GtkWidget *eventBox, GdkEventKey *event, gpointer data){
char c = event->keyval;
switch(c){
case 'q':
gtk_widget_destroy(eventBox);
gtk_main_quit();
break;
}
return true;
}
static gboolean exposeCb(GtkWidget *eventBox, GtkWidget *event, gpointer data){
return false;
}
};
int main (int argc, char *argv[]) {
TestApp gtkObj(argc,argv);
cv::Mat im=cv::imread("colour256.png");
cv::Mat imOld=im.clone();
cv::cvtColor(im,im,CV_BGR2RGB);
gtkObj.imshow(&im);
gtkObj.refresh(&imOld);
return 0;
}
Should I redraw image somehow?

FALSE is correct return value. Returning TRUE means "I have dealt with this event" so no further callbacks for particular event ("signal" in GTK+ parlance) are called, in particular default callback is not called so window is not painted. Returning FALSE doesn't stop other callbacks.
I don't think there's much difference. Id' leave it in refresh in order to avoid doing too much work in callbacks.
Quoted warning means that pointer passed to gtk_widget_queue_draw does not contain a GtkWidget. Maybe it hot corrupt? Maybe refresh is called prior to imshow?

Related

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)

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.

GTK+ Use one handler for multiple widgets

I have a callback function as follows:
void handle(GtkWidget *widget, gpointer data) {...}
Since I have a lot of widgets for this window, I would like to use this callback as the only handler to avoid writing a bunch of small functions. Initially I thought of using an enum that's stored in the UI class which wraps around the window, and then I would test for it as follows:
UIClass::Signal signal = (UIClass::Signal) data;
switch (signal) {
case UIClass::main_button:
// handle
case UIClass::check_box:
...
}
But the compiler refuses the cast in the first line of that snippet.
Is there a standard way to accomplish this? Or was GTK+ designed to have one handler for every widget?
Store a pointer to a widget in class and pass whole object to the handler:
#include <gtk/gtk.h>
#include <iostream>
struct UIClass
{
GtkWidget* widget1, *widget2, *box;
UIClass()
{
widget1 = gtk_button_new_with_label("button1");
widget2 = gtk_button_new_with_label("button2");
box = gtk_hbox_new(true, 10);
gtk_box_pack_start(GTK_BOX(box), widget1, 0 ,0, 1);
gtk_box_pack_start(GTK_BOX(box), widget2, 0 ,0, 1);
}
static void handle(GtkWidget* sender, UIClass* uiClass)
{
if(sender == uiClass->widget1)
std::cout<<"widget1"<<std::endl;
else if(sender == uiClass->widget2)
std::cout<<"widget2"<<std::endl;
else
std::cout<<"something else"<<std::endl;
}
void connect()
{
g_signal_connect(widget1, "clicked", G_CALLBACK(UIClass::handle), this);
g_signal_connect(widget2, "clicked", G_CALLBACK(UIClass::handle), this);
}
};
int main(int argc, char *argv[])
{
gtk_init (&argc, &argv);
GtkWidget* window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
UIClass ui;
ui.connect();
gtk_container_add(GTK_CONTAINER(window), ui.box);
gtk_widget_show_all(window);
gtk_main();
return 0;
}

SFML and GTK+ - GtkFileChooserDialog

I'm writing an app using SFML and I want to use GTK+ to create file chooser dialogs. I have this code:
gtk_init(&argc, &argv);
GtkWidget *dialog;
dialog = gtk_file_chooser_dialog_new ("Open file...", NULL, GTK_FILE_CHOOSER_ACTION_OPEN, GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL, GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT, NULL);
gtk_dialog_run (GTK_DIALOG (dialog));
And the dialog is showing, but it doesn't get destroyed :(
In the gtk_dialog_run documentation there is a note
After gtk_dialog_run() returns, you are responsible for hiding or destroying the dialog if you wish to do so.
So the dialog should not get destroyed automaticaly, the programmer must do it.
EDIT:
The another problem is that you are not running GTK main loop (gtk_main() or its variant) so the GTK can not deal with events necessary to destroy a widget (no part of GTK is running in the time the events are present). The sollution for this is in answer to another question using gtk_idle_add() to invoke function after gtk_main()
is called. In this function the dialog is shown, the result is given to the caller, the dialog is destroyed and gtk_main_quit() is called to terminate GTK main loop.
However, gtk_idle_add() is deprecated in GTK+2.6 and is not present in GTK+3.0, so g_idle_add() should be used instead. Your code could be somethink like
struct fch_result {
gint response;
// other information to return like filename,...
};
static gboolean fch_dialog(gpointer user_data)
{
struct fch_result *result = (struct fch_result *) user_data;
GtkWidget *dialog = gtk_file_chooser_dialog_new ( ... );
result->response = gtk_dialog_run (GTK_DIALOG(dialog));
// now add other information to result
gtk_widget_destroy(dialog);
gtk_main_quit(); // terminate the gtk_main loop called from caller
return FALSE;
}
int main(int argc, char** argv)
{
gtk_init(&argc, &argv);
struct fch_result data;
g_idle_add(fch_dialog, &data);
gtk_main();
// continue with the program
return 0;
}

passing argument/arguments signal to callback on GTK+ in C

Here is my code:
#include <gtk/gtk.h>
G_MODULE_EXPORT void waka(GtkWidget *button, GtkWidget* entry1)
{
printf("%s",gtk_entry_get_text(GTK_ENTRY(entry1)));
}
int main(int argc, char * argv[])
{
GtkWidget *window, *button, *entry;
gtk_init(&argc,&argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
button = gtk_button_new_with_label("hha");
g_signal_connect(G_OBJECT(button),"clicked",G_CALLBACK(waka),entry);
entry = gtk_entry_new();
GtkWidget *vbox;
vbox = gtk_vbox_new(FALSE,2);
gtk_box_pack_start_defaults(GTK_BOX(vbox),button);
gtk_box_pack_start_defaults(GTK_BOX(vbox),entry);
gtk_container_add(GTK_CONTAINER(window),vbox);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
When I launch it, and try to fill in some string in the entry box, and then click the button, it said 'segmentation fault'. What is going on?
Also, the callback only works with one user data argument. How about if I want two or more arguments, what must I do in the callback function, and in the call to g_signal_connect()?
the problem is that you're trying to use pointer to entry before initializing it. I've changed a bit your code to fix this, see if it will work for you:
#include <gtk/gtk.h>
void waka(GtkWidget *button, GtkWidget* entry1)
{
g_print("entry: %s\n", gtk_entry_get_text(GTK_ENTRY(entry1)));
}
int main(int argc, char * argv[])
{
GtkWidget *window, *button, *entry, *vbox;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
button = gtk_button_new_with_label("hha");
entry = gtk_entry_new();
vbox = gtk_vbox_new(FALSE, 2);
gtk_box_pack_start_defaults(GTK_BOX(vbox),button);
gtk_box_pack_start_defaults(GTK_BOX(vbox),entry);
gtk_container_add(GTK_CONTAINER(window),vbox);
g_signal_connect(G_OBJECT(button), "clicked", G_CALLBACK(waka), entry);
gtk_widget_show_all(window);
gtk_main();
return 0;
}
hope this helps, regards