GTK+ Forums

Discussion forum for GTK+ and Programming. Ask questions, troubleshoot problems, view and post example code, or express your opinions.
It is currently Mon Sep 01, 2014 7:32 am

All times are UTC




Post new topic Reply to topic  [ 8 posts ] 
Author Message
 Post subject: Display a dialog while executing some code at startup
PostPosted: Wed Jun 05, 2013 8:48 am 
Offline
Familiar Face

Joined: Wed Jun 05, 2013 7:39 am
Posts: 6
Location: France
Hello,

I'd like to display a message dialog (a kind of "Please wait" with additional information) while executing some code that may take a long time...
My problem is that I want this code to be executed when the application starts, not on user action.

Since GTK main loop must be running to display the dialog, I tried to execute this code in a thread.
I also tried to use gobject.idle_add to wait for main loop to start before executing the code, although I'm not sure this is the meaning of this function.

But nothing is displayed until the end of the thread.
The window content ("My application") is not visible.
The message dialog content is not visible.
NB: If I remove message_dialog.destroy(), the message dialog becomes visible at the end of the thread - which is too late of course.

The code is writtent in Python (2.7.3)
PyGTK version 2.24.0
GTK version 2.24.10

Code:
#!/usr/bin/env python

import gobject
import gtk
from threading import Thread
import time

def _destroy(widget, data=None):
    gtk.main_quit()

def _thread():
    gobject.idle_add(_thread_actions)

def _thread_actions():
    message_dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
    message_dialog.set_markup('Please wait...')
    message_dialog.show_all()

    gtk.gdk.threads_enter()
    message_dialog.set_markup('Step 1')
    gtk.gdk.threads_leave()
    time.sleep(1)
    gtk.gdk.threads_enter()
    message_dialog.set_markup('Step 2')
    gtk.gdk.threads_leave()
    time.sleep(1)
    message_dialog.destroy()

window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.connect('destroy', _destroy)

vbox = gtk.VBox(False)
label = gtk.Label()
label.set_text('My application')
vbox.pack_start(label, False)
window.add(vbox)
window.maximize()
window.set_title('test')
window.show_all()

gtk.gdk.threads_init()

thread = Thread(None, _thread, 'my thread')
thread.start()

gtk.main()


Can anyone tell me what is wrong, and what I should have done to achieve this?
Thanks.


Top
 Profile  
 
 Post subject: Re: Display a dialog while executing some code at startup
PostPosted: Thu Jun 06, 2013 11:02 am 
Offline
Never Seen the Sunlight

Joined: Mon Apr 28, 2008 5:52 am
Posts: 745
Location: UK
Hello and welcome,

Below is the corrected version of your code.
Code:
#!/usr/bin/env python

import gobject
import gtk
from threading import Thread
import time

def _destroy(widget, data=None):
    gtk.main_quit()

def _thread():
    time.sleep(1)
    gtk.gdk.threads_enter()
    message_dialog.set_markup('Step 1')
    gtk.gdk.threads_leave()
    time.sleep(1)
    gtk.gdk.threads_enter()
    message_dialog.set_markup('Step 2')
    gtk.gdk.threads_leave()
    time.sleep(1)
    gtk.gdk.threads_enter()
    message_dialog.destroy()
    gtk.gdk.threads_leave()

window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.connect('destroy', _destroy)

vbox = gtk.VBox(False)
label = gtk.Label()
label.set_text('My application')
vbox.pack_start(label, False)
window.add(vbox)
window.set_title('test')
window.show_all()

message_dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
message_dialog.set_markup('Please wait...')
message_dialog.show_all()

gtk.gdk.threads_init()

thread = Thread(None, _thread, 'my thread')
thread.start()

gtk.gdk.threads_enter()
gtk.main()
gtk.gdk.threads_leave()


What you did in your original code was to create a thread with its only jobs was to add to the queue of events a call back to be done when the main threads event loop is free to do other jobs. The thread then promptly exits leaving only one thread running.

