backup_many() added and fixed; GUI backups "live" and "active"

This commit is contained in:
Christian Moser 2025-02-21 00:49:48 +01:00
parent e2302d79d4
commit 039534fbf1
Failed to extract signature
3 changed files with 110 additions and 58 deletions

View File

@ -157,13 +157,6 @@ class ArchiverManager(GObject):
def archivers(self):
return self.__archivers
def _on_archiver_backup_progress_single(self,game:Game,fraction:float,message:str):
self.emit('backup-game-progress',game,fraction,str)
def _on_archiver_backup_progress_multi(self,fraction):
self.emit('backup-progress',fraction)
@Signal(name="backup-game-progress",return_type=None,arg_types=(Game,float,str),flags=SignalFlags.RUN_FIRST)
def do_backup_game_progress(self,game,fraction,message):
pass
@ -198,7 +191,7 @@ class ArchiverManager(GObject):
if not multi_backups:
self.emit("backup-progress",fraction)
if self.backup_in_progress:
if not multi_backups and self.backup_in_progress:
raise RuntimeError("A backup is already in progress!!!")
self.backup_in_progress = True
@ -220,59 +213,79 @@ class ArchiverManager(GObject):
self.backup_in_progress = False
def backup_many(self,games:list[Game]):
def on_game_progress(game,fraction,message,game_progress):
with game_progress._mutex:
def on_game_progress(archiver,game,fraction,message,game_progress,mutex):
with mutex:
game_progress[game.key] = fraction
sum_fractions = 0.0
for f in game_progress.values():
sum_fractions += f
n = len(game_progress)
self._on_archiver_backup_progress_multi(sum_fractions/n if n > 0 else 0.0)
progress = ((sum_fractions / n) if n > 0 else 1.0)
if progress < 0.0:
progress = 0.0
elif progress > 1.0:
progress = 1.0
self.emit('backup-progress',progress)
def thread_function(game):
archiver = self.standard_archiver
self.backup(game,True)
if self.backup_in_progress:
raise RuntimeError("A backup is already in progress!!!")
self.backup_in_progress = True
game_list = list(games)
game_progress = dict(((game.key,0.0) for game in game_list))
game_progress._mutex = threading.RLock()
game_progress._game_progress_connection = self.connect('backup-game-progress',game_progress)
print(games)
game_progress = dict([(game.key,0.0) for game in game_list])
mutex = threading.RLock()
self.__backup_many_game_progress_connection = self.connect('backup-game-progress',on_game_progress,game_progress,mutex)
threadpool = {}
if len(game_list) > settings.backup_threads:
n = settings.backup_threads
if settings.backup_threads == 0:
backup_threads = 1
else:
backup_threads = settings.backup_threads
if len(game_list) > backup_threads:
n = backup_threads
else:
n = len(games)
print("Starting backup with {n} threads".format(n=n))
for i in range(n):
game=game_list[0]
del game_list[0]
thread = threading.Thread(thread_function,args=game,daemon=True)
threadpool.append(thread)
thread = threading.Thread(target=thread_function,args=(game,),daemon=True)
threadpool[i]=thread
thread.start()
while threadpool:
time.sleep(0.02)
for i in range(len(threadpool)):
rm_thread=[]
for i in threadpool.keys():
thread = threadpool[i]
if thread.is_alive():
continue
del threadpool[i]
if game_list:
game = game_list[0]
del game_list[0]
thread = threading.Thread(thread_function,args=game,daemon=True)
threadpool.append(thread)
thread = threading.Thread(target=thread_function,args=(game,),daemon=True)
threadpool[i] = thread
thread.start()
else:
rm_thread.append(i)
self.disconnect(game_progress._game_progress_connection)
for i in rm_thread:
del threadpool[i]
time.sleep(0.02)
self.disconnect(self.__backup_many_game_progress_connection)
self.emit("backup-finished")
self.backup_in_progress = False

View File

@ -31,7 +31,7 @@ from ._gamedialog import GameDialog
from ..game import Game,GameManager,SAVEGAME_TYPE_ICONS
from ._steam import SteamLibrariesDialog,NewSteamAppsDialog,NoNewSteamAppsDialog
from ..steam import Steam
from ._backupdialog import BackupSingleDialog
from ._backupdialog import BackupSingleDialog,BackupManyDialog
from ..archiver import ArchiverManager
@ -289,10 +289,10 @@ class GameView(Gtk.Box):
if game.is_live and game.is_active and os.path.exists(os.path.join(game.savegame_root,game.savegame_dir)):
backup_games.append(game)
# TODO:
#dialog = BackupManyDialog(parent=self.get_root(),games=backup_games)
#dialog.set_modal(False)
#dialog.present()
dialog = BackupManyDialog(parent=self.get_root(),games=backup_games)
dialog.set_modal(False)
dialog.run()
def _on_icon_column_setup(self,factory,item):
image = Gtk.Image()

