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

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;
}

Related

Gtk/Gstreamer application freezes on first frame

I'm trying to make an application that streams video through a gtk draw area. The pipeline I'm currently trying to run is videotestsrc ! ximagesink. My problem is, when I try to run my program, it displays videotestsrc, but only as a still image. This is different from running "gst-launch-1.0 videotestsrc ! ximagesink" through a terminal, where the static in the bottom right moves.
Any ideas on what I'm doing wrong?
int main(int argc, char* argv[])
{
Gst::init(argc, argv);
auto app = Gtk::Application::create(argc, argv, "gtkmm.video.sunshine.test");
Program_Window window;
return app->run(window);
}
class Program_Window : public Gtk::Window
{
public:
Program_Window();
virtual ~Program_Window();
protected:
Gtk::DrawingArea* display;
Glib::RefPtr<Gst::Pipeline> playbin;
gulong window_handler;
GstVideoOverlay* overlay;
void on_display_realize();
};
Program_Window::Program_Window()
{
//initialize variables
display = new Gtk::DrawingArea();
window_handler = 0;
//connect realize callback
display->signal_realize().connect( sigc::mem_fun( *this, &Program_Window::on_display_realize ));
//create playbin
playbin = Gst::PlayBin::create("playbin");
//prepare elements for the pipeline
Glib::RefPtr<Gst::Element> source = Gst::ElementFactory::create_element("videotestsrc", "src");
Glib::RefPtr<Gst::Element> sink = Gst::ElementFactory::create_element("ximagesink", "sink");
//add elements to the pipeline
playbin->add(source)->add(sink);
//link elements
source->link(sink);
//prep video overlay interface
overlay = (GstVideoOverlay*) sink->gobj();
//add drawing area to main window
add(*display);
show_all_children();
}
void Program_Window::on_display_realize()
{
//acquire an xwindow pointer to our draw area
window_handler = GDK_WINDOW_XID( display->get_window()->gobj() );
//give xwindow pointer to our pipeline via video overlay interface
gst_video_overlay_set_window_handle(overlay, window_handler);
//start video
playbin->set_state(Gst::STATE_PLAYING);
}
Could be that in the app it is a tad slower causing following frames to miss their clock times and get discarded. Try setting the sync=false option for the video sink and check if it changes anything. Else use GST_DEBUG to get some logs from the pipeline about what is happening.
P.S. When using Gtk consider using gtksink and gtkglsink to make you life easier.
Fixed it. For whatever reason, the program didn't like the way I used playbin. Changing it to a normal Gst::pipeline worked.
//create playbin //in the above code
//playbin = Gst::PlayBin::create("playbin"); //change this
playbin = Gst::Pipeline::create("pipeline"); //to this

Using signal() inside handler function

I have taken this example from GNU library. And I wonder why they call signal() function twice, first time in main() when setting up the signal handler and second time inside handler function itself.
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
/* This flag controls termination of the main loop. */
volatile sig_atomic_t keep_going = 1;
/* The signal handler just clears the flag and re-enables itself. */
void
catch_alarm (int sig)
{
keep_going = 0;
signal (sig, catch_alarm);
}
void
do_stuff (void)
{
puts ("Doing stuff while waiting for alarm....");
}
int
main (void)
{
/* Establish a handler for SIGALRM signals. */
signal (SIGALRM, catch_alarm);
/* Set an alarm to go off in a little while. */
alarm (2);
/* Check the flag once in a while to see when to quit. */
while (keep_going)
do_stuff ();
return EXIT_SUCCESS;
}
Now my code...
void createTimer(long freq_nanosec)
{
timer_t timerid;
struct sigevent sev;
struct itimerspec timerint;
struct sigaction saction;
/* Establish handler for timer signal */
saction.sa_flags = 0;
saction.sa_handler = OnTimer;
sigemptyset(&saction.sa_mask);
sigaddset (&saction.sa_mask, SIGIO);
if (sigaction(SIGALRM, &saction, NULL) == -1) error("sigaction");
else printf("OnTimer handler created\n");
/* Create real time signal */
sev.sigev_notify = SIGEV_SIGNAL;
sev.sigev_signo = SIGALRM;
sev.sigev_value.sival_ptr = &timerid;
if (timer_create(CLOCKID, &sev, &timerid) == -1) error("timer_create");
else printf("timer ID is 0x%lx\n", (long) timerid);
/* Arm the timer */
timerint.it_value.tv_sec = timerint.it_interval.tv_sec =
freq_nanosec / 1000000000;
timerint.it_value.tv_nsec = timerint.it_interval.tv_nsec =
freq_nanosec % 1000000000;
if (timer_settime(timerid, 0, &timerint, NULL) == -1)
error("timer_settime");
else printf("Timer armed\n");
}
From the man page for signal, we see that when a signal arrives:
first either the disposition is reset to SIG_DFL, or the signal is blocked (see Portability below), and then handler is called with argument signum.
So after the signal arrives, further signals will revert to default behavior. In your sample code, the handler is choosing to re-set the signal handler so further signals will be handled in the same manner as the first.
This is noted in the comment for the function catch_alarm in the code you found.
There are two popular versions of signal, which differ in whether the disposition of a signal is reset to the default when the handler is called, and whether a signal is blocked for the duration of its handler's execution.
The standard says those two behaviors are implementation-defined. The first code sample
void
catch_alarm (int sig)
{
keep_going = 0;
signal (sig, catch_alarm);
}
is assuming that the implementation may reset the signal disposition to the default when the handler is called. That's like calling signal(sig, SIG_DFL) in the first line of the handler. You almost never want that, because the next time a SIGALRM signal comes in, the default action is for the program to be killed. So the handler calls signal(sig, catch_alarm) to re-establish itself as the handler.
Your second code sample
saction.sa_flags = 0;
saction.sa_handler = OnTimer;
sigemptyset(&saction.sa_mask);
sigaddset (&saction.sa_mask, SIGIO);
if (sigaction(SIGALRM, &saction, NULL) == -1) error("sigaction");
uses sigaction, which is generally preferred over signal because you can specify exactly the behavior you want. The standard says
new applications should use sigaction() rather than signal().
When .sa_flags has the SA_RESETHAND flag on, the disposition of the signal is reset to the default when the handler starts, just like in (one version of) signal.
But in your case, that flag is off because you set .sa_flags to 0, so you don't need to write any code to re-establish the handler.

