|
Hello, I send you a new patch (for revison 503): * I moved the class GPodderStatusIcon in it´s own file (it's easier to maintain) * it uses python-notify instead of dbus * during download, the status icon shows a small arrow * during download tooltip and notification also list successfully dowloaded files (idea from Pieter De Decker) * notification and icon for feed update Thank you for you comments. See mine bellow: Pieter De Decker a écrit : I have two of things to say about the notifications:You'll probably like this version. I don't notify every file download because it's very annoying having to much popup windows, but the tooltip displays during dowload already dowloaded files, and the notify also lists them nikosapi a écrit : It sound good, but I was unfortunately unable to apply the patch . I just integrated manualy the thinks you modifed in GPodderStatusIcon. Can somebody, merge it with my attached patch?Attached is a very basic autoupdate addition to your patch. What it does, is every 20min it runs update_feed_cache. This behaviour is controlled by a configuration option in the Preferences window. Shouldn't also the 20min delay be configurable in the preference window? Paul Rudkin a écrit : Thanks for trying it, Thanks to all developpers, Thomas first, for gPodder.I love this, thanks for the time and effort you put into creating this patch. Thomas Perl a écrit : The "countdown timer" actually appears when you add an action (a button within the bubble).* You could use python-notify instead of directly accessing d-busFor the python-notify (pynotify) usage: I think you can then use the "set_timeout()" function on a Notification object to add the "coutdown timer" to the notification windows, as Pieter De Decker suggested in his mail. I add actions, unfortunately It doesn't work, so I commented them. The buttons appears, but my callback function is not called. It certainly due to my poor experience in python but I can't figure out why. Could you (or somebody else) have a look at my code. See the commented lines "send_notification" and "__action_callback" methods of the GPodderStatusIcon class I used you code as inspiration but I had my own method for more flexibility (I use a dictionary, it's quite easy to add as status)get_tree_icon() from gpodder.util could be helpful here. This is the function that is used to add the bullet or padlock to the icons in the episode list. You could extend this function and be able to get the gPodder icon with some "downloading" (e.g. arrow down) or checking (e.g. the "refresh" icon) in the lower right-hand corner of the icon. I´ll check this |
Index: src/gpodder/statusIcon.py =================================================================== --- src/gpodder/statusIcon.py (révision 0) +++ src/gpodder/statusIcon.py (révision 0) @@ -0,0 +1,271 @@ +# -*- coding: utf-8 -*- +# +# gPodder - A media aggregator and podcast client +# Copyright (C) 2005-2007 Thomas Perl <thp at perli.net> +# +# gPodder is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# gPodder is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# + +import pynotify +import gtk +import gui + +from gpodder import services + +from liblogger import log + +class GPodderStatusIcon(gtk.StatusIcon): + """ this class display a status icon in the system tray + this icon serves to show or hide gPodder, notify dowload status + and provide a popupmenu for quick acces to some + gPodder functionalities + + author: Jérôme Chabod (JCH) <jerome.chabod at ifrance.com> + 12/20/2007 JCH: first release + 12/22/2007 JCH: + uses python-notify instead of dbus + during download, the status icon shows a small arrow + tooltip and notification also list successfully dowloaded files (idea from Pieter De Decker) + notification and icon for feed update + + """ + + def __init__( self, gpodder): + gtk.StatusIcon.__init__(self) + log("create status icon", sender = self) + + # reference to gPodder main class + self.__gpodder = gpodder + + self.__is_downloading = False + # this list store url successfully downloaded for notification + self.__url_successfully_downloaded = [] + + # status + self.__status = {"downloading": ["dowloading", gtk.STOCK_GO_DOWN], + "checkingNewEpisodes": ["looking for new episodes", gtk.STOCK_REFRESH] } + self.__smallStatusIconsCache = {} # will cache the small icons once generated + + + # try getting the icon + try: + self.__icon = gtk.gdk.pixbuf_new_from_file(gui.scalable_dir) + except Exception, detail: + log( "Warning: Cannot load gPodder icon, will use the default icon.\nerror is:%s", detail, sender =self) + self.__icon = gtk.icon_theme_get_default().load_icon(gtk.STOCK_DIALOG_QUESTION, 30, 30) + self.set_from_pixbuf(self.__icon) + + self.set_tooltip("gPodder") + + # build and connect the popup menu + menu = gtk.Menu() + menuItem = gtk.ImageMenuItem("Check for Updates") + menuItem.connect('activate', self.__gpodder.on_itemUpdate_activate) + menu.append(menuItem) + menuItem = gtk.ImageMenuItem("Download all new episodes") + menuItem.connect('activate', self.__gpodder.on_itemDownloadAllNew_activate) + menu.append(menuItem) + 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.__gpodder.close_gpodder, self) + menu.append(menuItem) + self.connect('popup-menu', self.__on_popup_menu, menu) + + self.connect('activate', self.__on_activate) + self.set_visible(True) + + # initialise pynotify + if not pynotify.init("gPodder"): + log("Error: unable to initialise pynotify", sender = self) + #TODO: disable notification + + # register the status icon to be notified + # by the download status manager + dsm = services.download_status_manager + dsm.register( 'progress-changed', self.__download_progress_changed) + dsm.register( 'download-completed', self.__download_completed) + log("status icon created", sender = self) + + def __on_popup_menu(self, widget, button, time, data = None): + """ Triggered when the popup menu is called, + generaly by right clicking on the icon. + Display the popup menu created in cronstructor. + + """ + log("popup systray menu", sender = self) + data.show_all() + data.popup(None, None, None, 3, time) + + def __on_activate(self, widget, data=None): + """ Triggered when the icon is activated, + generaly by left clicking on the icon. + Iconify or deiconify the gpodder gui. + When iconified, the application is hidden from taskbar + + """ + if self.is_iconified(): + self.uniconify_main_window() + else: + self.iconify_main_window() + + + def __download_completed(self, url): + """ callback by dowload manager during dowloading. + track files succesfully dowload for notification + """ + log("added url %s to successfully dowloaded", url, sender = self) + self.__url_successfully_downloaded.append(url) + + def __format_list_downloaded(self): + """ format the list of already dowloaded files for displaying + in tooltip or notification. + return the formated string + """ + result = "" + count = 0 + for url in self.__url_successfully_downloaded: + count=+1 + #limit the list to 20 items + if count > 20: + result += "\n(...)" + break + title = self.__gpodder.get_episode_by_url(url).title + # cut title if too long + if len(title) > 100: title=title[0:47] + "(...)" + title[-47:0] + result += "\n%s" % title + return result + + def __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 + + """ + toolType = "" + if count == 0: + # dowload finished + toolType = "gPodder" + self.__is_downloading = False + self.change_status(None) + msg = "%i episodes downloaded:" % len(self.__url_successfully_downloaded) + msg += self.__format_list_downloaded() + self.send_notification(msg, "gPodder - download finished") + self.__url_successfully_downloaded = [] + else: + if not self.__is_downloading: + # change the icon to status downloading + self.__is_downloading = True + self.change_status("downloading") + if count == 1: + toolType = "downloading one file" + else: + toolType = "downloading %d files" % (count) + toolType += " - %d%% accomplished" % (percentage) + if len(self.__url_successfully_downloaded) > 0: + toolType += "\nalready downloaded:" + self.__format_list_downloaded() + self.set_tooltip(toolType) + + def __get_icon_for_status( self, status): + """ Return an icon + """ + # is in cache ? + if status in self.__smallStatusIconsCache.keys(): + log("small status icon found in cache for status %s", status, sender = self) + return self.__smallStatusIconsCache[status] + + # is there in icon for this status + if status in self.__status: + log("Creating small status icon for status %s", status, sender = self) + + # transform + try: + new_icon = self.__icon.copy() + emblem = gtk.icon_theme_get_default().load_icon(self.__status[status][1], new_icon.get_width()/1.5, 0) + size = emblem.get_width() + pos = new_icon.get_width() - size + emblem.composite(new_icon, pos, pos, size, size, pos, pos, 1, 1, gtk.gdk.INTERP_BILINEAR, 255) + + self.__smallStatusIconsCache[status] = new_icon + return new_icon + except Exception, detail: + log( "Warning: Cannot load small status icon.\nerror is:%s", detail, sender =self) + + # no icon for this status, return the normal one + log("no small icon found for status %s", status, sender = self) + return self.__icon + + def __action_callback(self, n, action): + """ call back when a button is clicked in a notify bubble """ + log("action triggered %s", action, sender = self) + if action == "show": + self.uniconify_main_window + elif action == "close": + self.__gpodder.close_gpodder + elif action == "Ignore": + pass + else: + log("Warning: don't know what to do with action %s", action, sender = self) + + def send_notification( self, message, title = "gPodder"): + """ Use the gnome notifier system to display a notification message + + """ + log(' systray icon notifies: "%s"', message, sender = self) + #TODO: a configuration flag to make this optional + notification = pynotify.Notification(title, message, gui.scalable_dir) + #FIXME: following action do not work. the call back function is never called + #notification.add_action("show", "show", self.__action_callback) + #notification.add_action("exit", "exit gPodder", self.__action_callback) + #notification.add_action("ignore", "ignore", self.__action_callback) + if not notification.show(): + log("Error: enable to send notification %s", notification) + + def change_status(self, status = None): + if status is None: + self.set_from_pixbuf(self.__icon) + self.set_tooltip("gPodder") + elif not status in self.__status.keys(): + log("status %s is unknown", status, sender = self) + self.set_from_pixbuf(self.__icon) + self.set_tooltip(status) + else: + self.set_from_pixbuf(self.__get_icon_for_status(status)) + self.set_tooltip(self.__status[status][0]) + + def is_iconified(self): + return not self.__gpodder.gpodder_main_window.get_property('is-active') + + def uniconify_main_window(self): + log("gpodder restored", sender = self) + if self.is_iconified(): + self.__gpodder.gpodder_main_window.deiconify() + self.__gpodder.gpodder_main_window.set_skip_taskbar_hint(False) + + def iconify_main_window(self): + log("gpodder iconified", sender = self) + if not self.is_iconified(): + self.__gpodder.gpodder_main_window.set_skip_taskbar_hint(True) + self.__gpodder.gpodder_main_window.iconify() + + + + + Index: src/gpodder/download.py =================================================================== --- src/gpodder/download.py (révision 503) +++ src/gpodder/download.py (copie de travail) @@ -153,6 +153,7 @@ self.downloader.retrieve( self.episode.url, self.tempname, reporthook = self.status_updated) shutil.move( self.tempname, self.filename) self.channel.addDownloadedItem( self.episode) + services.download_status_manager.notify_download_completed( self.download_id) finally: services.download_status_manager.remove_download_id( self.download_id) services.download_status_manager.s_release( acquired) Index: src/gpodder/services.py =================================================================== --- src/gpodder/services.py (révision 503) +++ src/gpodder/services.py (copie de travail) @@ -82,7 +82,7 @@ self.tree_model = gtk.ListStore( *self.COLUMN_TYPES) self.tree_model_lock = threading.Lock() - signal_names = ['list-changed', 'progress-changed', 'progress-detail'] + signal_names = ['list-changed', 'progress-changed', 'progress-detail','download-completed'] ObservableService.__init__(self, signal_names) def notify_progress( self): @@ -170,6 +170,10 @@ self.notify( 'progress-detail', self.status_list[id]['url'], kwargs['progress'], kwargs['speed']) self.notify_progress() + + def notify_download_completed( self, id): + if id in self.status_list: + self.notify( 'download-completed', self.status_list[id]['url']) def request_progress_detail( self, url): for status in self.status_list.values(): Index: src/gpodder/gui.py =================================================================== --- src/gpodder/gui.py (révision 503) +++ src/gpodder/gui.py (copie de travail) @@ -54,6 +54,8 @@ from libtagupdate import tagging_supported +from statusIcon import GPodderStatusIcon + app_name = "gpodder" app_version = "unknown" # will be set in main() call app_authors = [ 'Thomas Perl <[EMAIL PROTECTED]' ] @@ -163,7 +165,10 @@ self.uar = None gl = gPodderLib() - + + #TODO: use a configuration flag to make it optional + self.status_icon = GPodderStatusIcon(self) + gl.config.connect_gtk_window( self.gPodder) gl.config.connect_gtk_paned( 'paned_position', self.channelPaned) @@ -289,6 +294,12 @@ self.delete_episode_list(old_episodes, confirm=False) self.updateComboBox() + def get_episode_by_url(self, url): + for channel in self.channels: + episode = channel.find_episode(url) + if episode is not None: return episode + log("warning: no episode found for url %s", url, sender = self) + return url def treeview_channels_query_tooltip(self, treeview, x, y, keyboard_tooltip, tooltip): # FIXME: Do not hardcode treeview header height @@ -757,6 +768,7 @@ title = _('Downloading podcast feeds') heading = _('Downloading feeds') body = _('Podcast feeds contain channel metadata and information about current episodes.') + self.status_icon.change_status("checkingNewEpisodes") please_wait = gtk.Dialog( title, self.gPodder, gtk.DIALOG_MODAL, ( gtk.STOCK_CANCEL, gtk.RESPONSE_CANCEL, )) please_wait.set_transient_for( self.gPodder) @@ -808,6 +820,7 @@ please_wait.run() please_wait.destroy() + self.status_icon.change_status(None) self.updateComboBox() @@ -2189,9 +2202,9 @@ def on_btnCancel_clicked( self, widget): self.gPodderEpisodeSelector.destroy() if self.callback is not None: - self.callback([]) - + self.callback([]) + def main(): gobject.threads_init() gtk.window_set_default_icon_name( 'gpodder')
_______________________________________________ gpodder-devel mailing list [email protected] https://lists.berlios.de/mailman/listinfo/gpodder-devel
