Change client.py to use multicalls. This forces all client methods to

be async.
This commit is contained in:
Andrew Resch 2008-01-19 22:33:30 +00:00
parent 29c77e1a04
commit 1fa301cb69
14 changed files with 388 additions and 365 deletions

View File

@ -35,7 +35,6 @@ import gettext
import locale import locale
import pkg_resources import pkg_resources
import sys import sys
import cPickle as pickle
import shutil import shutil
import os import os
@ -119,7 +118,9 @@ class Core(
except: except:
log.info("Daemon already running or port not available..") log.info("Daemon already running or port not available..")
sys.exit(0) sys.exit(0)
self.register_multicall_functions()
# Register all export_* functions # Register all export_* functions
for func in dir(self): for func in dir(self):
if func.startswith("export_"): if func.startswith("export_"):
@ -350,14 +351,9 @@ class Core(
leftover_fields = list(set(keys) - set(status.keys())) leftover_fields = list(set(keys) - set(status.keys()))
if len(leftover_fields) > 0: if len(leftover_fields) > 0:
status.update(self.plugins.get_status(torrent_id, leftover_fields)) 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): 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) status_dict = {}.fromkeys(torrent_ids)
# Get the torrent status for each torrent_id # Get the torrent status for each torrent_id
@ -374,9 +370,8 @@ class Core(
status_dict[torrent_id] = status status_dict[torrent_id] = status
# Emit the torrent_status signal to the clients # Emit the torrent_status signal to the clients
self.torrent_status(pickle.dumps(status_dict)) return status_dict
return False
def export_get_session_state(self): def export_get_session_state(self):
"""Returns a list of torrent_ids in the session.""" """Returns a list of torrent_ids in the session."""
# Get the torrent list from the TorrentManager # Get the torrent list from the TorrentManager
@ -477,11 +472,7 @@ class Core(
"""Emitted when all torrents have been resumed""" """Emitted when all torrents have been resumed"""
log.debug("torrent_all_resumed signal emitted") log.debug("torrent_all_resumed signal emitted")
self.signals.emit("torrent_all_resumed", torrent_id) 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 # Config set functions
def _on_set_torrentfiles_location(self, key, value): def _on_set_torrentfiles_location(self, key, value):
try: try:

View File

@ -132,7 +132,42 @@ class CoreProxy(gobject.GObject):
gobject.GObject.__init__(self) gobject.GObject.__init__(self)
self._uri = None self._uri = None
self._core = 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): def set_core_uri(self, uri):
log.info("Setting core uri as %s", 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: if self._core is None and self._uri is not None:
log.debug("Creating ServerProxy..") log.debug("Creating ServerProxy..")
self._core = xmlrpclib.ServerProxy(self._uri, allow_none=True) self._core = xmlrpclib.ServerProxy(self._uri, allow_none=True)
self._multi = xmlrpclib.MultiCall(self._core)
# Call any callbacks registered # Call any callbacks registered
self.emit("new_core") self.emit("new_core")
return self._core return self._core
_core = CoreProxy() _core = CoreProxy()
def get_core(): def get_core():
"""Get the core object and return it""" """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): def connect_on_new_core(callback):
"""Connect a callback whenever a new core is connected to.""" """Connect a callback whenever a new core is connected to."""
return _core.connect("new_core", callback) return _core.connect("new_core", callback)
@ -211,15 +238,27 @@ def connected():
if get_core_uri() != None: if get_core_uri() != None:
return True return True
return False return False
def register_client(port):
get_core().call("register_client", None, port)
def deregister_client():
get_core().call("deregister_client", None)
def shutdown(): def shutdown():
"""Shutdown the core daemon""" """Shutdown the core daemon"""
try: try:
get_core().shutdown() get_core().call("shutdown", None)
force_call(block=False)
except: except:
# Ignore everything # Ignore everything
set_core_uri(None) 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): def add_torrent_file(torrent_files, torrent_options=None):
"""Adds torrent files to the core """Adds torrent files to the core
Expects a list of torrent files Expects a list of torrent files
@ -252,216 +291,101 @@ def add_torrent_file(torrent_files, torrent_options=None):
else: else:
options = None options = None
try: get_core().call("add_torrent_file", None,
result = get_core().add_torrent_file( filename, str(), fdump, options)
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)
def add_torrent_url(torrent_url, options=None): def add_torrent_url(torrent_url, options=None):
"""Adds torrents to the core via url""" """Adds torrents to the core via url"""
from deluge.common import is_url from deluge.common import is_url
if is_url(torrent_url): if is_url(torrent_url):
try: get_core().call("add_torrent_url", None,
result = get_core().add_torrent_url(torrent_url, str(), options) 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)
else: else:
log.warning("Invalid URL %s", torrent_url) log.warning("Invalid URL %s", torrent_url)
def remove_torrent(torrent_ids, remove_torrent=False, remove_data=False): def remove_torrent(torrent_ids, remove_torrent=False, remove_data=False):
"""Removes torrent_ids from the core.. Expects a list of torrent_ids""" """Removes torrent_ids from the core.. Expects a list of torrent_ids"""
log.debug("Attempting to removing torrents: %s", torrent_ids) log.debug("Attempting to removing torrents: %s", torrent_ids)
try: for torrent_id in torrent_ids:
for torrent_id in torrent_ids: get_core().call("remove_torrent", None, torrent_id, remove_torrent, remove_data)
get_core().remove_torrent(torrent_id, remove_torrent, remove_data)
except (AttributeError, socket.error):
set_core_uri(None)
def pause_torrent(torrent_ids): def pause_torrent(torrent_ids):
"""Pauses torrent_ids""" """Pauses torrent_ids"""
try: for torrent_id in torrent_ids:
for torrent_id in torrent_ids: get_core().call("pause_torrent", None, torrent_id)
get_core().pause_torrent(torrent_id)
except (AttributeError, socket.error):
set_core_uri(None)
def pause_all_torrents(): def pause_all_torrents():
"""Pauses all torrents""" """Pauses all torrents"""
try: get_core().call("pause_all_torrents", None)
get_core().pause_all_torrents()
except (AttributeError, socket.error):
set_core_uri(None)
def resume_all_torrents(): def resume_all_torrents():
"""Resumes all torrents""" """Resumes all torrents"""
try: get_core().call("resume_all_torrents", None)
get_core().resume_all_torrents()
except (AttributeError, socket.error):
set_core_uri(None)
def resume_torrent(torrent_ids): def resume_torrent(torrent_ids):
"""Resume torrent_ids""" """Resume torrent_ids"""
try: for torrent_id in torrent_ids:
for torrent_id in torrent_ids: get_core().call("resume_torrent", None, torrent_id)
get_core().resume_torrent(torrent_id)
except (AttributeError, socket.error):
set_core_uri(None)
def force_reannounce(torrent_ids): def force_reannounce(torrent_ids):
"""Reannounce to trackers""" """Reannounce to trackers"""
try: for torrent_id in torrent_ids:
for torrent_id in torrent_ids: get_core().call("force_reannounce", None, torrent_id)
get_core().force_reannounce(torrent_id)
except (AttributeError, socket.error):
set_core_uri(None)
@cache_dict def get_torrent_status(callback, torrent_id, keys):
def get_torrent_status(torrent_id, keys):
"""Builds the status dictionary and returns it""" """Builds the status dictionary and returns it"""
try: get_core().call("get_torrent_status", callback, torrent_id, keys)
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)
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 """Builds a dictionary of torrent_ids status. Expects 2 lists. This is
asynchronous so the return value will be sent as the signal asynchronous so the return value will be sent as the signal
'torrent_status'""" 'torrent_status'"""
try: get_core().call("get_torrents_status", callback, torrent_ids, keys)
get_core().get_torrents_status(torrent_ids, keys)
except (AttributeError, socket.error):
set_core_uri(None)
return None def get_session_state(callback):
get_core().call("get_session_state", callback)
@cache
def get_session_state():
try:
state = get_core().get_session_state()
except (AttributeError, socket.error):
set_core_uri(None)
state = []
return state
@cache def get_config(callback):
def get_config(): get_core().call("get_config", callback)
try:
config = get_core().get_config()
except (AttributeError, socket.error):
set_core_uri(None)
config = {}
return config
@cache def get_config_value(callback, key):
def get_config_value(key): get_core().call("get_config_value", callback, 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 set_config(config): def set_config(config):
if config == {}: if config == {}:
return return
try: get_core().call("set_config", None, config)
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)
@cache def get_listen_port(callback):
def get_available_plugins(): get_core().call("get_listen_port", callback)
try:
available = get_core().get_available_plugins()
except (AttributeError, socket.error):
set_core_uri(None)
available = []
return available
@cache def get_available_plugins(callback):
def get_enabled_plugins(): get_core().call("get_available_plugins", callback)
try:
enabled = get_core().get_enabled_plugins()
except (AttributeError, socket.error):
set_core_uri(None)
enabled = []
return enabled
@cache def get_enabled_plugins(callback):
def get_download_rate(): get_core().call("get_enabled_plugins", callback)
try:
rate = get_core().get_download_rate()
except (AttributeError, socket.error):
set_core_uri(None)
rate = -1
return rate
@cache def get_download_rate(callback):
def get_upload_rate(): get_core().call("get_download_rate", callback)
try:
rate = get_core().get_upload_rate()
except (AttributeError, socket.error):
set_core_uri(None)
rate = -1
return rate
@cache def get_upload_rate(callback):
def get_num_connections(): get_core().call("get_upload_rate", callback)
try:
num_connections = get_core().get_num_connections() def get_num_connections(callback):
except (AttributeError, socket.error): get_core().call("get_num_connections", callback)
set_core_uri(None)
num_connections = 0
return num_connections
def enable_plugin(plugin): def enable_plugin(plugin):
try: get_core().call("enable_plugin", None, plugin)
get_core().enable_plugin(plugin)
except (AttributeError, socket.error):
set_core_uri(None)
def disable_plugin(plugin): def disable_plugin(plugin):
try: get_core().call("disable_plugin", None, plugin)
get_core().disable_plugin(plugin)
except (AttributeError, socket.error):
set_core_uri(None)
def force_recheck(torrent_ids): def force_recheck(torrent_ids):
"""Forces a data recheck on torrent_ids""" """Forces a data recheck on torrent_ids"""
try: for torrent_id in torrent_ids:
for torrent_id in torrent_ids: get_core().call("force_recheck", None, torrent_id)
get_core().force_recheck(torrent_id)
except (AttributeError, socket.error):
set_core_uri(None)
def set_torrent_trackers(torrent_id, trackers): def set_torrent_trackers(torrent_id, trackers):
"""Sets the torrents trackers""" """Sets the torrents trackers"""
try: get_core().call("set_torrent_trackers", None, torrent_id, trackers)
get_core().set_torrent_trackers(torrent_id, trackers)
except (AttributeError, socket.error):
set_core_uri(None)

View File

@ -119,10 +119,14 @@ class AddTorrentDialog:
"add_paused", "add_paused",
"default_private" "default_private"
] ]
self.core_config = {}
# Send requests to the core for these config values
for key in self.core_keys: 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() self.set_default_options()
def show(self): def show(self):
@ -133,6 +137,12 @@ class AddTorrentDialog:
self.dialog.destroy() self.dialog.destroy()
return None 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): def add_to_torrent_list(self, filenames):
import deluge.libtorrent as lt import deluge.libtorrent as lt
import os.path import os.path

