#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Nov 2012
    Posts
    3
    Rep Power
    0

    Communicating from a thread to an interface


    My Python experience has mostly been number-crunching, so I'm not especially familiar with multi-threaded programs or graphical interfaces. I'm trying to learn a few more tricks here, I'm just not sure how to structure the program correctly.

    I'd like to have a thread that just sits in the background executing a series of functions. As it proceeds, I'd like it to update some things on a GUI window (gtk now, possibly other options later). I can think of two ways of doing this, and I'm not sure which is the more "correct" way.

    The first way would be to send the gtk window's reference to the processing thread and have it update whatever display components need to be updated (would that even work?) while it calculates things. I don't really like this, because it means the processing thread will contain code that's specific to gtk and the window.

    The other way would be to send a queue reference to the processing thread, which will then enqueue messages for some generic interface. Then whatever the window is, gtk, qt, wx, it can unpack those messages and display things however it needs to. I don't especially like this, because it means the GUI has to check the queue in a loop without getting in the way of gtk's main loop (I don't actually know how that main loop works yet).

    There's probably a lot that's wrong with what I wrote there. But the basic idea is that I want a GUI display that shows some info from a thread that's constantly calculating something. What's the accepted method of communicating between the two?

    Thanks!
  2. #2
  3. Contributing User
    Devshed Demi-God (4500 - 4999 posts)

    Join Date
    Aug 2011
    Posts
    4,711
    Rep Power
    480

    Overjoyed you asked!


    Using gui.mainloop() with call backs to your main computation is, as you suspect, a hideous evil wrong contortion. Recently I realized a better way! For many applications the main program should take control. Use gui.update() at your schedule. I've written 3 codes (including this example) this way. Works great. This code might be valid in python2 if you capitalize tkinter.
    Code:
    # python3
    
    # This code demonstrates "use update instead of mainloop".
    # It does not demonstrate a "good convergence monitor".
    
    # Canvas objects are somewhat permanent.
    # Rather than drawing new items the program should modify the old ones.
    # I wish I knew this years ago.
    
    import tkinter
    import time
    import math
    
    def fit(L,w,h):
        assert w
        assert h
        assert L
        yn = min(L)
        yx = max(L)
        my = h/((yx-yn) or 1)
        result = []
        for (x,y) in enumerate(L):
            result.append(h-my*y)
            result.append(w*x/len(L))
        return result
    
    class convergence_monitor:
    
        def __init__(self):
            self.canvas = tkinter.Canvas()
            self.canvas.pack()                        # display the canvas
            self.curve = self.canvas.create_line(0,0, 1,0) # install a line
            self.history = []
    
        def __call__(self,y):
            # demonstration only
            history = self.history
            history.append(y)
            if 1 < len(history):
                canvas = self.canvas
                curve = self.curve
                h = canvas.winfo_height()
                w = canvas.winfo_width()
                canvas.coords(curve,*fit(history,w,h))   # modify the line's coordinates
                canvas.itemconfig(curve,fill='red black'.split()[len(history)&1]) # color flicker
                canvas.update()          # use update, do not use mainloop
    
    def main():
        '''
            simulate your simulation
        '''
        cm = convergence_monitor()
        for i in range(100):
            cm(math.exp(-i))
            time.sleep(0.1)
    
    if '__main__' == __name__:
        main()
    [code]Code tags[/code] are essential for python code and Makefiles!
  4. #3
  5. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Nov 2012
    Posts
    3
    Rep Power
    0
    Thanks for the reply, b49P23TIvg, I appreciate the help. Your code works well, thanks for the sample. I hope you won't mind me picking your brain a bit more (sorry, I'm still learning quite a bit about programming practices, so perhaps there are obvious answers).

    In your code, new values to be plotted are computed inside the convergence_monitor class, which already has access to all the window objects, and therefore also the canvas object (with which to call update() ). Let's say the calculations were done in a separate thread that doesn't know about the canvas object, and needed to be sent back to the convergence_monitor class somehow. How would you achieve that?

    Thanks!
  6. #4
  7. Contributing User
    Devshed Demi-God (4500 - 4999 posts)

    Join Date
    Aug 2011
    Posts
    4,711
    Rep Power
    480
    The new values are computed in main, not in the convergence_monitor object. main presents the monitor with data. The gui handling object scales data and changes axes every iteration most annoyingly.

    So you knew all that and I haven't helped answer your question a bit.

    SETI main
    -> farms signal processing jobs to 300 thousand home computers which report back to the SETI main. ->
    -> gui for display. I have no idea what it displays.
    [code]Code tags[/code] are essential for python code and Makefiles!
  8. #5
  9. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Nov 2012
    Posts
    3
    Rep Power
    0
    Sorry about the misunderstanding, my point was just that the call function has access to both the data and the canvas object.

    So, here's a simple example that I can't actually write because I'm not sure how yet... say we have a window with a checkbox in it. When we start the program, it shows the window and starts another thread, and that thread's job is just to sleep for some arbitrary amount of time and then toggle the checkbox.

    You and I both agree that we shouldn't let the thread know anything about the window. So, when we want to toggle the checkbox, we need the thread to somehow send a message to the window to do it itself. My best guess is that I put some kind of message in a queue that's shared between the window and the thread. The problem is that I then wouldn't know how to make the window handle both its own main gtk loop and some other loop that keeps checking the queue. So, maybe since you clarified which of my two initial guesses was the better one, I need to learn a little more about how to work with gtk's main loop.

    Thanks!

IMN logo majestic logo threadwatch logo seochat tools logo