rsaw
rsaw

Reputation: 3537

pygtk glib.timeout_add(): How to tell if timer not being destroyed?

In my application I use a function to show GtkInfoBars with a timeout (as described https://stackoverflow.com/a/1309257/406281) thanks to glib.timeout_add_seconds().

I understand that glib.timeout_add_seconds() is supposed to set a function to be called at regular intervals until said function returns False.

I'm not doing that, but everything works as expected. It's perfectly simple. Here's my code:

def infobar(self, message, msgtype=gtk.MESSAGE_INFO, timeout=5):
    bar = gtk.InfoBar()
    bar.set_message_type(msgtype)
    bar.add_button(gtk.STOCK_OK, gtk.RESPONSE_OK)
    bar.connect("response", lambda *args: bar.hide())
    self.vb2.pack_end(bar, False, False)
    label = gtk.Label()
    label.set_markup(message)
    content = bar.get_content_area()
    content.add(label)
    label.show()
    bar.show()
    if timeout:
        glib.timeout_add_seconds(timeout, bar.hide)

SO. Question time. Look at what I did in the last line. Simply put, is this okay? Will the timeout destroy itself after the second call to bar.hide() fails? Or am I accumulating extra neutered timeouts that will wake up every 5 seconds [technically] using up resources?


As an addendum:

If, as I suspect, this is bad, and I really do need to return False to destroy the timeouts, I need some help -- I can't figure out how to play around with the code in order to satisfy these conditions: I need to allow multiple InfoBars at the same time, with each still staying connected to their own timer and button-response signal (as the code is now) OR I need each new InfoBar to replace any previous one (I can't figure out how to do this without inheriting the timer from the previous InfoBar--it gets messy).

Thanks for reading!

Upvotes: 6

Views: 5007

Answers (2)

Johannes Sasongko
Johannes Sasongko

Reputation: 4238

Widget.hide always returns None, which glib.timeout_add_seconds treats identically to False. Thus, your timer will always terminate after running once. There is a possibility that hide will be called twice (when the user clicks OK and at the 5 s timeout) but that's alright. So yes, your code is fine.

Edit: Just realised something. You're creating a new info bar every time the function runs, then only hiding it. Instead of using hide, use destroy so it gets properly cleaned up.

... OR I need each new InfoBar to replace any previous one (I can't figure out how to do this without inheriting the timer from the previous InfoBar--it gets messy).

It's not too complicated; just reuse the same info bar. Record the return value of glib.timeout_add_seconds, then while replacing the content of the info bar, kill the previous timer with glib.source_remove.

There are usability trade-offs between the two choices you present (clean UI vs. making sure the user doesn't miss important info) but that's another topic and perhaps not that important.

Upvotes: 4

jcollado
jcollado

Reputation: 40414

Regarding bar.hide being called more than once, I think that's not happening because, as far as I know, it should be enough to return a value that is evaluated as False and bar.hide returns None which should be enough. However, if you want to make completely sure, you can verify with something like this:

import gtk, glib

def callback():
    print 'called'

glib.timeout_add_seconds(1, callback)
gtk.main()

In this example, the callback returns None as well and is called just once as you can see from the output to stdout.

Regarding your final question, I'd say this is already an acceptable way of having multiple infobars with a separate timer for each one. Please add more information if I'm missing something.

Upvotes: 6

Related Questions