Skip to content

Instantly share code, notes, and snippets.

@mturoci
Created September 20, 2022 16:08
Show Gist options
  • Save mturoci/4c5d1e684896f689ad54caf59f22e19b to your computer and use it in GitHub Desktop.
Save mturoci/4c5d1e684896f689ad54caf59f22e19b to your computer and use it in GitHub Desktop.
import asyncio
import time
import concurrent.futures
from threading import Event
from h2o_wave import main, app, Q, ui
# This takes a lot of time (compute heavy).
def blocking_function(q: Q, loop: asyncio.AbstractEventLoop):
count = 0
total = 10
future = None
while count < total:
# Check if cancelled.
if q.client.event.is_set():
asyncio.ensure_future(show_cancel(q), loop=loop)
return
# This blocks the main thread and prevents any other execution.
# This would be the compute in the real world.
time.sleep(1)
count += 1
# If future is not done yet, skip the update to keep the correct order.
if not future or future.done():
# Assume you are able to emit some kind of progress.
future = asyncio.ensure_future(update_ui(q, count / total), loop=loop)
# Show a notification at the end.
asyncio.ensure_future(show_notification(q), loop=loop)
async def show_cancel(q: Q):
q.page['meta'].dialog.items[0].progress.caption = 'Cancelled'
q.page['meta'].dialog.closable = True
await q.page.save()
async def show_notification(q: Q):
q.page['meta'].dialog = None
q.page['meta'].notification_bar = ui.notification_bar(
name='success_notification',
text='Job done!',
type='success',
events=['dismissed']
)
await q.page.save()
async def update_ui(q: Q, value: int):
q.page['meta'].dialog.items[0].progress.value = value
await q.page.save()
@app('/')
async def serve(q: Q):
# Unimportant, draw initial UI.
if not q.client.initialized:
q.page['meta'] = ui.meta_card(box='')
q.page['form'] = ui.form_card(box='1 1 1 1', items=[
ui.button(name='start_job', label='Start job')
])
q.client.initialized = True
# Handle start job button click.
if q.args.start_job:
q.page['meta'].dialog = ui.dialog(title='Blocking job', blocking=True, items=[
ui.progress(label='Progress', value=0),
ui.button(name='cancel', label='Cancel')
])
# Do not run like this - will block the whole thread - freeze the app.
# blocking_function(q, loop)
# Get the current event loop - will be used for
# running async functions within the blocking.
loop = asyncio.get_event_loop()
# Create an event to use for cancellation.
q.client.event = Event()
with concurrent.futures.ThreadPoolExecutor() as pool:
await q.exec(pool, blocking_function, q, loop)
if q.args.cancel:
q.client.event.set()
# Unimportant, just handle notification dismissal.
if q.events.success_notification and q.events.success_notification.dismissed:
q.page['meta'].notification_bar = None
await q.page.save()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment