Trying to make a timer app with anvil

trying to make a timer that displays in format HH:MM:SS instead of .seconds

tried doing this;

 def timer_progress_tick(self, **event_args):
     """This method is called Every [interval] seconds. Does not trigger
     if [interval] is 0."""
     if self.start_time is not None:
      self.label_time_elapsed.text = "{}".format(
         (datetime.now() - self.start_time).strptime('%H:%M:%S', self))

Get this bug;

AttributeError: ‘timedelta’ object has no attribute ‘strptime’ at [Form1, line 40](javascript:void(0))

my imports;

**from anvil import *
import anvil.server
import anvil.tables as tables
import anvil.tables.query as q
from anvil.tables import app_tables
from datetime import datetime

  • List item

**

Depends what start_time is. If it’s a counter updated by the timer, then maybe something like this :

import time
...
self.label_time_elapsed.text = "{}".format(
  time.strftime("%H:%M:%S", time.gmtime(self.start_time))
)

Ah, on re-reading it looks like you are working out elapsed time from a start time rather than a counter per se, and I see @shaun has a better answer :slight_smile:

Hi @thefool363!

There are two similar methods for converting datetimes to and from strings.

  • strptime is the method to parse a string and return a datetime - I assume the p stands for ‘parse’.
  • strftime is the method to format a datetime into a string - I assume the f stands for format.

You would want to use strftime in this case to convert a datetime to a string.

But unfortunately, when you subtract one datetime from another, as in:

datetime.now() - self.start_time

you get a timedelta - this is Python’s way of representing time differences. timedelta objects don’t have a strftime method. Instead, you can get the total number of seconds the timedelta represents and work out the hours, minutes and seconds from that.

If I do this:

    self.elapsed = timedelta(3, 25, 989321)
    
    total_seconds = int(self.elapsed.total_seconds())
    hours = str(total_seconds / 3600).rjust(2, '0')
    minutes = str((total_seconds % 3600) / 60).rjust(2, '0')
    seconds = str((total_seconds % 60)).rjust(2, '0')
    
    print('{}:{}:{}'.format(hours, minutes, seconds))

I get this:

72:00:25

1 Like
     def timer_progress_tick(self, **event_args):
         """This method is called Every [interval] seconds. Does not trigger
         if [interval] is 0."""
         self.elapsed = timedelta()
    
         total_seconds = int(self.elapsed.total_seconds())
         hours = str(total_seconds / 3600).rjust(2, '0')
         minutes = str((total_seconds % 3600) / 60).rjust(2, '0')
         seconds = str((total_seconds % 60)).rjust(2, '0')
    
         if self.start_time is not None:
          self.label_time_elapsed.text = "{}".format(hours, minutes, 
          seconds)

this is what i tried in the def timer_progress_tick(self, **event_args):;

NameError: name 'timedelta' is not defined at [Form1, line 40](javascript:void(0))

another method i tried is;

import datetime

def cust(seconds):
    m, s = divmod(seconds, 60)
    h, m = divmod(m, 60)
    return "{hour}:{min}:{sec}".format(hour=int(h), min=int(m), sec=int(s))    

now = datetime.datetime.now()
d = (now - datetime.datetime.strptime("2016-01-01", "%Y-%m-%d")).total_seconds()
print(cust(d))

with the format of def imter_progress_tick(self, **event_args): being

cust(datetime.now() - self.start_time).total_seconds())

seems its works, but there are other complications

Regarding your first error, you would need to import timedelta:

from datetime import timedelta

The second code block you shared looks like it would do the job :slight_smile:

