How to cancel a background task

When using QueryOp with a progress bar to run a background task, is it possible to cancel the background task?

Your background task needs to be written to handle such a case. Eg if it has a long-running loop, it could check for a ‘abort’ flag set somewhere, and exit early if it is set.

Are there any examples? I’m developing a progress bar for rescheduling cards in FSRS4Anki Helper.

The main loop is here:

    for nid, cards in siblings.items():
        best_due_dates = disperse(cards)
        for cid, due in best_due_dates.items():
            card = mw.col.get_card(cid)
            last_revlog = mw.col.card_stats_data(cid).revlog[0]
            last_due = get_last_review_date(last_revlog)
            card = update_card_due_ivl(card, last_revlog, due - last_due)
            card.flush()
            card_cnt += 1
        note_cnt += 1

        if note_cnt % 500 == 499:
            finished_text = f"{text_from_reschedule +', ' if text_from_reschedule != '' else ''}{card_cnt} cards in {note_cnt} notes dispersed."
            mw.taskman.run_on_main(lambda: mw.progress.update(value=note_cnt, label=f"{note_cnt}/{len(siblings)} notes dispersed"))

I’d be interested in an example well. In particular, is there a way to communicate to the main loop that the “x” button on the dialog has been pressed, so we can handle it ourselves?

After testing for a night, I find out the solution.

def task_function(arg):
    cancel_flag == False

    main loop:
        if cancel_flag:
            break

...
...
...
     
        def set_cancel_flag():
            nonlocal cancel_flag
            cancel_flag = True

        mw.taskman.run_on_main(lambda: set_cancel_flag() if mw.progress.want_cancel() else None)

Edited: Thanks to @dae, I refined the code:


def task(arg):
    mw.taskman.run_in_background(lambda: task_function(arg))

def task_function(arg):
...
...
...

    main loop:
...
...
...

        if mw.progress.want_cancel()
            break

3 Likes

mw.progress.want_cancel() is probably safe to call without run_on_main, as I don’t think it actually touches any Qt objects.

2 Likes