View File

@ -152,21 +152,9 @@ class GtkUI:
# Make sure the config is saved. # Make sure the config is saved.
config.save() config.save()
del config
# Clean-up
# Shutdown all components # Shutdown all components
component.shutdown() 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): def _on_new_core(self, data):
component.start() component.start()

View File

@ -72,7 +72,7 @@ def cell_data_time(column, cell, model, row, data):
"""Display value as time, eg 1m10s""" """Display value as time, eg 1m10s"""
time = model.get_value(row, data) time = model.get_value(row, data)
if time <= 0: if time <= 0:
time_str = _("Infinity") time_str = ""
else: else:
time_str = deluge.common.ftime(time) time_str = deluge.common.ftime(time)
cell.set_property('text', time_str) cell.set_property('text', time_str)

View File

@ -48,17 +48,19 @@ class PluginManager(deluge.pluginmanagerbase.PluginManagerBase,
def start(self): def start(self):
"""Start the plugin manager""" """Start the plugin manager"""
# Update the enabled_plugins from the core # 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) log.debug("Core has these plugins enabled: %s", enabled_plugins)
self.config["enabled_plugins"] = enabled_plugins self.config["enabled_plugins"] = enabled_plugins
# Enable the plugins that are enabled in the config and core # Enable the plugins that are enabled in the config and core
self.enable_plugins() self.enable_plugins()
def stop(self):
# Disable the plugins
self.disable_plugins()
## Plugin functions.. will likely move to own class.. ## Plugin functions.. will likely move to own class..
def add_torrentview_text_column(self, *args, **kwargs): def add_torrentview_text_column(self, *args, **kwargs):

View File

@ -147,10 +147,28 @@ class Preferences(component.Component):
self.notebook.remove_page(self.page_num_to_remove) self.notebook.remove_page(self.page_num_to_remove)
if self.iter_to_remove != None: if self.iter_to_remove != None:
self.liststore.remove(self.iter_to_remove) 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): def show(self):
# Update the preferences dialog to reflect current config settings # 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: if self.core_config != {} and self.core_config != None:
core_widgets = { core_widgets = {
@ -171,7 +189,7 @@ class Preferences(component.Component):
self.core_config["prioritize_first_last_pieces"]), self.core_config["prioritize_first_last_pieces"]),
"spin_port_min": ("value", self.core_config["listen_ports"][0]), "spin_port_min": ("value", self.core_config["listen_ports"][0]),
"spin_port_max": ("value", self.core_config["listen_ports"][1]), "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_random_port": ("active", self.core_config["random_port"]),
"chk_dht": ("active", self.core_config["dht"]), "chk_dht": ("active", self.core_config["dht"]),
"chk_upnp": ("active", self.core_config["upnp"]), "chk_upnp": ("active", self.core_config["upnp"]),
@ -297,8 +315,8 @@ class Preferences(component.Component):
self.gtkui_config["send_info"]) self.gtkui_config["send_info"])
## Plugins tab ## ## Plugins tab ##
all_plugins = client.get_available_plugins() all_plugins = self.all_plugins
enabled_plugins = client.get_enabled_plugins() enabled_plugins = self.enabled_plugins
# Clear the existing list so we don't duplicate entries. # Clear the existing list so we don't duplicate entries.
self.plugin_liststore.clear() self.plugin_liststore.clear()
# Iterate through the lists and add them to the liststore # Iterate through the lists and add them to the liststore

