我的程序使用了很多ram内存,该怎么办?

时间:2018-07-16 17:17:08

标签: python python-3.x tkinter ram

我的程序正在使用大量的ram内存,它的作用是:

  1. 从在线相机下载图像。
  2. 在窗口(Tkinter窗口)中显示图像
  3. 删除图像。
  4. 再次执行相同的过程(无限循环直到窗口关闭)

这是代码:

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)

我尝试使用gc.collect(),但似乎无法正常工作。

2 个答案:

答案 0 :(得分:4)

您的代码正在尝试创建无限数量的Label对象,将每个对象隐藏在无限数量的其他标签后面。除非您拥有无限的内存,否则这显然会导致问题。

想要在任何给定时间都是四个标签。您需要跟踪已创建的标签,理想情况下只是重复使用它们,否则,请销毁它们,而不仅仅是将它们隐藏在新标签后面。


当前,您的代码正在跟踪img1img4变量中的标签,只是直到第一次显示图像时它们才被创建,所以您无法检查他们。因此,在循环之前,您要么要创建四个空标签,要么只需将变量设置为None。然后,在循环内部,您可以执行此操作(假设您使用None代替了空标签):

    if cam.getPlace() == "FrontYard1":
        try:
            if img1 is None:
                img1 = Label(image=render)
                img1.grid(row=0, column=0)
            else:
                img1.image = render
        # etc.

您可以通过将标签存储在dict中而不是四个单独的变量中来简化此操作:

imgs = {“Front Yard 1": img1, …}

…,然后用字典查找替换if / elif链:

img = imgs.get(cam.getPlace())

尽管在这里,您几乎肯定会希望使用空标签,而不是None作为初始值。


请注意,您的程序还有另一个非常严重的问题。 while True:循环显然永远不会返回到tkinter事件循环。这意味着您不会给tkinter一个更新显示的机会,也不会响应窗口系统中的鼠标事件或退出或其他事件,因此您的应用将无响应,并弹出沙滩球或沙漏或其他任何内容。 / p>

要解决此问题,您需要删除循环,而让您的方法只是检索和处理一张图像,然后调用after要求tkinter下次在事件循环中再次调用它。

这还需要将这些本地img1变量(或上面建议的字典imgs)更改为实例变量。

如果您尝试使用后台线程来执行此任务,则从主线程以外的任何线程对tkinter小部件进行任何操作都是非法的。在某些平台上,这只会给您立即出现错误,或挂起GUI。在其他平台上,它似乎可以正常工作,但是偶尔会做一些奇怪的事情,甚至更糟。

如果您正在通过mtTkinter上的Python 3分支(包含the Effbot Book中显示的Queue逻辑)来解决此问题,那么实际上您在小部件上所做的一切发布消息以供主线程处理),则可以忽略此部分。但是,如果不是这样,则可以执行此操作,或者手动执行同一操作(如链接页面中所示),或者只是摆脱后台线程并使用after


在我们这样做的时候,仅显示except:的裸Error隐藏了任何可能出现的问题,使调试变得更加困难,因此您几乎永远都不要这样做。 / p>

此外,Label.image作业真的是唯一可以在这里提出的地方吗?


将它们放在一起:

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)

答案 1 :(得分:-1)

在快速查看后,如果删除了第二步而不是实际显示它,而是直接将其保存到计算机,这将有助于减少ram。您是否拥有有关在此过程中拥有多少RAM和计算机正在使用多少内存的详细信息?另一个可能是增加一个步骤以将图像格式化为特定大小,因为它可能太大而无法处理大量图像。