goocanvas signal handling corrupted after using a dialog

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;
}

Why is the signal callback data pointer pointing to a junk value when the callback is fired?

Ok, I'm writing a method that creates an entire panel and it's containing contents and adds it to the form. The panels are stored in an array.
Here's the basic idea.
void vscale1Event(GtkWidget *widget, int *vscale_id)
{
int value = gtk_range_get_value(GTK_RANGE(vscale_struct[*vscale_id]->vscale1));
do stuff with this value;
}
void add_vscale_panel(int vscale_id)
{
vscale_struct[vscale_id]->vscale1 = ..... ;
vscale_struct[vscale_id]->vscale2 = ..... ;
add buttons to form;
gtk_signal_connect(GTK_OBJECT(vscale_struct[button_id]), "value_changed", (GtkSignalFunc)vscale1Event, &vscale_id);
gtk_signal_connect(GTK_OBJECT(vscale_struct[button_id]), "value_changed", (GtkSignalFunc)vscale2Event, &vscale_id);
}
int main()
{
for (i = 0; i<n; i++)
{
add_vscale_panel(i);
}
}
The problem I'm having, is that &vscale_id that I'm passing in, later becomes junk (it's value is a junk number around 32000) when I move the scale.
But - the gtk_signal_connect is only being called that once.
Ok, I get that it's probably something to do with the call stack, that bit of memory no longer being reserved.
But I did this same thing earlier for another panel, and it's working fine.
what I've changed - is trying to make things a bit tidier.
The previous version I had all the panels and widgets each in seperate arrays.
eg
GtkWidget **outerPanel;
GtkWidget **innerPanel1;
GtkWidget **vscale1;
whereas this one I'm doing it:
typedef struct
{
GtkWidget **vscale1;
Gtkwidget **vscale2;
} V_Panel;
V_Panel **vscale_struct;
Not bothering putting the panels into arrays or structs - because I figure I don't need to access them later? ( I found that you can 'recycle' labels so I figure panels (h and vboxes), are the same.
Also - an interesting clue - when I run valgrind - it works fine. Some how valgrind changes the way the program uses it's memory.
Any help here?
If you can perhaps explain what's happening when you call gtk_signal_connect. -
Here's my actual code: http://pastebin.com/MGfUihjM
relevant lines are
45, 145, 274, 308, 391
The problem is that your taking the address of a variable on the stack - in this case the parameter to the function. That address in memory is definitely not guaranteed to continue to hold the value you expect it to since it is just part of the stack frame
The correct way to pack your integer value_id into the callback userdata pointer is to use GINT_TO_POINTER and to reverse it using GPOINTER_TO_INT.
So your signal connection would be:
gtk_signal_connect(GTK_OBJECT(vscale_struct[button_id]),
"value_changed",
(GtkSignalFunc)vscale1Event,
GINT_TO_POINTER(value_id));
And in your signal handler would look like:
void vscale1Event(GtkWidget *widget, gpointer userdata)
{
int vscale_id = GPOINTER_TO_INT (userdata);
int value = gtk_range_get_value(GTK_RANGE(vscale_struct[vscale_id]->vscale1));
do stuff with this value;
}

How to determine which model is connected to a treeview given a callback?

In Gtk 2.0 the cell toggled event, for example, passes the cell rendered and the path to the callback. But in order to change the underlying model data I need to know which treeview or which model this event is for. What is the proper way to determine that?
EDIT:
To clarify, the GtkCellRenderer toggled signal calls a callback with 3 parameters: *cell_renderer, *path, user_data. In this callback, how do I retrieve the model that is connected to the treeview that received this signal?
When you connect the "toggled" signal, you can pass the GtkTreeView or the model as the user_data. I'm not an expert on Gtk+ with C programming language :), but I guess the following code will help you:
void cb_toggled (GtkCellRendererToggle *cell_renderer,gchar *path, gpointer user_data)
{
GtkListStore *store = (GtkListStore *) user_data;
...
}
int main(int argc, char **argv)
{
...
g_signal_connect(G_OBJECT(cell_renderer), "toggled", G_CALLBACK(cb_toggled), (gpointer)gtk_tree_view_get_model(treeview));
...
}