goocanvas signal handling corrupted after using a dialog - gtk

I use a goocanvas and use signals for mouse events connected to some graphical items. If I use a dialog from a signal handler, all signals are broken after closing the dialog. Is this a bug of gtkmm/goocanvas or is this some kind of misuse in my program?
The wrong behavior is:
You can click somewhere in the canvas area, nothing is happen which is correct. If you click on the circle the signal handler starts the dialog which is also expected. After closing the dialog ( OK button ) you can click somewhere on the canvas and the signal handler is called which is wrong.
In my real program the signal handlers are sometimes never called and sometimes called on wrong areas and so on. A bit strange behavior. I hope someone can find the problem.
#include <gtkmm.h>
#include <goocanvasmm.h>
#include <sigc++/sigc++.h>
bool ShowDialog( const Glib::RefPtr<Goocanvas::Item>& item, GdkEventButton* ev)
{
enum { OK };
Gtk::Dialog dialog;
dialog.add_button( Gtk::Stock::OK, OK);
dialog.show_all_children();
dialog.run();
return false;
}
int main(int argc, char* argv[])
{
Gtk::Main app(&argc, &argv);
Goocanvas::init("example", "0.1", argc, argv);
Gtk::Window win;
Goocanvas::Canvas m_canvas;
m_canvas.set_size_request(640, 480);
m_canvas.set_bounds(0, 0, 800, 800);
Glib::RefPtr<Goocanvas::Item> root = m_canvas.get_root_item();
Glib::RefPtr<Goocanvas::Ellipse> outer = Goocanvas::Ellipse::create( 100,100,20,20);
outer->property_line_width() = 5;
outer->property_stroke_color() = "red";
outer->property_fill_color()="blue";
root->add_child( outer );
sigc::connection conn2= outer->signal_button_press_event().connect( sigc::ptr_fun(&ShowDialog));
win.add(m_canvas);
win.show_all_children();
Gtk::Main::run(win);
return 0;
}

Related

Why doesn't my GtkBox update until I resize the whole window?

When I attach a menu to a window I detach the existing root control, add a vertical GtkBox to hold the menu and the root control and then attach that box to the GtkWindow, like so:
Gtk::GtkWidget *menubar = GTK_WIDGET(Info.obj);
Wnd->_VBox = Gtk::gtk_box_new(Gtk::GTK_ORIENTATION_VERTICAL, 0);
Gtk::GtkBox *vbox = GTK_BOX(Wnd->_VBox);
Gtk::GtkContainer *wndcontainer = GTK_CONTAINER(Wnd->Wnd);
g_object_ref(Wnd->_Root);
gtk_container_remove(wndcontainer, Wnd->_Root);
gtk_box_pack_start(vbox, menubar, false, false, 0);
gtk_box_pack_end(vbox, Wnd->_Root, true, true, 0);
gtk_container_add(wndcontainer, Wnd->_VBox);
gtk_widget_show_all(GTK_WIDGET(Wnd->Wnd));
g_object_unref(Wnd->_Root);
gtk_window_add_accel_group(Wnd->Wnd, AccelGrp);
In practice it looks like this:
What I'd like is the menu to appear in the correct place automatically without having to resize the window to force a layout update.
I've tried calling gtk_widget_queue_draw on the window but that made no difference. Am I doing something wrong here? Can a call an extra function to invalidate the layout and get it to refresh?
It's difficult to answer without having a minimal reproducible example. Your code in not even plain GTK or gtkmm... it seems to be some exotic variant between the two.
Here is my attempt: I tried to be as close as possible to your code. The issue you are describing is not present though.
/* gcc -o test test.c $(pkg-config --cflags --libs gtk+-3.0) */
#include <gtk/gtk.h>
static GMenu *
menu_model(void)
{
GMenu *menu = g_menu_new();
g_menu_append(menu, "File", NULL);
g_menu_append(menu, "Edit", NULL);
g_menu_append(menu, "Project", NULL);
/* ... */
return menu;
}
int main(int argc, char **argv)
{
GtkWidget *window;
GMenuModel *model;
GtkWidget *menubar;
GtkWidget *content;
GtkWidget *vbox;
gtk_init(&argc, &argv);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
model = G_MENU_MODEL(menu_model());
menubar = gtk_menu_bar_new_from_model(model);
g_object_unref(model);
content = gtk_label_new("Some content here");
vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 0);
gtk_box_pack_end(GTK_BOX(vbox), content, TRUE, TRUE, 0);
gtk_container_add(GTK_CONTAINER(window), vbox);
gtk_widget_show_all(GTK_WIDGET(window));
gtk_main();
return 0;
}
So while it should "just work" out of the box. And indeed on my Raspberry Pi the code does just do what it's supposed to do, this is still an issue on my Ubuntu 18 VM. I have found somewhat of a work around to kick the GtkBox into reconfiguring the child widget's layout:
GdkRectangle allocation = Wnd->GetClient();
g_signal_emit_by_name(G_OBJECT(vbox), "size-allocate", GTK_WIDGET(vbox), &allocation, NULL, NULL);
The menu now appears in the right location automatically. It seems like a "hack" that may stop working in the future or crash on some systems? IDK. But in terms of right now and Ubuntu 18, I don't have anything better.

