首页 > 解决方案 > 从 ipywidget 取消 Jupyter 执行

问题描述

我想创建一个按钮 ipywidget,允许在点击 Jupyter 中的取消按钮时停止执行。我见过几个例子(包括这个),但它们都需要能够修改进程本身正在运行的函数。

这听起来很简单,但我开始认为这是不可能的。然而,我对多进程缺乏了解可能正是我沮丧的原因。我尝试过的版本都没有工作。没有人真正运行我的功能(或者他们可能在后台运行)并且似乎没有人成功取消执行。

到目前为止,我已经尝试过:

选项1

import ipywidgets as widgets
from IPython.display import display
from functools import wraps
from time import sleep
from multiprocessing import Process as Thread


def stoppable_fun(stopper):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            threaded_fun = Thread(target=func, args=args, kwargs=kwargs)
            threaded_fun.start()
            while threaded_fun.is_alive():
                print("alive")
                if not stopper.is_alive():
                    print("stopped", stopper.is_alive())
                    threaded_fun.terminate()
                sleep(0.05)
        return wrapper
    return decorator


cancel_button_ly = widgets.Layout(width="auto", visibility="hidden")
cancel_button = widgets.Button(description="Cancel", layout=cancel_button_ly)
cancel_flag = False


def cancel_button_event_handler(obj):
    print("Cancel")
    cancel_flag = True


cancel_button.on_click(cancel_button_event_handler)


def check_cancel(timeout):
    print("check def")
    i = 0
    while not cancel_flag and i < timeout:
        print("checking")
        sleep(0.1)
        i += 0.1
        print("check", cancel_flag)


class CancelableButton(widgets.HBox):
    
    def __init__(self, *args, **kwargs):
        self.button = widgets.Button(*args, **kwargs)
        widgets.HBox.__init__(self, children=[self.button, cancel_button])

    def on_click(self, func, *args, **kwargs):

        def new_function(*args, **kwargs):
            self.button.disabled = True
            cancel_button_ly.visibility = "visible"
            sleep(0.5)
            thread_check_cancel = Thread(target=check_cancel, args=(10,))
            stoppable = stoppable_fun(thread_check_cancel)(func)
            try:
        
                thread_check_cancel.start()
                stoppable()
                print("is check alive?", thread_check_cancel.is_alive())
            except KeyboardInterrupt:
                print("Canceled!")
            finally:
                thread_check_cancel.terminate()
                cancel_button_ly.visibility = "hidden"
                self.button.disabled = False
                cancel_flag = False
        self.button.on_click(new_function, *args, **kwargs)

选项 2

import ipywidgets as widgets
from IPython.display import display
from time import sleep
import multiprocessing

cancel_event = multiprocessing.Event()

def cancel_button_event_handler(obj):
    print("Cancelled")
    cancel_event.set()

cancel_button = widgets.Button(description="Cancel")
cancel_button.on_click(cancel_button_event_handler)

def func(obj):
    for i in range(10):
        print("Runing calculation", i)
        sleep(0.5)

threaded_func = multiprocessing.Process(target=func)

def new_function(obj):
    threaded_func = multiprocessing.Process(target=func)
    threaded_func.start()
    while threaded_func.is_alive():
        print("alive")
        sleep(0.1)
        if cancel_event.is_set():
            print("cancelled")
            threaded_func.terminate()
    cancel_event.clear()

button = widgets.Button(description="Calculate")
button.on_click(new_function)

display(button)
display(cancel_button)

选项 3

import ipywidgets as widgets
from IPython.display import display
from functools import wraps
from time import sleep
import multiprocessing


class CancelableButton(widgets.HBox):

    def __init__(self, func):
        self.cancel_button = widgets.Button(description="Cancel")
        self.button = widgets.Button(description="Calculate")
        widgets.HBox.__init__(self, children=[self.button, self.cancel_button])

    def on_click(self, func):

        def new_function(obj):
            threaded_func = multiprocessing.Process(target=func)

            def cancel_button_event_handler(obj):
                print("Cancelled")
                threaded_func.terminate()

            self.cancel_button.on_click(cancel_button_event_handler)
            self.button.disabled = True
            try:
                print("Start")
                threaded_func.start()
                print("Calculating", threaded_func.is_alive())
                threaded_func.join()
                print("Done", threaded_func.exitcode)
            finally:
                self.button.disabled = False

        self.button.on_click(new_function)


btn_ly = widgets.Layout(width="auto")

def func(obj):
    for i in range(10):
        print("Runing calculation", i)
        sleep(0.5)

btn = CancelableButton(func)
btn_o = widgets.Output()
display(btn, btn_o)

btn.on_click(func)

任何帮助澄清我做错了什么,或者为什么不可能实现我正在尝试的事情,我们将不胜感激。

标签: pythonjupyterpython-multiprocessingpython-multithreadingipywidgets

解决方案


推荐阅读