(By the way, when you put code blocks in your posts, it’s helpful if you put them between triple backticks ```, a bit like this:

```
Your code goes here
```

)

now i tried using this method because with the method you proposed after importing “timdelta” , the timer doesn’t show seconds or hours and minutes. with this program, the timer does show elapsed in HH:MM:SS

 from anvil import *
import anvil.server
import anvil.tables as tables
import anvil.tables.query as q
from anvil.tables import app_tables
from datetime import datetime, timedelta





class Form1(Form1Template):

  def __init__(self, **properties):
    # Set Form properties and Data Bindings.
    self.init_components(**properties)

    # Any code you write here will run when the form opens.
    self.start_time = None

  def button_start_click(self, **event_args):
    """This method is called when the button is clicked"""
    self.start_time = datetime.now()
    self.button_start.visible = False
    self.button_stop.visible = True

  def button_stop_click(self, **event_args):
    """This method is called when the button is clicked"""
    self.stop_time = datetime.now()
    self.total_time = self.stop_time - self.start_time
    anvil.server.call('add_session', self.start_time, 
 self.total_time.seconds)

    self.start_time = None
    self.button_start.visible = True
    self.button_stop.visible = False

  def timer_progress_tick(self, **event_args):
     """This method is called Every [interval] seconds. Does not trigger
     if [interval] is 0."""
     self.total_time = (datetime.now() - self.start_time)
     minutes, seconds = divmod(self.total_time.seconds + self.total_time.days * 86400, 60)
     hours, minutes = divmod(minutes, 60)
    
     
     if self.start_time is not None:
      self.label_time_elapsed.text = '{:d}:{:02d}:{:02d}'.format(hours,minutes,seconds)


now i get the bug of when i hit the stop button. it seems save correctly in terms of seconds in to the database though;

“TypeError: unsupported operand type(s) for Sub: ‘datetime’ and ‘NoneType’ at [Form1, line 41]”

Suggestion: You may want to show people the actual code at line 41 (and above), and indicate which of those lines of code is line 41. Those are important clues for anyone trying to help you.

I edited it. Sorry, new to the forums and new to programming as a whole

No problem and welcome to the forum. I think @p.colbert was suggesting that you indicate which line is line 41. At the moment, we can see the error, but not the line of code that corresponds to that error. Sometimes this can be determined, but it just makes it a easier for others to quickly see what the issue is without having to sleuth around.

One other thing that I find to be very helpful in terms of getting some quick assistance is to share a clone link to your actual app (if possible).

You can click the :gear: icon in the IDE and select the link for “sharing your source code with others”. In this way others can click that link and get an exact copy of your app, which makes debugging easier in many cases.

One other thing that I find to be very helpful in terms of getting some quick assistance is to share a clone link to your actual app (if possible).

You can click the :gear: icon in the IDE and select the link for “sharing your source code with others”. In this way others can click that link and get an exact copy of your app, which makes debugging easier in many cases.

This is a good suggestion!

Regarding your particular error: Error messages in Python are usually really helpful, but this one is uncharacteristically cryptic.

“TypeError: unsupported operand type(s) for Sub: ‘datetime’ and ‘NoneType’ at [Form1, line 41]”

An operand is the thing that an operator operates on: if I have a - b, then a and b are the operands. - is the operator.

‘unsupported operand type(s)’ means that some operator in your code has been given one or more operands of a type that it doesn’t know how to deal with.

‘for Sub’: this refers to the subtract operator.

‘datetime’ and ‘NoneType’: this means that the types of the operands were datetime and NoneType.

The code on line 41 of Form1 is:

     self.total_time = (datetime.now() - self.start_time)

So we can see why one of the operands was a datetime - that must be the datetime.now(). The other operand is of type NoneType. There’s only one thing that can ever be of type NoneType, and that’s None (None is special in Python, there’s only ever one of it - it’s a singleton. All variables that are set to None are references to the same instance.)

So your problem is that self.start_time was None.

You’re setting self.start_time to None on line 34. It looks like you’ve stopped your timing process at that point, so you should disable the Timer component as well. You can do that by setting the Timer component’s interval to 0:

self.timer_1.interval = 0

You can set it back to something > 0 in the button_start_click method, so it’s running again when you re-start the clock.

1 Like

https://anvil.works/build#clone:S4K6LRFPR6OS4VOS=4XSA7OR3UJYF4XUUQQCBO4UQ like this?

2 Likes