Change client.py to use multicalls. This forces all client methods to
be async.
This commit is contained in:
parent
29c77e1a04
commit
1fa301cb69
|
@ -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:
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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")
|
||||
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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."""
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue