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 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:

View File

@ -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)

View File

@ -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

View File

@ -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()

View File

@ -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)

View File

@ -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):

View File

@ -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

View File

@ -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")

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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."""

View File

@ -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.