Estimated time left and synchronization support for tray icon by Jérôme Chabod

git-svn-id: svn://svn.berlios.de/gpodder/trunk@579 b0d088ad-0a06-0410-aad2-9ed5178a7e87
This commit is contained in:
Thomas Perl 2008-02-20 12:46:51 +00:00
parent 86a29f82e9
commit 6276017d60
4 changed files with 172 additions and 44 deletions

View File

@ -1,3 +1,16 @@
Wed, 20 Feb 2008 13:38:58 +0100 <thp@perli.net>
Estimated time left and synchronization support for tray icon by Jérôme Chabod
* src/gpodder/gui.py: Send update status information to the tray icon
on update; add glue code for synchronization device and tray icon
* src/gpodder/trayicon.py: Add status and tooltip during device
synchronization; add estimated download time to tooltip during
download; set the correct caption for the synchronization menu item in
the tray icon (and don't add a synchronize menu item when not needed);
some small code-cleanups and fixes
* src/gpodder/util.py: Add format_seconds_to_hour_min_sec() function
that converts a numeric amount of seconds into a human-readable string
Tue, 19 Feb 2008 07:46:28 +0100 <thp@perli.net>
Fix bug with MP3 player synchronization file name encodings

View File

@ -753,7 +753,10 @@ class gPodder(GladeWidget):
def update_feed_cache_callback(self, progressbar, position, count):
title = self.channels[position].title
progressbar.set_text(_('Updating %s (%d/%d)')%(title, position+1, count))
progression = _('Updating %s (%d/%d)')%(title, position+1, count)
progressbar.set_text(progression)
if self.tray_icon:
self.tray_icon.set_status(self.tray_icon.STATUS_UPDATING_FEED_CACHE, progression)
if count > 0:
progressbar.set_fraction(float(position)/float(count))
@ -1121,7 +1124,9 @@ class gPodder(GladeWidget):
self.notification(message, title)
return
gPodderSync(device=device)
gPodderSync(device=device, gPodder=self)
if self.tray_icon:
self.tray_icon.set_synchronisation_device(device)
if episodes is None:
episodes_to_sync = []
@ -1142,6 +1147,9 @@ class gPodder(GladeWidget):
message = _('There has been an error closing your device.')
self.notification(message, title)
return
if self.tray_icon:
self.tray_icon.release_synchronisation_device()
# update model for played state updates after sync
for channel in self.channels:
@ -1181,7 +1189,7 @@ class gPodder(GladeWidget):
self.show_message(message, title)
return
gPodderSync(device=device)
gPodderSync(device=device, gPodder=self)
tracks = device.get_all_tracks()
if len(tracks) > 0:
@ -2117,7 +2125,8 @@ class gPodderSync(GladeWidget):
def on_done(self):
util.idle_add(self.gPodderSync.destroy)
util.idle_add(self.notification, _('Your device has been updated by gPodder.'), _('Operation finished'))
if not self.gPodder.minimized:
util.idle_add(self.notification, _('Your device has been updated by gPodder.'), _('Operation finished'))
def on_gPodderSync_destroy(self, widget, *args):
self.device.unregister('progress', self.on_progress)
@ -2477,3 +2486,5 @@ def main():
gPodder().run()

View File

@ -23,6 +23,7 @@
import gtk
import datetime
from gpodder.liblogger import log
from gpodder.libpodcasts import podcastItem
@ -74,6 +75,8 @@ class GPodderStatusIcon(gtk.StatusIcon):
self.__icon_filename = icon_filename
self.__current_icon = -1
self.__is_downloading = False
self.__synchronisation_device = None
self.__download_start_time = None
self.__previous_notification = []
@ -87,39 +90,8 @@ class GPodderStatusIcon(gtk.StatusIcon):
# Reset trayicon (default icon, default tooltip)
self.set_status()
# build and connect the popup menu
menu = gtk.Menu()
menuItem = gtk.ImageMenuItem(_("Check for new episodes"))
menuItem.set_image(gtk.image_new_from_stock(gtk.STOCK_FIND, gtk.ICON_SIZE_MENU))
menuItem.connect('activate', self.__gpodder.on_itemUpdate_activate)
menu.append(menuItem)
menuItem = gtk.ImageMenuItem(_("Download all new episodes"))
menuItem.set_image(gtk.image_new_from_stock(gtk.STOCK_GO_DOWN, gtk.ICON_SIZE_MENU))
menuItem.connect('activate', self.__gpodder.on_itemDownloadAllNew_activate)
menu.append(menuItem)
menuItem = gtk.ImageMenuItem(_("Synchronize to iPod/player"))
menuItem.set_image(gtk.image_new_from_stock(gtk.STOCK_REFRESH, gtk.ICON_SIZE_MENU))
menuItem.connect('activate', self.__gpodder.on_sync_to_ipod_activate)
menu.append(menuItem)
menu.append( gtk.SeparatorMenuItem())
self.menuItem_previous_msg = gtk.ImageMenuItem(_('Show previous message again'))
self.menuItem_previous_msg.set_image(gtk.image_new_from_stock(gtk.STOCK_INFO, gtk.ICON_SIZE_MENU))
self.menuItem_previous_msg.connect('activate', self.__on_show_previous_message_callback)
self.menuItem_previous_msg.set_sensitive(False)
menu.append(self.menuItem_previous_msg)
menu.append( gtk.SeparatorMenuItem())
menuItem = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
menuItem.connect('activate', self.__gpodder.on_itemPreferences_activate)
menu.append(menuItem)
menuItem = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
menuItem.connect('activate', self.__gpodder.on_itemAbout_activate)
menu.append(menuItem)
menu.append( gtk.SeparatorMenuItem())
menuItem = gtk.ImageMenuItem(gtk.STOCK_QUIT)
menuItem.connect('activate', self.__on_exit_callback)
menu.append(menuItem)
self.connect('activate', self.__on_left_click)
menu = self.__create_context_menu()
self.connect('popup-menu', self.__on_right_click, menu)
self.set_visible(True)
@ -129,8 +101,56 @@ class GPodderStatusIcon(gtk.StatusIcon):
log('Error: unable to initialise pynotify', sender=self)
# Register with the download status manager
services.download_status_manager.register('progress-changed', self.__download_progress_changed)
services.download_status_manager.register('download-complete', self.__download_complete)
dl_man = services.download_status_manager
dl_man.register('progress-changed', self.__on_download_progress_changed)
dl_man.register('download-complete', self.__on_download_complete)
def __create_context_menu(self):
# build and connect the popup menu
menu = gtk.Menu()
menuItem = gtk.ImageMenuItem(_("Check for new episodes"))
menuItem.set_image(gtk.image_new_from_stock(gtk.STOCK_FIND, gtk.ICON_SIZE_MENU))
menuItem.connect('activate', self.__gpodder.on_itemUpdate_activate)
menu.append(menuItem)
menuItem = gtk.ImageMenuItem(_("Download all new episodes"))
menuItem.set_image(gtk.image_new_from_stock(gtk.STOCK_GO_DOWN, gtk.ICON_SIZE_MENU))
menuItem.connect('activate', self.__gpodder.on_itemDownloadAllNew_activate)
menu.append(menuItem)
# menus's label will adapt to the synchronisation device name
gl = gPodderLib()
if gl.config.device_type != 'none':
sync_label = _('Synchronize to %s') % (gl.get_device_name(),)
menuItem = gtk.ImageMenuItem(sync_label)
menuItem.set_sensitive(gl.config.device_type != 'none')
menuItem.set_image(gtk.image_new_from_stock(gtk.STOCK_REFRESH, gtk.ICON_SIZE_MENU))
menuItem.connect('activate', self.__gpodder.on_sync_to_ipod_activate)
menu.append(menuItem)
menu.append( gtk.SeparatorMenuItem())
self.menuItem_previous_msg = gtk.ImageMenuItem(_('Show previous message again'))
self.menuItem_previous_msg.set_image(gtk.image_new_from_stock(gtk.STOCK_INFO, gtk.ICON_SIZE_MENU))
self.menuItem_previous_msg.connect('activate', self.__on_show_previous_message_callback)
self.menuItem_previous_msg.set_sensitive(False)
menu.append(self.menuItem_previous_msg)
menu.append( gtk.SeparatorMenuItem())
menuItem = gtk.ImageMenuItem(gtk.STOCK_PREFERENCES)
menuItem.connect('activate', self.__gpodder.on_itemPreferences_activate)
menu.append(menuItem)
menuItem = gtk.ImageMenuItem(gtk.STOCK_ABOUT)
menuItem.connect('activate', self.__gpodder.on_itemAbout_activate)
menu.append(menuItem)
menu.append( gtk.SeparatorMenuItem())
menuItem = gtk.ImageMenuItem(gtk.STOCK_QUIT)
menuItem.connect('activate', self.__on_exit_callback)
menu.append(menuItem)
return menu
def __on_exit_callback(self, widget, *args):
gl = gPodderLib()
@ -162,13 +182,12 @@ class GPodderStatusIcon(gtk.StatusIcon):
else:
self.__gpodder.iconify_main_window()
def __download_complete(self, episode):
def __on_download_complete(self, episode):
"""Remember finished downloads
"""
self.__finished_downloads.append(episode)
def __download_progress_changed( self, count, percentage):
def __on_download_progress_changed( self, count, percentage):
""" callback by download manager during dowloading.
It updates the tooltip with information on how many
files are dowloaded and the percentage of dowload
@ -177,6 +196,8 @@ class GPodderStatusIcon(gtk.StatusIcon):
tooltip = []
if count > 0:
self.__is_downloading = True
if not self.__download_start_time:
self.__download_start_time = datetime.datetime.now()
if count == 1:
tooltip.append(_('downloading one episode'))
else:
@ -184,12 +205,19 @@ class GPodderStatusIcon(gtk.StatusIcon):
tooltip.append(' (%d%%)'%percentage)
if percentage <> 0:
date_diff = datetime.datetime.now() - self.__download_start_time
estim = date_diff.seconds * 100 // percentage - date_diff.seconds
tooltip.append('\n' + _('estimated remaining time: '))
tooltip.append(util.format_seconds_to_hour_min_sec(estim))
if len(self.__finished_downloads) > 0:
tooltip.append(self.format_episode_list(self.__finished_downloads, _('Finished downloads:')))
self.set_status(self.STATUS_DOWNLOAD_IN_PROGRESS, ''.join(tooltip))
else:
self.__is_downloading = False
self.__download_start_time = None
self.set_status()
num = len(self.__finished_downloads)
if num == 1:
@ -204,7 +232,7 @@ class GPodderStatusIcon(gtk.StatusIcon):
self.send_notification(message, _('gPodder downloads finished'))
self.__finished_downloads = []
def __get_status_icon(self, icon):
if icon in self.__icon_cache:
return self.__icon_cache[icon]
@ -249,7 +277,7 @@ class GPodderStatusIcon(gtk.StatusIcon):
def destroy(self, n=None, action=None):
gtk.main_quit()
def send_notification( self, message, title = "gPodder", actions = [], is_error=False):
if not self.__is_notification_on(): return
@ -272,7 +300,6 @@ class GPodderStatusIcon(gtk.StatusIcon):
gtk.main()
def set_status(self, status=None, tooltip=None):
#TODO: add a stack for icons (an update can occure while dowloading and ending before)
if status is None:
if tooltip is None:
tooltip = self.DEFAULT_TOOLTIP
@ -327,3 +354,35 @@ class GPodderStatusIcon(gtk.StatusIcon):
return ''.join(result)
def set_synchronisation_device(self, synchronisation_device):
assert not self.__synchronisation_device, "a device was already set without have been released"
self.__synchronisation_device = synchronisation_device
self.__synchronisation_device.register('progress', self.__on_synchronisation_progress)
self.__synchronisation_device.register('status', self.__on_synchronisation_status)
self.__synchronisation_device.register('done', self.__on_synchronisation_done)
def release_synchronisation_device(self):
assert self.__synchronisation_device, "request for releasing a device which was never set"
self.__synchronisation_device.unregister('progress', self.__on_synchronisation_progress)
self.__synchronisation_device.unregister('status', self.__on_synchronisation_status)
self.__synchronisation_device.unregister('done', self.__on_synchronisation_done)
self.__synchronisation_device = None
def __on_synchronisation_progress(self, pos, max):
self.__sync_progress = _('%d of %d done') % (pos, max)
def __on_synchronisation_status(self, status):
tooltip = _('%s\n%s') % (status, self.__sync_progress)
self.set_status(self.STATUS_SYNCHRONIZING, tooltip)
log("tooltip: %s", tooltip, sender=self)
def __on_synchronisation_done(self):
if self.__gpodder.minimized:
# this might propably never appends so long gPodder synchronizes in a modal windows
self.send_notification(_('Your device has been updated by gPodder.'), _('Operation finished'))
self.set_status()

View File

@ -702,4 +702,49 @@ def bluetooth_send_file(filename, device=None, callback_finished=None):
if callback_finished is not None:
callback_finished(False)
return False
def format_seconds_to_hour_min_sec(seconds):
"""
Take the number of seconds and format it into a
human-readable string (duration).
>>> format_seconds_to_hour_min_sec(3834)
'1 hour, 3 minutes and 54 seconds'
>>> format_seconds_to_hour_min_sec(2600)
'1 hour'
>>> format_seconds_to_hour_min_sec(62)
'1 minute and 2 seconds'
"""
if seconds < 1:
return _('0 seconds')
result = []
hours = seconds/3600
seconds = seconds%3600
minutes = seconds/60
seconds = seconds%60
if hours == 1:
result.append(_('1 hour'))
elif hours > 1:
result.append(_('%i hours') % hours)
if minutes == 1:
result.append(_('1 minute'))
elif minutes > 1:
result.append(_('%i minutes') % minutes)
if seconds == 1:
result.append(_('1 second'))
elif seconds > 1:
result.append(_('%i seconds') % seconds)
if len(result) > 1:
return (' '+_('and')+' ').join((', '.join(result[:-1]), result[-1]))
else:
return result[0]