Streamlit crashes when it runs a turtle drawing more than once

Viewed 383

Intro

I'm building a drawing app using the Streamlit library as a frontend and the Turtle library as the drawing engine.

Issue

Streamlit crashes and throw the following message when the drawing is invoked more than once:

Exception ignored in: <function Image.__del__ at 0x0000017A47EF3558>
Traceback (most recent call last):
  File "c:\users\johnsmith\anaconda3\lib\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

I want the user to be able to change the inputs and re-run the app as many times as they want without crashes.

Code

Frontend:

# frontend.py

import streamlit as st
from backend import *

st.title("Turtle App")
title = st.text_input("Canvas Title", value="My Canvas")
width = st.number_input("Canvas Width", value=500)
height = st.number_input("Canvas Height", value=500)
length = st.number_input("Square Length", value=200)

clicked = st.button("Paint")

if clicked:
    canvas_builder(title, width, height, length)

Backend:

# backend.py

import turtle

def canvas_builder(title, canvas_width, canvas_height, square_length):
    CANVAS_COLOR = "red"
    PEN_COLOR = "black"
    scr = turtle.Screen()
    scr.screensize(canvas_width, canvas_height)
    scr.title(title)
    scr.bgcolor(CANVAS_COLOR)
    turtle.setworldcoordinates(0, 0, canvas_width, canvas_height)
    t = turtle.Turtle()
    t.color(PEN_COLOR)
    t.begin_fill()
    for i in range(4):
        t.forward(square_length)
        t.left(90)
    t.end_fill()
    turtle.done()

Re-production

  1. All the file in the same folder
  2. From Conda's prompt in the same folder, run:
streamlit run ./st.py
  1. Open the app in the browser as indicated by the shell
  2. Press the "Paint" button at the bottom of the UI
  3. Close the Turtle window
  4. Press the "Paint" button again
  5. Check the prompt of the streamlit app for the error

Notes

  • It seems to be an issue with tkinter processes. I tried to get hold of the root/main one and kill it, but it didn't work.
  • Ideally, I would like to embed the the Turtle window inside the streamlit app as a plot.
  • I don't want to replace streamlit and/or turtle.
1 Answers

I solved the problem by running turtle in a child process. New frontend.py code:

import multiprocessing
import streamlit as st
from backend import *

st.title("Turtle App")
title = st.text_input("Canvas Title", value="My Canvas")
width = st.number_input("Canvas Width", value=500)
height = st.number_input("Canvas Height", value=500)
length = st.number_input("Square Length", value=200)
clicked = st.button("Paint")

t = multiprocessing.Process(target=canvas_builder, args=(title, width, height, length,))

if clicked:
   t.start()
 
Related