最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

python - How to interrupt a while loop using a button click with Jupyter Notebook - Stack Overflow

programmeradmin2浏览0评论

I have a dashboard that gets node count, etc. from slurm and displays that as a button grid, and is refreshed every n seconds. I want the ability to click on any of the buttons and have something happen, e.g. get additional info, run a script, etc.

The problem is that interactivity with the buttons in the grid does not work in a while loop.

In the following test code, if event_loop = False, grid button clicks work as expected, but there is no update (no loop). If event_loop = True, the grid is updated, but the grid buttons no longer work.

from ipywidgets import GridspecLayout, Button, Layout, Output
from IPython.display import display, clear_output
import time

nodes = [1, 2, 3, 4, 5, 6]
rows = 3
cols = 3
event_loop = False

button_go = Button(
    description='Go',
    disabled=False,
    ayout={'width': '150px'}
)

output_command = Output()
output_command.append_stdout('')

@output_command.capture()
def button_go_clicked(event):
    print(event)
    if event_loop:
        while event_loop:
            update_grid(rows, cols, nodes)
            time.sleep(10)
    else:
        update_grid(rows, cols, nodes) 

button_go.on_click(button_go_clicked)

def create_dynamic_grid(rows, cols, nodes):
    grid = GridspecLayout(rows, cols)
    node = 0
    for i in range(rows):
        for j in range(cols):
            if node < len(nodes):
                if nodes[node]:
                    button_grid = Button(description='Node_{}-{}'.format(i, j), button_style='primary', layout=Layout(width='auto', height='auto'))
                
                grid[i, j] = button_grid
                grid[i, j].on_click(button_grid_clicked)

                node += 1
    return grid

output_grid = Output()

def update_grid(rows, cols, nodes):
    with output_grid:
         output_grid.clear_output()
         new_grid = create_dynamic_grid(rows, cols, nodes)
         display(new_grid)

@output_command.capture()
def button_grid_clicked(btn):
    print(btn)

display(button_go, 
        output_grid, 
        output_command)

I need some way to 1. immediately interrupt/pause the loop when clicking a grid button, and 2. maintain/resume the loop refresh time of n seconds.

After searching around on google, I found two interesting methods and integrated them. The first is from 'Kill a loop with Button Jupyter Notebook?'

The second was from a google Generative AI from the prompt 'python immediately break from sleep', which generated the following:

import time
import threading

def sleeper(seconds, event):
    while seconds > 0 and not event.is_set():
        time.sleep(min(seconds, 1))
        seconds -= 1
    if event.is_set():
      print("Sleep interrupted")
    else:
      print("Slept for", seconds, "seconds")

def main():
    sleep_event = threading.Event()
    sleep_thread = threading.Thread(target=sleeper, args=(5, sleep_event))
    sleep_thread.start()

    input("Press Enter to interrupt sleep...\n")
    sleep_event.set()
    sleep_thread.join()
    print("Continuing main thread")

if __name__ == "__main__":
    main() 

I have attempted to integrate these into my test code, my apologies if it's a bit of a jumble. The results are that when event_loop = True the grid is updated constantly, and if event_loop = False, no update.

from ipywidgets import GridspecLayout, Button, Output, Layout
from IPython.display import display, clear_output
import asyncio, threading, time

nodes = [1, 2, 3, 4, 5, 6]
rows = 3
cols = 3
event_loop = False
sleep_event = None
sleep_thread = None

button_go = Button(
    description='Go',
    disabled=False,
    layout={'width': '150px'}
)

output_command = Output()
output_command.append_stdout('')

async def function():
    while event_loop:
        print('function() event_loop: ', event_loop)
    print('done')
    
def sleeper(seconds, event):
    while seconds > 0 and not event.is_set():
        print('sleeping: ', event)
        print('sleep_time: ', seconds)
        time.sleep(seconds)
        if event.is_set():
            print('sleep interrupted: ', event)

def set_sleeper(sleep_time):
    global sleep_event
    global sleep_thread
    sleep_event = threading.Event()
    sleep_thread = threading.Thread(target=sleeper, args=(sleep_time, sleep_event))
    sleep_thread.start()
    return sleep_event

@output_command.capture()
def button_go_clicked(event):
    loop = asyncio.get_event_loop()    
    loop.create_task(function())
    print('button_go_clicked event_loop: ', event_loop)
    se = set_sleeper(10)
    print('set_sleeper: ', se)
    if event_loop:
        while event_loop:
            update_grid(rows, cols, nodes) # grid updates constantly in free run 
            # time.sleep(10) # <-- stop free run, but looking for the correct way to sleep here AND be able to interrupt on_click
    else:
        update_grid(rows, cols, nodes)

button_go.on_click(button_go_clicked)

def create_dynamic_grid(rows, cols, nodes):
    grid = GridspecLayout(rows, cols)
    node = 0
    for i in range(rows):
        for j in range(cols):
            if node < len(nodes):
                if nodes[node]:
                    button_grid = Button(description='Node_{}-{}'.format(i, j), button_style='primary', layout=Layout(width='auto', height='auto'))
                 
                grid[i, j] = button_grid
                grid[i, j].on_click(button_grid_clicked)

                node += 1
    return grid

output_grid = Output()

def update_grid(rows, cols, nodes):
    with output_grid:
         output_grid.clear_output()
         new_grid = create_dynamic_grid(rows, cols, nodes)
         display(new_grid)

@output_command.capture()
def button_grid_clicked(btn):
    print(btn)
    if event_loop:
        global sleep_event
        global sleep_thread
        print('interrupting sleep...\n')
        sleep_event.set()
        sleep_thread.join()
        print('continuing thread')

display(button_go,
        output_grid,
        output_command)

My anticipation was that set_sleeper(10) would act as the refresh time (10 seconds).

Any suggestions on how I can make this work as desired?

Regards

发布评论

评论列表(0)

  1. 暂无评论