Hi, I haven't read the linked program, but based on the dummy program and the previous discussion:
Le 26/08/2012 19:13, Frank Cox a écrit : > On Sun, 26 Aug 2012 17:36:01 +0700 > Ardhan Madras wrote: > > [...] > > The objective of the program is to maintain an email mailing list for my > theatre. I have a webpage here > http://www.melvilletheatre.com/mailinglist.html where interested people can > sign up to receive an email from me every time I have a new movie booked. I > wrote a program a few years ago using ncurses to manage the mailing list and > send out the email and now that I want to learn to use GTK I decided to > rewrite > that mailing list program as a learning project. A GUI program should never block the GUI thread. Blocking the GUI thread has many kind of unwanted implications, all resulting of the GUI unresponsiveness. It's even worse than with a "CLI GUI" like ncurse, because generally in "real" GUI, the GUI is responsible of drawing itself and may need to do that at *any given time* -- e.g. after its window have been brought back from behind another window. If the GUI doesn't refresh at this point, the user sees a blank window [1]. Anyway, what to remember is "never block GUI thread". If you need to do something that take a noticeable amount of time (e.g. > ~100ms), don't do that in the GUI thread, or split it in steps taking less than 100ms. The first solution is to split the long work into smaller steps and ask the main loop to run these steps in idle time (see g_idle_add() and friends). This works if you can split your greedy work into small pieces, but not all kind of work splits well like that, and it still blocks for Xms (but in-between, all events are handled normally since we use the main loop dispatcher). The second solution is to use a separate thread to do the work. This may look simpler since you don't have to split up the algorithm, but beware threading. It's probably the right solution if you have a complex algorithm or no control over what takes a long time to proceed, but if you do anything wrong with threading, your program will blow up faster than an A bomb. Note that GTK *is NOT thread-safe!* This basically means you MUST NOT, EVER, call any GTK function from another thread than the one which called gtk_main() (normally the application's main thread). To update the GUI from the worker thread you'll need to "message" the GUI thread in some way. Fortunately, there are some facilities to do that. For example, I attached a dummy program that replicates yours but using threading. I replaced the label with a dummy progress bar just to be a little more realistic (although the progress bar isn't really controlled), but you can very well put whatever widget you want here. A few details: The main() is the same as yours. The button_clicked_handler() is the callback called when the button is clicked (like your subroutine()). What it does it creating the second window, then launching a second thread (worker()) to perform the time-consuming operation. update_progress_in_timeout() is registered as a timeout function (a function called every Xms by the main loop) to update the progress bar every 100ms. This is totally dummy and don't reflect a progress (it pulsates actually), but it shows clearly that the UI thread is responsive. The worker() function is, as said above, launched in a separate thread. It first does the time-consuming operation (here a simple g_usleep() like your sleep() to wait 5 seconds), and then registers an idle function (a function called by the main loop whenever it has nothing more important to do) which will update the UI (worker_finish_in_idle()). This is an useful trick: functions called by the main loop (like idle or timeout functions) are, obviously, launched in the main loop thread (the gtk_main() one then) -- and, as said above, GTK calls MUST be done from the main loop thread. Fortunately, the GLib/GTK main loop is thread-safe, and so you can call g_idle_add()/g_timeout_add() and friends from any thread, using those to "message" the main thread (actually call a function on it). Finally worker_finish_in_idle() stops the progress bar updates and destroys the window. You can also see I used a structure, WorkerData, to pass data to the worker thread and the further. This is needed because I wanted to pass more than a pointer to my various functions and they only expect one pointer argument, and so it allows me to pass as much data as I want. If you only need one pointer, you don't need it, but you'll probably actually need a lot more stuff than what I passed myself. But beware here too: don't access the data in that structure from different threads at the same time, prefer deal with a copy when possible (or use locks, though locks are complex and somewhat evil if they can be avoided). Of course, if for any reason you access any data simultaneously from worker() and an idle or timeout function, you must be aware of the synchronizations issues related to threading like with any other threaded program (as said above, locks or any other mean to prevent concurrent read and writes). Finally, if you can avoid using threads, do so. If you could avoid it and still don't listen to me, you either know what you do and don't need my advices, or you're somewhat crazy :) I hope this somewhat helps. Regards, Colomban [1] This is less true with compositing window manager since those generally keep "screenshots" of the windows and won't loose them if the window doesn't redraws appropriately. It's e.g. how Compiz can "gray out" unresponsive windows.
_______________________________________________ gtk-app-devel-list mailing list gtk-app-devel-list@gnome.org https://mail.gnome.org/mailman/listinfo/gtk-app-devel-list