View File

@ -57,8 +57,6 @@ class Signals(component.Component):
self.torrent_all_paused) self.torrent_all_paused)
self.receiver.connect_to_signal("torrent_all_resumed", self.receiver.connect_to_signal("torrent_all_resumed",
self.torrent_all_resumed) self.torrent_all_resumed)
self.receiver.connect_to_signal("torrent_status",
self.torrent_status)
def stop(self): def stop(self):
self.receiver.shutdown() self.receiver.shutdown()
@ -80,22 +78,20 @@ class Signals(component.Component):
def torrent_paused(self, torrent_id): def torrent_paused(self, torrent_id):
log.debug("torrent_paused signal received..") log.debug("torrent_paused signal received..")
component.get("TorrentView").update() component.get("TorrentView").update()
component.get("ToolBar").update_buttons() component.get("ToolBar").update_buttons("paused", torrent_id)
def torrent_resumed(self, torrent_id): def torrent_resumed(self, torrent_id):
log.debug("torrent_resumed signal received..") log.debug("torrent_resumed signal received..")
component.get("TorrentView").update() component.get("TorrentView").update()
component.get("ToolBar").update_buttons() component.get("ToolBar").update_buttons("resumed", torrent_id)
def torrent_all_paused(self): def torrent_all_paused(self):
log.debug("torrent_all_paused signal received..") log.debug("torrent_all_paused signal received..")
component.get("TorrentView").update() component.get("TorrentView").update()
component.get("ToolBar").update_buttons() component.get("ToolBar").update_buttons("paused")
def torrent_all_resumed(self): def torrent_all_resumed(self):
log.debug("torrent_all_resumed signal received..") log.debug("torrent_all_resumed signal received..")
component.get("TorrentView").update() component.get("TorrentView").update()
component.get("ToolBar").update_buttons() component.get("ToolBar").update_buttons("resumed")
def torrent_status(self, status):
component.get("TorrentView").on_torrent_status_signal(status)

