views:

48

answers:

2

Ok, I've got the GUI in tkinter working, and I'm trying to grab and image every 5 seconds and display it in a Label named Picturelabel.

from Tkinter import *
from PIL import ImageGrab
import cStringIO, base64, time, threading

class PictureThread(threading.Thread):
    def run(self):
        print "test"
        box = (0,0,500,500) #x,x,width,height
        MyImage = ImageGrab.grab(box)

        fp = cStringIO.StringIO()
        MyImage.save(fp, 'GIF')
        MyPhotoImage = PhotoImage(data=base64.encodestring(fp.getvalue()))

        time.sleep(5)
        PictureThread().run() #If I get rid of this then it just display one image
        return MyPhotoImage

MyVeryNewImage = PictureThread().run()

Picturelabel = Label(BalanceFrame, image=MyVeryNewImage)
Picturelabel.grid(row=3, column=2, columnspan=3)
Picturelabel.image = MyVeryNewImage

window.mainloop()

Firstly how can I clean up this code, as starting a thread inside another thread can't be good practice.

Also when I run this it prints "test" in the console, but it does not bring up the GUI. If I comment out the commented text (PictureThread().run() where I'm creating yet another thread inside it.) then it displays the first image, but not any more.

A: 

The problem is that you return the new image from the PictureThread().run() in the method, but you never save it.

How about:

from Tkinter import *
from PIL import ImageGrab
import cStringIO, base64, time, threading

box = (0,0,500,500) #x,x,width,height
MyImage = ImageGrab.grab(box)

fp = cStringIO.StringIO()
MyImage.save(fp, 'GIF')
MyPhotoImage = PhotoImage(data=base64.encodestring(fp.getvalue()))
Picturelabel = Label(BalanceFrame, image=MyPhotoImage)
Picturelabel.grid(row=3, column=2, columnspan=3)

class PictureThread(threading.Thread):
    def run(self):
        while True:
            box = (0,0,500,500) #x,x,width,height
            MyImage = ImageGrab.grab(box)

            fp = cStringIO.StringIO()
            MyImage.save(fp, 'GIF')
            MyPhotoImage = PhotoImage(data=base64.encodestring(fp.getvalue()))

            time.sleep(5)
            Picturelabel.image = MyPhotoImage 

PictureThread().start()

window.mainloop()
KLee1
This still produces no GUI.
code_by_night
Try start instead of run, as suggested by pcostesi
KLee1
just tried PictureThread().start() and it actually brings up the GUI, but no picture is displayed :( (Attempting the edit now)
code_by_night
Just tried your edit and got "RuntimeError: Too early to create image"Any idea what this is doing? I had something similar before I posted on SO
code_by_night
A: 

You should call start() instead of run(). From the Documentation:

Once a thread object is created, its activity must be started by calling the thread’s start() method. This invokes the run() method in a separate thread of control.

I see you're invoking a new thread inside your run() method. This will cause you to spawn infinite threads!

EDIT: I'm not sure if this works:

from Tkinter import *
from PIL import ImageGrab
import cStringIO, base64, time, threading


Picturelabel = Label(BalanceFrame)
Picturelabel.grid(row=3, column=2, columnspan=3)

class PictureThread(threading.Thread):

    def run(self):
        print "test"
        box = (0,0,500,500) #x,x,width,height
        fp = cStringIO.StringIO()
    while(1):
            MyImage = ImageGrab.grab(box)
            MyImage.save(fp, 'GIF')
            self.image = PhotoImage(data=base64.encodestring(fp.getvalue()))
            Picturelabel.image = self.image
            fp.reset() # reset the fp position to the start
            fp.truncate() # and truncate the file so we don't get garbage 
            time.sleep(5)

PictureThread().start()
window.mainloop()
Pablo Alejandro Costesich
Well aware of this, which is why I asked for help as spawning infinite threads is not acceptable. It seems like the answer above as answered this problem. Thanks
code_by_night
The number of threads should not be more than 2 in most cases, because of the sleep, which would hand off control to the other thread and finish it.
KLee1
NameError: global name 'PictureLabel' is not definedCould this be because PhotoImage does not exist? But this version does bring up the GUI, but no images.
code_by_night
It's because I wrote PictureLabel instead of Picturelabel. This should work, though I haven't tested it.
Pablo Alejandro Costesich
Yep that stop that error, still got "RuntimeError: Too early to create image"I found this: http://effbot.org/pyfaq/why-do-my-tkinter-images-not-appear.htm , but I'm not sure how to implement it or where. If you could help me I would greatly appreciate it.
code_by_night
Just keep a reference to the image. Save the image to self.image and that should fix it.
Pablo Alejandro Costesich
self.image = PhotoImage(data=base64.encodestring(fp.getvalue())) Picturelabel.image = PhotoImage(data=base64.encodestring(fp.getvalue()))You mean like this in the while loop? (Sorry this is probably going to look terrible in a comment) because it still shows the RuntimeError
code_by_night
Ok my first try was actually the same as your edit above, but its still getting that runtime error (RuntimeError: Too early to create image), and no images are being created at all.
code_by_night