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:
parent
86a29f82e9
commit
6276017d60
13
ChangeLog
13
ChangeLog
|
@ -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
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
Loading…
Reference in New Issue