How to avoid blocking function g_application_run

I have an application with the following pattern (it's an embedded application) that I don't want to change:
void
main(int argc, char *argv[])
{
some_init_functions();
while(1) {
some_functions();
}
}
I'd like to simulate the application on Windows so I'd like to use Gtk+3 to show a window that simulate the display used in the original application.
The problem is the function g_application_run() that is blocking.
Is it possible to manually call a Gtk+/GLib function to process pending events only and returns immediately? I would call this function in the while(1) loop.
I read about gtk_main_iteration_do(), but I didn't understand how to use it without calling g_application_run().
It's your job to integrate your application in the GUI, not the other way around. What you should do is call g_application_run, and add an event source with g_timeout_add or g_idle_add that will call your callback. In that callback, just call some_functions() once. The GTK main loop will take care of calling it again and again.
It is possible to get out of the g_application_run loop. Events can be handled by g_main_context_iteration(GMainContext *context, gboolean may_block). With may_block = TRUE, it will only return to the main loop after an event has been handled. With may_block = FALSE, only the highest priority events will be handled after which control returns to the loop.
The code below is based upon the example-0.c code from the GTK Reference Manual, yet making the process flow from g_application_run explicit. Note that command line arguments are not processed, and several checks are omitted.
A timeout event is added such that, in combination with may_block = TRUE, the main loop advances at least once per second. Also, a callback is added on the destroy event, such that, in combination with a global variable, the main loop can stop if the window is closed.
As a direct answer to the question asked: you would need to make sure correct set-up and break-down is performed, and add g_main_context_iteration (context, may_block); to your while (1) loop.
#include <gtk/gtk.h>
static bool application_running; // As use_count is private
// Add close_window event watcher to tell our application loop we should stop
static void
close_window (void)
{
printf("Window is closed\n");
application_running = FALSE;
}
// Add timeout to make sure the application loop is unblocked once per second
static gboolean
timeout (gpointer data)
{
printf("timeout\n");
return true;
}
static void
activate (GtkApplication* app,
gpointer user_data)
{
GtkWidget *window;
window = gtk_application_window_new (app);
// Add destroy call-back so we know when window is closed.
g_signal_connect (window, "destroy", G_CALLBACK (close_window), NULL);
gtk_window_set_title (GTK_WINDOW (window), "Window");
gtk_window_set_default_size (GTK_WINDOW (window), 200, 200);
gtk_widget_show (window);
}
int
main (int argc,
char **argv)
{
GtkApplication *app;
int status;
app = gtk_application_new ("org.gtk.example", G_APPLICATION_FLAGS_NONE);
g_signal_connect (app, "activate", G_CALLBACK (activate), NULL);
g_timeout_add_seconds (1, timeout, NULL);
// **** begin g_application_run alternative ****
// Setup
GMainContext *context;
gboolean acquired_context;
context = g_main_context_default ();
acquired_context = g_main_context_acquire (context);
g_return_val_if_fail (acquired_context, 0);
GError *error = NULL;
if (!g_application_register (G_APPLICATION (app), NULL, &error))
{
g_printerr ("Failed to register: %s\n", error->message);
g_error_free (error);
return 1;
}
g_application_activate (G_APPLICATION (app));
// Main event loop
application_running = true;
int loop = 0;
while (application_running) {
bool may_block = TRUE;
// may_block = TRUE: g_main_context_iteration blocks execution if no events are coming
// may_block = FALSE: g_main_context_iterations processes pending events and continues
g_main_context_iteration (context, may_block);
printf("Loop %i\n", loop++);
}
// Release
g_settings_sync ();
while (g_main_context_iteration (context, FALSE))
;
g_main_context_release (context);
// **** end g_application_run alternative ****
g_object_unref (app);
return status;
}

blank dialogue box without my text nor OK button

I'm trying to popup an OK message box in my gtk browser, but all I'm getting is blank grey square patch without my text in it and even button is not visible.
PFB the function which I'm using to render message box :
Void DisplayOKPopup()
{
dialogue=gtk_message_dialogue_new(GTK_WINDOW(WebtBrowserWindow),
GTK_DIALOGUE_DESTROY_WITH_PARENT,
GTK_MESSAGE_INFO,
GTK_BUTTONS_OK,
"text message");
gtk_widget_show(dialogue);
LOGDEBUG(" 1");
gtk_dialogue_run(GTK_DIALOGUE (dialogue));
LOGDEBUG("2");
gtk_widget_destroy (dialogue);
}
As per my debug log, I can see that control is passing till LOGDEBUG("1") and after that it goes to gtk_dialogue_run after that UI is getting crashed, the line next to run i.e LOGDEBUG("2") is not getting executed.
Kindly provide your inputs as I'm working on this since 3 days:!
As concluded, the problem arises from the use of threads. There are several approaches to solve the issue but since there is not code, I'll try with a simple example so that you can recreate it on your code.
Take a global Boolean variable as a flag and on your thread, set it to true so that a "periodic" idle callback can check it and if the flag is TRUE then show the dialog. The logic is that the Gtk UI functions are called from the mainloop/main thread and not from the worker threads.
DisplayOkPopup just has a simple "counter" from 0 to MAX_INT (32 bits) and set's the global flag as TRUE.
check_for_dialog it's a callback that runs on mainloop idle time and check for the flag, if TRUE then runs the dialog.
exit is dirty and will output errors but the goal ain't that, its just a tip/hint for your solution.
Example:
#include <gtk/gtk.h>
gboolean dialog_active;
gboolean show_dialog_popup;
GtkWidget *window;
gpointer DisplayOKPopup (gpointer user_data) {
int i;
while (TRUE) {
for (i = 0; i < G_MAXINT32; i++) {
// nop
}
show_dialog_popup = TRUE;
}
return NULL;
}
gboolean check_for_dialog (gpointer user_data) {
if (show_dialog_popup == TRUE && dialog_active == FALSE) {
dialog_active = TRUE;
GtkWidget *dialog = gtk_message_dialog_new(GTK_WINDOW(window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_INFO,
GTK_BUTTONS_OK,
"text message");
gtk_dialog_run(GTK_DIALOG (dialog));
show_dialog_popup = FALSE;
dialog_active = FALSE;
gtk_widget_destroy(dialog);
}
return TRUE;
}
static void app_activate(GtkApplication *app, gpointer user_data) {
window = gtk_application_window_new(app);
gtk_window_set_title(GTK_WINDOW(window), "Window Title Here");
gtk_window_set_default_size(GTK_WINDOW(window), 700, 400);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
//gtk_container_add(GTK_CONTAINER(window), fixed);
gtk_widget_show_all(window);
g_idle_add(check_for_dialog, NULL);
g_thread_new("my thread", DisplayOKPopup, NULL);
}
int main(int argc, char **argv) {
GtkApplication *app;
int status;
show_dialog_popup = FALSE;
dialog_active = FALSE;
app = gtk_application_new("your.application.id", G_APPLICATION_FLAGS_NONE);
g_signal_connect(app, "activate", G_CALLBACK(app_activate), NULL);
status = g_application_run(G_APPLICATION(app), argc, argv);
g_object_unref(app);
return status;
}
Build and run:
$ gcc -o test test.c `pkg-config --cflags --libs gtk+-3.0`
$ ./test
Result:
EDIT:
Answer to your comment is:
To have custom buttons on your dialog then use:
gtk_dialog_new_with_buttons
gtk_dialog_add_button (if dialog exists, will add 1 button)
gtk_dialog_add_buttons (same as above but can add many buttons)
Example for 1):
dialog = gtk_dialog_new_with_buttons ("My dialog",
main_app_window,
flags,
"_OK",
GTK_RESPONSE_ACCEPT,
"_Cancel",
GTK_RESPONSE_REJECT,
NULL);
Example for 2):
gtk_dialog_add_button (GTK_DIALOG(dialog),
"This is my button",
GTK_RESPONSE_ACCEPT);
Example for 3) is same as 2) but can handle many buttons and terminates with NULL.