In the idle call back function you are doing all your changing of the display and waiting. The problem here is that this is done in the same thread as then main event loop and control is never returned to it until this has finished. Therefore nothing gets displayed. In my version I had corrected access to gtk from threads by using the correct locking and put the working code into a thread.

Personally I would try to avoid the use of gtk.gdk_threads_enter() and gtk.gdk_threads_leave() as this is deprecated in GTK+ v3

_________________
E.


Top
 Profile  
 
 Post subject: Re: Display a dialog while executing some code at startup
PostPosted: Thu Jun 06, 2013 12:35 pm 
Offline
Familiar Face

Joined: Wed Jun 05, 2013 7:39 am
Posts: 6
Location: France
Thanks a lot! It seems to work fine.

In the beginning I didn't use idle_add function, but I decided to use it after having read about it - and obviously misunderstood - in other forums. I thought that my error was to call GTK before the main loop was started, that's why I wanted to wait for GTK main loop to start, but I didn't realize that my code would be a callback, called from the main loop thread.
I think that the problems I had - before I tried to use idle_add - were due to missing threads_enter/threads_leave. At that time I was testing my real application, not the small example code that I prepared to explain my problem.

I have a few questions about your changes:

1) Is there any advantage of moving message_dialog creation out of the thread? I put it back in the thread and it works as well.

2) I understand I forgot to protect message_dialog.destroy() with threads_enter/threads_leave, but what's the use of protecting gtk.main() with threads_enter/threads_leave?
I have read from this page that it was not necessary (bottom of the page):
http://www.pardon-sleeuwaegen.be/antoon ... page0.html
(well, in fact, the author explains that he doesn't really understand the need for it...)

3) Can you explain the deprecated status of threads_enter/threads_leave?
Should we just remove these calls when using GTK+ v3, or replace them with something else?
I suppose it is necessary to test GTK version, since these calls seem to be necessary with GTK+ v2, right?

Thanks a lot for your answer, and thanks a lot in advance for explaining the few questions I still have...


Top
 Profile  
 
 Post subject: Re: Display a dialog while executing some code at startup
PostPosted: Fri Jun 07, 2013 6:05 am 
Offline
Never Seen the Sunlight

Joined: Mon Apr 28, 2008 5:52 am
Posts: 745
Location: UK
To answer your numbered questions :-

1 - It is best not to do too many GTK/GDK API calls from any thread except the main one. First there is no performance gain and from GTK+ 3.6 all calls to GTK should be done from the main thread only. Also calling GTK/GDK from a thread is not portable to a MS Windows computer.

2 - I just followed the documentation, nothing else.

3 - From GTK+ v3.6 gdk_threads_enter()/leave() are deprecated. This means that new code should not use this system and in this case also calling GTK/GDK from a thread is also deprecated. On the next major version change this would be removed and would be when GTK+ changes to v4.0

Is there a good reason for using PyGTK and GTK+ v2.24?
From the PyGTK web site
Quote:
New users are encouraged to use GTK+3 through the PyGObject bindings instead of using PyGTK with GTK+2. Windows users may still want to keep using PyGTK until more convenient installers are published.

_________________
E.


Top
 Profile  
 
 Post subject: Re: Display a dialog while executing some code at startup
PostPosted: Fri Jun 07, 2013 7:06 am 
Offline
Familiar Face

Joined: Wed Jun 05, 2013 7:39 am
Posts: 6
Location: France
Oops... I was not aware that PyGTK had become deprecated!...
I started writing my application in 2010 and that's what I used at that time.
I'll have to rewrite everything... :-(
And find another mechanism to do the things I used to do with thread... (the one we have discussed, and another one to show the progression of time - my application is an audio player).

Anyway, I will first keep on using PyGTK to make the evolution I wanted to do. I'll work on porting to PyGObject later...

Thanks a lot for your help (and for PyGTK status information!)


Top
 Profile  
 
 Post subject: Re: Display a dialog while executing some code at startup
PostPosted: Sat Jun 08, 2013 7:49 am 
Offline
Never Seen the Sunlight

