CODE QUESTION: Timer to animate a progress bar?

I’m a seasoned web/javascript developer, but new to Tidbyt, Starlark, and Python. I’m trying to make an app but running into a bit of a wall, most likely due to my unfamiliarity with these things. My goal is to have a trivia app that shows a question, and gives you, say, 10 seconds before the answer is shown. During that 10 seconds I want basically a render.Box to grow in width from 0 to 100% across the top of the screen, basically a progress bar. I’ve got some code that seems to be running the 10 second timer correctly but I can’t figure out how to hook up the width of the Box to it properly. Here’s what I currently have:

load("render.star", "render")
load("time.star", "time")
load("math.star", "math")

# define progressBar (set to 0x0 for now so it doesnt display anything yet)
progressBar = render.Box(width=0, height=0, color="#f00")

def startTimer(seconds):
  start = time.now()
  elapsed = 0
  while elapsed < seconds:
    delta = time.now() - start # how much time has passed since we started?
    elapsed = delta.seconds # get int value of above 
    percent = elapsed / seconds # what percent of 'seconds' has elapsed so far?
    w = math.floor(64 * percent) # get same percent of 64 pixels width and round down
    print(delta, delta.seconds, elapsed, percent, w)
    progressBar = render.Box(width=w, height=5, color="#f00") 
  

def main(config):
  startTimer(10)
  return render.Root(
    child = render.Column(
      children = [
        progressBar,
        render.Text(content="TEMP TEXT")
      ]
    )
  )

When this runs, the print statement appears to output correct values for all the variables, so it appears its tracking the time correctly, but nothing is displayed on screen. As soon as the 10 seconds are up and the while loop completes, the entire screen fills with red pixels, almost as if it’s repeatedly rendering my red progress bar over and over.

What am I missing to get this to display properly?

I am far less experienced at this than others, so I think others’ responses will likely help you more.

That said, I think the main issue with the code you included is that it is picturing the display as operating in real time instead of building an animation slide by slide that will then be played back at a later time. The latter is how the Tidbyt works.

For this reason, instead of relying on a timer and checking the time now compared to before, you should build an animation using Pixlet’s render.Animation

Time can be managed by setting a delay in the animation that will determine the number of milliseconds that each frame will displaying, and then adding successive images to the animation that each have a slightly longer progress bar than the last one.

Not sure if this helps.

1 Like

Ok that makes sense, I think I can set that up. But what about having the animation NOT loop? Is there a way to just pause it on the last frame when it’s done? And also, I’d like a textbox to appear when it’s done as well, is there a way to trigger that? That’s why I was doing all this timer stuff in code first and hoping to generate the display widgets to insert into the display tree…

Animations are timed to last 15 seconds, so the best way to prevent the animation from looping is to make the animation 15 seconds long (or just a bit longer). If you are going to release the App for others to use, there are settings to play the entire animation that can help out too, and then you can go even longer than 15 seconds.

Ok I’ve got a progress bar animation using the render.Animation() widget, as suggested, but I’m not certain how to implement the following few things:

  • The progress bar animation needs to pause on the last frame so the progress bar stays “full” (or some kind of workaround to fake it)
  • I want the answer text hidden at the start, and then shown once the progress bar completes. I could set the color to black to hide it, and white to show it, but how can i trigger that when the animation completes?
  • Once the progress bar animation completes and the answer is shown, I want everything to pause as-is for like 5 seconds or so to give the user time to read it.

Here’s my current code:

load("render.star", "render")
load("math.star", "math")

PROGRESS_BAR_COLOR = "#f00"
PROGRESS_BAR_HEIGHT = 2
PROGRESS_BAR_DURATION = 10 #seconds