How to get the end of continuous value update for Gtk Spinbox?

I'm using Gtk2 to make a small tool, it works like this:
Several Scales and Spinboxes control parameters of an algorithm.
When parameter changes, the algorithm will execute, and the updated result is rendered as a picture, shown in UI.
As the algorithm's workload is heavy, I don't want it run frequently during frequent parameter change. Specifically, during Scales are dragged or Spinbox's arrows buttons are pressed. Instead, I want the algorithm to be run "after" users have determined the parameters.
Currently, I listened the button-release event of the Scales, so the algorithm will run only on Scale dragging is done. However, this not fit for the Spinboxes, as they have separate entry and button sub-area. If I listen to Spinbox's button-release, it would behave weirdly.
So what event (or events) should I listen to obtain the occation that a continuous value update is finished for a Spinbox?
Could I see the code you have? The button-release works great for me. However, depending on your algorithm, you may be getting 'feedback'. Are you sure the rest of the code is not updating your spinbutton in some way?
I'd use a deferred computation, independent from the device you use to modify the data. In this way you can also input the numbers with the keyboard or copy and paste their content and the program will still work as expected.
A way to do this in GTK+ is by leveraging the main loop and using a timeout GSource, e.g.:
#include <gtk/gtk.h>
typedef struct {
guint event;
GSourceFunc callback;
GtkWidget *spin_button;
} Algorithm;
static gboolean your_callback(Algorithm *algorithm)
{
g_print("Your heavy computations go here...\n");
/* ... */
algorithm->event = 0;
return FALSE;
}
static void postpone(Algorithm *algorithm)
{
if (algorithm->event > 0) {
g_source_remove(algorithm->event);
}
/* Default delay is 1 second (1000 milliseconds) */
algorithm->event = g_timeout_add(1000, algorithm->callback, algorithm);
}
int main(int argc, char **argv)
{
GtkWidget *window, *spin_button;
Algorithm algorithm;
gtk_init(&argc, &argv);
spin_button = gtk_spin_button_new_with_range(0, 100, 0.1);
g_signal_connect_swapped(spin_button, "value-changed",
G_CALLBACK(postpone), &algorithm);
window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_container_add(GTK_CONTAINER(window), spin_button);
algorithm.event = 0;
algorithm.callback = (GSourceFunc) your_callback;
algorithm.spin_button = spin_button;
gtk_widget_show_all(window);
gtk_main();
return 0;
}

