From ca5297e20daff6189f921715910331cf43e3052c Mon Sep 17 00:00:00 2001 From: Andrew Resch Date: Tue, 24 Jul 2007 01:53:35 +0000 Subject: [PATCH] Can now remove torrents. Torrent view is now updating in timer. --- deluge/core/core.py | 38 +++++- deluge/core/torrent.py | 21 +++- deluge/core/torrentmanager.py | 34 ++++++ deluge/core/torrentqueue.py | 6 +- deluge/ui/gtkui/columns.py | 2 +- deluge/ui/gtkui/functions.py | 25 ++++ deluge/ui/gtkui/glade/torrent_menu.glade | 2 +- deluge/ui/gtkui/mainwindow.py | 7 ++ deluge/ui/gtkui/menubar.py | 2 + deluge/ui/gtkui/signals.py | 12 +- deluge/ui/gtkui/torrentview.py | 141 +++++++++++++++++------ 11 files changed, 238 insertions(+), 52 deletions(-) diff --git a/deluge/core/core.py b/deluge/core/core.py index b8e51a3fe..f16cc91e1 100644 --- a/deluge/core/core.py +++ b/deluge/core/core.py @@ -143,7 +143,21 @@ class Core(dbus.service.Object): self.torrent_added(torrent_id) @dbus.service.method(dbus_interface="org.deluge_torrent.Deluge", - in_signature="s", out_signature="(six)") + in_signature="s", out_signature="") + def remove_torrent(self, torrent_id): + log.debug("Removing torrent %s from the core.", torrent_id) + try: + # Remove from libtorrent session + self.session.remove_torrent(self.torrents[torrent_id].handle) + # Remove from TorrentManager + self.torrents.remove(torrent_id) + # Emit the torrent_removed signal + self.torrent_removed(torrent_id) + except RuntimeError, KeyError: + log.warning("Error removing torrent") + + @dbus.service.method(dbus_interface="org.deluge_torrent.Deluge", + in_signature="s", out_signature="(sxi)") def get_torrent_info(self, torrent_id): # Get the info tuple from the torrent and return it return self.torrents[torrent_id].get_info() @@ -154,6 +168,20 @@ class Core(dbus.service.Object): def get_torrent_status(self, torrent_id): # Get the status tuple from the torrent and return it return self.torrents[torrent_id].get_status() + + @dbus.service.method(dbus_interface="org.deluge_torrent.Deluge", + in_signature="", + out_signature="as") + def get_torrent_status_template(self): + # A list of strings the correspond to the status tuple + return self.torrents.get_status_template() + + @dbus.service.method(dbus_interface="org.deluge_torrent.Deluge", + in_signature="", + out_signature="as") + def get_torrent_info_template(self): + # A list of strings the correspond to the info tuple + return self.torrents.get_info_template() ## Queueing functions ###### @dbus.service.method(dbus_interface="org.deluge_torrent.Deluge", @@ -162,8 +190,6 @@ class Core(dbus.service.Object): # If the queue method returns True, then we should emit a signal if self.queue.top(torrent_id): self.torrent_queue_top() - # Store the new torrent position in the torrent object -# self.torrents[torrent_id].set_position(self.queue[torrent_id]) @dbus.service.method(dbus_interface="org.deluge_torrent.Deluge", in_signature="s", out_signature="") @@ -199,6 +225,12 @@ class Core(dbus.service.Object): """Emitted when a new torrent fails addition to the session""" log.debug("torrent_add_failed signal emitted") + @dbus.service.signal(dbus_interface="org.deluge_torrent.Deluge", + signature="s") + def torrent_removed(self, torrent_id): + """Emitted when a torrent has been removed from the core""" + log.debug("torrent_remove signal emitted") + @dbus.service.signal(dbus_interface="org.deluge_torrent.Deluge", signature="s") def torrent_queue_top(self, torrent_id): diff --git a/deluge/core/torrent.py b/deluge/core/torrent.py index 8b59dac59..54d695e88 100644 --- a/deluge/core/torrent.py +++ b/deluge/core/torrent.py @@ -31,8 +31,13 @@ # 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 logging + import deluge.libtorrent as lt +# Get the logger +log = logging.getLogger("deluge") + class Torrent: def __init__(self, handle, queue): # Set the libtorrent handle @@ -42,17 +47,20 @@ class Torrent: # Set the torrent_id for this torrent self.torrent_id = str(handle.info_hash()) + def __del__(self): + self.queue.remove(self.torrent_id) + def get_eta(self): """Returns the ETA in seconds for this torrent""" left = self.handle.status().total_wanted \ - self.handle.status().total_done - # The torrent file is done - if left == 0: - return 0 - - # Calculate the ETA in seconds and return it - return (left / self.handle.status().download_payload_rate) + try: + eta = left / self.handle.status().download_payload_rate + except ZeroDivisionError: + eta = 0 + + return eta def get_info(self): """Returns the torrents info.. stuff that remains constant, such as @@ -83,3 +91,4 @@ class Torrent: self.get_eta(), self.queue[self.torrent_id] ) + diff --git a/deluge/core/torrentmanager.py b/deluge/core/torrentmanager.py index bb71fec54..af715935e 100644 --- a/deluge/core/torrentmanager.py +++ b/deluge/core/torrentmanager.py @@ -59,3 +59,37 @@ class TorrentManager: # Add the torrent to the queue self.queue.append(torrent.torrent_id) return torrent.torrent_id + + def remove(self, torrent_id): + """Remove a torrent from the manager""" + try: + del self.torrents[torrent_id] + except KeyError, ValueError: + return False + return True + + def get_info_template(self): + """Returns a list of strings that correspond to the info tuple""" + return [ + "name", + "total_size", + "num_pieces" + ] + + def get_status_template(self): + """Returns a list of strings that correspond to the status tuple""" + return [ + "state", + "paused", + "progress", + "next_announce", + "total_payload_download", + "total_payload_upload", + "download_payload_rate", + "upload_payload_rate", + "num_peers", + "num_seeds", + "total_wanted", + "eta", + "position" + ] diff --git a/deluge/core/torrentqueue.py b/deluge/core/torrentqueue.py index 3e09cadcb..5694f592e 100644 --- a/deluge/core/torrentqueue.py +++ b/deluge/core/torrentqueue.py @@ -54,7 +54,11 @@ class TorrentQueue: """Prepend torrent_id to the top of the queue""" log.debug("Prepend torrent %s to queue..", torrent_id) self.queue.insert(0, torrent_id) - + + def remove(self, torrent_id): + """Removes torrent_id from the list""" + self.queue.remove(torrent_id) + def up(self, torrent_id): """Move torrent_id up one in the queue""" if torrent_id not in self.queue: diff --git a/deluge/ui/gtkui/columns.py b/deluge/ui/gtkui/columns.py index 23ceeda12..d47cd3c5a 100644 --- a/deluge/ui/gtkui/columns.py +++ b/deluge/ui/gtkui/columns.py @@ -62,7 +62,7 @@ def cell_data_time(column, cell, model, iter, data): time_str = _("Infinity") else: time_str = deluge.common.ftime(time) - cell.set_property('text', time_str) + cell.set_property('text', time_str) def cell_data_ratio(column, cell, model, iter, data): ratio = float(model.get_value(iter, data)) diff --git a/deluge/ui/gtkui/functions.py b/deluge/ui/gtkui/functions.py index f06e8f332..5e6fb7226 100644 --- a/deluge/ui/gtkui/functions.py +++ b/deluge/ui/gtkui/functions.py @@ -85,3 +85,28 @@ def add_torrent_file(): (path, filename) = os.path.split(torrent_file) core.add_torrent_file(filename, f.read()) f.close() + +def remove_torrent(torrent_ids): + """Removes torrent_ids from the core.. Expects a list of torrent_ids""" + log.debug("Attempting to removing torrents: %s", torrent_ids) + core = get_core() + for torrent_id in torrent_ids: + core.remove_torrent(torrent_id) + +def get_torrent_status_dict(core, torrent_id): + """Builds and returns a status dictionary using the status template""" + status = core.get_torrent_status(torrent_id) + template = core.get_torrent_status_template() + status_dict = {} + for string in template: + status_dict[string] = status[template.index(string)] + return status_dict + +def get_torrent_info_dict(core, torrent_id): + """Builds and returns an info dictionary using the info template""" + info = core.get_torrent_info(torrent_id) + template = core.get_torrent_info_template() + info_dict = {} + for string in template: + info_dict[string] = info[template.index(string)] + return info_dict diff --git a/deluge/ui/gtkui/glade/torrent_menu.glade b/deluge/ui/gtkui/glade/torrent_menu.glade index 21b1a7f12..dc1c89d74 100644 --- a/deluge/ui/gtkui/glade/torrent_menu.glade +++ b/deluge/ui/gtkui/glade/torrent_menu.glade @@ -58,7 +58,7 @@ GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK _Remove Torrent True - + True diff --git a/deluge/ui/gtkui/mainwindow.py b/deluge/ui/gtkui/mainwindow.py index f9641954c..ffa8b66c9 100644 --- a/deluge/ui/gtkui/mainwindow.py +++ b/deluge/ui/gtkui/mainwindow.py @@ -36,6 +36,7 @@ import logging import pygtk pygtk.require('2.0') import gtk, gtk.glade +import gobject import pkg_resources from menubar import MenuBar @@ -58,7 +59,13 @@ class MainWindow: self.menubar = MenuBar(self) self.toolbar = ToolBar(self) self.torrentview = TorrentView(self) + + gobject.timeout_add(1000, self.update) + def update(self): + self.torrentview.update() + return True + def show(self): self.window.show_all() diff --git a/deluge/ui/gtkui/menubar.py b/deluge/ui/gtkui/menubar.py index 0b3e6a036..5e758a788 100644 --- a/deluge/ui/gtkui/menubar.py +++ b/deluge/ui/gtkui/menubar.py @@ -125,6 +125,8 @@ class MenuBar: log.debug("on_menuitem_edittrackers_activate") def on_menuitem_remove_activate(self, data=None): log.debug("on_menuitem_remove_activate") + functions.remove_torrent( + self.window.torrentview.get_selected_torrents()) def on_menuitem_queuetop_activate(self, data=None): log.debug("on_menuitem_queuetop_activate") def on_menuitem_queueup_activate(self, data=None): diff --git a/deluge/ui/gtkui/signals.py b/deluge/ui/gtkui/signals.py index 00827cdcc..c1d4431d8 100644 --- a/deluge/ui/gtkui/signals.py +++ b/deluge/ui/gtkui/signals.py @@ -61,11 +61,17 @@ class Signals: 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) def torrent_added_signal(self, torrent_id): log.debug("torrent_added signal received..") log.debug("torrent id: %s", torrent_id) # Add the torrent to the treeview - self.ui.main_window.torrentview.add_torrent(torrent_id, - self.core.get_torrent_info(torrent_id), - self.core.get_torrent_status(torrent_id)) + self.ui.main_window.torrentview.add_row(torrent_id) + + def torrent_removed_signal(self, torrent_id): + log.debug("torrent_remove signal received..") + log.debug("torrent id: %s", torrent_id) + # Remove the torrent from the treeview + self.ui.main_window.torrentview.remove_row(torrent_id) diff --git a/deluge/ui/gtkui/torrentview.py b/deluge/ui/gtkui/torrentview.py index 5f2341a4c..c09177da9 100644 --- a/deluge/ui/gtkui/torrentview.py +++ b/deluge/ui/gtkui/torrentview.py @@ -40,14 +40,33 @@ import gobject import gettext import columns +import functions # Get the logger log = logging.getLogger("deluge") +# Initializes the columns for the torrent_view +(TORRENT_VIEW_COL_UID, +TORRENT_VIEW_COL_QUEUE, +TORRENT_VIEW_COL_STATUSICON, +TORRENT_VIEW_COL_NAME, +TORRENT_VIEW_COL_SIZE, +TORRENT_VIEW_COL_PROGRESS, +TORRENT_VIEW_COL_STATUS, +TORRENT_VIEW_COL_CONNECTED_SEEDS, +TORRENT_VIEW_COL_SEEDS, +TORRENT_VIEW_COL_CONNECTED_PEERS, +TORRENT_VIEW_COL_PEERS, +TORRENT_VIEW_COL_DOWNLOAD, +TORRENT_VIEW_COL_UPLOAD, +TORRENT_VIEW_COL_ETA, +TORRENT_VIEW_COL_RATIO) = range(15) + class TorrentView: def __init__(self, window): log.debug("TorrentView Init..") self.window = window + self.core = functions.get_core() # Get the torrent_view widget self.torrent_view = self.window.main_glade.get_widget("torrent_view") @@ -63,23 +82,6 @@ class TorrentView: self.torrent_view.set_reorderable(True) self.torrent_view.get_selection().set_mode(gtk.SELECTION_MULTIPLE) - # Initializes the columns for the torrent_view - (TORRENT_VIEW_COL_UID, - TORRENT_VIEW_COL_QUEUE, - TORRENT_VIEW_COL_STATUSICON, - TORRENT_VIEW_COL_NAME, - TORRENT_VIEW_COL_SIZE, - TORRENT_VIEW_COL_PROGRESS, - TORRENT_VIEW_COL_STATUS, - TORRENT_VIEW_COL_CONNECTED_SEEDS, - TORRENT_VIEW_COL_SEEDS, - TORRENT_VIEW_COL_CONNECTED_PEERS, - TORRENT_VIEW_COL_PEERS, - TORRENT_VIEW_COL_DOWNLOAD, - TORRENT_VIEW_COL_UPLOAD, - TORRENT_VIEW_COL_ETA, - TORRENT_VIEW_COL_RATIO) = range(15) - self.queue_column = columns.add_text_column( self.torrent_view, "#", TORRENT_VIEW_COL_QUEUE) @@ -149,32 +151,97 @@ class TorrentView: self.torrent_view.get_selection().connect("changed", self.on_selection_changed) - def add_torrent(self, torrent_id, info, status): + def update(self): + """Update the view, this is likely called by a timer""" + # Iterate through the torrent_model and update rows + row = self.torrent_model.get_iter_first() + while row is not None: + torrent_id = self.torrent_model.get_value(row, 0) + status = functions.get_torrent_status_dict(self.core, torrent_id) + # Set values for each column in the row + self.torrent_model.set_value(row, TORRENT_VIEW_COL_QUEUE, + status["position"]+1) + self.torrent_model.set_value(row, TORRENT_VIEW_COL_PROGRESS, + status["progress"]*100) + self.torrent_model.set_value(row, TORRENT_VIEW_COL_STATUS, + status["state"]) + self.torrent_model.set_value(row, TORRENT_VIEW_COL_CONNECTED_SEEDS, + status["num_seeds"]) + self.torrent_model.set_value(row, TORRENT_VIEW_COL_SEEDS, + status["num_seeds"]) + self.torrent_model.set_value(row, TORRENT_VIEW_COL_CONNECTED_PEERS, + status["num_peers"]) + self.torrent_model.set_value(row, TORRENT_VIEW_COL_PEERS, + status["num_peers"]) + self.torrent_model.set_value(row, TORRENT_VIEW_COL_DOWNLOAD, + status["download_payload_rate"]) + self.torrent_model.set_value(row, TORRENT_VIEW_COL_UPLOAD, + status["upload_payload_rate"]) + self.torrent_model.set_value(row, TORRENT_VIEW_COL_ETA, + status["eta"]) + row = self.torrent_model.iter_next(row) + + def add_row(self, torrent_id): """Adds a new torrent row to the treeview""" - # UID, Q#, Status Icon, Name, Size, Progress, Message, Seeders, Peers, - # DL, UL, ETA, Share - self.torrent_model.insert(status[12], [ + # Get the status and info dictionaries + status = functions.get_torrent_status_dict(self.core, torrent_id) + info = functions.get_torrent_info_dict(self.core, torrent_id) + + # Insert the row with info provided from core + self.torrent_model.insert(status["position"], [ torrent_id, - status[12]+1, - None, ## Status Icon - info[0], - info[2], - status[2], - status[0], - status[9], - status[9], - status[8], - status[8], - status[6], - status[7], - status[11], - 0.0 + status["position"]+1, + None, + info["name"], + info["total_size"], + status["progress"]*100, + status["state"], + status["num_seeds"], + status["num_seeds"], + status["num_peers"], + status["num_peers"], + status["download_payload_rate"], + status["upload_payload_rate"], + status["eta"], + 0.0 ]) + + def remove_row(self, torrent_id): + """Removes a row with torrent_id""" + row = self.torrent_model.get_iter_first() + while row is not None: + # Check if this row is the row we want to remove + if self.torrent_model.get_value(row, 0) == torrent_id: + self.torrent_model.remove(row) + # Force an update of the torrentview + self.update() + break + row = self.torrent_model.iter_next(row) + + def get_selected_torrents(self): + """Returns a list of selected torrents or None""" + torrent_ids = [] + paths = self.torrent_view.get_selection().get_selected_rows()[1] + + try: + for path in paths: + torrent_ids.append( + self.torrent_model.get_value( + self.torrent_model.get_iter(path), 0)) + return torrent_ids + except ValueError: + return None ### Callbacks ### - def on_button_press_event(self, widget, event, data): + def on_button_press_event(self, widget, event): log.debug("on_button_press_event") + # We only care about right-clicks + if event.button == 3: + # Show the Torrent menu from the MenuBar + torrentmenu = self.window.menubar.torrentmenu.get_widget( + "torrent_menu") + torrentmenu.popup(None, None, None, event.button, event.time) - def on_selection_changed(self, treeselection, data): + def on_selection_changed(self, treeselection): log.debug("on_selection_changed")