View File

@ -82,7 +82,8 @@ class StatusBarItem:
self._image.set_from_stock(stock, gtk.ICON_SIZE_MENU) self._image.set_from_stock(stock, gtk.ICON_SIZE_MENU)
def set_text(self, text): 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): def get_widgets(self):
return self._widgets() return self._widgets()
@ -96,6 +97,14 @@ class StatusBar(component.Component):
self.window = component.get("MainWindow") self.window = component.get("MainWindow")
self.statusbar = self.window.main_glade.get_widget("statusbar") 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 # Add a HBox to the statusbar after removing the initial label widget
self.hbox = gtk.HBox() self.hbox = gtk.HBox()
self.hbox.set_spacing(5) self.hbox.set_spacing(5)
@ -121,6 +130,8 @@ class StatusBar(component.Component):
image=deluge.common.get_pixmap("seeding16.png")) image=deluge.common.get_pixmap("seeding16.png"))
self.hbox.pack_start( self.hbox.pack_start(
self.upload_item.get_eventbox(), expand=False, fill=False) self.upload_item.get_eventbox(), expand=False, fill=False)
self.send_status_request()
def stop(self): def stop(self):
# When stopped, we just show the not connected thingy # When stopped, we just show the not connected thingy
@ -153,35 +164,65 @@ class StatusBar(component.Component):
def remove(child): def remove(child):
self.hbox.remove(child) self.hbox.remove(child)
self.hbox.foreach(remove) 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): def update(self):
# Set the max connections label # Set the max connections label
max_connections = client.get_config_value("max_connections_global") max_connections = self.max_connections
if max_connections < 0: if max_connections < 0:
max_connections = _("Unlimited") max_connections = _("Unlimited")
self.connections_item.set_text("%s (%s)" % ( self.connections_item.set_text("%s (%s)" % (
client.get_num_connections(), max_connections)) self.num_connections, max_connections))
# Set the download speed label # 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: if max_download_speed < 0:
max_download_speed = _("Unlimited") max_download_speed = _("Unlimited")
else: else:
max_download_speed = "%s %s" % (max_download_speed, _("KiB/s")) max_download_speed = "%s %s" % (max_download_speed, _("KiB/s"))
self.download_item.set_text("%s/s (%s)" % ( self.download_item.set_text("%s/s (%s)" % (
deluge.common.fsize(client.get_download_rate()), self.download_rate, max_download_speed))
max_download_speed))
# Set the upload speed label # 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: if max_upload_speed < 0:
max_upload_speed = _("Unlimited") max_upload_speed = _("Unlimited")
else: else:
max_upload_speed = "%s %s" % (max_upload_speed, _("KiB/s")) max_upload_speed = "%s %s" % (max_upload_speed, _("KiB/s"))
self.upload_item.set_text("%s/s (%s)" % ( self.upload_item.set_text("%s/s (%s)" % (
deluge.common.fsize(client.get_upload_rate()), self.upload_rate,
max_upload_speed)) max_upload_speed))
self.send_status_request()