View File

@ -46,12 +46,9 @@ class BackupSingleDialog(Gtk.Dialog):
self.__progressbar.set_fraction(0.0)
self.get_content_area().append(self.__progressbar)
self.set_modal(False)
self.set_modal(True)
self.__ok_button = self.add_button('Close',Gtk.ResponseType.OK)
self.__am_signal_progress = None
self.__am_signal_finished = None
def _on_propgress(self,fraction,message):
self.__progressbar.set_text(message if message else "Working ...")
@ -70,11 +67,11 @@ class BackupSingleDialog(Gtk.Dialog):
if self.__am_signal_finished is not None:
am.disconnect(self.__am_signal_finished)
self.__am_signal_finished = None
del self.__am_signal_finished
if self.__am_signal_progress is not None:
am.disconnect(self.__am_signal_progress)
self.__am_signal_progress = None
del self.__am_signal_progress
#if settings.backup_dialog_close_when_finished:
# self.response(Gtk.ResponseType.OK)
@ -97,8 +94,10 @@ class BackupSingleDialog(Gtk.Dialog):
am = ArchiverManager.get_global()
self.__am_signal_progress = am.connect('backup-game-progress',self._on_am_backup_game_progress)
self.__am_signal_finished = am.connect('backup-game-finished',self._on_am_backup_game_finished)
if not hasattr(self,'__am_signal_progress'):
self.__am_signal_progress = am.connect('backup-game-progress',self._on_am_backup_game_progress)
if not hasattr(self,'__am_signal_finished'):
self.__am_signal_finished = am.connect('backup-game-finished',self._on_am_backup_game_finished)
thread = Thread(target=_thread_func,args=(am,self.__game),daemon=True)
thread.start()
@ -143,7 +142,7 @@ class BackupGameDataSorter(Gtk.Sorter):
name1 = item1.game.name.lower()
name2 = item2.game.name.lower()
if item1.finsihed:
if item1.finished:
if not item2.finished:
return Gtk.Ordering.LARGER
elif name1 > name2:
@ -166,19 +165,23 @@ class BackupGameDataSorter(Gtk.Sorter):
return Gtk.Ordering.EQUAL
class BackupMultiDialog(Gtk.Dialog):
class BackupManyDialog(Gtk.Dialog):
logger = logger.getChild('BackupMultiDialog')
def __init__(self,parent:Gtk.Window|None=None,games:list[Game]|None=None):
Gtk.Dialog.__init__(self)
if parent:
self.set_transient_for(parent)
self.set_decorated(False)
self.set_modal(False)
self.set_modal(True)
self.set_default_size(640,480)
self.__scrolled = Gtk.ScrolledWindow()
self.__games_liststore = Gio.ListStore(BackupGameData)
self.__games_liststore = Gio.ListStore.new(BackupGameData)
self.__games_progress_sorter = BackupGameDataSorter()
self.__games_sortmodel = Gtk.SortListModel.new(self.__games_liststore,
BackupGameDataSorter())
self.__games_progress_sorter)
self.__games_selection = Gtk.SingleSelection.new(self.__games_sortmodel)
name_factory = Gtk.SignalListItemFactory()
name_factory.connect('setup',self._on_column_name_setup)
name_factory.connect('bind',self._on_column_name_bind)
@ -190,11 +193,13 @@ class BackupMultiDialog(Gtk.Dialog):
progress_column = Gtk.ColumnViewColumn.new('Progress',progress_factory)
progress_column.set_expand(True)
self.__games_columnview = Gtk.ColumnView.new(self.__games_sortmodel)
self.__games_columnview = Gtk.ColumnView.new(self.__games_selection)
self.__games_columnview.set_vexpand(True)
self.__games_columnview.append_column(name_column)
self.__games_columnview.append_column(progress_column)
self.__scrolled.set_child(self.__games_columnview)
self.__scrolled.set_vexpand(True)
self.get_content_area().append(self.__scrolled)
self.__progressbar = Gtk.ProgressBar()
@ -221,18 +226,17 @@ class BackupMultiDialog(Gtk.Dialog):
raise TypeError("\"games\" is not an Iterable of \"Game\" instances!")
if g.get_backup_files():
self.__games.append(g)
if self.__games:
self.__ok_button.set_sensitive(False)
else:
self.__ok_button.set_sensitive(True)
#if self.__games:
#self.__ok_button.set_sensitive(False)
#else:
#self.__ok_button.set_sensitive(True)
def do_response(self,response):
self.hide()
self.destroy()
def run(self):
def thread_func(self,games):
am = ArchiverManager.get_global()
def thread_func(am,games):
am.backup_many(games)
return 0
@ -243,26 +247,40 @@ class BackupMultiDialog(Gtk.Dialog):
GLib.idle_add(self._on_backup_game_finished,game)
def on_am_backup_progress(am,progress):
GLib.idle_add(self._on_backup_progress)
GLib.idle_add(self._on_backup_progress,progress)
def on_am_backup_finished(am):
GLib.idle_add(self._on_backup_finished)
if not self.games:
print("no games to backup!")
self.desotroy()
self.response(Gtk.Response.OK)
return
self.present()
am = ArchiverManager.get_global()
am.connect('backup-progress',on_am_backup_progress)
am.connect('backup-finished',on_am_backup_finished)
am.connect('backup-game-progress',on_am_backup_game_progress)
am.connect('backup-game-finished',on_am_backup_game_finished)
thread = Thread(target=thread_func,args=(list(self.__games),),daemon=True)
if not hasattr(self,'__signal_backup_progress'):
self.__signal_backup_progress = am.connect('backup-progress',
on_am_backup_progress)
if not hasattr(self,'__signal_backup_finished'):
self.__signal_backup_finished = am.connect('backup-finished',
on_am_backup_finished)
if not hasattr(self,'__signal_backup_game_progress'):
self.__signal_backup_game_progress = am.connect('backup-game-progress',
on_am_backup_game_progress)
if not hasattr(self,'__signal_backup_game_finished'):
self.__signal_backup_game_finished = am.connect('backup-game-finished',
on_am_backup_game_finished)
print ("Starting thread")
print(self.games)
thread = Thread(target=thread_func,args=(am,list(self.__games)),daemon=True)
self.present()
thread.start()
def _on_column_name_setup(self,factory,item):
label = Gtk.Label()
label.set_xalign(0.0)
@ -291,10 +309,13 @@ class BackupMultiDialog(Gtk.Dialog):
gamedata = self.__games_liststore.get_item(i)
if gamedata.key == game.key:
gamedata.progress = progress
self.__games_progress_sorter.changed(Gtk.SorterChange.DIFFERENT)
return
gamedata = BackupGameData(game)
gamedata.progress = progress
self.__games_liststore.append(gamedata)
vadjustment = self.__scrolled.get_vadjustment()
vadjustment.set_value(vadjustment.get_upper() - vadjustment.get_page_size())
return False
@ -304,11 +325,15 @@ class BackupMultiDialog(Gtk.Dialog):
if gamedata.key == game.key:
gamedata.progress = 1.0
gamedata.finished = True
self.__games_progress_sorter.changed(Gtk.SorterChange.DIFFERENT)
return
gamedata = BackupGameData(game)
gamedata.progress = 1.0
gamedata.finished = True
self.__games_liststore.append(gamedata)
self.__games_progress_sorter.changed(Gtk.SorterChange.DIFFERENT)
vadjustment = self.__scrolled.get_vadjustment()
vadjustment.set_value(vadjustment.get_upper() - vadjustment.get_page_size())
return False
@ -322,6 +347,20 @@ class BackupMultiDialog(Gtk.Dialog):
self.__ok_button.set_sensitive(True)
self.set_decorated(True)
am = ArchiverManager.get_global()
if hasattr(self,'__signal_backup_game_finished'):
am.diconnect(self.__signal_backup_game_finished)
del self.__signal_backup_game_finished
if hasattr(self,'__signal_backup_game_progress'):
am.disconnect(self.__signal_backup_game_progress)
del self.__signal_backup_game_progress
if hasattr(self,'__signal_backup_finished'):
am.disconnect(self.__signal_backup_finished)
del self.__signal_backup_finished
if hasattr(self,'__signal_backup_progress'):
am.disconnect(self.__signal_backup_progress)
del self.__signal_backup_progress
return False