2

Can someone give me a simple example involving threads in this manner, please.

Problem with my code is that when I click button One, GUI freezes until its finished. I want buttons to stay responsive when def is being executed. How can i fix that?

class fun:
        wTree = None
        def __init__( self ):                
                self.wTree = gtk.glade.XML( "ui.glade" )

                dic = {
                        "on_buttonOne" : self.one,
                        "on_buttonTwo" : self.two,
                }
                self.wTree.signal_autoconnect( dic )              
                gtk.main()

        def sone(self, widget):
           time.sleep(1)
           print "1"
           time.sleep(1)
           print "2"
           time.sleep(1)
           print "3"
        def stwo(self, widget):
           time.sleep(1)
           print "4"
           time.sleep(1)
           print "5"
           time.sleep(1)
           print "6"
do=fun()

Pretty please, help me.

wtz
  • 375
  • 1
  • 5
  • 18

2 Answers2

4

Use Python Threads: http://docs.python.org/library/threading.html

Something like:

class SoneThread(threading.Thread):
    def __init__(self):
        threading.Thread.__init__(self)
        self.start() # invoke the run method

    def run(self):
       time.sleep(1)
       print "1"
       time.sleep(1)
       print "2"
       time.sleep(1)
       print "3"

Now in sone just call SoneThread(), that should work.

Also you need to call gtk.gdk.threads_init() in order to make python threads work with your GTK+ application.

See: http://library.gnome.org/devel/pygtk/stable/gdk-functions.html#function-gdk--threads-init

Ivo Wetzel
  • 46,459
  • 16
  • 98
  • 112
1

When using gtk, it will run a main loop, and you schedule everything you want to do as events to the gtk loop. You don't need threads to do anything.

Here's a complete, full, ready-to-run example that uses glib.timeout_add to do what you want.

Note that clicking on both buttons (or multiple times on a button) doesn't freeze the gui and everything happens "at the same time"...

import gtk
import glib

def yieldsleep(func):
    def start(*args, **kwds):
        iterable = func(*args, **kwds)
        def step(*args, **kwds):
            try:
                time = next(iterable)
                glib.timeout_add_seconds(time, step)
            except StopIteration:
                pass
        glib.idle_add(step)
    return start

class Fun(object):
    def __init__(self):
        window = gtk.Window()

        vbox = gtk.VBox()

        btnone = gtk.Button('one')
        btnone.connect('clicked', self.click_one)
        btnone.show()
        vbox.pack_start(btnone)

        btntwo = gtk.Button('two')
        btntwo.connect('clicked', self.click_two)
        btntwo.show()
        vbox.pack_start(btntwo)

        vbox.show()
        window.add(vbox)
        window.show()

    @yieldsleep
    def click_one(self, widget, data=None):
        yield 1 #time.sleep(1)
        print '1'
        yield 1 #time.sleep(1)
        print '2'
        yield 1 #time.sleep(1)
        print '3'

    @yieldsleep
    def click_two(self, widget, data=None):
        yield 1 #time.sleep(1)
        print '4'
        yield 1 #time.sleep(1)
        print '5'
        yield 1 #time.sleep(1)
        print '6'

do = Fun()
gtk.main()
nosklo
  • 217,122
  • 57
  • 293
  • 297
  • 2
    Note that this does only work if you really have those time.sleep() calls in there, if you fetch a url or do another blocking operation your GUI will still freeze up, in those cases you need to use threads. – Ivo Wetzel Apr 10 '10 at 23:30
  • @Ivo Wetzel: True - That's why you *don't* use `time.sleep()`!! If you have to fetch a url you could use `gio` or the lower-level `glib.io_add_watch` to be notified when the operation finishes, without freezing up the GUI. The secret is to **never** do something that blocks. There are alternatives for this. – nosklo Apr 11 '10 at 16:31
  • I don't think going lower and lower is the best way in most cases, I definitively don't want to rewrite API libraries(in this case one for the Twitter API) that I use just to get rid of a simple Thread. – Ivo Wetzel Apr 11 '10 at 17:51
  • @Ivo Wetzel: There are plenty asynchronous API libraries you could use with gtk's mainloop. Since you have a mainloop to take account, it doesn't make any sense to choose a blocking API. For twitter there are `twitty` and `twinado` - I've worked with `twitty` and it works fine with GTK without using any threads. Also, threads are hardly *simple* - concurrency is never simple. It's always easier to debug, extend and scale applications when the behavior is proven and predictable. – nosklo Apr 11 '10 at 18:41
  • I'd take a look at twitty, it's missing a lot of stuff that I need, I've already patched in xAuth support and some other stuff to tweepy. async-python-twitter(That's what I've found when searching for twinado) doesn't even offer oAuth support. So removing threads and having to reimplement a lot of the API stuff just isn't worth it in this case. – Ivo Wetzel Apr 12 '10 at 13:34
  • 1
    @nosklo, I really agree with your general point about avoiding threads, but this is a horrible example for asynchronous programming. Please use something practical (fetching an URL, processing a generator, ...) – schlamar Dec 04 '12 at 15:09
  • @schlamar: I didn't create the example, just rewrite the OP code. fetching an URL would need `glib.io_add_watch` or some higher level thing like `gio.File`. Please ask a separate question. – nosklo Dec 06 '12 at 13:14
  • 1
    @nosklo I'm well aware how to do that. I just wanted to say that your example is not really helpful for beginners. – schlamar Dec 06 '12 at 13:45