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