View File

@ -60,6 +60,11 @@ class SystemTray(component.Component):
] ]
self.config.register_set_function("enable_system_tray", self.config.register_set_function("enable_system_tray",
self.on_enable_system_tray_set) 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): def enable(self):
"""Enables the system tray icon.""" """Enables the system tray icon."""
@ -111,6 +116,8 @@ class SystemTray(component.Component):
# Build the bandwidth speed limit menus # Build the bandwidth speed limit menus
self.build_tray_bwsetsubmenu() self.build_tray_bwsetsubmenu()
self.send_status_request()
def stop(self): def stop(self):
try: try:
@ -120,36 +127,62 @@ class SystemTray(component.Component):
except Exception, e: except Exception, e:
log.debug("Unable to hide system tray menu widgets: %s", 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): def update(self):
# Set the tool tip text # Set the tool tip text
max_download_speed = client.get_config_value("max_download_speed") max_download_speed = self.max_download_speed
max_upload_speed = client.get_config_value("max_upload_speed") max_upload_speed = self.max_upload_speed
if max_download_speed == -1: if max_download_speed == -1:
max_download_speed = _("Unlimited") max_download_speed = _("Unlimited")
if max_upload_speed == -1: if max_upload_speed == -1:
max_upload_speed = _("Unlimited") max_upload_speed = _("Unlimited")
msg = '%s\n%s: %s (%s)\n%s: %s (%s)' % (\ msg = '%s\n%s: %s (%s)\n%s: %s (%s)' % (
_("Deluge Bittorrent Client"), _("Down Speed"), \ _("Deluge Bittorrent Client"), _("Down Speed"),
deluge.common.fspeed(client.get_download_rate()), self.download_rate,
max_download_speed, _("Up Speed"), \ max_download_speed, _("Up Speed"),
deluge.common.fspeed(client.get_upload_rate()), max_upload_speed) self.upload_rate, max_upload_speed)
# Set the tooltip # Set the tooltip
self.tray.set_tooltip(msg) self.tray.set_tooltip(msg)
self.send_status_request()
def build_tray_bwsetsubmenu(self): def build_tray_bwsetsubmenu(self):
# Create the Download speed list sub-menu # Create the Download speed list sub-menu
submenu_bwdownset = self.build_menu_radio_list( submenu_bwdownset = self.build_menu_radio_list(
self.config["tray_download_speed_list"], self.tray_setbwdown, 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) _("KiB/s"), show_notset=True, show_other=True)
# Create the Upload speed list sub-menu # Create the Upload speed list sub-menu
submenu_bwupset = self.build_menu_radio_list( submenu_bwupset = self.build_menu_radio_list(
self.config["tray_upload_speed_list"], self.tray_setbwup, 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) _("KiB/s"), show_notset=True, show_other=True)
# Add the sub-menus to the tray menu # Add the sub-menus to the tray menu

