0

I am creating an app who is combining Tkinter GUI with web scraping. The "searching_window" function is using two threads: one for a long process (the web scraping process) and one for GUI. On the GUI I simulated a GIF using canvas and 24 frames of a gif. The problem i encounter i think is when i delete all the images created in canvas, after the long process is done, because i get 24 different errors, the exact number of images, when they are deleted. Or maybe it's because i'm using a thread for Tkinter? But, the errors does not appear immediate, i can go and use the next GUI window and, after i close every Tkinter window, the errors appear.

def prev_window():
    root = Tk()
    root.mainloop()

def next_window():
    root = Tk()
    root.mainloop()

def searching_window(settings):
    def long_process(settings):
        ...
    def app(settings):
        def event_gif(n=0):
            global timer_id
            gif = giflist[n%len(giflist)]
            canvas.create_image(gif.width()//2, gif.height()//2, image=gif)
            if t1.is_alive():
                timer_id = root.after(100, event_gif, n+1)
            else:
                canvas.delete(ALL)
                root.after_cancel(timer_id)
                canvas.place_forget()
                img_done.place(x=335, y=30)
                lbl_done.place(x=330, y=183)
                after = utils.number_html_files()
                lbl_found.configure(text="I found {} items.".format(after-before))
                lbl_found.place(x=217, y=205)

        def event_btn(event):
            if not t1.is_alive():
                root.destroy()
                if utils.number_html_files() != 0:
                    next_window()

        gifpath = datapath + "gif"
        _ = ["frame_{}.png".format(i) for i in range(1, 25)]
        imgfn = "checked.png"
        global timer_id
        timer_id = "after#0"
        before = utils.number_html_files()
        root = Tk()
        root.geometry("650x300+100+100")
        root.title("Searching...")

        font = Font(family="Times New Roman", size=15)
        font2 = Font(family="Times New Roman", size=12)
        background = Label(root, image=smallbackground)
        background.image = smallbackground


        __ = PhotoImage(file="{}{}".format(gifpath, _[0]))
        canvas = Canvas(root, width=300, height=198)

        giflist = []
        for img in _:
            photo = PhotoImage(file="{}{}".format(gifpath, img))
            giflist.append(photo)

        img_done = Label(root, image=imgfn)
        img_done.image = imgfn
        lbl_done = Label(root, text="Done!", font=font)
        lbl_found = Label(root, font=font, text="I found {} items.")
        btn_done = Button(root, text="Continue", font=font)
        btn_done.bind("<Button-1>", event_btn)

        background.place(x=-1, y=-1)
        canvas.place(x=258, y=30)
        btn_done.place(x=343, y=235)

        root.after(100, event_gif)

        root.mainloop()

    t1 = threading.Thread(target=long_process, args=[settings])
    t2 = threading.Thread(target=app, args=[settings])

    t2.start()
    t1.start()

    t2.join()
    t1.join()

Here are the errors:

Exception ignored in: <bound method Image.__del__ of <tkinter.PhotoImage object at 0x7fc45c92f0b8>>
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 3507, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Exception ignored in: <bound method Image.__del__ of <tkinter.PhotoImage object at 0x7fc45c92f0f0>>
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 3507, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Exception ignored in: <bound method Image.__del__ of <tkinter.PhotoImage object at 0x7fc45c92f128>>
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 3507, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Exception ignored in: <bound method Image.__del__ of <tkinter.PhotoImage object at 0x7fc45c92f160>>
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 3507, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Exception ignored in: <bound method Image.__del__ of <tkinter.PhotoImage object at 0x7fc45c92f198>>
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 3507, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Exception ignored in: <bound method Image.__del__ of <tkinter.PhotoImage object at 0x7fc45c92f1d0>>
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 3507, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Exception ignored in: <bound method Image.__del__ of <tkinter.PhotoImage object at 0x7fc45c92f208>>
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 3507, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Exception ignored in: <bound method Image.__del__ of <tkinter.PhotoImage object at 0x7fc45c92f240>>
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 3507, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Exception ignored in: <bound method Image.__del__ of <tkinter.PhotoImage object at 0x7fc45c92f278>>
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 3507, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Exception ignored in: <bound method Image.__del__ of <tkinter.PhotoImage object at 0x7fc45c92f2b0>>
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 3507, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Exception ignored in: <bound method Image.__del__ of <tkinter.PhotoImage object at 0x7fc45c92f2e8>>
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 3507, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Exception ignored in: <bound method Image.__del__ of <tkinter.PhotoImage object at 0x7fc45c92f320>>
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 3507, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Exception ignored in: <bound method Image.__del__ of <tkinter.PhotoImage object at 0x7fc45c92f358>>
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 3507, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Exception ignored in: <bound method Image.__del__ of <tkinter.PhotoImage object at 0x7fc45c92f390>>
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 3507, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Exception ignored in: <bound method Image.__del__ of <tkinter.PhotoImage object at 0x7fc45c92f3c8>>
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 3507, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Exception ignored in: <bound method Image.__del__ of <tkinter.PhotoImage object at 0x7fc45c92f400>>
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 3507, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Exception ignored in: <bound method Image.__del__ of <tkinter.PhotoImage object at 0x7fc45c92f438>>
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 3507, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Exception ignored in: <bound method Image.__del__ of <tkinter.PhotoImage object at 0x7fc45c92f470>>
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 3507, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Exception ignored in: <bound method Image.__del__ of <tkinter.PhotoImage object at 0x7fc45c92f4a8>>
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 3507, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Exception ignored in: <bound method Image.__del__ of <tkinter.PhotoImage object at 0x7fc45c92f4e0>>
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 3507, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Exception ignored in: <bound method Image.__del__ of <tkinter.PhotoImage object at 0x7fc45c92f518>>
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 3507, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Exception ignored in: <bound method Image.__del__ of <tkinter.PhotoImage object at 0x7fc45c92f550>>
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 3507, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Exception ignored in: <bound method Image.__del__ of <tkinter.PhotoImage object at 0x7fc45c92f588>>
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 3507, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Exception ignored in: <bound method Image.__del__ of <tkinter.PhotoImage object at 0x7fc45c92f5c0>>
Traceback (most recent call last):
  File "/usr/lib/python3.6/tkinter/__init__.py", line 3507, in __del__
    self.tk.call('image', 'delete', self.name)
RuntimeError: main thread is not in main loop
Tcl_AsyncDelete: async handler deleted by the wrong thread

Process finished with exit code 134 (interrupted by signal 6: SIGABRT)

EDIT: I fixed the error by moving the root creation and mainloop to where threads are defined and by deleting last canvas image before creating new one:

def prev_window():
    root = Tk()
    root.mainloop()

def next_window():
    root = Tk()
    root.mainloop()

def searching_window(settings):
    def long_process(settings):
        ...
    def app(settings):
        def event_gif(n=0):
            global timer_id
            gif = giflist[n%len(giflist)]
            canvas.delete(ALL)
            canvas.create_image(gif.width()//2, gif.height()//2, image=gif)
            if t1.is_alive():
                timer_id = root.after(100, event_gif, n+1)
            else:
                canvas.delete(ALL)
                root.after_cancel(timer_id)
                canvas.place_forget()
                img_done.place(x=335, y=30)
                lbl_done.place(x=330, y=183)
                after = utils.number_html_files()
                lbl_found.configure(text="I found {} items.".format(after-before))
                lbl_found.place(x=217, y=205)

        def event_btn(event):
            if not t1.is_alive():
                root.destroy()
                if utils.number_html_files() != 0:
                    next_window()

        gifpath = datapath + "gif"
        _ = ["frame_{}.png".format(i) for i in range(1, 25)]
        imgfn = "checked.png"
        global timer_id
        timer_id = "after#0"
        before = utils.number_html_files()

        font = Font(family="Times New Roman", size=15)
        font2 = Font(family="Times New Roman", size=12)
        background = Label(root, image=smallbackground)
        background.image = smallbackground


        __ = PhotoImage(file="{}{}".format(gifpath, _[0]))
        canvas = Canvas(root, width=300, height=198)

        giflist = []
        for img in _:
            photo = PhotoImage(file="{}{}".format(gifpath, img))
            giflist.append(photo)

        img_done = Label(root, image=imgfn)
        img_done.image = imgfn
        lbl_done = Label(root, text="Done!", font=font)
        lbl_found = Label(root, font=font, text="I found {} items.")
        btn_done = Button(root, text="Continue", font=font)
        btn_done.bind("<Button-1>", event_btn)

        background.place(x=-1, y=-1)
        canvas.place(x=258, y=30)
        btn_done.place(x=343, y=235)

        root.after(100, event_gif)

    root = Tk()
    root.geometry("650x300+100+100")
    root.title("Searching...")
    t1 = threading.Thread(target=long_process, args=[settings])
    t2 = threading.Thread(target=app, args=[settings])

    t2.start()
    t1.start()

    root.mainloop()

    t2.join()
    t1.join()
U. Alin
  • 3
  • 5
  • Your code is creating a _lot_ of `Canvas` image objects—10 per sec—and stacking them on top of one another. You could avoid that by only reading in enough to hold all the frames of the animation, and then cycle through those as needed. To display them one at a time, just iteratively update the image of some widget, like a `Label`, using its `configure()` method with an `image=` option. See this [answer](https://stackoverflow.com/a/28549748/355230) to an animation question for a runnable example. – martineau Jan 13 '19 at 13:22
  • P.S. Another simpler alternative would be to delete the previous `Canvas` image object before creating new ones. That way there would not be a bunch of them to delete all-at-once. – martineau Jan 13 '19 at 13:26
  • Possible duplicate of [RuntimeError: main thread is not in main loop](https://stackoverflow.com/questions/14694408/runtimeerror-main-thread-is-not-in-main-loop). Also read [why-are-multiple-instances-of-tk-discouraged](https://stackoverflow.com/questions/48045401/why-are-multiple-instances-of-tk-discouraged) – stovfl Jan 13 '19 at 14:12
  • @martineau I tried deleting previous Canvas image before creating new ones, but i still got the same errors. And using a Label instead of a Canvas also gives me the same error, but only one, not 24. – U. Alin Jan 13 '19 at 19:36
  • In that case perhaps it has something with your multithreading. When you do that with `tkinter` you have to be extremely careful that only one thread—usually just the main one — **ever** makes any call to `tkinter` functions/methods. – martineau Jan 13 '19 at 20:07

0 Answers0