mirror of
https://github.com/c9moser/sgbackup.git
synced 2026-01-19 19:40:13 +00:00
backup_many() added and fixed; GUI backups "live" and "active"
This commit is contained in:
parent
e2302d79d4
commit
039534fbf1
@ -157,13 +157,6 @@ class ArchiverManager(GObject):
|
|||||||
def archivers(self):
|
def archivers(self):
|
||||||
return self.__archivers
|
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)
|
@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):
|
def do_backup_game_progress(self,game,fraction,message):
|
||||||
pass
|
pass
|
||||||
@ -198,7 +191,7 @@ class ArchiverManager(GObject):
|
|||||||
if not multi_backups:
|
if not multi_backups:
|
||||||
self.emit("backup-progress",fraction)
|
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!!!")
|
raise RuntimeError("A backup is already in progress!!!")
|
||||||
|
|
||||||
self.backup_in_progress = True
|
self.backup_in_progress = True
|
||||||
@ -220,59 +213,79 @@ class ArchiverManager(GObject):
|
|||||||
self.backup_in_progress = False
|
self.backup_in_progress = False
|
||||||
|
|
||||||
def backup_many(self,games:list[Game]):
|
def backup_many(self,games:list[Game]):
|
||||||
def on_game_progress(game,fraction,message,game_progress):
|
def on_game_progress(archiver,game,fraction,message,game_progress,mutex):
|
||||||
with game_progress._mutex:
|
with mutex:
|
||||||
game_progress[game.key] = fraction
|
game_progress[game.key] = fraction
|
||||||
sum_fractions = 0.0
|
sum_fractions = 0.0
|
||||||
for f in game_progress.values():
|
for f in game_progress.values():
|
||||||
sum_fractions += f
|
sum_fractions += f
|
||||||
n = len(game_progress)
|
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):
|
def thread_function(game):
|
||||||
archiver = self.standard_archiver
|
|
||||||
self.backup(game,True)
|
self.backup(game,True)
|
||||||
|
|
||||||
|
|
||||||
if self.backup_in_progress:
|
if self.backup_in_progress:
|
||||||
raise RuntimeError("A backup is already in progress!!!")
|
raise RuntimeError("A backup is already in progress!!!")
|
||||||
self.backup_in_progress = True
|
self.backup_in_progress = True
|
||||||
game_list = list(games)
|
game_list = list(games)
|
||||||
game_progress = dict(((game.key,0.0) for game in game_list))
|
print(games)
|
||||||
game_progress._mutex = threading.RLock()
|
game_progress = dict([(game.key,0.0) for game in game_list])
|
||||||
game_progress._game_progress_connection = self.connect('backup-game-progress',game_progress)
|
mutex = threading.RLock()
|
||||||
|
|
||||||
|
self.__backup_many_game_progress_connection = self.connect('backup-game-progress',on_game_progress,game_progress,mutex)
|
||||||
threadpool = {}
|
threadpool = {}
|
||||||
|
|
||||||
if len(game_list) > settings.backup_threads:
|
if settings.backup_threads == 0:
|
||||||
n = settings.backup_threads
|
backup_threads = 1
|
||||||
|
else:
|
||||||
|
backup_threads = settings.backup_threads
|
||||||
|
if len(game_list) > backup_threads:
|
||||||
|
n = backup_threads
|
||||||
else:
|
else:
|
||||||
n = len(games)
|
n = len(games)
|
||||||
|
|
||||||
|
print("Starting backup with {n} threads".format(n=n))
|
||||||
|
|
||||||
for i in range(n):
|
for i in range(n):
|
||||||
game=game_list[0]
|
game=game_list[0]
|
||||||
del 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()
|
thread.start()
|
||||||
|
|
||||||
while threadpool:
|
while threadpool:
|
||||||
time.sleep(0.02)
|
rm_thread=[]
|
||||||
for i in range(len(threadpool)):
|
for i in threadpool.keys():
|
||||||
thread = threadpool[i]
|
thread = threadpool[i]
|
||||||
if thread.is_alive():
|
if thread.is_alive():
|
||||||
continue
|
continue
|
||||||
del threadpool[i]
|
|
||||||
|
|
||||||
if game_list:
|
if game_list:
|
||||||
game = game_list[0]
|
game = game_list[0]
|
||||||
del game_list[0]
|
del game_list[0]
|
||||||
thread = threading.Thread(thread_function,args=game,daemon=True)
|
thread = threading.Thread(target=thread_function,args=(game,),daemon=True)
|
||||||
threadpool.append(thread)
|
threadpool[i] = thread
|
||||||
thread.start()
|
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.emit("backup-finished")
|
||||||
self.backup_in_progress = False
|
self.backup_in_progress = False
|
||||||
|
|
||||||
|
|||||||
@ -31,7 +31,7 @@ from ._gamedialog import GameDialog
|
|||||||
from ..game import Game,GameManager,SAVEGAME_TYPE_ICONS
|
from ..game import Game,GameManager,SAVEGAME_TYPE_ICONS
|
||||||
from ._steam import SteamLibrariesDialog,NewSteamAppsDialog,NoNewSteamAppsDialog
|
from ._steam import SteamLibrariesDialog,NewSteamAppsDialog,NoNewSteamAppsDialog
|
||||||
from ..steam import Steam
|
from ..steam import Steam
|
||||||
from ._backupdialog import BackupSingleDialog
|
from ._backupdialog import BackupSingleDialog,BackupManyDialog
|
||||||
from ..archiver import ArchiverManager
|
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)):
|
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)
|
backup_games.append(game)
|
||||||
|
|
||||||
# TODO:
|
|
||||||
#dialog = BackupManyDialog(parent=self.get_root(),games=backup_games)
|
dialog = BackupManyDialog(parent=self.get_root(),games=backup_games)
|
||||||
#dialog.set_modal(False)
|
dialog.set_modal(False)
|
||||||
#dialog.present()
|
dialog.run()
|
||||||
|
|
||||||
def _on_icon_column_setup(self,factory,item):
|
def _on_icon_column_setup(self,factory,item):
|
||||||
image = Gtk.Image()
|
image = Gtk.Image()
|
||||||
|
|||||||
@ -46,12 +46,9 @@ class BackupSingleDialog(Gtk.Dialog):
|
|||||||
self.__progressbar.set_fraction(0.0)
|
self.__progressbar.set_fraction(0.0)
|
||||||
|
|
||||||
self.get_content_area().append(self.__progressbar)
|
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.__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):
|
def _on_propgress(self,fraction,message):
|
||||||
self.__progressbar.set_text(message if message else "Working ...")
|
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:
|
if self.__am_signal_finished is not None:
|
||||||
am.disconnect(self.__am_signal_finished)
|
am.disconnect(self.__am_signal_finished)
|
||||||
self.__am_signal_finished = None
|
del self.__am_signal_finished
|
||||||
|
|
||||||
if self.__am_signal_progress is not None:
|
if self.__am_signal_progress is not None:
|
||||||
am.disconnect(self.__am_signal_progress)
|
am.disconnect(self.__am_signal_progress)
|
||||||
self.__am_signal_progress = None
|
del self.__am_signal_progress
|
||||||
|
|
||||||
#if settings.backup_dialog_close_when_finished:
|
#if settings.backup_dialog_close_when_finished:
|
||||||
# self.response(Gtk.ResponseType.OK)
|
# self.response(Gtk.ResponseType.OK)
|
||||||
@ -97,7 +94,9 @@ class BackupSingleDialog(Gtk.Dialog):
|
|||||||
|
|
||||||
|
|
||||||
am = ArchiverManager.get_global()
|
am = ArchiverManager.get_global()
|
||||||
|
if not hasattr(self,'__am_signal_progress'):
|
||||||
self.__am_signal_progress = am.connect('backup-game-progress',self._on_am_backup_game_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)
|
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 = Thread(target=_thread_func,args=(am,self.__game),daemon=True)
|
||||||
thread.start()
|
thread.start()
|
||||||
@ -143,7 +142,7 @@ class BackupGameDataSorter(Gtk.Sorter):
|
|||||||
name1 = item1.game.name.lower()
|
name1 = item1.game.name.lower()
|
||||||
name2 = item2.game.name.lower()
|
name2 = item2.game.name.lower()
|
||||||
|
|
||||||
if item1.finsihed:
|
if item1.finished:
|
||||||
if not item2.finished:
|
if not item2.finished:
|
||||||
return Gtk.Ordering.LARGER
|
return Gtk.Ordering.LARGER
|
||||||
elif name1 > name2:
|
elif name1 > name2:
|
||||||
@ -166,19 +165,23 @@ class BackupGameDataSorter(Gtk.Sorter):
|
|||||||
return Gtk.Ordering.EQUAL
|
return Gtk.Ordering.EQUAL
|
||||||
|
|
||||||
|
|
||||||
class BackupMultiDialog(Gtk.Dialog):
|
class BackupManyDialog(Gtk.Dialog):
|
||||||
logger = logger.getChild('BackupMultiDialog')
|
logger = logger.getChild('BackupMultiDialog')
|
||||||
def __init__(self,parent:Gtk.Window|None=None,games:list[Game]|None=None):
|
def __init__(self,parent:Gtk.Window|None=None,games:list[Game]|None=None):
|
||||||
Gtk.Dialog.__init__(self)
|
Gtk.Dialog.__init__(self)
|
||||||
if parent:
|
if parent:
|
||||||
self.set_transient_for(parent)
|
self.set_transient_for(parent)
|
||||||
self.set_decorated(False)
|
self.set_decorated(False)
|
||||||
self.set_modal(False)
|
self.set_modal(True)
|
||||||
|
self.set_default_size(640,480)
|
||||||
|
|
||||||
self.__scrolled = Gtk.ScrolledWindow()
|
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,
|
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 = Gtk.SignalListItemFactory()
|
||||||
name_factory.connect('setup',self._on_column_name_setup)
|
name_factory.connect('setup',self._on_column_name_setup)
|
||||||
name_factory.connect('bind',self._on_column_name_bind)
|
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 = Gtk.ColumnViewColumn.new('Progress',progress_factory)
|
||||||
progress_column.set_expand(True)
|
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(name_column)
|
||||||
self.__games_columnview.append_column(progress_column)
|
self.__games_columnview.append_column(progress_column)
|
||||||
|
|
||||||
self.__scrolled.set_child(self.__games_columnview)
|
self.__scrolled.set_child(self.__games_columnview)
|
||||||
|
self.__scrolled.set_vexpand(True)
|
||||||
self.get_content_area().append(self.__scrolled)
|
self.get_content_area().append(self.__scrolled)
|
||||||
|
|
||||||
self.__progressbar = Gtk.ProgressBar()
|
self.__progressbar = Gtk.ProgressBar()
|
||||||
@ -221,18 +226,17 @@ class BackupMultiDialog(Gtk.Dialog):
|
|||||||
raise TypeError("\"games\" is not an Iterable of \"Game\" instances!")
|
raise TypeError("\"games\" is not an Iterable of \"Game\" instances!")
|
||||||
if g.get_backup_files():
|
if g.get_backup_files():
|
||||||
self.__games.append(g)
|
self.__games.append(g)
|
||||||
if self.__games:
|
#if self.__games:
|
||||||
self.__ok_button.set_sensitive(False)
|
#self.__ok_button.set_sensitive(False)
|
||||||
else:
|
#else:
|
||||||
self.__ok_button.set_sensitive(True)
|
#self.__ok_button.set_sensitive(True)
|
||||||
|
|
||||||
def do_response(self,response):
|
def do_response(self,response):
|
||||||
self.hide()
|
self.hide()
|
||||||
self.destroy()
|
self.destroy()
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
def thread_func(self,games):
|
def thread_func(am,games):
|
||||||
am = ArchiverManager.get_global()
|
|
||||||
am.backup_many(games)
|
am.backup_many(games)
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
@ -243,26 +247,40 @@ class BackupMultiDialog(Gtk.Dialog):
|
|||||||
GLib.idle_add(self._on_backup_game_finished,game)
|
GLib.idle_add(self._on_backup_game_finished,game)
|
||||||
|
|
||||||
def on_am_backup_progress(am,progress):
|
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):
|
def on_am_backup_finished(am):
|
||||||
GLib.idle_add(self._on_backup_finished)
|
GLib.idle_add(self._on_backup_finished)
|
||||||
|
|
||||||
if not self.games:
|
if not self.games:
|
||||||
|
print("no games to backup!")
|
||||||
|
self.desotroy()
|
||||||
self.response(Gtk.Response.OK)
|
self.response(Gtk.Response.OK)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.present()
|
|
||||||
|
|
||||||
am = ArchiverManager.get_global()
|
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()
|
thread.start()
|
||||||
|
|
||||||
|
|
||||||
def _on_column_name_setup(self,factory,item):
|
def _on_column_name_setup(self,factory,item):
|
||||||
label = Gtk.Label()
|
label = Gtk.Label()
|
||||||
label.set_xalign(0.0)
|
label.set_xalign(0.0)
|
||||||
@ -291,10 +309,13 @@ class BackupMultiDialog(Gtk.Dialog):
|
|||||||
gamedata = self.__games_liststore.get_item(i)
|
gamedata = self.__games_liststore.get_item(i)
|
||||||
if gamedata.key == game.key:
|
if gamedata.key == game.key:
|
||||||
gamedata.progress = progress
|
gamedata.progress = progress
|
||||||
|
self.__games_progress_sorter.changed(Gtk.SorterChange.DIFFERENT)
|
||||||
return
|
return
|
||||||
gamedata = BackupGameData(game)
|
gamedata = BackupGameData(game)
|
||||||
gamedata.progress = progress
|
gamedata.progress = progress
|
||||||
self.__games_liststore.append(gamedata)
|
self.__games_liststore.append(gamedata)
|
||||||
|
vadjustment = self.__scrolled.get_vadjustment()
|
||||||
|
vadjustment.set_value(vadjustment.get_upper() - vadjustment.get_page_size())
|
||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -304,11 +325,15 @@ class BackupMultiDialog(Gtk.Dialog):
|
|||||||
if gamedata.key == game.key:
|
if gamedata.key == game.key:
|
||||||
gamedata.progress = 1.0
|
gamedata.progress = 1.0
|
||||||
gamedata.finished = True
|
gamedata.finished = True
|
||||||
|
self.__games_progress_sorter.changed(Gtk.SorterChange.DIFFERENT)
|
||||||
return
|
return
|
||||||
gamedata = BackupGameData(game)
|
gamedata = BackupGameData(game)
|
||||||
gamedata.progress = 1.0
|
gamedata.progress = 1.0
|
||||||
gamedata.finished = True
|
gamedata.finished = True
|
||||||
self.__games_liststore.append(gamedata)
|
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
|
return False
|
||||||
|
|
||||||
@ -322,6 +347,20 @@ class BackupMultiDialog(Gtk.Dialog):
|
|||||||
self.__ok_button.set_sensitive(True)
|
self.__ok_button.set_sensitive(True)
|
||||||
self.set_decorated(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
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user