From 64a94ec197ed19a760df3cc0aa8cfbc9b602f2d8 Mon Sep 17 00:00:00 2001 From: Andrew Resch Date: Sun, 14 Oct 2007 07:06:19 +0000 Subject: [PATCH] Add SignalReceiver and use it in the client. Add SignalManager in the core for emitting signals to clients. --- deluge/core/core.py | 22 +++++++- deluge/core/signalmanager.py | 53 ++++++++++++++++++ deluge/ui/gtkui/gtkui.py | 5 +- deluge/ui/gtkui/signals.py | 20 ++++--- deluge/ui/signalreceiver.py | 105 +++++++++++++++++++++++++++++++++++ 5 files changed, 192 insertions(+), 13 deletions(-) create mode 100644 deluge/core/signalmanager.py create mode 100644 deluge/ui/signalreceiver.py diff --git a/deluge/core/core.py b/deluge/core/core.py index c830ed496..6d266ad0c 100644 --- a/deluge/core/core.py +++ b/deluge/core/core.py @@ -49,6 +49,7 @@ import deluge.common from deluge.core.torrentmanager import TorrentManager from deluge.core.pluginmanager import PluginManager from deluge.core.alertmanager import AlertManager +from deluge.core.signalmanager import SignalManager from deluge.log import LOG as log DEFAULT_PREFS = { @@ -76,7 +77,10 @@ DEFAULT_PREFS = { "enabled_plugins": ["Queue"] } -class Core(threading.Thread, ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServer): +class Core( + threading.Thread, + ThreadingMixIn, + SimpleXMLRPCServer.SimpleXMLRPCServer): def __init__(self): log.debug("Core init..") threading.Thread.__init__(self) @@ -161,7 +165,10 @@ class Core(threading.Thread, ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServ # Start the AlertManager self.alerts = AlertManager(self.session) - + + # Start the SignalManager + self.signals = SignalManager() + # Start the TorrentManager self.torrents = TorrentManager(self.session, self.alerts) @@ -197,6 +204,11 @@ class Core(threading.Thread, ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServ # Make shutdown an async call gobject.idle_add(self._shutdown) + def export_register_client(self, uri): + """Registers a client with the signal manager so that signals are + sent to it.""" + self.signals.register_client(uri) + def export_add_torrent_file(self, filename, save_path, filedump): """Adds a torrent file to the libtorrent session This requires the torrents filename and a dump of it's content @@ -352,26 +364,32 @@ class Core(threading.Thread, ThreadingMixIn, SimpleXMLRPCServer.SimpleXMLRPCServ def torrent_added(self, torrent_id): """Emitted when a new torrent is added to the core""" log.debug("torrent_added signal emitted") + self.signals.emit("torrent_added", torrent_id) def torrent_removed(self, torrent_id): """Emitted when a torrent has been removed from the core""" log.debug("torrent_remove signal emitted") + self.signals.emit("torrent_removed", torrent_id) def torrent_paused(self, torrent_id): """Emitted when a torrent is paused""" log.debug("torrent_paused signal emitted") + self.signals.emit("torrent_paused", torrent_id) def torrent_resumed(self, torrent_id): """Emitted when a torrent is resumed""" log.debug("torrent_resumed signal emitted") + self.signals.emit("torrent_resumed", torrent_id) def torrent_all_paused(self): """Emitted when all torrents have been paused""" log.debug("torrent_all_paused signal emitted") + self.signals.emit("torrent_all_paused", torrent_id) def torrent_all_resumed(self): """Emitted when all torrents have been resumed""" log.debug("torrent_all_resumed signal emitted") + self.signals.emit("torrent_all_resumed", torrent_id) # Config set functions def _on_set_listen_ports(self, key, value): diff --git a/deluge/core/signalmanager.py b/deluge/core/signalmanager.py new file mode 100644 index 000000000..ec3fdf318 --- /dev/null +++ b/deluge/core/signalmanager.py @@ -0,0 +1,53 @@ +# +# signalmanager.py +# +# Copyright (C) 2007 Andrew Resch ('andar') +# +# Deluge is free software. +# +# You may redistribute it and/or modify it under the terms of the +# GNU General Public License, as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) +# any later version. +# +# deluge is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with deluge. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. +# +# In addition, as a special exception, the copyright holders give +# permission to link the code of portions of this program with the OpenSSL +# library. +# You must obey the GNU General Public License in all respects for all of +# the code used other than OpenSSL. If you modify file(s) with this +# exception, you may extend this exception to your version of the file(s), +# but you are not obligated to do so. If you do not wish to do so, delete +# this exception statement from your version. If you delete this exception +# statement from all source files in the program, then also delete it here. + +import xmlrpclib + +from deluge.log import LOG as log + +class SignalManager: + def __init__(self): + self.clients = [] + + def register_client(self, uri): + """Registers a client to emit signals to.""" + log.debug("Registering %s as a signal reciever..", uri) + self.clients.append(xmlrpclib.ServerProxy(uri)) + + def emit(self, signal, data): + for client in self.clients: + try: + client.emit_signal(signal, data) + except: + log.warning("Unable to emit signal to client %s", client) + diff --git a/deluge/ui/gtkui/gtkui.py b/deluge/ui/gtkui/gtkui.py index 6d0b0f6a7..f11c89198 100644 --- a/deluge/ui/gtkui/gtkui.py +++ b/deluge/ui/gtkui/gtkui.py @@ -93,8 +93,8 @@ class GtkUI: self.mainwindow = MainWindow() # Start the signal receiver - #self.signal_receiver = Signals(self) - + self.signal_receiver = Signals(self) + # Initalize the plugins self.plugins = PluginManager(self) @@ -102,6 +102,7 @@ class GtkUI: self.mainwindow.start() # Start the gtk main loop + gtk.gdk.threads_init() gtk.main() log.debug("gtkui shutting down..") diff --git a/deluge/ui/gtkui/signals.py b/deluge/ui/gtkui/signals.py index 1698af094..37b9f35e3 100644 --- a/deluge/ui/gtkui/signals.py +++ b/deluge/ui/gtkui/signals.py @@ -31,21 +31,23 @@ # this exception statement from your version. If you delete this exception # statement from all source files in the program, then also delete it here. -import deluge.ui.functions as functions +from deluge.ui.signalreceiver import SignalReceiver from deluge.log import LOG as log class Signals: def __init__(self, ui): self.ui = ui - self.core = functions.get_core() - self.core.connect_to_signal("torrent_added", self.torrent_added_signal) - self.core.connect_to_signal("torrent_removed", - self.torrent_removed_signal) - self.core.connect_to_signal("torrent_paused", self.torrent_paused) - self.core.connect_to_signal("torrent_resumed", self.torrent_resumed) - self.core.connect_to_signal("torrent_all_paused", + self.receiver = SignalReceiver(6667, "http://localhost:6666") + self.receiver.start() + self.receiver.connect_to_signal("torrent_added", + self.torrent_added_signal) + self.receiver.connect_to_signal("torrent_removed", + self.torrent_removed_signal) + self.receiver.connect_to_signal("torrent_paused", self.torrent_paused) + self.receiver.connect_to_signal("torrent_resumed", self.torrent_resumed) + self.receiver.connect_to_signal("torrent_all_paused", self.torrent_all_paused) - self.core.connect_to_signal("torrent_all_resumed", + self.receiver.connect_to_signal("torrent_all_resumed", self.torrent_all_resumed) def torrent_added_signal(self, torrent_id): diff --git a/deluge/ui/signalreceiver.py b/deluge/ui/signalreceiver.py new file mode 100644 index 000000000..d22809942 --- /dev/null +++ b/deluge/ui/signalreceiver.py @@ -0,0 +1,105 @@ +# +# signalreceiver.py +# +# Copyright (C) 2007 Andrew Resch ('andar') +# +# Deluge is free software. +# +# You may redistribute it and/or modify it under the terms of the +# GNU General Public License, as published by the Free Software +# Foundation; either version 2 of the License, or (at your option) +# any later version. +# +# deluge is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. +# See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with deluge. If not, write to: +# The Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor +# Boston, MA 02110-1301, USA. +# +# In addition, as a special exception, the copyright holders give +# permission to link the code of portions of this program with the OpenSSL +# library. +# You must obey the GNU General Public License in all respects for all of +# the code used other than OpenSSL. If you modify file(s) with this +# exception, you may extend this exception to your version of the file(s), +# but you are not obligated to do so. If you do not wish to do so, delete +# this exception statement from your version. If you delete this exception +# statement from all source files in the program, then also delete it here. + +import sys +import SimpleXMLRPCServer +from SocketServer import ThreadingMixIn +import xmlrpclib +import threading + +from deluge.log import LOG as log + +class SignalReceiver( + threading.Thread, + ThreadingMixIn, + SimpleXMLRPCServer.SimpleXMLRPCServer): + + def __init__(self, port, core_uri): + log.debug("SignalReceiver init..") + threading.Thread.__init__(self) + + # Daemonize the thread so it exits when the main program does + self.setDaemon(True) + + # Setup the xmlrpc server + try: + SimpleXMLRPCServer.SimpleXMLRPCServer.__init__( + self, ("localhost", port), logRequests=False, allow_none=True) + except: + log.info("SignalReceiver already running or port not available..") + sys.exit(0) + + self.signals = {} + + # Register the emit_signal function + self.register_function(self.emit_signal) + + # Register the signal receiver with the core + # FIXME: send actual URI not localhost + core = xmlrpclib.ServerProxy(core_uri) + core.register_client("http://localhost:" + str(port)) + + def run(self): + """This gets called when we start the thread""" + t = threading.Thread(target=self.serve_forever) + t.start() + + def emit_signal(self, signal, data): + """Exported method used by the core to emit a signal to the client""" + log.debug("Received signal %s with data %s from core..", signal, data) + try: + if data != None: + for callback in self.signals[signal]: + try: + callback(data) + except: + log.warning("Unable to call callback for signal %s", + signal) + else: + for callback in self.signals[signal]: + try: + callback() + except: + log.warning("Unable to call callback for signal %s", + signal) + except KeyError: + log.debug("There are no callbacks registered for this signal..") + + def connect_to_signal(self, signal, callback): + """Connect to a signal""" + try: + self.signals[signal].append(callback) + except KeyError: + self.signals[signal] = [callback] + +