I am trying to use multithreading to show a loading wheel while doing some background calculations.
I try to start the multithreaded function, and then run a timer to simulate the calculations.
The problem is the thread first starts running when the timer is over, even though I am starting the thread first...
from tkinter import *
from tkinter import messagebox
import time
from threading import *
from PIL import Image, ImageTk
# Create Object
root = Tk()
# Set geometry
root.geometry("400x400")
flag = True
# use threading
def threading():
t1=Thread(target=work)
t1.start()
time.sleep(10)
# work function
def work():
print("sleep time start")
image = Image.open("Static/spinner0.png")
img = ImageTk.PhotoImage(image)
lab = Label(root,image=img)
lab.pack()
while flag:
for i in range(8):
image = Image.open("Static/spinner"+str(i)+".png")
img = ImageTk.PhotoImage(image)
lab['image'] = img
time.sleep(0.2)
#return False
print("sleep time stop")
def on_closing():
if messagebox.askokcancel("Quit", "Do you want to quit?"):
flag = False
root.destroy()
# Create Button
Button(root,text="Click Me",command = threading).pack()
# Execute Tkinter
root.protocol("WM_DELETE_WINDOW", on_closing)
root.mainloop()
Just putting the answer here to avoid people digging comments for the solution.
One way to do this is to have both the calculations and the updating function running on extra different threads, this way the main process event loop stays uninterrupted and you get to call time.sleep, which is fine so long as it doesn't happen in your main thread.
A thread in python consumes below 100KB of memory (plus memory for variables in code running on it), so it's fine to have multiple threads running.
from tkinter import *
from tkinter import messagebox
import time
from threading import *
from PIL import Image, ImageTk
# Create Object
root = Tk()
# Set geometry
root.geometry("400x400")
flag = True
# use threading
def threading():
t1 = Thread(target=work)
t1.start()
t2 = Thread(target=time.sleep,args=(10,)) # the heavy computation function.
t2.start()
# control is given back to the main event loop
# work function
def work():
print("sleep time start")
image = Image.open("Static/spinner0.png")
img = ImageTk.PhotoImage(image)
lab = Label(root,image=img)
lab.pack()
while flag:
for i in range(8):
image = Image.open("Static/spinner"+str(i)+".png")
img = ImageTk.PhotoImage(image)
lab['image'] = img
time.sleep(0.2)
#return False
print("sleep time stop")
def on_closing():
if messagebox.askokcancel("Quit", "Do you want to quit?"):
flag = False
root.destroy()
# Create Button
Button(root, text="Click Me", command=threading).pack()
# Execute Tkinter
root.protocol("WM_DELETE_WINDOW", on_closing)
root.mainloop()
Related
I am trying to write a Tkinter application that will also process QR codes. For that to work, I need to have a loop checking if the QR code is valid and I need to make a post request. I'm fully aware that the way I have coded this is highly inefficient. Is there a better way to do this? Here is what I have so far:
import cv2
import tkinter as tk
from PIL import Image, ImageTk
import sys
import os
import pyzbar.pyzbar as zbar
import threading
import requests
import queue
result = []
decodedCode = ""
logo = "logo.png"
settings = "settings.png"
if os.environ.get('DISPLAY','') == '':
print('no display found. Using :0.0')
os.environ.__setitem__('DISPLAY', ':0.0')
#create main window
master = tk.Tk()
master.title("tester")
master.geometry("480x800")
master.configure(bg='white')
ttelogo = tk.PhotoImage(file = logo)
settingslogo = tk.PhotoImage(file = settings)
#settings button
settings_frame = tk.Frame(master,width=50,height=50,bg="white")
settings_frame.pack_propagate(0) # Stops child widgets of label_frame from resizing it
settingsBtn = tk.Button(settings_frame, image=settingslogo).pack()
settings_frame.place(x=430,y=0)
#logo
img = tk.Label(master, image=ttelogo, bg='white')
img.image = ttelogo
img.place(x=176.5,y=10)
#Name Label
label_frame = tk.Frame(master,width=400,height=100,bg="white")
label_frame.pack_propagate(0) # Stops child widgets of label_frame from resizing it
tk.Label(label_frame,bg="white",fg="black",text="John Smith Smithington III",font=("Calibri",22)).pack()
label_frame.place(x=40,y=140)
#Instructions Label
instructions_frame = tk.Frame(master,width=440,height=100,bg="white")
instructions_frame.pack_propagate(0) # Stops child widgets of label_frame from resizing it
tk.Label(instructions_frame,bg="white",fg="black",text="Place your pass under the scanner below.",font=("Calibri",10)).pack()
instructions_frame.place(x=20,y=210)
#Camera Window
cameraFrame = tk.Frame(master, width=440, height=480)
cameraFrame.place(x=20, y=260)
#Camera Feed
lmain = tk.Label(cameraFrame)
lmain.place(x=0, y=0)
cap = cv2.VideoCapture(0)
def startScanning():
global cap
_, frame = cap.read()
frame = cv2.flip(frame, 1)
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(cv2image)
imgtk = ImageTk.PhotoImage(image=img)
lmain.imgtk = imgtk
lmain.configure(image=imgtk)
lmain.after(10, startScanning)
def processScan():
global decodedCode
stopped = False
delay = 1
while(True):
ret = cv2.waitKey(delay) & 0xFF
if ret == ord('c'): # continue
stopped = False
delay = 1
if ret == ord('q'):
break
if stopped or (ret == ord('s')): # stop
stopped = True
delay = 30
continue
# Capture frame-by-frame
ret, frame = cap.read()
decodedObjects = zbar.decode(frame)
if len(decodedObjects) > 0:
stopped = True
for code in decodedObjects:
#print("Data", obj.data)
#API Calls
decodedCode = code.data.decode('utf-8')
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()
def checkCode():
global decodedCode
while True:
if decodedCode != "":
print (decodedCode)
result = requests.post("https://example.com/login/index.php", data={'action': 'validate_scan', 'uuid': decodedCode}).text
print(result)
decodedCode = ""
startScanning() #Display 2
threading.Thread(name='background', target=processScan).start()
threading.Thread(name='background2', target=checkCode).start()
master.mainloop() #Starts GUI
Edit: New version in queue form:
# import all the necessary modules
from tkinter import *
from multiprocessing import Process, Queue
from queue import Empty # for excepting a specific error
import numpy as np
import cv2
from PIL import Image, ImageTk
import sys
import os
import pyzbar.pyzbar as zbar
import threading
import requests
# this is the function that will be run in a child process
def processScan(queue_): # pass the queue as an argument
stopped = False
delay = 1
while(True):
ret = cv2.waitKey(delay) & 0xFF
if ret == ord('c'): # continue
stopped = False
delay = 1
if ret == ord('q'):
break
if stopped or (ret == ord('s')): # stop
stopped = True
delay = 30
continue
# Capture frame-by-frame
ret, frame = cap.read()
decodedObjects = zbar.decode(frame)
if len(decodedObjects) > 0:
stopped = True
for code in decodedObjects:
#print("Data", obj.data)
#API Calls
queue_.put(code.data.decode('utf-8'))
# When everything done, release the capture
cap.release()
cv2.destroyAllWindows()
#master.after(2000, processScan)
#return r
# just a function to not write a `lambda`, just easier
# to read code
def startScanning():
global cap
_, frame = cap.read()
frame = cv2.flip(frame, 1)
cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA)
img = Image.fromarray(cv2image)
imgtk = ImageTk.PhotoImage(image=img)
lmain.imgtk = imgtk
lmain.configure(image=imgtk)
lmain.after(10, startScanning)
#processScan()
#threading.Thread(name='background', target=processScan).start()
# set process `daemon = True` so that it gets terminated with the
# main process, this approach is not suggested if you write to file
# but otherwise it shouldn't cause any issues (maybe an error but
# that probably can be handled with `try/except`)
Process(target=processScan, args=(queue, ), daemon=True).start()
# here is the loop for updating the label
# basically get the info from the queue
def update_label():
try:
# try getting data but since it is `block=False`
# if there is nothing in the queue it will not block
# this process waiting for data to appear in the queue
# but it will raise the Empty error
data = queue.get(block=False)
except Empty:
pass
else:
# if no error was raised just config
# label to new data
labelFrame.config(text=data)
finally:
# and simply schedule this function to run again in
# 100 milliseconds (this btw is not recursion)
master.after(100, update_label)
# crucial part is to use this if statement because
# child processes run this whole script again
if __name__ == '__main__':
master = Tk()
logo = "logo.png"
settings = "settings.png"
if os.environ.get('DISPLAY','') == '':
print('no display found. Using :0.0')
os.environ.__setitem__('DISPLAY', ':0.0')
#create main window
master.attributes("-fullscreen", True)
master.title("Title")
master.geometry("480x800")
master.configure(bg='white')
ttelogo = PhotoImage(file = logo)
settingslogo = PhotoImage(file = settings)
#settings button
settings_frame = Frame(master,width=50,height=50,bg="white")
settings_frame.pack_propagate(0) # Stops child widgets of label_frame from resizing it
settingsBtn = Button(settings_frame, image=settingslogo).pack()
settings_frame.place(x=430,y=0)
#logo
img = Label(master, image=ttelogo, bg='white')
img.image = ttelogo
img.place(x=176.5,y=10)
#Name Label
label_frame = Frame(master,width=400,height=100,bg="white")
label_frame.pack_propagate(0) # Stops child widgets of label_frame from resizing it
Label(label_frame,bg="white",fg="black",text="John Smith Smithington III",font=("Calibri",22)).pack()
label_frame.place(x=40,y=140)
#Instructions Label
instructions_frame = Frame(master,width=440,height=100,bg="white")
instructions_frame.pack_propagate(0) # Stops child widgets of label_frame from resizing it
Label(instructions_frame,bg="white",fg="black",text="Place your pass under the scanner below.",font=("Calibri",15)).pack()
instructions_frame.place(x=20,y=210)
#Camera Window
cameraFrame = Frame(master, width=440, height=480)
cameraFrame.place(x=20, y=260)
#Camera Feed
lmain = Label(cameraFrame)
lmain.place(x=0, y=0)
cap = cv2.VideoCapture(0)
# define queue (since it is a global variable now
# it can be easily used in the functions)
queue = Queue()
#label = Label(root)
#label.pack()
# initially start the update function
update_label()
# just a button for starting the process, but you can also simply
# call the function
#Button(root, text='Start', command=start_process).pack()
startScanning()
master.mainloop()
Still running into errors. Also am not sure if this is correct Queue syntax. The Camera feed is not live. Just a static image is showing up at the moment.
This is about as simple as it gets with multiprocessing (explanation in code comments (which is why the code may seem like it has a lot going on when if the comments were removed it would be approx only 40 lines of code for this example)):
# import all the necessary modules
from tkinter import Tk, Label, Button
from multiprocessing import Process, Queue
from queue import Empty # for excepting a specific error
# this is the function that will be run in a child process
def count(queue_): # pass the queue as an argument
# these modules are just to show the functionality
# but time might be necessary otherwise
# if theses are imported in the global namespace
# then you don't need to import them here again
import itertools
import time
for i in itertools.count():
# do the main looping
# put data in the queue
queue_.put(i)
# you may not need to use sleep here depending
# on how long it takes for the
# loop to finish the iteration
# the issue is that the loop may finish
# way faster than the update function runs
# and that will fill up the queue
# I think the queue size can be limited
# but putting sleep like this to allow
# the update loop to update will work too
time.sleep(0.2)
# just a function to not write a `lambda`, just easier
# to read code
def start_process():
# set process `daemon = True` so that it gets terminated with the
# main process, this approach is not suggested if you write to file
# but otherwise it shouldn't cause any issues (maybe an error but
# that probably can be handled with `try/except`)
Process(target=count, args=(queue, ), daemon=True).start()
# here is the loop for updating the label
# basically get the info from the queue
def update_label():
try:
# try getting data but since it is `block=False`
# if there is nothing in the queue it will not block
# this process waiting for data to appear in the queue
# but it will raise the Empty error
data = queue.get(block=False)
except Empty:
pass
else:
# if no error was raised just config
# label to new data
label.config(text=data)
finally:
# and simply schedule this function to run again in
# 100 milliseconds (this btw is not recursion)
root.after(100, update_label)
# crucial part is to use this if statement because
# child processes run this whole script again
if __name__ == '__main__':
root = Tk()
# define queue (since it is a global variable now
# it can be easily used in the functions)
queue = Queue()
label = Label(root)
label.pack()
# initially start the update function
update_label()
# just a button for starting the process, but you can also simply
# call the function
Button(root, text='Start', command=start_process).pack()
root.mainloop()
I am not too familiar with opencv so there might be a chance it won't work in a child process (tho I think I have done that), in that case you will have to either use threading or two alternatives using subprocess: put the opencv part in another file and run that file using python interpreter in the subprocess.Popen (won't work for computers that don't have Python installed) or convert that other python file with the opencv part to an .exe file or sth and run that executable file in the subprocess.Popen (both approaches tho are probably unnecessary and you probably will be fine with either multiprocessing or threading)
To use threading instead change these lines:
# from multiprocessing import Process, Queue
from threading import Thread
from queue import Empty, Queue
and
# Process(target=count, args=(queue, ), daemon=True).start()
Thread(target=count, args=(queue, ), daemon=True).start()
I'm writing a program in tkinter using Progressbar. But there is a problem when I added stop function it doesn't work. When I press "stop" button nothing happens, it should stop loading progressbar. I use Python version 3.8. The code below:
from tkinter import *
from tkinter import ttk
import time
root = Tk()
def run():
pb['maximum']=100
for i in range(101):
time.sleep(0.05)
pb['value']=i
pb.update()
def stop():
pb.stop()
runbutt = Button(root,text="Runprogr",command=run)
runbutt.pack()
stopbutt = Button(root,text="Stopbut",command=stop)
stopbutt.pack()
pb = ttk.Progressbar(root,length=300,orient="horizontal")
pb.pack()
root.geometry("300x300")
root.mainloop()
The cause is that pb.stop couldn't stop the function in run.it will also increase by itself.
You could use .after(ms, callback) to add the value(then you no longer need to use time.sleep()).
If you want to stop it,use .after_cancel():
from tkinter import *
from tkinter import ttk
import time
root = Tk()
root.add_value = None
def run():
def add():
if pb['value'] >= 100:
return
pb['value'] += 1
root.add_value = root.after(50, add)
if root.add_value: # to prevent increasing the speed when user pressed "Runprogr" many times.
return
root.add_value = root.after(50, add)
def stop():
if not root.add_value: # to prevent raising Exception when user pressed "Stopbut" button many times.
return
root.after_cancel(root.add_value)
root.add_value = None
runbutt = Button(root, text="Runprogr", command=run)
runbutt.pack()
stopbutt = Button(root, text="Stopbut", command=stop)
stopbutt.pack()
pb = ttk.Progressbar(root, length=300, orient="horizontal")
pb.pack()
root.geometry("300x300")
root.mainloop()
I am using NI instrument to read data and display it on GUI. Have used tkinter. But could not find a way to update the data using while loop.
import nidaqmx
import time
from tkinter import *
master = Tk()
while True:
with nidaqmx.Task() as task:
task.ai_channels.add_ai_voltage_chan("Dev1/ai0")
print('1 Channel 1 Sample Read: ')
data = task.read()
sensor_value ='%.2f' % data #said sensor value
master.minsize(width=400, height=400)
w = Label(master, text=sensor_value) #shows as text in the window
w.pack() #organizes widgets in blocks before placing them in the parent.
time.sleep(5)
mainloop()
When working with Tkinter we should avoid Threading, while loop with root.update() it is not like we can't use them but not advisable instead use after(delay_ms, callback=None, *args) method provided by Tkinter itself for a reason.
Now to your code, there are few issues in your code.
In while loop you are creating a Label every 5 secs instead create one label and update the value of that label inside the loop with w['text'] = '...' or w.configure(text='...').
Don't put mainloop inside the loop, instead call it in the last line with the instance of the main window in your case master (master.mainloop()).
Same with master.minsize(width=400, height=400), you don't have to tell the master window every 5 sec to set the minimum size to 400x400 it should be called once if not decide to change the minimum size to different geomentry.
Your code should look like this.
import nidaqmx
from tkinter import *
master = Tk()
master.minsize(width=400, height=400)
w = Label(master) #shows as text in the window
w.pack() #organizes widgets in blocks before placing them in the parent.
def run():
with nidaqmx.Task() as task:
task.ai_channels.add_ai_voltage_chan("Dev1/ai0")
print('1 Channel 1 Sample Read: ')
data = task.read()
w['text'] = '%.2f' % data #said sensor value
master.after(5000, run)
run() # run the function once.
master.mainloop()
This should be the right way of doing this and as i couldn't run your code if anything doesn't work, let me know otherwise.
try this code:
import time
from tkinter import *
import nidaqmx
master = Tk()
def test():
while True:
with nidaqmx.Task() as task:
task.ai_channels.add_ai_voltage_chan("Dev1/ai0")
print('1 Channel 1 Sample Read: ')
data = task.read()
sensor_value = '%.2f' % data # said sensor value
w['text'] = sensor_value
master.update()
time.sleep(2)
w = Label(master)
w.pack()
btn = Button(text="Start read from sensor", command=test)
btn.pack()
mainloop()
You'll likely need a second thread to poll the sensor.
import nidaqmx
import time
import threading
from tkinter import *
stop_signal = threading.Event()
def read_loop():
with nidaqmx.Task() as task:
task.ai_channels.add_ai_voltage_chan("Dev1/ai0")
while True:
data = task.read()
label["text"] = "%.2f" % data
# Wait for the signal, or 5 seconds
if stop_signal.wait(timeout=5):
break # If the signal was set, break
# Build window
master = Tk()
master.minsize(width=400, height=400)
label = Label(master, text="")
label.pack()
# Set up & start reading thread
threading.Thread(target=read_loop).start()
try:
# Enter Tk main loop
mainloop()
finally:
# Clean up afterwards
stop_signal.set()
Keep in mind that mainloop() is blocking, so time.sleep() is useless as you won't get back to the task.read() line.
Consider using the Thread module from the threading lib importing it like this:
from threading import Thread
or using a button to refresh the value as someone else suggested you.
I am having two or more python Tkinter files. Each file is opening one window, how can run all the Tkinter windows functionality in one main window.
Ex : I have two files one is usbcam.py which will open USB camera and give the video steaming and the other one is ipcam.py it opens the IP camera and give the live streaming.This two files are opening in two windows how can make this to work in one window
usbcam.py
import cv2
import PIL.Image
import PIL.ImageTk
import Tkinter as tk
def update_image(image_label, cv_capture):
cv_image = cv_capture.read()[1]
cv_image = cv2.cvtColor(cv_image, cv2.COLOR_BGR2RGB)
pil_image = PIL.Image.fromarray(cv_image)
pil_image.save('image3.jpg')
tk_image = PIL.ImageTk.PhotoImage(image=pil_image)
image_label.configure(image=tk_image)
image_label._image_cache = tk_image # avoid garbage collection
root.update()
def update_all(root, image_label, cv_capture):
if root.quit_flag:
root.destroy() # this avoids the update event being in limbo
else:
update_image(image_label, cv_capture)
root.after(10, func=lambda: update_all(root, image_label, cv_capture))
if __name__ == '__main__':
cv_capture = cv2.VideoCapture()
cv_capture.open(0) # have to use whatever your camera id actually is
root = tk.Tk()
setattr(root, 'quit_flag', False)
def set_quit_flag():
root.quit_flag = True
root.protocol('WM_DELETE_WINDOW', set_quit_flag) # avoid errors on exit
image_label = tk.Label(master=root) # the video will go here
image_label.pack()
root.after(0, func=lambda: update_all(root, image_label, cv_capture))
root.mainloop()
ipcam.py
import cv2
import numpy as np
import PIL.Image
import PIL.ImageTk
import Tkinter as tk
import urllib
stream = urllib.urlopen("http://192.168.2.195:80/capture/scapture").read()
bytes_ = ''
def update_image(image_label):
global bytes_
bytes_ += stream.read(1024)
a = bytes_.find('\xff\xd8')
b = bytes_.find('\xff\xd9')
if (a != -1) and (b != -1):
jpg = bytes_[a:b+2]
bytes_ = bytes_[b+2:]
cv_image = cv2.imdecode(np.fromstring(jpg, dtype=np.uint8),
cv2.CV_LOAD_IMAGE_COLOR)
cv_image = cv2.cvtColor(cv_image, cv2.COLOR_BGR2RGB)
pil_image = PIL.Image.fromarray(cv_image)
tk_image = PIL.ImageTk.PhotoImage(image=pil_image)
image_label.configure(image=tk_image)
image_label._image_cache = tk_image # avoid garbage collection
root.update()
def update_all(root, image_label):
if root.quit_flag:
print "coming if"
root.destroy() # this avoids the update event being in limbo
else:
print "coming else"
update_image(image_label)
root.after(1, func=lambda: update_all(root, image_label))
def timer(interval = 100):
root.after(0, func=lambda: update_all(root, image_label))
#.................................................................................................
root.after(interval, timer)
if __name__ == '__main__':
root = tk.Tk()
setattr(root, 'quit_flag', False)
def set_quit_flag():
root.quit_flag = True
root.protocol('WM_DELETE_WINDOW', set_quit_flag)
image_label = tk.Label(master=root) # label for the video frame
image_label.pack()
root.after(2, func=lambda: update_all(root, image_label))
# timer()
root.mainloop()
You need to designate one script as the main script, and in that one you can import the other. Here's an example of doing this using a simple subclass of the Frame widget:
The primary script (tkA.py):
from Tkinter import *
from tkB import Right # bring in the class Right from secondary script
class Left(Frame):
'''just a frame widget with a white background'''
def __init__(self, parent):
Frame.__init__(self, parent, width=200, height=200)
self.config(bg='white')
if __name__ == "__main__":
# if this script is run, make an instance of the left frame from here
# and right right frame from tkB
root = Tk()
Left(root).pack(side=LEFT) # instance of Left from this script
Right(root).pack(side=RIGHT) # instance of Right from secondary script
root.mainloop()
The secondary script (tkB.py):
from Tkinter import *
class Right(Frame):
'''just a frame widget with a black background'''
def __init__(self, parent):
Frame.__init__(self, parent, width=200, height=200)
self.config(bg='black')
if __name__ == "__main__":
# if this script is run, just do this:
root = Tk()
Right(root).pack()
root.mainloop()
Hope that helps.
First, you need to include it in the top. So in the top of ipcam.py write 'import usbcam' (without quotations).
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.
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()
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()