def main(config):
  return render.Root(
    delay=math.floor((PROGRESS_BAR_DURATION*1000) / 65), # delay is ms/frame
    show_full_animation=True,
    child = render.Column(
      expanded=True,
      main_align="space_between",
      children = [
        render.Animation(
          children = [
            render.Box(width=0, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=1, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=2, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=3, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=4, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=5, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=6, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=7, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=8, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=9, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=10, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=11, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=12, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=13, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=14, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=15, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=16, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=17, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=18, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=19, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=20, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=21, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=22, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=23, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=24, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=25, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=26, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=27, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=28, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=29, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=30, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=31, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=32, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=33, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=34, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=35, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=36, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=37, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=38, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=39, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=40, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=41, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=42, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=43, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=44, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=45, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=46, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=47, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=48, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=49, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=50, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=51, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=52, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=53, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=54, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=55, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=56, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=57, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=58, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=59, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=60, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=61, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=62, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=63, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
            render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
          ]
        ),
        render.WrappedText(content="What is the capital of California?", font="tom-thumb"),
        render.WrappedText(content="Sacramento", font="tom-thumb")
      ]
    )
  )

One way to get “pauses” in an animation is just to put the same frame in multiple times so its net time on the display is longer than the “unpaused” frames. Does anyone know any other way?

That works for the progress bar. Im basically chaining two animations together, one for the progress bar growing, and the other for it pausing. Three’s 2 problems now:

  1. The duration of the grow and pause states are the same, there’s no way to make them different. So if I wanted to grow the bar for 10 seconds and pause for 5, I don’t see a way to do that presently…
  2. I still need to show the answer once the bar is done growing. I tried a bunch of stuff with other fake animations and sequences but couldn’t seem to get it to work…

Here’s my current code:

load("render.star", "render")
load("math.star", "math")

PROGRESS_BAR_COLOR = "#f00"
PROGRESS_BAR_HEIGHT = 2
PROGRESS_BAR_DURATION = 10 #seconds
SHOW_ANSWER_DURATION = 10 #seconds

def main(config):
  return render.Root(
    delay=math.floor((PROGRESS_BAR_DURATION*1000) / 65), # delay is ms/frame
    show_full_animation=True,
    child = render.Column(
      expanded=True,
      main_align="space_between",
      children = [
        render.Sequence(
          children = [
            render.Animation(
              children = [
                render.Box(width=0, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=1, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=2, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=3, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=4, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=5, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=6, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=7, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=8, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=9, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=10, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=11, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=12, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=13, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=14, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=15, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=16, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=17, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=18, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=19, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=20, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=21, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=22, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=23, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=24, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=25, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=26, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=27, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=28, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=29, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=30, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=31, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=32, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=33, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=34, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=35, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=36, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=37, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=38, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=39, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=40, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=41, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=42, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=43, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=44, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=45, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=46, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=47, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=48, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=49, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=50, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=51, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=52, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=53, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=54, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=55, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=56, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=57, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=58, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=59, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=60, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=61, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=62, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=63, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
              ]
            ),
            render.Animation(
              children = [
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
                render.Box(width=64, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR),
              ]
            ),
          ]
        ),
        render.WrappedText(content="What is the capital of California?", font="tom-thumb"),
        render.Marquee(
          width=64, 
          align="center", 
          child = render.Text(content="Sacramento", font="tom-thumb")
        ),
      ]
    )
  )

BTW Thanks so much for your input so far, I think I have a little better understanding how these things integrate, even if I haven’t found a complete solution yet.

If you look at the “Date Progress” app, that is currently available on the Community Repo, it has a similar growing of bars (for a different purpose and also has a pause.)

I think of the way of doing different lengths as a lowest common denominator sort of thing. If it is growing for 10 seconds, maybe there would be a total of 200 frames for it to grow for those 10 seconds, each shown for 10 / 200 = .05 seconds = 50 milliseconds. In each of those frames, the bar would be slightly larger than before, thus showing the appearance of growing. Then I’d insert 100 more frames for which the bar is not growing. Here I might add the additional text that would be the answer.

Sorry I’ve been out of town, just now getting back to this. I see how the author of the Date Progress app runs a loop in the main render component and then within that loop constructs the child and its children. That helps me solve my problem of how to display the progress bar and question/answer, and trigger the answer to show. Here’s my current code implementing that technique:

load("render.star", "render")
load("math.star", "math")

PROGRESS_BAR_COLOR = "#f00"
PROGRESS_BAR_HEIGHT = 2
QUESTION = "What is the capital of California?"
ANSWER = "Sacramento"
ANSWER_HIDE_COLOR = "#000"
ANSWER_SHOW_COLOR = "#fff"
FRAMES = 300


def main(config):
  return render.Root(
    delay=30,
    show_full_animation=True,
    child = render.Box(
      child = render.Animation(
        children = [
          get_frame(fr)
          for fr in range(FRAMES)
        ],
      ),
    ),
  )


def get_frame(fr):
  children = []
  
  percent = fr / FRAMES
  fill = math.floor(64 * percent) if percent < .5 else 64
  answer_color = ANSWER_HIDE_COLOR if percent < .5 else ANSWER_SHOW_COLOR

  # progress bar
  children.append(
    render.Row(
      expanded=True,
      children = [
        render.Box(width=fill, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR)
      ]
    )
  )
  
  # question text
  children.append(
    render.WrappedText(content=QUESTION, font="tom-thumb")
  )

  # answer text
  children.append(
    render.Marquee(width=64, align="center", child = render.Text(content=ANSWER, font="tom-thumb", color=answer_color))
  )
 
  return render.Column(
    expanded=True,
    main_align = "space_between",
    cross_align = "center",
    children = children,
  )


Two things I notice that I still have yet to solve:
1) The bar "grows" for about 5 seconds (real time), and then the answer shows for about 5 seconds. I'm having trouble understanding the relationship between seconds and frames/delay, so I'm not sure how to adjust this. I'd prefer 10-15 seconds, though I'd ultimately prefer to have this set in a variable (in seconds) so adjusting is easy down the road if needed.
2) Also it seems like the progress bar only grows about half the width its supposed to before "snapping" to full width. I'm guessing this is related to #1 and not having the correct seconds/frames math?

