2022-08-19 09:33:36 +01:00
from time import monotonic
2022-08-18 16:05:11 +01:00
from textual . app import App , ComposeResult
2024-11-19 11:41:26 +00:00
from textual . containers import HorizontalGroup , VerticalScroll
2022-09-09 11:38:26 +01:00
from textual . reactive import reactive
2024-11-19 11:41:26 +00:00
from textual . widgets import Button , Digits , Footer , Header
2022-08-18 16:05:11 +01:00
2024-11-18 22:01:44 +00:00
class TimeDisplay ( Digits ) :
2022-08-22 22:26:03 +01:00
""" A widget to display elapsed time. """
2022-08-18 16:05:11 +01:00
2022-09-09 11:38:26 +01:00
start_time = reactive ( monotonic )
time = reactive ( 0.0 )
total = reactive ( 0.0 )
2022-08-22 22:26:03 +01:00
def on_mount ( self ) - > None :
""" Event handler called when widget is added to the app. """
self . update_timer = self . set_interval ( 1 / 60 , self . update_time , pause = True )
def update_time ( self ) - > None :
""" Method to update time to current. """
self . time = self . total + ( monotonic ( ) - self . start_time )
2022-08-18 16:05:11 +01:00
2022-08-19 22:06:08 +01:00
def watch_time ( self , time : float ) - > None :
2022-08-22 22:26:03 +01:00
""" Called when the time attribute changes. """
2022-08-19 22:06:08 +01:00
minutes , seconds = divmod ( time , 60 )
2022-08-18 16:05:11 +01:00
hours , minutes = divmod ( minutes , 60 )
2022-08-22 11:26:39 +01:00
self . update ( f " { hours : 02,.0f } : { minutes : 02.0f } : { seconds : 05.2f } " )
2022-08-18 16:05:11 +01:00
2022-08-22 22:26:03 +01:00
def start ( self ) - > None :
""" Method to start (or resume) time updating. """
self . start_time = monotonic ( )
self . update_timer . resume ( )
2022-08-18 16:05:11 +01:00
2022-08-22 22:26:03 +01:00
def stop ( self ) :
""" Method to stop the time display updating. """
self . update_timer . pause ( )
self . total + = monotonic ( ) - self . start_time
self . time = self . total
2022-08-18 16:05:11 +01:00
2022-08-22 22:26:03 +01:00
def reset ( self ) :
""" Method to reset the time display to zero. """
self . total = 0
self . time = 0
2022-08-18 16:05:11 +01:00
2024-11-19 11:41:26 +00:00
class Stopwatch ( HorizontalGroup ) :
2022-08-22 22:26:03 +01:00
""" A stopwatch widget. """
2022-08-20 20:29:19 +01:00
2022-08-22 22:26:03 +01:00
def on_button_pressed ( self , event : Button . Pressed ) - > None :
""" Event handler called when a button is pressed. """
button_id = event . button . id
time_display = self . query_one ( TimeDisplay )
if button_id == " start " :
time_display . start ( )
self . add_class ( " started " )
elif button_id == " stop " :
time_display . stop ( )
self . remove_class ( " started " )
elif button_id == " reset " :
time_display . reset ( )
2022-08-20 20:29:19 +01:00
def compose ( self ) - > ComposeResult :
2022-08-22 22:26:03 +01:00
""" Create child widgets of a stopwatch. """
2022-08-20 20:29:19 +01:00
yield Button ( " Start " , id = " start " , variant = " success " )
yield Button ( " Stop " , id = " stop " , variant = " error " )
yield Button ( " Reset " , id = " reset " )
yield TimeDisplay ( )
2022-08-18 16:05:11 +01:00
2022-08-19 22:06:08 +01:00
class StopwatchApp ( App ) :
2022-08-23 11:25:03 +01:00
""" A Textual app to manage stopwatches. """
2022-08-18 16:05:11 +01:00
2023-08-22 11:48:17 +01:00
CSS_PATH = " stopwatch.tcss "
2022-09-18 22:02:08 +01:00
2022-09-09 11:38:26 +01:00
BINDINGS = [
( " d " , " toggle_dark " , " Toggle dark mode " ) ,
( " a " , " add_stopwatch " , " Add " ) ,
( " r " , " remove_stopwatch " , " Remove " ) ,
]
2022-08-18 16:05:11 +01:00
def compose ( self ) - > ComposeResult :
2022-08-24 09:39:00 +01:00
""" Called to add widgets to the app. """
2022-08-18 16:05:11 +01:00
yield Header ( )
yield Footer ( )
2024-11-19 11:41:26 +00:00
yield VerticalScroll ( Stopwatch ( ) , Stopwatch ( ) , Stopwatch ( ) , id = " timers " )
2022-08-18 16:05:11 +01:00
2022-08-22 22:26:03 +01:00
def action_add_stopwatch ( self ) - > None :
2022-08-19 11:06:10 +01:00
""" An action to add a timer. """
2022-08-22 22:26:03 +01:00
new_stopwatch = Stopwatch ( )
self . query_one ( " #timers " ) . mount ( new_stopwatch )
new_stopwatch . scroll_visible ( )
2022-08-18 16:05:11 +01:00
2022-08-22 22:26:03 +01:00
def action_remove_stopwatch ( self ) - > None :
2022-08-19 11:06:10 +01:00
""" Called to remove a timer. """
2022-08-23 11:25:03 +01:00
timers = self . query ( " Stopwatch " )
2022-08-18 16:05:11 +01:00
if timers :
timers . last ( ) . remove ( )
2022-08-19 17:32:56 +01:00
def action_toggle_dark ( self ) - > None :
2022-08-22 22:38:08 +01:00
""" An action to toggle dark mode. """
2024-10-24 16:54:54 +01:00
self . theme = (
" textual-dark " if self . theme == " textual-light " else " textual-light "
)
2022-08-19 17:32:56 +01:00
2022-08-18 16:05:11 +01:00
if __name__ == " __main__ " :
2022-09-18 22:02:08 +01:00
app = StopwatchApp ( )
2022-08-18 16:05:11 +01:00
app . run ( )