Shift elements that are already on the page?

I have figured out how to use columns to section off elements and have learned how to use functions to simplify the process of displaying grouped elements, but one thing I cant seem to figure out is how to shift elements because my goal is for every card_widget to push the previous one out of the way, that way the newest one is at the leftmost and topmost part of the page. Does anyone know how to do that?
This is the code I have so far, Ill attach some images below to give a visual representation for what im trying to achieve.

import streamlit as st

st.set_page_config(page_title="Card GUI", layout="wide")
cols = st.columns([3, 3, 3, 3, 3, 3, 3, 3], gap="small")

def card_widget(name, image, rarity, trait, col):
    cols[col].text(name)
    cols[col].image(image)
    cols[col].text('Rarity: '+ rarity)
    cols[col].text('Trait: '+ trait)

I’m not sure how to replicate this where the newest one shifts the other one to the right or to the next row like this. Thanks ahead of time!


It’s a bit complicated, but here’s one way to do it by creating an empty container in each column, using session state to keep track of all the cards, and then overwriting them in reverse order each time one gets added.

import streamlit as st

st.set_page_config(page_title="Card GUI", layout="wide")
cols = st.columns([3, 3, 3, 3, 3, 3, 3, 3], gap="small")
empties = []
for i in range(8):
    with cols[i]:
        empties.append(st.empty())

if "contents" not in st.session_state:
    st.session_state["contents"] = {}


def card_widget(name, image, rarity, trait, col):
    st.session_state["contents"][col] = {
        "name": name,
        "image": image,
        "rarity": rarity,
        "trait": trait,
    }

    for idx, content in enumerate(reversed(st.session_state["contents"].values())):
        with empties[idx].container():
            st.write(content["name"])
            st.image(content["image"])
            st.write("Rarity: " + content["rarity"])
            st.write("Trait: " + content["trait"])


card_widget("Card 1", "https://i.imgur.com/1ZQZ1Zy.png", "Common", "Human", 1)

if "card_count" not in st.session_state:
    st.session_state["card_count"] = 1

if st.button("Add card"):
    st.session_state["card_count"] += 1
    i = st.session_state["card_count"]
    card_widget(f"Card {i}", "https://i.imgur.com/1ZQZ1Zy.png", "Common", "Human", i)

Here’s what it looks like in action https://playground.streamlitapp.com/?q=b21eb6

3 Likes

Thank you so much, I just spend the past few days stuck on this issue, then realized I could use st.empty(), only I executed that approach very inefficiently and didn’t even use session_state. Your code is a massive upgrade to what I have made and it works amazing, so I want to start out by thanking you for putting in the time to help a newbie like me. I just realized though that after reaching the last column it hits the error “list index out of range” because there is no range, I wanted to make a fix for this that would prevent it from trying to add elements beyond the end of the index, and a good place to do that would be in the for loop for the card widget, but I am struggling to even understand that for loop. Also if I add a button to the card_widget function like the following code and change the final if statement to a function (also demonstrated in the following code) that I can call in a “while True:” loop, in which I cycle through and display some card data, the button seems to lose functionality and the print function doesn’t execute. Do you know a possible workaround for this also?

def card_widget(name, image, rarity, trait, upgrade_url, col):
    st.session_state["contents"][col+1] = {
        "name": name,
        "image": image,
        "rarity": rarity,
        "trait": trait,
        "upgrade_url": upgrade_url,
    }

    for idx, content in enumerate(reversed(st.session_state["contents"].values())):
        with empties[idx].container():
            st.write(content["name"])
            st.image(content["image"])
            st.write("Rarity: " + content["rarity"])
            st.write("Trait: " + content["trait"])
            if st.button('Upgrade', (random.random())*10000000):
                print(upgrade_url)
                time.sleep(5)

def create_card(name, image, rarity, trait, upgrade_url):
    st.session_state["card_count"] += 1
    i = st.session_state["card_count"]
    card_widget(name, image, rarity, trait, upgrade_url, i)

I’m not sure exactly what’s happening in your while True loop – can you share the specific code you’re using? In general, while True will give you an infinite loop, and unless you explicitly put in a break statement, it will keep looping forever, and probably overwriting things over and over with no break in-between.

If you want to reuse the same 8 columns over and over again, an easy way to do it is with %

st.session_state["contents"][(col+1) % 8] = { ...

This will make the contents dict entries never go above 7

That’s a pretty good solution right there, thanks! I don’t mind showing you the code, but I don’t want to show the code publicly, I wouldn’t mind sending you the project privately though, and might actually be very helpful for someone more experienced to review the project.

This is the basic logic for my code, but it is way more complex than this, where I didn’t want to include the code that makes my application work I instead commented out a section to describe the logic that goes on:

import streamlit as st

st.set_page_config(page_title="Card Game GUI", layout="wide")
cols1 = st.columns([6, 3, 3, 3, 3, 3, 3, 6], gap="small")
cols2 = st.columns([6, 3, 3, 3, 3, 3, 3, 6], gap="small")
empties = []

for i in range(6):
    with cols1[1+i]:
        empties.append(st.empty())
for i in range(6):
    with cols2[1+i]:
        empties.append(st.empty())

if "contents" not in st.session_state:
    st.session_state["contents"] = {}

def card_widget(name, image, rarity, trait, upgrade_url, col):
    st.session_state["contents"][(col+1)%12] = {
        "name": name,
        "image": image,
        "rarity": rarity,
        "trait": trait,
        "upgrade_url": upgrade_url,
    }

    for idx, content in enumerate(reversed(st.session_state["contents"].values())):
        with empties[idx].container():
            st.write(content["name"])
            st.image(content["image"])
            st.write("Rarity: " + content["rarity"])
            st.write("Trait: " + content["trait"])
            if st.button('Upgrade', (random.random())*10000000):
                print(upgrade_url)#I plan on adding more functionality to this button
                time.sleep(5)     #later, but for now it just prints the data passed
                                  #to upgrade_url to test if it is getting the correct
                                  #data and to test if the button can perform a
                                  #function without resetting the py script

def create_card(name, image, rarity, trait, upgrade_url):
    st.session_state["card_count"] += 1
    i = st.session_state["card_count"]
    card_widget(name, image, rarity, trait, upgrade_url, i)

while True:
    #scrape website to check if there are new cards on a website
    while new_cards_ammount > 0:
        #scrape data for specific card, this loops
        #through the card pages for every new card
        create_card(name, image, rarity, trait, upgrade_url)#then I taked the scraped
                                                            #data and update it on my 
                                                            #own page using create_card