21
21
# pylint: disable=import-error
22
22
"""Global Qubes Config tool."""
23
23
import sys
24
+ import threading
25
+ import time
24
26
from typing import Dict , Optional , List , Union , Any
25
27
from html import escape
26
28
import importlib .resources
27
29
import logging
28
30
29
31
import qubesadmin
30
- import qubesadmin .events
31
32
import qubesadmin .exc
32
33
import qubesadmin .vm
33
34
from ..widgets .gtk_utils import (
36
37
load_theme ,
37
38
is_theme_light ,
38
39
resize_window_to_reasonable ,
40
+ show_dialog ,
39
41
)
40
42
from ..widgets .gtk_widgets import ProgressBarDialog , ViewportHandler
41
43
from ..widgets .utils import open_url_in_disposable
@@ -267,6 +269,8 @@ def __init__(self, qapp: qubesadmin.Qubes, policy_manager: PolicyManager):
267
269
self .progress_bar_dialog = ProgressBarDialog (
268
270
self , _ ("Loading system settings..." )
269
271
)
272
+ self .save_thread : threading .Thread | None = None
273
+ self .save_errors : List [str ] = []
270
274
self .handlers : Dict [str , PageHandler ] = {}
271
275
272
276
def do_command_line (self , command_line ):
@@ -555,13 +559,11 @@ def get_current_page(self) -> Optional[PageHandler]:
555
559
self .main_notebook .get_nth_page (page_num ).get_name (), None
556
560
)
557
561
558
- def save_page (self , page : PageHandler ) -> bool :
559
- """Save provided page and emit any necessary signals;
560
- return True if successful, False otherwise"""
562
+ def perform_save (self , page ):
563
+ """Actual saving thread"""
561
564
# pylint: disable=protected-access
562
565
# need to invalidate cache before and after saving to avoid
563
566
# stale cache
564
-
565
567
self .qapp ._invalidate_cache_all ()
566
568
try :
567
569
page .save ()
@@ -572,11 +574,52 @@ def save_page(self, page: PageHandler) -> bool:
572
574
self .qapp ._invalidate_cache_all ()
573
575
page .reset ()
574
576
except Exception as ex :
577
+ self .save_errors .append (str (ex ))
578
+
579
+ def save_page (self , page : PageHandler ) -> bool :
580
+ """Save provided page and emit any necessary signals;
581
+ return True if successful, False otherwise"""
582
+ self .save_thread = threading .Thread (target = self .perform_save , args = [page ])
583
+ self .save_thread .start ()
584
+
585
+ spinner = None
586
+ dialog = None
587
+ time .sleep (0.01 )
588
+
589
+ if self .save_thread .is_alive ():
590
+ # show waiting dialog
591
+ spinner = Gtk .Spinner ()
592
+ spinner .start ()
593
+ dialog = show_dialog (
594
+ self .main_window ,
595
+ _ ("Saving changes" ),
596
+ _ ("Saving global configuration changes..." ),
597
+ {},
598
+ spinner ,
599
+ )
600
+ dialog .set_deletable (False )
601
+ dialog .show ()
602
+
603
+ # wait for thread and spin spinner
604
+ while self .save_thread .is_alive ():
605
+ while Gtk .events_pending ():
606
+ Gtk .main_iteration ()
607
+ time .sleep (0.1 )
608
+
609
+ # cleanup
610
+ if spinner :
611
+ spinner .stop ()
612
+ if dialog :
613
+ dialog .destroy ()
614
+
615
+ if self .save_errors :
575
616
show_error (
576
617
self .main_window ,
577
618
_ ("Could not save changes" ),
578
- _ ("The following error occurred: " ) + escape (str (ex )),
619
+ _ ("The following error occurred: " )
620
+ + escape ("\n " .join (self .save_errors )),
579
621
)
622
+ self .save_errors = []
580
623
return False
581
624
return True
582
625
0 commit comments