View File

@ -164,56 +164,55 @@ class ToolBar(component.Component):
# Use the menubar's callbacks # Use the menubar's callbacks
component.get("MenuBar").on_menuitem_connectionmanager_activate(data) component.get("MenuBar").on_menuitem_connectionmanager_activate(data)
def update_buttons(self): def update_buttons(self, action=None, torrent_id=None):
log.debug("update_buttons") if action == None:
# If all the selected torrents are paused, then disable the 'Pause' # If all the selected torrents are paused, then disable the 'Pause'
# button. # button.
# The same goes for the 'Resume' button. # The same goes for the 'Resume' button.
pause = False pause = False
resume = False resume = False
# Disable the 'Clear Seeders' button if there's no finished torrent selected = component.get('TorrentView').get_selected_torrents()
finished = False if not selected:
selected = []
selected = component.get('TorrentView').get_selected_torrents() for torrent in selected:
if not selected: try:
selected = [] status = component.get("TorrentView").get_torrent_status(torrent)['state']
except KeyError, e:
for torrent in selected: log.debug("Error getting torrent state: %s", e)
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:
continue continue
status = client.get_torrent_status(torrent, ['state'])['state'] if status == self.STATE_PAUSED:
if status in [self.STATE_FINISHED, self.STATE_SEEDING]: resume = True
finished = True else:
pause = True
if pause and resume:
break break
for name, sensitive in (("toolbutton_pause", pause), # Enable the 'Remove Torrent' button only if there's some selected
("toolbutton_resume", resume), # torrent.
("toolbutton_remove", remove), remove = (len(selected) > 0)
("toolbutton_clear", finished)):
self.window.main_glade.get_widget(name).set_sensitive(sensitive) 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 return False

View File