I feel like I'm getting closer here, any thoughts on the seconds/frames? I'm sure I'm just not understanding how to do that part properly.

The length of time the animation lasts will be the product of delay (the number of milliseconds each frame shows) and FRAMES which is the number of total frames. Right now, those numbers are 300*30 = 9000 ms = 9 seconds. If you want it to last for 15 seconds, those numbers should be adjusted to get to have a product of 15000. One example that would fix this would be to change the 30 to a 50.

To get the question to last for 10 seconds and the answer to show for 5 seconds, you could change each instance of “if percent <.5” to “if percent < 0.67”

Just mathematically speaking regarding the jumping of the bar, I’d suggest changing (64*percent) to (64/percent/.67). This way, the part won’t have a big jump up to having a length of 64 once this part of the calculation is over because the bar will have reached a length of 64 at that point. The previous calculation had the par reach only a length of 32 before it jumped to 64. This recommendation assumes you accepted the previous suggestion of changing the .5 to .67.

Firstly, I decided to keep the .5 rather than change to .67. Im ok with the bar and answer being the same length, ideally 10 seconds for now. I can always adjust that duration once I get it all working.

Next I dont think I want to change 64percent to 64/percent/.5. I think you may have meant 64percent/.5. Otherwise I get a ridiculously high number.

The problem now is that even with delay=30 and FRAMES=300 (which should equate to 9000ms=9s), it actually only takes 5 seconds of real time to grow the bar. So why the difference? What you described makes sense, but I’m not seeing that in practice…

Current code:

load("render.star", "render")
load("math.star", "math")

PROGRESS_BAR_COLOR = "#f00"
PROGRESS_BAR_HEIGHT = 2
QUESTION = "What is the capital of California?"
ANSWER = "Sacramento"
ANSWER_HIDE_COLOR = "#000"
ANSWER_SHOW_COLOR = "#fff"
DELAY = 30 # ms each frame shows
FRAMES = 300 # total frames


def main(config):
  return render.Root(
    delay=DELAY,
    show_full_animation=True,
    child = render.Box(
      child = render.Animation(
        children = [
          get_frame(fr)
          for fr in range(FRAMES)
        ],
      ),
    ),
  )


def get_frame(fr):
  children = []
  
  percent = fr / FRAMES
  fill = math.floor(64*percent/.5) if percent < .5 else 64
  answer_color = ANSWER_HIDE_COLOR if percent < .5 else ANSWER_SHOW_COLOR

  # progress bar
  children.append(
    render.Row(
      expanded=True,
      children = [
        render.Box(width=fill, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR)
      ]
    )
  )
  
  # question text
  children.append(
    render.WrappedText(content=QUESTION, font="tom-thumb")
  )

  # answer text
  children.append(
    render.Marquee(width=64, align="center", child = render.Text(content=ANSWER, font="tom-thumb", color=answer_color))
  )
 
  return render.Column(
    expanded=True,
    main_align = "space_between",
    cross_align = "center",
    children = children,
  )

