I have a program (written in C, currently running on Mac OS X 10.8.4) that simulates a world and updates the world's state multiple times per second.
At startup this program creates a background thread which creates a simple GTK window and calls gtk_main.
After each time the world's state is updated (in the main thread), I would like the window to reflect that change. My first approach was to simply update the GTK widgets after the world was updated, but because that was in a different thread things broke quite messily.
Is there some sort of mechanism where the main thread can update some state on the graphics thread and then queue an event that prompts the graphics thread to redraw? I.e.
void draw() {
// This can only be called from the graphics thread.
gtk_label_set(GTK_LABEL(label1), "some state");
}
// This causes draw() to be called on the graphics thread.
gtk_please_redraw_this_thing_on_the_graphics_thread();
Is there any way to do this? Or any tutorials that cover it?
Turns out this is quite simple.
First create the function that will do the drawing (on the graphics thread):
gboolean draw_function(GtkWidget *w, GdkEventExpose *event) {
// Draw things.
return TRUE;
}
Then, during set up, connect the draw event of your widget to your draw function:
g_signal_connect(G_OBJECT(some_widget), "draw", G_CALLBACK(draw_function), NULL);
Finally, to force the widget to redraw from another thread you can call:
gtk_widget_queue_draw(some_widget);
Related
I'm working on a library that creates transparent X windows and uses cairo to draw on them. There is an event loop implemented in the main thread, while the drawing operations take place in a separate thread within a loop. The latter looks like this
while (self->_running) {
PyGILState_Release(gstate);
usleep(1000); // Sleep 1 ms
gstate = PyGILState_Ensure();
if (self->_expiry <= gettime()) {
draw(self, args_tuple); // All the cairo code is in here
self->_expiry += interval;
interval = self->interval;
}
}
The event loop calls XNextEvent periodically to trap key/button presses only. The window is mapped before the new UI thread is started from the main thread.
When the interval between iterations on the UI thread (the self->inteval value above) is large (order of seconds), the window stays transparent on the first iteration of the loop, and it only gets painted on from the second iteration onward. Calling draw right before the while loop doesn't help, unless there is a pause of some milliseconds in between calls to draw. For example, if I put interval = 25 right before the while loop, then the second call to draw paints on the window in most of the executions of the application implementing this code.
Things that I have tried:
cairo_surface_flush and XFlush right after draw don't seem to work
Sending an Expose event doesn't seem to help either.
How can I make sure that my loop starts painting on the window from the first iteration?
What I'm missing is the ExposureMask flag in the call to XSelectInput. With this flag set, one then has to look for Expose events in the event loop with the following pattern:
switch (e.type) {
case Expose:
if (e.xexpose.count == 0) {
BaseCanvas__redraw(canvas);
}
return;
}
The redraw operation doesn't need to be the full set of draw operations. Having modified the cairo context, it is enough to repaint it on the destination with no more than this
void
BaseCanvas__redraw(BaseCanvas * self) {
cairo_save(self->context);
cairo_set_operator(self->context, CAIRO_OPERATOR_SOURCE);
cairo_paint(self->context);
cairo_restore(self->context);
}
To me it looks like you are mapping the window and then immediately starting a thread that tries to draw to the window. Thus, if you try to draw before the window manager actually made the window visible, your drawing goes nowhere. If the window manager wins the race and the window actually becomes visible before your draw, the drawing actually works.
Your own answer is not an answer to your question. The question is "why does it stay transparent in the first iteration?" while your answer is (basically) "Don't use threads, just do all drawing in the main loop".
(Of course, handling Expose events is the right thing to do, but that's not what the question asked.)
I am trying to create a library where it's possible to create a OpenGL context with GTK3 which gets rendered automatically or with 1 function call (something similar to a swap-buffers function). I was looking at the source code of GtkGLArea, which seems to have the following order of execution:
A new GtkGLArea is created with the superclass GtkWidget.
When the realize signal of the superclass is captured, a GdkGLContext
is created and a realize signal is given to the user. The user is supposed to put the initial OpenGL functions in a function passed to this signal.
The draw signal of the superclass is also captured, some OpenGL initialization code is called for binding the framebuffer (or texture) and the renderbuffer. The buffers are created if they don't exist. After this the render signal is given where the user is supposed to put the rendering OpenGL code. Finally the gdk_cairo_draw_from_gl function is called to draw the renderbuffer (or texture) on the Cairo context of the superclass passed through the draw signal.
For the user it basically comes down to this:
void realize(GtkGLarea *area){
// OpenGL initialization functions
}
gboolean render(GtkGLArea *area, GdkGLContext *context){
// OpenGL rendering functions
return TRUE;
}
// Init functions, setup a GTK window
GtkWidget *gl_area = gtk_gl_area_new();
g_signal_connect(gl_area, "realize", G_CALLBACK(realize), NULL);
g_signal_connect(gl_area, "render", G_CALLBACK(render), NULL);
// Add gl_area to the window and display the window
But that's not what I want, what I want is this:
// Init functions, setup a GTK window
// Setup a OpenGL context on the GTK window
// OpenGL initialization functions
while(1){ // Main loop
if(gtk_events_pending()){
gtk_main_iteration();
}
// OpenGL rendering functions
swapBuffers();
}
What would be the best way to achieve this? I tried this (unsuccessfully) by simulating the functions called around the signals, and by using a GdkGLContext created on a GdkWindow taken from a GtkDrawingArea.
These are the options which I can imagine as solutions now:
Create a custom GTK3 class extending GtkWidget and using GdkGLContext and somehow track when the drawing signal is called so the appropriate OpenGL functions are called around that signal.
Find a way to ignore the whole GTK3 part and try to use the underlying Xlib functions to create a OpenGL context through X11 (which isn't portable at all).
Use a evil hack with threads and setjmp and longjmp to enter and exit the signals from the swapBuffers function.
None of these "solutions" are really appealing to me, is the thing I want impossible, or am I just missing some information?
I will keep it simple
I have the function inside_thread that runs a while loop and calls the function update_progressbar that simply updates a GtkStatusBar.
I am calling inside_thread in a thread using g_thread_new("processing", GThreadFunc, NULL); from a callback function on_starter_clicked.
It tends to do the job, it works fast and it updates the status bar according to the progress of the while loop.
The problem is that sometimes (at unspecified place) the progress bar gets stuck and it gets unstuck if I mouseenter or mouseleave a button in the application. Very strange behavior I must admit
What could be wrong?
No, you can't update the GtkProgressBar from another thread. There are two solutions to your problem, and both involve gdk_threads_idle_add():
1. Wait for the Idle Callback
In this case,your thread would schedule the progress bar update with gdk_threads_add_idle(), then wait for it to finish. I don't know what the best way to do this with GLib is, but there's probably something you can do (GMutex?). The idea is this:
gboolean updateProgressBar(gpointer data)
{
gtk_progress_bar_set_fraction(progressbar, value);
tellOtherThreadToContinue();
return G_SOURCE_REMOVE; // == FALSE
}
gpointer otherThread(gpointer data)
{
while (condition) {
doStep();
gdk_threads_idle_add(updateProgressBar, NULL);
waitForProgressBarToBeUpdated();
}
return (gpointer) 0;
}
2. Forego the Thread Entirely and Loop the Idle Callback
If the idle callback returns G_SOURCE_CONTINUE (== TRUE), GTK+ will schedule your function again for the next idle period. You can take advantage of this to rewrite your while loop as an idle callback:
gboolean loop(gpointer data)
{
if (condition)
return G_SOURCE_REMOVE;
doStep();
gtk_progress_bar_set_fraction(progressbar, value);
return G_SOURCE_CONTINUE;
}
// in your code, start the loop going with
gdk_threads_idle_add(loop, NULL);
Regardless of what you choose, you can take advantage of the data parameter to both the idle callback and the thread to pass data those functions would need around. Just be careful not to pass a pointer to a local variable to the idle callback, as that might run after your function returns (and the variable goes out of scope/ceases to exist).
Note that regardless of what you do, you'll be waiting for the progress bar to update (and in the latter case, redraw). If things become too slow, you may want to look into updating the progress bar in chunks. This, however, depends entirely on how many steps you have.
The answer to this question is very specific. Assuming you want to update your progress bar according to the progress of a loop you have to do 3 things.
Create a thread in which you will run your loop
Ensure the loop with debugging and if there is a need for mutexes
[a] Schedule the progress bar update when GTK is able to using gdk_threads_add_idle ()
and do note that this function will call the progress bar updating function with G_PRIORITY_DEFAULT_IDLE and let it be. Otherwise if your curious look at g_idle_add_full ()
[b] Terminate the thread when it is no longer needed (progress 100%)
GTK+ is not multithreading safe. It can not always re-draw components, especially if it is busy, deadlocked in an unspecified iteration of the main-loop. It is a matter of synchronization, which is why you need to schedule the update.
[a] Since you have values to give to the progress bar updating functions in order to do your interval arithmetics, you have to pass them as an array to give to the function.
[b] The other problem occurs at the end of the whole process. Your application will crash unless you ensure to no longer update the progress bar. You can imply a mutex or any sort of restrictions you come up with.
IMPORTANT: This could not be the only reason for crash after the processing. As the other answer clearly states.. you can not just update gtk component through another thread. To omit this, simply call gtk_main_iteration() after the loop, before the thread terminates so it iterates one more time to update the progress bar properly and on time.
A simple exemplification with pseudo-code implementation of the whole thing:
gboolean on_starter_clicked () // callback function
{
g_thread_new("processing", proceed_thread, NULL);
}
gpointer proceed_thread (gpointer data) // processing-thread
{
while(1)
{
// Process whatever needs to be processed
// break when it needs to be breaked
gint data[] = { partial, full };
gdk_threads_add_idle(update_progressbar, data);
}
gtk_main_teration();
}
gboolean update_progressbar (gpointer data) // updating function
{
gint *d = (gint*)data;
gdouble fraction = ( (d[0] * 100.0) / d[1] ); // Calculating your interval
gtk_progress_bar_set_fraction(GTK_PROGRESS_BAR(...), fraction / 100.0 );
}
One more thing. You may notice that there will be a slightly decrease of the movement smoothness of the progress indicator. That because GTK+ schedules the update, which is fully understandable that doesn't mean "immediately".
I have seen a rare amount of programs, having animated window icons.
From a distant vantage point, this could be rather beneficially isn't it..
I took the rudder in the process of inventing one function to do so.
It does what you probably thought of and using Sleep() to simulate intervals after each "frame".
I don't have to say that using WinAPI in major programs is a bad idea as well as the fact that the program will freeze until the function terminates.
Back on track. I am looking for a possibility to avoid this behavior.
It is understandable, hence no one wants a non-functional but take a note WITH ANIMATED ICON program.
You are probably calling your function from the Main Event Loop. This is the thread where all sort of GUI events are handled, including mouse clicks and key presses. If you call Sleep from within this thread, all these processing tasks will be pending and your program will freeze.
To avoid this behavior, use g_timeout_add. This function will call a function at regular intervals from within the Main Event Loop (so no need to do it yourself). You need to define a function like this:
// define this function somewhere
gboolean animate(gpointer user_data) {
// cast user_data to appropriate type
// supposing you have a AnimationParameters class with everything you need...
AnimationParameters* animParams = (AnimationParameters*)user_data;
// update animation
// ...
return TRUE; // return TRUE to call function again, FALSE to stop
}
Elsewhere in your code, to start the animation:
AnimationParameters* animationParameters = new AnimationParameters(...);
g_timeout_add (30, // call every 30 milliseconds
animate,
animationParameters);
Hope it helps.
My code launches a background thread. The background thread makes changes and wants the UI in the main thread to update. The code that launches the thread then waits looks something like:
Thread fThread = new Thread(new ThreadStart(PerformSync));
fThread.IsBackground = true;
fThread.Start();
fThread.Join();
MessageBox.Show("Synchronization complete");
When the background wants to update the UI, it sets a StatusMessage and calls the code below:
static StatusMessage _statusMessage;
public delegate void AddStatusDelegate();
private void AddStatus()
{
AddStatusDelegate methodForUIThread = delegate
{
_statusMessageList.Add(_statusMessage);
};
this.Dispatcher.BeginInvoke(methodForUIThread, System.Windows.Threading.DispatcherPriority.Send);
}
_statusMessageList is an ObservableCollection that is the source for a ListBox.
The AddStatus method is called but the code on the main thread never executes - that is, _statusMessage is not added to _statusMessageList while the thread is executing. However, once it is complete (fThread.Join() returns), all the stacked up calls on the main thread are executed.
But, if I display a message box between the calls to fThread.Start() and fThread.Join(), then the status messages are updated properly.
What do I need to change so that the code in the main thread executes (UI updates) while waiting for the thread to terminate?
Thanks.
fThread.Join causes your main thread to block until the background thread finishes. As long as the main thread is blocked, the UI cannot be updated.
What you need to do is something like this (untested, but you should get the idea):
void someUiEventThatCausesTheBackgroundProcessingToStart() {
Thread fThread = new Thread(new ThreadStart(PerformSync));
fThread.IsBackground = true;
// disable some UI components here, so that the user cannot
// start the thread a second time
...
fThread.Start();
// *don't* call Thread.Join here, so that your main thread does not block!
}
void PerformSync() {
try {
// do your stuff here
...
} finally {
Dispatcher.Invoke(new Action(ProcessingDone));
}
}
void ProcessingDone() {
// re-enable the UI components
...
MessageBox.Show("Synchronization complete");
}
Of course, in WPF, disabling/enabling UI components is ideally done using some IsBusyProcessing dependency property which is bound with a trigger to the UI elements in question, but this is another story...
EDIT: As another option, you might want to check out the BackgroundWorker class, which contains ProgressChanged and RunWorkerCompleted events that automatically fire in the main thread.
fThread.IsBackground = true;
fThread.Start();
fThread.Join();
The call to Join() is blocking your main thread until the background thread finishes. So the UI thread can't do anything while the other thread is running. You need to not call Join(). If you need to do something on the main thread after the background thread finishes, find another way to do it.
EDIT: The reason it works if you display a message box between the call to Start and Join is because then the main thread is running the message loop for the message box, instead of being blocked at the Join call.
The BackgroundWorker is the right tool for this job. It's no more complicated than is absolutely necessary, gives you all of the hooks that you need for progress reporting, and there's plenty of information available (such as this answer) on how to use it.