How do I send a client-event asynchronously to a GtkWidget?

I'm trying to send and receive a client-event using a GtkWidget on the win32 platform. The sending code looks like this:
GtkWidget *Wnd;
GdkNativeWindow Hnd =
#ifdef WIN32
GDK_WINDOW_HWND(Wnd->window);
#else
GDK_WINDOW_XWINDOW(Wnd->window);
#endif
GdkEvent *Event = gdk_event_new(GDK_CLIENT_EVENT);
// fill out Event params
gdk_event_send_client_message(Event, Hnd);
Receiving code looks like this:
static gboolean MyClientEvent(GtkWidget *widget, GdkEventClient *ev, MyWnd *Wnd)
{
// breakpoint here...
return TRUE;
}
GtkWidget *Wnd = gtk_window_new(GTK_WINDOW_TOPLEVEL);
g_signal_connect( G_OBJECT(Wnd),
"client-event",
G_CALLBACK(MyClientEvent),
this);
gtk_widget_add_events(Wnd, GDK_ALL_EVENTS_MASK);
I used Spy++ to see the message getting sent, so I know the sending side is ok. The receiving side however doesn't get the client-event. I was expecting my breakpoint in the callback to trigger... but it doesn't.
I'm not even sure if a GtkWindow can receive a client-event... from past experience on X11 I thought it was pretty much the same as any other GtkWidget in that respect. Maybe on the win32 platform it's kinda different. But still I'd like to be able to get this working.
I would like this to work with asynchronously, and in a thread-safe fashion, so that I can send events from worker threads up to the GUI thread.
I have a solution that seems to work. It may not be optimal but it's in the ball park.
struct GlibEventParams
{
GtkWidget *w;
GdkEvent *e;
};
static gboolean
GlibPostMessage(GlibEventParams *p)
{
GDK_THREADS_ENTER ();
gtk_propagate_event(p->w, p->e);
gdk_event_free(p->e);
delete p;
GDK_THREADS_LEAVE ();
return FALSE;
}
bool MySendEvent(GtkWidget *Wnd, GtkEvent *Event)
{
bool Status = false;
if (Event && Wnd)
{
GlibEventParams *p = new GlibEventParams;
p->w = Wnd;
p->e = gdk_event_new(GDK_CLIENT_EVENT);
*p->e = *Event;
Status = g_idle_add((GSourceFunc)GlibPostMessage, p) > 0;
}
else assert(!"No Event or Wnd");
return Status;
}
If someone else has constructive comments I'll add/modify this as required.