The reason I suggest .67 is mostly if you are doing this as an API for yourself, you are limited to 15 seconds and so .67 would make the question 10 of those seconds and the answer the next 5.

Yes 64 * percent / (.67) indeed.

It actually is taking 4.5 seconds to grow the bar and another 4.5 seconds to show the answer. This is because the 9000 ms = 9s is not for the bar growing but for the entire animation.

You’re right, I forgot it was for the whole animation. Thanks for the reminder.

And as far being limited to 15 seconds, doesnt the show_full_animation=True property on the Root child override this?

Ok I’m 99% there! I think I’ve got everything working as intended, and although I may end up doing a question/answer trivia app, what I really wanted was to do a flag quiz app. So I swapped out the text for flag images.

Everything looks good except the Marquee for the answer is a bit wonky with a long country name that needs to scroll. SInce Im basically scrolling it the whole time and just changing colors when its time to show the answer, the horizontal position is not necessarily at the start of the scroll when its shown. So it appears like its off center. Also since its already scrolled across the screen once, it scrolls until the first character hits the left edge then stops, even though the rest of the app is still paused for a while yet. So not 100% if there’s a solution for that or not.

I tried rendering 2 different marquees, one for before and after showing the answer, thinking the 2nd one would reset since its a new marquee child, but that didnt seem to have any effect.

I also tried making the 1st one just a Text instead, thinking again that since theres no Marquee until its shown it would start in a reasonable position, but that didnt seem to change anything either.

So it works, just the country name is not ideal if its super long. If you have any thoughts on that, I’d love to hear them. Otherwise, thank you so much for taking the time to help guide me and clarify some things along the way. Definitely learned a bunch, and really appreciate it!

I should be submitting this app soon, final code below in case you’re interested:

load("render.star", "render")
load("http.star", "http")
load("random.star", "random")
load("math.star", "math")

API_URL = "https://restcountries.com/v2/all"

PROGRESS_BAR_COLOR = "#f00"
PROGRESS_BAR_HEIGHT = 2
ANSWER_HIDE_COLOR = "#000"
ANSWER_SHOW_COLOR = "#fff"

# DELAY * FRAMES = total ms for entire animation (progress bar growth + answer show duration)
# Ex. 100 DELAY * 300 FRAMES = 30000ms = 30s (half will be for the progress bar, and half for the duration of the answer being shown)
# The "half" is set by SPLIT_POINT 
DELAY = 100 # ms each frame shows
FRAMES = 300 # total frames

# what point of the animation should the progress bar be full and the answer be shown?
# Ex. 30s from calculation above, then .5 = 15s
SPLIT_POINT = .5 



def main(config):
  res = http.get(API_URL)
  if res.status_code != 200:
    fail("API request failed with status %d", res.status_code)

  countries = res.json()
  rand = random.number(0, len(countries) - 1)
  country_name = countries[rand]["name"]
  
  res2 = http.get(countries[rand]["flags"]["png"])
  if res2.status_code != 200:
    fail("Flag request failed with status %d", res2.status_code)
  country_flag = res2.body()

  return render.Root(
    delay=DELAY,
    show_full_animation=True,
    child = render.Box(
      child = render.Animation(
        children = [
          get_frame(fr, country_name, country_flag)
          for fr in range(FRAMES)
        ],
      ),
    ),
  )


def get_frame(fr, cName, cFlag):
  children = []
  
  percent = fr / FRAMES
  fill = math.floor(64*percent/SPLIT_POINT) if percent < SPLIT_POINT else 64
  answer_color = ANSWER_HIDE_COLOR if percent < SPLIT_POINT else ANSWER_SHOW_COLOR

  # progress bar
  children.append(
    render.Row(
      expanded=True,
      children = [
        render.Box(width=fill, height=PROGRESS_BAR_HEIGHT, color=PROGRESS_BAR_COLOR)
      ]
    )
  )
  
  # question text
  children.append(
    render.Row(
      expanded=True,
      main_align="center",
      children = [
        render.Image(src=cFlag, width=30, height=20),
      ]
    ),
  )

  # answer text
  children.append(
    render.Marquee(width=64, align="center", child = render.Text(content=cName, font="tom-thumb", color=answer_color))
  )
 
  return render.Column(
    expanded=True,
    main_align = "space_between",
    cross_align = "center",
    children = children,
  )