diff --git a/deluge/core/core.py b/deluge/core/core.py index 051548784..05dda81b7 100644 --- a/deluge/core/core.py +++ b/deluge/core/core.py @@ -35,7 +35,6 @@ import gettext import locale import pkg_resources import sys -import cPickle as pickle import shutil import os @@ -119,7 +118,9 @@ class Core( except: log.info("Daemon already running or port not available..") sys.exit(0) - + + self.register_multicall_functions() + # Register all export_* functions for func in dir(self): if func.startswith("export_"): @@ -350,14 +351,9 @@ class Core( leftover_fields = list(set(keys) - set(status.keys())) if len(leftover_fields) > 0: status.update(self.plugins.get_status(torrent_id, leftover_fields)) - return pickle.dumps(status) + return status def export_get_torrents_status(self, torrent_ids, keys): - """Returns dictionary of statuses for torrent_ids""" - # This is an async command, so we want to return right away - gobject.idle_add(self._get_torrents_status, torrent_ids, keys) - - def _get_torrents_status(self, torrent_ids, keys): status_dict = {}.fromkeys(torrent_ids) # Get the torrent status for each torrent_id @@ -374,9 +370,8 @@ class Core( status_dict[torrent_id] = status # Emit the torrent_status signal to the clients - self.torrent_status(pickle.dumps(status_dict)) - return False - + return status_dict + def export_get_session_state(self): """Returns a list of torrent_ids in the session.""" # Get the torrent list from the TorrentManager @@ -477,11 +472,7 @@ class Core( """Emitted when all torrents have been resumed""" log.debug("torrent_all_resumed signal emitted") self.signals.emit("torrent_all_resumed", torrent_id) - - def torrent_status(self, status): - """Emitted when the torrent statuses are ready to be sent""" - self.signals.emit("torrent_status", status) - + # Config set functions def _on_set_torrentfiles_location(self, key, value): try: diff --git a/deluge/ui/client.py b/deluge/ui/client.py index a6c2bc3a7..536e6dcd7 100644 --- a/deluge/ui/client.py +++ b/deluge/ui/client.py @@ -132,7 +132,42 @@ class CoreProxy(gobject.GObject): gobject.GObject.__init__(self) self._uri = None self._core = None + self._multi = None + self._callbacks = [] + gobject.timeout_add(10, self.do_multicall) + + def call(self, func, callback, *args): + if self._core is None or self._multi is None: + if self.get_core() is None: + return + _func = getattr(self._multi, func) + + if _func is not None: + if len(args) == 0: + _func() + else: + _func(*args) + self._callbacks.append(callback) + + + def do_multicall(self, block=False): + if len(self._callbacks) == 0: + return True + if self._multi is not None: + for i, ret in enumerate(self._multi()): + try: + if block == False: + gobject.idle_add(self._callbacks[i], ret) + else: + self._callbacks[i](ret) + except: + pass + + self._callbacks = [] + self._multi = xmlrpclib.MultiCall(self._core) + return True + def set_core_uri(self, uri): log.info("Setting core uri as %s", uri) @@ -158,26 +193,18 @@ class CoreProxy(gobject.GObject): if self._core is None and self._uri is not None: log.debug("Creating ServerProxy..") self._core = xmlrpclib.ServerProxy(self._uri, allow_none=True) + self._multi = xmlrpclib.MultiCall(self._core) # Call any callbacks registered self.emit("new_core") return self._core - + _core = CoreProxy() def get_core(): """Get the core object and return it""" - return _core.get_core() + return _core -def get_core_plugin(plugin): - """Get the core plugin object and return it""" - log.debug("Getting core plugin %s from DBUS..", plugin) - bus = dbus.SessionBus() - proxy = bus.get_object("org.deluge_torrent.Deluge", - "/org/deluge_torrent/Plugin/" + plugin) - core = dbus.Interface(proxy, "org.deluge_torrent.Deluge." + plugin) - return core - def connect_on_new_core(callback): """Connect a callback whenever a new core is connected to.""" return _core.connect("new_core", callback) @@ -211,15 +238,27 @@ def connected(): if get_core_uri() != None: return True return False - + +def register_client(port): + get_core().call("register_client", None, port) + +def deregister_client(): + get_core().call("deregister_client", None) + def shutdown(): """Shutdown the core daemon""" try: - get_core().shutdown() + get_core().call("shutdown", None) + force_call(block=False) except: # Ignore everything set_core_uri(None) - + +def force_call(block=True): + """Forces the multicall batch to go now and not wait for the timer. This + call also blocks until all callbacks have been dealt with.""" + get_core().do_multicall(block=block) + def add_torrent_file(torrent_files, torrent_options=None): """Adds torrent files to the core Expects a list of torrent files @@ -252,216 +291,101 @@ def add_torrent_file(torrent_files, torrent_options=None): else: options = None - try: - result = get_core().add_torrent_file( - filename, str(), fdump, options) - except (AttributeError, socket.error): - set_core_uri(None) - result = False - - if result is False: - # The torrent was not added successfully. - log.warning("Torrent %s was not added successfully.", filename) + get_core().call("add_torrent_file", None, + filename, str(), fdump, options) def add_torrent_url(torrent_url, options=None): """Adds torrents to the core via url""" from deluge.common import is_url if is_url(torrent_url): - try: - result = get_core().add_torrent_url(torrent_url, str(), options) - except (AttributeError, socket.error): - set_core_uri(None) - result = False - - if result is False: - # The torrent url was not added successfully. - log.warning("Torrent %s was not added successfully.", torrent_url) + get_core().call("add_torrent_url", None, + torrent_url, str(), options) else: log.warning("Invalid URL %s", torrent_url) def remove_torrent(torrent_ids, remove_torrent=False, remove_data=False): """Removes torrent_ids from the core.. Expects a list of torrent_ids""" log.debug("Attempting to removing torrents: %s", torrent_ids) - try: - for torrent_id in torrent_ids: - get_core().remove_torrent(torrent_id, remove_torrent, remove_data) - except (AttributeError, socket.error): - set_core_uri(None) + for torrent_id in torrent_ids: + get_core().call("remove_torrent", None, torrent_id, remove_torrent, remove_data) def pause_torrent(torrent_ids): """Pauses torrent_ids""" - try: - for torrent_id in torrent_ids: - get_core().pause_torrent(torrent_id) - except (AttributeError, socket.error): - set_core_uri(None) + for torrent_id in torrent_ids: + get_core().call("pause_torrent", None, torrent_id) def pause_all_torrents(): """Pauses all torrents""" - try: - get_core().pause_all_torrents() - except (AttributeError, socket.error): - set_core_uri(None) + get_core().call("pause_all_torrents", None) def resume_all_torrents(): """Resumes all torrents""" - try: - get_core().resume_all_torrents() - except (AttributeError, socket.error): - set_core_uri(None) + get_core().call("resume_all_torrents", None) def resume_torrent(torrent_ids): """Resume torrent_ids""" - try: - for torrent_id in torrent_ids: - get_core().resume_torrent(torrent_id) - except (AttributeError, socket.error): - set_core_uri(None) + for torrent_id in torrent_ids: + get_core().call("resume_torrent", None, torrent_id) def force_reannounce(torrent_ids): """Reannounce to trackers""" - try: - for torrent_id in torrent_ids: - get_core().force_reannounce(torrent_id) - except (AttributeError, socket.error): - set_core_uri(None) + for torrent_id in torrent_ids: + get_core().call("force_reannounce", None, torrent_id) -@cache_dict -def get_torrent_status(torrent_id, keys): +def get_torrent_status(callback, torrent_id, keys): """Builds the status dictionary and returns it""" - try: - status = get_core().get_torrent_status(torrent_id, keys) - except (AttributeError, socket.error): - set_core_uri(None) - return {} - - if status == None: - return {} - - return pickle.loads(status) + get_core().call("get_torrent_status", callback, torrent_id, keys) -def get_torrents_status(torrent_ids, keys): +def get_torrents_status(callback, torrent_ids, keys): """Builds a dictionary of torrent_ids status. Expects 2 lists. This is asynchronous so the return value will be sent as the signal 'torrent_status'""" - try: - get_core().get_torrents_status(torrent_ids, keys) - except (AttributeError, socket.error): - set_core_uri(None) + get_core().call("get_torrents_status", callback, torrent_ids, keys) - return None - -@cache -def get_session_state(): - try: - state = get_core().get_session_state() - except (AttributeError, socket.error): - set_core_uri(None) - state = [] - return state +def get_session_state(callback): + get_core().call("get_session_state", callback) -@cache -def get_config(): - try: - config = get_core().get_config() - except (AttributeError, socket.error): - set_core_uri(None) - config = {} - return config +def get_config(callback): + get_core().call("get_config", callback) -@cache -def get_config_value(key): - try: - config_value = get_core().get_config_value(key) - except (AttributeError, socket.error): - set_core_uri(None) - config_value = None - return config_value +def get_config_value(callback, key): + get_core().call("get_config_value", callback, key) def set_config(config): if config == {}: return - try: - get_core().set_config(config) - except (AttributeError, socket.error): - set_core_uri(None) -@cache -def get_listen_port(): - try: - port = get_core().get_listen_port() - except (AttributeError, socket.error): - set_core_uri(None) - port = 0 - return int(port) + get_core().call("set_config", None, config) -@cache -def get_available_plugins(): - try: - available = get_core().get_available_plugins() - except (AttributeError, socket.error): - set_core_uri(None) - available = [] - return available +def get_listen_port(callback): + get_core().call("get_listen_port", callback) -@cache -def get_enabled_plugins(): - try: - enabled = get_core().get_enabled_plugins() - except (AttributeError, socket.error): - set_core_uri(None) - enabled = [] - return enabled +def get_available_plugins(callback): + get_core().call("get_available_plugins", callback) -@cache -def get_download_rate(): - try: - rate = get_core().get_download_rate() - except (AttributeError, socket.error): - set_core_uri(None) - rate = -1 - return rate +def get_enabled_plugins(callback): + get_core().call("get_enabled_plugins", callback) -@cache -def get_upload_rate(): - try: - rate = get_core().get_upload_rate() - except (AttributeError, socket.error): - set_core_uri(None) - rate = -1 - return rate +def get_download_rate(callback): + get_core().call("get_download_rate", callback) -@cache -def get_num_connections(): - try: - num_connections = get_core().get_num_connections() - except (AttributeError, socket.error): - set_core_uri(None) - num_connections = 0 - return num_connections +def get_upload_rate(callback): + get_core().call("get_upload_rate", callback) + +def get_num_connections(callback): + get_core().call("get_num_connections", callback) def enable_plugin(plugin): - try: - get_core().enable_plugin(plugin) - except (AttributeError, socket.error): - set_core_uri(None) + get_core().call("enable_plugin", None, plugin) def disable_plugin(plugin): - try: - get_core().disable_plugin(plugin) - except (AttributeError, socket.error): - set_core_uri(None) + get_core().call("disable_plugin", None, plugin) def force_recheck(torrent_ids): """Forces a data recheck on torrent_ids""" - try: - for torrent_id in torrent_ids: - get_core().force_recheck(torrent_id) - except (AttributeError, socket.error): - set_core_uri(None) + for torrent_id in torrent_ids: + get_core().call("force_recheck", None, torrent_id) def set_torrent_trackers(torrent_id, trackers): """Sets the torrents trackers""" - try: - get_core().set_torrent_trackers(torrent_id, trackers) - except (AttributeError, socket.error): - set_core_uri(None) + get_core().call("set_torrent_trackers", None, torrent_id, trackers) + diff --git a/deluge/ui/gtkui/addtorrentdialog.py b/deluge/ui/gtkui/addtorrentdialog.py index 49fdc1e66..a6b555b6f 100644 --- a/deluge/ui/gtkui/addtorrentdialog.py +++ b/deluge/ui/gtkui/addtorrentdialog.py @@ -119,10 +119,14 @@ class AddTorrentDialog: "add_paused", "default_private" ] + self.core_config = {} + # Send requests to the core for these config values for key in self.core_keys: - self.core_config[key] = client.get_config_value(key) + client.get_config_value(self._on_config_value, key) + # Force a call to the core because we need this data now + client.force_call() self.set_default_options() def show(self): @@ -133,6 +137,12 @@ class AddTorrentDialog: self.dialog.destroy() return None + def _on_config_value(self, value): + for key in self.core_keys: + if not self.core_config.has_key(key): + self.core_config[key] = value + break + def add_to_torrent_list(self, filenames): import deluge.libtorrent as lt import os.path diff --git a/deluge/ui/gtkui/gtkui.py b/deluge/ui/gtkui/gtkui.py index becfd4737..a5158e845 100644 --- a/deluge/ui/gtkui/gtkui.py +++ b/deluge/ui/gtkui/gtkui.py @@ -152,21 +152,9 @@ class GtkUI: # Make sure the config is saved. config.save() - del config - # Clean-up # Shutdown all components component.shutdown() - del self.mainwindow - del self.systemtray - del self.menubar - del self.toolbar - del self.torrentview - del self.torrentdetails - del self.preferences - del self.signal_receiver - del self.plugins - del deluge.configmanager def _on_new_core(self, data): component.start() diff --git a/deluge/ui/gtkui/listview.py b/deluge/ui/gtkui/listview.py index b93689136..75bc1dcc2 100644 --- a/deluge/ui/gtkui/listview.py +++ b/deluge/ui/gtkui/listview.py @@ -72,7 +72,7 @@ def cell_data_time(column, cell, model, row, data): """Display value as time, eg 1m10s""" time = model.get_value(row, data) if time <= 0: - time_str = _("Infinity") + time_str = "" else: time_str = deluge.common.ftime(time) cell.set_property('text', time_str) diff --git a/deluge/ui/gtkui/pluginmanager.py b/deluge/ui/gtkui/pluginmanager.py index b3e2416a9..4a6c7fd23 100644 --- a/deluge/ui/gtkui/pluginmanager.py +++ b/deluge/ui/gtkui/pluginmanager.py @@ -48,17 +48,19 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase, def start(self): """Start the plugin manager""" # Update the enabled_plugins from the core - enabled_plugins = client.get_enabled_plugins() + client.get_enabled_plugins(self._on_get_enabled_plugins) + + def stop(self): + # Disable the plugins + self.disable_plugins() + + def _on_get_enabled_plugins(self, enabled_plugins): log.debug("Core has these plugins enabled: %s", enabled_plugins) self.config["enabled_plugins"] = enabled_plugins # Enable the plugins that are enabled in the config and core self.enable_plugins() - - def stop(self): - # Disable the plugins - self.disable_plugins() - + ## Plugin functions.. will likely move to own class.. def add_torrentview_text_column(self, *args, **kwargs): diff --git a/deluge/ui/gtkui/preferences.py b/deluge/ui/gtkui/preferences.py index 192fc75de..6f3546519 100644 --- a/deluge/ui/gtkui/preferences.py +++ b/deluge/ui/gtkui/preferences.py @@ -147,10 +147,28 @@ class Preferences(component.Component): self.notebook.remove_page(self.page_num_to_remove) if self.iter_to_remove != None: self.liststore.remove(self.iter_to_remove) - + + def _on_get_config(self, config): + self.core_config = config + + def _on_get_available_plugins(self, plugins): + self.all_plugins = plugins + + def _on_get_enabled_plugins(self, plugins): + self.enabled_plugins = plugins + + def _on_get_listen_port(self, port): + self.active_port = port + def show(self): # Update the preferences dialog to reflect current config settings - self.core_config = client.get_config() + self.core_config = {} + client.get_config(self._on_get_config) + client.get_available_plugins(self._on_get_available_plugins) + client.get_enabled_plugins(self._on_get_enabled_plugins) + client.get_listen_port(self._on_get_listen_port) + # Force these calls and block until we've done them all + client.force_call() if self.core_config != {} and self.core_config != None: core_widgets = { @@ -171,7 +189,7 @@ class Preferences(component.Component): self.core_config["prioritize_first_last_pieces"]), "spin_port_min": ("value", self.core_config["listen_ports"][0]), "spin_port_max": ("value", self.core_config["listen_ports"][1]), - "active_port_label": ("text", str(client.get_listen_port())), + "active_port_label": ("text", str(self.active_port)), "chk_random_port": ("active", self.core_config["random_port"]), "chk_dht": ("active", self.core_config["dht"]), "chk_upnp": ("active", self.core_config["upnp"]), @@ -297,8 +315,8 @@ class Preferences(component.Component): self.gtkui_config["send_info"]) ## Plugins tab ## - all_plugins = client.get_available_plugins() - enabled_plugins = client.get_enabled_plugins() + all_plugins = self.all_plugins + enabled_plugins = self.enabled_plugins # Clear the existing list so we don't duplicate entries. self.plugin_liststore.clear() # Iterate through the lists and add them to the liststore diff --git a/deluge/ui/gtkui/signals.py b/deluge/ui/gtkui/signals.py index 33022d3c4..5cb91296a 100644 --- a/deluge/ui/gtkui/signals.py +++ b/deluge/ui/gtkui/signals.py @@ -57,8 +57,6 @@ class Signals(component.Component): self.torrent_all_paused) self.receiver.connect_to_signal("torrent_all_resumed", self.torrent_all_resumed) - self.receiver.connect_to_signal("torrent_status", - self.torrent_status) def stop(self): self.receiver.shutdown() @@ -80,22 +78,20 @@ class Signals(component.Component): def torrent_paused(self, torrent_id): log.debug("torrent_paused signal received..") component.get("TorrentView").update() - component.get("ToolBar").update_buttons() + component.get("ToolBar").update_buttons("paused", torrent_id) def torrent_resumed(self, torrent_id): log.debug("torrent_resumed signal received..") component.get("TorrentView").update() - component.get("ToolBar").update_buttons() + component.get("ToolBar").update_buttons("resumed", torrent_id) def torrent_all_paused(self): log.debug("torrent_all_paused signal received..") component.get("TorrentView").update() - component.get("ToolBar").update_buttons() + component.get("ToolBar").update_buttons("paused") def torrent_all_resumed(self): log.debug("torrent_all_resumed signal received..") component.get("TorrentView").update() - component.get("ToolBar").update_buttons() - - def torrent_status(self, status): - component.get("TorrentView").on_torrent_status_signal(status) + component.get("ToolBar").update_buttons("resumed") + diff --git a/deluge/ui/gtkui/statusbar.py b/deluge/ui/gtkui/statusbar.py index 945d0c00f..1b2cf4c22 100644 --- a/deluge/ui/gtkui/statusbar.py +++ b/deluge/ui/gtkui/statusbar.py @@ -82,7 +82,8 @@ class StatusBarItem: self._image.set_from_stock(stock, gtk.ICON_SIZE_MENU) def set_text(self, text): - self._label.set_text(text) + if self._label.get_text() != text: + self._label.set_text(text) def get_widgets(self): return self._widgets() @@ -96,6 +97,14 @@ class StatusBar(component.Component): self.window = component.get("MainWindow") self.statusbar = self.window.main_glade.get_widget("statusbar") + # Status variables that are updated via callback + self.max_connections = -1 + self.num_connections = 0 + self.max_download_speed = -1.0 + self.download_rate = 0.0 + self.max_upload_speed = -1.0 + self.upload_rate = 0.0 + # Add a HBox to the statusbar after removing the initial label widget self.hbox = gtk.HBox() self.hbox.set_spacing(5) @@ -121,6 +130,8 @@ class StatusBar(component.Component): image=deluge.common.get_pixmap("seeding16.png")) self.hbox.pack_start( self.upload_item.get_eventbox(), expand=False, fill=False) + + self.send_status_request() def stop(self): # When stopped, we just show the not connected thingy @@ -153,35 +164,65 @@ class StatusBar(component.Component): def remove(child): self.hbox.remove(child) self.hbox.foreach(remove) + + def send_status_request(self): + # Sends an async request for data from the core + client.get_config_value( + self._on_max_connections_global, "max_connections_global") + client.get_num_connections(self._on_get_num_connections) + client.get_config_value( + self._on_max_download_speed, "max_download_speed") + client.get_download_rate(self._on_get_download_rate) + client.get_config_value( + self._on_max_upload_speed, "max_upload_speed") + client.get_upload_rate(self._on_get_upload_rate) + + def _on_max_connections_global(self, max_connections): + self.max_connections = max_connections + def _on_get_num_connections(self, num_connections): + self.num_connections = num_connections + + def _on_max_download_speed(self, max_download_speed): + self.max_download_speed = max_download_speed + + def _on_get_download_rate(self, download_rate): + self.download_rate = deluge.common.fsize(download_rate) + + def _on_max_upload_speed(self, max_upload_speed): + self.max_upload_speed = max_upload_speed + + def _on_get_upload_rate(self, upload_rate): + self.upload_rate = deluge.common.fsize(upload_rate) + def update(self): # Set the max connections label - max_connections = client.get_config_value("max_connections_global") + max_connections = self.max_connections if max_connections < 0: max_connections = _("Unlimited") self.connections_item.set_text("%s (%s)" % ( - client.get_num_connections(), max_connections)) + self.num_connections, max_connections)) # Set the download speed label - max_download_speed = client.get_config_value("max_download_speed") + max_download_speed = self.max_download_speed if max_download_speed < 0: max_download_speed = _("Unlimited") else: max_download_speed = "%s %s" % (max_download_speed, _("KiB/s")) self.download_item.set_text("%s/s (%s)" % ( - deluge.common.fsize(client.get_download_rate()), - max_download_speed)) + self.download_rate, max_download_speed)) # Set the upload speed label - max_upload_speed = client.get_config_value("max_upload_speed") + max_upload_speed = self.max_upload_speed if max_upload_speed < 0: max_upload_speed = _("Unlimited") else: max_upload_speed = "%s %s" % (max_upload_speed, _("KiB/s")) self.upload_item.set_text("%s/s (%s)" % ( - deluge.common.fsize(client.get_upload_rate()), + self.upload_rate, max_upload_speed)) - + + self.send_status_request() diff --git a/deluge/ui/gtkui/systemtray.py b/deluge/ui/gtkui/systemtray.py index 0fa339303..622182bdf 100644 --- a/deluge/ui/gtkui/systemtray.py +++ b/deluge/ui/gtkui/systemtray.py @@ -60,6 +60,11 @@ class SystemTray(component.Component): ] self.config.register_set_function("enable_system_tray", self.on_enable_system_tray_set) + + self.max_download_speed = -1.0 + self.download_rate = 0.0 + self.max_upload_speed = -1.0 + self.upload_rate = 0.0 def enable(self): """Enables the system tray icon.""" @@ -111,6 +116,8 @@ class SystemTray(component.Component): # Build the bandwidth speed limit menus self.build_tray_bwsetsubmenu() + + self.send_status_request() def stop(self): try: @@ -120,36 +127,62 @@ class SystemTray(component.Component): except Exception, e: log.debug("Unable to hide system tray menu widgets: %s", e) + def send_status_request(self): + client.get_config_value( + self._on_max_download_speed, "max_download_speed") + client.get_download_rate(self._on_get_download_rate) + client.get_config_value( + self._on_max_upload_speed, "max_upload_speed") + client.get_upload_rate(self._on_get_upload_rate) + + def _on_max_download_speed(self, max_download_speed): + if self.max_download_speed != max_download_speed: + self.max_download_speed = max_download_speed + self.build_tray_bwsetsubmenu() + + def _on_get_download_rate(self, download_rate): + self.download_rate = deluge.common.fsize(download_rate) + + def _on_max_upload_speed(self, max_upload_speed): + if self.max_upload_speed != max_upload_speed: + self.max_upload_speed = max_upload_speed + self.build_tray_bwsetsubmenu() + + def _on_get_upload_rate(self, upload_rate): + self.upload_rate = deluge.common.fsize(upload_rate) + def update(self): # Set the tool tip text - max_download_speed = client.get_config_value("max_download_speed") - max_upload_speed = client.get_config_value("max_upload_speed") + max_download_speed = self.max_download_speed + max_upload_speed = self.max_upload_speed if max_download_speed == -1: max_download_speed = _("Unlimited") if max_upload_speed == -1: max_upload_speed = _("Unlimited") - msg = '%s\n%s: %s (%s)\n%s: %s (%s)' % (\ - _("Deluge Bittorrent Client"), _("Down Speed"), \ - deluge.common.fspeed(client.get_download_rate()), - max_download_speed, _("Up Speed"), \ - deluge.common.fspeed(client.get_upload_rate()), max_upload_speed) + msg = '%s\n%s: %s (%s)\n%s: %s (%s)' % ( + _("Deluge Bittorrent Client"), _("Down Speed"), + self.download_rate, + max_download_speed, _("Up Speed"), + self.upload_rate, max_upload_speed) # Set the tooltip self.tray.set_tooltip(msg) + self.send_status_request() + def build_tray_bwsetsubmenu(self): # Create the Download speed list sub-menu submenu_bwdownset = self.build_menu_radio_list( self.config["tray_download_speed_list"], self.tray_setbwdown, - client.get_config_value("max_download_speed"), + self.max_download_speed, _("KiB/s"), show_notset=True, show_other=True) # Create the Upload speed list sub-menu submenu_bwupset = self.build_menu_radio_list( self.config["tray_upload_speed_list"], self.tray_setbwup, - client.get_config_value("max_upload_speed"), + self.max_upload_speed, _("KiB/s"), show_notset=True, show_other=True) # Add the sub-menus to the tray menu diff --git a/deluge/ui/gtkui/toolbar.py b/deluge/ui/gtkui/toolbar.py index e776de875..472f1b9ee 100644 --- a/deluge/ui/gtkui/toolbar.py +++ b/deluge/ui/gtkui/toolbar.py @@ -164,56 +164,55 @@ class ToolBar(component.Component): # Use the menubar's callbacks component.get("MenuBar").on_menuitem_connectionmanager_activate(data) - def update_buttons(self): - log.debug("update_buttons") - # If all the selected torrents are paused, then disable the 'Pause' - # button. - # The same goes for the 'Resume' button. - pause = False - resume = False + def update_buttons(self, action=None, torrent_id=None): + if action == None: + # If all the selected torrents are paused, then disable the 'Pause' + # button. + # The same goes for the 'Resume' button. + pause = False + resume = False - # Disable the 'Clear Seeders' button if there's no finished torrent - finished = False + selected = component.get('TorrentView').get_selected_torrents() + if not selected: + selected = [] - selected = component.get('TorrentView').get_selected_torrents() - if not selected: - selected = [] - - for torrent in selected: - try: - status = client.get_torrent_status(torrent, ['state'])['state'] - except KeyError, e: - log.debug("Error getting torrent state: %s", e) - continue - if status == self.STATE_PAUSED: - resume = True - elif status in [self.STATE_FINISHED, self.STATE_SEEDING]: - finished = True - pause = True - else: - pause = True - if pause and resume and finished: - break - - # Enable the 'Remove Torrent' button only if there's some selected - # torrent. - remove = (len(selected) > 0) - - if not finished: - torrents = client.get_session_state() - for torrent in torrents: - if torrent in selected: + for torrent in selected: + try: + status = component.get("TorrentView").get_torrent_status(torrent)['state'] + except KeyError, e: + log.debug("Error getting torrent state: %s", e) continue - status = client.get_torrent_status(torrent, ['state'])['state'] - if status in [self.STATE_FINISHED, self.STATE_SEEDING]: - finished = True + if status == self.STATE_PAUSED: + resume = True + else: + pause = True + if pause and resume: break - for name, sensitive in (("toolbutton_pause", pause), - ("toolbutton_resume", resume), - ("toolbutton_remove", remove), - ("toolbutton_clear", finished)): - self.window.main_glade.get_widget(name).set_sensitive(sensitive) - + # Enable the 'Remove Torrent' button only if there's some selected + # torrent. + remove = (len(selected) > 0) + + for name, sensitive in (("toolbutton_pause", pause), + ("toolbutton_resume", resume), + ("toolbutton_remove", remove)): + self.window.main_glade.get_widget(name).set_sensitive(sensitive) + + return False + + pause = False + resume = False + if action == "paused": + pause = False + resume = True + elif action == "resumed": + pause = True + resume = False + selected = component.get('TorrentView').get_selected_torrents() + if torrent_id == None or (torrent_id in selected and len(selected) == 1): + self.window.main_glade.get_widget("toolbutton_pause").set_sensitive(pause) + self.window.main_glade.get_widget("toolbutton_resume").set_sensitive(resume) + else: + self.update_buttons() + return False - diff --git a/deluge/ui/gtkui/torrentdetails.py b/deluge/ui/gtkui/torrentdetails.py index 580cf360f..649361b24 100644 --- a/deluge/ui/gtkui/torrentdetails.py +++ b/deluge/ui/gtkui/torrentdetails.py @@ -92,7 +92,7 @@ class TorrentDetails(component.Component): def stop(self): self.clear() - + def update(self): # Show tabs if more than 1 page if self.notebook.get_n_pages() > 1: @@ -107,7 +107,7 @@ class TorrentDetails(component.Component): selected = component.get("TorrentView").get_selected_torrents() # Only use the first torrent in the list or return if None selected - if selected is not None: + if len(selected) != 0: selected = selected[0] else: # No torrent is selected in the torrentview @@ -121,50 +121,54 @@ class TorrentDetails(component.Component): "upload_payload_rate", "num_peers", "num_seeds", "total_peers", "total_seeds", "eta", "ratio", "tracker", "next_announce", "tracker_status", "save_path"] - status = client.get_torrent_status(selected, status_keys) + client.get_torrent_status( + self._on_get_torrent_status, selected, status_keys) + + def _on_get_torrent_status(self, status): + # Check to see if we got valid data from the core + if status is None: + return - # Check to see if we got valid data from the core - if status is None: - return - - # We need to adjust the value core gives us for progress - try: - progress = status["progress"]/100 - - self.progress_bar.set_fraction(progress) - self.progress_bar.set_text(deluge.common.fpcnt(progress)) - - self.name.set_text(status["name"]) - self.total_size.set_text( - deluge.common.fsize(status["total_size"])) - self.num_files.set_text(str(status["num_files"])) - self.pieces.set_text("%s (%s)" % (status["num_pieces"], - deluge.common.fsize(status["piece_length"]))) - self.availability.set_text( - "%.3f" % status["distributed_copies"]) - self.total_downloaded.set_text("%s (%s)" % \ - (deluge.common.fsize(status["total_done"]), - deluge.common.fsize(status["total_payload_download"]))) - self.total_uploaded.set_text("%s (%s)" % \ - (deluge.common.fsize(status["total_uploaded"]), - deluge.common.fsize(status["total_payload_upload"]))) - self.download_speed.set_text( - deluge.common.fspeed(status["download_payload_rate"])) - self.upload_speed.set_text( - deluge.common.fspeed(status["upload_payload_rate"])) - self.seeders.set_text(deluge.common.fpeer(status["num_seeds"], - status["total_seeds"])) - self.peers.set_text(deluge.common.fpeer(status["num_peers"], - status["total_peers"])) - self.eta.set_text(deluge.common.ftime(status["eta"])) - self.share_ratio.set_text("%.3f" % status["ratio"]) - self.tracker.set_text(status["tracker"]) - self.tracker_status.set_text(status["tracker_status"]) - self.next_announce.set_text( - deluge.common.ftime(status["next_announce"])) - self.torrent_path.set_text(status["save_path"]) - except KeyError, e: - log.debug(e) + # We need to adjust the value core gives us for progress + try: + progress = status["progress"]/100 + + self.progress_bar.set_fraction(progress) + self.progress_bar.set_text(deluge.common.fpcnt(progress)) + + self.name.set_text(status["name"]) + self.total_size.set_text( + deluge.common.fsize(status["total_size"])) + self.num_files.set_text(str(status["num_files"])) + self.pieces.set_text("%s (%s)" % (status["num_pieces"], + deluge.common.fsize(status["piece_length"]))) + self.availability.set_text( + "%.3f" % status["distributed_copies"]) + self.total_downloaded.set_text("%s (%s)" % \ + (deluge.common.fsize(status["total_done"]), + deluge.common.fsize(status["total_payload_download"]))) + self.total_uploaded.set_text("%s (%s)" % \ + (deluge.common.fsize(status["total_uploaded"]), + deluge.common.fsize(status["total_payload_upload"]))) + self.download_speed.set_text( + deluge.common.fspeed(status["download_payload_rate"])) + self.upload_speed.set_text( + deluge.common.fspeed(status["upload_payload_rate"])) + self.seeders.set_text(deluge.common.fpeer(status["num_seeds"], + status["total_seeds"])) + self.peers.set_text(deluge.common.fpeer(status["num_peers"], + status["total_peers"])) + self.eta.set_text(deluge.common.ftime(status["eta"])) + self.share_ratio.set_text("%.3f" % status["ratio"]) + self.tracker.set_text(status["tracker"]) + self.tracker_status.set_text(status["tracker_status"]) + self.next_announce.set_text( + deluge.common.ftime(status["next_announce"])) + self.torrent_path.set_text(status["save_path"]) + except KeyError, e: + log.debug(e) + + def clear(self): # Only update if this page is showing diff --git a/deluge/ui/gtkui/torrentview.py b/deluge/ui/gtkui/torrentview.py index 1a9845fba..3195755d8 100644 --- a/deluge/ui/gtkui/torrentview.py +++ b/deluge/ui/gtkui/torrentview.py @@ -40,6 +40,7 @@ import gettext import gobject import cPickle as pickle import time +import traceback import deluge.common import deluge.component as component @@ -186,10 +187,14 @@ class TorrentView(listview.ListView, component.Component): """Start the torrentview""" # We need to get the core session state to know which torrents are in # the session so we can add them to our list. - session_state = client.get_session_state() - for torrent_id in session_state: - self.add_row(torrent_id) + client.get_session_state(self._on_session_state) + def _on_session_state(self, state): + for torrent_id in state: + self.add_row(torrent_id) + + self.update() + def stop(self): """Stops the torrentview""" # We need to clear the liststore @@ -245,18 +250,17 @@ class TorrentView(listview.ListView, component.Component): torrent_ids.append(self.liststore.get_value( row, self.columns["torrent_id"].column_indices[0])) row = self.liststore.iter_next(row) - + if torrent_ids == []: return # Request the statuses for all these torrent_ids, this is async so we # will deal with the return in a signal callback. - client.get_torrents_status(torrent_ids, status_keys) - - def update(self, columns=None): - """Update the view. If columns is not None, it will attempt to only - update those columns selected. - """ + client.get_torrents_status( + self._on_get_torrents_status, torrent_ids, status_keys) + + def update(self): + # Update the filter view def foreachrow(model, path, row, data): filter_column = self.columns["filter"].column_indices[0] # Create a function to create a new liststore with only the @@ -275,7 +279,13 @@ class TorrentView(listview.ListView, component.Component): model.set_value(row, filter_column, False) self.liststore.foreach(foreachrow, self.filter) + # Send a status request + self.send_status_request() + def update_view(self, columns=None): + """Update the view. If columns is not None, it will attempt to only + update those columns selected. + """ # Update the torrent view model with data we've received status = self.status row = self.liststore.get_iter_first() @@ -320,15 +330,18 @@ class TorrentView(listview.ListView, component.Component): except: pass row = self.liststore.iter_next(row) - - # Send a request for a status update - self.send_status_request(columns) - - def on_torrent_status_signal(self, status): + + def _on_get_torrents_status(self, status): """Callback function for get_torrents_status(). 'status' should be a dictionary of {torrent_id: {key, value}}.""" - self.status = pickle.loads(status) - + if status != None: + self.status = status + else: + self.status = {} + + if self.status != {}: + self.update_view() + def add_row(self, torrent_id): """Adds a new torrent row to the treeview""" # Insert a new row to the liststore @@ -338,7 +351,6 @@ class TorrentView(listview.ListView, component.Component): row, self.columns["torrent_id"].column_indices[0], torrent_id) - self.update() def remove_row(self, torrent_id): """Removes a row with torrent_id""" @@ -366,22 +378,31 @@ class TorrentView(listview.ListView, component.Component): try: paths = self.treeview.get_selection().get_selected_rows()[1] except AttributeError: - # paths is likely None .. so lets return None - return None + # paths is likely None .. so lets return [] + return [] try: for path in paths: torrent_ids.append( - self.liststore.get_value( - self.liststore.get_iter(path), 0)) + self.model_filter.get_value( + self.model_filter.get_iter(path), 0)) if len(torrent_ids) is 0: - # Only return a list if there is something in it. - return None + return [] return torrent_ids except ValueError: - return None - + return [] + + def get_torrent_status(self, torrent_id): + """Returns data stored in self.status, it may not be complete""" + try: + return self.status[torrent_id] + except: + return {} + + def get_visible_torrents(self): + return self.status.keys() + ### Callbacks ### def on_button_press_event(self, widget, event): """This is a callback for showing the right-click context menu.""" diff --git a/deluge/ui/signalreceiver.py b/deluge/ui/signalreceiver.py index 60689d474..606d8a6cd 100644 --- a/deluge/ui/signalreceiver.py +++ b/deluge/ui/signalreceiver.py @@ -79,17 +79,13 @@ class SignalReceiver( self.register_function(self.emit_signal) # Register the signal receiver with the core - core = client.get_core() - core.register_client(str(port)) + client.register_client(str(port)) def shutdown(self): """Shutdowns receiver thread""" self._shutdown = True # De-register with the daemon so it doesn't try to send us more signals - try: - client.get_core().deregister_client() - except (socket.error, AttributeError): - pass + client.deregister_client() # Hacky.. sends a request to our local receiver to ensure that it # shutdowns.. This is because handle_request() is a blocking call.