[Question about add-on development] How to use popup to display the details of exceptions in background task?

When the python code raises an exception in the main thread, a popup will show the details about the exception:

But when I move the task into background, the error will be silent. The progress bar gets stuck:

image

Are there any methods to use popup to display the details of exceptions in background task?

It appears you’re trying to call GUI code inside the background task (.setChecked). This can result in Anki hanging/crashing.

The currently recommended approach is to use CollectionOp/QueryOp to run background tasks. See Background Operations - Writing Anki Add-ons for an example. You can add a failure handler and call showWarning/tooltip inside it.

But I didn’t call GUI code. The popup of error is called by Anki itself.

I mean the menu_auto_disperse.setCheked line in your screenshot (If that’s called in the background). Can you post a code example?

The Error is just a example of popup. It is not the error happened in my background task.

For example, if I raise ValueException in the background task.

What if fsrs.due_cnt_perday_from_first_day[due_before] raise a indexError?

My guess is that the progress dialog is getting stuck because mw.progress.finish() is not getting called when an exception occurs. I recommend removing the on_finish functions and putting the code in the on_done handler of mw.taskman.run_in_background() instead:

diff --git a/disperse_siblings.py b/disperse_siblings.py
index d6035a4..c52e1a4 100644
--- a/disperse_siblings.py
+++ b/disperse_siblings.py
@@ -76,7 +76,13 @@ def disperse(siblings):
     return best_due_dates
 
 def disperse_siblings(did, filter_flag=False, filtered_nid_string="", text_from_reschedule=""):
-    mw.taskman.run_in_background(lambda: disperse_siblings_backgroud(did, filter_flag, filtered_nid_string, text_from_reschedule))  
+    def on_done(future):
+        mw.progress.finish()
+        tooltip(future.result())
+        mw.col.reset()
+        mw.reset()
+
+    mw.taskman.run_in_background(lambda: disperse_siblings_backgroud(did, filter_flag, filtered_nid_string, text_from_reschedule), on_done)
 
 def disperse_siblings_backgroud(did, filter_flag=False, filtered_nid_string="", text_from_reschedule=""):
     custom_scheduler = check_fsrs4anki(mw.col.all_config())
@@ -126,13 +132,6 @@ def disperse_siblings_backgroud(did, filter_flag=False, filtered_nid_string="",

     finished_text = f"{text_from_reschedule +', ' if text_from_reschedule != '' else ''}{card_cnt} cards in {note_cnt} notes dispersed."

-    def on_finish():
-        tooltip(finished_text)
-        mw.progress.finish()
-        mw.col.reset()
-        mw.reset()
-
-    mw.taskman.run_on_main(on_finish)
     return finished_text

 # https://stackoverflow.com/questions/68180974/given-n-points-where-each-point-has-its-own-range-adjust-all-points-to-maximize      
diff --git a/reschedule.py b/reschedule.py
index ae91c9c..b12e908 100644
--- a/reschedule.py
+++ b/reschedule.py
@@ -101,17 +101,21 @@ class FSRS:
 def reschedule(did, recent=False, filter_flag=False, filtered_cids={}, filtered_nid_string=""):

     def on_done(future):
+        mw.progress.finish()
         tooltip(future.result())
+        mw.col.reset()
+        mw.reset()
+

     if filter_flag and len(filtered_cids) > 0:
-        fut = mw.taskman.run_in_background(lambda: reschedule_background(did, recent, filter_flag, filtered_cids))
+        fut = mw.taskman.run_in_background(lambda: reschedule_background(did, recent, filter_flag, filtered_cids), on_done)
         config = Config()
         config.load()
         if config.auto_disperse:
             text = fut.result()
             fut = mw.taskman.run_in_background(lambda: disperse_siblings_backgroud(did, filter_flag, filtered_nid_string, text_from_reschedule=text), on_done)
     else:
-        fut = mw.taskman.run_in_background(lambda: reschedule_background(did, recent, filter_flag, filtered_cids))
+        fut = mw.taskman.run_in_background(lambda: reschedule_background(did, recent, filter_flag, filtered_cids), on_done)

     return fut

@@ -287,11 +291,4 @@ def reschedule_background(did, recent=False, filter_flag=False, filtered_cids={}

     finished_text = f"{cnt} cards rescheduled"

-    def on_finish():
-        tooltip(finished_text)
-        mw.progress.finish()
-        mw.col.reset()
-        mw.reset()
-    
-    mw.taskman.run_on_main(on_finish)
     return finished_text

An important thing to notice is that mw.progress.finish() should be called before future.result() to ensure the progress dialog is proerly closed if an exception occurs.

2 Likes

Thank you very much!

This topic was automatically closed 30 days after the last reply. New replies are no longer allowed.