@ -92,7 +92,7 @@ class TorrentDetails(component.Component):
def stop(self): def stop(self):
self.clear() self.clear()
def update(self): def update(self):
# Show tabs if more than 1 page # Show tabs if more than 1 page
if self.notebook.get_n_pages() > 1: if self.notebook.get_n_pages() > 1:
@ -107,7 +107,7 @@ class TorrentDetails(component.Component):
selected = component.get("TorrentView").get_selected_torrents() selected = component.get("TorrentView").get_selected_torrents()
# Only use the first torrent in the list or return if None selected # 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] selected = selected[0]
else: else:
# No torrent is selected in the torrentview # No torrent is selected in the torrentview
@ -121,50 +121,54 @@ class TorrentDetails(component.Component):
"upload_payload_rate", "num_peers", "num_seeds", "total_peers", "upload_payload_rate", "num_peers", "num_seeds", "total_peers",
"total_seeds", "eta", "ratio", "tracker", "next_announce", "total_seeds", "eta", "ratio", "tracker", "next_announce",
"tracker_status", "save_path"] "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 # We need to adjust the value core gives us for progress
if status is None: try:
return progress = status["progress"]/100
# We need to adjust the value core gives us for progress self.progress_bar.set_fraction(progress)
try: self.progress_bar.set_text(deluge.common.fpcnt(progress))
progress = status["progress"]/100
self.name.set_text(status["name"])
self.progress_bar.set_fraction(progress) self.total_size.set_text(
self.progress_bar.set_text(deluge.common.fpcnt(progress)) deluge.common.fsize(status["total_size"]))
self.num_files.set_text(str(status["num_files"]))
self.name.set_text(status["name"]) self.pieces.set_text("%s (%s)" % (status["num_pieces"],
self.total_size.set_text( deluge.common.fsize(status["piece_length"])))
deluge.common.fsize(status["total_size"])) self.availability.set_text(
self.num_files.set_text(str(status["num_files"])) "%.3f" % status["distributed_copies"])
self.pieces.set_text("%s (%s)" % (status["num_pieces"], self.total_downloaded.set_text("%s (%s)" % \
deluge.common.fsize(status["piece_length"]))) (deluge.common.fsize(status["total_done"]),
self.availability.set_text( deluge.common.fsize(status["total_payload_download"])))
"%.3f" % status["distributed_copies"]) self.total_uploaded.set_text("%s (%s)" % \
self.total_downloaded.set_text("%s (%s)" % \ (deluge.common.fsize(status["total_uploaded"]),
(deluge.common.fsize(status["total_done"]), deluge.common.fsize(status["total_payload_upload"])))
deluge.common.fsize(status["total_payload_download"]))) self.download_speed.set_text(
self.total_uploaded.set_text("%s (%s)" % \ deluge.common.fspeed(status["download_payload_rate"]))
(deluge.common.fsize(status["total_uploaded"]), self.upload_speed.set_text(
deluge.common.fsize(status["total_payload_upload"]))) deluge.common.fspeed(status["upload_payload_rate"]))
self.download_speed.set_text( self.seeders.set_text(deluge.common.fpeer(status["num_seeds"],
deluge.common.fspeed(status["download_payload_rate"])) status["total_seeds"]))
self.upload_speed.set_text( self.peers.set_text(deluge.common.fpeer(status["num_peers"],
deluge.common.fspeed(status["upload_payload_rate"])) status["total_peers"]))
self.seeders.set_text(deluge.common.fpeer(status["num_seeds"], self.eta.set_text(deluge.common.ftime(status["eta"]))
status["total_seeds"])) self.share_ratio.set_text("%.3f" % status["ratio"])
self.peers.set_text(deluge.common.fpeer(status["num_peers"], self.tracker.set_text(status["tracker"])
status["total_peers"])) self.tracker_status.set_text(status["tracker_status"])
self.eta.set_text(deluge.common.ftime(status["eta"])) self.next_announce.set_text(
self.share_ratio.set_text("%.3f" % status["ratio"]) deluge.common.ftime(status["next_announce"]))
self.tracker.set_text(status["tracker"]) self.torrent_path.set_text(status["save_path"])
self.tracker_status.set_text(status["tracker_status"]) except KeyError, e:
self.next_announce.set_text( log.debug(e)
deluge.common.ftime(status["next_announce"]))
self.torrent_path.set_text(status["save_path"])
except KeyError, e:
log.debug(e)
def clear(self): def clear(self):
# Only update if this page is showing # Only update if this page is showing

View File