Joined: Mon Apr 28, 2008 5:52 am
Posts: 745
Location: UK
Since you are using PyGTK on a project you started some time ago that is OK. To help with your porting I would start by moving any GTK/GDK calls out of the worker threads. So using your example this would change to
Code:
#!/usr/bin/env python

import gobject
import gtk
from threading import Thread
import time

def _end_msg_win():
    message_dialog.destroy()
    return False

def _msg_win_set_text(text):
    message_dialog.set_markup(text)
    return False

def _thread():
    time.sleep(1)
    gobject.idle_add(_msg_win_set_text, 'Step 1')
    time.sleep(1)
    gobject.idle_add(_msg_win_set_text, 'Step 2')
    time.sleep(1)
    gobject.idle_add(_end_msg_win)

window = gtk.Window(gtk.WINDOW_TOPLEVEL)
window.connect('destroy', lambda wid: gtk.main_quit())

vbox = gtk.VBox(False)
label = gtk.Label()
label.set_text('My application')
vbox.pack_start(label, False)
window.add(vbox)
window.set_title('test')
window.show_all()

message_dialog = gtk.MessageDialog(None, gtk.DIALOG_MODAL | gtk.DIALOG_DESTROY_WITH_PARENT)
message_dialog.set_markup('Please wait...')
message_dialog.show_all()

gobject.threads_init()

thread = Thread(None, _thread, 'my thread')
thread.start()

gtk.main()
Here the call-back functions are set up in the thread using gobject.idle_add() and the call-back functions are actually called in the main thread. Note that GLib is always thread safe (except when accessing the same object from different threads). You will also need to call
Code:
gobject.threads_init()
to initialise Glib and Python's threading systems.

Other tips for getting ready to port your code are to look at removing dead code, simplify code to make it easier to read make more use of Python's Object Orientated features.

I have an interest in audio processing, with the application that I work on being able to play unlimited (within the bounds of the computer) tracks to multiple output and MIDI. All written in C++ using multiple threads with GTK being called only in the main thread. Have fun with your code.

_________________
E.


Top
 Profile  
 
 Post subject: Re: Display a dialog while executing some code at startup
PostPosted: Mon Jun 10, 2013 9:52 am 
Offline
Familiar Face

Joined: Wed Jun 05, 2013 7:39 am
Posts: 6
Location: France
Indeed, gobject.idle_add seems to be an easy and appropriate way to update GUI from a thread.
Thanks for the tip.

I think my application is far more simple than yours, this is just a player :)


Top
 Profile  
 
 Post subject: Re: Display a dialog while executing some code at startup
PostPosted: Tue Jun 25, 2013 2:33 pm 
Offline
Familiar Face

Joined: Wed Jun 05, 2013 7:39 am
Posts: 6
Location: France
Hello,

This is becoming a little bit off-topic, but that might be helpful for people who have read the last replies about PyGI.

I have (kind of) successfully ported my application to PyGI.
The main issues that I had to face are:

1) I used to save the current selection in a TreeView with TreeIter object. It seems to be unsafe - I suppose it was already when I used it with PyGTK - because the reference is lost.
I had to use TreePath instead, and convert it to TreeIter when necessary

2) GStreamer 0.10 is not fully compliant with PyGI.
It is not possible to subscribe to EOS and stream-changed events!...
See http://stackoverflow.com/questions/7005 ... ne-message
I managed to make it work, because I have a thread that display the progression of time (every 0.2 second). I used this thread to monitor the current time when 'about-to-finish' event has been received (this one works!).
- If the current time decreases, I suppose the track has changed => stream-changed
- If the current time remains the same, I suppose we have reached the end of file => EOS
When GStreamer 1.0 is old enough to be available on most desktops, I will use it!...

3) I also had some UTF-8 encoding errors with minidom. I don't understand why it appeared with the porting to pyGI, but I solved it by using codecs module (codecs.open) and by applying .decode('utf8') on filenames.

4) I have many "assertion `GST_IS_ELEMENT (element)' failed" errors from GStreamer, but... I just ignore them and it works...
(I just had to close stderr in the "terminal mode" version of my audio player.)

Now everything is back to normal :)


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 8 posts ] 

All times are UTC


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group