St.empty() in container within tabs looking for example

Summary

I like to find an example in which st.empty() is used within tabs. The application is as follows:

a) assume a file is changed on disk
b) one of the tabs includes a Preview container
c) the app runs automatically and once it finds the file gets changed the contents is displayed and the old contents is overwitten
e) I am using that with images that I read remotely from a camera

Steps to reproduce

I do not have a working example. It would be great to have such an example. What I tried did not work and when using the code I have (which is large) it repeats the image multiple times.

code is at GitHub - laszewsk/canon-r7-ccapi
see line 60 in:
canon-r7-ccapi/Canon_Remote_Control.py at main · laszewsk/canon-r7-ccapi · GitHub

when I change look to while True:
Expected behavior:

right now I only enable a one time update on button click, but I like to change that to update whenever the preview image is changed.

Actual behavior:

Explain the undesired behavior or error you see when you run the code above.
If you’re seeing an error message, share the full contents of the error message here.

Debug info

  • Streamlit version: Streamlit, version 1.17.0
  • Python version: Python 3.11.0
  • OS version: macOS ventura
  • Browser version: Chrome Version 109.0.5414.87

Requirements file

In github repo

Links

I’m not sure it would be easy to do this entirely in streamlit, but here’s a convoluted way to accomplish it, using the watchdog library.

streamlit_app.py

from datetime import datetime

import streamlit as st

st.write("Last time this page ran:", datetime.now())

watching.py

import time
from datetime import datetime
from pathlib import Path

from watchdog.events import FileSystemEventHandler
from watchdog.observers import Observer


class MonitorFolder(FileSystemEventHandler):
    def on_modified(self, event):
        print(event.src_path, event.event_type)

        current_time = datetime.now()

        contents = Path(".streamlit/secrets.toml").read_text()

        # Replace last_run = "<previous datetime>" with new datetime
        current_lines = contents.splitlines()

        for i, line in enumerate(current_lines):
            if line.startswith("last_run"):
                current_lines[i] = f"last_run = '{current_time}'"

        contents = "\n".join(current_lines)

        Path(".streamlit/secrets.toml").write_text(contents)

        print(contents)


if __name__ == "__main__":
    file_path = "dir_to_watch" # UPDATE this to the directory of the file you expect to be changing

    event_handler = MonitorFolder()
    observer = Observer()
    observer.schedule(event_handler, path=file_path, recursive=True)
    print("Monitoring started")
    observer.start()
    try:
        while True:
            time.sleep(1)

    except KeyboardInterrupt:
        observer.stop()
        observer.join()

Steps to get it working:

  1. Add a line to .streamlit/secrets.toml which is last_run = ''
  2. Update the watcher.py with the directory you want to watch
  3. Run the streamlit app with streamlit run streamlit_app.py
  4. In a separate process, run python watching.py
  5. Whenever something in the directory_to_watch folder changes, watchdog will then update the secrets.toml with a new value. I happen to know that if you update that file, your streamit app will automatically rerun, even if it’s not using that value.

Quite convoluted, but hopefully that works for you, or at least gives you some ideas.

1 Like

yes its a good idea to do this with watchdog. But my setup is actually much easier, I can just poll for the image from the camera in a loop in the application. The actual issue I have is that for some reason I can not get st.empty() do work so that the image is not appended every time in a tab that contains a container.
I am sure that I likely do not know how to uses the empty in relationship to a tab or a container.

so I have something like

tab_preview, tab_general, tab_focusbracketing = \
    st.tabs(["Preview", "Settings", "Focusbraceting"])


def preview(position=tab_preview):
    camera = CCAPI()
    device = camera.get_deviceinformation()

    name = "./preview.jpeg"
    r = camera.liveview(display="on", size="medium")
    r = camera.get_liveview_image(name)

   c = position.empty()
   
    while True:
      try:
          c = position.empty()
         
          c = position.container()
          position.markdown("# Preview")

          image = Image.open('./preview.jpeg')
          position.image(image, caption='Preview')
          position.session_state.image_available = True
      except Exception as e:
          position.write("error loading preview")
          position.write(e)
      print("preview")

st.sidebar.button("Preview Image from :camera:", on_click=preview)

so for some reason the empty() does not empty the container, but just appends

Yes its a good idea to do this with watchdog, but I am lucky, I can just poll for the image. SO the real issue I have is with st.empty in a container within a tab

note i removed some more stuff, but left CCAPI in as this is my python library to interface with a canon camera

mport streamlit as st
from PIL import Image
import streamlit as st
import requests

from ccapi.ccapi import CCAPI

tab_preview, tab_general, tab_focusbracketing = \
    st.tabs(["Preview", "Settings", "Focusbraceting"])


camera = CCAPI()

settings = camera.get_settings()


def preview(position=tab_preview):
    camera = CCAPI()
    device = camera.get_deviceinformation()

    name = "./preview.jpeg"
    # camera.release()
    r = camera.liveview(display="on", size="medium")
    r = camera.get_liveview_image(name)
    # camera.preview(name)

    position.markdown("# Preview")
    while True:
      try:
          image = Image.open('./preview.jpeg')
          position.image(image, caption='Preview')
          position.session_state.image_available = True
      except Exception as e:
          position.write("error loading preview")
          position.write(e)
      print("preview")


st.sidebar.button("Preview Image from :camera:", on_click=preview)

So somehow I have to convert the code in the loop in some fashion so that an st.empty is used so that when I repeat the look it overwrites the image.
I also want to preserve that the tab is on top

I thought first I can do

while True:
  area = position.empty()
  area = position.container
  area.markdown("# Preview")
  image = Image.open('./preview.jpeg')
  area.image(image, caption='Preview')

but for some reason this seems not to work

Ah, in that case, the fix isn’t too bad – just declare the area = position.empty() outside of the while loop, so it doesn’t get created over and over again.

Here’s a simplified version of the script that I think does what you want:

import time

import streamlit as st
from randimage import get_random_image

tab_preview, tab_general, tab_focusbracketing = st.tabs(
    ["Preview", "Settings", "Focusbraceting"]
)

position = tab_preview

area = position.empty()
while True:
    with area.container():
        st.markdown("# Preview")
        image = get_random_image((128, 128))
        st.image(image, caption="Preview")
        time.sleep(1)

thanks this worked. previously i use the empty in the loop also but did not realize its just a placeholder.

In addition I changed the st. to area. and that works now

thanks a lot

1 Like

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.