@ -40,6 +40,7 @@ import gettext
import gobject import gobject
import cPickle as pickle import cPickle as pickle
import time import time
import traceback
import deluge.common import deluge.common
import deluge.component as component import deluge.component as component
@ -186,10 +187,14 @@ class TorrentView(listview.ListView, component.Component):
"""Start the torrentview""" """Start the torrentview"""
# We need to get the core session state to know which torrents are in # We need to get the core session state to know which torrents are in
# the session so we can add them to our list. # the session so we can add them to our list.
session_state = client.get_session_state() client.get_session_state(self._on_session_state)
for torrent_id in session_state:
self.add_row(torrent_id)
def _on_session_state(self, state):
for torrent_id in state:
self.add_row(torrent_id)
self.update()
def stop(self): def stop(self):
"""Stops the torrentview""" """Stops the torrentview"""
# We need to clear the liststore # We need to clear the liststore
@ -245,18 +250,17 @@ class TorrentView(listview.ListView, component.Component):
torrent_ids.append(self.liststore.get_value( torrent_ids.append(self.liststore.get_value(
row, self.columns["torrent_id"].column_indices[0])) row, self.columns["torrent_id"].column_indices[0]))
row = self.liststore.iter_next(row) row = self.liststore.iter_next(row)
if torrent_ids == []: if torrent_ids == []:
return return
# Request the statuses for all these torrent_ids, this is async so we # Request the statuses for all these torrent_ids, this is async so we
# will deal with the return in a signal callback. # will deal with the return in a signal callback.
client.get_torrents_status(torrent_ids, status_keys) client.get_torrents_status(
self._on_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 def update(self):
update those columns selected. # Update the filter view
"""
def foreachrow(model, path, row, data): def foreachrow(model, path, row, data):
filter_column = self.columns["filter"].column_indices[0] filter_column = self.columns["filter"].column_indices[0]
# Create a function to create a new liststore with only the # 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) model.set_value(row, filter_column, False)
self.liststore.foreach(foreachrow, self.filter) 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 # Update the torrent view model with data we've received
status = self.status status = self.status
row = self.liststore.get_iter_first() row = self.liststore.get_iter_first()
@ -320,15 +330,18 @@ class TorrentView(listview.ListView, component.Component):
except: except:
pass pass
row = self.liststore.iter_next(row) row = self.liststore.iter_next(row)
# Send a request for a status update def _on_get_torrents_status(self, status):
self.send_status_request(columns)
def on_torrent_status_signal(self, status):
"""Callback function for get_torrents_status(). 'status' should be a """Callback function for get_torrents_status(). 'status' should be a
dictionary of {torrent_id: {key, value}}.""" 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): def add_row(self, torrent_id):
"""Adds a new torrent row to the treeview""" """Adds a new torrent row to the treeview"""
# Insert a new row to the liststore # Insert a new row to the liststore
@ -338,7 +351,6 @@ class TorrentView(listview.ListView, component.Component):
row, row,
self.columns["torrent_id"].column_indices[0], self.columns["torrent_id"].column_indices[0],
torrent_id) torrent_id)
self.update()
def remove_row(self, torrent_id): def remove_row(self, torrent_id):
"""Removes a row with torrent_id""" """Removes a row with torrent_id"""
@ -366,22 +378,31 @@ class TorrentView(listview.ListView, component.Component):
try: try:
paths = self.treeview.get_selection().get_selected_rows()[1] paths = self.treeview.get_selection().get_selected_rows()[1]
except AttributeError: except AttributeError:
# paths is likely None .. so lets return None # paths is likely None .. so lets return []
return None return []
try: try:
for path in paths: for path in paths:
torrent_ids.append( torrent_ids.append(
self.liststore.get_value( self.model_filter.get_value(
self.liststore.get_iter(path), 0)) self.model_filter.get_iter(path), 0))
if len(torrent_ids) is 0: if len(torrent_ids) is 0:
# Only return a list if there is something in it. return []
return None
return torrent_ids return torrent_ids
except ValueError: 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 ### ### Callbacks ###
def on_button_press_event(self, widget, event): def on_button_press_event(self, widget, event):
"""This is a callback for showing the right-click context menu.""" """This is a callback for showing the right-click context menu."""

View File

@ -79,17 +79,13 @@ class SignalReceiver(
self.register_function(self.emit_signal) self.register_function(self.emit_signal)
# Register the signal receiver with the core # Register the signal receiver with the core
core = client.get_core() client.register_client(str(port))
core.register_client(str(port))
def shutdown(self): def shutdown(self):
"""Shutdowns receiver thread""" """Shutdowns receiver thread"""
self._shutdown = True self._shutdown = True
# De-register with the daemon so it doesn't try to send us more signals # De-register with the daemon so it doesn't try to send us more signals
try: client.deregister_client()
client.get_core().deregister_client()
except (socket.error, AttributeError):
pass
# Hacky.. sends a request to our local receiver to ensure that it # Hacky.. sends a request to our local receiver to ensure that it
# shutdowns.. This is because handle_request() is a blocking call. # shutdowns.. This is because handle_request() is a blocking call.