Issue
My program is using a lot of ram memory, what it does is this:
- Downloads an image from an online camera.
- Shows the image in the window (Tkinter window)
- Deletes the image.
- The same process again (infinite loop untill the window is closed)
This is the code:
def show_image(self, cam):
while True:
start_time = time.time()
self.update_image(cam)
load = Image.open(cam.getPlace() + "/image.jpeg")
load.thumbnail((400, 300))
render = ImageTk.PhotoImage(load)
if cam.getPlace() == "FrontYard1":
try:
img1 = Label(image=render)
img1.image = render
img1.grid(row=0, column=0)
except:
print("Error")
elif cam.getPlace() == "FrontYard2":
try:
img2 = Label(image=render)
img2.image = render
img2.grid(row=0, column=1)
except:
print("Error")
elif cam.getPlace() == "Garden1":
try:
img3 = Label(image=render)
img3.image = render
img3.grid(row=1, column=0)
except:
print("Error")
else:
try:
img4 = Label(image=render)
img4.image = render
img4.grid(row=1, column=1)
except:
print("Error")
print("And " + str(time.time() - start_time) + " to run show image")
def update_image(self, cam):
os.remove(cam.getPlace()+"/image.jpeg")
if cam.getModel() == "Model":
urllib.request.urlretrieve(#link and folder to download the image)
else:
urllib.request.urlretrieve(#link and folder to download the image)
I tried using gc.collect() but it doesn't seems to be working.
Solution
Your code is attempting to create an infinite number of Label
objects, hiding each one behind an infinite stack of other labels. This obviously leads to problems unless you have infinite memory.
What you want is just four labels at any given time. You need to keep track of the labels you’ve created and ideally just reuse them, or, if not, destroy them instead of just hiding them behind new ones.
Currently, your code is keeping track of the labels in those img1
through img4
variables, except that they don’t get created until the first time they show an image, so you can’t check them. So, before the loop, you’ll either want to create four empty labels, or just set the variables to None
. Then, inside the loop, you can do this (assuming you went withNone
instead of empty labels):
if cam.getPlace() == "FrontYard1":
try:
if img1 is None:
img1 = Label(image=render)
img1.grid(row=0, column=0)
else:
img1.image = render
# etc.
You could simplify this by storing the labels in a dict instead of four separate variables:
imgs = {“Front Yard 1": img1, …}
… and then replacing the if/elif chain with a dict lookup:
img = imgs.get(cam.getPlace())
Although here, you’re almost certainly going to want to go with empty labels instead of None
for the initial values.
As a side note, your program has another very serious problem. A while True:
loop obviously never returns to the tkinter event loop. This means you don’t give tkinter a chance to update the display, or respond to mouse events or quits or other events from the window system, so your app is going to be nonresponsive and pop up a beach ball or hourglass or whatever.
To fix this, you need to remove the loop and instead have your method just retrieve and process one image and then call after
to ask tkinter to call it again the next time through the event loop.
This will also require changing those local img1
variables (or the dict imgs
suggested above) into instance variables.
If you were trying to use a background thread for this task, it's illegal to do anything to tkinter widgets from any thread but the main thread. On some platforms, this will just give you an immediate error, or hang the GUI. On other platforms, it will seem to work, but every once in a while do something weird—which is even worse.
If you're working around this via, e.g., a Python 3 fork on mtTkinter
(which wraps the Queue
logic shown in the Effbot Book, so everything you do on a widget actually posts a message for the main thread to process), then you can ignore this section. But if not, you want to either do that, or do the same thing manually (as shown in the linked page), or just get rid of the background thread and use after
.
While we're at it, a bare except:
that just prints Error
is hiding any problems that might show up and making it harder to debug things, so you should almost never do that.
Also, is the Label.image
assignment really the only place that can raise here?
Putting it all together:
def __init__(self):
self.imgs = {}
img1 = Label()
img1.grid(row=0, column=0)
self.imgs["Frontyard1"] = img1
# ... likewise for 2-4
def show_image(self, cam):
start_time = time.time()
try:
self.update_image(cam)
load = Image.open(cam.getPlace() + "/image.jpeg")
load.thumbnail((400, 300))
render = ImageTk.PhotoImage(load)
img = self.imgs.get(cam.getPlace())
if img:
img.image = render
# What do you want to do if it's not one of the four expected?
# Your existing code just ignores the image but still counts time,
# so that's what I did here.
except Exception as e:
print(f"Error updating cam '{cam.getPlace()}: {e!r}")
else:
print("And " + str(time.time() - start_time) + " to run show image")
self.root.after(0, self.show_image, cam)
Answered By - abarnert
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.