Refactor ProgressIndicator.
Add on_ticks() and max_ticks to simplify code using progress indicators. And support final ticks to set progress to 100% before a final long operation. Change interval from 100ms to 250ms, to allow more time to be spent on the operation.
This commit is contained in:
parent
733cdd2169
commit
36dbad9c53
2
bin/gpo
2
bin/gpo
|
@ -628,6 +628,7 @@ class gPodderCli(object):
|
|||
show_guid = '--guid' in args
|
||||
|
||||
common.find_partial_downloads(self._model.get_podcasts(),
|
||||
noop,
|
||||
noop,
|
||||
noop,
|
||||
on_finish)
|
||||
|
@ -687,6 +688,7 @@ class gPodderCli(object):
|
|||
self._download_episodes(episodes)
|
||||
|
||||
common.find_partial_downloads(self._model.get_podcasts(),
|
||||
noop,
|
||||
noop,
|
||||
noop,
|
||||
on_finish)
|
||||
|
|
|
@ -62,25 +62,19 @@ class gPodderExtension:
|
|||
number_of_episodes = len(episodes)
|
||||
progress_indicator = ProgressIndicator(
|
||||
_('Renaming all downloaded episodes'),
|
||||
'', True, self.gpodder.get_dialog_parent())
|
||||
progress_indicator.on_message('0 / %d' % number_of_episodes)
|
||||
'', True, self.gpodder.get_dialog_parent(), number_of_episodes)
|
||||
|
||||
renamed_count = 0
|
||||
for episode in episodes:
|
||||
self.on_episode_downloaded(episode)
|
||||
|
||||
renamed_count += 1
|
||||
progress_indicator.on_message('%d / %d' % (renamed_count, number_of_episodes))
|
||||
progress_indicator.on_progress(renamed_count / number_of_episodes)
|
||||
if time.time() >= progress_indicator.next_update:
|
||||
progress_indicator.update_gui()
|
||||
self.gpodder.force_ui_update()
|
||||
if not progress_indicator.cancellable:
|
||||
if not progress_indicator.on_tick():
|
||||
break
|
||||
renamed_count = progress_indicator.tick_counter
|
||||
|
||||
progress_indicator.on_finished()
|
||||
|
||||
self.gpodder.show_message(_('Renamed %(count)d downloaded episodes') % {'count': number_of_episodes},
|
||||
if renamed_count > 0:
|
||||
self.gpodder.show_message(_('Renamed %(count)d downloaded episodes') % {'count': renamed_count},
|
||||
_('Renaming finished'), important=True)
|
||||
|
||||
def make_filename(self, current_filename, title, sortdate, podcast_title):
|
||||
|
|
|
@ -47,7 +47,7 @@ def clean_up_downloads(delete_partial=False):
|
|||
util.delete_file(tempfile)
|
||||
|
||||
|
||||
def find_partial_downloads(channels, start_progress_callback, progress_callback, finish_progress_callback):
|
||||
def find_partial_downloads(channels, start_progress_callback, progress_callback, final_progress_callback, finish_progress_callback):
|
||||
"""Find partial downloads and match them with episodes
|
||||
|
||||
channels - A list of all model.PodcastChannel objects
|
||||
|
@ -86,6 +86,8 @@ def find_partial_downloads(channels, start_progress_callback, progress_callback,
|
|||
if not candidates:
|
||||
break
|
||||
|
||||
final_progress_callback()
|
||||
|
||||
for f in partial_files:
|
||||
logger.warning('Partial file without episode: %s', f)
|
||||
util.delete_file(f)
|
||||
|
|
|
@ -22,6 +22,7 @@ import time
|
|||
from gi.repository import GLib, Gtk, Pango
|
||||
|
||||
import gpodder
|
||||
from gpodder import util
|
||||
from gpodder.gtkui.widgets import SpinningProgressIndicator
|
||||
|
||||
_ = gpodder.gettext
|
||||
|
@ -32,14 +33,15 @@ class ProgressIndicator(object):
|
|||
DELAY = 500
|
||||
|
||||
# Time between GUI updates after window creation
|
||||
INTERVAL = 100
|
||||
INTERVAL = 250
|
||||
|
||||
def __init__(self, title, subtitle=None, cancellable=False, parent=None):
|
||||
def __init__(self, title, subtitle=None, cancellable=False, parent=None, max_ticks=None):
|
||||
self.title = title
|
||||
self.subtitle = subtitle
|
||||
self.cancellable = True if cancellable else False
|
||||
self.cancel_callback = cancellable
|
||||
self.cancel_id = 0
|
||||
self.cancelled = False
|
||||
self.next_update = time.time() + (self.DELAY / 1000)
|
||||
self.parent = parent
|
||||
self.dialog = None
|
||||
|
@ -50,10 +52,19 @@ class ProgressIndicator(object):
|
|||
self._progress_set = False
|
||||
self.source_id = GLib.timeout_add(self.DELAY, self._create_progress)
|
||||
|
||||
self.set_max_ticks(max_ticks)
|
||||
|
||||
def set_max_ticks(self, max_ticks):
|
||||
self.max_ticks = max_ticks
|
||||
self.tick_counter = 0
|
||||
if max_ticks is not None:
|
||||
self.on_message('0 / %d' % max_ticks)
|
||||
|
||||
def _on_delete_event(self, window, event):
|
||||
if self.cancellable:
|
||||
self.dialog.response(Gtk.ResponseType.CANCEL)
|
||||
self.cancellable = False
|
||||
self.cancelled = True
|
||||
return True
|
||||
|
||||
def _create_progress(self):
|
||||
|
@ -64,6 +75,7 @@ class ProgressIndicator(object):
|
|||
if self.cancellable:
|
||||
def cancel_callback(dialog, response):
|
||||
self.cancellable = False
|
||||
self.cancelled = True
|
||||
self.dialog.set_deletable(False)
|
||||
self.dialog.set_response_sensitive(Gtk.ResponseType.CANCEL, False)
|
||||
if callable(self.cancel_callback):
|
||||
|
@ -78,8 +90,7 @@ class ProgressIndicator(object):
|
|||
if isinstance(label, Gtk.Label):
|
||||
label.set_selectable(False)
|
||||
|
||||
self.dialog.set_response_sensitive(Gtk.ResponseType.CANCEL,
|
||||
self.cancellable)
|
||||
self.dialog.set_response_sensitive(Gtk.ResponseType.CANCEL, self.cancellable)
|
||||
|
||||
self.progressbar = Gtk.ProgressBar()
|
||||
self.progressbar.set_show_text(True)
|
||||
|
@ -111,13 +122,6 @@ class ProgressIndicator(object):
|
|||
self.next_update = time.time() + (self.INTERVAL / 1000)
|
||||
return True
|
||||
|
||||
def update_gui(self):
|
||||
if self.dialog:
|
||||
if self.source_id:
|
||||
GLib.source_remove(self.source_id)
|
||||
self.source_id = 0
|
||||
self._update_gui()
|
||||
|
||||
def on_message(self, message):
|
||||
if self.progressbar:
|
||||
self.progressbar.set_text(message)
|
||||
|
@ -131,6 +135,39 @@ class ProgressIndicator(object):
|
|||
else:
|
||||
self._initial_progress = progress
|
||||
|
||||
def on_tick(self, final=False):
|
||||
if final:
|
||||
# Dialog is no longer cancellable
|
||||
self.cancellable = False
|
||||
if self.dialog is not None:
|
||||
self.dialog.set_response_sensitive(Gtk.ResponseType.CANCEL, False)
|
||||
self.dialog.set_deletable(False)
|
||||
elif 2 * (time.time() - (self.next_update - (self.DELAY / 1000))) > (self.DELAY / 1000):
|
||||
# Assume final operation will take as long as all ticks and open dialog
|
||||
if self.source_id:
|
||||
GLib.source_remove(self.source_id)
|
||||
self._create_progress()
|
||||
|
||||
if self.max_ticks is not None and not final:
|
||||
self.tick_counter += 1
|
||||
|
||||
if time.time() >= self.next_update or (final and self.dialog):
|
||||
if type(final) == str:
|
||||
self.on_message(final)
|
||||
self.on_progress(1.0)
|
||||
elif self.max_ticks is not None:
|
||||
self.on_message('%d / %d' % (self.tick_counter, self.max_ticks))
|
||||
self.on_progress(self.tick_counter / self.max_ticks)
|
||||
|
||||
# Allow UI to redraw.
|
||||
util.idle_add(Gtk.main_quit)
|
||||
# self._create_progress() or self._update_gui() is called by a timer to update the dialog
|
||||
Gtk.main()
|
||||
|
||||
if self.cancelled:
|
||||
return False
|
||||
return True
|
||||
|
||||
def on_finished(self):
|
||||
if self.dialog is not None:
|
||||
if self.cancel_id:
|
||||
|
|
|
@ -429,9 +429,10 @@ class gPodder(BuilderWidget, dbus.service.Object):
|
|||
def progress_callback(title, progress):
|
||||
self.partial_downloads_indicator.on_message(title)
|
||||
self.partial_downloads_indicator.on_progress(progress)
|
||||
if time.time() >= self.partial_downloads_indicator.next_update:
|
||||
self.partial_downloads_indicator.update_gui()
|
||||
self.force_ui_update()
|
||||
self.partial_downloads_indicator.on_tick() # not cancellable
|
||||
|
||||
def final_progress_callback():
|
||||
self.partial_downloads_indicator.on_tick(final=_('Finishing...'))
|
||||
|
||||
def finish_progress_callback(resumable_episodes):
|
||||
def offer_resuming():
|
||||
|
@ -452,6 +453,7 @@ class gPodder(BuilderWidget, dbus.service.Object):
|
|||
common.find_partial_downloads(self.channels,
|
||||
start_progress_callback,
|
||||
progress_callback,
|
||||
final_progress_callback,
|
||||
finish_progress_callback)
|
||||
|
||||
def episode_object_by_uri(self, uri):
|
||||
|
@ -1624,40 +1626,22 @@ class gPodder(BuilderWidget, dbus.service.Object):
|
|||
else:
|
||||
self.download_queue_manager.queue_task(task)
|
||||
|
||||
def force_ui_update(self):
|
||||
def callback():
|
||||
Gtk.main_quit()
|
||||
GLib.timeout_add(1, callback)
|
||||
Gtk.main()
|
||||
|
||||
def _for_each_task_set_status(self, tasks, status, force_start=False):
|
||||
count = len(tasks)
|
||||
if count:
|
||||
progress_indicator = ProgressIndicator(
|
||||
_('Queueing') if status == download.DownloadTask.QUEUED else
|
||||
_('Removing') if status is None else download.DownloadTask.STATUS_MESSAGE[status],
|
||||
'', True, self.get_dialog_parent())
|
||||
progress_indicator.on_message('0 / %d' % count)
|
||||
'', True, self.get_dialog_parent(), count)
|
||||
else:
|
||||
progress_indicator = None
|
||||
|
||||
def progress_callback(title, progress):
|
||||
progress_indicator.on_message(title)
|
||||
progress_indicator.on_progress(progress)
|
||||
if time.time() >= progress_indicator.next_update:
|
||||
progress_indicator.update_gui()
|
||||
self.force_ui_update()
|
||||
if not progress_indicator.cancellable:
|
||||
return False
|
||||
return True
|
||||
self.__for_each_task_set_status(tasks, status, force_start=force_start, progress_callback=progress_callback)
|
||||
self.__for_each_task_set_status(tasks, status, force_start=force_start, progress_indicator=progress_indicator)
|
||||
|
||||
if progress_indicator:
|
||||
progress_indicator.on_finished()
|
||||
|
||||
def __for_each_task_set_status(self, tasks, status, force_start=False, progress_callback=None):
|
||||
count = len(tasks)
|
||||
n = 0
|
||||
def __for_each_task_set_status(self, tasks, status, force_start=False, progress_indicator=None):
|
||||
episode_urls = set()
|
||||
model = self.treeDownloads.get_model()
|
||||
has_queued_tasks = False
|
||||
|
@ -1707,10 +1691,11 @@ class gPodder(BuilderWidget, dbus.service.Object):
|
|||
else:
|
||||
# We can (hopefully) simply set the task status here
|
||||
task.status = status
|
||||
if progress_callback:
|
||||
n += 1
|
||||
if not progress_callback('%d / %d' % (n, count), n / count):
|
||||
if progress_indicator:
|
||||
if not progress_indicator.on_tick():
|
||||
break
|
||||
if progress_indicator:
|
||||
progress_indicator.on_tick(final=_('Finishing...'))
|
||||
|
||||
# Update the tab title and downloads list
|
||||
if has_queued_tasks:
|
||||
|
@ -3245,13 +3230,11 @@ class gPodder(BuilderWidget, dbus.service.Object):
|
|||
|
||||
def download_episode_list(self, episodes, add_paused=False, force_start=False, downloader=None, hide_progress=False):
|
||||
def queue_tasks(tasks, queued_existing_task):
|
||||
n = 0
|
||||
count = len(tasks)
|
||||
if count and not hide_progress:
|
||||
progress_indicator = ProgressIndicator(
|
||||
_('Queueing'),
|
||||
'', True, self.get_dialog_parent())
|
||||
progress_indicator.on_message('0 / %d' % count)
|
||||
'', True, self.get_dialog_parent(), count)
|
||||
else:
|
||||
progress_indicator = None
|
||||
|
||||
|
@ -3263,14 +3246,11 @@ class gPodder(BuilderWidget, dbus.service.Object):
|
|||
self.mygpo_client.on_download([task.episode])
|
||||
self.queue_task(task, force_start)
|
||||
if progress_indicator:
|
||||
n += 1
|
||||
progress_indicator.on_message('%d / %d' % (n, count))
|
||||
progress_indicator.on_progress(n / count)
|
||||
if time.time() >= progress_indicator.next_update:
|
||||
progress_indicator.update_gui()
|
||||
self.force_ui_update()
|
||||
if not progress_indicator.cancellable:
|
||||
if not progress_indicator.on_tick():
|
||||
break
|
||||
if progress_indicator:
|
||||
progress_indicator.on_tick(final=_('Finishing...'))
|
||||
|
||||
if tasks or queued_existing_task:
|
||||
self.set_download_list_state(gPodderSyncUI.DL_ONEOFF)
|
||||
# Flush updated episode status
|
||||
|
|
Loading…
Reference in New Issue